diff --git a/lwb/metalang/cfg/cfg.spoofax2/syntax/part/language.sdf3 b/lwb/metalang/cfg/cfg.spoofax2/syntax/part/language.sdf3 index 13e546fd4..180c660b9 100644 --- a/lwb/metalang/cfg/cfg.spoofax2/syntax/part/language.sdf3 +++ b/lwb/metalang/cfg/cfg.spoofax2/syntax/part/language.sdf3 @@ -8,7 +8,7 @@ imports context-free sorts Sdf3Option Sdf3Source Sdf3FilesOption Sdf3PrebuiltOption - Sdf3ParseTableGeneratorOption + Sdf3ParseTableGeneratorOption Sdf3PlaceholderOption context-free syntax @@ -16,19 +16,19 @@ context-free syntax <{Sdf3Option "\n"}*> }> Sdf3Option.Sdf3Source = > - + Sdf3Source.Sdf3Files = }> Sdf3FilesOption.Sdf3FilesMainSourceDirectory = > Sdf3FilesOption.Sdf3FilesMainFile = > - + Sdf3Source.Sdf3Prebuilt = }> Sdf3PrebuiltOption.Sdf3PrebuiltParseTableAtermFile = > Sdf3PrebuiltOption.Sdf3PrebuiltParseTablePersistedFile = > - + // TODO: move into source after CC lab. Sdf3Option.Sdf3ParseTableGeneratorSection = @@ -40,6 +40,12 @@ context-free syntax Sdf3ParseTableGeneratorOption.Sdf3ParseTableGeneratorCheckOverlap = > Sdf3ParseTableGeneratorOption.Sdf3ParseTableGeneratorCheckPriorities = > + Sdf3Option.Sdf3PlaceholderSection = +}> + Sdf3PlaceholderOption.Sdf3PlaceholderPrefix = > + Sdf3PlaceholderOption.Sdf3PlaceholderSuffix = > + context-free sorts EsvOption EsvSource EsvFilesOption EsvPrebuiltOption @@ -50,7 +56,7 @@ context-free syntax <{EsvOption "\n"}*> }> EsvOption.EsvSource = > - + EsvSource.EsvFiles = }> @@ -58,7 +64,7 @@ context-free syntax EsvFilesOption.EsvFilesMainFile = > EsvFilesOption.EsvFilesIncludeDirectory = > EsvFilesOption.EsvFilesIncludeLibspoofax2Exports = > - + EsvSource.EsvPrebuilt = }> @@ -74,14 +80,14 @@ context-free syntax <{StatixOption "\n"}*> }> StatixOption.StatixSource = > - + StatixSource.StatixFiles = }> StatixFilesOption.StatixFilesMainSourceDirectory = > StatixFilesOption.StatixFilesMainFile = > StatixFilesOption.StatixFilesIncludeDirectory = > - + StatixSource.StatixPrebuilt = }> @@ -100,16 +106,16 @@ context-free syntax <{StrategoOption "\n"}*> }> StrategoOption.StrategoSource = > - + StrategoSource.StrategoFiles = }> StrategoFilesOption.StrategoFilesMainSourceDirectory = > StrategoFilesOption.StrategoFilesMainFile = > StrategoFilesOption.StrategoFilesIncludeDirectory = > - + // TODO: move into source after CC lab. StrategoOption.StrategoSdf3StatixExplicationGen = > - + StrategoOption.StrategoLanguageStrategyAffix = > StrategoOption.StrategoOutputJavaPackageId = > diff --git a/lwb/metalang/cfg/cfg.spoofax2/trans/statsem/part/language.stx b/lwb/metalang/cfg/cfg.spoofax2/trans/statsem/part/language.stx index 7b28dce9f..02be44b14 100644 --- a/lwb/metalang/cfg/cfg.spoofax2/trans/statsem/part/language.stx +++ b/lwb/metalang/cfg/cfg.spoofax2/trans/statsem/part/language.stx @@ -4,23 +4,23 @@ imports statsem/part statsem/expr - + signatures/part/language-sig rules // Sdf3 section and options partOk(s, Sdf3Section(options)) :- sdf3OptionsOk(s, options). - + sdf3OptionOk : scope * Sdf3Option sdf3OptionsOk maps sdf3OptionOk(*, list(*)) - + sdf3OptionOk(s, Sdf3Source(source)) :- sdf3SourceOk(s, source). - + sdf3SourceOk : scope * Sdf3Source sdf3SourceOk(s, Sdf3Files(options)) :- sdf3FilesOptionsOk(s, options). sdf3SourceOk(s, Sdf3Prebuilt(options)) :- sdf3PrebuiltOptionsOk(s, options). - + sdf3FilesOptionOk : scope * Sdf3FilesOption sdf3FilesOptionsOk maps sdf3FilesOptionOk(*, list(*)) sdf3FilesOptionOk(s, Sdf3FilesMainSourceDirectory(e)) :- @@ -37,37 +37,48 @@ rules // Sdf3 section and options sdf3OptionOk(s, Sdf3ParseTableGeneratorSection(options)) :- sdf3ParseTableGeneratorOptionsOk(s, options). - + sdf3ParseTableGeneratorOptionOk : scope * Sdf3ParseTableGeneratorOption sdf3ParseTableGeneratorOptionsOk maps sdf3ParseTableGeneratorOptionOk(*, list(*)) - - sdf3ParseTableGeneratorOptionOk(s, Sdf3ParseTableGeneratorDynamic(e)) :- + + sdf3ParseTableGeneratorOptionOk(s, Sdf3ParseTableGeneratorDynamic(e)) :- typeOfExpr(s, e) == BOOL() | error $[Expected boolean]@e. - sdf3ParseTableGeneratorOptionOk(s, Sdf3ParseTableGeneratorDataDependent(e)) :- + sdf3ParseTableGeneratorOptionOk(s, Sdf3ParseTableGeneratorDataDependent(e)) :- typeOfExpr(s, e) == BOOL() | error $[Expected boolean]@e. - sdf3ParseTableGeneratorOptionOk(s, Sdf3ParseTableGeneratorLayoutSensitive(e)) :- + sdf3ParseTableGeneratorOptionOk(s, Sdf3ParseTableGeneratorLayoutSensitive(e)) :- typeOfExpr(s, e) == BOOL() | error $[Expected boolean]@e. - sdf3ParseTableGeneratorOptionOk(s, Sdf3ParseTableGeneratorSolveDeepConflicts(e)) :- + sdf3ParseTableGeneratorOptionOk(s, Sdf3ParseTableGeneratorSolveDeepConflicts(e)) :- typeOfExpr(s, e) == BOOL() | error $[Expected boolean]@e. - sdf3ParseTableGeneratorOptionOk(s, Sdf3ParseTableGeneratorCheckOverlap(e)) :- + sdf3ParseTableGeneratorOptionOk(s, Sdf3ParseTableGeneratorCheckOverlap(e)) :- typeOfExpr(s, e) == BOOL() | error $[Expected boolean]@e. - sdf3ParseTableGeneratorOptionOk(s, Sdf3ParseTableGeneratorCheckPriorities(e)) :- + sdf3ParseTableGeneratorOptionOk(s, Sdf3ParseTableGeneratorCheckPriorities(e)) :- typeOfExpr(s, e) == BOOL() | error $[Expected boolean]@e. + sdf3OptionOk(s, Sdf3PlaceholderSection(options)) :- + sdf3PlaceholderOptionsOk(s, options). + + sdf3PlaceholderOptionOk : scope * Sdf3PlaceholderOption + sdf3PlaceholderOptionsOk maps sdf3PlaceholderOptionOk(*, list(*)) + + sdf3PlaceholderOptionOk(s, Sdf3PlaceholderPrefix(e)) :- + typeOfExpr(s, e) == STRING() | error $[Expected string]@e. + sdf3PlaceholderOptionOk(s, Sdf3PlaceholderSuffix(e)) :- + typeOfExpr(s, e) == STRING() | error $[Expected string]@e. + rules // Esv section and options partOk(s, EsvSection(options)) :- esvOptionsOk(s, options). - + esvOptionOk : scope * EsvOption esvOptionsOk maps esvOptionOk(*, list(*)) esvOptionOk(s, EsvSource(source)) :- esvSourceOk(s, source). - + esvSourceOk : scope * EsvSource esvSourceOk(s, EsvFiles(options)) :- esvFilesOptionsOk(s, options). esvSourceOk(s, EsvPrebuilt(options)) :- esvPrebuiltOptionsOk(s, options). - + esvFilesOptionOk : scope * EsvFilesOption esvFilesOptionsOk maps esvFilesOptionOk(*, list(*)) esvFilesOptionOk(s, EsvFilesMainSourceDirectory(e)) :- @@ -87,17 +98,17 @@ rules // Esv section and options rules // Statix section and options partOk(s, StatixSection(options)) :- statixOptionsOk(s, options). - + statixOptionOk : scope * StatixOption statixOptionsOk maps statixOptionOk(*, list(*)) - + statixOptionOk(s, StatixSource(source)) :- statixSourceOk(s, source). - + statixSourceOk : scope * StatixSource statixSourceOk(s, StatixFiles(options)) :- statixFilesOptionsOk(s, options). statixSourceOk(s, StatixPrebuilt(options)) :- statixPrebuiltOptionsOk(s, options). - + statixFilesOptionOk : scope * StatixFilesOption statixFilesOptionsOk maps statixFilesOptionOk(*, list(*)) statixFilesOptionOk(s, StatixFilesMainSourceDirectory(e)) :- @@ -113,21 +124,21 @@ rules // Statix section and options typeOfExpr(s, e) == PATH() | error $[Expected path]@e. statixOptionOk(s, StatixSdf3SignatureGen(e)) :- - typeOfExpr(s, e) == BOOL() | error $[Expected boolean]@e. - + typeOfExpr(s, e) == BOOL() | error $[Expected boolean]@e. + rules // Stratego section and options partOk(s, StrategoSection(options)) :- strategoOptionsOk(s, options). - + strategoOptionOk : scope * StrategoOption strategoOptionsOk maps strategoOptionOk(*, list(*)) strategoOptionOk(s, StrategoSource(source)) :- strategoSourceOk(s, source). - + strategoSourceOk : scope * StrategoSource strategoSourceOk(s, StrategoFiles(options)) :- strategoFilesOptionsOk(s, options). - + strategoFilesOptionOk : scope * StrategoFilesOption strategoFilesOptionsOk maps strategoFilesOptionOk(*, list(*)) strategoFilesOptionOk(s, StrategoFilesMainSourceDirectory(e)) :- diff --git a/lwb/metalang/cfg/cfg/src/main/java/mb/cfg/convert/CfgAstToObject.java b/lwb/metalang/cfg/cfg/src/main/java/mb/cfg/convert/CfgAstToObject.java index fb4c75837..0e9ea822e 100644 --- a/lwb/metalang/cfg/cfg/src/main/java/mb/cfg/convert/CfgAstToObject.java +++ b/lwb/metalang/cfg/cfg/src/main/java/mb/cfg/convert/CfgAstToObject.java @@ -187,6 +187,11 @@ public static Output convert( ptgParts.forOneSubtermAsBool("Sdf3ParseTableGeneratorCheckOverlap", builder::checkOverlapInParseTable); ptgParts.forOneSubtermAsBool("Sdf3ParseTableGeneratorCheckPriorities", builder::checkPrioritiesInParseTable); }); + + subParts.getAllSubTermsInListAsParts("Sdf3PlaceholderSection").ifSome(plhdrParts -> { + plhdrParts.forOneSubtermAsString("Sdf3PlaceholderPrefix", builder::sdf3PlaceholderPrefix); + plhdrParts.forOneSubtermAsString("Sdf3PlaceholderSuffix", builder::sdf3PlaceholderSuffix); + }); }); parts.getAllSubTermsInListAsParts("EsvSection").ifSome(subParts -> { final CfgEsvConfig.Builder builder = languageCompilerInputBuilder.withEsv(); diff --git a/lwb/metalang/cfg/cfg/src/main/java/mb/cfg/metalang/CfgSdf3Config.java b/lwb/metalang/cfg/cfg/src/main/java/mb/cfg/metalang/CfgSdf3Config.java index f60cd4406..e1d551233 100644 --- a/lwb/metalang/cfg/cfg/src/main/java/mb/cfg/metalang/CfgSdf3Config.java +++ b/lwb/metalang/cfg/cfg/src/main/java/mb/cfg/metalang/CfgSdf3Config.java @@ -79,6 +79,13 @@ default ResourcePath parseTablePersistedOutputFile() { ; } + @Value.Default default String sdf3PlaceholderPrefix() { + return "$"; + } + + @Value.Default default String sdf3PlaceholderSuffix() { + return ""; + } /// Automatically provided sub-inputs diff --git a/lwb/metalang/sdf3/sdf3/build.gradle.kts b/lwb/metalang/sdf3/sdf3/build.gradle.kts index 4d2bca11c..6937d40c1 100644 --- a/lwb/metalang/sdf3/sdf3/build.gradle.kts +++ b/lwb/metalang/sdf3/sdf3/build.gradle.kts @@ -197,7 +197,20 @@ fun AdapterProjectCompiler.Input.Builder.configureCompilerInput() { val showDesugarCommand = showCommand(showDesugar, "desugared") val showPermissiveCommand = showCommand(showPermissive, "permissive grammar") - val showNormalFormCommand = showCommand(showNormalForm, "normal-form") + //val showNormalFormCommand = showCommand(showNormalForm, "normal-form") + val showNormalFormCommand = CommandDefRepr.builder() + .type(commandPackageId, showNormalForm.id() + "Command") + .taskDefType(showNormalForm) + .argType(showNormalForm.appendToId(".Args")) + .displayName("Show normal form") + .description("Shows normal form") + .addSupportedExecutionTypes(CommandExecutionType.ManualOnce, CommandExecutionType.ManualContinuous) + .addAllParams(listOf( + ParamRepr.of("root", TypeInfo.of("mb.resource.hierarchical", "ResourcePath"), true, ArgProviderRepr.enclosingContext(EnclosingCommandContextType.Project)), + ParamRepr.of("file", TypeInfo.of("mb.resource", "ResourceKey"), true, ArgProviderRepr.context(CommandContextType.File)), + ParamRepr.of("concrete", TypeInfo.ofBoolean(), true) + )) + .build() val showSignatureCommand = showAnalyzedCommand(showSignature, "Stratego signatures") val showDynsemSignatureCommand = showAnalyzedCommand(showDynsemSignature, "DynSem signatures") @@ -243,7 +256,8 @@ fun AdapterProjectCompiler.Input.Builder.configureCompilerInput() { .description("Shows the parse table built from given main file") .addSupportedExecutionTypes(CommandExecutionType.ManualOnce, CommandExecutionType.ManualContinuous) .addAllParams(listOf( - ParamRepr.of("root", TypeInfo.of("mb.resource.hierarchical", "ResourcePath"), true, ArgProviderRepr.enclosingContext(EnclosingCommandContextType.Project)) + ParamRepr.of("root", TypeInfo.of("mb.resource.hierarchical", "ResourcePath"), true, ArgProviderRepr.enclosingContext(EnclosingCommandContextType.Project)), + ParamRepr.of("strategyAffix", TypeInfo.ofString(), true) )) .build() addCommandDefs( diff --git a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/stratego/Sdf3Context.java b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/stratego/Sdf3Context.java index 1791d724e..a813eb09b 100644 --- a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/stratego/Sdf3Context.java +++ b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/stratego/Sdf3Context.java @@ -1,9 +1,53 @@ package mb.sdf3.stratego; -public class Sdf3Context { +import java.io.Serializable; +import java.util.Objects; + +/** + * SDF3 context. + * + * Add this context to a Stratego context using {@link mb.stratego.common.StrategoRuntime#addContextObject}. + */ +public final class Sdf3Context implements Serializable { + /** The language specification name as a Stratego qualifier. */ public final String strategoQualifier; + /** The prefix for placeholders; or {@code null} to use the default. */ + public final String placeholderPrefix; + /** The suffix for placeholders; or {@code null} to use the default. */ + public final String placeholderSuffix; - public Sdf3Context(String strategoQualifier) { + public Sdf3Context( + String strategoQualifier, + String placeholderPrefix, + String placeholderSuffix + ) { this.strategoQualifier = strategoQualifier; + this.placeholderPrefix = placeholderPrefix; + this.placeholderSuffix = placeholderSuffix; + } + + @Override public boolean equals(Object o) { + if(this == o) return true; + if(o == null || getClass() != o.getClass()) return false; + Sdf3Context that = (Sdf3Context)o; + return strategoQualifier.equals(that.strategoQualifier) + && placeholderPrefix.equals(that.placeholderPrefix) + && placeholderSuffix.equals(that.placeholderSuffix); + } + + @Override public int hashCode() { + return Objects.hash( + strategoQualifier, + placeholderPrefix, + placeholderSuffix + ); + } + + @Override public String toString() { + return "Sdf3Context{" + + "strategoQualifier='" + strategoQualifier + '\'' + + ", placeholderPrefix='" + placeholderPrefix + '\'' + + ", placeholderSuffix='" + placeholderSuffix + '\'' + + '}'; } } diff --git a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/stratego/Sdf3PrimitiveLibrary.java b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/stratego/Sdf3PrimitiveLibrary.java index 8f7ed6baa..6d5b5f473 100644 --- a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/stratego/Sdf3PrimitiveLibrary.java +++ b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/stratego/Sdf3PrimitiveLibrary.java @@ -6,7 +6,7 @@ public class Sdf3PrimitiveLibrary extends AbstractStrategoOperatorRegistry { public Sdf3PrimitiveLibrary() { add(new Sdf3PpLanguageSpecNamePrimitive()); - add(new FailingPrimitive("SSL_EXT_placeholder_chars")); + add(new Sdf3SslExtPlaceholderCharsPrimitive()); } @Override public String getOperatorRegistryName() { diff --git a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/stratego/Sdf3SslExtPlaceholderCharsPrimitive.java b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/stratego/Sdf3SslExtPlaceholderCharsPrimitive.java new file mode 100644 index 000000000..b986e9032 --- /dev/null +++ b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/stratego/Sdf3SslExtPlaceholderCharsPrimitive.java @@ -0,0 +1,33 @@ +package mb.sdf3.stratego; + +import org.spoofax.interpreter.library.AbstractPrimitive; +import mb.stratego.common.AdaptableContext; +import org.spoofax.interpreter.core.IContext; +import org.spoofax.interpreter.core.InterpreterException; +import org.spoofax.interpreter.stratego.Strategy; +import org.spoofax.interpreter.terms.IStrategoTerm; +import org.spoofax.interpreter.terms.ITermFactory; + +/** + * Returns a tuple with the prefix and suffix, respectively, to use to parse/pretty-print placeholders. + */ +public final class Sdf3SslExtPlaceholderCharsPrimitive extends AbstractPrimitive { + + public Sdf3SslExtPlaceholderCharsPrimitive() { + super("SSL_EXT_placeholder_chars", 0, 0); + } + + @Override public boolean call(IContext env, Strategy[] svars, IStrategoTerm[] tvars) throws InterpreterException { + final ITermFactory factory = env.getFactory(); + try { + final Sdf3Context context = AdaptableContext.adaptContextObject(env.contextObject(), Sdf3Context.class); + env.setCurrent(factory.makeTuple( + factory.makeString(context.placeholderPrefix), + factory.makeString(context.placeholderSuffix) + )); + return true; + } catch(RuntimeException e) { + return false; // Context not available; fail + } + } +} diff --git a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/Sdf3AstStrategoTransformTaskDef.java b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/Sdf3AstStrategoTransformTaskDef.java new file mode 100644 index 000000000..b26f29ecf --- /dev/null +++ b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/Sdf3AstStrategoTransformTaskDef.java @@ -0,0 +1,89 @@ +package mb.sdf3.task; + +import mb.common.result.Result; +import mb.pie.api.ExecContext; +import mb.pie.api.Interactivity; +import mb.pie.api.Supplier; +import mb.pie.api.TaskDef; +import mb.sdf3.Sdf3Scope; +import mb.sdf3.stratego.Sdf3Context; +import mb.sdf3.task.spec.Sdf3Config; +import mb.stratego.common.StrategoException; +import mb.stratego.common.StrategoRuntime; +import mb.stratego.pie.AstStrategoTransformTaskDef; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spoofax.interpreter.terms.IStrategoTerm; + +import javax.inject.Inject; +import javax.inject.Provider; +import java.io.Serializable; +import java.util.Objects; +import java.util.Set; + +/** + * Abstract task definition similar to {@link AstStrategoTransformTaskDef} + * but injects a {@link Sdf3Context} into the {@link StrategoRuntime}. + */ +public abstract class Sdf3AstStrategoTransformTaskDef implements TaskDef> { + public /* open */ static class Input implements Serializable { + public final Supplier> astSupplier; + public final Sdf3Config sdf3Config; + public final String strategyAffix; + + public Input( + Supplier> astSupplier, + Sdf3Config sdf3Config, + String strategyAffix + ) { + this.astSupplier = astSupplier; + this.sdf3Config = sdf3Config; + this.strategyAffix = strategyAffix; + } + + @Override public boolean equals(@Nullable Object o) { + if(this == o) return true; + if(o == null || getClass() != o.getClass()) return false; + final Input input = (Input)o; + return astSupplier.equals(input.astSupplier) + && sdf3Config.equals(input.sdf3Config) + && strategyAffix.equals(input.strategyAffix); + } + + @Override public int hashCode() { + return Objects.hash(astSupplier, sdf3Config, strategyAffix); + } + + @Override public String toString() { + return "Sdf3AstStrategoTransformTaskDef$Input{" + + "astSupplier=" + astSupplier + + ", sdf3Config=" + sdf3Config + + ", strategyAffix='" + strategyAffix + '\'' + + '}'; + } + } + + private final Provider strategoRuntimeProvider; + + protected Sdf3AstStrategoTransformTaskDef(Provider strategoRuntimeProvider) { + this.strategoRuntimeProvider = strategoRuntimeProvider; + } + + @Override public Result exec(ExecContext context, I input) throws Exception { + final Sdf3Context sdf3Context = new Sdf3Context( + input.strategyAffix, + input.sdf3Config.placeholderPrefix, + input.sdf3Config.placeholderSuffix + ); + final StrategoRuntime strategoRuntime = strategoRuntimeProvider.get().addContextObject(sdf3Context); + return context.require(input.astSupplier).flatMapOrElse((ast) -> { + try { + ast = doExec(context, input, strategoRuntime, ast); + return Result.ofOk(ast); + } catch(Exception e) { + return Result.ofErr(e); + } + }, Result::ofErr); + } + + protected abstract IStrategoTerm doExec(ExecContext context, I input, StrategoRuntime strategoRuntime, IStrategoTerm ast) throws StrategoException, Exception; +} diff --git a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/Sdf3ToNormalForm.java b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/Sdf3ToNormalForm.java index 454d39d7f..de1252a61 100644 --- a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/Sdf3ToNormalForm.java +++ b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/Sdf3ToNormalForm.java @@ -1,26 +1,35 @@ package mb.sdf3.task; import mb.common.result.Result; +import mb.pie.api.ExecContext; import mb.pie.api.Interactivity; import mb.pie.api.Supplier; import mb.sdf3.Sdf3Scope; +import mb.stratego.common.StrategoException; +import mb.stratego.common.StrategoRuntime; import mb.stratego.pie.AstStrategoTransformTaskDef; import org.spoofax.interpreter.terms.IStrategoTerm; import javax.inject.Inject; +import javax.inject.Provider; import java.util.Set; @Sdf3Scope -public class Sdf3ToNormalForm extends AstStrategoTransformTaskDef { - @Inject public Sdf3ToNormalForm(Sdf3GetStrategoRuntimeProvider getStrategoRuntimeProvider) { - super(getStrategoRuntimeProvider, "module-to-normal-form"); +public class Sdf3ToNormalForm extends Sdf3AstStrategoTransformTaskDef { + @Inject public Sdf3ToNormalForm(Provider strategoRuntimeProvider) { + super(strategoRuntimeProvider); } @Override public String getId() { return getClass().getName(); } - @Override public boolean shouldExecWhenAffected(Supplier> input, Set tags) { + @Override public boolean shouldExecWhenAffected(Input input, Set tags) { return tags.isEmpty() || tags.contains(Interactivity.NonInteractive); } + + @Override + protected IStrategoTerm doExec(ExecContext context, Input input, StrategoRuntime strategoRuntime, IStrategoTerm ast) throws StrategoException, Exception { + return strategoRuntime.invoke("module-to-normal-form", ast); + } } diff --git a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/Sdf3ToPrettyPrinter.java b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/Sdf3ToPrettyPrinter.java index d1595afbb..fe35b245a 100644 --- a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/Sdf3ToPrettyPrinter.java +++ b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/Sdf3ToPrettyPrinter.java @@ -1,74 +1,30 @@ package mb.sdf3.task; -import mb.common.result.Result; import mb.pie.api.ExecContext; import mb.pie.api.Interactivity; -import mb.pie.api.Supplier; -import mb.pie.api.TaskDef; -import mb.sdf3.stratego.Sdf3Context; import mb.sdf3.Sdf3Scope; import mb.stratego.common.StrategoException; import mb.stratego.common.StrategoRuntime; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spoofax.interpreter.terms.IStrategoTerm; import javax.inject.Inject; import javax.inject.Provider; -import java.io.Serializable; -import java.util.Objects; import java.util.Set; @Sdf3Scope -public class Sdf3ToPrettyPrinter implements TaskDef> { - public static class Input implements Serializable { - public final Supplier> astSupplier; - public final String strategoQualifier; - - public Input(Supplier> astSupplier, String strategoQualifier) { - this.astSupplier = astSupplier; - this.strategoQualifier = strategoQualifier; - } - - @Override public boolean equals(@Nullable Object o) { - if(this == o) return true; - if(o == null || getClass() != o.getClass()) return false; - final Input input = (Input)o; - return astSupplier.equals(input.astSupplier) && strategoQualifier.equals(input.strategoQualifier); - } - - @Override public int hashCode() { - return Objects.hash(astSupplier, strategoQualifier); - } - - @Override public String toString() { - return "Input{" + - "astSupplier=" + astSupplier + - ", strategoQualifier='" + strategoQualifier + '\'' + - '}'; - } - } - - private final Provider strategoRuntimeProvider; +public class Sdf3ToPrettyPrinter extends Sdf3AstStrategoTransformTaskDef { @Inject public Sdf3ToPrettyPrinter(Provider strategoRuntimeProvider) { - this.strategoRuntimeProvider = strategoRuntimeProvider; + super(strategoRuntimeProvider); } - @Override public String getId() { return getClass().getName(); } - @Override public Result exec(ExecContext context, Input input) throws Exception { - final StrategoRuntime strategoRuntime = strategoRuntimeProvider.get().addContextObject(new Sdf3Context(input.strategoQualifier)); - return context.require(input.astSupplier).flatMapOrElse((ast) -> { - try { - ast = strategoRuntime.invoke("module-to-pp", ast, strategoRuntime.getTermFactory().makeString("2")); - return Result.ofOk(ast); - } catch(StrategoException e) { - return Result.ofErr(e); - } - }, Result::ofErr); + @Override + protected IStrategoTerm doExec(ExecContext context, Input input, StrategoRuntime strategoRuntime, IStrategoTerm ast) throws StrategoException, Exception { + return strategoRuntime.invoke("module-to-pp", ast, strategoRuntime.getTermFactory().makeString("2")); } @Override public boolean shouldExecWhenAffected(Input input, Set tags) { diff --git a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/debug/Sdf3ShowNormalForm.java b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/debug/Sdf3ShowNormalForm.java index f46a7bfc1..49b7631ea 100644 --- a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/debug/Sdf3ShowNormalForm.java +++ b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/debug/Sdf3ShowNormalForm.java @@ -1,27 +1,112 @@ package mb.sdf3.task.debug; +import mb.pie.api.ExecContext; +import mb.pie.api.TaskDef; +import mb.resource.ResourceKey; +import mb.resource.hierarchical.ResourcePath; import mb.sdf3.Sdf3Scope; +import mb.sdf3.task.Sdf3AstStrategoTransformTaskDef; import mb.sdf3.task.Sdf3Desugar; import mb.sdf3.task.Sdf3GetStrategoRuntimeProvider; import mb.sdf3.task.Sdf3Parse; import mb.sdf3.task.Sdf3ToNormalForm; -import mb.stratego.common.StrategoRuntime; +import mb.sdf3.task.spec.Sdf3Config; +import mb.sdf3.task.spoofax.Sdf3ConfigSupplierWrapper; +import mb.spoofax.core.language.command.CommandFeedback; +import mb.spoofax.core.language.command.ShowFeedback; +import org.checkerframework.checker.nullness.qual.Nullable; import javax.inject.Inject; -import javax.inject.Provider; +import java.io.Serializable; +import java.util.Objects; @Sdf3Scope -public class Sdf3ShowNormalForm extends ShowTaskDef { +public final class Sdf3ShowNormalForm extends ProvideOutputShared implements TaskDef { + public static class Args implements Serializable { + // TODO: this should take a Sdf3SpecConfig and Sdf3Config directly, + // which must be assignable from CLI and such, but this is not possible yet. + public final ResourcePath root; + public final ResourceKey file; + public final boolean concrete; + + public Args(ResourcePath root, ResourceKey file, boolean concrete) { + this.root = root; + this.file = file; + this.concrete = concrete; + } + @Override public boolean equals(@Nullable Object o) { + if(this == o) return true; + if(o == null || getClass() != o.getClass()) return false; + final Args that = (Args)o; + return this.root.equals(that.root) + && this.file.equals(that.file) + && this.concrete == that.concrete; + } + + @Override public int hashCode() { + return Objects.hash( + this.root, + this.file, + this.concrete + ); + } + + @Override public String toString() { + return "Sdf3ShowNormalForm$Input{" + + "root=" + root + + ", file=" + file + + ", concrete=" + concrete + + '}'; + } + } + + + private final Sdf3Parse parse; + private final Sdf3Desugar desugar; + private final Sdf3ToNormalForm operation; + private final Sdf3ConfigSupplierWrapper configSupplierWrapper; + @Inject public Sdf3ShowNormalForm( Sdf3Parse parse, Sdf3Desugar desugar, Sdf3ToNormalForm operation, - Sdf3GetStrategoRuntimeProvider getStrategoRuntimeProvider + Sdf3GetStrategoRuntimeProvider getStrategoRuntimeProvider, + Sdf3ConfigSupplierWrapper configSupplierWrapper ) { - super(parse, desugar, operation, getStrategoRuntimeProvider, "pp-SDF3-string", "normal-form"); + super(getStrategoRuntimeProvider, "pp-SDF3-string", "normal-form"); + this.parse = parse; + this.desugar = desugar; + this.operation = operation; + this.configSupplierWrapper = configSupplierWrapper; } @Override public String getId() { return getClass().getName(); } + + @Override public CommandFeedback exec(ExecContext context, Args args) { + return context.require(configSupplierWrapper.get()).mapOrElse( + configOpt -> configOpt.mapOrElse( + config -> run(context, config, args), + () -> CommandFeedback.of(ShowFeedback.showText("Cannot show normal form; SDF3 was not configured in '" + args.root + "'", "Normal form")) + ), + // TODO: should we propagate configuration errors here? Every task that requires some configuration will + // propagate the same configuration errors, which would lead to duplicates. + e -> CommandFeedback.ofTryExtractMessagesFrom(e, args.root) + ); + } + + private CommandFeedback run(ExecContext context, Sdf3Config config, Args args) { + final String strategyAffix = ""; + return context.require(operation.createTask( + new Sdf3AstStrategoTransformTaskDef.Input( + desugar.createSupplier(parse.inputBuilder().withFile(args.file).buildRecoverableAstSupplier()), + config, + strategyAffix + ) + )).mapOrElse( + ast -> provideOutput(context, args.concrete, ast, args.file), + e -> CommandFeedback.ofTryExtractMessagesFrom(e, args.file) + ); + } } diff --git a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/debug/Sdf3ShowSpecParenthesizer.java b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/debug/Sdf3ShowSpecParenthesizer.java index 7c0107612..c01d26f02 100644 --- a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/debug/Sdf3ShowSpecParenthesizer.java +++ b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/debug/Sdf3ShowSpecParenthesizer.java @@ -1,5 +1,6 @@ package mb.sdf3.task.debug; +import mb.common.option.Option; import mb.common.result.Result; import mb.pie.api.ExecContext; import mb.pie.api.Supplier; @@ -7,9 +8,11 @@ import mb.resource.hierarchical.ResourcePath; import mb.sdf3.Sdf3Scope; import mb.sdf3.task.Sdf3GetStrategoRuntimeProvider; +import mb.sdf3.task.spec.Sdf3Config; import mb.sdf3.task.spec.Sdf3ParseTableToParenthesizer; import mb.sdf3.task.spec.Sdf3SpecConfig; import mb.sdf3.task.spec.Sdf3SpecToParseTable; +import mb.sdf3.task.spoofax.Sdf3ConfigSupplierWrapper; import mb.sdf3.task.spoofax.Sdf3SpecConfigFunctionWrapper; import mb.spoofax.core.language.command.CommandFeedback; import mb.spoofax.core.language.command.ShowFeedback; @@ -18,6 +21,7 @@ import javax.inject.Inject; import java.io.Serializable; +import java.util.NoSuchElementException; @Sdf3Scope public class Sdf3ShowSpecParenthesizer extends ProvideOutputShared implements TaskDef { @@ -53,18 +57,21 @@ public Args(ResourcePath root, boolean concrete) { } } - private final Sdf3SpecConfigFunctionWrapper configFunctionWrapper; + private final Sdf3SpecConfigFunctionWrapper specConfigFunctionWrapper; + private final Sdf3ConfigSupplierWrapper configSupplierWrapper; private final Sdf3SpecToParseTable specToParseTable; private final Sdf3ParseTableToParenthesizer specToParenthesizer; @Inject public Sdf3ShowSpecParenthesizer( Sdf3GetStrategoRuntimeProvider getStrategoRuntimeProvider, - Sdf3SpecConfigFunctionWrapper configFunctionWrapper, + Sdf3SpecConfigFunctionWrapper specConfigFunctionWrapper, + Sdf3ConfigSupplierWrapper configSupplierWrapper, Sdf3SpecToParseTable specToParseTable, Sdf3ParseTableToParenthesizer sdf3ParseTableToParenthesizer ) { super(getStrategoRuntimeProvider, "pp-stratego-string", "parenthesizer"); - this.configFunctionWrapper = configFunctionWrapper; + this.specConfigFunctionWrapper = specConfigFunctionWrapper; + this.configSupplierWrapper = configSupplierWrapper; this.specToParseTable = specToParseTable; this.specToParenthesizer = sdf3ParseTableToParenthesizer; } @@ -74,20 +81,23 @@ public Args(ResourcePath root, boolean concrete) { } @Override public CommandFeedback exec(ExecContext context, Args args) throws Exception { - return context.require(configFunctionWrapper.get(), args.root).mapOrElse( - o -> o.mapOrElse( - c -> run(context, c, args), - () -> CommandFeedback.of(ShowFeedback.showText("Cannot show parenthesizer; SDF3 was not configured in '" + args.root + "'", getName(args.concrete, args.root))) - ), - // TODO: should we propagate configuration errors here? Every task that requires some configuration will - // propagate the same configuration errors, which would lead to duplicates. - e -> CommandFeedback.ofTryExtractMessagesFrom(e, args.root) - ); + try { + final Sdf3SpecConfig specConfig = context.require(specConfigFunctionWrapper.get(), args.root).unwrap().unwrap(); + final Sdf3Config config = context.require(configSupplierWrapper.get()).unwrap().unwrap(); + + return run(context, specConfig, config, "" /* TODO: Set this correctly */, args); + } catch (NoSuchElementException ex) { + return CommandFeedback.of(ShowFeedback.showText("Cannot show parenthesizer; SDF3 was not configured in '" + args.root + "'", getName(args.concrete, args.root))); + } catch (Exception ex) { + return CommandFeedback.ofTryExtractMessagesFrom(ex, args.root); + } } - private CommandFeedback run(ExecContext context, Sdf3SpecConfig config, Args args) { + private CommandFeedback run(ExecContext context, Sdf3SpecConfig specConfig, Sdf3Config config, String strategyAffix, Args args) { final Supplier> parseTableSupplier = specToParseTable.createSupplier(new Sdf3SpecToParseTable.Input( + specConfig, config, + strategyAffix, false )); return context.require(specToParenthesizer, new Sdf3ParseTableToParenthesizer.Args(parseTableSupplier, "parenthesizer")).mapOrElse( diff --git a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/debug/Sdf3ShowSpecParseTable.java b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/debug/Sdf3ShowSpecParseTable.java index 582db340c..8e7a99a8b 100644 --- a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/debug/Sdf3ShowSpecParseTable.java +++ b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/debug/Sdf3ShowSpecParseTable.java @@ -6,8 +6,11 @@ import mb.pie.api.TaskDef; import mb.resource.hierarchical.ResourcePath; import mb.sdf3.Sdf3Scope; +import mb.sdf3.task.Sdf3GetStrategoRuntimeProvider; +import mb.sdf3.task.spec.Sdf3Config; import mb.sdf3.task.spec.Sdf3SpecConfig; import mb.sdf3.task.spec.Sdf3SpecToParseTable; +import mb.sdf3.task.spoofax.Sdf3ConfigSupplierWrapper; import mb.sdf3.task.spoofax.Sdf3SpecConfigFunctionWrapper; import mb.spoofax.core.language.command.CommandFeedback; import mb.spoofax.core.language.command.ShowFeedback; @@ -17,22 +20,26 @@ import javax.inject.Inject; import java.io.Serializable; +import java.util.NoSuchElementException; @Sdf3Scope public class Sdf3ShowSpecParseTable implements TaskDef { public static class Args implements Serializable { // TODO: this should take a Sdf3SpecConfig directly, which must be assignable from CLI and such, but this is not possible yet. public final ResourcePath root; + public final String strategyAffix; - public Args(ResourcePath root) { + public Args(ResourcePath root, String strategyAffix) { this.root = root; + this.strategyAffix = strategyAffix; } @Override public boolean equals(@Nullable Object o) { if(this == o) return true; if(o == null || getClass() != o.getClass()) return false; - final Args args = (Args)o; - return root.equals(args.root); + final Args that = (Args)o; + return this.root.equals(that.root) + && this.strategyAffix.equals(that.strategyAffix); } @Override public int hashCode() { @@ -40,20 +47,24 @@ public Args(ResourcePath root) { } @Override public String toString() { - return "Args{" + + return "Sdf3ShowSpecParseTable$Args{" + "root=" + root + + ", strategyAffix='" + strategyAffix + '\'' + '}'; } } - private final Sdf3SpecConfigFunctionWrapper configFunctionWrapper; + private final Sdf3SpecConfigFunctionWrapper specConfigFunctionWrapper; + private final Sdf3ConfigSupplierWrapper configSupplierWrapper; private final Sdf3SpecToParseTable specToParseTable; @Inject public Sdf3ShowSpecParseTable( - Sdf3SpecConfigFunctionWrapper configFunctionWrapper, + Sdf3SpecConfigFunctionWrapper specConfigFunctionWrapper, + Sdf3ConfigSupplierWrapper configSupplierWrapper, Sdf3SpecToParseTable specToParseTable ) { - this.configFunctionWrapper = configFunctionWrapper; + this.specConfigFunctionWrapper = specConfigFunctionWrapper; + this.configSupplierWrapper = configSupplierWrapper; this.specToParseTable = specToParseTable; } @@ -63,20 +74,24 @@ public Args(ResourcePath root) { @Override public CommandFeedback exec(ExecContext context, Args args) { final String name = "Parse table for project '" + args.root + "'"; - return context.require(configFunctionWrapper.get(), args.root).mapOrElse( - o -> o.mapOrElse( - c -> run(context, c, args, name), - () -> CommandFeedback.of(ShowFeedback.showText("Cannot show parse table; SDF3 was not configured in '" + args.root + "'", name)) - ), + try { + final Sdf3SpecConfig specConfig = context.require(specConfigFunctionWrapper.get(), args.root).unwrap().unwrap(); + final Sdf3Config config = context.require(configSupplierWrapper.get()).unwrap().unwrap(); + return run(context, specConfig, config, args.strategyAffix, args, name); + } catch (NoSuchElementException ex) { + return CommandFeedback.of(ShowFeedback.showText("Cannot show parse table; SDF3 was not configured in '" + args.root + "'", name)); + } catch (Exception ex) { // TODO: should we propagate configuration errors here? Every task that requires some configuration will // propagate the same configuration errors, which would lead to duplicates. - e -> CommandFeedback.ofTryExtractMessagesFrom(e, args.root) - ); + return CommandFeedback.ofTryExtractMessagesFrom(ex, args.root); + } } - private CommandFeedback run(ExecContext context, Sdf3SpecConfig config, Args args, String name) { + private CommandFeedback run(ExecContext context, Sdf3SpecConfig specConfig, Sdf3Config config, String strategyAffix, Args args, String name) { final Result parseTableResult = context.require(specToParseTable, new Sdf3SpecToParseTable.Input( + specConfig, config, + strategyAffix, false )); return parseTableResult diff --git a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/spec/Sdf3Config.java b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/spec/Sdf3Config.java new file mode 100644 index 000000000..525ebfa11 --- /dev/null +++ b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/spec/Sdf3Config.java @@ -0,0 +1,54 @@ +package mb.sdf3.task.spec; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.io.Serializable; +import java.util.Objects; + +/** + * SDF3 configuration parameters. + */ +public final class Sdf3Config implements Serializable { + public final String placeholderPrefix; + public final String placeholderSuffix; + + /** + * Initializes a new instance of the {@link Sdf3Config} class. + * + * @param placeholderPrefix the placeholder prefix + * @param placeholderSuffix the placeholder suffix + */ + public Sdf3Config( + String placeholderPrefix, + String placeholderSuffix + ) { + this.placeholderPrefix = placeholderPrefix; + this.placeholderSuffix = placeholderSuffix; + } + + public static Sdf3Config createDefault() { + return new Sdf3Config("$", ""); + } + + @Override public boolean equals(@Nullable Object o) { + if(this == o) return true; + if(o == null || getClass() != o.getClass()) return false; + final Sdf3Config that = (Sdf3Config)o; + return placeholderPrefix.equals(that.placeholderPrefix) + && placeholderSuffix.equals(that.placeholderSuffix); + } + + @Override public int hashCode() { + return Objects.hash( + placeholderPrefix, + placeholderSuffix + ); + } + + @Override public String toString() { + return "Sdf3Config{" + + "placeholderPrefix='" + placeholderPrefix + '\'' + + ", placeholderSuffix='" + placeholderSuffix + '\'' + + '}'; + } +} diff --git a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/spec/Sdf3SpecConfig.java b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/spec/Sdf3SpecConfig.java index 2b8719c17..6dad976f7 100644 --- a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/spec/Sdf3SpecConfig.java +++ b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/spec/Sdf3SpecConfig.java @@ -44,10 +44,10 @@ public static ParseTableConfiguration createDefaultParseTableConfiguration() { if(this == o) return true; if(o == null || getClass() != o.getClass()) return false; final Sdf3SpecConfig that = (Sdf3SpecConfig)o; - if(!rootDirectory.equals(that.rootDirectory)) return false; - if(!mainSourceDirectory.equals(that.mainSourceDirectory)) return false; - if(!mainFile.equals(that.mainFile)) return false; - return parseTableConfig.equals(that.parseTableConfig); + return rootDirectory.equals(that.rootDirectory) + && mainSourceDirectory.equals(that.mainSourceDirectory) + && mainFile.equals(that.mainFile) + && parseTableConfig.equals(that.parseTableConfig); } @Override public int hashCode() { diff --git a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/spec/Sdf3SpecToParseTable.java b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/spec/Sdf3SpecToParseTable.java index 727bac647..ef7390a47 100644 --- a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/spec/Sdf3SpecToParseTable.java +++ b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/spec/Sdf3SpecToParseTable.java @@ -14,6 +14,7 @@ import mb.resource.hierarchical.walk.ResourceWalker; import mb.sdf3.Sdf3ClassLoaderResources; import mb.sdf3.Sdf3Scope; +import mb.sdf3.task.Sdf3AstStrategoTransformTaskDef; import mb.sdf3.task.Sdf3Desugar; import mb.sdf3.task.Sdf3Parse; import mb.sdf3.task.Sdf3ToCompletion; @@ -30,6 +31,7 @@ import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -37,31 +39,42 @@ @Sdf3Scope public class Sdf3SpecToParseTable implements TaskDef> { public static class Input implements Serializable { - private final Sdf3SpecConfig config; + private final Sdf3SpecConfig specConfig; + private final Sdf3Config config; + private final String strategyAffix; private final boolean createCompletionTable; - public Input(Sdf3SpecConfig config, boolean createCompletionTable) { + public Input(Sdf3SpecConfig specConfig, Sdf3Config config, String strategyAffix, boolean createCompletionTable) { + this.specConfig = specConfig; this.config = config; + this.strategyAffix = strategyAffix; this.createCompletionTable = createCompletionTable; } @Override public boolean equals(@Nullable Object o) { if(this == o) return true; if(o == null || getClass() != o.getClass()) return false; - final Input input = (Input)o; - if(createCompletionTable != input.createCompletionTable) return false; - return config.equals(input.config); + final Input that = (Input)o; + return this.createCompletionTable == that.createCompletionTable + && this.specConfig.equals(that.specConfig) + && this.config.equals(that.config) + && this.strategyAffix.equals(that.strategyAffix); } @Override public int hashCode() { - int result = config.hashCode(); - result = 31 * result + (createCompletionTable ? 1 : 0); - return result; + return Objects.hash( + this.createCompletionTable, + this.specConfig, + this.config, + this.strategyAffix + ); } @Override public String toString() { return "Sdf3SpecToParseTable$Input{" + - "config=" + config + + "specConfig=" + specConfig + + ", config=" + config + + ", strategyAffix='" + strategyAffix + '\'' + ", createCompletionTable=" + createCompletionTable + '}'; } @@ -95,31 +108,31 @@ public Input(Sdf3SpecConfig config, boolean createCompletionTable) { } @Override public Result exec(ExecContext context, Input input) throws IOException { - final JsglrParseTaskInput.Builder parseInputBuilder = parse.inputBuilder().rootDirectoryHint(input.config.rootDirectory); - final Supplier> mainModuleAstSupplier = desugar.createSupplier(parseInputBuilder.withFile(input.config.mainFile).buildAstSupplier()); + final JsglrParseTaskInput.Builder parseInputBuilder = parse.inputBuilder().rootDirectoryHint(input.specConfig.rootDirectory); + final Supplier> mainModuleAstSupplier = desugar.createSupplier(parseInputBuilder.withFile(input.specConfig.mainFile).buildAstSupplier()); final ResourceWalker walker = Sdf3Util.createResourceWalker(); final ResourceMatcher matcher = Sdf3Util.createResourceMatcher(); - final HierarchicalResource mainSourceDirectory = context.require(input.config.mainSourceDirectory, ResourceStampers.modifiedDirRec(walker, matcher)); + final HierarchicalResource mainSourceDirectory = context.require(input.specConfig.mainSourceDirectory, ResourceStampers.modifiedDirRec(walker, matcher)); if(!mainSourceDirectory.exists() || !mainSourceDirectory.isDirectory()) { return Result.ofErr(new IOException("Main SDF3 source directory '" + mainSourceDirectory +"' does not exist or is not a directory")); } final ArrayList>> modulesAstSuppliers; try(final Stream stream = mainSourceDirectory.walk(walker, matcher)) { modulesAstSuppliers = stream - .filter(file -> !file.getPath().equals(input.config.mainFile)) // Filter out main module, as it is supplied separately. + .filter(file -> !file.getPath().equals(input.specConfig.mainFile)) // Filter out main module, as it is supplied separately. .map(file -> desugar.createSupplier(parseInputBuilder.withFile(file.getKey()).buildAstSupplier())) .collect(Collectors.toCollection(ArrayList::new)); } modulesAstSuppliers.add(parseInputBuilder.withFile(classLoaderResources.getDefinitionResource("permissive-water.sdf3").getPath()).buildAstSupplier()); try { - final IStrategoTerm mainNormalizedGrammar = context.require(toNormalized(mainModuleAstSupplier)) + final IStrategoTerm mainNormalizedGrammar = context.require(toNormalized(mainModuleAstSupplier, input)) .expect(e -> new ExpectException("Transforming SDF3 grammar of main module " + mainModuleAstSupplier + " to normal form failed", e)); final NormGrammarReader normGrammarReader = new NormGrammarReader(); for(Supplier> astSupplier : modulesAstSuppliers) { - final IStrategoTerm normalizedGrammarTerm = context.require(toNormalized(astSupplier)) + final IStrategoTerm normalizedGrammarTerm = context.require(toNormalized(astSupplier, input)) .expect(e -> new ExpectException("Transforming SDF3 grammar of " + astSupplier + " to normal form failed", e)); normGrammarReader.addModuleAst(normalizedGrammarTerm); } @@ -138,11 +151,11 @@ public Input(Sdf3SpecConfig config, boolean createCompletionTable) { // main module is the actual main module in case of creating a completion parse table. normGrammarReader.addModuleAst(mainNormalizedGrammar); - final IStrategoTerm mainCompletionNormalizedGrammar = context.require(toCompletionNormalized(mainModuleAstSupplier)) + final IStrategoTerm mainCompletionNormalizedGrammar = context.require(toCompletionNormalized(mainModuleAstSupplier, input)) .expect(e -> new ExpectException("Transforming SDF3 grammar of main module " + mainModuleAstSupplier + " to completion normal form failed", e)); for(Supplier> astSupplier : modulesAstSuppliers) { - final IStrategoTerm normalizedGrammarTerm = context.require(toCompletionNormalized(astSupplier)) + final IStrategoTerm normalizedGrammarTerm = context.require(toCompletionNormalized(astSupplier, input)) .expect(e -> new ExpectException("Transforming SDF3 grammar of " + astSupplier + " to completion normal form failed", e)); normGrammarReader.addModuleAst(normalizedGrammarTerm); } @@ -161,7 +174,7 @@ public Input(Sdf3SpecConfig config, boolean createCompletionTable) { // corresponding signatures file. normalizedGrammar.getModulesRead().remove("normalized/permissive-water-norm"); - return Result.ofOk(new ParseTable(normalizedGrammar, input.config.parseTableConfig)); + return Result.ofOk(new ParseTable(normalizedGrammar, input.specConfig.parseTableConfig)); } catch(ExpectException e) { return Result.ofErr(e); } @@ -171,11 +184,19 @@ public Input(Sdf3SpecConfig config, boolean createCompletionTable) { return tags.isEmpty() || tags.contains(Interactivity.NonInteractive); } - private Task> toNormalized(Supplier> astSupplier) { - return toNormalForm.createTask(toPermissive.createSupplier(astSupplier)); + private Task> toNormalized(Supplier> astSupplier, Input input) { + return toNormalForm.createTask(new Sdf3AstStrategoTransformTaskDef.Input( + toPermissive.createSupplier(astSupplier), + input.config, + input.strategyAffix + )); } - private Task> toCompletionNormalized(Supplier> astSupplier) { - return toNormalForm.createTask(toCompletion.createSupplier(astSupplier)); + private Task> toCompletionNormalized(Supplier> astSupplier, Input input) { + return toNormalForm.createTask(new Sdf3AstStrategoTransformTaskDef.Input( + toCompletion.createSupplier(astSupplier), + input.config, + input.strategyAffix + )); } } diff --git a/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/spoofax/Sdf3ConfigSupplierWrapper.java b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/spoofax/Sdf3ConfigSupplierWrapper.java new file mode 100644 index 000000000..69697c9b4 --- /dev/null +++ b/lwb/metalang/sdf3/sdf3/src/main/java/mb/sdf3/task/spoofax/Sdf3ConfigSupplierWrapper.java @@ -0,0 +1,57 @@ +package mb.sdf3.task.spoofax; + +import mb.common.option.Option; +import mb.common.result.Result; +import mb.pie.api.ExecContext; +import mb.pie.api.Supplier; +import mb.sdf3.Sdf3Scope; +import mb.sdf3.task.spec.Sdf3Config; +import org.checkerframework.checker.nullness.qual.Nullable; + +import javax.inject.Inject; + +@Sdf3Scope +public class Sdf3ConfigSupplierWrapper { + private @Nullable Supplier, ?>> supplier = null; + + + @Inject public Sdf3ConfigSupplierWrapper() {} + + + public Supplier, ?>> get() { + if(supplier == null) { + supplier = Sdf3DefaultSpecConfigSupplier.instance; + } + return supplier; + } + + public void set(Supplier, ?>> supplier) { + if(this.supplier != null) { + throw new IllegalStateException("Supplier in Sdf3ConfigFunctionWrapper was already set or used. After setting or using the supplier, it may not be changed any more to guarantee sound incrementality"); + } + this.supplier = supplier; + } + + + private static class Sdf3DefaultSpecConfigSupplier implements Supplier, ?>> { + private static final Sdf3DefaultSpecConfigSupplier instance = new Sdf3DefaultSpecConfigSupplier(); + + private Sdf3DefaultSpecConfigSupplier() {} + + @Override + public Result, ?> get(ExecContext context) { + return Result.ofOk(Option.ofSome(Sdf3Config.createDefault())); + } + + + @Override public boolean equals(@Nullable Object other) { + return this == other || other != null && this.getClass() == other.getClass(); + } + + @Override public int hashCode() { return 0; } + + @Override public String toString() { return "Sdf3DefaultSpecConfigFunction()"; } + + private Object readResolve() { return instance; } + } +} diff --git a/lwb/metalang/sdf3/sdf3/src/test/java/mb/sdf3/adapter/SpecToParenthesizerTest.java b/lwb/metalang/sdf3/sdf3/src/test/java/mb/sdf3/adapter/SpecToParenthesizerTest.java index f17ce55c0..cd2e74ea5 100644 --- a/lwb/metalang/sdf3/sdf3/src/test/java/mb/sdf3/adapter/SpecToParenthesizerTest.java +++ b/lwb/metalang/sdf3/sdf3/src/test/java/mb/sdf3/adapter/SpecToParenthesizerTest.java @@ -5,7 +5,9 @@ import mb.pie.api.MixedSession; import mb.resource.fs.FSResource; import mb.resource.text.TextResource; +import mb.sdf3.task.spec.Sdf3Config; import mb.sdf3.task.spec.Sdf3ParseTableToParenthesizer; +import mb.sdf3.task.spec.Sdf3SpecConfig; import mb.sdf3.task.spec.Sdf3SpecToParseTable; import org.junit.jupiter.api.Test; import org.spoofax.interpreter.terms.IStrategoTerm; @@ -17,9 +19,14 @@ class SpecToParenthesizerTest extends TestBase { @Test void testTask() throws Exception { final FSResource resource = textFile("a.sdf3", "module test context-free syntax A = "); final Sdf3ParseTableToParenthesizer taskDef = component.getSdf3ParseTableToParenthesizer(); + final Sdf3SpecConfig sdf3SpecConfig = specConfig(rootDirectory.getPath(), rootDirectory.getPath(), resource.getPath()); + final Sdf3Config sdf3Config = new Sdf3Config("$", ""); + final String strategyAffix = "lang"; try(final MixedSession session = newSession()) { final Sdf3SpecToParseTable.Input parseTableInput = new Sdf3SpecToParseTable.Input( - specConfig(rootDirectory.getPath(), rootDirectory.getPath(), resource.getPath()), + sdf3SpecConfig, + sdf3Config, + strategyAffix, false ); final Sdf3ParseTableToParenthesizer.Args parenthesizerArgs = new Sdf3ParseTableToParenthesizer.Args( diff --git a/lwb/metalang/sdf3/sdf3/src/test/java/mb/sdf3/adapter/SpecToParseTableTest.java b/lwb/metalang/sdf3/sdf3/src/test/java/mb/sdf3/adapter/SpecToParseTableTest.java index a81159b3b..c905cc7a8 100644 --- a/lwb/metalang/sdf3/sdf3/src/test/java/mb/sdf3/adapter/SpecToParseTableTest.java +++ b/lwb/metalang/sdf3/sdf3/src/test/java/mb/sdf3/adapter/SpecToParseTableTest.java @@ -5,6 +5,8 @@ import mb.pie.api.MixedSession; import mb.resource.fs.FSResource; import mb.resource.text.TextResource; +import mb.sdf3.task.spec.Sdf3Config; +import mb.sdf3.task.spec.Sdf3SpecConfig; import mb.sdf3.task.spec.Sdf3SpecToParseTable; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -28,9 +30,14 @@ void testTask(boolean createCompletionTable) throws Exception { textFile("src/nested/a.sdf3", "module nested/a context-free syntax A.A = "); textFile("src/nested/b.sdf3", "module nested/b context-free syntax B.B = "); final Sdf3SpecToParseTable taskDef = component.getSdf3SpecToParseTable(); + final Sdf3SpecConfig sdf3SpecConfig = specConfig(rootDirectory.getPath()); + final Sdf3Config sdf3Config = new Sdf3Config("$", ""); + final String strategyAffix = "lang"; try(final MixedSession session = newSession()) { final Sdf3SpecToParseTable.Input input = new Sdf3SpecToParseTable.Input( - specConfig(rootDirectory.getPath()), + sdf3SpecConfig, + sdf3Config, + strategyAffix, createCompletionTable ); final Result parseTableResult = session.require(taskDef.createTask(input)); diff --git a/lwb/metalang/sdf3/sdf3/src/test/java/mb/sdf3/adapter/ToNormalFormTest.java b/lwb/metalang/sdf3/sdf3/src/test/java/mb/sdf3/adapter/ToNormalFormTest.java index 545ec3550..1da2d822f 100644 --- a/lwb/metalang/sdf3/sdf3/src/test/java/mb/sdf3/adapter/ToNormalFormTest.java +++ b/lwb/metalang/sdf3/sdf3/src/test/java/mb/sdf3/adapter/ToNormalFormTest.java @@ -3,7 +3,9 @@ import mb.common.result.Result; import mb.pie.api.MixedSession; import mb.resource.text.TextResource; +import mb.sdf3.task.Sdf3AstStrategoTransformTaskDef; import mb.sdf3.task.Sdf3ToNormalForm; +import mb.sdf3.task.spec.Sdf3Config; import org.junit.jupiter.api.Test; import org.spoofax.interpreter.terms.IStrategoTerm; @@ -13,9 +15,15 @@ class ToNormalFormTest extends TestBase { @Test void testTask() throws Exception { final TextResource resource = textResource("a.sdf3", "module nested/a context-free syntax A = "); + final Sdf3Config sdf3Config = new Sdf3Config("$", ""); + final String strategyAffix = "lang"; final Sdf3ToNormalForm taskDef = component.getSdf3ToNormalForm(); try(final MixedSession session = newSession()) { - final Result result = session.require(taskDef.createTask(desugarSupplier(resource))); + final Result result = session.require(taskDef.createTask(new Sdf3AstStrategoTransformTaskDef.Input( + desugarSupplier(resource), + sdf3Config, + strategyAffix + ))); assertTrue(result.isOk()); final IStrategoTerm output = result.unwrap(); log.info("{}", output); diff --git a/lwb/metalang/sdf3/sdf3/src/test/java/mb/sdf3/adapter/ToPrettyPrinterTest.java b/lwb/metalang/sdf3/sdf3/src/test/java/mb/sdf3/adapter/ToPrettyPrinterTest.java index dc3fc4740..c5c4200c7 100644 --- a/lwb/metalang/sdf3/sdf3/src/test/java/mb/sdf3/adapter/ToPrettyPrinterTest.java +++ b/lwb/metalang/sdf3/sdf3/src/test/java/mb/sdf3/adapter/ToPrettyPrinterTest.java @@ -3,7 +3,9 @@ import mb.common.result.Result; import mb.pie.api.MixedSession; import mb.resource.text.TextResource; +import mb.sdf3.stratego.Sdf3Context; import mb.sdf3.task.Sdf3ToPrettyPrinter; +import mb.sdf3.task.spec.Sdf3Config; import org.junit.jupiter.api.Test; import org.spoofax.interpreter.terms.IStrategoTerm; @@ -13,9 +15,15 @@ class ToPrettyPrinterTest extends TestBase { @Test void testTask() throws Exception { final TextResource resource = textResource("a.sdf3", "module nested/a context-free syntax A = "); + final Sdf3Config sdf3Config = new Sdf3Config("$", ""); + final String strategyAffix = "lang"; final Sdf3ToPrettyPrinter taskDef = component.getSdf3ToPrettyPrinter(); try(final MixedSession session = newSession()) { - final Result result = session.require(taskDef.createTask(new Sdf3ToPrettyPrinter.Input(desugarSupplier(resource), "lang"))); + final Result result = session.require(taskDef.createTask(new Sdf3ToPrettyPrinter.Input( + desugarSupplier(resource), + sdf3Config, + strategyAffix + ))); assertOk(result); final IStrategoTerm output = result.unwrap(); log.info("{}", output); diff --git a/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/esv/SpoofaxEsvConfigure.java b/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/esv/SpoofaxEsvConfigure.java index 435296608..7f04a15b3 100644 --- a/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/esv/SpoofaxEsvConfigure.java +++ b/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/esv/SpoofaxEsvConfigure.java @@ -29,6 +29,8 @@ import mb.resource.hierarchical.ResourcePath; import mb.resource.hierarchical.match.path.string.PathStringMatcher; import mb.sdf3.task.Sdf3ToCompletionColorer; +import mb.sdf3.task.spec.Sdf3Config; +import mb.sdf3.task.spec.Sdf3SpecConfig; import mb.spoofax.lwb.compiler.sdf3.SpoofaxSdf3ConfigureException; import mb.spoofax.lwb.compiler.sdf3.SpoofaxSdf3GenerationUtil; import org.checkerframework.checker.nullness.qual.Nullable; @@ -190,7 +192,7 @@ public Result configureSourceFiles( try { spoofaxSdf3GenerationUtil.performSdf3GenerationIfEnabled(context, rootDirectory, new SpoofaxSdf3GenerationUtil.Callbacks() { @Override - public void generateFromAst(ExecContext context, STask> astSupplier) { + public void generateFromAst(ExecContext context, STask> astSupplier, Sdf3Config sdf3Config) { includeAstSuppliers.add(sdf3ToCompletionColorer.createSupplier(astSupplier)); } }); diff --git a/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/sdf3/SpoofaxSdf3Check.java b/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/sdf3/SpoofaxSdf3Check.java index 2019cb113..493540d81 100644 --- a/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/sdf3/SpoofaxSdf3Check.java +++ b/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/sdf3/SpoofaxSdf3Check.java @@ -7,6 +7,8 @@ import mb.pie.api.TaskDef; import mb.resource.hierarchical.ResourcePath; import mb.sdf3.task.spec.Sdf3CheckSpec; +import mb.sdf3.task.spec.Sdf3Config; +import mb.sdf3.task.spec.Sdf3SpecConfig; import javax.inject.Inject; @@ -43,7 +45,7 @@ public KeyedMessages exec(ExecContext context, ResourcePath rootDirectory) { public KeyedMessages check(ExecContext context, SpoofaxSdf3Config config) { return config.caseOf() - .files((sdf3SpecConfig, outputParseTableAtermFile, outputParseTablePersistedFile) -> context.require(check, sdf3SpecConfig)) + .files((sdf3SpecConfig, sdf3Config, outputParseTableAtermFile, outputParseTablePersistedFile) -> context.require(check, sdf3SpecConfig)) .prebuilt((inputParseTableAtermFile, inputParseTablePersistedFile, outputParseTableAtermFile, outputParseTablePersistedFile) -> KeyedMessages.of()) ; } diff --git a/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/sdf3/SpoofaxSdf3Compile.java b/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/sdf3/SpoofaxSdf3Compile.java index c1c2d2136..f30f69389 100644 --- a/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/sdf3/SpoofaxSdf3Compile.java +++ b/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/sdf3/SpoofaxSdf3Compile.java @@ -10,6 +10,7 @@ import mb.pie.api.TaskDef; import mb.resource.hierarchical.ResourcePath; import mb.sdf3.task.spec.Sdf3CheckSpec; +import mb.sdf3.task.spec.Sdf3Config; import mb.sdf3.task.spec.Sdf3ParseTableToFile; import mb.sdf3.task.spec.Sdf3SpecConfig; import mb.sdf3.task.spec.Sdf3SpecToParseTable; @@ -71,23 +72,32 @@ public Result exec(ExecContext conte public Result compile(ExecContext context, SpoofaxSdf3Config config) { return config.caseOf() - .files((sdf3SpecConfig, outputParseTableAtermFile, outputParseTablePersistedFile) -> compileFromSourceFiles(context, sdf3SpecConfig, outputParseTableAtermFile, outputParseTablePersistedFile)) + .files((sdf3SpecConfig, sdf3Config, outputParseTableAtermFile, outputParseTablePersistedFile) -> compileFromSourceFiles( + context, + sdf3SpecConfig, + sdf3Config, + "", + outputParseTableAtermFile, + outputParseTablePersistedFile + )) .prebuilt((inputParseTableAtermFile, inputParseTablePersistedFile, outputParseTableAtermFile, outputParseTablePersistedFile) -> copyPrebuilt(context, inputParseTableAtermFile, inputParseTablePersistedFile, outputParseTableAtermFile, outputParseTablePersistedFile)) ; } public Result compileFromSourceFiles( ExecContext context, - Sdf3SpecConfig config, + Sdf3SpecConfig specConfig, + Sdf3Config config, + String strategyAffix, ResourcePath outputParseTableAtermFile, ResourcePath outputParseTablePersistedFile ) { - final KeyedMessages messages = context.require(check, config); + final KeyedMessages messages = context.require(check, specConfig); if(messages.containsError()) { return Result.ofErr(SpoofaxSdf3CompileException.checkFail(messages)); } - final Supplier> parseTableSupplier = toParseTable.createSupplier(new Sdf3SpecToParseTable.Input(config, false)); + final Supplier> parseTableSupplier = toParseTable.createSupplier(new Sdf3SpecToParseTable.Input(specConfig, config, strategyAffix, false)); final Result compileResult = context.require(parseTableToFile, new Sdf3ParseTableToFile.Input( parseTableSupplier, outputParseTableAtermFile, diff --git a/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/sdf3/SpoofaxSdf3Config.java b/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/sdf3/SpoofaxSdf3Config.java index f8a4cd8e6..6f80bd8fe 100644 --- a/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/sdf3/SpoofaxSdf3Config.java +++ b/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/sdf3/SpoofaxSdf3Config.java @@ -3,6 +3,7 @@ import mb.common.option.Option; import mb.common.util.ADT; import mb.resource.hierarchical.ResourcePath; +import mb.sdf3.task.spec.Sdf3Config; import mb.sdf3.task.spec.Sdf3SpecConfig; import org.checkerframework.checker.nullness.qual.Nullable; @@ -14,17 +15,47 @@ @ADT public abstract class SpoofaxSdf3Config implements Serializable { public interface Cases { - R files(Sdf3SpecConfig sdf3SpecConfig, ResourcePath outputParseTableAtermFile, ResourcePath outputParseTablePersistedFile); - - R prebuilt(ResourcePath inputParseTableAtermFile, ResourcePath inputParseTablePersistedFile, ResourcePath outputParseTableAtermFile, ResourcePath outputParseTablePersistedFile); + R files( + Sdf3SpecConfig sdf3SpecConfig, + Sdf3Config sdf3Config, + ResourcePath outputParseTableAtermFile, + ResourcePath outputParseTablePersistedFile + ); + + R prebuilt( + ResourcePath inputParseTableAtermFile, + ResourcePath inputParseTablePersistedFile, + ResourcePath outputParseTableAtermFile, + ResourcePath outputParseTablePersistedFile + ); } - public static SpoofaxSdf3Config files(Sdf3SpecConfig sdf3SpecConfig, ResourcePath outputParseTableAtermFile, ResourcePath outputParseTablePersistedFile) { - return SpoofaxSdf3Configs.files(sdf3SpecConfig, outputParseTableAtermFile, outputParseTablePersistedFile); + public static SpoofaxSdf3Config files( + Sdf3SpecConfig sdf3SpecConfig, + Sdf3Config sdf3Config, + ResourcePath outputParseTableAtermFile, + ResourcePath outputParseTablePersistedFile + ) { + return SpoofaxSdf3Configs.files( + sdf3SpecConfig, + sdf3Config, + outputParseTableAtermFile, + outputParseTablePersistedFile + ); } - public static SpoofaxSdf3Config prebuilt(ResourcePath inputParseTableAtermFile, ResourcePath inputParseTablePersistedFile, ResourcePath outputParseTableAtermFile, ResourcePath outputParseTablePersistedFile) { - return SpoofaxSdf3Configs.prebuilt(inputParseTableAtermFile, inputParseTablePersistedFile, outputParseTableAtermFile, outputParseTablePersistedFile); + public static SpoofaxSdf3Config prebuilt( + ResourcePath inputParseTableAtermFile, + ResourcePath inputParseTablePersistedFile, + ResourcePath outputParseTableAtermFile, + ResourcePath outputParseTablePersistedFile + ) { + return SpoofaxSdf3Configs.prebuilt( + inputParseTableAtermFile, + inputParseTablePersistedFile, + outputParseTableAtermFile, + outputParseTablePersistedFile + ); } @@ -43,6 +74,9 @@ public Option getSdf3SpecConfig() { return Option.ofOptional(SpoofaxSdf3Configs.getSdf3SpecConfig(this)); } + public Option getSdf3Config() { + return Option.ofOptional(SpoofaxSdf3Configs.getSdf3Config(this)); + } @Override public abstract int hashCode(); diff --git a/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/sdf3/SpoofaxSdf3Configure.java b/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/sdf3/SpoofaxSdf3Configure.java index ce476f93a..6d78bbfb4 100644 --- a/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/sdf3/SpoofaxSdf3Configure.java +++ b/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/sdf3/SpoofaxSdf3Configure.java @@ -14,6 +14,7 @@ import mb.pie.api.stamp.resource.ResourceStampers; import mb.resource.hierarchical.HierarchicalResource; import mb.resource.hierarchical.ResourcePath; +import mb.sdf3.task.spec.Sdf3Config; import mb.sdf3.task.spec.Sdf3SpecConfig; import org.metaborg.sdf2table.parsetable.ParseTableConfiguration; @@ -99,8 +100,22 @@ public Result configureSourceF cfgSdf3Config.checkPrioritiesInParseTable(), cfgSdf3Config.createLayoutSensitiveParseTable() ); - final Sdf3SpecConfig sdf3SpecConfig = new Sdf3SpecConfig(rootDirectory, mainSourceDirectory.getPath(), mainFile.getPath(), parseTableConfiguration); - return Result.ofOk(SpoofaxSdf3Config.files(sdf3SpecConfig, cfgSdf3Config.parseTableAtermOutputFile(), cfgSdf3Config.parseTablePersistedOutputFile())); + final Sdf3SpecConfig sdf3SpecConfig = new Sdf3SpecConfig( + rootDirectory, + mainSourceDirectory.getPath(), + mainFile.getPath(), + parseTableConfiguration + ); + final Sdf3Config sdf3Config = new Sdf3Config( + cfgSdf3Config.sdf3PlaceholderPrefix(), + cfgSdf3Config.sdf3PlaceholderSuffix() + ); + return Result.ofOk(SpoofaxSdf3Config.files( + sdf3SpecConfig, + sdf3Config, + cfgSdf3Config.parseTableAtermOutputFile(), + cfgSdf3Config.parseTablePersistedOutputFile()) + ); } public Result configurePrebuilt( diff --git a/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/sdf3/SpoofaxSdf3GenerationUtil.java b/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/sdf3/SpoofaxSdf3GenerationUtil.java index 39a394fa5..b23c3861b 100644 --- a/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/sdf3/SpoofaxSdf3GenerationUtil.java +++ b/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/sdf3/SpoofaxSdf3GenerationUtil.java @@ -12,6 +12,7 @@ import mb.sdf3.task.Sdf3Desugar; import mb.sdf3.task.Sdf3GetSourceFiles; import mb.sdf3.task.Sdf3Parse; +import mb.sdf3.task.spec.Sdf3Config; import mb.sdf3.task.spec.Sdf3SpecConfig; import org.spoofax.interpreter.terms.IStrategoTerm; @@ -44,11 +45,11 @@ public class SpoofaxSdf3GenerationUtil { public interface Callbacks { - default void generateFromAst(ExecContext context, STask> astSupplier) throws E, IOException, InterruptedException {} + default void generateFromAst(ExecContext context, STask> astSupplier, Sdf3Config sdf3Config) throws E, IOException, InterruptedException {} default void generateFromAnalyzed(ExecContext context, Supplier> singleFileAnalysisOutputSupplier) throws E, IOException, InterruptedException {} - default void generateFromConfig(ExecContext context, Sdf3SpecConfig sdf3Config) throws E, IOException, InterruptedException {} + default void generateFromConfig(ExecContext context, Sdf3SpecConfig sdf3SpecConfig, Sdf3Config sdf3Config) throws E, IOException, InterruptedException {} } public void performSdf3GenerationIfEnabled( @@ -63,16 +64,17 @@ public void performSdf3GenerationIfEnabled( if(!spoofaxSdf3Config.getSdf3SpecConfig().isSome()) { return; // Only generate when there are SDF3 source files (not prebuilt). } - final Sdf3SpecConfig config = spoofaxSdf3Config.getSdf3SpecConfig().unwrap(); + final Sdf3SpecConfig specConfig = spoofaxSdf3Config.getSdf3SpecConfig().unwrap(); + final Sdf3Config config = spoofaxSdf3Config.getSdf3Config().unwrap(); final JsglrParseTaskInput.Builder parseInputBuilder = parse.inputBuilder().rootDirectoryHint(rootDirectory); - final Sdf3AnalyzeMulti.Input analyzeInput = new Sdf3AnalyzeMulti.Input(config.rootDirectory, parse.createRecoverableMultiAstSupplierFunction(getSourceFiles.createFunction())); + final Sdf3AnalyzeMulti.Input analyzeInput = new Sdf3AnalyzeMulti.Input(specConfig.rootDirectory, parse.createRecoverableMultiAstSupplierFunction(getSourceFiles.createFunction())); for(ResourcePath file : context.require(getSourceFiles, rootDirectory)) { final Supplier> singleFileAnalysisOutputSupplier = analyze.createSingleFileOutputSupplier(analyzeInput, file); callbacks.generateFromAnalyzed(context, singleFileAnalysisOutputSupplier); final STask> astSupplier = desugar.createSupplier(parseInputBuilder.withFile(file).buildAstSupplier()); - callbacks.generateFromAst(context, astSupplier); + callbacks.generateFromAst(context, astSupplier, config); } - callbacks.generateFromConfig(context, config); + callbacks.generateFromConfig(context, specConfig, config); } } } diff --git a/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/statix/SpoofaxStatixConfigure.java b/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/statix/SpoofaxStatixConfigure.java index 33dfc70d6..1954a58a3 100644 --- a/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/statix/SpoofaxStatixConfigure.java +++ b/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/statix/SpoofaxStatixConfigure.java @@ -17,6 +17,7 @@ import mb.pie.api.stamp.resource.ResourceStampers; import mb.resource.hierarchical.HierarchicalResource; import mb.resource.hierarchical.ResourcePath; +import mb.sdf3.task.spec.Sdf3Config; import mb.sdf3.task.spec.Sdf3SpecConfig; import mb.sdf3_ext_statix.task.Sdf3ExtStatixGenerateStatix; import mb.spoofax.lwb.compiler.sdf3.SpoofaxSdf3ConfigureException; @@ -129,7 +130,7 @@ public Result configureSou try { spoofaxSdf3GenerationUtil.performSdf3GenerationIfEnabled(context, rootDirectory, new SpoofaxSdf3GenerationUtil.Callbacks() { @Override - public void generateFromAst(ExecContext context, STask> astSupplier) throws SpoofaxStatixConfigureException, InterruptedException { + public void generateFromAst(ExecContext context, STask> astSupplier, Sdf3Config sdf3Config) throws SpoofaxStatixConfigureException, InterruptedException { try { sdf3ToStatixGenInj(context, generatedSourcesDirectory, astSupplier); } catch(RuntimeException | InterruptedException e) { @@ -140,7 +141,7 @@ public void generateFromAst(ExecContext context, STask> } @Override - public void generateFromConfig(ExecContext context, Sdf3SpecConfig sdf3Config) { + public void generateFromConfig(ExecContext context, Sdf3SpecConfig sdf3SpecConfig, Sdf3Config sdf3Config) { // Add generated sources directory as an include Statix imports. includeDirectories.add(generatedSourcesDirectory); // Add this as an origin, as this task provides the Statix files (in statixGenerationUtil.writePrettyPrintedFile). diff --git a/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/stratego/SpoofaxStrategoConfigure.java b/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/stratego/SpoofaxStrategoConfigure.java index 7b1348465..988f12d7e 100644 --- a/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/stratego/SpoofaxStrategoConfigure.java +++ b/lwb/spoofax.lwb.compiler/src/main/java/mb/spoofax/lwb/compiler/stratego/SpoofaxStrategoConfigure.java @@ -1,5 +1,6 @@ package mb.spoofax.lwb.compiler.stratego; +import mb.cfg.metalang.CfgSdf3Config; import mb.cfg.metalang.CfgStrategoConfig; import mb.cfg.metalang.CfgStrategoSource; import mb.cfg.task.CfgRootDirectoryToObject; @@ -30,9 +31,11 @@ import mb.resource.hierarchical.HierarchicalResource; import mb.resource.hierarchical.ResourcePath; import mb.resource.hierarchical.match.path.string.PathStringMatcher; +import mb.sdf3.stratego.Sdf3Context; import mb.sdf3.task.Sdf3ToCompletionRuntime; import mb.sdf3.task.Sdf3ToPrettyPrinter; import mb.sdf3.task.Sdf3ToSignature; +import mb.sdf3.task.spec.Sdf3Config; import mb.sdf3.task.spec.Sdf3ParseTableToParenthesizer; import mb.sdf3.task.spec.Sdf3SpecConfig; import mb.sdf3.task.spec.Sdf3SpecToParseTable; @@ -242,9 +245,9 @@ public Result toStrate try { spoofaxSdf3GenerationUtil.performSdf3GenerationIfEnabled(context, rootDirectory, new SpoofaxSdf3GenerationUtil.Callbacks() { @Override - public void generateFromAst(ExecContext context, STask> astSupplier) throws SpoofaxStrategoConfigureException, InterruptedException { + public void generateFromAst(ExecContext context, STask> astSupplier, Sdf3Config sdf3Config) throws SpoofaxStrategoConfigureException, InterruptedException { try { - sdf3ToPrettyPrinter(context, strategyAffix, generatedSourcesDirectory, astSupplier); + sdf3ToPrettyPrinter(context, strategyAffix, sdf3Config, generatedSourcesDirectory, astSupplier); } catch(RuntimeException | InterruptedException e) { throw e; // Do not wrap runtime and interrupted exceptions, rethrow them. } catch(Exception e) { @@ -281,11 +284,16 @@ public void generateFromAnalyzed(ExecContext context, Supplier> parseTableSupplier = sdf3ToParseTable.createSupplier(new Sdf3SpecToParseTable.Input(sdf3Config, false)); - sdf3ToParenthesizer(context, strategyAffix, generatedSourcesDirectory, parseTableSupplier); + final STask> parseTableSupplier = sdf3ToParseTable.createSupplier(new Sdf3SpecToParseTable.Input( + sdf3SpecConfig, + sdf3Config, + strategyAffix, + false + )); + sdf3ToParenthesizer(context, strategyAffix, sdf3Config, generatedSourcesDirectory, parseTableSupplier); } catch(RuntimeException | InterruptedException e) { throw e; // Do not wrap runtime and interrupted exceptions, rethrow them. } catch(Exception e) { @@ -296,7 +304,7 @@ public void generateFromConfig(ExecContext context, Sdf3SpecConfig sdf3Config) t final HashMap map = new HashMap<>(); map.put("name", strategyAffix); map.put("ppName", strategyAffix); - map.put("sdf3MainModule", sdf3Config.getMainModuleName()); + map.put("sdf3MainModule", sdf3SpecConfig.getMainModuleName()); completionTemplate.write(context, generatedSourcesDirectory.appendRelativePath("completion.str2"), cfgStrategoConfig, map); ppTemplate.write(context, generatedSourcesDirectory.appendRelativePath("pp.str2"), cfgStrategoConfig, map); } @@ -351,16 +359,18 @@ private void sdf3ToSignature( private void sdf3ToPrettyPrinter( ExecContext context, String strategyAffix, + Sdf3Config sdf3Config, ResourcePath generatesSourcesDirectory, STask> astSupplier ) throws Exception { - final STask> supplier = sdf3ToPrettyPrinter.createSupplier(new Sdf3ToPrettyPrinter.Input(astSupplier, strategyAffix)); + final STask> supplier = sdf3ToPrettyPrinter.createSupplier(new Sdf3ToPrettyPrinter.Input(astSupplier, sdf3Config, strategyAffix)); spoofaxStrategoGenerationUtil.writePrettyPrintedFile(context, generatesSourcesDirectory, supplier); } private void sdf3ToParenthesizer( ExecContext context, String strategyAffix, + Sdf3Config sdf3Config, ResourcePath generatesSourcesDirectory, STask> parseTableSupplier ) throws Exception {