In language composition, the source code of the imported language is conceptually at the same path as the source code of the importing language. Because of this, you get at least warnings from the Stratego compiler, when Stratego modules in the importing language contain wildcard imports for generated files such as signatures or pretty-printing rules:

 [ strj | warning ] Found more than one matching subdirectory found for 'signatures'

This is actually a hint at a bigger issue. Module dependencies in the imported language break when building the importing language. Modules of the imported language are hidden by modules of the importing language, even when a module of the imported language depends on a module of the imported language. This is an issue (at least) in SDF3 and in Stratego. In the best case, this results in build failures, since part of the source code of the imported language is no longer included in the build. However, the error messages typically point out missing strategies in the imported language, which is confusing, since the imported language itself builds fine, but breaks in the build of the importing language. In even more subtle cases, the build succeeds, but the importing language shows unintended behavior. This can be hard to spot, and when you spot it, it is completely unclear what is going on, until you understand that overlapping paths result in unexpected module dependencies.

How to reproduce

Create a new Spoofax language project with project name A, leave everything else on default.

Create a new file syntax/I.sdf3:

module I

context-free syntax

  I.I = [defined in language A]

Change syntax/A.sdf3 to

module A

context-free start-symbols

context-free syntax
  Start = I

Add the following lines to the end of metaborg.yaml:

- language: Stratego-Sugar
  directory: src-gen
- language: SDF
  directory: src-gen/syntax
- language: TemplateLang
  directory: syntax
- language: EditorService
  directory: src-gen
- language: EditorService
  directory: editor

Next, create a second Spoofax language project with project name B, leave everything else on default.

Change the dependencies in metaborg.yaml to include a source dependency:

  - org.metaborg:org.metaborg.meta.lang.esv:${metaborgVersion}
  - org.metaborg:org.metaborg.meta.lang.template:${metaborgVersion}
  - org.metaborg:org.metaborg.meta.lang.nabl:${metaborgVersion}
  - org.metaborg:org.metaborg.meta.lang.ts:${metaborgVersion}
  - org.metaborg:org.metaborg.meta.lib.analysis:${metaborgVersion}
  - org.example:A:0.1.0-SNAPSHOT

Create a new file syntax/I.sdf3:

module I

context-free syntax

  I.I = [defined in language B]

Change syntax/B.sdf3 to

module B imports A

Note that module I in project B is never imported. Reference navigation in SDF3 works as intended, A in B.sdf3 brings you to A.sdf3 in project A, and I in A.sdf3 brings you to I.sdf3 in the same project.

Build both projects and create a new file b.b:

defined in language A

This results in a parse error. Change b.b to

defined in language B

This is accepted as a valid program, showing that I.sdf3 from project B made it into the parse table, even though no module imports it.

You can do similar things with Stratego modules.


Use extra subfolders in the importing language. This allows you to distinguish modules of the imported language from modules of the importing language, including generated modules. Make sure to move files from the initial project, e.g. trans/pp.str into your subfolder.

System information

Eclipse: org.eclipse.platform.ide 4.5.2.M20160212-1500
Spoofax: org.metaborg.spoofax.eclipse
System: Mac OS X x86_64 10.11.4
Submitted by Guido Wachsmuth on 16 June 2016 at 17:34
example.zip16 June 2016 at 17:36

On 17 June 2016 at 11:22 Gabriël Konat commented:

This is indeed a problem which I encountered as well. It is really noticeable with pretty-printers with overlapping sorts, which fail or start looping when composed. It also breaks wildcard imports in Stratego, since importing signatures/- will only import files from the first available signatures directory, instead of all of them. This problem is caused by of a lack of a proper module/package/namespace system in our metalanguages.

The best thing we can do with our current metalanguages is make sure that generated things which can overlap are qualified by language name. E.g. pretty-printer rules should be prefixed with the language names, Stratego files in src-gen should be generated into a subdirectory of the language name, etc.

The problem of using the language name here again is that it makes changing the language name harder, because strategy calls and imports will have the language name in them. However, if we use a single name consistently everywhere, we can more easily change the name with a single find/replace in users of a language. Using the language identifier is not really possible, since it has dots in them, e.g. org.metaborg.meta.lang.template.

On 17 August 2016 at 14:43 Gabriël Konat tagged composition

Log in to post comments