diff --git a/haskell-backend/src/main/java/org/kframework/backend/haskell/HaskellBackend.java b/haskell-backend/src/main/java/org/kframework/backend/haskell/HaskellBackend.java index 610d83eef8f..9d4da4f131d 100644 --- a/haskell-backend/src/main/java/org/kframework/backend/haskell/HaskellBackend.java +++ b/haskell-backend/src/main/java/org/kframework/backend/haskell/HaskellBackend.java @@ -2,6 +2,12 @@ package org.kframework.backend.haskell; import com.google.inject.Inject; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import org.kframework.attributes.Att; import org.kframework.backend.kore.KoreBackend; import org.kframework.compile.Backend; @@ -9,76 +15,70 @@ import org.kframework.main.GlobalOptions; import org.kframework.main.Tool; import org.kframework.utils.Stopwatch; -import org.kframework.utils.errorsystem.KExceptionManager; import org.kframework.utils.errorsystem.KEMException; +import org.kframework.utils.errorsystem.KExceptionManager; import org.kframework.utils.file.FileUtil; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Set; -import java.util.HashSet; - public class HaskellBackend extends KoreBackend { - private final KompileOptions kompileOptions; - private final GlobalOptions globalOptions; - private final FileUtil files; - private final HaskellKompileOptions haskellKompileOptions; - - @Inject - public HaskellBackend( - KompileOptions kompileOptions, - GlobalOptions globalOptions, - HaskellKompileOptions haskellKompileOptions, - FileUtil files, - KExceptionManager kem, - Tool tool) { - super(kompileOptions, files, kem, false, tool); - this.files = files; - this.haskellKompileOptions = haskellKompileOptions; - this.kompileOptions = kompileOptions; - this.globalOptions = globalOptions; - } + private final KompileOptions kompileOptions; + private final GlobalOptions globalOptions; + private final FileUtil files; + private final HaskellKompileOptions haskellKompileOptions; + @Inject + public HaskellBackend( + KompileOptions kompileOptions, + GlobalOptions globalOptions, + HaskellKompileOptions haskellKompileOptions, + FileUtil files, + KExceptionManager kem, + Tool tool) { + super(kompileOptions, files, kem, false, tool); + this.files = files; + this.haskellKompileOptions = haskellKompileOptions; + this.kompileOptions = kompileOptions; + this.globalOptions = globalOptions; + } - @Override - public void accept(Backend.Holder h) { - Stopwatch sw = new Stopwatch(globalOptions); - String kore = getKompiledString(h.def, true); - h.def = null; - files.saveToKompiled("definition.kore", kore); - sw.printIntermediate(" Print definition.kore"); - ProcessBuilder pb = files.getProcessBuilder(); - List args = new ArrayList<>(); - if (haskellKompileOptions.noHaskellBinary) { - args.add("kore-parser"); - args.add("--no-print-definition"); - args.add("definition.kore"); - } else { - args.add(haskellKompileOptions.haskellBackendCommand); - args.add("definition.kore"); - args.add("--module"); - args.add(kompileOptions.mainModule(files)); - args.add("--output"); - args.add("haskellDefinition.bin"); - args.add("--serialize"); - } - try { - Process p = pb.command(args).directory(files.resolveKompiled(".")).inheritIO().start(); - int exit = p.waitFor(); - if (exit != 0) { - throw KEMException.criticalError("Haskell backend reported errors validating compiled definition.\nExamine output to see errors."); - } - } catch (IOException | InterruptedException e) { - throw KEMException.criticalError("Error with I/O while executing kore-parser", e); - } - sw.printIntermediate(" Validate def"); + @Override + public void accept(Backend.Holder h) { + Stopwatch sw = new Stopwatch(globalOptions); + String kore = getKompiledString(h.def, true); + h.def = null; + files.saveToKompiled("definition.kore", kore); + sw.printIntermediate(" Print definition.kore"); + ProcessBuilder pb = files.getProcessBuilder(); + List args = new ArrayList<>(); + if (haskellKompileOptions.noHaskellBinary) { + args.add("kore-parser"); + args.add("--no-print-definition"); + args.add("definition.kore"); + } else { + args.add(haskellKompileOptions.haskellBackendCommand); + args.add("definition.kore"); + args.add("--module"); + args.add(kompileOptions.mainModule(files)); + args.add("--output"); + args.add("haskellDefinition.bin"); + args.add("--serialize"); } - - @Override - public Set excludedModuleTags() { - return new HashSet<>(Arrays.asList(Att.CONCRETE(), Att.KAST())); + try { + Process p = pb.command(args).directory(files.resolveKompiled(".")).inheritIO().start(); + int exit = p.waitFor(); + if (exit != 0) { + throw KEMException.criticalError( + "Haskell backend reported errors validating compiled definition.\n" + + "Examine output to see errors."); + } + } catch (IOException | InterruptedException e) { + throw KEMException.criticalError("Error with I/O while executing kore-parser", e); } + sw.printIntermediate(" Validate def"); + } + + @Override + public Set excludedModuleTags() { + return new HashSet<>(Arrays.asList(Att.CONCRETE(), Att.KAST())); + } } diff --git a/haskell-backend/src/main/java/org/kframework/backend/haskell/HaskellBackendKModule.java b/haskell-backend/src/main/java/org/kframework/backend/haskell/HaskellBackendKModule.java index d86230dbafa..7eda4dc7a61 100644 --- a/haskell-backend/src/main/java/org/kframework/backend/haskell/HaskellBackendKModule.java +++ b/haskell-backend/src/main/java/org/kframework/backend/haskell/HaskellBackendKModule.java @@ -6,80 +6,81 @@ import com.google.inject.Module; import com.google.inject.TypeLiteral; import com.google.inject.multibindings.MapBinder; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; import org.apache.commons.lang3.tuple.Pair; import org.kframework.definition.Definition; import org.kframework.main.AbstractKModule; import org.kframework.rewriter.Rewriter; -import java.util.Collections; -import java.util.List; -import java.util.function.Function; - -/** - * Created by traiansf on 9/13/18. - */ +/** Created by traiansf on 9/13/18. */ public class HaskellBackendKModule extends AbstractKModule { - @Override - public List getKompileModules() { - List mods = super.getKompileModules(); - mods.add(new AbstractModule() { - @Override - protected void configure() { - binder().requireAtInjectOnConstructors(); - bindOptions(HaskellBackendKModule.this::kompileOptions, binder()); - installHaskellBackend(binder()); - } + @Override + public List getKompileModules() { + List mods = super.getKompileModules(); + mods.add( + new AbstractModule() { + @Override + protected void configure() { + binder().requireAtInjectOnConstructors(); + bindOptions(HaskellBackendKModule.this::kompileOptions, binder()); + installHaskellBackend(binder()); + } }); - return mods; - } + return mods; + } - private void installHaskellBackend(Binder binder) { - MapBinder mapBinder = MapBinder.newMapBinder( - binder, String.class, org.kframework.compile.Backend.class); - mapBinder.addBinding("haskell").to(HaskellBackend.class); - } + private void installHaskellBackend(Binder binder) { + MapBinder mapBinder = + MapBinder.newMapBinder(binder, String.class, org.kframework.compile.Backend.class); + mapBinder.addBinding("haskell").to(HaskellBackend.class); + } - @Override - public List, Boolean>> krunOptions() { - return Collections.singletonList(Pair.of(HaskellKRunOptions.class, true)); - } + @Override + public List, Boolean>> krunOptions() { + return Collections.singletonList(Pair.of(HaskellKRunOptions.class, true)); + } - @Override - public List, Boolean>> kompileOptions() { - return Collections.singletonList(Pair.of(HaskellKompileOptions.class, true)); - } + @Override + public List, Boolean>> kompileOptions() { + return Collections.singletonList(Pair.of(HaskellKompileOptions.class, true)); + } - @Override - public List getKRunModules() { - return Collections.singletonList(new AbstractModule() { - @Override - protected void configure() { - binder().requireAtInjectOnConstructors(); - installHaskellRewriter(binder()); - } + @Override + public List getKRunModules() { + return Collections.singletonList( + new AbstractModule() { + @Override + protected void configure() { + binder().requireAtInjectOnConstructors(); + installHaskellRewriter(binder()); + } }); - } - - private void installHaskellRewriter(Binder binder) { - bindOptions(HaskellBackendKModule.this::krunOptions, binder); + } - MapBinder> rewriterBinder = MapBinder.newMapBinder( - binder, TypeLiteral.get(String.class), new TypeLiteral>() { - }); - rewriterBinder.addBinding("haskell").to(HaskellRewriter.class); - } + private void installHaskellRewriter(Binder binder) { + bindOptions(HaskellBackendKModule.this::krunOptions, binder); + MapBinder> rewriterBinder = + MapBinder.newMapBinder( + binder, + TypeLiteral.get(String.class), + new TypeLiteral>() {}); + rewriterBinder.addBinding("haskell").to(HaskellRewriter.class); + } - @Override - public List getKProveModules() { - return Collections.singletonList(new AbstractModule() { - @Override - protected void configure() { - binder().requireAtInjectOnConstructors(); - installHaskellBackend(binder()); - installHaskellRewriter(binder()); - } + @Override + public List getKProveModules() { + return Collections.singletonList( + new AbstractModule() { + @Override + protected void configure() { + binder().requireAtInjectOnConstructors(); + installHaskellBackend(binder()); + installHaskellRewriter(binder()); + } }); - } + } } diff --git a/haskell-backend/src/main/java/org/kframework/backend/haskell/HaskellKRunOptions.java b/haskell-backend/src/main/java/org/kframework/backend/haskell/HaskellKRunOptions.java index 3a6649ef34c..aff63eface2 100644 --- a/haskell-backend/src/main/java/org/kframework/backend/haskell/HaskellKRunOptions.java +++ b/haskell-backend/src/main/java/org/kframework/backend/haskell/HaskellKRunOptions.java @@ -9,31 +9,39 @@ @RequestScoped public class HaskellKRunOptions { - @Inject - public HaskellKRunOptions() {} - - @Parameter(names="--haskell-backend-command", descriptionKey = "command", - description="Command to run the Haskell backend execution engine.", hidden = true) - public String haskellBackendCommand = "kore-exec"; - - @Parameter(names="--haskell-backend-home", descriptionKey = "directory", - description="Directory where the Haskell backend source installation resides.", hidden = true) - public String haskellBackendHome = System.getenv("KORE_HOME"); - - @Parameter(names="--default-claim-type", descriptionKey = "type", converter = SentenceTypeConverter.class, - description="Default type for claims. Values: [all-path|one-path].") - public ModuleToKORE.SentenceType defaultClaimType = ModuleToKORE.SentenceType.ALL_PATH; - - public static class SentenceTypeConverter extends BaseEnumConverter { - - public SentenceTypeConverter(String optionName) { - super(optionName); - } - - @Override - public Class enumClass() { - return ModuleToKORE.SentenceType.class; - } + @Inject + public HaskellKRunOptions() {} + + @Parameter( + names = "--haskell-backend-command", + descriptionKey = "command", + description = "Command to run the Haskell backend execution engine.", + hidden = true) + public String haskellBackendCommand = "kore-exec"; + + @Parameter( + names = "--haskell-backend-home", + descriptionKey = "directory", + description = "Directory where the Haskell backend source installation resides.", + hidden = true) + public String haskellBackendHome = System.getenv("KORE_HOME"); + + @Parameter( + names = "--default-claim-type", + descriptionKey = "type", + converter = SentenceTypeConverter.class, + description = "Default type for claims. Values: [all-path|one-path].") + public ModuleToKORE.SentenceType defaultClaimType = ModuleToKORE.SentenceType.ALL_PATH; + + public static class SentenceTypeConverter extends BaseEnumConverter { + + public SentenceTypeConverter(String optionName) { + super(optionName); } + @Override + public Class enumClass() { + return ModuleToKORE.SentenceType.class; + } + } } diff --git a/haskell-backend/src/main/java/org/kframework/backend/haskell/HaskellKompileOptions.java b/haskell-backend/src/main/java/org/kframework/backend/haskell/HaskellKompileOptions.java index 081be372136..fba09ea18bb 100644 --- a/haskell-backend/src/main/java/org/kframework/backend/haskell/HaskellKompileOptions.java +++ b/haskell-backend/src/main/java/org/kframework/backend/haskell/HaskellKompileOptions.java @@ -8,11 +8,21 @@ @RequestScoped public class HaskellKompileOptions { - @Inject - public HaskellKompileOptions() {} - @Parameter(names="--haskell-backend-command", description="Command to run the Haskell backend execution engine.", descriptionKey = "command", hidden = true) - public String haskellBackendCommand = "kore-exec"; + @Inject + public HaskellKompileOptions() {} - @Parameter(names="--no-haskell-binary", description="Use the textual KORE format in the haskell backend instead of the binary KORE format. This is a development option, but may be necessary on MacOS due to known issues with the binary format.") - public boolean noHaskellBinary = false; + @Parameter( + names = "--haskell-backend-command", + description = "Command to run the Haskell backend execution engine.", + descriptionKey = "command", + hidden = true) + public String haskellBackendCommand = "kore-exec"; + + @Parameter( + names = "--no-haskell-binary", + description = + "Use the textual KORE format in the haskell backend instead of the binary KORE format." + + " This is a development option, but may be necessary on MacOS due to known issues" + + " with the binary format.") + public boolean noHaskellBinary = false; } diff --git a/haskell-backend/src/main/java/org/kframework/backend/haskell/HaskellRewriter.java b/haskell-backend/src/main/java/org/kframework/backend/haskell/HaskellRewriter.java index 0c5c7e4a435..43760f0ec54 100644 --- a/haskell-backend/src/main/java/org/kframework/backend/haskell/HaskellRewriter.java +++ b/haskell-backend/src/main/java/org/kframework/backend/haskell/HaskellRewriter.java @@ -1,7 +1,19 @@ // Copyright (c) Runtime Verification, Inc. All Rights Reserved. package org.kframework.backend.haskell; +import static org.kframework.builtin.BooleanUtils.*; + import com.google.inject.Inject; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import org.kframework.RewriterResult; import org.kframework.attributes.Att; import org.kframework.backend.kore.KoreBackend; import org.kframework.backend.kore.ModuleToKORE; @@ -26,7 +38,6 @@ import org.kframework.main.Tool; import org.kframework.parser.KoreParser; import org.kframework.parser.kore.parser.ParseError; -import org.kframework.RewriterResult; import org.kframework.rewriter.Rewriter; import org.kframework.rewriter.SearchType; import org.kframework.unparser.KPrint; @@ -39,419 +50,460 @@ import org.kframework.utils.inject.RequestScoped; import org.kframework.utils.options.BackendOptions; import org.kframework.utils.options.SMTOptions; - import scala.Tuple2; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; - -import static org.kframework.builtin.BooleanUtils.*; - @RequestScoped public record HaskellRewriter( - GlobalOptions globalOptions, - SMTOptions smtOptions, - KompileOptions kompileOptions, - KProveOptions kProveOptions, - HaskellKRunOptions haskellKRunOptions, - BackendOptions backendOptions, - FileUtil files, - CompiledDefinition def, - KExceptionManager kem, - KPrint kprint, - Tool tool -) implements Function { - - @Inject - public HaskellRewriter {} - - @Override - public Rewriter apply(Definition definition) { - Module module = definition.mainModule(); - return new Rewriter() { - @Override - public RewriterResult execute(K k, Optional depth) { - Module mod = getExecutionModule(module); - ModuleToKORE converter = new ModuleToKORE(mod, def.topCellInitializer, kompileOptions); - String koreOutput = getKoreString(k, mod, converter); - String defPath = files.resolveKompiled("haskellDefinition.bin").exists() ? - files.resolveKompiled("haskellDefinition.bin").getAbsolutePath() : - files.resolveKompiled("definition.kore").getAbsolutePath(); - String moduleName = mod.name(); - - files.saveToTemp("execute-initial.kore", koreOutput); - String pgmPath = files.resolveTemp("execute-initial.kore").getAbsolutePath(); - String[] koreCommand = haskellKRunOptions.haskellBackendCommand.split("\\s+"); - String koreDirectory = haskellKRunOptions.haskellBackendHome; - File koreOutputFile = files.resolveTemp("execute-result.kore"); - List args = new ArrayList(); - args.addAll(Arrays.asList(koreCommand)); - args.addAll(Arrays.asList( - defPath, - "--module", moduleName, - "--pattern", pgmPath, - "--output", koreOutputFile.getAbsolutePath())); - if (depth.isPresent()) { - args.add("--depth"); - args.add(Integer.toString(depth.get())); - } - if (smtOptions.smtPrelude != null) { - args.add("--smt-prelude"); - args.add(smtOptions.smtPrelude); - } - if (smtOptions.smtTimeout != null) { - args.add("--smt-timeout"); - args.add(Integer.toString(smtOptions.smtTimeout)); - } - koreCommand = args.toArray(koreCommand); - if (backendOptions.dryRun) { - System.out.println(String.join(" ", koreCommand)); - kprint.options.output = OutputModes.NONE; - return new RewriterResult(Optional.empty(), Optional.empty(), k); - } - try { - File korePath = koreDirectory == null ? null : new File(koreDirectory); - int execStatus = executeCommandBasic(korePath, koreCommand); - checkOutput(koreOutputFile, execStatus); - K outputK = new KoreParser(mod.sortAttributesFor()).parseFile(koreOutputFile); - return new RewriterResult(Optional.empty(), Optional.of(execStatus), outputK); - } catch (IOException e) { - throw KEMException.criticalError("I/O Error while executing", e); - } catch (InterruptedException e) { - throw KEMException.criticalError("Interrupted while executing", e); - } catch (ParseError parseError) { - throw KEMException.criticalError("Error parsing haskell backend output", parseError); - } - } - - @Override - public K match(K k, Rule rule) { - return search(k, Optional.of(0), Optional.empty(), rule, SearchType.STAR); - } - - @Override - public Tuple2 executeAndMatch(K k, Optional depth, Rule rule) { - RewriterResult res = execute(k, depth); - return Tuple2.apply(res, match(res.k(), rule)); - } - - @Override - public K search(K initialConfiguration, Optional depth, Optional bound, Rule pattern, SearchType searchType) { - Module mod = getExecutionModule(module); - String koreOutput = getKoreString(initialConfiguration, mod, new ModuleToKORE(mod, def.topCellInitializer, kompileOptions)); - Sort initializerSort = mod.productionsFor().get(def.topCellInitializer).get().head().sort(); - K patternTerm = RewriteToTop.toLeft(pattern.body()); - if (patternTerm instanceof KVariable) { - patternTerm = KORE.KVariable(((KVariable) patternTerm).name(), Att.empty().add(Sort.class, initializerSort)); - } - K patternCondition = pattern.requires(); - String patternTermKore = getKoreString(patternTerm, mod, new ModuleToKORE(mod, def.topCellInitializer, kompileOptions)); - String patternConditionKore; - if (patternCondition.equals(TRUE)) { - patternConditionKore = "\\top{Sort" + initializerSort.name() + "{}}()"; - } else { - patternConditionKore = - "\\equals{SortBool{},Sort" + initializerSort.name() + "{}}(" - + getKoreString(patternCondition, mod, new ModuleToKORE(mod, def.topCellInitializer, kompileOptions)) - + ", \\dv{SortBool{}}(\"true\")" - + ")"; - } - String korePatternOutput = "\\and{Sort" + initializerSort.name() + "{}}(" - + patternTermKore - + ", " + patternConditionKore - + ")"; - String defPath = files.resolveKompiled("haskellDefinition.bin").exists() ? - files.resolveKompiled("haskellDefinition.bin").getAbsolutePath() : - files.resolveKompiled("definition.kore").getAbsolutePath(); - String moduleName = mod.name(); - - files.saveToTemp("search-initial.kore", koreOutput); - String pgmPath = files.resolveTemp("search-initial.kore").getAbsolutePath(); - files.saveToTemp("search-pattern.kore", korePatternOutput); - String patternPath = files.resolveTemp("search-pattern.kore").getAbsolutePath(); - String[] koreCommand = haskellKRunOptions.haskellBackendCommand.split("\\s+"); - String koreDirectory = haskellKRunOptions.haskellBackendHome; - File koreOutputFile = files.resolveTemp("search-result.kore"); - List args = new ArrayList(); - args.addAll(Arrays.asList(koreCommand)); - args.addAll(Arrays.asList( - defPath, - "--module", moduleName, - "--pattern", pgmPath, - "--output", koreOutputFile.getAbsolutePath(), - "--searchType", searchType.toString(), - "--search", patternPath - ) - - ); - if (depth.isPresent()) { - args.add("--depth"); - args.add(depth.get().toString()); - } - if (bound.isPresent()) { - args.add("--bound"); - args.add(bound.get().toString()); - } - if (smtOptions.smtPrelude != null) { - args.add("--smt-prelude"); - args.add(smtOptions.smtPrelude); - } - if (smtOptions.smtTimeout != null) { - args.add("--smt-timeout"); - args.add(Integer.toString(smtOptions.smtTimeout)); - } - koreCommand = args.toArray(koreCommand); - if (backendOptions.dryRun) { - System.out.println(String.join(" ", koreCommand)); - kprint.options.output = OutputModes.NONE; - return initialConfiguration; - } - try { - File korePath = koreDirectory == null ? null : new File(koreDirectory); - if (executeCommandBasic(korePath, koreCommand) != 0) { - throw KEMException.criticalError("Haskell backend returned non-zero exit code"); - } - K outputK = new KoreParser(mod.sortAttributesFor()).parseFile(koreOutputFile); - outputK = addAnonymousAttributes(outputK, pattern); - return outputK; - } catch (IOException e) { - throw KEMException.criticalError("I/O Error while executing", e); - } catch (InterruptedException e) { - throw KEMException.criticalError("Interrupted while executing", e); - } catch (ParseError parseError) { - throw KEMException.criticalError("Error parsing haskell backend output", parseError); - } - } - - private K addAnonymousAttributes(K input, Rule pattern) { - Map anonVars = new HashMap<>(); - VisitK visitor = new VisitK() { - @Override - public void apply(KVariable var) { - anonVars.put(var, var); - } - }; - visitor.apply(pattern.body()); - visitor.apply(pattern.requires()); - visitor.apply(pattern.ensures()); - return new TransformK() { - @Override - public K apply(KVariable var) { - return anonVars.getOrDefault(var, var); - } - }.apply(input); - } - - private Module getExecutionModule(Module module) { - Module mod = def.executionModule(); - if (!module.equals(mod)) { - throw KEMException.criticalError("Invalid module specified for rewriting. Haskell backend only supports rewriting over" + - " the definition's main module."); - } - return mod; - } - - - private String saveKoreDefinitionToTemp(ModuleToKORE converter) { - String kompiledString = KoreBackend.getKompiledString(converter, files, false, tool); - files.saveToTemp("vdefinition.kore", kompiledString); - String defPath = files.resolveTemp("vdefinition.kore").getAbsolutePath(); - return defPath; - } - - private String saveKoreSpecToTemp(ModuleToKORE converter, Module rules) { - StringBuilder sb = new StringBuilder(); - String koreOutput = converter.convertSpecificationModule(module, rules, - haskellKRunOptions.defaultClaimType, sb); - files.saveToTemp("spec.kore", koreOutput); - return files.resolveTemp("spec.kore").getAbsolutePath(); - } - - private List buildCommonProvingCommand(String defPath, String specPath, String outPath, - String defModuleName, String specModuleName){ - String[] koreCommand; - if (kProveOptions.debugger && !haskellKRunOptions.haskellBackendCommand.equals("kore-exec")) { - throw KEMException.criticalError("Cannot pass --debugger with --haskell-backend-command."); - } else if (kProveOptions.debugger) { - koreCommand = "kore-repl".split("\\s+"); - } else { - koreCommand = haskellKRunOptions.haskellBackendCommand.split("\\s+"); - } - - List args = new ArrayList<>(); - args.addAll(Arrays.asList(koreCommand)); - args.addAll(Arrays.asList( - defPath, - "--module", defModuleName, - "--prove", specPath, - "--spec-module", specModuleName, - "--output", outPath)); - if (smtOptions.smtPrelude != null) { - args.add("--smt-prelude"); - args.add(smtOptions.smtPrelude); - } - if (smtOptions.smtTimeout != null) { - args.add("--smt-timeout"); - args.add(Integer.toString(smtOptions.smtTimeout)); - } - if (kProveOptions.debugScript != null) { - if (!kProveOptions.debugger) { - throw KEMException.criticalError("Can only use --debug-script with --debugger."); - } - args.add("--repl-script"); - args.add(files.resolveWorkingDirectory(kProveOptions.debugScript).getAbsolutePath()); - } - return args; - } - - private RewriterResult executeKoreCommands(Module rules, String[] koreCommand, - String koreDirectory, File koreOutputFile) { - int exit; - try { - File korePath = koreDirectory == null ? null : new File(koreDirectory); - exit = executeCommandBasic(korePath, koreCommand); - checkOutput(koreOutputFile, exit); - } catch (IOException e) { - throw KEMException.criticalError("I/O Error while executing", e); - } catch (InterruptedException e) { - throw KEMException.criticalError("Interrupted while executing", e); - } - K outputK; - try { - outputK = new KoreParser(rules.sortAttributesFor()) - .parseFile(koreOutputFile); - } catch (ParseError parseError) { - kem.registerCriticalWarning(ExceptionType.PROOF_LINT, "Error parsing haskell backend output", parseError); - outputK = KORE.KApply(KLabels.ML_FALSE); - } - return new RewriterResult(Optional.empty(), Optional.of(exit), outputK); - } - - @Override - public RewriterResult prove(Module rules, Boolean reuseDef) { - Module kompiledModule = KoreBackend.getKompiledModule(module, true); - ModuleToKORE converter = new ModuleToKORE(kompiledModule, def.topCellInitializer, kompileOptions, kem); - String defPath = reuseDef ? files.resolveKompiled("definition.kore").getAbsolutePath() : saveKoreDefinitionToTemp(converter); - String specPath = saveKoreSpecToTemp(converter, rules); - File koreOutputFile = files.resolveTemp("result.kore"); - - String koreDirectory = haskellKRunOptions.haskellBackendHome; + GlobalOptions globalOptions, + SMTOptions smtOptions, + KompileOptions kompileOptions, + KProveOptions kProveOptions, + HaskellKRunOptions haskellKRunOptions, + BackendOptions backendOptions, + FileUtil files, + CompiledDefinition def, + KExceptionManager kem, + KPrint kprint, + Tool tool) + implements Function { + + @Inject + public HaskellRewriter {} + + @Override + public Rewriter apply(Definition definition) { + Module module = definition.mainModule(); + return new Rewriter() { + @Override + public RewriterResult execute(K k, Optional depth) { + Module mod = getExecutionModule(module); + ModuleToKORE converter = new ModuleToKORE(mod, def.topCellInitializer, kompileOptions); + String koreOutput = getKoreString(k, mod, converter); + String defPath = + files.resolveKompiled("haskellDefinition.bin").exists() + ? files.resolveKompiled("haskellDefinition.bin").getAbsolutePath() + : files.resolveKompiled("definition.kore").getAbsolutePath(); + String moduleName = mod.name(); + + files.saveToTemp("execute-initial.kore", koreOutput); + String pgmPath = files.resolveTemp("execute-initial.kore").getAbsolutePath(); + String[] koreCommand = haskellKRunOptions.haskellBackendCommand.split("\\s+"); + String koreDirectory = haskellKRunOptions.haskellBackendHome; + File koreOutputFile = files.resolveTemp("execute-result.kore"); + List args = new ArrayList(); + args.addAll(Arrays.asList(koreCommand)); + args.addAll( + Arrays.asList( + defPath, + "--module", + moduleName, + "--pattern", + pgmPath, + "--output", + koreOutputFile.getAbsolutePath())); + if (depth.isPresent()) { + args.add("--depth"); + args.add(Integer.toString(depth.get())); + } + if (smtOptions.smtPrelude != null) { + args.add("--smt-prelude"); + args.add(smtOptions.smtPrelude); + } + if (smtOptions.smtTimeout != null) { + args.add("--smt-timeout"); + args.add(Integer.toString(smtOptions.smtTimeout)); + } + koreCommand = args.toArray(koreCommand); + if (backendOptions.dryRun) { + System.out.println(String.join(" ", koreCommand)); + kprint.options.output = OutputModes.NONE; + return new RewriterResult(Optional.empty(), Optional.empty(), k); + } + try { + File korePath = koreDirectory == null ? null : new File(koreDirectory); + int execStatus = executeCommandBasic(korePath, koreCommand); + checkOutput(koreOutputFile, execStatus); + K outputK = new KoreParser(mod.sortAttributesFor()).parseFile(koreOutputFile); + return new RewriterResult(Optional.empty(), Optional.of(execStatus), outputK); + } catch (IOException e) { + throw KEMException.criticalError("I/O Error while executing", e); + } catch (InterruptedException e) { + throw KEMException.criticalError("Interrupted while executing", e); + } catch (ParseError parseError) { + throw KEMException.criticalError("Error parsing haskell backend output", parseError); + } + } + + @Override + public K match(K k, Rule rule) { + return search(k, Optional.of(0), Optional.empty(), rule, SearchType.STAR); + } + + @Override + public Tuple2 executeAndMatch(K k, Optional depth, Rule rule) { + RewriterResult res = execute(k, depth); + return Tuple2.apply(res, match(res.k(), rule)); + } + + @Override + public K search( + K initialConfiguration, + Optional depth, + Optional bound, + Rule pattern, + SearchType searchType) { + Module mod = getExecutionModule(module); + String koreOutput = + getKoreString( + initialConfiguration, + mod, + new ModuleToKORE(mod, def.topCellInitializer, kompileOptions)); + Sort initializerSort = mod.productionsFor().get(def.topCellInitializer).get().head().sort(); + K patternTerm = RewriteToTop.toLeft(pattern.body()); + if (patternTerm instanceof KVariable) { + patternTerm = + KORE.KVariable( + ((KVariable) patternTerm).name(), Att.empty().add(Sort.class, initializerSort)); + } + K patternCondition = pattern.requires(); + String patternTermKore = + getKoreString( + patternTerm, mod, new ModuleToKORE(mod, def.topCellInitializer, kompileOptions)); + String patternConditionKore; + if (patternCondition.equals(TRUE)) { + patternConditionKore = "\\top{Sort" + initializerSort.name() + "{}}()"; + } else { + patternConditionKore = + "\\equals{SortBool{},Sort" + + initializerSort.name() + + "{}}(" + + getKoreString( + patternCondition, + mod, + new ModuleToKORE(mod, def.topCellInitializer, kompileOptions)) + + ", \\dv{SortBool{}}(\"true\")" + + ")"; + } + String korePatternOutput = + "\\and{Sort" + + initializerSort.name() + + "{}}(" + + patternTermKore + + ", " + + patternConditionKore + + ")"; + String defPath = + files.resolveKompiled("haskellDefinition.bin").exists() + ? files.resolveKompiled("haskellDefinition.bin").getAbsolutePath() + : files.resolveKompiled("definition.kore").getAbsolutePath(); + String moduleName = mod.name(); + + files.saveToTemp("search-initial.kore", koreOutput); + String pgmPath = files.resolveTemp("search-initial.kore").getAbsolutePath(); + files.saveToTemp("search-pattern.kore", korePatternOutput); + String patternPath = files.resolveTemp("search-pattern.kore").getAbsolutePath(); + String[] koreCommand = haskellKRunOptions.haskellBackendCommand.split("\\s+"); + String koreDirectory = haskellKRunOptions.haskellBackendHome; + File koreOutputFile = files.resolveTemp("search-result.kore"); + List args = new ArrayList(); + args.addAll(Arrays.asList(koreCommand)); + args.addAll( + Arrays.asList( + defPath, + "--module", + moduleName, + "--pattern", + pgmPath, + "--output", + koreOutputFile.getAbsolutePath(), + "--searchType", + searchType.toString(), + "--search", + patternPath)); + if (depth.isPresent()) { + args.add("--depth"); + args.add(depth.get().toString()); + } + if (bound.isPresent()) { + args.add("--bound"); + args.add(bound.get().toString()); + } + if (smtOptions.smtPrelude != null) { + args.add("--smt-prelude"); + args.add(smtOptions.smtPrelude); + } + if (smtOptions.smtTimeout != null) { + args.add("--smt-timeout"); + args.add(Integer.toString(smtOptions.smtTimeout)); + } + koreCommand = args.toArray(koreCommand); + if (backendOptions.dryRun) { + System.out.println(String.join(" ", koreCommand)); + kprint.options.output = OutputModes.NONE; + return initialConfiguration; + } + try { + File korePath = koreDirectory == null ? null : new File(koreDirectory); + if (executeCommandBasic(korePath, koreCommand) != 0) { + throw KEMException.criticalError("Haskell backend returned non-zero exit code"); + } + K outputK = new KoreParser(mod.sortAttributesFor()).parseFile(koreOutputFile); + outputK = addAnonymousAttributes(outputK, pattern); + return outputK; + } catch (IOException e) { + throw KEMException.criticalError("I/O Error while executing", e); + } catch (InterruptedException e) { + throw KEMException.criticalError("Interrupted while executing", e); + } catch (ParseError parseError) { + throw KEMException.criticalError("Error parsing haskell backend output", parseError); + } + } + + private K addAnonymousAttributes(K input, Rule pattern) { + Map anonVars = new HashMap<>(); + VisitK visitor = + new VisitK() { + @Override + public void apply(KVariable var) { + anonVars.put(var, var); + } + }; + visitor.apply(pattern.body()); + visitor.apply(pattern.requires()); + visitor.apply(pattern.ensures()); + return new TransformK() { + @Override + public K apply(KVariable var) { + return anonVars.getOrDefault(var, var); + } + }.apply(input); + } + + private Module getExecutionModule(Module module) { + Module mod = def.executionModule(); + if (!module.equals(mod)) { + throw KEMException.criticalError( + "Invalid module specified for rewriting. Haskell backend only supports rewriting over" + + " the definition's main module."); + } + return mod; + } - String defModuleName = def.executionModule().name(); - String specModuleName = kProveOptions.specModule == null ? rules.name() : kProveOptions.specModule; + private String saveKoreDefinitionToTemp(ModuleToKORE converter) { + String kompiledString = KoreBackend.getKompiledString(converter, files, false, tool); + files.saveToTemp("vdefinition.kore", kompiledString); + String defPath = files.resolveTemp("vdefinition.kore").getAbsolutePath(); + return defPath; + } - List args = buildCommonProvingCommand(defPath, specPath, koreOutputFile.getAbsolutePath(), - defModuleName, specModuleName); + private String saveKoreSpecToTemp(ModuleToKORE converter, Module rules) { + StringBuilder sb = new StringBuilder(); + String koreOutput = + converter.convertSpecificationModule( + module, rules, haskellKRunOptions.defaultClaimType, sb); + files.saveToTemp("spec.kore", koreOutput); + return files.resolveTemp("spec.kore").getAbsolutePath(); + } + + private List buildCommonProvingCommand( + String defPath, + String specPath, + String outPath, + String defModuleName, + String specModuleName) { + String[] koreCommand; + if (kProveOptions.debugger + && !haskellKRunOptions.haskellBackendCommand.equals("kore-exec")) { + throw KEMException.criticalError( + "Cannot pass --debugger with --haskell-backend-command."); + } else if (kProveOptions.debugger) { + koreCommand = "kore-repl".split("\\s+"); + } else { + koreCommand = haskellKRunOptions.haskellBackendCommand.split("\\s+"); + } - if (kProveOptions.depth != null) { - args.addAll(Arrays.asList( - "--depth", kProveOptions.depth.toString())); - } - if (kProveOptions.branchingAllowed != Integer.MAX_VALUE) { - args.add("--breadth"); - args.add(String.valueOf(kProveOptions.branchingAllowed)); - } - String[] koreCommand = args.toArray(new String[args.size()]); - if (backendOptions.dryRun) { - globalOptions.debugWarnings = true; // sets this so the kprove directory is not removed. - System.out.println(String.join(" ", koreCommand)); - kprint.options.output = OutputModes.NONE; - return new RewriterResult(Optional.empty(), Optional.of(0),KORE.KApply(KLabels.ML_FALSE)); - } - if (globalOptions.verbose) { - System.err.println("Executing " + args); - } + List args = new ArrayList<>(); + args.addAll(Arrays.asList(koreCommand)); + args.addAll( + Arrays.asList( + defPath, + "--module", + defModuleName, + "--prove", + specPath, + "--spec-module", + specModuleName, + "--output", + outPath)); + if (smtOptions.smtPrelude != null) { + args.add("--smt-prelude"); + args.add(smtOptions.smtPrelude); + } + if (smtOptions.smtTimeout != null) { + args.add("--smt-timeout"); + args.add(Integer.toString(smtOptions.smtTimeout)); + } + if (kProveOptions.debugScript != null) { + if (!kProveOptions.debugger) { + throw KEMException.criticalError("Can only use --debug-script with --debugger."); + } + args.add("--repl-script"); + args.add(files.resolveWorkingDirectory(kProveOptions.debugScript).getAbsolutePath()); + } + return args; + } - return executeKoreCommands(rules, koreCommand, koreDirectory, koreOutputFile); - } + private RewriterResult executeKoreCommands( + Module rules, String[] koreCommand, String koreDirectory, File koreOutputFile) { + int exit; + try { + File korePath = koreDirectory == null ? null : new File(koreDirectory); + exit = executeCommandBasic(korePath, koreCommand); + checkOutput(koreOutputFile, exit); + } catch (IOException e) { + throw KEMException.criticalError("I/O Error while executing", e); + } catch (InterruptedException e) { + throw KEMException.criticalError("Interrupted while executing", e); + } + K outputK; + try { + outputK = new KoreParser(rules.sortAttributesFor()).parseFile(koreOutputFile); + } catch (ParseError parseError) { + kem.registerCriticalWarning( + ExceptionType.PROOF_LINT, "Error parsing haskell backend output", parseError); + outputK = KORE.KApply(KLabels.ML_FALSE); + } + return new RewriterResult(Optional.empty(), Optional.of(exit), outputK); + } + + @Override + public RewriterResult prove(Module rules, Boolean reuseDef) { + Module kompiledModule = KoreBackend.getKompiledModule(module, true); + ModuleToKORE converter = + new ModuleToKORE(kompiledModule, def.topCellInitializer, kompileOptions, kem); + String defPath = + reuseDef + ? files.resolveKompiled("definition.kore").getAbsolutePath() + : saveKoreDefinitionToTemp(converter); + String specPath = saveKoreSpecToTemp(converter, rules); + File koreOutputFile = files.resolveTemp("result.kore"); + + String koreDirectory = haskellKRunOptions.haskellBackendHome; + + String defModuleName = def.executionModule().name(); + String specModuleName = + kProveOptions.specModule == null ? rules.name() : kProveOptions.specModule; + + List args = + buildCommonProvingCommand( + defPath, specPath, koreOutputFile.getAbsolutePath(), defModuleName, specModuleName); + + if (kProveOptions.depth != null) { + args.addAll(Arrays.asList("--depth", kProveOptions.depth.toString())); + } + if (kProveOptions.branchingAllowed != Integer.MAX_VALUE) { + args.add("--breadth"); + args.add(String.valueOf(kProveOptions.branchingAllowed)); + } + String[] koreCommand = args.toArray(new String[args.size()]); + if (backendOptions.dryRun) { + globalOptions.debugWarnings = true; // sets this so the kprove directory is not removed. + System.out.println(String.join(" ", koreCommand)); + kprint.options.output = OutputModes.NONE; + return new RewriterResult( + Optional.empty(), Optional.of(0), KORE.KApply(KLabels.ML_FALSE)); + } + if (globalOptions.verbose) { + System.err.println("Executing " + args); + } - @Override - public boolean equivalence(Rewriter firstDef, Rewriter secondDef, Module firstSpec, Module secondSpec) { - throw new UnsupportedOperationException(); - } - }; + return executeKoreCommands(rules, koreCommand, koreDirectory, koreOutputFile); + } + + @Override + public boolean equivalence( + Rewriter firstDef, Rewriter secondDef, Module firstSpec, Module secondSpec) { + throw new UnsupportedOperationException(); + } + }; + } + + private void checkOutput(File koreOutputFile, int execStatus) { + if (execStatus != 0) { + if (!koreOutputFile.isFile()) { + throw KEMException.criticalError( + "Haskell Backend execution failed with code " + + execStatus + + " and produced no output."); + } } - - private void checkOutput(File koreOutputFile, int execStatus) { - if (execStatus != 0) { - if (!koreOutputFile.isFile()) { - throw KEMException.criticalError("Haskell Backend execution failed with code " + execStatus - + " and produced no output."); - } - } + } + + private String getKoreString(K initialConfiguration, Module mod, ModuleToKORE converter) { + ExpandMacros macroExpander = ExpandMacros.forNonSentences(mod, files, kompileOptions, false); + K withMacros = macroExpander.expand(initialConfiguration); + K kWithInjections = new AddSortInjections(mod).addInjections(withMacros); + StringBuilder sb = new StringBuilder(); + converter.convert(kWithInjections, sb); + return sb.toString(); + } + + /** + * Runs a command in the given directory, + * + * @param workingDir directory to run in + * @param command commandline to run + * @return + * @throws IOException + * @throws InterruptedException + */ + private int executeCommandBasic(File workingDir, String... command) + throws IOException, InterruptedException { + if (globalOptions.verbose) { + System.err.println("Executing command: " + String.join(" ", Arrays.asList(command))); } - - private String getKoreString(K initialConfiguration, Module mod, ModuleToKORE converter) { - ExpandMacros macroExpander = ExpandMacros.forNonSentences(mod, files, kompileOptions, false); - K withMacros = macroExpander.expand(initialConfiguration); - K kWithInjections = new AddSortInjections(mod).addInjections(withMacros); - StringBuilder sb = new StringBuilder(); - converter.convert(kWithInjections, sb); - return sb.toString(); + int exit; + ProcessBuilder pb = files.getProcessBuilder().command(command); + if (workingDir != null) { + pb.directory(workingDir); } + if (Main.isNailgun()) { + Process p2 = pb.start(); - /** - * Runs a command in the given directory, - * @param workingDir directory to run in - * @param command commandline to run - * @return - * @throws IOException - * @throws InterruptedException - */ - private int executeCommandBasic(File workingDir, String... command) throws IOException, InterruptedException { - if (globalOptions.verbose) { - System.err.println("Executing command: " + String.join(" ", Arrays.asList(command))); - } - int exit; - ProcessBuilder pb = files.getProcessBuilder() - .command(command); - if (workingDir != null) { - pb.directory(workingDir); - } - if (Main.isNailgun()) { - - Process p2 = pb.start(); - - Thread in = new Thread(() -> { + Thread in = + new Thread( + () -> { int count; byte[] buffer = new byte[8192]; try { - while (true) { - count = System.in.read(buffer); - if (count < 0) - break; - p2.getOutputStream().write(buffer, 0, count); - p2.getOutputStream().flush(); - } - } catch (IOException ignored) {} - }); - Thread out = RunProcess.getOutputStreamThread(p2::getInputStream, System.out); - Thread err = RunProcess.getOutputStreamThread(p2::getErrorStream, System.err); - in.start(); - out.start(); - err.start(); - - exit = p2.waitFor(); - in.interrupt(); - in.join(); - out.join(); - err.join(); - System.out.flush(); - System.err.flush(); - return exit; - } else { - // if we're not nailgun, we can't do the above because System.in won't be interruptible, - // and we don't really want or need to anyway. - return pb.inheritIO().start().waitFor(); - } + while (true) { + count = System.in.read(buffer); + if (count < 0) break; + p2.getOutputStream().write(buffer, 0, count); + p2.getOutputStream().flush(); + } + } catch (IOException ignored) { + } + }); + Thread out = RunProcess.getOutputStreamThread(p2::getInputStream, System.out); + Thread err = RunProcess.getOutputStreamThread(p2::getErrorStream, System.err); + in.start(); + out.start(); + err.start(); + + exit = p2.waitFor(); + in.interrupt(); + in.join(); + out.join(); + err.join(); + System.out.flush(); + System.err.flush(); + return exit; + } else { + // if we're not nailgun, we can't do the above because System.in won't be interruptible, + // and we don't really want or need to anyway. + return pb.inheritIO().start().waitFor(); } + } } - diff --git a/k-distribution/src/test/java/org/kframework/kore/convertors/BaseTest.java b/k-distribution/src/test/java/org/kframework/kore/convertors/BaseTest.java index 7cebecbbefe..20fc21dea5d 100644 --- a/k-distribution/src/test/java/org/kframework/kore/convertors/BaseTest.java +++ b/k-distribution/src/test/java/org/kframework/kore/convertors/BaseTest.java @@ -2,109 +2,111 @@ package org.kframework.kore.convertors; +import static org.junit.Assert.assertEquals; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Calendar; +import java.util.function.Function; import org.apache.commons.io.FileUtils; import org.junit.Rule; import org.junit.rules.TestName; import org.kframework.attributes.Source; import org.kframework.compile.ProcessGroupAttributes; import org.kframework.kil.Definition; -import org.kframework.parser.inner.CollectProductionsVisitor; import org.kframework.kil.loader.Context; +import org.kframework.parser.inner.CollectProductionsVisitor; import org.kframework.parser.outer.Outer; -import java.io.File; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.Calendar; -import java.util.function.Function; - -import static org.junit.Assert.assertEquals; - public abstract class BaseTest { - private static final String COPYRIGHT_HEADER = "// Copyright (c) " + Calendar.getInstance().get(Calendar.YEAR) + " K Team. All Rights Reserved."; - private static final String COPYRIGHT_HEADER_REGEX = "// Copyright \\(c\\) [0-9\\-]* K Team. All Rights Reserved."; + private static final String COPYRIGHT_HEADER = + "// Copyright (c) " + + Calendar.getInstance().get(Calendar.YEAR) + + " K Team. All Rights Reserved."; + private static final String COPYRIGHT_HEADER_REGEX = + "// Copyright \\(c\\) [0-9\\-]* K Team. All Rights Reserved."; - @Rule - public TestName name = new TestName(); + @Rule public TestName name = new TestName(); - public BaseTest() { - super(); - } + public BaseTest() { + super(); + } - protected void outerOnlyTest() throws IOException { - testConversion(this::parseUsingOuter); - } + protected void outerOnlyTest() throws IOException { + testConversion(this::parseUsingOuter); + } - public static class DefinitionWithContext { - public final Definition definition; - public final Context context; + public static class DefinitionWithContext { + public final Definition definition; + public final Context context; - public DefinitionWithContext(Definition d, Context c) { - this.definition = d; - this.context = c; - new CollectProductionsVisitor(c).visit(d); - } + public DefinitionWithContext(Definition d, Context c) { + this.definition = d; + this.context = c; + new CollectProductionsVisitor(c).visit(d); } + } - protected File testResource(String baseName) { - return new File("src/test/resources" + baseName); - // a bit of a hack to get around not having a clear working directory - // Eclipse runs tests within k/k-distribution, IntelliJ within /k - } + protected File testResource(String baseName) { + return new File("src/test/resources" + baseName); + // a bit of a hack to get around not having a clear working directory + // Eclipse runs tests within k/k-distribution, IntelliJ within /k + } - // WARNING: only use this after checking the results manually - private static final boolean forceFixAssertionFiles = false; + // WARNING: only use this after checking the results manually + private static final boolean forceFixAssertionFiles = false; - private void testConversion(Function parse) throws IOException { - File kilDefinitionFile = testResource("/convertor-tests/" + name.getMethodName() + ".k"); - File kilExpectedDefinitionFile = testResource("/convertor-tests/" + name.getMethodName() + expectedFilePostfix()); + private void testConversion(Function parse) throws IOException { + File kilDefinitionFile = testResource("/convertor-tests/" + name.getMethodName() + ".k"); + File kilExpectedDefinitionFile = + testResource("/convertor-tests/" + name.getMethodName() + expectedFilePostfix()); - DefinitionWithContext defWithContext = parse.apply(kilDefinitionFile); + DefinitionWithContext defWithContext = parse.apply(kilDefinitionFile); - String actualOutput = convert(defWithContext); + String actualOutput = convert(defWithContext); - if (forceFixAssertionFiles) { - PrintWriter printWriter = new PrintWriter(kilExpectedDefinitionFile); - String sep = "\n"; - if (actualOutput.startsWith("\n")) - sep = ""; + if (forceFixAssertionFiles) { + PrintWriter printWriter = new PrintWriter(kilExpectedDefinitionFile); + String sep = "\n"; + if (actualOutput.startsWith("\n")) sep = ""; - actualOutput = actualOutput.replaceAll(" +\n", "\n"); + actualOutput = actualOutput.replaceAll(" +\n", "\n"); - printWriter.print(COPYRIGHT_HEADER + sep + actualOutput + "\n"); - printWriter.close(); - } else { - String expectedOutput = FileUtils.readFileToString(kilExpectedDefinitionFile).replaceAll("\r\n", "\n"); - // fixing Windows line endings (git autocrlf=auto generates Windows line endings on checkout) + printWriter.print(COPYRIGHT_HEADER + sep + actualOutput + "\n"); + printWriter.close(); + } else { + String expectedOutput = + FileUtils.readFileToString(kilExpectedDefinitionFile).replaceAll("\r\n", "\n"); + // fixing Windows line endings (git autocrlf=auto generates Windows line endings on checkout) - assertEquals(clean(expectedOutput), clean(actualOutput)); - } + assertEquals(clean(expectedOutput), clean(actualOutput)); } + } - protected abstract String convert(DefinitionWithContext defWithContext); - - protected abstract String expectedFilePostfix(); - - protected DefinitionWithContext parseUsingOuter(File definitionFile) { - Definition def = new Definition(); - String definitionText; - try { - definitionText = FileUtils.readFileToString(definitionFile); - } catch (IOException e) { - throw new RuntimeException(e); - } - def.setItems(Outer.parse(Source.apply(definitionFile.getPath()), definitionText, null)); - def.setMainModule("TEST"); - def.setMainSyntaxModule("TEST"); - - ProcessGroupAttributes.apply(def); - Context context = new Context(); - return new DefinitionWithContext(def, context); - } + protected abstract String convert(DefinitionWithContext defWithContext); - private String clean(String definitionText) { - return definitionText.replaceAll(COPYRIGHT_HEADER_REGEX, "").replaceAll(" *\n", "\n").trim(); - } + protected abstract String expectedFilePostfix(); + protected DefinitionWithContext parseUsingOuter(File definitionFile) { + Definition def = new Definition(); + String definitionText; + try { + definitionText = FileUtils.readFileToString(definitionFile); + } catch (IOException e) { + throw new RuntimeException(e); + } + def.setItems(Outer.parse(Source.apply(definitionFile.getPath()), definitionText, null)); + def.setMainModule("TEST"); + def.setMainSyntaxModule("TEST"); + + ProcessGroupAttributes.apply(def); + Context context = new Context(); + return new DefinitionWithContext(def, context); + } + + private String clean(String definitionText) { + return definitionText.replaceAll(COPYRIGHT_HEADER_REGEX, "").replaceAll(" *\n", "\n").trim(); + } } diff --git a/k-distribution/src/test/java/org/kframework/kore/convertors/TstKILtoKOREIT.java b/k-distribution/src/test/java/org/kframework/kore/convertors/TstKILtoKOREIT.java index 5448cb26827..03ea8aac08d 100644 --- a/k-distribution/src/test/java/org/kframework/kore/convertors/TstKILtoKOREIT.java +++ b/k-distribution/src/test/java/org/kframework/kore/convertors/TstKILtoKOREIT.java @@ -2,64 +2,71 @@ package org.kframework.kore.convertors; +import java.io.IOException; import org.junit.FixMethodOrder; import org.junit.Ignore; import org.junit.Test; import org.junit.runners.MethodSorters; -import java.io.IOException; - @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TstKILtoKOREIT extends BaseTest { - @Test @Ignore - public void emptyModule() throws IOException { - outerOnlyTest(); - } + @Test + @Ignore + public void emptyModule() throws IOException { + outerOnlyTest(); + } - @Test @Ignore - public void simpleSyntax() throws IOException { - outerOnlyTest(); - } + @Test + @Ignore + public void simpleSyntax() throws IOException { + outerOnlyTest(); + } - @Test @Ignore - public void syntaxWithAttributes() throws IOException { - outerOnlyTest(); - } + @Test + @Ignore + public void syntaxWithAttributes() throws IOException { + outerOnlyTest(); + } - @Test @Ignore - public void syntaxWithRhs() throws IOException { - outerOnlyTest(); - } + @Test + @Ignore + public void syntaxWithRhs() throws IOException { + outerOnlyTest(); + } - @Test @Ignore - public void imports() throws IOException { - outerOnlyTest(); - } + @Test + @Ignore + public void imports() throws IOException { + outerOnlyTest(); + } - @Test @Ignore - public void syntaxPriorities() throws IOException { - outerOnlyTest(); - } + @Test + @Ignore + public void syntaxPriorities() throws IOException { + outerOnlyTest(); + } - @Test @Ignore - public void syntaxWithPriorities() throws IOException { - outerOnlyTest(); - } + @Test + @Ignore + public void syntaxWithPriorities() throws IOException { + outerOnlyTest(); + } - @Test @Ignore - public void syntaxWithOR() throws IOException { - outerOnlyTest(); - } + @Test + @Ignore + public void syntaxWithOR() throws IOException { + outerOnlyTest(); + } - protected String convert(DefinitionWithContext defWithContext) { - KILtoKORE kilToKore = new KILtoKORE(defWithContext.context, false, false); - org.kframework.definition.Definition koreDef = kilToKore.apply(defWithContext.definition); - String koreDefString = koreDef.toString(); - return koreDefString; - } + protected String convert(DefinitionWithContext defWithContext) { + KILtoKORE kilToKore = new KILtoKORE(defWithContext.context, false, false); + org.kframework.definition.Definition koreDef = kilToKore.apply(defWithContext.definition); + String koreDefString = koreDef.toString(); + return koreDefString; + } - protected String expectedFilePostfix() { - return "-expected.k"; - } + protected String expectedFilePostfix() { + return "-expected.k"; + } } diff --git a/kernel/src/main/java/com/davekoelle/AlphanumComparator.java b/kernel/src/main/java/com/davekoelle/AlphanumComparator.java index 7bb24b20402..da8e2491046 100644 --- a/kernel/src/main/java/com/davekoelle/AlphanumComparator.java +++ b/kernel/src/main/java/com/davekoelle/AlphanumComparator.java @@ -27,106 +27,86 @@ import java.util.Comparator; /** - * This is an updated version with enhancements made by Daniel Migowski, - * Andre Bogus, and David Koelle + * This is an updated version with enhancements made by Daniel Migowski, Andre Bogus, and David + * Koelle * - * To convert to use Templates (Java 1.5+): - * - Change "implements Comparator" to "implements Comparator" - * - Change "compare(Object o1, Object o2)" to "compare(String s1, String s2)" - * - Remove the type checking and casting in compare(). + *

To convert to use Templates (Java 1.5+): - Change "implements Comparator" to "implements + * Comparator" - Change "compare(Object o1, Object o2)" to "compare(String s1, String s2)" - + * Remove the type checking and casting in compare(). * - * To use this class: - * Use the static "sort" method from the java.util.Collections class: - * Collections.sort(your list, new AlphanumComparator()); + *

To use this class: Use the static "sort" method from the java.util.Collections class: + * Collections.sort(your list, new AlphanumComparator()); */ -public class AlphanumComparator implements Comparator -{ - private final boolean isDigit(char ch) - { - return ch >= 48 && ch <= 57; - } +public class AlphanumComparator implements Comparator { + private final boolean isDigit(char ch) { + return ch >= 48 && ch <= 57; + } - /** Length of string is passed in for improved efficiency (only need to calculate it once) **/ - private final String getChunk(String s, int slength, int marker) - { - StringBuilder chunk = new StringBuilder(); - char c = s.charAt(marker); + /** Length of string is passed in for improved efficiency (only need to calculate it once) * */ + private final String getChunk(String s, int slength, int marker) { + StringBuilder chunk = new StringBuilder(); + char c = s.charAt(marker); + chunk.append(c); + marker++; + if (isDigit(c)) { + while (marker < slength) { + c = s.charAt(marker); + if (!isDigit(c)) break; chunk.append(c); marker++; - if (isDigit(c)) - { - while (marker < slength) - { - c = s.charAt(marker); - if (!isDigit(c)) - break; - chunk.append(c); - marker++; - } - } else - { - while (marker < slength) - { - c = s.charAt(marker); - if (isDigit(c)) - break; - chunk.append(c); - marker++; - } - } - return chunk.toString(); + } + } else { + while (marker < slength) { + c = s.charAt(marker); + if (isDigit(c)) break; + chunk.append(c); + marker++; + } } + return chunk.toString(); + } - public int compare(String o1, String o2) - { - if (!(o1 instanceof String) || !(o2 instanceof String)) - { - return 0; - } - String s1 = o1; - String s2 = o2; + public int compare(String o1, String o2) { + if (!(o1 instanceof String) || !(o2 instanceof String)) { + return 0; + } + String s1 = o1; + String s2 = o2; - int thisMarker = 0; - int thatMarker = 0; - int s1Length = s1.length(); - int s2Length = s2.length(); + int thisMarker = 0; + int thatMarker = 0; + int s1Length = s1.length(); + int s2Length = s2.length(); - while (thisMarker < s1Length && thatMarker < s2Length) - { - String thisChunk = getChunk(s1, s1Length, thisMarker); - thisMarker += thisChunk.length(); + while (thisMarker < s1Length && thatMarker < s2Length) { + String thisChunk = getChunk(s1, s1Length, thisMarker); + thisMarker += thisChunk.length(); - String thatChunk = getChunk(s2, s2Length, thatMarker); - thatMarker += thatChunk.length(); + String thatChunk = getChunk(s2, s2Length, thatMarker); + thatMarker += thatChunk.length(); - // If both chunks contain numeric characters, sort them numerically - int result = 0; - if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) - { - // Simple chunk comparison by length. - int thisChunkLength = thisChunk.length(); - result = thisChunkLength - thatChunk.length(); - // If equal, the first different number counts - if (result == 0) - { - for (int i = 0; i < thisChunkLength; i++) - { - result = thisChunk.charAt(i) - thatChunk.charAt(i); - if (result != 0) - { - return result; - } - } - } - } else - { - result = thisChunk.compareTo(thatChunk); + // If both chunks contain numeric characters, sort them numerically + int result = 0; + if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) { + // Simple chunk comparison by length. + int thisChunkLength = thisChunk.length(); + result = thisChunkLength - thatChunk.length(); + // If equal, the first different number counts + if (result == 0) { + for (int i = 0; i < thisChunkLength; i++) { + result = thisChunk.charAt(i) - thatChunk.charAt(i); + if (result != 0) { + return result; } - - if (result != 0) - return result; + } } + } else { + result = thisChunk.compareTo(thatChunk); + } - return s1Length - s2Length; + if (result != 0) return result; } + + return s1Length - s2Length; + } } diff --git a/kernel/src/main/java/org/kframework/backend/Backends.java b/kernel/src/main/java/org/kframework/backend/Backends.java index 00ec358b18f..1877bb898fb 100644 --- a/kernel/src/main/java/org/kframework/backend/Backends.java +++ b/kernel/src/main/java/org/kframework/backend/Backends.java @@ -3,8 +3,8 @@ public class Backends { - public static final String PDF = "pdf"; - public static final String HTML = "html"; - public static final String HASKELL = "haskell"; - public static final String LLVM = "llvm"; + public static final String PDF = "pdf"; + public static final String HTML = "html"; + public static final String HASKELL = "haskell"; + public static final String LLVM = "llvm"; } diff --git a/kernel/src/main/java/org/kframework/backend/PosterBackend.java b/kernel/src/main/java/org/kframework/backend/PosterBackend.java index e6eb54af744..4aa49a5c831 100644 --- a/kernel/src/main/java/org/kframework/backend/PosterBackend.java +++ b/kernel/src/main/java/org/kframework/backend/PosterBackend.java @@ -7,14 +7,13 @@ public abstract class PosterBackend { - protected final Stopwatch sw; - protected final Context context; + protected final Stopwatch sw; + protected final Context context; - public PosterBackend(Stopwatch sw, Context context) { - this.sw = sw; - this.context = context; - } - - public abstract void run(Definition def); + public PosterBackend(Stopwatch sw, Context context) { + this.sw = sw; + this.context = context; + } + public abstract void run(Definition def); } diff --git a/kernel/src/main/java/org/kframework/backend/kore/ConstructorChecks.java b/kernel/src/main/java/org/kframework/backend/kore/ConstructorChecks.java index 7e058f2e729..56f195c5a73 100644 --- a/kernel/src/main/java/org/kframework/backend/kore/ConstructorChecks.java +++ b/kernel/src/main/java/org/kframework/backend/kore/ConstructorChecks.java @@ -14,85 +14,99 @@ import scala.collection.Set; public class ConstructorChecks { - private final Module module; + private final Module module; - public ConstructorChecks(Module m) { - module = m; - } + public ConstructorChecks(Module m) { + module = m; + } - /** - * Checks whether a lhs pattern is constructor-based, i.e., an application of a - * function symbol to a list of constructor terms. - * - * @param term - * @return - */ - public boolean isConstructorBased(K term) { - if (term instanceof KApply) { - return ((KApply) term).klist().items().stream().allMatch(this::isConstructorTerm); - } - if (term instanceof KAs) { - return isConstructorBased(((KAs) term).pattern()); - } - KException.criticalError("Unexpecting isConstructorBased call on " + term.getClass()); - return false; + /** + * Checks whether a lhs pattern is constructor-based, i.e., an application of a function symbol to + * a list of constructor terms. + * + * @param term + * @return + */ + public boolean isConstructorBased(K term) { + if (term instanceof KApply) { + return ((KApply) term).klist().items().stream().allMatch(this::isConstructorTerm); } - - /** - * Checks whether a pattern is a constructor term, i.e., - * it is not formed by means of reducible symbols (functions). - * - * @param term - * @return - */ - public boolean isConstructorTerm(K term) { - return (new FoldK() { - @Override - public Boolean unit() { - return true; - } - - @Override - public Boolean merge(Boolean a, Boolean b) { - return a && b; - } - - @Override - public Boolean apply(KApply k) { - return isConstructorLike(k.klabel()) && super.apply(k); - } - }).apply(term); + if (term instanceof KAs) { + return isConstructorBased(((KAs) term).pattern()); } + KException.criticalError("Unexpecting isConstructorBased call on " + term.getClass()); + return false; + } - // the functions below could be moved to a common place and made public + /** + * Checks whether a pattern is a constructor term, i.e., it is not formed by means of reducible + * symbols (functions). + * + * @param term + * @return + */ + public boolean isConstructorTerm(K term) { + return (new FoldK() { + @Override + public Boolean unit() { + return true; + } - private boolean isConstructorLike(KLabel klabel) { - String labelName = klabel.name(); - if (isBuiltinLabel(klabel)) return false; - if (isInjectionLabel(labelName) || isBuiltinModuloConstructor(klabel)) return true; - Set productionSet = module.productionsFor().apply(klabel.head()); - assert productionSet.size() == 1 : "Should not have more than one production"; - Production production = productionSet.head(); - return !production.att().contains(Att.FUNCTION()); - } + @Override + public Boolean merge(Boolean a, Boolean b) { + return a && b; + } - public static boolean isBuiltinLabel(KLabel label) { - return label.name().equals(KLabels.ML_FALSE.name()) || label.name().equals(KLabels.ML_TRUE.name()) || label.name().equals(KLabels.ML_NOT.name()) || - label.name().equals(KLabels.ML_OR.name()) || label.name().equals(KLabels.ML_AND.name()) || label.name().equals(KLabels.ML_IMPLIES.name()) || - label.name().equals(KLabels.ML_EQUALS.name()) || label.name().equals(KLabels.ML_CEIL.name()) || label.name().equals(KLabels.ML_FLOOR.name()) || - label.name().equals(KLabels.ML_EXISTS.name()) || label.name().equals(KLabels.ML_FORALL.name()) || label.name().equals(KLabels.CTL_AG.name()) || - label.name().equals(KLabels.RL_wEF.name()) || label.name().equals(KLabels.RL_wAF.name()); - } + @Override + public Boolean apply(KApply k) { + return isConstructorLike(k.klabel()) && super.apply(k); + } + }) + .apply(term); + } - private boolean isBuiltinModuloConstructor(KLabel label) { - return label.equals(KLabels.DotMap) || label.equals(KLabels.Map) || label.equals(KLabels.MapItem) || - label.equals(KLabels.DotList) || label.equals(KLabels.List) || label.equals(KLabels.ListItem) || - label.equals(KLabels.DotSet) || label.equals(KLabels.Set) || label.equals(KLabels.SetItem); - } + // the functions below could be moved to a common place and made public - private boolean isInjectionLabel(String labelName) { - return labelName.equals(KLabels.INJ); - } + private boolean isConstructorLike(KLabel klabel) { + String labelName = klabel.name(); + if (isBuiltinLabel(klabel)) return false; + if (isInjectionLabel(labelName) || isBuiltinModuloConstructor(klabel)) return true; + Set productionSet = module.productionsFor().apply(klabel.head()); + assert productionSet.size() == 1 : "Should not have more than one production"; + Production production = productionSet.head(); + return !production.att().contains(Att.FUNCTION()); + } + + public static boolean isBuiltinLabel(KLabel label) { + return label.name().equals(KLabels.ML_FALSE.name()) + || label.name().equals(KLabels.ML_TRUE.name()) + || label.name().equals(KLabels.ML_NOT.name()) + || label.name().equals(KLabels.ML_OR.name()) + || label.name().equals(KLabels.ML_AND.name()) + || label.name().equals(KLabels.ML_IMPLIES.name()) + || label.name().equals(KLabels.ML_EQUALS.name()) + || label.name().equals(KLabels.ML_CEIL.name()) + || label.name().equals(KLabels.ML_FLOOR.name()) + || label.name().equals(KLabels.ML_EXISTS.name()) + || label.name().equals(KLabels.ML_FORALL.name()) + || label.name().equals(KLabels.CTL_AG.name()) + || label.name().equals(KLabels.RL_wEF.name()) + || label.name().equals(KLabels.RL_wAF.name()); + } + private boolean isBuiltinModuloConstructor(KLabel label) { + return label.equals(KLabels.DotMap) + || label.equals(KLabels.Map) + || label.equals(KLabels.MapItem) + || label.equals(KLabels.DotList) + || label.equals(KLabels.List) + || label.equals(KLabels.ListItem) + || label.equals(KLabels.DotSet) + || label.equals(KLabels.Set) + || label.equals(KLabels.SetItem); + } + private boolean isInjectionLabel(String labelName) { + return labelName.equals(KLabels.INJ); + } } diff --git a/kernel/src/main/java/org/kframework/backend/kore/KoreBackend.java b/kernel/src/main/java/org/kframework/backend/kore/KoreBackend.java index a0101f9bead..efd8777f64e 100644 --- a/kernel/src/main/java/org/kframework/backend/kore/KoreBackend.java +++ b/kernel/src/main/java/org/kframework/backend/kore/KoreBackend.java @@ -1,14 +1,21 @@ // Copyright (c) Runtime Verification, Inc. All Rights Reserved. package org.kframework.backend.kore; +import static org.kframework.Collections.*; + import com.google.inject.Inject; +import java.io.File; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Function; import org.apache.commons.io.FilenameUtils; import org.kframework.Strategy; import org.kframework.attributes.Att; import org.kframework.backend.Backends; import org.kframework.compile.*; -import org.kframework.definition.Module; import org.kframework.definition.*; +import org.kframework.definition.Module; import org.kframework.kompile.CompiledDefinition; import org.kframework.kompile.Kompile; import org.kframework.kompile.KompileOptions; @@ -20,256 +27,385 @@ import org.kframework.utils.file.FileUtil; import scala.Function1; -import java.io.File; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.function.Function; - -import static org.kframework.Collections.*; - public class KoreBackend extends AbstractBackend { - private final KompileOptions kompileOptions; - protected final FileUtil files; - private final KExceptionManager kem; - protected final boolean heatCoolEquations; - private final Tool tool; + private final KompileOptions kompileOptions; + protected final FileUtil files; + private final KExceptionManager kem; + protected final boolean heatCoolEquations; + private final Tool tool; - @Inject - public KoreBackend( - KompileOptions kompileOptions, - FileUtil files, - KExceptionManager kem, - Tool tool) { - this(kompileOptions, files, kem, false, tool); - } + @Inject + public KoreBackend( + KompileOptions kompileOptions, FileUtil files, KExceptionManager kem, Tool tool) { + this(kompileOptions, files, kem, false, tool); + } - public KoreBackend(KompileOptions kompileOptions, FileUtil files, KExceptionManager kem, boolean heatCoolEquations, Tool tool) { - this.kompileOptions = kompileOptions; - this.files = files; - this.kem = kem; - this.heatCoolEquations = heatCoolEquations; - this.tool = tool; - } + public KoreBackend( + KompileOptions kompileOptions, + FileUtil files, + KExceptionManager kem, + boolean heatCoolEquations, + Tool tool) { + this.kompileOptions = kompileOptions; + this.files = files; + this.kem = kem; + this.heatCoolEquations = heatCoolEquations; + this.tool = tool; + } - @Override - public void accept(Backend.Holder h) { - CompiledDefinition def = h.def; - String kore = getKompiledString(def, true); - File defFile = kompileOptions.outerParsing.mainDefinitionFile(files); - String name = defFile.getName(); - String basename = FilenameUtils.removeExtension(name); - files.saveToDefinitionDirectory(basename + ".kore", kore); - } + @Override + public void accept(Backend.Holder h) { + CompiledDefinition def = h.def; + String kore = getKompiledString(def, true); + File defFile = kompileOptions.outerParsing.mainDefinitionFile(files); + String name = defFile.getName(); + String basename = FilenameUtils.removeExtension(name); + files.saveToDefinitionDirectory(basename + ".kore", kore); + } - /** - * Convert a CompiledDefinition to a String of a KORE definition. - * @param hasAnd whether the backend in question supports and-patterns during pattern matching. - */ - protected String getKompiledString(CompiledDefinition def, boolean hasAnd) { - Module mainModule = getKompiledModule(def.kompiledDefinition.mainModule(), hasAnd); - ModuleToKORE converter = new ModuleToKORE(mainModule, def.topCellInitializer, def.kompileOptions); - return getKompiledString(converter, files, heatCoolEquations, tool); - } + /** + * Convert a CompiledDefinition to a String of a KORE definition. + * + * @param hasAnd whether the backend in question supports and-patterns during pattern matching. + */ + protected String getKompiledString(CompiledDefinition def, boolean hasAnd) { + Module mainModule = getKompiledModule(def.kompiledDefinition.mainModule(), hasAnd); + ModuleToKORE converter = + new ModuleToKORE(mainModule, def.topCellInitializer, def.kompileOptions); + return getKompiledString(converter, files, heatCoolEquations, tool); + } - public static String getKompiledString(ModuleToKORE converter, FileUtil files, boolean heatCoolEquations, Tool t) { - StringBuilder sb = new StringBuilder(); - String kompiledString = getKompiledStringAndWriteSyntaxMacros(converter, files, heatCoolEquations, sb, t); - return kompiledString; - } + public static String getKompiledString( + ModuleToKORE converter, FileUtil files, boolean heatCoolEquations, Tool t) { + StringBuilder sb = new StringBuilder(); + String kompiledString = + getKompiledStringAndWriteSyntaxMacros(converter, files, heatCoolEquations, sb, t); + return kompiledString; + } - public static String getKompiledStringAndWriteSyntaxMacros(ModuleToKORE converter, FileUtil files, boolean heatCoolEq, StringBuilder sb, Tool t) { - StringBuilder semantics = new StringBuilder(); - StringBuilder syntax = new StringBuilder(); - StringBuilder macros = new StringBuilder(); - String prelude = files.loadFromKIncludeDir("kore/prelude.kore"); - converter.convert(heatCoolEq, prelude, semantics, syntax, macros); - if (t == Tool.KOMPILE) { - files.saveToKompiled("syntaxDefinition.kore", syntax.toString()); - files.saveToKompiled("macros.kore", macros.toString()); - } - return semantics.toString(); + public static String getKompiledStringAndWriteSyntaxMacros( + ModuleToKORE converter, FileUtil files, boolean heatCoolEq, StringBuilder sb, Tool t) { + StringBuilder semantics = new StringBuilder(); + StringBuilder syntax = new StringBuilder(); + StringBuilder macros = new StringBuilder(); + String prelude = files.loadFromKIncludeDir("kore/prelude.kore"); + converter.convert(heatCoolEq, prelude, semantics, syntax, macros); + if (t == Tool.KOMPILE) { + files.saveToKompiled("syntaxDefinition.kore", syntax.toString()); + files.saveToKompiled("macros.kore", macros.toString()); } + return semantics.toString(); + } - public static Module getKompiledModule(Module mainModule, boolean hasAnd) { - mainModule = ModuleTransformer.fromSentenceTransformer(new AddSortInjections(mainModule)::addInjections, "Add sort injections").apply(mainModule); - mainModule = ModuleTransformer.from(new RemoveUnit()::apply, "Remove unit applications for collections").apply(mainModule); - if (hasAnd) { - mainModule = ModuleTransformer.fromSentenceTransformer(new MinimizeTermConstruction(mainModule)::resolve, "Minimize term construction").apply(mainModule); - } - return mainModule; + public static Module getKompiledModule(Module mainModule, boolean hasAnd) { + mainModule = + ModuleTransformer.fromSentenceTransformer( + new AddSortInjections(mainModule)::addInjections, "Add sort injections") + .apply(mainModule); + mainModule = + ModuleTransformer.from(new RemoveUnit()::apply, "Remove unit applications for collections") + .apply(mainModule); + if (hasAnd) { + mainModule = + ModuleTransformer.fromSentenceTransformer( + new MinimizeTermConstruction(mainModule)::resolve, "Minimize term construction") + .apply(mainModule); } + return mainModule; + } - @Override - public Function steps() { - DefinitionTransformer resolveComm = DefinitionTransformer.from(new ResolveComm(kem)::resolve, "resolve comm simplification rules"); - Function1 resolveStrict = d -> DefinitionTransformer.from(new ResolveStrict(kompileOptions, d)::resolve, "resolving strict and seqstrict attributes").apply(d); - DefinitionTransformer resolveHeatCoolAttribute = DefinitionTransformer.fromSentenceTransformer(new ResolveHeatCoolAttribute(new HashSet<>())::resolve, "resolving heat and cool attributes"); - DefinitionTransformer resolveAnonVars = DefinitionTransformer.fromSentenceTransformer(new ResolveAnonVar()::resolve, "resolving \"_\" vars"); - DefinitionTransformer guardOrs = DefinitionTransformer.fromSentenceTransformer(new GuardOrPatterns()::resolve, "resolving or patterns"); - DefinitionTransformer resolveSemanticCasts = - DefinitionTransformer.fromSentenceTransformer(new ResolveSemanticCasts(true)::resolve, "resolving semantic casts"); - DefinitionTransformer resolveFun = DefinitionTransformer.from(new ResolveFun()::resolve, "resolving #fun"); - Function1 resolveFunctionWithConfig = d -> DefinitionTransformer.from(new ResolveFunctionWithConfig(d)::moduleResolve, "resolving functions with config context").apply(d); - DefinitionTransformer generateSortPredicateSyntax = DefinitionTransformer.from(new GenerateSortPredicateSyntax()::gen, "adding sort predicate productions"); - DefinitionTransformer generateSortPredicateRules = DefinitionTransformer.from(new GenerateSortPredicateRules()::gen, "adding sort predicate rules"); - DefinitionTransformer generateSortProjections = DefinitionTransformer.from(new GenerateSortProjections(kompileOptions.coverage)::gen, "adding sort projections"); - DefinitionTransformer subsortKItem = DefinitionTransformer.from(Kompile::subsortKItem, "subsort all sorts to KItem"); - Function1 addCoolLikeAtt = d -> DefinitionTransformer.fromSentenceTransformer(new AddCoolLikeAtt(d.mainModule())::add, "add cool-like attribute").apply(d); - Function1 propagateMacroToRules = - d -> DefinitionTransformer.fromSentenceTransformer((m, s) -> new PropagateMacro(m).propagate(s), "propagate macro labels from production to rules").apply(d); - Function1 expandMacros = d -> { + @Override + public Function steps() { + DefinitionTransformer resolveComm = + DefinitionTransformer.from( + new ResolveComm(kem)::resolve, "resolve comm simplification rules"); + Function1 resolveStrict = + d -> + DefinitionTransformer.from( + new ResolveStrict(kompileOptions, d)::resolve, + "resolving strict and seqstrict attributes") + .apply(d); + DefinitionTransformer resolveHeatCoolAttribute = + DefinitionTransformer.fromSentenceTransformer( + new ResolveHeatCoolAttribute(new HashSet<>())::resolve, + "resolving heat and cool attributes"); + DefinitionTransformer resolveAnonVars = + DefinitionTransformer.fromSentenceTransformer( + new ResolveAnonVar()::resolve, "resolving \"_\" vars"); + DefinitionTransformer guardOrs = + DefinitionTransformer.fromSentenceTransformer( + new GuardOrPatterns()::resolve, "resolving or patterns"); + DefinitionTransformer resolveSemanticCasts = + DefinitionTransformer.fromSentenceTransformer( + new ResolveSemanticCasts(true)::resolve, "resolving semantic casts"); + DefinitionTransformer resolveFun = + DefinitionTransformer.from(new ResolveFun()::resolve, "resolving #fun"); + Function1 resolveFunctionWithConfig = + d -> + DefinitionTransformer.from( + new ResolveFunctionWithConfig(d)::moduleResolve, + "resolving functions with config context") + .apply(d); + DefinitionTransformer generateSortPredicateSyntax = + DefinitionTransformer.from( + new GenerateSortPredicateSyntax()::gen, "adding sort predicate productions"); + DefinitionTransformer generateSortPredicateRules = + DefinitionTransformer.from( + new GenerateSortPredicateRules()::gen, "adding sort predicate rules"); + DefinitionTransformer generateSortProjections = + DefinitionTransformer.from( + new GenerateSortProjections(kompileOptions.coverage)::gen, "adding sort projections"); + DefinitionTransformer subsortKItem = + DefinitionTransformer.from(Kompile::subsortKItem, "subsort all sorts to KItem"); + Function1 addCoolLikeAtt = + d -> + DefinitionTransformer.fromSentenceTransformer( + new AddCoolLikeAtt(d.mainModule())::add, "add cool-like attribute") + .apply(d); + Function1 propagateMacroToRules = + d -> + DefinitionTransformer.fromSentenceTransformer( + (m, s) -> new PropagateMacro(m).propagate(s), + "propagate macro labels from production to rules") + .apply(d); + Function1 expandMacros = + d -> { ResolveFunctionWithConfig transformer = new ResolveFunctionWithConfig(d); - return DefinitionTransformer.fromSentenceTransformer((m, s) -> new ExpandMacros(transformer, m, files, kem, kompileOptions, false).expand(s), "expand macros").apply(d); + return DefinitionTransformer.fromSentenceTransformer( + (m, s) -> + new ExpandMacros(transformer, m, files, kem, kompileOptions, false).expand(s), + "expand macros") + .apply(d); }; - Function1 checkSimplificationRules = d -> DefinitionTransformer.from(m -> { m.localRules().foreach(r -> checkSimpIsFunc(m, r)); return m;}, "Check simplification rules").apply(d); - DefinitionTransformer constantFolding = DefinitionTransformer.fromSentenceTransformer(new ConstantFolding()::fold, "constant expression folding"); - ResolveFreshConfigConstants freshConfigResolver = new ResolveFreshConfigConstants(); - Function1 resolveFreshConfigConstants = d -> - DefinitionTransformer.from(m -> freshConfigResolver.resolve(m), "resolving !Var config variables").apply(d); - Function1 resolveFreshConstants = d -> - DefinitionTransformer.from(m -> new ResolveFreshConstants(d, kompileOptions.topCell, files, freshConfigResolver.getCurrentFresh()).resolve(m), "resolving !Var variables").apply(d); - GenerateCoverage cov = new GenerateCoverage(kompileOptions.coverage, files); - Function1 genCoverage = d -> DefinitionTransformer.fromRuleBodyTransformerWithRule((r, body) -> cov.gen(r, body, d.mainModule()), "generate coverage instrumentation").apply(d); - DefinitionTransformer numberSentences = DefinitionTransformer.fromSentenceTransformer(NumberSentences::number, "number sentences uniquely"); - Function1 resolveConfigVar = d -> DefinitionTransformer.fromSentenceTransformer(new ResolveFunctionWithConfig(d)::resolveConfigVar, "Adding configuration variable to lhs").apply(d); - Function1 resolveIO = (d -> Kompile.resolveIOStreams(kem, d)); - Function1 markExtraConcreteRules = d -> MarkExtraConcreteRules.mark(d, kompileOptions.extraConcreteRuleLabels); - Function1 removeAnywhereRules = - d -> DefinitionTransformer.from(this::removeAnywhereRules, - "removing anywhere rules for the Haskell backend").apply(d); + Function1 checkSimplificationRules = + d -> + DefinitionTransformer.from( + m -> { + m.localRules().foreach(r -> checkSimpIsFunc(m, r)); + return m; + }, + "Check simplification rules") + .apply(d); + DefinitionTransformer constantFolding = + DefinitionTransformer.fromSentenceTransformer( + new ConstantFolding()::fold, "constant expression folding"); + ResolveFreshConfigConstants freshConfigResolver = new ResolveFreshConfigConstants(); + Function1 resolveFreshConfigConstants = + d -> + DefinitionTransformer.from( + m -> freshConfigResolver.resolve(m), "resolving !Var config variables") + .apply(d); + Function1 resolveFreshConstants = + d -> + DefinitionTransformer.from( + m -> + new ResolveFreshConstants( + d, + kompileOptions.topCell, + files, + freshConfigResolver.getCurrentFresh()) + .resolve(m), + "resolving !Var variables") + .apply(d); + GenerateCoverage cov = new GenerateCoverage(kompileOptions.coverage, files); + Function1 genCoverage = + d -> + DefinitionTransformer.fromRuleBodyTransformerWithRule( + (r, body) -> cov.gen(r, body, d.mainModule()), + "generate coverage instrumentation") + .apply(d); + DefinitionTransformer numberSentences = + DefinitionTransformer.fromSentenceTransformer( + NumberSentences::number, "number sentences uniquely"); + Function1 resolveConfigVar = + d -> + DefinitionTransformer.fromSentenceTransformer( + new ResolveFunctionWithConfig(d)::resolveConfigVar, + "Adding configuration variable to lhs") + .apply(d); + Function1 resolveIO = (d -> Kompile.resolveIOStreams(kem, d)); + Function1 markExtraConcreteRules = + d -> MarkExtraConcreteRules.mark(d, kompileOptions.extraConcreteRuleLabels); + Function1 removeAnywhereRules = + d -> + DefinitionTransformer.from( + this::removeAnywhereRules, "removing anywhere rules for the Haskell backend") + .apply(d); - return def -> resolveComm - .andThen(resolveIO) - .andThen(resolveFun) - .andThen(resolveFunctionWithConfig) - .andThen(resolveStrict) - .andThen(resolveAnonVars) - .andThen(d -> new ResolveContexts(kompileOptions).resolve(d)) - .andThen(numberSentences) - .andThen(resolveHeatCoolAttribute) - .andThen(resolveSemanticCasts) - .andThen(subsortKItem) - .andThen(generateSortPredicateSyntax) - .andThen(generateSortProjections) - .andThen(constantFolding) - .andThen(propagateMacroToRules) - .andThen(expandMacros) - .andThen(checkSimplificationRules) - .andThen(guardOrs) - .andThen(AddImplicitComputationCell::transformDefinition) - .andThen(resolveFreshConfigConstants) - .andThen(resolveFreshConstants) - .andThen(generateSortPredicateSyntax) - .andThen(generateSortProjections) - .andThen(subsortKItem) - .andThen(d -> new Strategy().addStrategyCellToRulesTransformer(d).apply(d)) - .andThen(d -> Strategy.addStrategyRuleToMainModule(def.mainModule().name()).apply(d)) - .andThen(d -> ConcretizeCells.transformDefinition(d)) - .andThen(genCoverage) - .andThen(Kompile::addSemanticsModule) - .andThen(resolveConfigVar) - .andThen(addCoolLikeAtt) - .andThen(markExtraConcreteRules) - .andThen(removeAnywhereRules) - .andThen(generateSortPredicateRules) - .andThen(numberSentences) - .apply(def); - } + return def -> + resolveComm + .andThen(resolveIO) + .andThen(resolveFun) + .andThen(resolveFunctionWithConfig) + .andThen(resolveStrict) + .andThen(resolveAnonVars) + .andThen(d -> new ResolveContexts(kompileOptions).resolve(d)) + .andThen(numberSentences) + .andThen(resolveHeatCoolAttribute) + .andThen(resolveSemanticCasts) + .andThen(subsortKItem) + .andThen(generateSortPredicateSyntax) + .andThen(generateSortProjections) + .andThen(constantFolding) + .andThen(propagateMacroToRules) + .andThen(expandMacros) + .andThen(checkSimplificationRules) + .andThen(guardOrs) + .andThen(AddImplicitComputationCell::transformDefinition) + .andThen(resolveFreshConfigConstants) + .andThen(resolveFreshConstants) + .andThen(generateSortPredicateSyntax) + .andThen(generateSortProjections) + .andThen(subsortKItem) + .andThen(d -> new Strategy().addStrategyCellToRulesTransformer(d).apply(d)) + .andThen(d -> Strategy.addStrategyRuleToMainModule(def.mainModule().name()).apply(d)) + .andThen(d -> ConcretizeCells.transformDefinition(d)) + .andThen(genCoverage) + .andThen(Kompile::addSemanticsModule) + .andThen(resolveConfigVar) + .andThen(addCoolLikeAtt) + .andThen(markExtraConcreteRules) + .andThen(removeAnywhereRules) + .andThen(generateSortPredicateRules) + .andThen(numberSentences) + .apply(def); + } - @Override - public Function specificationSteps(Definition def) { - ModuleTransformer resolveComm = ModuleTransformer.from(new ResolveComm(kem)::resolve, "resolve comm simplification rules"); - Module mod = def.mainModule(); - ConfigurationInfoFromModule configInfo = new ConfigurationInfoFromModule(mod); - LabelInfo labelInfo = new LabelInfoFromModule(mod); - SortInfo sortInfo = SortInfo.fromModule(mod); - ModuleTransformer resolveAnonVars = ModuleTransformer.fromSentenceTransformer( - new ResolveAnonVar()::resolve, - "resolving \"_\" vars"); - ModuleTransformer numberSentences = ModuleTransformer.fromSentenceTransformer(NumberSentences::number, "number sentences uniquely"); - ModuleTransformer resolveSemanticCasts = ModuleTransformer.fromSentenceTransformer( - new ResolveSemanticCasts(true)::resolve, - "resolving semantic casts"); - Function1 propagateMacroToRules = - m -> ModuleTransformer.fromSentenceTransformer((m2, s) -> new PropagateMacro(m2).propagate(s), "propagate macro labels from production to rules").apply(m); - Function1 expandMacros = m -> { + @Override + public Function specificationSteps(Definition def) { + ModuleTransformer resolveComm = + ModuleTransformer.from(new ResolveComm(kem)::resolve, "resolve comm simplification rules"); + Module mod = def.mainModule(); + ConfigurationInfoFromModule configInfo = new ConfigurationInfoFromModule(mod); + LabelInfo labelInfo = new LabelInfoFromModule(mod); + SortInfo sortInfo = SortInfo.fromModule(mod); + ModuleTransformer resolveAnonVars = + ModuleTransformer.fromSentenceTransformer( + new ResolveAnonVar()::resolve, "resolving \"_\" vars"); + ModuleTransformer numberSentences = + ModuleTransformer.fromSentenceTransformer( + NumberSentences::number, "number sentences uniquely"); + ModuleTransformer resolveSemanticCasts = + ModuleTransformer.fromSentenceTransformer( + new ResolveSemanticCasts(true)::resolve, "resolving semantic casts"); + Function1 propagateMacroToRules = + m -> + ModuleTransformer.fromSentenceTransformer( + (m2, s) -> new PropagateMacro(m2).propagate(s), + "propagate macro labels from production to rules") + .apply(m); + Function1 expandMacros = + m -> { ResolveFunctionWithConfig transformer = new ResolveFunctionWithConfig(m); - return ModuleTransformer.fromSentenceTransformer((m2, s) -> new ExpandMacros(transformer, m2, files, kem, kompileOptions, false).expand(s), "expand macros").apply(m); + return ModuleTransformer.fromSentenceTransformer( + (m2, s) -> + new ExpandMacros(transformer, m2, files, kem, kompileOptions, false) + .expand(s), + "expand macros") + .apply(m); }; - Function1 checkSimplificationRules = ModuleTransformer.from(m -> { m.localRules().foreach(r -> checkSimpIsFunc(m, r)); return m;}, "Check simplification rules"); - ModuleTransformer subsortKItem = ModuleTransformer.from(Kompile::subsortKItem, "subsort all sorts to KItem"); - ModuleTransformer addImplicitComputationCell = ModuleTransformer.fromSentenceTransformer( - new AddImplicitComputationCell(configInfo, labelInfo)::apply, - "concretizing configuration"); - Function1 resolveFreshConstants = d -> - ModuleTransformer.from(new ResolveFreshConstants(def, kompileOptions.topCell, files)::resolve, "resolving !Var variables").apply(d); - Function1 addImplicitCounterCell = ModuleTransformer.fromSentenceTransformer( - new AddImplicitCounterCell()::apply, - "adding to claims if necessary"); - ModuleTransformer concretizeCells = ModuleTransformer.fromSentenceTransformer( - new ConcretizeCells(configInfo, labelInfo, sortInfo, mod)::concretize, - "concretizing configuration"); - ModuleTransformer generateSortProjections = ModuleTransformer.from(new GenerateSortProjections(false)::gen, "adding sort projections"); + Function1 checkSimplificationRules = + ModuleTransformer.from( + m -> { + m.localRules().foreach(r -> checkSimpIsFunc(m, r)); + return m; + }, + "Check simplification rules"); + ModuleTransformer subsortKItem = + ModuleTransformer.from(Kompile::subsortKItem, "subsort all sorts to KItem"); + ModuleTransformer addImplicitComputationCell = + ModuleTransformer.fromSentenceTransformer( + new AddImplicitComputationCell(configInfo, labelInfo)::apply, + "concretizing configuration"); + Function1 resolveFreshConstants = + d -> + ModuleTransformer.from( + new ResolveFreshConstants(def, kompileOptions.topCell, files)::resolve, + "resolving !Var variables") + .apply(d); + Function1 addImplicitCounterCell = + ModuleTransformer.fromSentenceTransformer( + new AddImplicitCounterCell()::apply, + "adding to claims if necessary"); + ModuleTransformer concretizeCells = + ModuleTransformer.fromSentenceTransformer( + new ConcretizeCells(configInfo, labelInfo, sortInfo, mod)::concretize, + "concretizing configuration"); + ModuleTransformer generateSortProjections = + ModuleTransformer.from(new GenerateSortProjections(false)::gen, "adding sort projections"); - return m -> resolveComm - .andThen(addImplicitCounterCell) - .andThen(resolveAnonVars) - .andThen(numberSentences) - .andThen(resolveSemanticCasts) - .andThen(generateSortProjections) - .andThen(propagateMacroToRules) - .andThen(expandMacros) - .andThen(checkSimplificationRules) - .andThen(addImplicitComputationCell) - .andThen(resolveFreshConstants) - .andThen(concretizeCells) - .andThen(subsortKItem) - .andThen(restoreDefinitionModulesTransformer(def)) - .andThen(numberSentences) - .apply(m); - } + return m -> + resolveComm + .andThen(addImplicitCounterCell) + .andThen(resolveAnonVars) + .andThen(numberSentences) + .andThen(resolveSemanticCasts) + .andThen(generateSortProjections) + .andThen(propagateMacroToRules) + .andThen(expandMacros) + .andThen(checkSimplificationRules) + .andThen(addImplicitComputationCell) + .andThen(resolveFreshConstants) + .andThen(concretizeCells) + .andThen(subsortKItem) + .andThen(restoreDefinitionModulesTransformer(def)) + .andThen(numberSentences) + .apply(m); + } - // check that simplification rules have a functional symbol on the LHS - public Sentence checkSimpIsFunc(Module m, Sentence s) { - // need to check after macro expansion - if (s instanceof Rule && (s.att().contains(Att.SIMPLIFICATION()))) { - KLabel kl = m.matchKLabel((Rule) s); - Att atts = m.attributesFor().get(kl).getOrElse(Att::empty); - if (!(atts.contains(Att.FUNCTION()) || atts.contains(Att.FUNCTIONAL()) || atts.contains(Att.ML_OP()))) - throw KEMException.compilerError("Simplification rules expect function/functional/mlOp symbols at the top of the left hand side term.", s); - } - return s; + // check that simplification rules have a functional symbol on the LHS + public Sentence checkSimpIsFunc(Module m, Sentence s) { + // need to check after macro expansion + if (s instanceof Rule && (s.att().contains(Att.SIMPLIFICATION()))) { + KLabel kl = m.matchKLabel((Rule) s); + Att atts = m.attributesFor().get(kl).getOrElse(Att::empty); + if (!(atts.contains(Att.FUNCTION()) + || atts.contains(Att.FUNCTIONAL()) + || atts.contains(Att.ML_OP()))) + throw KEMException.compilerError( + "Simplification rules expect function/functional/mlOp symbols at the top of the left" + + " hand side term.", + s); } + return s; + } - // If a user guarantees that their semantics will never _dynamically_ try to rewrite an anywhere rule on the - // Haskell backend (with the --allow-anywhere-haskell flag), but cannot prove this statically, we allow them to - // strip out all those rules before sending the definition to the backend. If this transformation is applied - // unsoundly (i.e. an anywhere rule would have been attempted if it had not been stripped), the behaviour of the - // Haskell backend on that program is essentially undefined. - private Module removeAnywhereRules(Module m) { - java.util.Set sentences = mutable(m.localSentences()); - - if(kompileOptions.backend.equals(Backends.HASKELL) && kompileOptions.allowAnywhereRulesHaskell) { - java.util.Set filtered = new HashSet(); + // If a user guarantees that their semantics will never _dynamically_ try to rewrite an anywhere + // rule on the + // Haskell backend (with the --allow-anywhere-haskell flag), but cannot prove this statically, we + // allow them to + // strip out all those rules before sending the definition to the backend. If this transformation + // is applied + // unsoundly (i.e. an anywhere rule would have been attempted if it had not been stripped), the + // behaviour of the + // Haskell backend on that program is essentially undefined. + private Module removeAnywhereRules(Module m) { + java.util.Set sentences = mutable(m.localSentences()); - for(var s : sentences) { - if(s instanceof Rule && s.att().contains(Att.ANYWHERE())) { - kem.registerCompilerWarning(KException.ExceptionType.REMOVED_ANYWHERE, - "Removed anywhere rule for Haskell backend execution; this may change the behavior of your code.", s); - } else { - filtered.add(s); - } - } + if (kompileOptions.backend.equals(Backends.HASKELL) + && kompileOptions.allowAnywhereRulesHaskell) { + java.util.Set filtered = new HashSet(); - sentences = filtered; + for (var s : sentences) { + if (s instanceof Rule && s.att().contains(Att.ANYWHERE())) { + kem.registerCompilerWarning( + KException.ExceptionType.REMOVED_ANYWHERE, + "Removed anywhere rule for Haskell backend execution; this may change the behavior of" + + " your code.", + s); + } else { + filtered.add(s); } - return new Module(m.name(), m.imports(), immutable(sentences), m.att()); - } + } - @Override - public Set excludedModuleTags() { - return Collections.singleton(Att.SYMBOLIC()); + sentences = filtered; } + return new Module(m.name(), m.imports(), immutable(sentences), m.att()); + } + + @Override + public Set excludedModuleTags() { + return Collections.singleton(Att.SYMBOLIC()); + } } diff --git a/kernel/src/main/java/org/kframework/backend/kore/ModuleToKORE.java b/kernel/src/main/java/org/kframework/backend/kore/ModuleToKORE.java index fda179960ff..8996a6e19df 100644 --- a/kernel/src/main/java/org/kframework/backend/kore/ModuleToKORE.java +++ b/kernel/src/main/java/org/kframework/backend/kore/ModuleToKORE.java @@ -1,13 +1,29 @@ // Copyright (c) Runtime Verification, Inc. All Rights Reserved. package org.kframework.backend.kore; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.HashMultimap; import com.google.common.collect.ListMultimap; import com.google.common.collect.SetMultimap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.kframework.Collections; import org.kframework.attributes.Att; -import org.kframework.attributes.Att.Key; import org.kframework.attributes.HasLocation; import org.kframework.attributes.Source; import org.kframework.builtin.BooleanUtils; @@ -53,2000 +69,2242 @@ import scala.collection.JavaConverters; import scala.collection.Seq; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.function.Function; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import static org.kframework.Collections.*; -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; - class RuleInfo { - boolean isEquation; - boolean isOwise; - boolean isKore; - boolean isCeil; - Production production; - String productionSortStr; - List prodChildrenSorts; - KLabel productionLabel; - List leftChildren; - - public RuleInfo(boolean equation, boolean owise, boolean kore, boolean ceil, Production production, - String prodSortStr, List prodChildrenSorts, KLabel prodLabel, List leftChildren) { - this.isEquation = equation; - this.isOwise = owise; - this.isKore = kore; - this.isCeil = ceil; - this.production = production; - this.productionSortStr = prodSortStr; - this.prodChildrenSorts = prodChildrenSorts; - this.productionLabel = prodLabel; - this.leftChildren = leftChildren; - } + boolean isEquation; + boolean isOwise; + boolean isKore; + boolean isCeil; + Production production; + String productionSortStr; + List prodChildrenSorts; + KLabel productionLabel; + List leftChildren; + + public RuleInfo( + boolean equation, + boolean owise, + boolean kore, + boolean ceil, + Production production, + String prodSortStr, + List prodChildrenSorts, + KLabel prodLabel, + List leftChildren) { + this.isEquation = equation; + this.isOwise = owise; + this.isKore = kore; + this.isCeil = ceil; + this.production = production; + this.productionSortStr = prodSortStr; + this.prodChildrenSorts = prodChildrenSorts; + this.productionLabel = prodLabel; + this.leftChildren = leftChildren; + } } public class ModuleToKORE { - public enum SentenceType { - REWRITE_RULE, - ONE_PATH, - ALL_PATH - } - - public static final String ONE_PATH_OP = KLabels.RL_wEF.name(); - public static final String ALL_PATH_OP = KLabels.RL_wAF.name(); - private final Module module; - private final AddSortInjections addSortInjections; - private final KLabel topCellInitializer; - private final Set mlBinders = new HashSet<>(); - private final KompileOptions options; - - private final KExceptionManager kem; - - public ModuleToKORE(Module module, KLabel topCellInitializer, KompileOptions options) { - this(module, topCellInitializer, options, null); - } - - public ModuleToKORE(Module module, KLabel topCellInitializer, KompileOptions options, KExceptionManager kem) { - this.kem = kem; - this.module = module; - this.addSortInjections = new AddSortInjections(module); - this.topCellInitializer = topCellInitializer; - this.options = options; - for (Production prod : iterable(module.sortedProductions())) { - if (prod.att().contains(Att.ML_BINDER())) { - mlBinders.add(prod.klabel().get().name()); - } - } + public enum SentenceType { + REWRITE_RULE, + ONE_PATH, + ALL_PATH + } + + public static final String ONE_PATH_OP = KLabels.RL_wEF.name(); + public static final String ALL_PATH_OP = KLabels.RL_wAF.name(); + private final Module module; + private final AddSortInjections addSortInjections; + private final KLabel topCellInitializer; + private final Set mlBinders = new HashSet<>(); + private final KompileOptions options; + + private final KExceptionManager kem; + + public ModuleToKORE(Module module, KLabel topCellInitializer, KompileOptions options) { + this(module, topCellInitializer, options, null); + } + + public ModuleToKORE( + Module module, KLabel topCellInitializer, KompileOptions options, KExceptionManager kem) { + this.kem = kem; + this.module = module; + this.addSortInjections = new AddSortInjections(module); + this.topCellInitializer = topCellInitializer; + this.options = options; + for (Production prod : iterable(module.sortedProductions())) { + if (prod.att().contains(Att.ML_BINDER())) { + mlBinders.add(prod.klabel().get().name()); + } } - private static final boolean METAVAR = false; - - public void convert(boolean heatCoolEq, String prelude, StringBuilder semantics, StringBuilder syntax, StringBuilder macros) { - Sort topCellSort = Sorts.GeneratedTopCell(); - String topCellSortStr = getSortStr(topCellSort); - semantics.append("[topCellInitializer{}("); - convert(topCellInitializer, semantics); - semantics.append("()), "); - StringBuilder sb = new StringBuilder(); - HashMap considerSource = new HashMap<>(); - considerSource.put(Att.SOURCE(), true); - // insert the location of the main module so the backend can provide better error location - convert(considerSource, Att.empty().add(Source.class, module.att().get(Source.class)), sb, null, null); - semantics.append(sb.subSequence(1, sb.length() - 1)); - semantics.append("]\n\n"); - - semantics.append(prelude); - semantics.append("\n"); - - SentenceType sentenceType = getSentenceType(module.att()).orElse(SentenceType.REWRITE_RULE); - semantics.append("module "); - convert(module.name(), semantics); - semantics.append("\n\n// imports\n"); - semantics.append(" import K []\n\n// sorts\n"); - - Set tokenSorts = new HashSet<>(); - // Map attribute name to whether the attribute has a value - Map attributes = new HashMap<>(); - attributes.put(Att.NAT(), true); - attributes.put(Att.TERMINALS(), true); - attributes.put(Att.COLORS(), true); - attributes.put(Att.PRIORITY(), true); - Set priorities = new HashSet<>(); - collectTokenSortsAndAttributes(tokenSorts, attributes, priorities, heatCoolEq, topCellSortStr); - Map priorityToPreviousGroup = new HashMap<>(); - List priorityList = new ArrayList<>(priorities); - java.util.Collections.sort(priorityList); - if (priorityList.size() > 0 ) { - priorityToPreviousGroup.put(priorityList.get(0), ""); - } - for (int i = 1; i < priorityList.size(); i++) { - Integer previous = priorityList.get(i - 1); - Integer current = priorityList.get(i); - priorityToPreviousGroup.put(current, String.format("priorityLE%d", previous)); - } - - Set collectionSorts = new HashSet<>(); - collectionSorts.add("SET.Set"); - collectionSorts.add("MAP.Map"); - collectionSorts.add("LIST.List"); - collectionSorts.add("ARRAY.Array"); - collectionSorts.add("RANGEMAP.RangeMap"); - attributes.remove(Att.HAS_DOMAIN_VALUES()); - if (attributes.containsKey(Att.TOKEN())) { - attributes.put(Att.HAS_DOMAIN_VALUES(), false); - } - translateSorts(tokenSorts, attributes, collectionSorts, semantics); - - List sortedRules = new ArrayList<>(JavaConverters.seqAsJavaList(module.sortedRules())); - if (options.backend.equals("haskell")) { - module.sortedProductions().toStream().filter(this::isGeneratedInKeysOp).foreach( - prod -> { - if (!options.disableCeilSimplificationRules) { - genMapCeilAxioms(prod, sortedRules); - } - return prod; - } - ); - } - SetMultimap functionRules = HashMultimap.create(); - for (Rule rule : sortedRules) { - K left = RewriteToTop.toLeft(rule.body()); - if (left instanceof KApply kapp) { - Production prod = production(kapp); - if (prod.att().contains(Att.FUNCTION()) || rule.att().contains(Att.ANYWHERE()) || ExpandMacros.isMacro(rule)) { - functionRules.put(kapp.klabel(), rule); + } + + private static final boolean METAVAR = false; + + public void convert( + boolean heatCoolEq, + String prelude, + StringBuilder semantics, + StringBuilder syntax, + StringBuilder macros) { + Sort topCellSort = Sorts.GeneratedTopCell(); + String topCellSortStr = getSortStr(topCellSort); + semantics.append("[topCellInitializer{}("); + convert(topCellInitializer, semantics); + semantics.append("()), "); + StringBuilder sb = new StringBuilder(); + HashMap considerSource = new HashMap<>(); + considerSource.put(Att.SOURCE(), true); + // insert the location of the main module so the backend can provide better error location + convert( + considerSource, + Att.empty().add(Source.class, module.att().get(Source.class)), + sb, + null, + null); + semantics.append(sb.subSequence(1, sb.length() - 1)); + semantics.append("]\n\n"); + + semantics.append(prelude); + semantics.append("\n"); + + SentenceType sentenceType = getSentenceType(module.att()).orElse(SentenceType.REWRITE_RULE); + semantics.append("module "); + convert(module.name(), semantics); + semantics.append("\n\n// imports\n"); + semantics.append(" import K []\n\n// sorts\n"); + + Set tokenSorts = new HashSet<>(); + // Map attribute name to whether the attribute has a value + Map attributes = new HashMap<>(); + attributes.put(Att.NAT(), true); + attributes.put(Att.TERMINALS(), true); + attributes.put(Att.COLORS(), true); + attributes.put(Att.PRIORITY(), true); + Set priorities = new HashSet<>(); + collectTokenSortsAndAttributes(tokenSorts, attributes, priorities, heatCoolEq, topCellSortStr); + Map priorityToPreviousGroup = new HashMap<>(); + List priorityList = new ArrayList<>(priorities); + java.util.Collections.sort(priorityList); + if (priorityList.size() > 0) { + priorityToPreviousGroup.put(priorityList.get(0), ""); + } + for (int i = 1; i < priorityList.size(); i++) { + Integer previous = priorityList.get(i - 1); + Integer current = priorityList.get(i); + priorityToPreviousGroup.put(current, String.format("priorityLE%d", previous)); + } + + Set collectionSorts = new HashSet<>(); + collectionSorts.add("SET.Set"); + collectionSorts.add("MAP.Map"); + collectionSorts.add("LIST.List"); + collectionSorts.add("ARRAY.Array"); + collectionSorts.add("RANGEMAP.RangeMap"); + attributes.remove(Att.HAS_DOMAIN_VALUES()); + if (attributes.containsKey(Att.TOKEN())) { + attributes.put(Att.HAS_DOMAIN_VALUES(), false); + } + translateSorts(tokenSorts, attributes, collectionSorts, semantics); + + List sortedRules = new ArrayList<>(JavaConverters.seqAsJavaList(module.sortedRules())); + if (options.backend.equals("haskell")) { + module + .sortedProductions() + .toStream() + .filter(this::isGeneratedInKeysOp) + .foreach( + prod -> { + if (!options.disableCeilSimplificationRules) { + genMapCeilAxioms(prod, sortedRules); } - } - } - - semantics.append("\n// symbols\n"); - Set overloads = new HashSet<>(); - for (Production lesser : iterable(module.overloads().elements())) { - for (Production greater : iterable(module.overloads().relations().get(lesser).getOrElse(Collections::Set))) { - overloads.add(greater); - } - } - translateSymbols(attributes, functionRules, overloads, semantics); - - // print syntax definition - syntax.append(semantics); - for (Tuple2> sort : iterable(module.bracketProductionsFor())) { - for (Production prod : iterable(sort._2())) { - translateSymbol(attributes, functionRules, overloads, prod.att().get(Att.BRACKET_LABEL(), KLabel.class), prod, syntax); - } - } - for (Production prod : iterable(module.sortedProductions())) { - if (isBuiltinProduction(prod)) { - continue; - } - if (prod.isSubsort() && !prod.sort().equals(Sorts.K())) { - genSubsortAxiom(prod, syntax); - continue; - } - } - - for (Production lesser : iterable(module.overloads().elements())) { - for (Production greater : iterable(module.overloads().relations().get(lesser).getOrElse(() -> Collections.Set()))) { - genOverloadedAxiom(lesser, greater, syntax); - } - } - - syntax.append("endmodule []\n"); - - semantics.append("\n// generated axioms\n"); - Set> noConfusion = new HashSet<>(); - for (Production prod : iterable(module.sortedProductions())) { - if (isBuiltinProduction(prod)) { - continue; - } - if (prod.isSubsort() && !prod.sort().equals(Sorts.K())) { - genSubsortAxiom(prod, semantics); - continue; - } - if (prod.klabel().isEmpty()) { - continue; - } - if (prod.att().contains(Att.ASSOC())) { - genAssocAxiom(prod, semantics); - } - //if (prod.att().contains(Att.COMM())) { - // genCommAxiom(prod, semantics); - //} - if (prod.att().contains(Att.IDEM())) { - genIdemAxiom(prod, semantics); - } - if (isFunction(prod) && prod.att().contains(Att.UNIT())) { - genUnitAxiom(prod, semantics); - } - if (isFunctional(prod, functionRules)) { - genFunctionalAxiom(prod, semantics); - } - if (isConstructor(prod, functionRules)) { - genNoConfusionAxioms(prod, noConfusion, functionRules, semantics); - } - } - - for (Sort sort : iterable(module.sortedAllSorts())) { - genNoJunkAxiom(sort, semantics); - } - - for (Production lesser : iterable(module.overloads().elements())) { - for (Production greater : iterable(module.overloads().relations().get(lesser).getOrElse(() -> Collections.Set()))) { - genOverloadedAxiom(lesser, greater, semantics); - } + return prod; + }); + } + SetMultimap functionRules = HashMultimap.create(); + for (Rule rule : sortedRules) { + K left = RewriteToTop.toLeft(rule.body()); + if (left instanceof KApply kapp) { + Production prod = production(kapp); + if (prod.att().contains(Att.FUNCTION()) + || rule.att().contains(Att.ANYWHERE()) + || ExpandMacros.isMacro(rule)) { + functionRules.put(kapp.klabel(), rule); } + } + } - semantics.append("\n// rules\n"); + semantics.append("\n// symbols\n"); + Set overloads = new HashSet<>(); + for (Production lesser : iterable(module.overloads().elements())) { + for (Production greater : + iterable( + module.overloads().relations().get(lesser).getOrElse(Collections::Set))) { + overloads.add(greater); + } + } + translateSymbols(attributes, functionRules, overloads, semantics); + + // print syntax definition + syntax.append(semantics); + for (Tuple2> sort : + iterable(module.bracketProductionsFor())) { + for (Production prod : iterable(sort._2())) { + translateSymbol( + attributes, + functionRules, + overloads, + prod.att().get(Att.BRACKET_LABEL(), KLabel.class), + prod, + syntax); + } + } + for (Production prod : iterable(module.sortedProductions())) { + if (isBuiltinProduction(prod)) { + continue; + } + if (prod.isSubsort() && !prod.sort().equals(Sorts.K())) { + genSubsortAxiom(prod, syntax); + continue; + } + } - macros.append("// macros\n"); - int ruleIndex = 0; - ListMultimap priorityToAlias = ArrayListMultimap.create(); - for (Rule rule : sortedRules) { - if (ExpandMacros.isMacro(rule)) { - convertRule(rule, ruleIndex, heatCoolEq, topCellSortStr, attributes, functionRules, - priorityToPreviousGroup, priorityToAlias, sentenceType, macros); - } else { - convertRule(rule, ruleIndex, heatCoolEq, topCellSortStr, attributes, functionRules, - priorityToPreviousGroup, priorityToAlias, sentenceType, semantics); - } - ruleIndex++; - } + for (Production lesser : iterable(module.overloads().elements())) { + for (Production greater : + iterable( + module + .overloads() + .relations() + .get(lesser) + .getOrElse(() -> Collections.Set()))) { + genOverloadedAxiom(lesser, greater, syntax); + } + } - if (options.enableKoreAntileft) { - semantics.append("\n// priority groups\n"); - genPriorityGroups(priorityList, priorityToPreviousGroup, priorityToAlias, topCellSortStr, semantics); - } + syntax.append("endmodule []\n"); - semantics.append("endmodule "); - convert(attributes, module.att().remove(Att.DIGEST()), semantics, null, null); - semantics.append("\n"); + semantics.append("\n// generated axioms\n"); + Set> noConfusion = new HashSet<>(); + for (Production prod : iterable(module.sortedProductions())) { + if (isBuiltinProduction(prod)) { + continue; + } + if (prod.isSubsort() && !prod.sort().equals(Sorts.K())) { + genSubsortAxiom(prod, semantics); + continue; + } + if (prod.klabel().isEmpty()) { + continue; + } + if (prod.att().contains(Att.ASSOC())) { + genAssocAxiom(prod, semantics); + } + // if (prod.att().contains(Att.COMM())) { + // genCommAxiom(prod, semantics); + // } + if (prod.att().contains(Att.IDEM())) { + genIdemAxiom(prod, semantics); + } + if (isFunction(prod) && prod.att().contains(Att.UNIT())) { + genUnitAxiom(prod, semantics); + } + if (isFunctional(prod, functionRules)) { + genFunctionalAxiom(prod, semantics); + } + if (isConstructor(prod, functionRules)) { + genNoConfusionAxioms(prod, noConfusion, functionRules, semantics); + } } - private void collectTokenSortsAndAttributes(Set tokenSorts, Map attributes, - Set priorities, boolean heatCoolEq, String topCellSortStr) { - for (SortHead sort : iterable(module.sortedDefinedSorts())) { - Att att = module.sortAttributesFor().get(sort).getOrElse(() -> KORE.Att()); - if (att.contains(Att.TOKEN())) { - tokenSorts.add(sort); - } - collectAttributes(attributes, att); - } - for (Production prod : iterable(module.sortedProductions())) { - Att att = prod.att(); - if (att.contains(Att.TOKEN())) { - tokenSorts.add(prod.sort().head()); - } - collectAttributes(attributes, att); - } - for (Rule r : iterable(module.sortedRules())) { - Att att = r.att(); - collectAttributes(attributes, att); - RuleInfo ruleInfo = getRuleInfo(r, heatCoolEq, topCellSortStr); - // only collect priorities of semantics rules - if (!ruleInfo.isEquation && !ruleInfo.isKore && !ExpandMacros.isMacro(r)) { - priorities.add(getPriority(att)); - } - } + for (Sort sort : iterable(module.sortedAllSorts())) { + genNoJunkAxiom(sort, semantics); } - private void translateSorts(Set tokenSorts, Map attributes, - Set collectionSorts, StringBuilder sb) { - for (SortHead sort : iterable(module.sortedDefinedSorts())) { - if (sort.equals(Sorts.K().head()) || sort.equals(Sorts.KItem().head())) { - continue; - } - sb.append(" "); - Att att = module.sortAttributesFor().get(sort).getOrElse(() -> KORE.Att()); - if (att.contains(Att.HOOK())) { - if (collectionSorts.contains(att.get(Att.HOOK()))) { - if (att.get(Att.HOOK()).equals("ARRAY.Array")) { - att = att.remove(Att.ELEMENT()); - att = att.remove(Att.UNIT()); - att = att.remove(Att.HOOK()); - } else { - Production concatProd = stream(module.productionsForSort().apply(sort)).filter(p -> p.att().contains(Att.ELEMENT())).findAny().get(); - att = att.add(Att.ELEMENT(), K.class, KApply(KLabel(concatProd.att().get(Att.ELEMENT())))); - att = att.add(Att.CONCAT(), K.class, KApply(concatProd.klabel().get())); - att = att.add(Att.UNIT(), K.class, KApply(KLabel(concatProd.att().get(Att.UNIT())))); - sb.append("hooked-"); - } - } else { - sb.append("hooked-"); - } - } - att = att.remove(Att.HAS_DOMAIN_VALUES()); - if (tokenSorts.contains(sort)) { - att = att.add(Att.HAS_DOMAIN_VALUES()); - } - if (sort.params() == 0 && Sort(sort).isNat()) { - att = att.add(Att.NAT(), sort.name()); - } - sb.append("sort "); - convert(sort, sb); - sb.append(" "); - convert(attributes, att, sb, null, null); - sb.append("\n"); - } + for (Production lesser : iterable(module.overloads().elements())) { + for (Production greater : + iterable( + module + .overloads() + .relations() + .get(lesser) + .getOrElse(() -> Collections.Set()))) { + genOverloadedAxiom(lesser, greater, semantics); + } } - private void translateSymbols(Map attributes, SetMultimap functionRules, - Set overloads, StringBuilder sb) { - for (Production prod : iterable(module.sortedProductions())) { - if (isBuiltinProduction(prod)) { - continue; - } - if (prod.klabel().isEmpty()) { - continue; - } - translateSymbol(attributes, functionRules, overloads, prod.klabel().get(), prod, sb); - } + semantics.append("\n// rules\n"); + + macros.append("// macros\n"); + int ruleIndex = 0; + ListMultimap priorityToAlias = ArrayListMultimap.create(); + for (Rule rule : sortedRules) { + if (ExpandMacros.isMacro(rule)) { + convertRule( + rule, + ruleIndex, + heatCoolEq, + topCellSortStr, + attributes, + functionRules, + priorityToPreviousGroup, + priorityToAlias, + sentenceType, + macros); + } else { + convertRule( + rule, + ruleIndex, + heatCoolEq, + topCellSortStr, + attributes, + functionRules, + priorityToPreviousGroup, + priorityToAlias, + sentenceType, + semantics); + } + ruleIndex++; + } + + if (options.enableKoreAntileft) { + semantics.append("\n// priority groups\n"); + genPriorityGroups( + priorityList, priorityToPreviousGroup, priorityToAlias, topCellSortStr, semantics); + } + + semantics.append("endmodule "); + convert(attributes, module.att().remove(Att.DIGEST()), semantics, null, null); + semantics.append("\n"); + } + + private void collectTokenSortsAndAttributes( + Set tokenSorts, + Map attributes, + Set priorities, + boolean heatCoolEq, + String topCellSortStr) { + for (SortHead sort : iterable(module.sortedDefinedSorts())) { + Att att = module.sortAttributesFor().get(sort).getOrElse(() -> KORE.Att()); + if (att.contains(Att.TOKEN())) { + tokenSorts.add(sort); + } + collectAttributes(attributes, att); + } + for (Production prod : iterable(module.sortedProductions())) { + Att att = prod.att(); + if (att.contains(Att.TOKEN())) { + tokenSorts.add(prod.sort().head()); + } + collectAttributes(attributes, att); + } + for (Rule r : iterable(module.sortedRules())) { + Att att = r.att(); + collectAttributes(attributes, att); + RuleInfo ruleInfo = getRuleInfo(r, heatCoolEq, topCellSortStr); + // only collect priorities of semantics rules + if (!ruleInfo.isEquation && !ruleInfo.isKore && !ExpandMacros.isMacro(r)) { + priorities.add(getPriority(att)); + } } + } - private void translateSymbol(Map attributes, SetMultimap functionRules, Set overloads, - KLabel label, Production prod, StringBuilder sb) { - sb.append(" "); - if (isFunction(prod) && prod.att().contains(Att.HOOK()) && isRealHook(prod.att())) { + private void translateSorts( + Set tokenSorts, + Map attributes, + Set collectionSorts, + StringBuilder sb) { + for (SortHead sort : iterable(module.sortedDefinedSorts())) { + if (sort.equals(Sorts.K().head()) || sort.equals(Sorts.KItem().head())) { + continue; + } + sb.append(" "); + Att att = module.sortAttributesFor().get(sort).getOrElse(() -> KORE.Att()); + if (att.contains(Att.HOOK())) { + if (collectionSorts.contains(att.get(Att.HOOK()))) { + if (att.get(Att.HOOK()).equals("ARRAY.Array")) { + att = att.remove(Att.ELEMENT()); + att = att.remove(Att.UNIT()); + att = att.remove(Att.HOOK()); + } else { + Production concatProd = + stream(module.productionsForSort().apply(sort)) + .filter(p -> p.att().contains(Att.ELEMENT())) + .findAny() + .get(); + att = + att.add( + Att.ELEMENT(), K.class, KApply(KLabel(concatProd.att().get(Att.ELEMENT())))); + att = att.add(Att.CONCAT(), K.class, KApply(concatProd.klabel().get())); + att = att.add(Att.UNIT(), K.class, KApply(KLabel(concatProd.att().get(Att.UNIT())))); sb.append("hooked-"); + } + } else { + sb.append("hooked-"); } - sb.append("symbol "); - convert(label, prod.params(), sb); - String conn; - sb.append("("); - conn = ""; - for (NonTerminal nt : iterable(prod.nonterminals())) { - Sort sort = nt.sort(); - sb.append(conn); - convert(sort, prod, sb); - conn = ", "; - } - sb.append(") : "); - convert(prod.sort(), prod, sb); - sb.append(" "); - Att koreAtt = addKoreAttributes(prod, functionRules, overloads); - convert(attributes, koreAtt, sb, null, null); - sb.append("\n"); - } - - - private void genSubsortAxiom(Production prod, StringBuilder sb) { - Production finalProd = prod; - functionalPattern(prod, () -> { - sb.append("inj{"); - convert(finalProd.getSubsortSort(), finalProd, sb); - sb.append(", "); - convert(finalProd.sort(), finalProd, sb); - sb.append("} (From:"); - convert(finalProd.getSubsortSort(), finalProd, sb); - sb.append(")"); - }, sb); - sb.append(" [subsort{"); - convert(prod.getSubsortSort(), prod, sb); - sb.append(", "); - convert(prod.sort(), prod, sb); - sb.append("}()] // subsort\n"); + } + att = att.remove(Att.HAS_DOMAIN_VALUES()); + if (tokenSorts.contains(sort)) { + att = att.add(Att.HAS_DOMAIN_VALUES()); + } + if (sort.params() == 0 && Sort(sort).isNat()) { + att = att.add(Att.NAT(), sort.name()); + } + sb.append("sort "); + convert(sort, sb); + sb.append(" "); + convert(attributes, att, sb, null, null); + sb.append("\n"); + } + } + + private void translateSymbols( + Map attributes, + SetMultimap functionRules, + Set overloads, + StringBuilder sb) { + for (Production prod : iterable(module.sortedProductions())) { + if (isBuiltinProduction(prod)) { + continue; + } + if (prod.klabel().isEmpty()) { + continue; + } + translateSymbol(attributes, functionRules, overloads, prod.klabel().get(), prod, sb); + } + } + + private void translateSymbol( + Map attributes, + SetMultimap functionRules, + Set overloads, + KLabel label, + Production prod, + StringBuilder sb) { + sb.append(" "); + if (isFunction(prod) && prod.att().contains(Att.HOOK()) && isRealHook(prod.att())) { + sb.append("hooked-"); + } + sb.append("symbol "); + convert(label, prod.params(), sb); + String conn; + sb.append("("); + conn = ""; + for (NonTerminal nt : iterable(prod.nonterminals())) { + Sort sort = nt.sort(); + sb.append(conn); + convert(sort, prod, sb); + conn = ", "; + } + sb.append(") : "); + convert(prod.sort(), prod, sb); + sb.append(" "); + Att koreAtt = addKoreAttributes(prod, functionRules, overloads); + convert(attributes, koreAtt, sb, null, null); + sb.append("\n"); + } + + private void genSubsortAxiom(Production prod, StringBuilder sb) { + Production finalProd = prod; + functionalPattern( + prod, + () -> { + sb.append("inj{"); + convert(finalProd.getSubsortSort(), finalProd, sb); + sb.append(", "); + convert(finalProd.sort(), finalProd, sb); + sb.append("} (From:"); + convert(finalProd.getSubsortSort(), finalProd, sb); + sb.append(")"); + }, + sb); + sb.append(" [subsort{"); + convert(prod.getSubsortSort(), prod, sb); + sb.append(", "); + convert(prod.sort(), prod, sb); + sb.append("}()] // subsort\n"); + } + + private void genAssocAxiom(Production prod, StringBuilder sb) { + // s(s(K1,K2),K3) = s(K1,s(K2,K3)) + if (prod.arity() != 2) { + throw KEMException.compilerError( + "Found a non-binary production with the assoc attribute", prod); + } + if (!(module.subsorts().lessThanEq(prod.sort(), prod.nonterminal(0).sort()) + && module.subsorts().lessThanEq(prod.sort(), prod.nonterminal(1).sort()))) { + throw KEMException.compilerError( + "Found an associative production with ill formed sorts", prod); + } + sb.append(" axiom"); + convertParams(prod.klabel(), true, sb); + sb.append(" \\equals{"); + convert(prod.sort(), prod, sb); + sb.append(", R} ("); + convert(prod.klabel().get(), prod, sb); + sb.append("("); + convert(prod.klabel().get(), prod, sb); + sb.append("(K1:"); + convert(prod.sort(), prod, sb); + sb.append(",K2:"); + convert(prod.sort(), prod, sb); + sb.append("),K3:"); + convert(prod.sort(), prod, sb); + sb.append("),"); + convert(prod.klabel().get(), prod, sb); + sb.append("(K1:"); + convert(prod.sort(), prod, sb); + sb.append(","); + convert(prod.klabel().get(), prod, sb); + sb.append("(K2:"); + convert(prod.sort(), prod, sb); + sb.append(",K3:"); + convert(prod.sort(), prod, sb); + sb.append("))) [assoc{}()] // associativity\n"); + } + + private void genCommAxiom(Production prod, StringBuilder sb) { + // s(K1, K2) = s(K2, K1) + if (prod.arity() != 2) { + throw KEMException.compilerError( + "Found a non-binary production with the comm attribute", prod); + } + if (!(prod.nonterminal(0).sort().equals(prod.nonterminal(1).sort()))) { + throw KEMException.compilerError( + "Found a commutative production with ill formed sorts", prod); + } + Sort childSort = prod.nonterminal(0).sort(); + sb.append(" axiom"); + convertParams(prod.klabel(), true, sb); + sb.append(" \\equals{"); + convert(prod.sort(), prod, sb); + sb.append(", R} ("); + convert(prod.klabel().get(), prod, sb); + sb.append("(K1:"); + convert(childSort, prod, sb); + sb.append(",K2:"); + convert(childSort, prod, sb); + sb.append("),"); + convert(prod.klabel().get(), prod, sb); + sb.append("(K2:"); + convert(childSort, prod, sb); + sb.append(",K1:"); + convert(childSort, prod, sb); + sb.append(")) [comm{}()] // commutativity\n"); + } + + private void genIdemAxiom(Production prod, StringBuilder sb) { + // s(K, K) = K + if (prod.arity() != 2) { + throw KEMException.compilerError( + "Found a non-binary production with the assoc attribute", prod); + } + if (!(prod.sort().equals(prod.nonterminal(0).sort()) + && prod.sort().equals(prod.nonterminal(1).sort()))) { + throw KEMException.compilerError( + "Found an associative production with ill formed sorts", prod); + } + sb.append(" axiom"); + convertParams(prod.klabel(), true, sb); + sb.append(" \\equals{"); + convert(prod.sort(), prod, sb); + sb.append(", R} ("); + convert(prod.klabel().get(), prod, sb); + sb.append("(K:"); + convert(prod.sort(), prod, sb); + sb.append(",K:"); + convert(prod.sort(), prod, sb); + sb.append("),K:"); + convert(prod.sort(), prod, sb); + sb.append(") [idem{}()] // idempotency\n"); + } + + private void genUnitAxiom(Production prod, StringBuilder sb) { + // s(K, unit) = K + // s(unit, K) = K + if (prod.arity() != 2) { + throw KEMException.compilerError( + "Found a non-binary production with the assoc attribute", prod); + } + if (!(prod.sort().equals(prod.nonterminal(0).sort()) + && prod.sort().equals(prod.nonterminal(1).sort()))) { + throw KEMException.compilerError( + "Found an associative production with ill formed sorts", prod); + } + KLabel unit = KLabel(prod.att().get(Att.UNIT())); + sb.append(" axiom"); + convertParams(prod.klabel(), true, sb); + sb.append("\\equals{"); + convert(prod.sort(), prod, sb); + sb.append(", R} ("); + convert(prod.klabel().get(), prod, sb); + sb.append("(K:"); + convert(prod.sort(), prod, sb); + sb.append(","); + convert(unit, sb); + sb.append("()),K:"); + convert(prod.sort(), prod, sb); + sb.append(") [unit{}()] // right unit\n"); + + sb.append(" axiom"); + convertParams(prod.klabel(), true, sb); + sb.append("\\equals{"); + convert(prod.sort(), prod, sb); + sb.append(", R} ("); + convert(prod.klabel().get(), prod, sb); + sb.append("("); + convert(unit, sb); + sb.append("(),K:"); + convert(prod.sort(), prod, sb); + sb.append("),K:"); + convert(prod.sort(), prod, sb); + sb.append(") [unit{}()] // left unit\n"); + } + + private void genMapCeilAxioms(Production prod, Collection rules) { + Sort mapSort = prod.nonterminal(1).sort(); + scala.collection.Set mapProds = module.productionsForSort().apply(mapSort.head()); + Production concatProd = mapProds.find(p -> hasHookValue(p.att(), "MAP.concat")).get(); + Production elementProd = mapProds.find(p -> hasHookValue(p.att(), "MAP.element")).get(); + Seq nonterminals = elementProd.nonterminals(); + Sort sortParam = Sort(AddSortInjections.SORTPARAM_NAME, Sort("Q")); + + // rule + // #Ceil(MapItem(K1, K2, ..., Kn) Rest:Map) + // => + // {(@K1 in_keys(@Rest)) #Equals false} #And #Ceil(@K2) #And ... #And #Ceil(@Kn) + // Note: The {_ in_keys(_) #Equals false} condition implies + // #Ceil(@K1) and #Ceil(@Rest). + // [simplification] + + K restMapSet = KVariable("@Rest", Att.empty().add(Sort.class, mapSort)); + KLabel ceilMapLabel = KLabel(KLabels.ML_CEIL.name(), mapSort, sortParam); + KLabel andLabel = KLabel(KLabels.ML_AND.name(), sortParam); + + // arguments of MapItem and their #Ceils + List setArgs = new ArrayList<>(); + K setArgsCeil = KApply(KLabel(KLabels.ML_TRUE.name(), sortParam)); + for (int i = 0; i < nonterminals.length(); i++) { + Sort sort = nonterminals.apply(i).sort(); + KVariable setVar = KVariable("@K" + i, Att.empty().add(Sort.class, sort)); + setArgs.add(setVar); + if (i > 0) { + KLabel ceil = KLabel(KLabels.ML_CEIL.name(), sort, sortParam); + setArgsCeil = KApply(andLabel, setArgsCeil, KApply(ceil, setVar)); + } } - - private void genAssocAxiom(Production prod, StringBuilder sb) { - // s(s(K1,K2),K3) = s(K1,s(K2,K3)) - if (prod.arity() != 2) { - throw KEMException.compilerError("Found a non-binary production with the assoc attribute", prod); - } - if (!(module.subsorts().lessThanEq(prod.sort(), prod.nonterminal(0).sort()) && - module.subsorts().lessThanEq(prod.sort(), prod.nonterminal(1).sort()))) { - throw KEMException.compilerError("Found an associative production with ill formed sorts", prod); - } - sb.append(" axiom"); - convertParams(prod.klabel(), true, sb); - sb.append(" \\equals{"); - convert(prod.sort(), prod, sb); - sb.append(", R} ("); - convert(prod.klabel().get(), prod, sb); - sb.append("("); - convert(prod.klabel().get(), prod, sb); - sb.append("(K1:"); - convert(prod.sort(), prod, sb); - sb.append(",K2:"); - convert(prod.sort(), prod, sb); - sb.append("),K3:"); - convert(prod.sort(), prod, sb); - sb.append("),"); - convert(prod.klabel().get(), prod, sb); - sb.append("(K1:"); - convert(prod.sort(), prod, sb); - sb.append(","); - convert(prod.klabel().get(), prod, sb); - sb.append("(K2:"); - convert(prod.sort(), prod, sb); - sb.append(",K3:"); - convert(prod.sort(), prod, sb); - sb.append("))) [assoc{}()] // associativity\n"); - } - - private void genCommAxiom(Production prod, StringBuilder sb) { - // s(K1, K2) = s(K2, K1) - if (prod.arity() != 2) { - throw KEMException.compilerError("Found a non-binary production with the comm attribute", prod); - } - if (!(prod.nonterminal(0).sort().equals(prod.nonterminal(1).sort()))) { - throw KEMException.compilerError("Found a commutative production with ill formed sorts", prod); - } - Sort childSort = prod.nonterminal(0).sort(); - sb.append(" axiom"); - convertParams(prod.klabel(), true, sb); - sb.append(" \\equals{"); - convert(prod.sort(), prod, sb); - sb.append(", R} ("); - convert(prod.klabel().get(), prod, sb); - sb.append("(K1:"); - convert(childSort, prod, sb); - sb.append(",K2:"); - convert(childSort, prod, sb); - sb.append("),"); - convert(prod.klabel().get(), prod, sb); - sb.append("(K2:"); - convert(childSort, prod, sb); - sb.append(",K1:"); - convert(childSort, prod, sb); - sb.append(")) [comm{}()] // commutativity\n"); - } - - private void genIdemAxiom(Production prod, StringBuilder sb) { - // s(K, K) = K - if (prod.arity() != 2) { - throw KEMException.compilerError("Found a non-binary production with the assoc attribute", prod); - } - if (!(prod.sort().equals(prod.nonterminal(0).sort()) && prod.sort().equals(prod.nonterminal(1).sort()))) { - throw KEMException.compilerError("Found an associative production with ill formed sorts", prod); - } - sb.append(" axiom"); - convertParams(prod.klabel(), true, sb); - sb.append(" \\equals{"); - convert(prod.sort(), prod, sb); - sb.append(", R} ("); - convert(prod.klabel().get(), prod, sb); - sb.append("(K:"); - convert(prod.sort(), prod, sb); - sb.append(",K:"); - convert(prod.sort(), prod, sb); - sb.append("),K:"); - convert(prod.sort(), prod, sb); - sb.append(") [idem{}()] // idempotency\n"); - } - - private void genUnitAxiom(Production prod, StringBuilder sb) { - // s(K, unit) = K - // s(unit, K) = K - if (prod.arity() != 2) { - throw KEMException.compilerError("Found a non-binary production with the assoc attribute", prod); - } - if (!(prod.sort().equals(prod.nonterminal(0).sort()) && prod.sort().equals(prod.nonterminal(1).sort()))) { - throw KEMException.compilerError("Found an associative production with ill formed sorts", prod); - } - KLabel unit = KLabel(prod.att().get(Att.UNIT())); - sb.append(" axiom"); - convertParams(prod.klabel(), true, sb); - sb.append("\\equals{"); - convert(prod.sort(), prod, sb); - sb.append(", R} ("); - convert(prod.klabel().get(), prod, sb); - sb.append("(K:"); - convert(prod.sort(), prod, sb); - sb.append(","); - convert(unit, sb); - sb.append("()),K:"); - convert(prod.sort(), prod, sb); - sb.append(") [unit{}()] // right unit\n"); - - sb.append(" axiom"); - convertParams(prod.klabel(), true, sb); - sb.append("\\equals{"); - convert(prod.sort(), prod, sb); - sb.append(", R} ("); - convert(prod.klabel().get(), prod, sb); - sb.append("("); - convert(unit, sb); - sb.append("(),K:"); - convert(prod.sort(), prod, sb); - sb.append("),K:"); - convert(prod.sort(), prod, sb); - sb.append(") [unit{}()] // left unit\n"); - } - - private void genMapCeilAxioms(Production prod, Collection rules) { - Sort mapSort = prod.nonterminal(1).sort(); - scala.collection.Set mapProds = module.productionsForSort().apply(mapSort.head()); - Production concatProd = mapProds.find(p -> hasHookValue(p.att(), "MAP.concat")).get(); - Production elementProd = mapProds.find(p -> hasHookValue(p.att(), "MAP.element")).get(); - Seq nonterminals = elementProd.nonterminals(); - Sort sortParam = Sort(AddSortInjections.SORTPARAM_NAME, Sort("Q")); - - // rule - // #Ceil(MapItem(K1, K2, ..., Kn) Rest:Map) - // => - // {(@K1 in_keys(@Rest)) #Equals false} #And #Ceil(@K2) #And ... #And #Ceil(@Kn) - // Note: The {_ in_keys(_) #Equals false} condition implies - // #Ceil(@K1) and #Ceil(@Rest). - // [simplification] - - K restMapSet = KVariable("@Rest", Att.empty().add(Sort.class, mapSort)); - KLabel ceilMapLabel = KLabel(KLabels.ML_CEIL.name(), mapSort, sortParam); - KLabel andLabel = KLabel(KLabels.ML_AND.name(), sortParam); - - // arguments of MapItem and their #Ceils - List setArgs = new ArrayList<>(); - K setArgsCeil = KApply(KLabel(KLabels.ML_TRUE.name(), sortParam)); - for (int i = 0; i < nonterminals.length(); i++) { - Sort sort = nonterminals.apply(i).sort(); - KVariable setVar = KVariable("@K" + i, Att.empty().add(Sort.class, sort)); - setArgs.add(setVar); - if (i > 0) { - KLabel ceil = KLabel(KLabels.ML_CEIL.name(), sort, sortParam); - setArgsCeil = KApply(andLabel, setArgsCeil, KApply(ceil, setVar)); - } - } - Seq setArgsSeq = JavaConverters.iterableAsScalaIterable(setArgs).toSeq(); - - KLabel equalsLabel = KLabel(KLabels.ML_EQUALS.name(), Sorts.Bool(), sortParam); - Rule ceilMapRule = - Rule( - KRewrite( - KApply(ceilMapLabel, - KApply(concatProd.klabel().get(), - KApply(elementProd.klabel().get(), - setArgsSeq, - Att.empty() - ), - restMapSet - ) - ) - , - KApply(andLabel, - KApply(equalsLabel, - KApply(prod.klabel().get(), - setArgs.get(0), - restMapSet - ), - BooleanUtils.FALSE - ), - setArgsCeil - ) - ) - , BooleanUtils.TRUE - , BooleanUtils.TRUE - , Att.empty().add(Att.SIMPLIFICATION()) - ); - rules.add(ceilMapRule); - } - - static boolean hasHookValue(Att atts, String value) { - return atts.contains(Att.HOOK()) && - atts.get(Att.HOOK()).equals(value); - } - - private void genFunctionalAxiom(Production prod, StringBuilder sb) { - // exists y . f(...) = y - Production finalProd = prod; - functionalPattern(prod, () -> applyPattern(finalProd, "K", sb), sb); - sb.append(" [functional{}()] // functional\n"); - } - - private void genNoConfusionAxioms(Production prod, Set> noConfusion, - SetMultimap functionRulesMap, StringBuilder sb) { - // c(x1,x2,...) /\ c(y1,y2,...) -> c(x1/\y2,x2/\y2,...) - if (prod.arity() > 0) { - sb.append(" axiom"); - convertParams(prod.klabel(), false, sb); - sb.append("\\implies{"); - convert(prod.sort(), prod, sb); - sb.append("} (\\and{"); - convert(prod.sort(), prod, sb); - sb.append("} ("); - applyPattern(prod, "X", sb); - sb.append(", "); - applyPattern(prod, "Y", sb); - sb.append("), "); - convert(prod.klabel().get(), prod, sb); - sb.append("("); - String conn = ""; - for (int i = 0; i < prod.arity(); i++) { - sb.append(conn); - sb.append("\\and{"); - convert(prod.nonterminal(i).sort(), prod, sb); - sb.append("} (X").append(i).append(":"); - convert(prod.nonterminal(i).sort(), prod, sb); - sb.append(", Y").append(i).append(":"); - convert(prod.nonterminal(i).sort(), prod, sb); - sb.append(")"); - conn = ", "; - } - sb.append(")) [constructor{}()] // no confusion same constructor\n"); - } - for (Production prod2 : iterable(module.productionsForSort().apply(prod.sort().head()).toSeq().sorted(Production.ord()))) { - // !(cx(x1,x2,...) /\ cy(y1,y2,...)) - if (prod2.klabel().isEmpty() || noConfusion.contains(Tuple2.apply(prod, prod2)) || prod.equals(prod2) - || !isConstructor(prod2, functionRulesMap) || isBuiltinProduction(prod2)) { - // TODO (traiansf): add no confusion axioms for constructor vs inj. - continue; - } - noConfusion.add(Tuple2.apply(prod, prod2)); - noConfusion.add(Tuple2.apply(prod2, prod)); - sb.append(" axiom"); - convertParams(prod.klabel(), false, sb); - sb.append("\\not{"); - convert(prod.sort(), prod, sb); - sb.append("} (\\and{"); - convert(prod.sort(), prod, sb); - sb.append("} ("); - applyPattern(prod, "X", sb); - sb.append(", "); - applyPattern(prod2, "Y", sb); - sb.append(")) [constructor{}()] // no confusion different constructors\n"); + Seq setArgsSeq = JavaConverters.iterableAsScalaIterable(setArgs).toSeq(); + + KLabel equalsLabel = KLabel(KLabels.ML_EQUALS.name(), Sorts.Bool(), sortParam); + Rule ceilMapRule = + Rule( + KRewrite( + KApply( + ceilMapLabel, + KApply( + concatProd.klabel().get(), + KApply(elementProd.klabel().get(), setArgsSeq, Att.empty()), + restMapSet)), + KApply( + andLabel, + KApply( + equalsLabel, + KApply(prod.klabel().get(), setArgs.get(0), restMapSet), + BooleanUtils.FALSE), + setArgsCeil)), + BooleanUtils.TRUE, + BooleanUtils.TRUE, + Att.empty().add(Att.SIMPLIFICATION())); + rules.add(ceilMapRule); + } + + static boolean hasHookValue(Att atts, String value) { + return atts.contains(Att.HOOK()) && atts.get(Att.HOOK()).equals(value); + } + + private void genFunctionalAxiom(Production prod, StringBuilder sb) { + // exists y . f(...) = y + Production finalProd = prod; + functionalPattern(prod, () -> applyPattern(finalProd, "K", sb), sb); + sb.append(" [functional{}()] // functional\n"); + } + + private void genNoConfusionAxioms( + Production prod, + Set> noConfusion, + SetMultimap functionRulesMap, + StringBuilder sb) { + // c(x1,x2,...) /\ c(y1,y2,...) -> c(x1/\y2,x2/\y2,...) + if (prod.arity() > 0) { + sb.append(" axiom"); + convertParams(prod.klabel(), false, sb); + sb.append("\\implies{"); + convert(prod.sort(), prod, sb); + sb.append("} (\\and{"); + convert(prod.sort(), prod, sb); + sb.append("} ("); + applyPattern(prod, "X", sb); + sb.append(", "); + applyPattern(prod, "Y", sb); + sb.append("), "); + convert(prod.klabel().get(), prod, sb); + sb.append("("); + String conn = ""; + for (int i = 0; i < prod.arity(); i++) { + sb.append(conn); + sb.append("\\and{"); + convert(prod.nonterminal(i).sort(), prod, sb); + sb.append("} (X").append(i).append(":"); + convert(prod.nonterminal(i).sort(), prod, sb); + sb.append(", Y").append(i).append(":"); + convert(prod.nonterminal(i).sort(), prod, sb); + sb.append(")"); + conn = ", "; + } + sb.append(")) [constructor{}()] // no confusion same constructor\n"); + } + for (Production prod2 : + iterable( + module + .productionsForSort() + .apply(prod.sort().head()) + .toSeq() + .sorted(Production.ord()))) { + // !(cx(x1,x2,...) /\ cy(y1,y2,...)) + if (prod2.klabel().isEmpty() + || noConfusion.contains(Tuple2.apply(prod, prod2)) + || prod.equals(prod2) + || !isConstructor(prod2, functionRulesMap) + || isBuiltinProduction(prod2)) { + // TODO (traiansf): add no confusion axioms for constructor vs inj. + continue; + } + noConfusion.add(Tuple2.apply(prod, prod2)); + noConfusion.add(Tuple2.apply(prod2, prod)); + sb.append(" axiom"); + convertParams(prod.klabel(), false, sb); + sb.append("\\not{"); + convert(prod.sort(), prod, sb); + sb.append("} (\\and{"); + convert(prod.sort(), prod, sb); + sb.append("} ("); + applyPattern(prod, "X", sb); + sb.append(", "); + applyPattern(prod2, "Y", sb); + sb.append(")) [constructor{}()] // no confusion different constructors\n"); + } + } + + public static int getPriority(Att att) { + if (att.contains(Att.PRIORITY())) { + try { + return Integer.parseInt(att.get(Att.PRIORITY())); + } catch (NumberFormatException e) { + throw KEMException.compilerError( + "Invalid value for priority attribute: " + + att.get(Att.PRIORITY()) + + ". Must be an integer.", + e); + } + } else if (att.contains(Att.OWISE())) { + return 200; + } + return 50; + } + + private void genNoJunkAxiom(Sort sort, StringBuilder sb) { + StringBuilder sbTemp = new StringBuilder(); + sbTemp.append(" axiom{} "); + boolean hasToken = false; + int numTerms = 0; + sbTemp.append("\\or{"); + convert(sort, sbTemp); + sbTemp.append("} ("); + for (Production prod : + iterable( + mutable(module.productionsForSort()) + .getOrDefault(sort.head(), Set()) + .toSeq() + .sorted(Production.ord()))) { + if (isFunction(prod) || prod.isSubsort() || isBuiltinProduction(prod)) { + continue; + } + if (prod.klabel().isEmpty() + && !((prod.att().contains(Att.TOKEN()) && !hasToken) || prod.isSubsort())) { + continue; + } + numTerms++; + if (prod.att().contains(Att.TOKEN()) && !hasToken) { + convertTokenProd(sort, sbTemp); + hasToken = true; + } else if (prod.klabel().isDefined()) { + for (int i = 0; i < prod.arity(); i++) { + sbTemp.append("\\exists{"); + convert(sort, sbTemp); + sbTemp.append("} (X").append(i).append(":"); + convert(prod.nonterminal(i).sort(), prod, sbTemp); + sbTemp.append(", "); + } + convert(prod.klabel().get(), prod, sbTemp); + sbTemp.append("("); + String conn = ""; + for (int i = 0; i < prod.arity(); i++) { + sbTemp.append(conn).append("X").append(i).append(":"); + convert(prod.nonterminal(i).sort(), prod, sbTemp); + conn = ", "; } - } - - public static int getPriority(Att att) { - if (att.contains(Att.PRIORITY())) { - try { - return Integer.parseInt(att.get(Att.PRIORITY())); - } catch (NumberFormatException e) { - throw KEMException.compilerError("Invalid value for priority attribute: " + att.get(Att.PRIORITY()) + ". Must be an integer.", e); - } - } else if (att.contains(Att.OWISE())) { - return 200; + sbTemp.append(")"); + for (int i = 0; i < prod.arity(); i++) { + sbTemp.append(")"); } - return 50; + } + sbTemp.append(", "); } - - private void genNoJunkAxiom(Sort sort, StringBuilder sb) { - StringBuilder sbTemp = new StringBuilder(); - sbTemp.append(" axiom{} "); - boolean hasToken = false; - int numTerms = 0; - sbTemp.append("\\or{"); + for (Sort s : iterable(module.sortedAllSorts())) { + if (module.subsorts().lessThan(s, sort) && !sort.equals(Sorts.K())) { + numTerms++; + sbTemp.append("\\exists{"); convert(sort, sbTemp); - sbTemp.append("} ("); - for (Production prod : iterable(mutable(module.productionsForSort()).getOrDefault(sort.head(), Set()).toSeq().sorted(Production.ord()))) { - if (isFunction(prod) || prod.isSubsort() || isBuiltinProduction(prod)) { - continue; - } - if (prod.klabel().isEmpty() && !((prod.att().contains(Att.TOKEN()) && !hasToken) || prod.isSubsort())) { - continue; - } - numTerms++; - if (prod.att().contains(Att.TOKEN()) && !hasToken) { - convertTokenProd(sort, sbTemp); - hasToken = true; - } else if (prod.klabel().isDefined()) { - for (int i = 0; i < prod.arity(); i++) { - sbTemp.append("\\exists{"); - convert(sort, sbTemp); - sbTemp.append("} (X").append(i).append(":"); - convert(prod.nonterminal(i).sort(), prod, sbTemp); - sbTemp.append(", "); - } - convert(prod.klabel().get(), prod, sbTemp); - sbTemp.append("("); - String conn = ""; - for (int i = 0; i < prod.arity(); i++) { - sbTemp.append(conn).append("X").append(i).append(":"); - convert(prod.nonterminal(i).sort(), prod, sbTemp); - conn = ", "; - } - sbTemp.append(")"); - for (int i = 0; i < prod.arity(); i++) { - sbTemp.append(")"); - } - } - sbTemp.append(", "); - } - for (Sort s : iterable(module.sortedAllSorts())) { - if (module.subsorts().lessThan(s, sort) && !sort.equals(Sorts.K())) { - numTerms++; - sbTemp.append("\\exists{"); - convert(sort, sbTemp); - sbTemp.append("} (Val:"); - convert(s, sbTemp); - sbTemp.append(", inj{"); - convert(s, sbTemp); - sbTemp.append(", "); - convert(sort, sbTemp); - sbTemp.append("} (Val:"); - convert(s, sbTemp); - sbTemp.append("))"); - sbTemp.append(", "); - } - } - Att sortAtt = module.sortAttributesFor().get(sort.head()).getOrElse(() -> KORE.Att()); - if (!hasToken && sortAtt.contains(Att.TOKEN())) { - numTerms++; - convertTokenProd(sort, sbTemp); - sbTemp.append(", "); - hasToken = true; - } - sbTemp.append("\\bottom{"); + sbTemp.append("} (Val:"); + convert(s, sbTemp); + sbTemp.append(", inj{"); + convert(s, sbTemp); + sbTemp.append(", "); convert(sort, sbTemp); - sbTemp.append("}()) [constructor{}()] // no junk"); - if (hasToken && !METAVAR) { - sbTemp.append(" (TODO: fix bug with \\dv)"); + sbTemp.append("} (Val:"); + convert(s, sbTemp); + sbTemp.append("))"); + sbTemp.append(", "); + } + } + Att sortAtt = module.sortAttributesFor().get(sort.head()).getOrElse(() -> KORE.Att()); + if (!hasToken && sortAtt.contains(Att.TOKEN())) { + numTerms++; + convertTokenProd(sort, sbTemp); + sbTemp.append(", "); + hasToken = true; + } + sbTemp.append("\\bottom{"); + convert(sort, sbTemp); + sbTemp.append("}()) [constructor{}()] // no junk"); + if (hasToken && !METAVAR) { + sbTemp.append(" (TODO: fix bug with \\dv)"); + } + sbTemp.append("\n"); + + // If there are no terms, then we don't need to generate the axiom. + if (numTerms != 0) { + sb.append(sbTemp); + } + } + + private void genOverloadedAxiom(Production lesser, Production greater, StringBuilder sb) { + sb.append(" axiom{R} \\equals{"); + convert(greater.sort(), greater, sb); + sb.append(", R} ("); + convert(greater.klabel().get(), greater, sb); + sb.append("("); + String conn = ""; + for (int i = 0; i < greater.nonterminals().size(); i++) { + sb.append(conn); + if (greater.nonterminal(i).sort().equals(lesser.nonterminal(i).sort())) { + sb.append("K").append(i).append(":"); + convert(greater.nonterminal(i).sort(), greater, sb); + } else { + sb.append("inj{"); + convert(lesser.nonterminal(i).sort(), lesser, sb); + sb.append(", "); + convert(greater.nonterminal(i).sort(), greater, sb); + sb.append("} (K").append(i).append(":"); + convert(lesser.nonterminal(i).sort(), lesser, sb); + sb.append(")"); + } + conn = ","; + } + sb.append("), inj{"); + convert(lesser.sort(), lesser, sb); + sb.append(", "); + convert(greater.sort(), greater, sb); + sb.append("} ("); + convert(lesser.klabel().get(), lesser, sb); + sb.append("("); + conn = ""; + for (int i = 0; i < lesser.nonterminals().size(); i++) { + sb.append(conn); + sb.append("K").append(i).append(":"); + convert(lesser.nonterminal(i).sort(), lesser, sb); + conn = ","; + } + sb.append("))) [overload{}("); + convert(greater.klabel().get(), greater, sb); + sb.append("(), "); + convert(lesser.klabel().get(), lesser, sb); + sb.append("())] // overloaded production\n"); + } + + private boolean isRealHook(Att att) { + String hook = att.get(Att.HOOK()); + if (hook.startsWith("ARRAY.")) { + return false; + } + if (options.hookNamespaces.stream().anyMatch(ns -> hook.startsWith(ns + "."))) { + return true; + } + return Hooks.namespaces.stream().anyMatch(ns -> hook.startsWith(ns + ".")); + } + + private static boolean isBuiltinProduction(Production prod) { + return prod.klabel().nonEmpty() && ConstructorChecks.isBuiltinLabel(prod.klabel().get()); + } + + public String convertSpecificationModule( + Module definition, Module spec, SentenceType defaultSentenceType, StringBuilder sb) { + SentenceType sentenceType = getSentenceType(spec.att()).orElse(defaultSentenceType); + sb.setLength(0); // reset string writer + Sort topCellSort = Sorts.GeneratedTopCell(); + String topCellSortStr = getSortStr(topCellSort); + HashMap considerSource = new HashMap<>(); + considerSource.put(Att.SOURCE(), true); + convert( + considerSource, + Att.empty().add(Source.class, spec.att().get(Source.class)), + sb, + null, + null); + sb.append("\n"); + sb.append("module "); + convert(spec.name(), sb); + sb.append("\n\n// imports\n"); + sb.append("import "); + convert(definition.name(), sb); + sb.append(" []\n"); + sb.append("\n\n// claims\n"); + HashMap consideredAttributes = new HashMap<>(); + consideredAttributes.put(Att.PRIORITY(), true); + consideredAttributes.put(Att.LABEL(), true); + consideredAttributes.put(Att.GROUP(), true); + consideredAttributes.put(Att.SOURCE(), true); + consideredAttributes.put(Att.LOCATION(), true); + consideredAttributes.put(Att.UNIQUE_ID(), true); + + for (Sentence sentence : iterable(spec.sentencesExcept(definition))) { + if (sentence instanceof Claim + || (sentence instanceof Rule && sentence.att().contains(Att.SIMPLIFICATION()))) { + convertRule( + (RuleOrClaim) sentence, + 0, + false, + topCellSortStr, + consideredAttributes, + HashMultimap.create(), + new HashMap<>(), + ArrayListMultimap.create(), + sentenceType, + sb); + } + } + sb.append("endmodule "); + convert(consideredAttributes, spec.att().remove(Att.DIGEST()), sb, null, null); + sb.append("\n"); + return sb.toString(); + } + + private Optional getSentenceType(Att att) { + if (att.contains(Att.ONE_PATH())) { + return Optional.of(SentenceType.ONE_PATH); + } else if (att.contains(Att.ALL_PATH())) { + return Optional.of(SentenceType.ALL_PATH); + } + return Optional.empty(); + } + + private RuleInfo getRuleInfo(RuleOrClaim rule, boolean heatCoolEq, String topCellSortStr) { + boolean equation = false; + boolean owise = false; + boolean kore = rule.att().contains(Att.KORE()); + boolean ceil = false; + Production production = null; + Sort productionSort = null; + String productionSortStr = null; + List productionSorts = null; + KLabel productionLabel = null; + List leftChildren = null; + + K left = RewriteToTop.toLeft(rule.body()); + K leftPattern = left; + while (leftPattern instanceof KAs) { + leftPattern = ((KAs) leftPattern).pattern(); + } + if (leftPattern instanceof KApply) { + production = production((KApply) leftPattern, true); + productionSort = production.sort(); + productionSortStr = getSortStr(productionSort); + productionSorts = + stream(production.items()) + .filter(i -> i instanceof NonTerminal) + .map(i -> (NonTerminal) i) + .map(NonTerminal::sort) + .collect(Collectors.toList()); + productionLabel = production.klabel().get(); + if (productionLabel.name().equals("#Ceil") && rule.att().contains(Att.SIMPLIFICATION())) { + ceil = true; + } + if (isFunction(production) + || rule.att().contains(Att.SIMPLIFICATION()) + || rule.att().contains(Att.ANYWHERE()) && !kore) { + leftChildren = ((KApply) leftPattern).items(); + equation = true; + } else if ((rule.att().contains(Att.HEAT()) || rule.att().contains(Att.COOL())) + && heatCoolEq) { + equation = true; + productionSortStr = topCellSortStr; + } + owise = rule.att().contains(Att.OWISE()); + } + + return new RuleInfo( + equation, + owise, + kore, + ceil, + production, + productionSortStr, + productionSorts, + productionLabel, + leftChildren); + } + + private void convertRule( + RuleOrClaim rule, + int ruleIndex, + boolean heatCoolEq, + String topCellSortStr, + Map consideredAttributes, + SetMultimap functionRules, + Map priorityToPreviousGroup, + ListMultimap priorityToAlias, + SentenceType defaultSentenceType, + StringBuilder sb) { + SentenceType sentenceType = getSentenceType(rule.att()).orElse(defaultSentenceType); + // injections should already be present, but this is an ugly hack to get around the + // cache persistence issue that means that Sort attributes on k terms might not be present. + rule = addSortInjections.addInjections(rule); + Set existentials = getExistentials(rule); + ConstructorChecks constructorChecks = new ConstructorChecks(module); + K left = RewriteToTop.toLeft(rule.body()); + K requires = rule.requires(); + K right = RewriteToTop.toRight(rule.body()); + K ensures = rule.ensures(); + boolean constructorBased = constructorChecks.isConstructorBased(left); + RuleInfo ruleInfo = getRuleInfo(rule, heatCoolEq, topCellSortStr); + sb.append("// "); + sb.append(rule); + sb.append("\n"); + if (ruleInfo.isCeil && options.disableCeilSimplificationRules) { + return; + } + Set freeVariables = collectLHSFreeVariables(requires, left); + Map freeVarsMap = + freeVariables.stream().collect(Collectors.toMap(KVariable::name, Function.identity())); + if (ruleInfo.isEquation) { + assertNoExistentials(rule, existentials); + if (rule instanceof Claim) { + sb.append(" claim{R"); + if (kem != null) // TODO: remove once + // https://github.com/runtimeverification/haskell-backend/issues/3010 is + // implemented + kem.registerCompilerWarning( + KException.ExceptionType.FUTURE_ERROR, + "Functional claims not yet supported." + + " https://github.com/runtimeverification/haskell-backend/issues/3010", + rule); + } else { + sb.append(" axiom{R"); + } + Option sortParamsWrapper = rule.att().getOption(Att.SORT_PARAMS(), Sort.class); + Option> sortParams = + sortParamsWrapper.map( + s -> stream(s.params()).map(sort -> sort.name()).collect(Collectors.toSet())); + if (sortParams.nonEmpty()) { + for (Object sortParamName : sortParams.get()) sb.append("," + sortParamName); + } + sb.append("} "); + if (ruleInfo.isOwise) { + Set varNames = + freeVariables.stream().map(KVariable::name).collect(Collectors.toSet()); + sb.append("\\implies{R} (\n \\and{R} (\n \\not{R} (\n "); + for (Rule notMatching : + RefreshRules.refresh(functionRules.get(ruleInfo.productionLabel), varNames)) { + if (ignoreSideConditionsForOwise(notMatching)) { + continue; + } + sb.append("\\or{R} (\n"); + K notMatchingRequires = notMatching.requires(); + K notMatchingLeft = RewriteToTop.toLeft(notMatching.body()); + Set vars = collectLHSFreeVariables(notMatchingRequires, notMatchingLeft); + sb.append(" "); + for (KVariable var : vars) { + sb.append("\\exists{R} ("); + convert((K) var, sb); + sb.append(",\n "); + } + sb.append(" \\and{R} ("); + sb.append("\n "); + convertSideCondition(notMatchingRequires, sb); + sb.append(",\n "); + + assert notMatchingLeft instanceof KApply + : "expecting KApply but got " + notMatchingLeft.getClass(); + List notMatchingChildren = ((KApply) notMatchingLeft).items(); + assert notMatchingChildren.size() == ruleInfo.leftChildren.size() + : "assuming function with fixed arity"; + for (int childIdx = 0; childIdx < ruleInfo.leftChildren.size(); childIdx++) { + sb.append("\\and{R} ("); + sb.append("\n "); + sb.append("\\in{"); + Sort childSort = ruleInfo.prodChildrenSorts.get(childIdx); + convert(childSort, ruleInfo.production.params(), sb); + sb.append(", R} ("); + sb.append("\n "); + sb.append("X").append(childIdx).append(":"); + convert(childSort, ruleInfo.production.params(), sb); + sb.append(",\n "); + convert(notMatchingChildren.get(childIdx), sb); + sb.append("\n ),"); + } + sb.append("\n \\top{R} ()"); + sb.append("\n "); + for (int childIdx = 0; childIdx < ruleInfo.leftChildren.size(); childIdx++) { + sb.append(')'); + } + sb.append("\n )"); + for (KVariable ignored : vars) { + sb.append(")"); + } + sb.append(",\n "); } - sbTemp.append("\n"); - - // If there are no terms, then we don't need to generate the axiom. - if (numTerms != 0) { - sb.append(sbTemp); + sb.append("\\bottom{R}()"); + sb.append("\n "); + for (Rule notMatching : functionRules.get(ruleInfo.productionLabel)) { + if (ignoreSideConditionsForOwise(notMatching)) { + continue; + } + sb.append(")"); } - } + sb.append("\n ),\n \\and{R}(\n "); + convertSideCondition(requires, sb); + sb.append(",\n "); - private void genOverloadedAxiom(Production lesser, Production greater, StringBuilder sb) { - sb.append(" axiom{R} \\equals{"); - convert(greater.sort(), greater, sb); - sb.append(", R} ("); - convert(greater.klabel().get(), greater, sb); + for (int childIdx = 0; childIdx < ruleInfo.leftChildren.size(); childIdx++) { + sb.append("\\and{R} ("); + sb.append("\n "); + sb.append("\\in{"); + Sort childSort = ruleInfo.prodChildrenSorts.get(childIdx); + convert(childSort, ruleInfo.production.params(), sb); + sb.append(", R} ("); + sb.append("\n "); + sb.append("X").append(childIdx).append(":"); + convert(childSort, ruleInfo.production.params(), sb); + sb.append(",\n "); + convert(ruleInfo.leftChildren.get(childIdx), sb); + sb.append("\n ),"); + } + sb.append("\n \\top{R} ()"); + sb.append("\n "); + for (int childIdx = 0; childIdx < ruleInfo.leftChildren.size(); childIdx++) { + sb.append(')'); + } + + sb.append("\n )),\n \\equals{"); + sb.append(ruleInfo.productionSortStr); + sb.append(",R} (\n "); + convert(ruleInfo.productionLabel, sb); sb.append("("); String conn = ""; - for (int i = 0; i < greater.nonterminals().size(); i++) { - sb.append(conn); - if (greater.nonterminal(i).sort().equals(lesser.nonterminal(i).sort())) { - sb.append("K").append(i).append(":"); - convert(greater.nonterminal(i).sort(), greater, sb); - } else { - sb.append("inj{"); - convert(lesser.nonterminal(i).sort(), lesser, sb); - sb.append(", "); - convert(greater.nonterminal(i).sort(), greater, sb); - sb.append("} (K").append(i).append(":"); - convert(lesser.nonterminal(i).sort(), lesser, sb); - sb.append(")"); - } - conn = ","; + for (int childIdx = 0; childIdx < ruleInfo.leftChildren.size(); childIdx++) { + sb.append(conn).append("X").append(childIdx).append(":"); + Sort childSort = ruleInfo.prodChildrenSorts.get(childIdx); + convert(childSort, ruleInfo.production.params(), sb); + conn = ","; } - sb.append("), inj{"); - convert(lesser.sort(), lesser, sb); - sb.append(", "); - convert(greater.sort(), greater, sb); - sb.append("} ("); - convert(lesser.klabel().get(), lesser, sb); + sb.append(")"); + sb.append(",\n \\and{"); + sb.append(ruleInfo.productionSortStr); + sb.append("} (\n "); + convert(right, sb); + sb.append(",\n "); + convertSideCondition(ensures, ruleInfo.productionSortStr, sb); + sb.append(")))\n "); + convert(consideredAttributes, rule.att(), sb, freeVarsMap, rule); + sb.append("\n\n"); + } else if (rule.att().contains(Att.SIMPLIFICATION()) || rule instanceof Claim) { + sb.append("\\implies{R} (\n "); + convertSideCondition(requires, sb); + sb.append(",\n \\equals{"); + sb.append(ruleInfo.productionSortStr); + sb.append(",R} (\n "); + convert(left, sb); + sb.append(",\n \\and{"); + sb.append(ruleInfo.productionSortStr); + sb.append("} (\n "); + convert(right, sb); + sb.append(",\n "); + convertSideCondition(ensures, ruleInfo.productionSortStr, sb); + sb.append(")))\n "); + convert(consideredAttributes, rule.att(), sb, freeVarsMap, rule); + sb.append("\n\n"); + + } else { + sb.append("\\implies{R} (\n \\and{R}(\n "); + convertSideCondition(requires, sb); + sb.append(",\n "); + + for (int childIdx = 0; childIdx < ruleInfo.leftChildren.size(); childIdx++) { + sb.append("\\and{R} ("); + sb.append("\n "); + sb.append("\\in{"); + Sort childSort = ruleInfo.prodChildrenSorts.get(childIdx); + convert(childSort, ruleInfo.production.params(), sb); + sb.append(", R} ("); + sb.append("\n "); + sb.append("X").append(childIdx).append(":"); + convert(childSort, ruleInfo.production.params(), sb); + sb.append(",\n "); + convert(ruleInfo.leftChildren.get(childIdx), sb); + sb.append("\n ),"); + } + sb.append("\n \\top{R} ()"); + sb.append("\n "); + for (int childIdx = 0; childIdx < ruleInfo.leftChildren.size(); childIdx++) { + sb.append(')'); + } + sb.append("),\n \\equals{"); + sb.append(ruleInfo.productionSortStr); + sb.append(",R} (\n "); + convert(ruleInfo.productionLabel, sb); sb.append("("); - conn = ""; - for (int i = 0; i < lesser.nonterminals().size(); i++) { - sb.append(conn); - sb.append("K").append(i).append(":"); - convert(lesser.nonterminal(i).sort(), lesser, sb); - conn = ","; - } - sb.append("))) [overload{}("); - convert(greater.klabel().get(), greater, sb); - sb.append("(), "); - convert(lesser.klabel().get(), lesser, sb); - sb.append("())] // overloaded production\n"); - } - - private boolean isRealHook(Att att) { - String hook = att.get(Att.HOOK()); - if (hook.startsWith("ARRAY.")) { - return false; - } - if (options.hookNamespaces.stream().anyMatch(ns -> hook.startsWith(ns + "."))) { - return true; - } - return Hooks.namespaces.stream().anyMatch(ns -> hook.startsWith(ns + ".")); - } - - private static boolean isBuiltinProduction(Production prod) { - return prod.klabel().nonEmpty() && ConstructorChecks.isBuiltinLabel(prod.klabel().get()); - } - - public String convertSpecificationModule(Module definition, Module spec, SentenceType defaultSentenceType, StringBuilder sb) { - SentenceType sentenceType = getSentenceType(spec.att()).orElse(defaultSentenceType); - sb.setLength(0); // reset string writer - Sort topCellSort = Sorts.GeneratedTopCell(); - String topCellSortStr = getSortStr(topCellSort); - HashMap considerSource = new HashMap<>(); - considerSource.put(Att.SOURCE(), true); - convert(considerSource, Att.empty().add(Source.class, spec.att().get(Source.class)), sb, null, null); - sb.append("\n"); - sb.append("module "); - convert(spec.name(), sb); - sb.append("\n\n// imports\n"); - sb.append("import "); - convert(definition.name(), sb); - sb.append(" []\n"); - sb.append("\n\n// claims\n"); - HashMap consideredAttributes = new HashMap<>(); - consideredAttributes.put(Att.PRIORITY(), true); - consideredAttributes.put(Att.LABEL(), true); - consideredAttributes.put(Att.GROUP(), true); - consideredAttributes.put(Att.SOURCE(), true); - consideredAttributes.put(Att.LOCATION(), true); - consideredAttributes.put(Att.UNIQUE_ID(), true); - - for (Sentence sentence : iterable(spec.sentencesExcept(definition))) { - if (sentence instanceof Claim || (sentence instanceof Rule && sentence.att().contains(Att.SIMPLIFICATION()))) { - convertRule((RuleOrClaim) sentence, 0, false, topCellSortStr, - consideredAttributes, HashMultimap.create(), new HashMap<>(), ArrayListMultimap.create(), - sentenceType, sb); - } + String conn = ""; + for (int childIdx = 0; childIdx < ruleInfo.leftChildren.size(); childIdx++) { + sb.append(conn).append("X").append(childIdx).append(":"); + Sort childSort = ruleInfo.prodChildrenSorts.get(childIdx); + convert(childSort, ruleInfo.production.params(), sb); + conn = ","; } - sb.append("endmodule "); - convert(consideredAttributes, spec.att().remove(Att.DIGEST()), sb, null, null); - sb.append("\n"); - return sb.toString(); - } + sb.append(")"); + sb.append(",\n \\and{"); + sb.append(ruleInfo.productionSortStr); + sb.append("} (\n "); + convert(right, sb); + sb.append(",\n "); + convertSideCondition(ensures, ruleInfo.productionSortStr, sb); + sb.append(")))\n "); + convert(consideredAttributes, rule.att(), sb, freeVarsMap, rule); + sb.append("\n\n"); + } + } else if (ruleInfo.isKore) { + assertNoExistentials(rule, existentials); + if (rule instanceof Claim) { + sb.append(" claim{} "); + } else { + sb.append(" axiom{} "); + } + convert(left, sb); + sb.append("\n "); + convert(consideredAttributes, rule.att(), sb, freeVarsMap, rule); + sb.append("\n\n"); + } else if (!ExpandMacros.isMacro(rule)) { + // generate rule LHS + if (!(rule instanceof Claim)) { + // LHS for semantics rules + String ruleAliasName = String.format("rule%dLHS", ruleIndex); + int priority = getPriority(rule.att()); + List freeVars = new ArrayList<>(freeVariables); + Comparator compareByName = + (KVariable v1, KVariable v2) -> v1.name().compareTo(v2.name()); + java.util.Collections.sort(freeVars, compareByName); - private Optional getSentenceType(Att att) { - if (att.contains(Att.ONE_PATH())) { - return Optional.of(SentenceType.ONE_PATH); - } else if (att.contains(Att.ALL_PATH())) { - return Optional.of(SentenceType.ALL_PATH); - } - return Optional.empty(); - } - - private RuleInfo getRuleInfo(RuleOrClaim rule, boolean heatCoolEq, String topCellSortStr) { - boolean equation = false; - boolean owise = false; - boolean kore = rule.att().contains(Att.KORE()); - boolean ceil = false; - Production production = null; - Sort productionSort = null; - String productionSortStr = null; - List productionSorts = null; - KLabel productionLabel = null; - List leftChildren = null; - - K left = RewriteToTop.toLeft(rule.body()); - K leftPattern = left; - while (leftPattern instanceof KAs) { - leftPattern = ((KAs)leftPattern).pattern(); - } - if (leftPattern instanceof KApply) { - production = production((KApply) leftPattern, true); - productionSort = production.sort(); - productionSortStr = getSortStr(productionSort); - productionSorts = stream(production.items()) - .filter(i -> i instanceof NonTerminal) - .map(i -> (NonTerminal) i) - .map(NonTerminal::sort).collect(Collectors.toList()); - productionLabel = production.klabel().get(); - if (productionLabel.name().equals("#Ceil") && rule.att().contains(Att.SIMPLIFICATION())) { - ceil = true; - } - if (isFunction(production) || rule.att().contains(Att.SIMPLIFICATION()) || rule.att().contains(Att.ANYWHERE()) && !kore) { - leftChildren = ((KApply) leftPattern).items(); - equation = true; - } else if ((rule.att().contains(Att.HEAT()) || rule.att().contains(Att.COOL())) && heatCoolEq) { - equation = true; - productionSortStr = topCellSortStr; - } - owise = rule.att().contains(Att.OWISE()); + if (options.enableKoreAntileft) { + genAliasForSemanticsRuleLHS( + requires, + left, + ruleAliasName, + freeVars, + topCellSortStr, + priority, + priorityToAlias, + sb); + sb.append("\n"); } - return new RuleInfo(equation, owise, kore, ceil, production, - productionSortStr, productionSorts, productionLabel, leftChildren); - } - - private void convertRule(RuleOrClaim rule, int ruleIndex, boolean heatCoolEq, String topCellSortStr, - Map consideredAttributes, SetMultimap functionRules, - Map priorityToPreviousGroup, - ListMultimap priorityToAlias, - SentenceType defaultSentenceType, StringBuilder sb) { - SentenceType sentenceType = getSentenceType(rule.att()).orElse(defaultSentenceType); - // injections should already be present, but this is an ugly hack to get around the - // cache persistence issue that means that Sort attributes on k terms might not be present. - rule = addSortInjections.addInjections(rule); - Set existentials = getExistentials(rule); - ConstructorChecks constructorChecks = new ConstructorChecks(module); - K left = RewriteToTop.toLeft(rule.body()); - K requires = rule.requires(); - K right = RewriteToTop.toRight(rule.body()); - K ensures = rule.ensures(); - boolean constructorBased = constructorChecks.isConstructorBased(left); - RuleInfo ruleInfo = getRuleInfo(rule, heatCoolEq, topCellSortStr); - sb.append("// "); - sb.append(rule); - sb.append("\n"); - if (ruleInfo.isCeil && options.disableCeilSimplificationRules) { - return; - } - Set freeVariables = collectLHSFreeVariables(requires, left); - Map freeVarsMap = freeVariables - .stream().collect(Collectors.toMap(KVariable::name, Function.identity())); - if (ruleInfo.isEquation) { - assertNoExistentials(rule, existentials); - if (rule instanceof Claim) { - sb.append(" claim{R"); - if (kem != null) // TODO: remove once https://github.com/runtimeverification/haskell-backend/issues/3010 is implemented - kem.registerCompilerWarning(KException.ExceptionType.FUTURE_ERROR, "Functional claims not yet supported. https://github.com/runtimeverification/haskell-backend/issues/3010", rule); - } else { - sb.append(" axiom{R"); - } - Option sortParamsWrapper = rule.att().getOption(Att.SORT_PARAMS(), Sort.class); - Option> sortParams = sortParamsWrapper.map(s -> stream(s.params()).map(sort -> sort.name()).collect(Collectors.toSet())); - if (sortParams.nonEmpty()) { - for (Object sortParamName : sortParams.get()) - sb.append("," + sortParamName); - } - sb.append("} "); - if (ruleInfo.isOwise) { - Set varNames = freeVariables - .stream().map(KVariable::name).collect(Collectors.toSet()); - sb.append("\\implies{R} (\n \\and{R} (\n \\not{R} (\n "); - for (Rule notMatching : RefreshRules.refresh(functionRules.get(ruleInfo.productionLabel), varNames)) { - if (ignoreSideConditionsForOwise(notMatching)) { - continue; - } - sb.append("\\or{R} (\n"); - K notMatchingRequires = notMatching.requires(); - K notMatchingLeft = RewriteToTop.toLeft(notMatching.body()); - Set vars = collectLHSFreeVariables(notMatchingRequires, notMatchingLeft); - sb.append(" "); - for (KVariable var : vars) { - sb.append("\\exists{R} ("); - convert((K)var, sb); - sb.append(",\n "); - } - sb.append(" \\and{R} ("); - sb.append("\n "); - convertSideCondition(notMatchingRequires, sb); - sb.append(",\n "); - - assert notMatchingLeft instanceof KApply : "expecting KApply but got " + notMatchingLeft.getClass(); - List notMatchingChildren = ((KApply) notMatchingLeft).items(); - assert notMatchingChildren.size() == ruleInfo.leftChildren.size() : "assuming function with fixed arity"; - for (int childIdx = 0; childIdx < ruleInfo.leftChildren.size(); childIdx++) { - sb.append("\\and{R} ("); - sb.append("\n "); - sb.append("\\in{"); - Sort childSort = ruleInfo.prodChildrenSorts.get(childIdx); - convert(childSort, ruleInfo.production.params(), sb); - sb.append(", R} ("); - sb.append("\n "); - sb.append("X").append(childIdx).append(":"); - convert(childSort, ruleInfo.production.params(), sb); - sb.append(",\n "); - convert(notMatchingChildren.get(childIdx), sb); - sb.append("\n ),"); - } - sb.append("\n \\top{R} ()"); - sb.append("\n "); - for (int childIdx = 0; childIdx < ruleInfo.leftChildren.size(); childIdx++) { - sb.append(')'); - } - sb.append("\n )"); - for (KVariable ignored : vars) { - sb.append(")"); - } - sb.append(",\n "); - } - sb.append("\\bottom{R}()"); - sb.append("\n "); - for (Rule notMatching : functionRules.get(ruleInfo.productionLabel)) { - if (ignoreSideConditionsForOwise(notMatching)) { - continue; - } - sb.append(")"); - } - sb.append("\n ),\n \\and{R}(\n "); - convertSideCondition(requires, sb); - sb.append(",\n "); - - for (int childIdx = 0; childIdx < ruleInfo.leftChildren.size(); childIdx++) { - sb.append("\\and{R} ("); - sb.append("\n "); - sb.append("\\in{"); - Sort childSort = ruleInfo.prodChildrenSorts.get(childIdx); - convert(childSort, ruleInfo.production.params(), sb); - sb.append(", R} ("); - sb.append("\n "); - sb.append("X").append(childIdx).append(":"); - convert(childSort, ruleInfo.production.params(), sb); - sb.append(",\n "); - convert(ruleInfo.leftChildren.get(childIdx), sb); - sb.append("\n ),"); - } - sb.append("\n \\top{R} ()"); - sb.append("\n "); - for (int childIdx = 0; childIdx < ruleInfo.leftChildren.size(); childIdx++) { - sb.append(')'); - } + sb.append(" axiom{} "); + sb.append(String.format("\\rewrites{%s} (\n ", topCellSortStr)); - sb.append("\n )),\n \\equals{"); - sb.append(ruleInfo.productionSortStr); - sb.append(",R} (\n "); - convert(ruleInfo.productionLabel, sb); - sb.append("("); - String conn = ""; - for (int childIdx = 0; childIdx < ruleInfo.leftChildren.size(); childIdx++) { - sb.append(conn).append("X").append(childIdx).append(":"); - Sort childSort = ruleInfo.prodChildrenSorts.get(childIdx); - convert(childSort, ruleInfo.production.params(), sb); - conn = ","; - } - sb.append(")"); - sb.append(",\n \\and{"); - sb.append(ruleInfo.productionSortStr); - sb.append("} (\n "); - convert(right, sb); - sb.append(",\n "); - convertSideCondition(ensures, ruleInfo.productionSortStr, sb); - sb.append(")))\n "); - convert(consideredAttributes, rule.att(), sb, freeVarsMap, rule); - sb.append("\n\n"); - } else if (rule.att().contains(Att.SIMPLIFICATION()) || rule instanceof Claim) { - sb.append("\\implies{R} (\n "); - convertSideCondition(requires, sb); - sb.append(",\n \\equals{"); - sb.append(ruleInfo.productionSortStr); - sb.append(",R} (\n "); - convert(left, sb); - sb.append(",\n \\and{"); - sb.append(ruleInfo.productionSortStr); - sb.append("} (\n "); - convert(right, sb); - sb.append(",\n "); - convertSideCondition(ensures, ruleInfo.productionSortStr, sb); - sb.append(")))\n "); - convert(consideredAttributes, rule.att(), sb, freeVarsMap, rule); - sb.append("\n\n"); + if (options.enableKoreAntileft) { + genSemanticsRuleLHSWithAlias( + ruleAliasName, freeVars, topCellSortStr, priorityToPreviousGroup.get(priority), sb); + sb.append(",\n "); + } else { + genSemanticsRuleLHSNoAlias( + requires, left, freeVars, topCellSortStr, priorityToPreviousGroup.get(priority), sb); + sb.append(",\n "); + } + } else { + // LHS for claims + sb.append(" claim{} "); + sb.append(String.format("\\implies{%s} (\n ", topCellSortStr)); + sb.append(String.format(" \\and{%s} (\n ", topCellSortStr)); + convertSideCondition(requires, topCellSortStr, sb); + sb.append(", "); + convert(left, sb); + sb.append("), "); + } - } else { - sb.append("\\implies{R} (\n \\and{R}(\n "); - convertSideCondition(requires, sb); - sb.append(",\n "); - - for (int childIdx = 0; childIdx < ruleInfo.leftChildren.size(); childIdx++) { - sb.append("\\and{R} ("); - sb.append("\n "); - sb.append("\\in{"); - Sort childSort = ruleInfo.prodChildrenSorts.get(childIdx); - convert(childSort, ruleInfo.production.params(), sb); - sb.append(", R} ("); - sb.append("\n "); - sb.append("X").append(childIdx).append(":"); - convert(childSort, ruleInfo.production.params(), sb); - sb.append(",\n "); - convert(ruleInfo.leftChildren.get(childIdx), sb); - sb.append("\n ),"); - } - sb.append("\n \\top{R} ()"); - sb.append("\n "); - for (int childIdx = 0; childIdx < ruleInfo.leftChildren.size(); childIdx++) { - sb.append(')'); - } - sb.append("),\n \\equals{"); - sb.append(ruleInfo.productionSortStr); - sb.append(",R} (\n "); - convert(ruleInfo.productionLabel, sb); - sb.append("("); - String conn = ""; - for (int childIdx = 0; childIdx < ruleInfo.leftChildren.size(); childIdx++) { - sb.append(conn).append("X").append(childIdx).append(":"); - Sort childSort = ruleInfo.prodChildrenSorts.get(childIdx); - convert(childSort, ruleInfo.production.params(), sb); - conn = ","; - } - sb.append(")"); - sb.append(",\n \\and{"); - sb.append(ruleInfo.productionSortStr); - sb.append("} (\n "); - convert(right, sb); - sb.append(",\n "); - convertSideCondition(ensures, ruleInfo.productionSortStr, sb); - sb.append(")))\n "); - convert(consideredAttributes, rule.att(), sb, freeVarsMap, rule); - sb.append("\n\n"); - } - } else if (ruleInfo.isKore) { - assertNoExistentials(rule, existentials); - if (rule instanceof Claim) { - sb.append(" claim{} "); - } else { - sb.append(" axiom{} "); - } - convert(left, sb); - sb.append("\n "); - convert(consideredAttributes, rule.att(), sb, freeVarsMap, rule); - sb.append("\n\n"); - } else if (!ExpandMacros.isMacro(rule)) { - // generate rule LHS - if (!(rule instanceof Claim)) { - // LHS for semantics rules - String ruleAliasName = String.format("rule%dLHS", ruleIndex); - int priority = getPriority(rule.att()); - List freeVars = new ArrayList<>(freeVariables); - Comparator compareByName = (KVariable v1, KVariable v2) -> v1.name().compareTo(v2.name()); - java.util.Collections.sort(freeVars, compareByName); - - if (options.enableKoreAntileft) { - genAliasForSemanticsRuleLHS(requires, left, ruleAliasName, freeVars, topCellSortStr, - priority, priorityToAlias, sb); - sb.append("\n"); - } + // generate rule RHS + if (sentenceType == SentenceType.ALL_PATH) { + sb.append(String.format("%s{%s} (\n ", ALL_PATH_OP, topCellSortStr)); + } else if (sentenceType == SentenceType.ONE_PATH) { + sb.append(String.format("%s{%s} (\n ", ONE_PATH_OP, topCellSortStr)); + } + if (!existentials.isEmpty()) { + for (KVariable exists : existentials) { + sb.append(String.format(" \\exists{%s} (", topCellSortStr)); + convert((K) exists, sb); + sb.append(", "); + } + sb.append("\n "); + } + sb.append(String.format("\\and{%s} (\n ", topCellSortStr)); - sb.append(" axiom{} "); - sb.append(String.format("\\rewrites{%s} (\n ", topCellSortStr)); + if (options.enableKoreAntileft) { + convertSideCondition(ensures, topCellSortStr, sb); + sb.append(", "); + convert(right, sb); + } else { + convert(right, sb); + sb.append(", "); + convertSideCondition(ensures, topCellSortStr, sb); + } - if (options.enableKoreAntileft) { - genSemanticsRuleLHSWithAlias(ruleAliasName, freeVars, topCellSortStr, - priorityToPreviousGroup.get(priority), sb); - sb.append(",\n "); - } else { - genSemanticsRuleLHSNoAlias(requires, left, freeVars, topCellSortStr, priorityToPreviousGroup.get(priority), sb); - sb.append(",\n "); - } - } else { - // LHS for claims - sb.append(" claim{} "); - sb.append(String.format("\\implies{%s} (\n ", topCellSortStr)); - sb.append(String.format(" \\and{%s} (\n ", topCellSortStr)); - convertSideCondition(requires, topCellSortStr, sb); - sb.append(", "); - convert(left, sb); - sb.append("), "); - } - - // generate rule RHS - if (sentenceType == SentenceType.ALL_PATH) { - sb.append(String.format("%s{%s} (\n ", ALL_PATH_OP, topCellSortStr)); - } else if (sentenceType == SentenceType.ONE_PATH) { - sb.append(String.format("%s{%s} (\n ", ONE_PATH_OP, topCellSortStr)); - } - if (!existentials.isEmpty()) { - for (KVariable exists : existentials) { - sb.append(String.format(" \\exists{%s} (", topCellSortStr)); - convert((K)exists, sb); - sb.append(", "); - } - sb.append("\n "); - } - sb.append(String.format("\\and{%s} (\n ", topCellSortStr)); - - if (options.enableKoreAntileft) { - convertSideCondition(ensures, topCellSortStr, sb); - sb.append(", "); - convert(right, sb); - } else { - convert(right, sb); - sb.append(", "); - convertSideCondition(ensures, topCellSortStr, sb); - - } - - sb.append(')'); - for (KVariable ignored : existentials) { - sb.append(')'); - } - if (rule instanceof Claim) { - sb.append(')'); - } - sb.append(')'); - sb.append("\n "); - convert(consideredAttributes, rule.att(), sb, freeVarsMap, rule); - sb.append("\n\n"); - } else { - assertNoExistentials(rule, existentials); - sb.append(" axiom{R"); - Option sortParamsWrapper = rule.att().getOption(Att.SORT_PARAMS(), Sort.class); - Option> sortParams = sortParamsWrapper.map(s -> stream(s.params()).map(sort -> sort.name()).collect(Collectors.toSet())); - if (sortParams.nonEmpty()) { - for (Object sortParamName : sortParams.get()) - sb.append("," + sortParamName); - } - sb.append("} "); - sb.append("\\equals{"); - sb.append(ruleInfo.productionSortStr); - sb.append(",R} (\n "); - convert(left, sb); - sb.append(",\n "); - convert(right, sb); - sb.append(")\n "); - convert(consideredAttributes, rule.att().add(Att.PRIORITY(), Integer.toString(getPriority(rule.att()))), sb, freeVarsMap, rule); - sb.append("\n\n"); - } - } - - private boolean ignoreSideConditionsForOwise(Rule notMatching) { - return notMatching.att().contains(Att.OWISE()) - || notMatching.att().contains(Att.SIMPLIFICATION()) - || notMatching.att().contains(Att.NON_EXECUTABLE()); - } - - private void assertNoExistentials(Sentence sentence, Set existentials) { - if (!existentials.isEmpty()) { - throw KEMException.compilerError("Cannot encode equations with existential variables to KORE." + - "\n If this is desired, please use #Exists with regular variables." + - "\n Offending variables: " + existentials + - "\n context: " + sentence); - } - } - - private Set getExistentials(RuleOrClaim rule) { - Set res = new HashSet<>(); - VisitK visitor = new VisitK() { - @Override - public void apply(KVariable k) { - if (k.name().startsWith("?") || k.att().contains(Att.FRESH())) - res.add(k); - } + sb.append(')'); + for (KVariable ignored : existentials) { + sb.append(')'); + } + if (rule instanceof Claim) { + sb.append(')'); + } + sb.append(')'); + sb.append("\n "); + convert(consideredAttributes, rule.att(), sb, freeVarsMap, rule); + sb.append("\n\n"); + } else { + assertNoExistentials(rule, existentials); + sb.append(" axiom{R"); + Option sortParamsWrapper = rule.att().getOption(Att.SORT_PARAMS(), Sort.class); + Option> sortParams = + sortParamsWrapper.map( + s -> stream(s.params()).map(sort -> sort.name()).collect(Collectors.toSet())); + if (sortParams.nonEmpty()) { + for (Object sortParamName : sortParams.get()) sb.append("," + sortParamName); + } + sb.append("} "); + sb.append("\\equals{"); + sb.append(ruleInfo.productionSortStr); + sb.append(",R} (\n "); + convert(left, sb); + sb.append(",\n "); + convert(right, sb); + sb.append(")\n "); + convert( + consideredAttributes, + rule.att().add(Att.PRIORITY(), Integer.toString(getPriority(rule.att()))), + sb, + freeVarsMap, + rule); + sb.append("\n\n"); + } + } + + private boolean ignoreSideConditionsForOwise(Rule notMatching) { + return notMatching.att().contains(Att.OWISE()) + || notMatching.att().contains(Att.SIMPLIFICATION()) + || notMatching.att().contains(Att.NON_EXECUTABLE()); + } + + private void assertNoExistentials(Sentence sentence, Set existentials) { + if (!existentials.isEmpty()) { + throw KEMException.compilerError( + "Cannot encode equations with existential variables to KORE." + + "\n If this is desired, please use #Exists with regular variables." + + "\n Offending variables: " + + existentials + + "\n context: " + + sentence); + } + } + + private Set getExistentials(RuleOrClaim rule) { + Set res = new HashSet<>(); + VisitK visitor = + new VisitK() { + @Override + public void apply(KVariable k) { + if (k.name().startsWith("?") || k.att().contains(Att.FRESH())) res.add(k); + } }; - visitor.apply(rule.ensures()); - visitor.apply(RewriteToTop.toRight(rule.body())); - return res; - } - - private void genAliasForSemanticsRuleLHS(K requires, K left, - String ruleAliasName, List freeVars, String topCellSortStr, - Integer priority, ListMultimap priorityToAlias, - StringBuilder sb) { - sb.append(" alias "); - sb.append(ruleAliasName); - // We assume no sort variables. - sb.append("{}("); - String conn = ""; - for (KVariable var: freeVars) { - sb.append(conn); - convert(var.att().getOptional(Sort.class).orElse(Sorts.K()), sb); - conn = ","; - } - sb.append(") : "); - sb.append(topCellSortStr); - sb.append("\n where "); - genAliasDeclHead(ruleAliasName, freeVars, sb); - sb.append(") :=\n"); - sb.append(String.format(" \\and{%s} (\n ", topCellSortStr)); - convertSideCondition(requires, topCellSortStr, sb); + visitor.apply(rule.ensures()); + visitor.apply(RewriteToTop.toRight(rule.body())); + return res; + } + + private void genAliasForSemanticsRuleLHS( + K requires, + K left, + String ruleAliasName, + List freeVars, + String topCellSortStr, + Integer priority, + ListMultimap priorityToAlias, + StringBuilder sb) { + sb.append(" alias "); + sb.append(ruleAliasName); + // We assume no sort variables. + sb.append("{}("); + String conn = ""; + for (KVariable var : freeVars) { + sb.append(conn); + convert(var.att().getOptional(Sort.class).orElse(Sorts.K()), sb); + conn = ","; + } + sb.append(") : "); + sb.append(topCellSortStr); + sb.append("\n where "); + genAliasDeclHead(ruleAliasName, freeVars, sb); + sb.append(") :=\n"); + sb.append(String.format(" \\and{%s} (\n ", topCellSortStr)); + convertSideCondition(requires, topCellSortStr, sb); + sb.append(", "); + convert(left, sb); + sb.append(") []\n"); + + // build existential quantified pattern for alias + StringBuilder extStrBuilder = new StringBuilder(); + for (KVariable var : freeVars) { + extStrBuilder.append(String.format("\\exists{%s}(", topCellSortStr)); + convert((K) var, extStrBuilder); + extStrBuilder.append(","); + } + genAliasDeclHead(ruleAliasName, freeVars, extStrBuilder); + extStrBuilder.append(")"); + for (int i = 0; i < freeVars.size(); i++) { + extStrBuilder.append(")"); + } + priorityToAlias.put(priority, extStrBuilder.toString()); + } + + private void genAliasDeclHead(String aliasName, List freeVars, StringBuilder sb) { + sb.append(aliasName); + sb.append("{}("); + String conn = ""; + for (KVariable var : freeVars) { + sb.append(conn); + convert((K) var, sb); + conn = ","; + } + } + + private void genSemanticsRuleLHSWithAlias( + String ruleAliasName, + List freeVars, + String topCellSortStr, + String previousGroupName, + StringBuilder sb) { + if (!previousGroupName.equals("")) { + sb.append(String.format("\\and{%s}(\n ", topCellSortStr)); + sb.append(String.format("\\not{%s}(", topCellSortStr)); + sb.append(previousGroupName); + sb.append("{}()),\n "); + } + sb.append(ruleAliasName); + sb.append("{}("); + String conn = ""; + for (KVariable var : freeVars) { + sb.append(conn); + convert((K) var, sb); + conn = ","; + } + sb.append(")"); + if (!previousGroupName.equals("")) { + sb.append(")"); + } + } + + private void genSemanticsRuleLHSNoAlias( + K requires, + K left, + List freeVars, + String topCellSortStr, + String previousGroupName, + StringBuilder sb) { + sb.append(String.format(" \\and{%s} (\n ", topCellSortStr)); + convert(left, sb); + sb.append(",\n "); + convertSideCondition(requires, topCellSortStr, sb); + sb.append(")"); + } + + private void genPriorityGroups( + List priorityList, + Map priorityToPreviousGroup, + ListMultimap priorityToAlias, + String topCellSortStr, + StringBuilder sb) { + // skip generating alias for the last priority group + for (int index = 0; index < priorityList.size() - 1; index++) { + Integer priority = priorityList.get(index); + String priorityGroupName = String.format("priorityLE%d", priority); + sb.append(String.format(" alias %s{}() : %s", priorityGroupName, topCellSortStr)); + sb.append(String.format("\n where %s{}() := ", priorityGroupName)); + String previousGroupName = priorityToPreviousGroup.get(priority); + if (!previousGroupName.equals("")) { + sb.append(String.format("\\or{%s}(\n ", topCellSortStr)); + sb.append(previousGroupName); + sb.append("{}(), "); + } + // generate priority group body + List aliases = priorityToAlias.get(priority); + for (String ruleLHSAlias : aliases) { + sb.append(String.format("\\or{%s}(\n ", topCellSortStr)); + sb.append(ruleLHSAlias); sb.append(", "); - convert(left, sb); - sb.append(") []\n"); - - // build existential quantified pattern for alias - StringBuilder extStrBuilder = new StringBuilder(); - for (KVariable var: freeVars) { - extStrBuilder.append(String.format("\\exists{%s}(", topCellSortStr)); - convert((K)var, extStrBuilder); - extStrBuilder.append(","); - } - genAliasDeclHead(ruleAliasName, freeVars, extStrBuilder); - extStrBuilder.append(")"); - for (int i = 0; i < freeVars.size(); i++) { - extStrBuilder.append(")"); - } - priorityToAlias.put(priority, extStrBuilder.toString()); - } - - private void genAliasDeclHead(String aliasName, List freeVars, StringBuilder sb) { - sb.append(aliasName); - sb.append("{}("); - String conn = ""; - for (KVariable var: freeVars) { - sb.append(conn); - convert((K)var, sb); - conn = ","; - } - } - - private void genSemanticsRuleLHSWithAlias(String ruleAliasName, List freeVars, String topCellSortStr, - String previousGroupName, StringBuilder sb) { - if (!previousGroupName.equals("")) { - sb.append(String.format("\\and{%s}(\n ", topCellSortStr)); - sb.append(String.format("\\not{%s}(", topCellSortStr)); - sb.append(previousGroupName); - sb.append("{}()),\n "); - } - sb.append(ruleAliasName); - sb.append("{}("); - String conn = ""; - for (KVariable var: freeVars) { - sb.append(conn); - convert((K)var, sb); - conn = ","; - } + } + // bottom is the unit of "or" + sb.append(String.format("\\bottom{%s}()", topCellSortStr)); + // complete parenthesis + for (int i = 0; i < aliases.size(); i++) { sb.append(")"); - if (!previousGroupName.equals("")) { - sb.append(")"); - } - } - - private void genSemanticsRuleLHSNoAlias(K requires, K left, List freeVars, String topCellSortStr, - String previousGroupName, StringBuilder sb) { - sb.append(String.format(" \\and{%s} (\n ", topCellSortStr)); - convert(left, sb); - sb.append(",\n "); - convertSideCondition(requires, topCellSortStr, sb); + } + if (!previousGroupName.equals("")) { sb.append(")"); + } + sb.append(" []\n\n"); + } + } + + private void functionalPattern(Production prod, Runnable functionPattern, StringBuilder sb) { + sb.append(" axiom"); + convertParams(prod.klabel(), true, sb); + sb.append(" \\exists{R} (Val:"); + convert(prod.sort(), prod, sb); + sb.append(", \\equals{"); + convert(prod.sort(), prod, sb); + sb.append(", R} ("); + sb.append("Val:"); + convert(prod.sort(), prod, sb); + sb.append(", "); + functionPattern.run(); + sb.append("))"); + } + + private void applyPattern(Production prod, String varName, StringBuilder sb) { + convert(prod.klabel().get(), prod, sb); + sb.append("("); + String conn = ""; + for (int i = 0; i < prod.arity(); i++) { + sb.append(conn); + sb.append(varName).append(i).append(":"); + convert(prod.nonterminal(i).sort(), prod, sb); + conn = ", "; + } + sb.append(')'); + } + + private void convertTokenProd(Sort sort, StringBuilder sb) { + if (METAVAR) { + sb.append("\\exists{"); + convert(sort, sb); + sb.append("} (#Str:#String{}, \\dv{"); + convert(sort, sb); + sb.append("}(#Str:#String{}))"); + } else { + sb.append("\\top{"); + convert(sort, sb); + sb.append("}()"); + } + } + + private void convertParams(Option maybeKLabel, boolean hasR, StringBuilder sb) { + sb.append("{"); + String conn = ""; + if (hasR) { + sb.append("R"); + if (maybeKLabel.isDefined()) { + conn = ", "; + } } - - private void genPriorityGroups(List priorityList, - Map priorityToPreviousGroup, - ListMultimap priorityToAlias, - String topCellSortStr, StringBuilder sb) { - // skip generating alias for the last priority group - for (int index = 0; index < priorityList.size()-1; index++) { - Integer priority = priorityList.get(index); - String priorityGroupName = String.format("priorityLE%d", priority); - sb.append(String.format(" alias %s{}() : %s", priorityGroupName, topCellSortStr)); - sb.append(String.format("\n where %s{}() := ", priorityGroupName)); - String previousGroupName = priorityToPreviousGroup.get(priority); - if (!previousGroupName.equals("")) { - sb.append(String.format("\\or{%s}(\n ", topCellSortStr)); - sb.append(previousGroupName); - sb.append("{}(), "); - } - // generate priority group body - List aliases = priorityToAlias.get(priority); - for (String ruleLHSAlias : aliases) { - sb.append(String.format("\\or{%s}(\n ", topCellSortStr)); - sb.append(ruleLHSAlias); - sb.append(", "); - } - // bottom is the unit of "or" - sb.append(String.format("\\bottom{%s}()", topCellSortStr)); - // complete parenthesis - for (int i = 0; i < aliases.size(); i++) { - sb.append(")"); - } - if (!previousGroupName.equals("")) { - sb.append(")"); - } - sb.append(" []\n\n"); - } + if (maybeKLabel.isDefined()) { + for (Sort param : iterable(maybeKLabel.get().params())) { + sb.append(conn); + convert(param, Seq(param), sb); + conn = ", "; + } } - - private void functionalPattern(Production prod, Runnable functionPattern, StringBuilder sb) { - sb.append(" axiom"); - convertParams(prod.klabel(), true, sb); - sb.append(" \\exists{R} (Val:"); - convert(prod.sort(), prod, sb); - sb.append(", \\equals{"); - convert(prod.sort(), prod, sb); - sb.append(", R} ("); - sb.append("Val:"); - convert(prod.sort(), prod, sb); - sb.append(", "); - functionPattern.run(); - sb.append("))"); + sb.append("}"); + } + + private boolean isConstructor(Production prod, SetMultimap functionRules) { + Att att = addKoreAttributes(prod, functionRules, java.util.Collections.emptySet()); + return att.contains(Att.CONSTRUCTOR()); + } + + private boolean isFunctional(Production prod, SetMultimap functionRules) { + Att att = addKoreAttributes(prod, functionRules, java.util.Collections.emptySet()); + return att.contains(Att.FUNCTIONAL()); + } + + private boolean isGeneratedInKeysOp(Production prod) { + Option hook = prod.att().getOption(Att.HOOK()); + if (hook.isEmpty()) return false; + if (!hook.get().equals("MAP.in_keys")) return false; + return (!prod.klabel().isEmpty()); + } + + private Att addKoreAttributes( + Production prod, SetMultimap functionRules, Set overloads) { + boolean isFunctional = !isFunction(prod) || prod.att().contains(Att.TOTAL()); + boolean isConstructor = !isFunction(prod); + isConstructor &= !prod.att().contains(Att.ASSOC()); + isConstructor &= !prod.att().contains(Att.COMM()); + isConstructor &= !prod.att().contains(Att.IDEM()); + isConstructor &= !(prod.att().contains(Att.FUNCTION()) && prod.att().contains(Att.UNIT())); + + // Later we might set !isConstructor because there are anywhere rules, + // but if a symbol is a constructor at this point, then it is still + // injective. + boolean isInjective = isConstructor; + + boolean isMacro = false; + boolean isAnywhere = overloads.contains(prod); + if (prod.klabel().isDefined()) { + for (Rule r : functionRules.get(prod.klabel().get())) { + isMacro |= ExpandMacros.isMacro(r); + isAnywhere |= r.att().contains(Att.ANYWHERE()); + } } + isConstructor &= !isMacro; + isConstructor &= !isAnywhere; - private void applyPattern(Production prod, String varName, StringBuilder sb) { - convert(prod.klabel().get(), prod, sb); - sb.append("("); - String conn = ""; - for (int i = 0; i < prod.arity(); i++) { - sb.append(conn); - sb.append(varName).append(i).append(":"); - convert(prod.nonterminal(i).sort(), prod, sb); - conn = ", "; - } - sb.append(')'); + Att att = prod.att().remove(Att.CONSTRUCTOR()); + if (att.contains(Att.HOOK()) && !isRealHook(att)) { + att = att.remove(Att.HOOK()); } - - private void convertTokenProd(Sort sort, StringBuilder sb) { - if (METAVAR) { - sb.append("\\exists{"); - convert(sort, sb); - sb.append("} (#Str:#String{}, \\dv{"); - convert(sort, sb); - sb.append("}(#Str:#String{}))"); - } else { - sb.append("\\top{"); - convert(sort, sb); - sb.append("}()"); - } + if (isConstructor) { + att = att.add(Att.CONSTRUCTOR()); } - - private void convertParams(Option maybeKLabel, boolean hasR, StringBuilder sb) { - sb.append("{"); - String conn = ""; - if (hasR) { - sb.append("R"); - if (maybeKLabel.isDefined()) { - conn = ", "; - } - } - if (maybeKLabel.isDefined()) { - for (Sort param : iterable(maybeKLabel.get().params())) { - sb.append(conn); - convert(param, Seq(param), sb); - conn = ", "; - } - } - sb.append("}"); + if (isFunctional) { + att = att.add(Att.FUNCTIONAL()); } - - private boolean isConstructor(Production prod, SetMultimap functionRules) { - Att att = addKoreAttributes(prod, functionRules, java.util.Collections.emptySet()); - return att.contains(Att.CONSTRUCTOR()); + if (isAnywhere) { + att = att.add(Att.ANYWHERE()); } - - private boolean isFunctional(Production prod, SetMultimap functionRules) { - Att att = addKoreAttributes(prod, functionRules, java.util.Collections.emptySet()); - return att.contains(Att.FUNCTIONAL()); + if (isInjective) { + att = att.add(Att.INJECTIVE()); } - - private boolean isGeneratedInKeysOp(Production prod) { - Option hook = prod.att().getOption(Att.HOOK()); - if (hook.isEmpty()) return false; - if (!hook.get().equals("MAP.in_keys")) return false; - return (!prod.klabel().isEmpty()); + if (isMacro) { + att = att.add(Att.MACRO()); } + // update format attribute with structure expected by backend + String format = + att.getOptional(Att.FORMAT()).orElse(Formatter.defaultFormat(prod.items().size())); + int nt = 1; + boolean hasFormat = true; + boolean printName = + stream(prod.items()) + .noneMatch(pi -> pi instanceof NonTerminal && ((NonTerminal) pi).name().isEmpty()); + boolean printEllipses = false; - private Att addKoreAttributes(Production prod, SetMultimap functionRules, Set overloads) { - boolean isFunctional = !isFunction(prod) || prod.att().contains(Att.TOTAL()); - boolean isConstructor = !isFunction(prod); - isConstructor &= !prod.att().contains(Att.ASSOC()); - isConstructor &= !prod.att().contains(Att.COMM()); - isConstructor &= !prod.att().contains(Att.IDEM()); - isConstructor &= !(prod.att().contains(Att.FUNCTION()) && prod.att().contains(Att.UNIT())); - - // Later we might set !isConstructor because there are anywhere rules, - // but if a symbol is a constructor at this point, then it is still - // injective. - boolean isInjective = isConstructor; - - boolean isMacro = false; - boolean isAnywhere = overloads.contains(prod); - if (prod.klabel().isDefined()) { - for (Rule r : functionRules.get(prod.klabel().get())) { - isMacro |= ExpandMacros.isMacro(r); - isAnywhere |= r.att().contains(Att.ANYWHERE()); - } - } - isConstructor &= !isMacro; - isConstructor &= !isAnywhere; - - Att att = prod.att().remove(Att.CONSTRUCTOR()); - if (att.contains(Att.HOOK()) && !isRealHook(att)) { - att = att.remove(Att.HOOK()); - } - if (isConstructor) { - att = att.add(Att.CONSTRUCTOR()); - } - if (isFunctional) { - att = att.add(Att.FUNCTIONAL()); - } - if (isAnywhere) { - att = att.add(Att.ANYWHERE()); - } - if (isInjective) { - att = att.add(Att.INJECTIVE()); - } - if (isMacro) { - att = att.add(Att.MACRO()); - } - // update format attribute with structure expected by backend - String format = att.getOptional(Att.FORMAT()).orElse(Formatter.defaultFormat(prod.items().size())); - int nt = 1; - boolean hasFormat = true; - boolean printName = stream(prod.items()).noneMatch(pi -> pi instanceof NonTerminal && ((NonTerminal) pi).name().isEmpty()); - boolean printEllipses = false; - - for (int i = 0; i < prod.items().size(); i++) { - if (prod.items().apply(i) instanceof NonTerminal) { - String replacement; - if (printName && prod.isPrefixProduction()) { - replacement = ((NonTerminal) prod.items().apply(i)).name().get() + ": %" + (nt++); - printEllipses = true; - } else { - replacement = "%" + (nt++); - } - format = format.replaceAll("%" + (i+1) + "(?![0-9])", replacement); - } else if (prod.items().apply(i) instanceof Terminal) { - format = format.replaceAll("%" + (i+1) + "(?![0-9])", "%c" + ((Terminal)prod.items().apply(i)).value().replace("\\", "\\\\").replace("$", "\\$").replace("%", "%%") + "%r"); - } else { - hasFormat = false; + for (int i = 0; i < prod.items().size(); i++) { + if (prod.items().apply(i) instanceof NonTerminal) { + String replacement; + if (printName && prod.isPrefixProduction()) { + replacement = ((NonTerminal) prod.items().apply(i)).name().get() + ": %" + (nt++); + printEllipses = true; + } else { + replacement = "%" + (nt++); + } + format = format.replaceAll("%" + (i + 1) + "(?![0-9])", replacement); + } else if (prod.items().apply(i) instanceof Terminal) { + format = + format.replaceAll( + "%" + (i + 1) + "(?![0-9])", + "%c" + + ((Terminal) prod.items().apply(i)) + .value() + .replace("\\", "\\\\") + .replace("$", "\\$") + .replace("%", "%%") + + "%r"); + } else { + hasFormat = false; + } + } + if (printEllipses && format.contains("(")) { + int idxLParam = format.indexOf("(") + 1; + format = format.substring(0, idxLParam) + "... " + format.substring(idxLParam); + } + if (hasFormat) { + att = att.add(Att.FORMAT(), format); + if (att.contains(Att.COLOR())) { + boolean escape = false; + StringBuilder colors = new StringBuilder(); + String conn = ""; + for (int i = 0; i < format.length(); i++) { + if (escape && format.charAt(i) == 'c') { + colors.append(conn).append(att.get(Att.COLOR())); + conn = ","; } + escape = format.charAt(i) == '%'; } - if (printEllipses && format.contains("(")) { - int idxLParam = format.indexOf("(") + 1; - format = format.substring(0, idxLParam) + "... " + format.substring(idxLParam); + att = att.add(Att.COLORS(), colors.toString()); + } + StringBuilder sb = new StringBuilder(); + for (ProductionItem pi : iterable(prod.items())) { + if (pi instanceof NonTerminal) { + sb.append('0'); + } else { + sb.append('1'); } - if (hasFormat) { - att = att.add(Att.FORMAT(), format); - if (att.contains(Att.COLOR())) { - boolean escape = false; - StringBuilder colors = new StringBuilder(); - String conn = ""; - for (int i = 0; i < format.length(); i++) { - if (escape && format.charAt(i) == 'c') { - colors.append(conn).append(att.get(Att.COLOR())); - conn = ","; - } - escape = format.charAt(i) == '%'; - } - att = att.add(Att.COLORS(), colors.toString()); - } - StringBuilder sb = new StringBuilder(); - for (ProductionItem pi : iterable(prod.items())) { - if (pi instanceof NonTerminal) { - sb.append('0'); - } else { - sb.append('1'); - } - } - att = att.add(Att.TERMINALS(), sb.toString()); - if (prod.klabel().isDefined()) { - List lessThanK = new ArrayList<>(); - Option> lessThan = module.priorities().relations().get(Tag(prod.klabel().get().name())); - if (lessThan.isDefined()) { - for (Tag t : iterable(lessThan.get())) { - if (ConstructorChecks.isBuiltinLabel(KLabel(t.name()))) { - continue; - } - lessThanK.add(KApply(KLabel(t.name()))); - } - } - att = att.add(Att.PRIORITIES(), KList.class, KList(lessThanK)); - att = att.remove(Att.LEFT()); - att = att.remove(Att.RIGHT()); - att = att.add(Att.LEFT_INTERNAL(), KList.class, getAssoc(module.leftAssoc(), prod.klabel().get())); - att = att.add(Att.RIGHT_INTERNAL(), KList.class, getAssoc(module.rightAssoc(), prod.klabel().get())); + } + att = att.add(Att.TERMINALS(), sb.toString()); + if (prod.klabel().isDefined()) { + List lessThanK = new ArrayList<>(); + Option> lessThan = + module.priorities().relations().get(Tag(prod.klabel().get().name())); + if (lessThan.isDefined()) { + for (Tag t : iterable(lessThan.get())) { + if (ConstructorChecks.isBuiltinLabel(KLabel(t.name()))) { + continue; + } + lessThanK.add(KApply(KLabel(t.name()))); } - } else { - att = att.remove(Att.FORMAT()); } - // This attribute is a frontend attribute only and is removed from the kore - // Since it has no meaning outside the frontend - return att.remove(Att.ORIGINAL_PRD(), Production.class); - } - - private KList getAssoc(scala.collection.Set> assoc, KLabel klabel) { - return KList(stream(assoc).filter(t -> t._1().name().equals(klabel.name())).map(t -> KApply(KLabel(t._2().name()))).collect(Collectors.toList())); - } - - private boolean isFunction(Production prod) { - return prod.att().contains(Att.FUNCTION()); - } - - // Assume that there is no quantifiers - private Set collectLHSFreeVariables(K requires, K left) { - Set res = new HashSet<>(); - VisitK visitor = new VisitK() { - @Override - public void apply(KVariable k) { - res.add(k); - } + att = att.add(Att.PRIORITIES(), KList.class, KList(lessThanK)); + att = att.remove(Att.LEFT()); + att = att.remove(Att.RIGHT()); + att = + att.add( + Att.LEFT_INTERNAL(), + KList.class, + getAssoc(module.leftAssoc(), prod.klabel().get())); + att = + att.add( + Att.RIGHT_INTERNAL(), + KList.class, + getAssoc(module.rightAssoc(), prod.klabel().get())); + } + } else { + att = att.remove(Att.FORMAT()); + } + // This attribute is a frontend attribute only and is removed from the kore + // Since it has no meaning outside the frontend + return att.remove(Att.ORIGINAL_PRD(), Production.class); + } + + private KList getAssoc(scala.collection.Set> assoc, KLabel klabel) { + return KList( + stream(assoc) + .filter(t -> t._1().name().equals(klabel.name())) + .map(t -> KApply(KLabel(t._2().name()))) + .collect(Collectors.toList())); + } + + private boolean isFunction(Production prod) { + return prod.att().contains(Att.FUNCTION()); + } + + // Assume that there is no quantifiers + private Set collectLHSFreeVariables(K requires, K left) { + Set res = new HashSet<>(); + VisitK visitor = + new VisitK() { + @Override + public void apply(KVariable k) { + res.add(k); + } }; - visitor.apply(requires); - visitor.apply(left); - return res; + visitor.apply(requires); + visitor.apply(left); + return res; + } + + private void convertSideCondition(K k, StringBuilder sb) { + if (k.equals(BooleanUtils.TRUE)) { + sb.append("\\top{R}()"); + } else { + sb.append("\\equals{SortBool{},R}(\n "); + convert(k, sb); + sb.append(",\n \\dv{SortBool{}}(\"true\"))"); + } + } + + private void convertSideCondition(K k, String resultSortStr, StringBuilder sb) { + if (k.equals(BooleanUtils.TRUE)) { + sb.append(String.format("\\top{%s}()", resultSortStr)); + } else { + sb.append(String.format("\\equals{SortBool{},%s}(\n ", resultSortStr)); + convert(k, sb); + sb.append(",\n \\dv{SortBool{}}(\"true\"))"); + } + } + + private KLabel computePolyKLabel(KApply k) { + String labelName = k.klabel().name(); + if (mlBinders.contains( + labelName)) { // ML binders are not parametric in the variable so we remove it + List params = mutable(k.klabel().params()); + if (!params.isEmpty()) { + params.remove(0); + } + return KLabel(labelName, immutable(params)); + } else { + return k.klabel(); + } + } + + private void collectAttributes(Map attributes, Att att) { + for (Tuple2, ?> attribute : + iterable(att.withUserGroupsAsGroupAtt().att())) { + Att.Key name = attribute._1._1; + Object val = attribute._2; + String strVal = val.toString(); + if (strVal.equals("")) { + if (!attributes.containsKey(name)) { + attributes.put(name, false); + } + } else { + attributes.put(name, true); + } } - - private void convertSideCondition(K k, StringBuilder sb) { - if (k.equals(BooleanUtils.TRUE)) { - sb.append("\\top{R}()"); - } else { - sb.append("\\equals{SortBool{},R}(\n "); - convert(k, sb); - sb.append(",\n \\dv{SortBool{}}(\"true\"))"); + } + + private static final Production INJ_PROD = + Production( + KLabel(KLabels.INJ, Sort("S1"), Sort("S2")), + Sort("S2"), + Seq(NonTerminal(Sort("S1"))), + Att()); + + private Production production(KApply term) { + return production(term, false); + } + + private Production production(KApply term, boolean instantiatePolySorts) { + KLabel klabel = term.klabel(); + if (klabel.name().equals(KLabels.INJ)) + return instantiatePolySorts ? INJ_PROD.substitute(term.klabel().params()) : INJ_PROD; + Option> prods = module.productionsFor().get(klabel.head()); + if (!(prods.nonEmpty() && prods.get().size() == 1)) + throw KEMException.compilerError( + "Expected to find exactly one production for KLabel: " + + klabel + + " found: " + + prods.getOrElse(Collections::Set).size()); + return instantiatePolySorts + ? prods.get().head().substitute(term.klabel().params()) + : prods.get().head(); + } + + private static String convertBuiltinLabel(String klabel) { + return switch (klabel) { + case "#Bottom" -> "\\bottom"; + case "#Top" -> "\\top"; + case "#Or" -> "\\or"; + case "#And" -> "\\and"; + case "#Not" -> "\\not"; + case "#Floor" -> "\\floor"; + case "#Ceil" -> "\\ceil"; + case "#Equals" -> "\\equals"; + case "#Implies" -> "\\implies"; + case "#Exists" -> "\\exists"; + case "#Forall" -> "\\forall"; + case "#AG" -> "allPathGlobally"; + case "weakExistsFinally" -> ONE_PATH_OP; + case "weakAlwaysFinally" -> ALL_PATH_OP; + default -> throw KEMException.compilerError("Unsuppored kore connective in rule: " + klabel); + }; + } + + public static void convert(KLabel klabel, StringBuilder sb) { + convert(klabel, java.util.Collections.emptySet(), sb); + } + + private void convert(KLabel klabel, Seq params, StringBuilder sb) { + convert(klabel, mutable(params), sb); + } + + private static void convert(KLabel klabel, Collection params, StringBuilder sb) { + if (klabel.name().equals(KLabels.INJ)) { + sb.append(klabel.name()); + } else if (ConstructorChecks.isBuiltinLabel(klabel)) { + sb.append(convertBuiltinLabel(klabel.name())); + } else { + sb.append("Lbl"); + convert(klabel.name(), sb); + } + sb.append("{"); + String conn = ""; + for (Sort param : iterable(klabel.params())) { + sb.append(conn); + convert(param, params, sb); + conn = ", "; + } + sb.append("}"); + } + + private void convert(KLabel klabel, Production prod, StringBuilder sb) { + if (klabel.name().equals(KLabels.INJ)) { + sb.append(klabel.name()); + } else { + sb.append("Lbl"); + convert(klabel.name(), sb); + } + sb.append("{"); + String conn = ""; + for (Sort param : iterable(klabel.params())) { + sb.append(conn); + convert(param, prod, sb); + conn = ", "; + } + sb.append("}"); + } + + private void convert(Sort sort, Production prod, StringBuilder sb) { + convert(sort, prod.params(), sb); + } + + public static void convert(Sort sort, StringBuilder sb) { + convert(sort, java.util.Collections.emptySet(), sb); + } + + private void convert(SortHead sort, StringBuilder sb) { + List params = new ArrayList<>(); + for (int i = 0; i < sort.params(); i++) { + params.add(Sort("S" + i)); + } + convert(Sort(sort.name(), immutable(params)), params, sb); + } + + private void convert(Sort sort, Seq params, StringBuilder sb) { + convert(sort, mutable(params), sb); + } + + private static void convert(Sort sort, Collection params, StringBuilder sb) { + if (sort.name().equals(AddSortInjections.SORTPARAM_NAME)) { + String sortVar = sort.params().headOption().get().name(); + sb.append(sortVar); + return; + } + sb.append("Sort"); + convert(sort.name(), sb); + if (!params.contains(sort)) { + sb.append("{"); + String conn = ""; + for (Sort param : iterable(sort.params())) { + sb.append(conn); + convert(param, params, sb); + conn = ", "; + } + sb.append("}"); + } + } + + private String getSortStr(Sort sort) { + StringBuilder strBuilder = new StringBuilder(); + convert(sort, strBuilder); + return strBuilder.toString(); + } + + private void convert( + Map attributes, + Att att, + StringBuilder sb, + Map freeVarsMap, + HasLocation location) { + sb.append("["); + String conn = ""; + + // Emit user groups as group(_) to prevent conflicts between user groups and internals + att = att.withUserGroupsAsGroupAtt(); + + for (Tuple2, ?> attribute : + // Sort to stabilize error messages + stream(att.att()) + .sorted(Comparator.comparing(Tuple2::toString)) + .collect(Collectors.toList())) { + Att.Key key = attribute._1._1; + String strKey = key.key(); + String clsName = attribute._1._2; + Object val = attribute._2; + String strVal = val.toString(); + sb.append(conn); + if (clsName.equals(K.class.getName())) { + convert(strKey, sb); + sb.append("{}("); + convert((K) val, sb); + sb.append(")"); + } else if (clsName.equals(KList.class.getName())) { + convert(strKey, sb); + sb.append("{}("); + String conn2 = ""; + for (K item : ((KList) val).items()) { + sb.append(conn2); + convert(item, sb); + conn2 = ","; } - } - - private void convertSideCondition(K k, String resultSortStr, StringBuilder sb) { - if (k.equals(BooleanUtils.TRUE)) { - sb.append(String.format("\\top{%s}()", resultSortStr)); + sb.append(")"); + } else if (attributes.get(key) != null && attributes.get(key)) { + convert(strKey, sb); + sb.append("{}("); + if (isListOfVarsAttribute(key)) { + convertStringVarList(location, freeVarsMap, strVal, sb); } else { - sb.append(String.format("\\equals{SortBool{},%s}(\n ", resultSortStr)); - convert(k, sb); - sb.append(",\n \\dv{SortBool{}}(\"true\"))"); - } - } - - private KLabel computePolyKLabel(KApply k) { - String labelName = k.klabel().name(); - if (mlBinders.contains(labelName)) { // ML binders are not parametric in the variable so we remove it - List params = mutable(k.klabel().params()); - if (!params.isEmpty()) { - params.remove(0); + switch (strKey) { + case "unit", "element" -> { + Production prod = production(KApply(KLabel(strVal))); + convert(prod.klabel().get(), prod.params(), sb); + sb.append("()"); } - return KLabel(labelName, immutable(params)); - } else { - return k.klabel(); + default -> sb.append(StringUtil.enquoteKString(strVal)); + } } + sb.append(")"); + } else { + convert(strKey, sb); + sb.append("{}()"); + } + conn = ", "; + } + sb.append("]"); + } + + private void convertStringVarList( + HasLocation location, Map freeVarsMap, String strVal, StringBuilder sb) { + if (strVal.trim().isEmpty()) return; + Collection variables = + Arrays.stream(strVal.split(",")) + .map(String::trim) + .map( + s -> { + if (freeVarsMap.containsKey(s)) return freeVarsMap.get(s); + else + throw KEMException.criticalError("No free variable found for " + s, location); + }) + .collect(Collectors.toList()); + String conn = ""; + for (KVariable var : variables) { + sb.append(conn); + convert((K) var, sb); + conn = ","; + } + } + + private boolean isListOfVarsAttribute(Att.Key name) { + return name.equals(Att.CONCRETE()) || name.equals(Att.SYMBOLIC()); + } + + private static String[] asciiReadableEncodingKoreCalc() { + String[] koreEncoder = + Arrays.copyOf( + StringUtil.asciiReadableEncodingDefault, + StringUtil.asciiReadableEncodingDefault.length); + koreEncoder[0x26] = "And-"; + koreEncoder[0x3c] = "-LT-"; + koreEncoder[0x3e] = "-GT-"; + koreEncoder[0x40] = "-AT-"; + koreEncoder[0x5e] = "Xor-"; + return koreEncoder; + } + + private static final Pattern identChar = Pattern.compile("[A-Za-z0-9\\-]"); + public static String[] asciiReadableEncodingKore = asciiReadableEncodingKoreCalc(); + + private static void convert(String name, StringBuilder sb) { + switch (name) { + case "module", + "endmodule", + "sort", + "hooked-sort", + "symbol", + "hooked-symbol", + "alias", + "axiom" -> { + sb.append(name).append("'Kywd'"); + return; + } + default -> {} } + StringBuilder buffer = new StringBuilder(); + StringUtil.encodeStringToAlphanumeric(buffer, name, asciiReadableEncodingKore, identChar, "'"); + sb.append(buffer); + } - - private void collectAttributes(Map attributes, Att att) { - for (Tuple2, ?> attribute : iterable(att.withUserGroupsAsGroupAtt().att())) { - Att.Key name = attribute._1._1; - Object val = attribute._2; - String strVal = val.toString(); - if (strVal.equals("")) { - if (!attributes.containsKey(name)) { - attributes.put(name, false); - } - } else { - attributes.put(name, true); - } + public Set collectAnonymousVariables(K k) { + Set anonymousVariables = new HashSet<>(); + new VisitK() { + @Override + public void apply(KApply k) { + if (mlBinders.contains(k.klabel().name()) + && k.items().get(0).att().contains(Att.ANONYMOUS())) { + throw KEMException.internalError("Nested quantifier over anonymous variables."); } - } + for (K item : k.items()) { + apply(item); + } + } - private static final Production INJ_PROD = Production(KLabel(KLabels.INJ, Sort("S1"), Sort("S2")), Sort("S2"), Seq(NonTerminal(Sort("S1"))), Att()); - - - private Production production(KApply term) { - return production(term, false); - } - - private Production production(KApply term, boolean instantiatePolySorts) { - KLabel klabel = term.klabel(); - if (klabel.name().equals(KLabels.INJ)) - return instantiatePolySorts ? INJ_PROD.substitute(term.klabel().params()) : INJ_PROD; - Option> prods = module.productionsFor().get(klabel.head()); - if (!(prods.nonEmpty() && prods.get().size() == 1)) - throw KEMException.compilerError("Expected to find exactly one production for KLabel: " + klabel + " found: " + prods.getOrElse(Collections::Set).size()); - return instantiatePolySorts ? prods.get().head().substitute(term.klabel().params()) : prods.get().head(); - } - - private static String convertBuiltinLabel(String klabel) { - return switch (klabel) { - case "#Bottom" -> "\\bottom"; - case "#Top" -> "\\top"; - case "#Or" -> "\\or"; - case "#And" -> "\\and"; - case "#Not" -> "\\not"; - case "#Floor" -> "\\floor"; - case "#Ceil" -> "\\ceil"; - case "#Equals" -> "\\equals"; - case "#Implies" -> "\\implies"; - case "#Exists" -> "\\exists"; - case "#Forall" -> "\\forall"; - case "#AG" -> "allPathGlobally"; - case "weakExistsFinally" -> ONE_PATH_OP; - case "weakAlwaysFinally" -> ALL_PATH_OP; - default -> throw KEMException.compilerError("Unsuppored kore connective in rule: " + klabel); - }; - } + @Override + public void apply(KVariable k) { + if (k.att().contains(Att.ANONYMOUS())) { + anonymousVariables.add(k); + } + } + }.apply(k); + return anonymousVariables; + } + + public void convert(K k, StringBuilder sb) { + new VisitK() { + @Override + public void apply(KApply k) { + KLabel label = computePolyKLabel(k); + String conn = ""; + if (mlBinders.contains(k.klabel().name()) + && k.items().get(0).att().contains(Att.ANONYMOUS())) { + // Handle #Forall _ / #Exists _ + Set anonymousVariables = collectAnonymousVariables(k.items().get(1)); - public static void convert(KLabel klabel, StringBuilder sb) { - convert(klabel, java.util.Collections.emptySet(), sb); - } + // Quantify over all anonymous variables. + for (K variable : anonymousVariables) { + sb.append(conn); + convert(label, sb); + sb.append("("); + apply(variable); + conn = ","; + } - private void convert(KLabel klabel, Seq params, StringBuilder sb) { - convert(klabel, mutable(params), sb); - } + // We assume that mlBinder only has two children. + sb.append(conn); + apply(k.items().get(1)); - private static void convert(KLabel klabel, Collection params, StringBuilder sb) { - if (klabel.name().equals(KLabels.INJ)) { - sb.append(klabel.name()); - } else if (ConstructorChecks.isBuiltinLabel(klabel)) { - sb.append(convertBuiltinLabel(klabel.name())); + for (int i = 0; i < anonymousVariables.size(); i++) { + sb.append(")"); + } } else { - sb.append("Lbl"); - convert(klabel.name(), sb); - } - sb.append("{"); - String conn = ""; - for (Sort param : iterable(klabel.params())) { + convert(label, sb); + sb.append("("); + for (K item : k.items()) { sb.append(conn); - convert(param, params, sb); - conn = ", "; + apply(item); + conn = ","; + } + sb.append(")"); } - sb.append("}"); - } + } - private void convert(KLabel klabel, Production prod, StringBuilder sb) { - if (klabel.name().equals(KLabels.INJ)) { - sb.append(klabel.name()); + @Override + public void apply(KToken k) { + sb.append("\\dv{"); + convert(k.sort(), sb); + sb.append("}("); + if (module + .sortAttributesFor() + .get(k.sort().head()) + .getOrElse(Att::empty) + .getOptional(Att.HOOK()) + .orElse("") + .equals("STRING.String")) { + sb.append(StringUtil.escapeNonASCII(k.s())); + } else if (module + .sortAttributesFor() + .get(k.sort().head()) + .getOrElse(Att::empty) + .getOptional(Att.HOOK()) + .orElse("") + .equals("BYTES.Bytes")) { + sb.append(StringUtil.escapeNonASCII(k.s().substring(1))); // remove the leading `b` } else { - sb.append("Lbl"); - convert(klabel.name(), sb); - } - sb.append("{"); - String conn = ""; - for (Sort param : iterable(klabel.params())) { - sb.append(conn); - convert(param, prod, sb); - conn = ", "; + sb.append(StringUtil.enquoteKString(k.s())); } - sb.append("}"); - } - - private void convert(Sort sort, Production prod, StringBuilder sb) { - convert(sort, prod.params(), sb); - } - - public static void convert(Sort sort, StringBuilder sb) { - convert(sort, java.util.Collections.emptySet(), sb); - } - - private void convert(SortHead sort, StringBuilder sb) { - List params = new ArrayList<>(); - for (int i = 0; i < sort.params(); i++) { - params.add(Sort("S" + i)); + sb.append(")"); } - convert(Sort(sort.name(), immutable(params)), params, sb); - } - private void convert(Sort sort, Seq params, StringBuilder sb) { - convert(sort, mutable(params), sb); - } - - private static void convert(Sort sort, Collection params, StringBuilder sb) { - if (sort.name().equals(AddSortInjections.SORTPARAM_NAME)) { - String sortVar = sort.params().headOption().get().name(); - sb.append(sortVar); - return; - } - sb.append("Sort"); - convert(sort.name(), sb); - if (!params.contains(sort)) { - sb.append("{"); - String conn = ""; - for (Sort param : iterable(sort.params())) { - sb.append(conn); - convert(param, params, sb); - conn = ", "; + @Override + public void apply(KSequence k) { + for (int i = 0; i < k.items().size(); i++) { + K item = k.items().get(i); + boolean isList = item.att().get(Sort.class).equals(Sorts.K()); + if (i == k.items().size() - 1) { + if (isList) { + apply(item); + } else { + sb.append("kseq{}("); + apply(item); + sb.append(",dotk{}())"); } - sb.append("}"); - } - } - - private String getSortStr(Sort sort) { - StringBuilder strBuilder = new StringBuilder(); - convert(sort, strBuilder); - return strBuilder.toString(); - } - - private void convert(Map attributes, Att att, StringBuilder sb, Map freeVarsMap, HasLocation location) { - sb.append("["); - String conn = ""; - - // Emit user groups as group(_) to prevent conflicts between user groups and internals - att = att.withUserGroupsAsGroupAtt(); - - for (Tuple2, ?> attribute : - // Sort to stabilize error messages - stream(att.att()).sorted(Comparator.comparing(Tuple2::toString)).collect(Collectors.toList())) { - Att.Key key = attribute._1._1; - String strKey = key.key(); - String clsName = attribute._1._2; - Object val = attribute._2; - String strVal = val.toString(); - sb.append(conn); - if (clsName.equals(K.class.getName())) { - convert(strKey, sb); - sb.append("{}("); - convert((K) val, sb); - sb.append(")"); - } else if (clsName.equals(KList.class.getName())) { - convert(strKey, sb); - sb.append("{}("); - String conn2 = ""; - for (K item : ((KList)val).items()) { - sb.append(conn2); - convert(item, sb); - conn2 = ","; - } - sb.append(")"); - } else if (attributes.get(key) != null && attributes.get(key)) { - convert(strKey, sb); - sb.append("{}("); - if (isListOfVarsAttribute(key)) { - convertStringVarList(location, freeVarsMap, strVal, sb); - } else { - switch (strKey) { - case "unit", "element" -> { - Production prod = production(KApply(KLabel(strVal))); - convert(prod.klabel().get(), prod.params(), sb); - sb.append("()"); - } - default -> sb.append(StringUtil.enquoteKString(strVal)); - } - } - sb.append(")"); + } else { + if (item.att().get(Sort.class).equals(Sorts.K())) { + sb.append("append{}("); } else { - convert(strKey, sb); - sb.append("{}()"); + sb.append("kseq{}("); } - conn = ", "; - } - sb.append("]"); - } - - private void convertStringVarList(HasLocation location, Map freeVarsMap, String strVal, StringBuilder sb) { - if (strVal.trim().isEmpty()) return; - Collection variables = Arrays.stream(strVal.split(",")).map(String::trim) - .map(s -> { - if (freeVarsMap.containsKey(s)) return freeVarsMap.get(s); - else throw KEMException.criticalError("No free variable found for " + s, location); - }).collect(Collectors.toList()); - String conn = ""; - for (KVariable var : variables) { - sb.append(conn); - convert((K) var, sb); - conn = ","; - } - } - - private boolean isListOfVarsAttribute(Att.Key name) { - return name.equals(Att.CONCRETE()) || name.equals(Att.SYMBOLIC()); - } - - private static String[] asciiReadableEncodingKoreCalc() { - String[] koreEncoder = Arrays.copyOf(StringUtil.asciiReadableEncodingDefault, StringUtil.asciiReadableEncodingDefault.length); - koreEncoder[0x26] = "And-"; - koreEncoder[0x3c] = "-LT-"; - koreEncoder[0x3e] = "-GT-"; - koreEncoder[0x40] = "-AT-"; - koreEncoder[0x5e] = "Xor-"; - return koreEncoder; - } - - private static final Pattern identChar = Pattern.compile("[A-Za-z0-9\\-]"); - public static String[] asciiReadableEncodingKore = asciiReadableEncodingKoreCalc(); - - private static void convert(String name, StringBuilder sb) { - switch (name) { - case "module", "endmodule", "sort", "hooked-sort", "symbol", "hooked-symbol", "alias", "axiom" -> { - sb.append(name).append("'Kywd'"); - return; + apply(item); + sb.append(","); + } } - default -> { + if (k.items().size() == 0) { + sb.append("dotk{}()"); } + for (int i = 0; i < k.items().size() - 1; i++) { + sb.append(")"); } - StringBuilder buffer = new StringBuilder(); - StringUtil.encodeStringToAlphanumeric(buffer, name, asciiReadableEncodingKore, identChar, "'"); - sb.append(buffer); - } - - public Set collectAnonymousVariables(K k){ - Set anonymousVariables = new HashSet<>(); - new VisitK() { - @Override - public void apply(KApply k) { - if (mlBinders.contains(k.klabel().name()) && k.items().get(0).att().contains(Att.ANONYMOUS())){ - throw KEMException.internalError("Nested quantifier over anonymous variables."); - } - for (K item : k.items()) { - apply(item); - } - } - - @Override - public void apply(KVariable k) { - if (k.att().contains(Att.ANONYMOUS())) { - anonymousVariables.add(k); - } - } - - }.apply(k); - return anonymousVariables; - } - - public void convert(K k, StringBuilder sb) { - new VisitK() { - @Override - public void apply(KApply k) { - KLabel label = computePolyKLabel(k); - String conn = ""; - if (mlBinders.contains(k.klabel().name()) && k.items().get(0).att().contains(Att.ANONYMOUS())){ - // Handle #Forall _ / #Exists _ - Set anonymousVariables = collectAnonymousVariables(k.items().get(1)); - - // Quantify over all anonymous variables. - for (K variable : anonymousVariables) { - sb.append(conn); - convert(label, sb); - sb.append("("); - apply(variable); - conn = ","; - } - - // We assume that mlBinder only has two children. - sb.append(conn); - apply(k.items().get(1)); - - for (int i = 0; i < anonymousVariables.size(); i++) { - sb.append(")"); - } - } else { - convert(label, sb); - sb.append("("); - for (K item : k.items()) { - sb.append(conn); - apply(item); - conn = ","; - } - sb.append(")"); - } - } - - @Override - public void apply(KToken k) { - sb.append("\\dv{"); - convert(k.sort(), sb); - sb.append("}("); - if (module.sortAttributesFor().get(k.sort().head()).getOrElse(Att::empty).getOptional(Att.HOOK()).orElse("").equals("STRING.String")) { - sb.append(StringUtil.escapeNonASCII(k.s())); - } else if (module.sortAttributesFor().get(k.sort().head()).getOrElse(Att::empty).getOptional(Att.HOOK()).orElse("").equals("BYTES.Bytes")) { - sb.append(StringUtil.escapeNonASCII(k.s().substring(1))); // remove the leading `b` - } else { - sb.append(StringUtil.enquoteKString(k.s())); - } - sb.append(")"); - } - - @Override - public void apply(KSequence k) { - for (int i = 0; i < k.items().size(); i++) { - K item = k.items().get(i); - boolean isList = item.att().get(Sort.class).equals(Sorts.K()); - if (i == k.items().size() - 1) { - if (isList) { - apply(item); - } else { - sb.append("kseq{}("); - apply(item); - sb.append(",dotk{}())"); - } - } else { - if (item.att().get(Sort.class).equals(Sorts.K())) { - sb.append("append{}("); - } else { - sb.append("kseq{}("); - } - apply(item); - sb.append(","); - } - } - if (k.items().size() == 0) { - sb.append("dotk{}()"); - } - for (int i = 0; i < k.items().size() - 1; i++) { - sb.append(")"); - } - } + } - @Override - public void apply(KVariable k) { - boolean setVar = k.name().startsWith("@"); - if (setVar) { - sb.append('@'); - } - sb.append("Var"); - String name = setVar ? k.name().substring(1) : k.name(); - convert(name, sb); - sb.append(":"); - convert(k.att().getOptional(Sort.class).orElse(Sorts.K()), sb); - } + @Override + public void apply(KVariable k) { + boolean setVar = k.name().startsWith("@"); + if (setVar) { + sb.append('@'); + } + sb.append("Var"); + String name = setVar ? k.name().substring(1) : k.name(); + convert(name, sb); + sb.append(":"); + convert(k.att().getOptional(Sort.class).orElse(Sorts.K()), sb); + } - @Override - public void apply(KRewrite k) { - sb.append("\\rewrites{"); - convert(k.att().get(Sort.class), sb); - sb.append("}("); - apply(k.left()); - sb.append(","); - apply(k.right()); - sb.append(")"); - } + @Override + public void apply(KRewrite k) { + sb.append("\\rewrites{"); + convert(k.att().get(Sort.class), sb); + sb.append("}("); + apply(k.left()); + sb.append(","); + apply(k.right()); + sb.append(")"); + } - @Override - public void apply(KAs k) { - Sort sort = k.att().get(Sort.class); - sb.append("\\and{"); - convert(sort, sb); - sb.append("}("); - apply(k.pattern()); - sb.append(","); - apply(k.alias()); - sb.append(")"); - } + @Override + public void apply(KAs k) { + Sort sort = k.att().get(Sort.class); + sb.append("\\and{"); + convert(sort, sb); + sb.append("}("); + apply(k.pattern()); + sb.append(","); + apply(k.alias()); + sb.append(")"); + } - @Override - public void apply(InjectedKLabel k) { - throw KEMException.internalError("Cannot yet translate #klabel to kore", k); - } - }.apply(k); - } + @Override + public void apply(InjectedKLabel k) { + throw KEMException.internalError("Cannot yet translate #klabel to kore", k); + } + }.apply(k); + } } diff --git a/kernel/src/main/java/org/kframework/compile/AbstractBackend.java b/kernel/src/main/java/org/kframework/compile/AbstractBackend.java index 311de41c2b9..18e5f1dab88 100644 --- a/kernel/src/main/java/org/kframework/compile/AbstractBackend.java +++ b/kernel/src/main/java/org/kframework/compile/AbstractBackend.java @@ -1,24 +1,22 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; -import org.kframework.definition.Definition; -import scala.Function1; - -import javax.annotation.Nullable; import java.util.List; import java.util.function.Function; +import javax.annotation.Nullable; +import org.kframework.definition.Definition; +import scala.Function1; /** - * @author Denis Bogdanas - * Created on 09-Jan-20. + * @author Denis Bogdanas Created on 09-Jan-20. */ public abstract class AbstractBackend implements Backend { - @Override - public Function proofDefinitionNonCachedSteps( - @Nullable List extraConcreteRuleLabels) { - Function1 markExtraConcrete = - def -> MarkExtraConcreteRules.mark(def, extraConcreteRuleLabels); - return markExtraConcrete::apply; - } + @Override + public Function proofDefinitionNonCachedSteps( + @Nullable List extraConcreteRuleLabels) { + Function1 markExtraConcrete = + def -> MarkExtraConcreteRules.mark(def, extraConcreteRuleLabels); + return markExtraConcrete::apply; + } } diff --git a/kernel/src/main/java/org/kframework/compile/AddCoolLikeAtt.java b/kernel/src/main/java/org/kframework/compile/AddCoolLikeAtt.java index 983aff86842..cd2417ed296 100644 --- a/kernel/src/main/java/org/kframework/compile/AddCoolLikeAtt.java +++ b/kernel/src/main/java/org/kframework/compile/AddCoolLikeAtt.java @@ -7,74 +7,67 @@ import org.kframework.definition.Module; import org.kframework.definition.Rule; import org.kframework.definition.Sentence; +import org.kframework.kore.FoldK; import org.kframework.kore.K; import org.kframework.kore.KApply; import org.kframework.kore.KSequence; import org.kframework.kore.KVariable; -import org.kframework.kore.FoldK; public record AddCoolLikeAtt(Module mod) { - private Rule add(Rule rule) { - return new Rule( - rule.body(), - rule.requires(), - rule.ensures(), - transform(rule.body(), rule.att())); - } - - private Context add(Context context) { - return new Context( - context.body(), - context.requires(), - transform(context.body(), context.att())); - } + private Rule add(Rule rule) { + return new Rule( + rule.body(), rule.requires(), rule.ensures(), transform(rule.body(), rule.att())); + } - private ContextAlias add(ContextAlias context) { - return new ContextAlias( - context.body(), - context.requires(), - transform(context.body(), context.att())); - } + private Context add(Context context) { + return new Context( + context.body(), context.requires(), transform(context.body(), context.att())); + } - private Att transform(K body, Att att) { - if (new FoldK() { - @Override - public Boolean unit() { - return false; - } + private ContextAlias add(ContextAlias context) { + return new ContextAlias( + context.body(), context.requires(), transform(context.body(), context.att())); + } - @Override - public Boolean apply(KApply k) { - if (mod.attributesFor().get(k.klabel()).getOrElse(Att::empty).contains(Att.MAINCELL())) { - if (k.items().get(0) instanceof KSequence seq) { - if (seq.items().size() > 1 && seq.items().get(0) instanceof KVariable) { - return true; - } - } - } - return super.apply(k); - } + private Att transform(K body, Att att) { + if (new FoldK() { + @Override + public Boolean unit() { + return false; + } - @Override - public Boolean merge(Boolean a, Boolean b) { - return a || b; + @Override + public Boolean apply(KApply k) { + if (mod.attributesFor().get(k.klabel()).getOrElse(Att::empty).contains(Att.MAINCELL())) { + if (k.items().get(0) instanceof KSequence seq) { + if (seq.items().size() > 1 && seq.items().get(0) instanceof KVariable) { + return true; } - }.apply(RewriteToTop.toLeft(body))) { - return att.add(Att.COOL_LIKE()); + } } - return att; + return super.apply(k); + } + + @Override + public Boolean merge(Boolean a, Boolean b) { + return a || b; + } + }.apply(RewriteToTop.toLeft(body))) { + return att.add(Att.COOL_LIKE()); } + return att; + } - public synchronized Sentence add(Sentence s) { - if (s instanceof Rule) { - return add((Rule) s); - } else if (s instanceof Context) { - return add((Context) s); - } else if (s instanceof ContextAlias) { - return add((ContextAlias) s); - } else { - return s; - } + public synchronized Sentence add(Sentence s) { + if (s instanceof Rule) { + return add((Rule) s); + } else if (s instanceof Context) { + return add((Context) s); + } else if (s instanceof ContextAlias) { + return add((ContextAlias) s); + } else { + return s; } + } } diff --git a/kernel/src/main/java/org/kframework/compile/AddImplicitComputationCell.java b/kernel/src/main/java/org/kframework/compile/AddImplicitComputationCell.java index 78a23aea541..a7bc3873216 100644 --- a/kernel/src/main/java/org/kframework/compile/AddImplicitComputationCell.java +++ b/kernel/src/main/java/org/kframework/compile/AddImplicitComputationCell.java @@ -1,6 +1,8 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import java.util.List; +import java.util.stream.Stream; import org.kframework.attributes.Att; import org.kframework.builtin.KLabels; import org.kframework.definition.*; @@ -10,102 +12,104 @@ import org.kframework.kore.KLabel; import org.kframework.kore.KRewrite; -import java.util.List; -import java.util.stream.Stream; - /** - * If a SemanticSentence (Rule or Context) has a body that is not wrapped in any cell, - * wrap it in a {@code } cell + * If a SemanticSentence (Rule or Context) has a body that is not wrapped in any cell, wrap it in a + * {@code } cell */ public record AddImplicitComputationCell(ConfigurationInfo cfg, LabelInfo labelInfo) { - public static Definition transformDefinition(Definition input) { - ConfigurationInfoFromModule configInfo = new ConfigurationInfoFromModule(input.mainModule()); - LabelInfo labelInfo = new LabelInfoFromModule(input.mainModule()); - return DefinitionTransformer.fromSentenceTransformer( - new AddImplicitComputationCell(configInfo, labelInfo)::apply, - "concretizing configuration").apply(input); + public static Definition transformDefinition(Definition input) { + ConfigurationInfoFromModule configInfo = new ConfigurationInfoFromModule(input.mainModule()); + LabelInfo labelInfo = new LabelInfoFromModule(input.mainModule()); + return DefinitionTransformer.fromSentenceTransformer( + new AddImplicitComputationCell(configInfo, labelInfo)::apply, + "concretizing configuration") + .apply(input); + } + + public static Module transformModule(Module mod) { + ConfigurationInfoFromModule configInfo = new ConfigurationInfoFromModule(mod); + LabelInfo labelInfo = new LabelInfoFromModule(mod); + return ModuleTransformer.fromSentenceTransformer( + new AddImplicitComputationCell(configInfo, labelInfo)::apply, + "concretizing configuration") + .apply(mod); + } + + public Sentence apply(Module m, Sentence s) { + if (skipSentence(s)) { + return s; } - public static Module transformModule(Module mod) { - ConfigurationInfoFromModule configInfo = new ConfigurationInfoFromModule(mod); - LabelInfo labelInfo = new LabelInfoFromModule(mod); - return ModuleTransformer.fromSentenceTransformer( - new AddImplicitComputationCell(configInfo, labelInfo)::apply, - "concretizing configuration").apply(mod); + if (s instanceof RuleOrClaim rule) { + return rule.newInstance( + apply(rule.body(), m, rule instanceof Claim), + rule.requires(), + rule.ensures(), + rule.att()); + } else if (s instanceof Context context) { + return new Context(apply(context.body(), m, false), context.requires(), context.att()); + } else { + return s; } - - public Sentence apply(Module m, Sentence s) { - if (skipSentence(s)) { - return s; - } - - if (s instanceof RuleOrClaim rule) { - return rule.newInstance(apply(rule.body(), m, rule instanceof Claim), rule.requires(), rule.ensures(), rule.att()); - } else if (s instanceof Context context) { - return new Context(apply(context.body(), m, false), context.requires(), context.att()); - } else { - return s; - } + } + + private boolean skipSentence(Sentence s) { + return ExpandMacros.isMacro(s) + || s.att().contains(Att.ANYWHERE()) + || s.att().contains(Att.SIMPLIFICATION()) + || s.att().contains(Att.KORE()); + } + + // If there are multiple cells mentioned in the split configuration, we don't + // apply the implicit cell, unless the configuration is a claim and the second + // cell mentioned is the automatically-added cell. + private boolean shouldConsider(List items, boolean isClaim) { + if (items.size() == 1) { + return true; + } else if (items.size() == 2 && isClaim) { + K second = items.get(1); + if (second instanceof KApply app) { + return app.klabel() == KLabels.GENERATED_COUNTER_CELL; + } } - private boolean skipSentence(Sentence s) { - return ExpandMacros.isMacro(s) - || s.att().contains(Att.ANYWHERE()) - || s.att().contains(Att.SIMPLIFICATION()) - || s.att().contains(Att.KORE()); - } + return false; + } - // If there are multiple cells mentioned in the split configuration, we don't - // apply the implicit cell, unless the configuration is a claim and the second - // cell mentioned is the automatically-added cell. - private boolean shouldConsider(List items, boolean isClaim) { - if (items.size() == 1) { - return true; - } else if (items.size() == 2 && isClaim) { - K second = items.get(1); - if(second instanceof KApply app) { - return app.klabel() == KLabels.GENERATED_COUNTER_CELL; - } - } - - return false; + private boolean canAddImplicitKCell(K item) { + if (isCell(item)) { + return false; } - private boolean canAddImplicitKCell(K item) { - if(isCell(item)) { - return false; - } - - if (item instanceof final KRewrite rew) { - return Stream.concat( - IncompleteCellUtils.flattenCells(rew.left()).stream(), - IncompleteCellUtils.flattenCells(rew.right()).stream()) - .noneMatch(this::isCell); - } - - return true; + if (item instanceof final KRewrite rew) { + return Stream.concat( + IncompleteCellUtils.flattenCells(rew.left()).stream(), + IncompleteCellUtils.flattenCells(rew.right()).stream()) + .noneMatch(this::isCell); } - private K apply(K term, Module m, boolean isClaim) { - if (m.isFunction(term)) return term; + return true; + } - List items = IncompleteCellUtils.flattenCells(term); - if (!shouldConsider(items, isClaim)) { - return term; - } + private K apply(K term, Module m, boolean isClaim) { + if (m.isFunction(term)) return term; - K item = items.get(0); - if (!canAddImplicitKCell(item)) { - return term; - } - - KLabel computation = cfg.getCellLabel(cfg.getComputationCell()); - return IncompleteCellUtils.make(computation, false, item, true); + List items = IncompleteCellUtils.flattenCells(term); + if (!shouldConsider(items, isClaim)) { + return term; } - private boolean isCell(K k) { - return k instanceof KApply - && cfg.isCell(labelInfo.getCodomain(((KApply) k).klabel())); + K item = items.get(0); + if (!canAddImplicitKCell(item)) { + return term; } + + KLabel computation = cfg.getCellLabel(cfg.getComputationCell()); + return IncompleteCellUtils.make(computation, false, item, true); + } + + private boolean isCell(K k) { + return k instanceof KApply && cfg.isCell(labelInfo.getCodomain(((KApply) k).klabel())); + } } diff --git a/kernel/src/main/java/org/kframework/compile/AddImplicitCounterCell.java b/kernel/src/main/java/org/kframework/compile/AddImplicitCounterCell.java index 8fc7fb045b8..e644e9e305e 100644 --- a/kernel/src/main/java/org/kframework/compile/AddImplicitCounterCell.java +++ b/kernel/src/main/java/org/kframework/compile/AddImplicitCounterCell.java @@ -2,51 +2,52 @@ package org.kframework.compile; +import static org.kframework.kore.KORE.*; + +import java.util.*; import org.kframework.builtin.KLabels; import org.kframework.definition.Claim; -import org.kframework.definition.Sentence; import org.kframework.definition.Module; +import org.kframework.definition.Sentence; import org.kframework.kore.*; -import java.util.*; - -import static org.kframework.kore.KORE.*; - /** - * If a claim doesn't mention the generated counter cell (for resolving fresh variables), - * then add an implicit ` _ => ?_ ` to the claim. + * If a claim doesn't mention the generated counter cell (for resolving fresh variables), then add + * an implicit ` _ => ?_ ` to the claim. */ public class AddImplicitCounterCell { - public AddImplicitCounterCell() {} + public AddImplicitCounterCell() {} - public Sentence apply(Module m, Sentence s) { - if(s instanceof Claim claim) { - return claim.newInstance(apply(claim.body(), m), claim.requires(), claim.ensures(), claim.att()); - } - return s; + public Sentence apply(Module m, Sentence s) { + if (s instanceof Claim claim) { + return claim.newInstance( + apply(claim.body(), m), claim.requires(), claim.ensures(), claim.att()); } - - // We shouldn't add the implicit cell to the claim if the user has already written - // it explicitly. - private boolean alreadyHasGeneratedCounter(List items) { - return items.stream() - .filter(cell -> cell instanceof KApply) - .anyMatch(cell -> ((KApply) cell).klabel().equals(KLabels.GENERATED_COUNTER_CELL)); - } - - private K apply(K term, Module m) { - List items = IncompleteCellUtils.flattenCells(term); - if(alreadyHasGeneratedCounter(items)) { - return term; - } - - KRewrite rew = KRewrite( - KApply(KLabel("#SemanticCastToInt"), ResolveAnonVar.ANON_VAR), - KApply(KLabel("#SemanticCastToInt"), ResolveAnonVar.FRESH_ANON_VAR)); - items.add(IncompleteCellUtils.make( - KLabels.GENERATED_COUNTER_CELL, false, Collections.singletonList(rew), false)); - return IncompleteCellUtils.makeBody(items); + return s; + } + + // We shouldn't add the implicit cell to the claim if the user has already written + // it explicitly. + private boolean alreadyHasGeneratedCounter(List items) { + return items.stream() + .filter(cell -> cell instanceof KApply) + .anyMatch(cell -> ((KApply) cell).klabel().equals(KLabels.GENERATED_COUNTER_CELL)); + } + + private K apply(K term, Module m) { + List items = IncompleteCellUtils.flattenCells(term); + if (alreadyHasGeneratedCounter(items)) { + return term; } + KRewrite rew = + KRewrite( + KApply(KLabel("#SemanticCastToInt"), ResolveAnonVar.ANON_VAR), + KApply(KLabel("#SemanticCastToInt"), ResolveAnonVar.FRESH_ANON_VAR)); + items.add( + IncompleteCellUtils.make( + KLabels.GENERATED_COUNTER_CELL, false, Collections.singletonList(rew), false)); + return IncompleteCellUtils.makeBody(items); + } } diff --git a/kernel/src/main/java/org/kframework/compile/AddParentCells.java b/kernel/src/main/java/org/kframework/compile/AddParentCells.java index 3fd42ce8d18..7430f3bfd3a 100644 --- a/kernel/src/main/java/org/kframework/compile/AddParentCells.java +++ b/kernel/src/main/java/org/kframework/compile/AddParentCells.java @@ -2,19 +2,30 @@ package org.kframework.compile; +import static org.kframework.kore.KORE.*; + import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.google.common.collect.Sets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.kframework.builtin.KLabels; -import org.kframework.definition.Claim; import org.kframework.definition.Context; -import org.kframework.definition.Rule; import org.kframework.definition.RuleOrClaim; import org.kframework.definition.Sentence; import org.kframework.kore.K; -import org.kframework.kore.KAs; import org.kframework.kore.KApply; +import org.kframework.kore.KAs; import org.kframework.kore.KLabel; import org.kframework.kore.KRewrite; import org.kframework.kore.KSequence; @@ -22,299 +33,311 @@ import org.kframework.kore.Sort; import org.kframework.utils.errorsystem.KEMException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.TreeMap; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.kframework.kore.KORE.*; - /** - * Add omitted parent cells to a term written using configuration abstraction. - * It only completes the contents of existing cells, so an earlier pass should add - * a top cell around rule bodies if completion to the top is desired. - * Newly inserted cells have dots if and only if the parent cell they were added under did - * (dots are elimiinated in the {@link CloseCells} pass). + * Add omitted parent cells to a term written using configuration abstraction. It only completes the + * contents of existing cells, so an earlier pass should add a top cell around rule bodies if + * completion to the top is desired. Newly inserted cells have dots if and only if the parent cell + * they were added under did (dots are elimiinated in the {@link CloseCells} pass). */ public class AddParentCells { - private final ConcretizationInfo cfg; + private final ConcretizationInfo cfg; - public AddParentCells(ConfigurationInfo configInfo, LabelInfo labelInfo) { - cfg = new ConcretizationInfo(configInfo, labelInfo); - } + public AddParentCells(ConfigurationInfo configInfo, LabelInfo labelInfo) { + cfg = new ConcretizationInfo(configInfo, labelInfo); + } - Stream streamSideCells(K side) { - List cells = IncompleteCellUtils.flattenCells(side); - // TODO error handling - return cells.stream(); - } + Stream streamSideCells(K side) { + List cells = IncompleteCellUtils.flattenCells(side); + // TODO error handling + return cells.stream(); + } - protected List makeParents(KLabel parent, boolean ellipses, - List allChildren) { - // List rewrites -// rewrites.stream().map(r -> r.left()).flatMap(t -> if(t.)); + protected List makeParents(KLabel parent, boolean ellipses, List allChildren) { + // List rewrites + // rewrites.stream().map(r -> r.left()).flatMap(t -> if(t.)); - List children = allChildren.stream().filter(t -> !(t instanceof KRewrite)).collect(Collectors.toList()); - List rewrites = allChildren.stream().filter(t -> t instanceof KRewrite).map(t -> (KRewrite) t).collect(Collectors.toList()); + List children = + allChildren.stream().filter(t -> !(t instanceof KRewrite)).collect(Collectors.toList()); + List rewrites = + allChildren.stream() + .filter(t -> t instanceof KRewrite) + .map(t -> (KRewrite) t) + .collect(Collectors.toList()); - // see if all children can fit together - Set usedCells = Sets.newHashSet(); - BiFunction, Set, Boolean> useCells = (cells, used) -> { - for (K k : cells) { - Sort sort = cfg.getCellSort(k); - if (cfg.getMultiplicity(sort) != ConfigurationInfo.Multiplicity.STAR) { - if (used.contains(sort)) { - return false; - } else { - used.add(sort); - } - } + // see if all children can fit together + Set usedCells = Sets.newHashSet(); + BiFunction, Set, Boolean> useCells = + (cells, used) -> { + for (K k : cells) { + Sort sort = cfg.getCellSort(k); + if (cfg.getMultiplicity(sort) != ConfigurationInfo.Multiplicity.STAR) { + if (used.contains(sort)) { + return false; + } else { + used.add(sort); + } } - return true; + } + return true; }; - boolean allFitTogether = useCells.apply(children, usedCells); - if (allFitTogether) { - Function, List> flattenRewrite = f -> rewrites.stream().map(f).flatMap - (this::streamSideCells).collect(Collectors.toList()); + boolean allFitTogether = useCells.apply(children, usedCells); + if (allFitTogether) { + Function, List> flattenRewrite = + f -> rewrites.stream().map(f).flatMap(this::streamSideCells).collect(Collectors.toList()); - List leftChildren = flattenRewrite.apply(KRewrite::left); - Set usedLeft = Sets.newHashSet(usedCells); - boolean leftFit = useCells.apply(leftChildren, usedLeft); - List rightChildren = flattenRewrite.apply(KRewrite::right); - Set usedRight = Sets.newHashSet(usedCells); - boolean rightFit = useCells.apply(rightChildren, usedRight); - allFitTogether = leftFit && rightFit; - } - if (allFitTogether) { - return Lists.newArrayList(IncompleteCellUtils.make(parent, ellipses, allChildren, ellipses)); - } - - // Otherwise, see if they are forced to have separate parents... + List leftChildren = flattenRewrite.apply(KRewrite::left); + Set usedLeft = Sets.newHashSet(usedCells); + boolean leftFit = useCells.apply(leftChildren, usedLeft); + List rightChildren = flattenRewrite.apply(KRewrite::right); + Set usedRight = Sets.newHashSet(usedCells); + boolean rightFit = useCells.apply(rightChildren, usedRight); + allFitTogether = leftFit && rightFit; + } + if (allFitTogether) { + return Lists.newArrayList(IncompleteCellUtils.make(parent, ellipses, allChildren, ellipses)); + } - boolean forcedSeparate = true; - if (!children.isEmpty()) { - Sort label = cfg.getCellSort(children.get(0)); - if (cfg.getMultiplicity(label) == ConfigurationInfo.Multiplicity.STAR) { - forcedSeparate = false; - } else { - for (K child : children) { - Sort sort = cfg.getCellSort(child); - if (!sort.equals(label)) { - forcedSeparate = false; - break; - } - } - } - if (forcedSeparate) { - for (KRewrite rew : rewrites) { - if (!(streamSideCells(rew.left()).anyMatch(l -> cfg.getCellSort(l).equals(label)) - || streamSideCells(rew.left()).anyMatch(l -> cfg.getCellSort(l).equals(label)))) { - forcedSeparate = false; - break; - } - } - } - } - if (forcedSeparate) { - for (KRewrite rew1 : rewrites) { - for (KRewrite rew2 : rewrites) { - Set left1NonRepeatable = streamSideCells(rew1.left()).map(cfg::getCellSort) - .filter(l -> cfg.getMultiplicity(l) != ConfigurationInfo.Multiplicity.STAR) - .collect(Collectors.toSet()); - boolean lhsConflict = streamSideCells(rew2.left()).map(cfg::getCellSort) - .filter(left1NonRepeatable::contains).count() >= 1; + // Otherwise, see if they are forced to have separate parents... - Set right1NonRepeatable = streamSideCells(rew1.right()).map(cfg::getCellSort) - .filter(l -> cfg.getMultiplicity(l) != ConfigurationInfo.Multiplicity.STAR) - .collect(Collectors.toSet()); - boolean rhsConflict = streamSideCells(rew2.right()).map(cfg::getCellSort) - .filter(right1NonRepeatable::contains).count() >= 1; - if (!(lhsConflict || rhsConflict)) { - forcedSeparate = false; - break; - } - } - } + boolean forcedSeparate = true; + if (!children.isEmpty()) { + Sort label = cfg.getCellSort(children.get(0)); + if (cfg.getMultiplicity(label) == ConfigurationInfo.Multiplicity.STAR) { + forcedSeparate = false; + } else { + for (K child : children) { + Sort sort = cfg.getCellSort(child); + if (!sort.equals(label)) { + forcedSeparate = false; + break; + } } - if (forcedSeparate) { - return allChildren.stream() - .map(k -> IncompleteCellUtils.make(parent, ellipses, k, ellipses)) - .collect(Collectors.toList()); + } + if (forcedSeparate) { + for (KRewrite rew : rewrites) { + if (!(streamSideCells(rew.left()).anyMatch(l -> cfg.getCellSort(l).equals(label)) + || streamSideCells(rew.left()).anyMatch(l -> cfg.getCellSort(l).equals(label)))) { + forcedSeparate = false; + break; + } } + } + } + if (forcedSeparate) { + for (KRewrite rew1 : rewrites) { + for (KRewrite rew2 : rewrites) { + Set left1NonRepeatable = + streamSideCells(rew1.left()) + .map(cfg::getCellSort) + .filter(l -> cfg.getMultiplicity(l) != ConfigurationInfo.Multiplicity.STAR) + .collect(Collectors.toSet()); + boolean lhsConflict = + streamSideCells(rew2.left()) + .map(cfg::getCellSort) + .filter(left1NonRepeatable::contains) + .count() + >= 1; - // They were also not forced to be separate - throw KEMException.criticalError("Ambiguous completion: " + parent + "\n" + allChildren); + Set right1NonRepeatable = + streamSideCells(rew1.right()) + .map(cfg::getCellSort) + .filter(l -> cfg.getMultiplicity(l) != ConfigurationInfo.Multiplicity.STAR) + .collect(Collectors.toSet()); + boolean rhsConflict = + streamSideCells(rew2.right()) + .map(cfg::getCellSort) + .filter(right1NonRepeatable::contains) + .count() + >= 1; + if (!(lhsConflict || rhsConflict)) { + forcedSeparate = false; + break; + } + } + } } + if (forcedSeparate) { + return allChildren.stream() + .map(k -> IncompleteCellUtils.make(parent, ellipses, k, ellipses)) + .collect(Collectors.toList()); + } + + // They were also not forced to be separate + throw KEMException.criticalError("Ambiguous completion: " + parent + "\n" + allChildren); + } - boolean isCompletionItem(K k) { - return (k instanceof KApply || k instanceof KRewrite || k instanceof KVariable) - && getLevel(k).isPresent(); + boolean isCompletionItem(K k) { + return (k instanceof KApply || k instanceof KRewrite || k instanceof KVariable) + && getLevel(k).isPresent(); + } + + Optional getLevel(KApply k) { + int level = cfg.getLevel(k.klabel()); + if (level >= 0) { + return Optional.of(level); + } else { + return Optional.empty(); } + } - Optional getLevel(KApply k) { - int level = cfg.getLevel(k.klabel()); + Optional getLevel(K k) { + if (k instanceof KApply) { + return getLevel((KApply) k); + } else if (k instanceof KVariable) { + if (k.att().contains(Sort.class)) { + Sort sort = k.att().get(Sort.class); + int level = cfg.cfg().getLevel(sort); if (level >= 0) { - return Optional.of(level); - } else { - return Optional.empty(); + return Optional.of(level); } - } - - Optional getLevel(K k) { - if (k instanceof KApply) { - return getLevel((KApply) k); - } else if (k instanceof KVariable) { - if (k.att().contains(Sort.class)) { - Sort sort = k.att().get(Sort.class); - int level = cfg.cfg().getLevel(sort); - if (level >= 0) { - return Optional.of(level); - } - } - return Optional.empty(); - } else if (k instanceof KRewrite rew) { - List cells = IncompleteCellUtils.flattenCells(rew.left()); - cells.addAll(IncompleteCellUtils.flattenCells(rew.right())); - Optional level = Optional.empty(); - for (K item : cells) { - Optional level2 = getLevel(item); - if (item instanceof KVariable && !level2.isPresent()) { - continue; - } - if (!level.isPresent()) { - level = level2; - } else if (!level.equals(level2)) { - throw KEMException.criticalError("Can't mix cells at different levels under a rewrite"); - } - // else level is already correct - } - return level; - } else { - return Optional.empty(); + } + return Optional.empty(); + } else if (k instanceof KRewrite rew) { + List cells = IncompleteCellUtils.flattenCells(rew.left()); + cells.addAll(IncompleteCellUtils.flattenCells(rew.right())); + Optional level = Optional.empty(); + for (K item : cells) { + Optional level2 = getLevel(item); + if (item instanceof KVariable && !level2.isPresent()) { + continue; + } + if (!level.isPresent()) { + level = level2; + } else if (!level.equals(level2)) { + throw KEMException.criticalError("Can't mix cells at different levels under a rewrite"); } + // else level is already correct + } + return level; + } else { + return Optional.empty(); } + } - Optional getParent(K k) { - if (k instanceof final KApply app) { - if (KLabels.CELLS.equals(app.klabel())) { - List items = app.klist().items(); - if (items.isEmpty()) { - return Optional.empty(); - } - Optional parent = getParent(items.get(0)); - for (K item : items) { - if (!parent.equals(getParent(item))) { - throw KEMException.criticalError("Can't mix items with different parent cells under a rewrite," + - " found "+items.get(0)+" and "+item, k); - } - } - return parent; - } else if (cfg.isCell(app.klabel())) { - return Optional.of(cfg.getParent(app.klabel())); - } else { - return Optional.empty(); - } - } else if (k instanceof KVariable) { - Sort sort = k.att().get(Sort.class); - return Optional.of(cfg.getParent(sort)); - } else { - Optional leftParent = getParent(((KRewrite) k).left()); - Optional rightParent = getParent(((KRewrite) k).right()); - if (!leftParent.isPresent()) { - return rightParent; - } - if (!rightParent.isPresent()) { - return leftParent; - } - if (leftParent.equals(rightParent)) { - return leftParent; - } else { - throw KEMException.criticalError("All cells on the left and right of a rewrite must have the same parent: " + k); - } + Optional getParent(K k) { + if (k instanceof final KApply app) { + if (KLabels.CELLS.equals(app.klabel())) { + List items = app.klist().items(); + if (items.isEmpty()) { + return Optional.empty(); } + Optional parent = getParent(items.get(0)); + for (K item : items) { + if (!parent.equals(getParent(item))) { + throw KEMException.criticalError( + "Can't mix items with different parent cells under a rewrite," + + " found " + + items.get(0) + + " and " + + item, + k); + } + } + return parent; + } else if (cfg.isCell(app.klabel())) { + return Optional.of(cfg.getParent(app.klabel())); + } else { + return Optional.empty(); + } + } else if (k instanceof KVariable) { + Sort sort = k.att().get(Sort.class); + return Optional.of(cfg.getParent(sort)); + } else { + Optional leftParent = getParent(((KRewrite) k).left()); + Optional rightParent = getParent(((KRewrite) k).right()); + if (!leftParent.isPresent()) { + return rightParent; + } + if (!rightParent.isPresent()) { + return leftParent; + } + if (leftParent.equals(rightParent)) { + return leftParent; + } else { + throw KEMException.criticalError( + "All cells on the left and right of a rewrite must have the same parent: " + k); + } } + } - K concretizeCell(K k) { - if (!(k instanceof KApply app)) { - return k; + K concretizeCell(K k) { + if (!(k instanceof KApply app)) { + return k; + } else { + KLabel target = app.klabel(); + if (cfg.isLeafCell(target)) { + return k; + } + List children = Lists.newArrayList(); + List otherChildren = Lists.newArrayList(); + int ix = 0; + boolean ellipses = + IncompleteCellUtils.isOpenLeft(app) || IncompleteCellUtils.isOpenRight(app); + for (K item : IncompleteCellUtils.getChildren(app)) { + if (isCompletionItem(item)) { + children.add(item); } else { - KLabel target = app.klabel(); - if (cfg.isLeafCell(target)) { - return k; - } - List children = Lists.newArrayList(); - List otherChildren = Lists.newArrayList(); - int ix = 0; - boolean ellipses = IncompleteCellUtils.isOpenLeft(app) - || IncompleteCellUtils.isOpenRight(app); - for (K item : IncompleteCellUtils.getChildren(app)) { - if (isCompletionItem(item)) { - children.add(item); - } else { - otherChildren.add(item); - } - ++ix; - } - if (children.isEmpty()) { - return k; - } + otherChildren.add(item); + } + ++ix; + } + if (children.isEmpty()) { + return k; + } - int targetLevel = cfg.getLevel(target) + 1; - TreeMap> levelMap = new TreeMap<>(); - Multimap levels = Multimaps.newMultimap(levelMap, ArrayList::new); - for (K child : children) { - levels.put(getLevel(child).get(), child); - } - while (levelMap.lastKey() > targetLevel) { - Collection level = levels.removeAll(levelMap.lastKey()); - for (Map.Entry> e : level.stream().collect(Collectors.groupingBy(t -> getParent(t).get())).entrySet()) { - KLabel parent = e.getKey(); - List newCells = makeParents(parent, ellipses, e.getValue()); - levels.putAll(cfg.getLevel(parent), newCells); - } - } - otherChildren.addAll(levels.removeAll(levelMap.lastKey())); - return IncompleteCellUtils.make(target, ellipses, otherChildren, ellipses); + int targetLevel = cfg.getLevel(target) + 1; + TreeMap> levelMap = new TreeMap<>(); + Multimap levels = Multimaps.newMultimap(levelMap, ArrayList::new); + for (K child : children) { + levels.put(getLevel(child).get(), child); + } + while (levelMap.lastKey() > targetLevel) { + Collection level = levels.removeAll(levelMap.lastKey()); + for (Map.Entry> e : + level.stream().collect(Collectors.groupingBy(t -> getParent(t).get())).entrySet()) { + KLabel parent = e.getKey(); + List newCells = makeParents(parent, ellipses, e.getValue()); + levels.putAll(cfg.getLevel(parent), newCells); } + } + otherChildren.addAll(levels.removeAll(levelMap.lastKey())); + return IncompleteCellUtils.make(target, ellipses, otherChildren, ellipses); } + } - K concretize(K term) { - if (term instanceof KApply app) { - KApply newTerm = KApply(app.klabel(), KList(app.klist().stream() - .map(this::concretize).collect(Collectors.toList())), app.att()); - if (cfg.isParentCell(newTerm.klabel())) { - return concretizeCell(newTerm); - } else { - return newTerm; - } - } else if (term instanceof KRewrite rew) { - return KRewrite(concretize(rew.left()), concretize(rew.right())); - } else if (term instanceof KSequence) { - return KSequence(((KSequence) term).stream() - .map(this::concretize).collect(Collectors.toList())); - } else if (term instanceof KAs) { - return KAs(concretize(((KAs)term).pattern()), ((KAs)term).alias(), term.att()); - } else { - return term; - } + K concretize(K term) { + if (term instanceof KApply app) { + KApply newTerm = + KApply( + app.klabel(), + KList(app.klist().stream().map(this::concretize).collect(Collectors.toList())), + app.att()); + if (cfg.isParentCell(newTerm.klabel())) { + return concretizeCell(newTerm); + } else { + return newTerm; + } + } else if (term instanceof KRewrite rew) { + return KRewrite(concretize(rew.left()), concretize(rew.right())); + } else if (term instanceof KSequence) { + return KSequence( + ((KSequence) term).stream().map(this::concretize).collect(Collectors.toList())); + } else if (term instanceof KAs) { + return KAs(concretize(((KAs) term).pattern()), ((KAs) term).alias(), term.att()); + } else { + return term; } + } - public Sentence concretize(Sentence m) { - if (m instanceof RuleOrClaim r) { - return r.newInstance(concretize(r.body()), r.requires(), r.ensures(), r.att()); - } else if (m instanceof Context c) { - return new Context(concretize(c.body()), c.requires(), c.att()); - } else { - return m; - } + public Sentence concretize(Sentence m) { + if (m instanceof RuleOrClaim r) { + return r.newInstance(concretize(r.body()), r.requires(), r.ensures(), r.att()); + } else if (m instanceof Context c) { + return new Context(concretize(c.body()), c.requires(), c.att()); + } else { + return m; } + } } diff --git a/kernel/src/main/java/org/kframework/compile/AddSortInjections.java b/kernel/src/main/java/org/kframework/compile/AddSortInjections.java index 588a9fc9ea7..013cf1f4bb6 100644 --- a/kernel/src/main/java/org/kframework/compile/AddSortInjections.java +++ b/kernel/src/main/java/org/kframework/compile/AddSortInjections.java @@ -1,6 +1,21 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.Collections.*; +import static org.kframework.kore.KORE.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import org.kframework.Collections; import org.kframework.attributes.Att; import org.kframework.attributes.HasLocation; @@ -27,451 +42,507 @@ import scala.Option; import scala.Tuple2; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.function.BiFunction; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import static org.kframework.Collections.*; -import static org.kframework.kore.KORE.*; - public class AddSortInjections { - private final Module mod; - private final Map collectionFor; - private final ConfigurationInfoFromModule configurationInfo; - - private int freshSortParamCounter = 0; - private final Set sortParams = new HashSet<>(); - public static final String SORTPARAM_NAME = "#SortParam"; - private boolean isLHS = false; - - public AddSortInjections(Module mod) { - this.mod = mod; - this.collectionFor = ConvertDataStructureToLookup.collectionFor(mod); - this.configurationInfo = new ConfigurationInfoFromModule(mod); + private final Module mod; + private final Map collectionFor; + private final ConfigurationInfoFromModule configurationInfo; + + private int freshSortParamCounter = 0; + private final Set sortParams = new HashSet<>(); + public static final String SORTPARAM_NAME = "#SortParam"; + private boolean isLHS = false; + + public AddSortInjections(Module mod) { + this.mod = mod; + this.collectionFor = ConvertDataStructureToLookup.collectionFor(mod); + this.configurationInfo = new ConfigurationInfoFromModule(mod); + } + + public Sentence addInjections(Sentence s) { + if (s instanceof RuleOrClaim) { + return addInjections((RuleOrClaim) s); + } else { + return s; } - - public Sentence addInjections(Sentence s) { - if (s instanceof RuleOrClaim) { - return addInjections((RuleOrClaim) s); - } else { - return s; - } + } + + public RuleOrClaim addInjections(RuleOrClaim roc) { + initSortParams(); + K body = addTopSortInjections(roc.body()); + K requires = internalAddSortInjections(roc.requires(), Sorts.Bool()); + K ensures = internalAddSortInjections(roc.ensures(), Sorts.Bool()); + Att att = roc.att(); + if (!sortParams.isEmpty()) { + att = + att.add( + Att.SORT_PARAMS(), + Sort.class, + Sort("", sortParams.stream().map(s -> Sort(s)).collect(Collections.toList()))); } - - public RuleOrClaim addInjections(RuleOrClaim roc) { - initSortParams(); - K body = addTopSortInjections(roc.body()); - K requires = internalAddSortInjections(roc.requires(), Sorts.Bool()); - K ensures = internalAddSortInjections(roc.ensures(), Sorts.Bool()); - Att att = roc.att(); - if (!sortParams.isEmpty()) { - att = att.add(Att.SORT_PARAMS(), Sort.class, Sort("", sortParams.stream().map(s -> Sort(s)).collect(Collections.toList()))); - } - return roc.newInstance(body, requires, ensures, att); + return roc.newInstance(body, requires, ensures, att); + } + + public K addInjections(K term) { + Sort topSort = sort(term, Sorts.K()); + K result = addSortInjections(term, topSort); + return result; + } + + public K addSortInjections(K term, Sort topSort) { + initSortParams(); + return internalAddSortInjections(term, topSort); + } + + private boolean collectionIsMap(KLabel collectionLabel) { + return mod.attributesFor().apply(collectionLabel).contains(Att.COMM()) + && !mod.attributesFor().apply(collectionLabel).contains(Att.IDEM()) + && !mod.attributesFor().apply(collectionLabel).contains(Att.BAG()); + } + + private K addTopSortInjections(K body) { + if (new FoldK() { + @Override + public Boolean unit() { + return false; + } + + @Override + public Boolean apply(KRewrite k) { + return true; + } + + @Override + public Boolean merge(Boolean a, Boolean b) { + return a || b; + } + }.apply(body)) { + body = KRewrite(RewriteToTop.toLeft(body), RewriteToTop.toRight(body)); } - - public K addInjections(K term) { - Sort topSort = sort(term, Sorts.K()); - K result = addSortInjections(term, topSort); - return result; - } - - public K addSortInjections(K term, Sort topSort) { - initSortParams(); - return internalAddSortInjections(term, topSort); + Sort sort = topSort(body); + return internalAddSortInjections(body, sort); + } + + private K internalAddSortInjections(K term, Sort expectedSort) { + Sort actualSort = sort(term, expectedSort); + if (actualSort == null) { + actualSort = expectedSort; } - - private boolean collectionIsMap(KLabel collectionLabel) { - return mod.attributesFor().apply(collectionLabel).contains(Att.COMM()) - && !mod.attributesFor().apply(collectionLabel).contains(Att.IDEM()) - && !mod.attributesFor().apply(collectionLabel).contains(Att.BAG()); - } - - private K addTopSortInjections(K body) { - if (new FoldK() { - @Override - public Boolean unit() { - return false; - } - - @Override - public Boolean apply(KRewrite k) { - return true; - } - - @Override - public Boolean merge(Boolean a, Boolean b) { - return a || b; + if (actualSort.equals(expectedSort)) { + return visitChildren(term, actualSort, expectedSort); + } else if (expectedSort.equals(Sorts.K())) { + if (actualSort.equals(Sorts.KItem())) { + return KSequence(visitChildren(term, actualSort, expectedSort)); + } else { + return KSequence( + KApply( + KLabel("inj", actualSort, Sorts.KItem()), + KList(visitChildren(term, actualSort, expectedSort)), + Att.empty().add(Sort.class, Sorts.KItem()))); + } + } else { + String hookAtt = + mod.sortAttributesFor() + .get(expectedSort.head()) + .getOrElse(() -> Att()) + .getOptional(Att.HOOK()) + .orElse(""); + if ((hookAtt.equals("MAP.Map") || hookAtt.equals("SET.Set") || hookAtt.equals("LIST.List")) + && term instanceof KApply) { + for (KLabel collectionLabel : collectionFor.keySet()) { + Optional wrapElement = + mod.attributesFor().apply(collectionLabel).getOptional(Att.WRAP_ELEMENT()); + if (wrapElement.isPresent()) { + KLabel wrappedLabel = KLabel(wrapElement.get()); + KLabel elementLabel = + KLabel(mod.attributesFor().apply(collectionLabel).get(Att.ELEMENT())); + KApply k = (KApply) term; + if (configurationInfo.getCellSort(wrappedLabel).equals(actualSort)) { + if (collectionIsMap(collectionLabel)) { + // Map + K key; + if (k.klabel().equals(wrappedLabel)) { + key = k.klist().items().get(0); + } else { + key = + KApply( + KLabel(expectedSort.name() + "Key"), + KList(visitChildren(k, actualSort, expectedSort))); + } + Sort adjustedExpectedSort = expectedSort; + if (k.att().contains(Sort.class)) { + adjustedExpectedSort = k.att().get(Sort.class); + } + Production prod = production(k); + List children = new ArrayList<>(); + Production substituted = + substituteProd( + prod, + adjustedExpectedSort, + (i, fresh) -> sort(k.items().get(i), fresh.nonterminals().apply(i).sort()), + k); + Sort expectedKeySort = substituted.nonterminal(0).sort(); + Sort actualKeySort = sort(key, expectedKeySort); + return KApply( + elementLabel, + KList( + visitChildren(key, actualKeySort, expectedKeySort), + visitChildren(k, actualSort, expectedSort)), + Att.empty().add(Sort.class, expectedSort)); + } else { + return KApply( + elementLabel, + KList(visitChildren(k, actualSort, expectedSort)), + Att.empty().add(Sort.class, expectedSort)); + } } - }.apply(body)) { - body = KRewrite(RewriteToTop.toLeft(body), RewriteToTop.toRight(body)); + } } - Sort sort = topSort(body); - return internalAddSortInjections(body, sort); + } + return KApply( + KLabel("inj", actualSort, expectedSort), + KList(visitChildren(term, actualSort, expectedSort)), + Att.empty().add(Sort.class, expectedSort)); } - - private K internalAddSortInjections(K term, Sort expectedSort) { - Sort actualSort = sort(term, expectedSort); - if (actualSort == null) { - actualSort = expectedSort; + } + + private Context addInjections(Context context) { + return new Context( + internalAddSortInjections(context.body(), Sorts.K()), + internalAddSortInjections(context.requires(), Sorts.Bool()), + context.att()); + } + + private void initSortParams() { + freshSortParamCounter = 0; + sortParams.clear(); + } + + private K visitChildren(K term, Sort actualSort, Sort expectedSort) { + Att att = term.att().add(Sort.class, actualSort); + if (actualSort.name().equals(SORTPARAM_NAME)) { + sortParams.add(actualSort.params().head().name()); + } + if (term instanceof KApply kapp) { + if (kapp.klabel().name().equals("inj")) { + return term; + } + if (kapp.att().contains(Sort.class)) { + expectedSort = kapp.att().get(Sort.class); + } + Production prod = production(kapp); + List children = new ArrayList<>(); + Production substituted = + substituteProd( + prod, + expectedSort, + (i, fresh) -> sort(kapp.items().get(i), fresh.nonterminals().apply(i).sort()), + kapp); + for (int i = 0; i < kapp.items().size(); i++) { + if (kapp.items().size() != substituted.nonterminals().size()) { + throw KEMException.compilerError( + "Invalid sort predicate " + + kapp.klabel() + + " that depends directly or indirectly on the current configuration. " + + "Is it possible to replace the sort predicate with a regular function?", + kapp); } - if (actualSort.equals(expectedSort)) { - return visitChildren(term, actualSort, expectedSort); - } else if (expectedSort.equals(Sorts.K())) { - if (actualSort.equals(Sorts.KItem())) { - return KSequence(visitChildren(term, actualSort, expectedSort)); - } else { - return KSequence(KApply(KLabel("inj", actualSort, Sorts.KItem()), KList(visitChildren(term, actualSort, expectedSort)), Att.empty().add(Sort.class, Sorts.KItem()))); - } + Sort expectedSortOfChild = substituted.nonterminal(i).sort(); + K child = kapp.items().get(i); + children.add(internalAddSortInjections(child, expectedSortOfChild)); + } + return KApply(substituted.klabel().get(), KList(children), att); + } else if (term instanceof KRewrite rew) { + isLHS = true; + K lhs = internalAddSortInjections(rew.left(), actualSort); + isLHS = false; + return KRewrite(lhs, internalAddSortInjections(rew.right(), actualSort), att); + } else if (term instanceof KVariable) { + return KVariable(((KVariable) term).name(), att); + } else if (term instanceof KToken) { + return KToken(((KToken) term).s(), ((KToken) term).sort(), att); + } else if (term instanceof InjectedKLabel) { + return InjectedKLabel(((InjectedKLabel) term).klabel(), att); + } else if (term instanceof KSequence kseq) { + List children = new ArrayList<>(); + for (int i = 0; i < kseq.size(); i++) { + K child = kseq.items().get(i); + Sort childSort = sort(child, isLHS ? Sorts.KItem() : Sorts.K()); + if (childSort.equals(Sorts.K())) { + children.add(internalAddSortInjections(child, Sorts.K())); } else { - String hookAtt = mod.sortAttributesFor().get(expectedSort.head()).getOrElse(() -> Att()).getOptional(Att.HOOK()).orElse(""); - if ((hookAtt.equals("MAP.Map") || hookAtt.equals("SET.Set") || hookAtt.equals("LIST.List")) && term instanceof KApply) { - for (KLabel collectionLabel : collectionFor.keySet()) { - Optional wrapElement = mod.attributesFor().apply(collectionLabel).getOptional(Att.WRAP_ELEMENT()); - if (wrapElement.isPresent()) { - KLabel wrappedLabel = KLabel(wrapElement.get()); - KLabel elementLabel = KLabel(mod.attributesFor().apply(collectionLabel).get(Att.ELEMENT())); - KApply k = (KApply)term; - if (configurationInfo.getCellSort(wrappedLabel).equals(actualSort)) { - if (collectionIsMap(collectionLabel)) { - // Map - K key; - if (k.klabel().equals(wrappedLabel)) { - key = k.klist().items().get(0); - } else { - key = KApply(KLabel(expectedSort.name() + "Key"), KList(visitChildren(k, actualSort, expectedSort))); - } - Sort adjustedExpectedSort = expectedSort; - if (k.att().contains(Sort.class)) { - adjustedExpectedSort = k.att().get(Sort.class); - } - Production prod = production(k); - List children = new ArrayList<>(); - Production substituted = substituteProd(prod, adjustedExpectedSort, (i, fresh) -> sort(k.items().get(i), fresh.nonterminals().apply(i).sort()), k); - Sort expectedKeySort = substituted.nonterminal(0).sort(); - Sort actualKeySort = sort(key, expectedKeySort); - return KApply(elementLabel, KList(visitChildren(key, actualKeySort, expectedKeySort), visitChildren(k, actualSort, expectedSort)), Att.empty().add(Sort.class, expectedSort)); - } else { - return KApply(elementLabel, KList(visitChildren(k, actualSort, expectedSort)), Att.empty().add(Sort.class, expectedSort)); - } - } - } - } - } - return KApply(KLabel("inj", actualSort, expectedSort), KList(visitChildren(term, actualSort, expectedSort)), Att.empty().add(Sort.class, expectedSort)); + children.add(internalAddSortInjections(child, Sorts.KItem())); } + } + return KSequence(children, att); + } else if (term instanceof KAs kas) { + return KAs(internalAddSortInjections(kas.pattern(), actualSort), kas.alias(), att); + } else { + throw KEMException.internalError("Invalid category of k found.", term); } - - private Context addInjections(Context context) { - return new Context(internalAddSortInjections(context.body(), Sorts.K()), internalAddSortInjections(context.requires(), Sorts.Bool()), context.att()); + } + + /** + * Generate the substituted production with its sort parameters added for a parametric production. + * + * @param prod The production to add sort parameters to. + * @param expectedSort the sort context where the term with the specified production appears. + * @param getSort a function taking the 0-based index of the child of this production and the + * substitutedFresh production and returning the sort of the child. + * @param loc The location to report upon an error. + * @return The production substituted with the least upper bounds of its sort parameters based on + * its children's sorts. + */ + public Production substituteProd( + Production prod, + Sort expectedSort, + BiFunction getSort, + HasLocation loc) { + Production substituted = prod; + List args = new ArrayList<>(); + List fresh = new ArrayList<>(); + for (int i = 0; i < prod.params().size(); i++) { + if (prod.params().apply(i).equals(prod.sort())) { + fresh.add(expectedSort); + } else { + fresh.add(freshSortParam()); + } } - - private void initSortParams() { - freshSortParamCounter = 0; - sortParams.clear(); - } - - private K visitChildren(K term, Sort actualSort, Sort expectedSort) { - Att att = term.att().add(Sort.class, actualSort); - if (actualSort.name().equals(SORTPARAM_NAME)) { - sortParams.add(actualSort.params().head().name()); - } - if (term instanceof KApply kapp) { - if (kapp.klabel().name().equals("inj")) { - return term; - } - if (kapp.att().contains(Sort.class)) { - expectedSort = kapp.att().get(Sort.class); - } - Production prod = production(kapp); - List children = new ArrayList<>(); - Production substituted = substituteProd(prod, expectedSort, (i, fresh) -> sort(kapp.items().get(i), fresh.nonterminals().apply(i).sort()), kapp); - for (int i = 0; i < kapp.items().size(); i++) { - if (kapp.items().size() != substituted.nonterminals().size()) { - throw KEMException.compilerError("Invalid sort predicate " + kapp.klabel() + - " that depends directly or indirectly on the current configuration. " + - "Is it possible to replace the sort predicate with a regular function?", kapp); - } - Sort expectedSortOfChild = substituted.nonterminal(i).sort(); - K child = kapp.items().get(i); - children.add(internalAddSortInjections(child, expectedSortOfChild)); - } - return KApply(substituted.klabel().get(), KList(children), att); - } else if (term instanceof KRewrite rew) { - isLHS = true; - K lhs = internalAddSortInjections(rew.left(), actualSort); - isLHS = false; - return KRewrite(lhs, internalAddSortInjections(rew.right(), actualSort), att); - } else if (term instanceof KVariable) { - return KVariable(((KVariable) term).name(), att); - } else if (term instanceof KToken) { - return KToken(((KToken) term).s(), ((KToken) term).sort(), att); - } else if (term instanceof InjectedKLabel) { - return InjectedKLabel(((InjectedKLabel) term).klabel(), att); - } else if (term instanceof KSequence kseq) { - List children = new ArrayList<>(); - for (int i = 0; i < kseq.size(); i++) { - K child = kseq.items().get(i); - Sort childSort = sort(child, isLHS ? Sorts.KItem() : Sorts.K()); - if (childSort.equals(Sorts.K())) { - children.add(internalAddSortInjections(child, Sorts.K())); - } else { - children.add(internalAddSortInjections(child, Sorts.KItem())); - } - } - return KSequence(children, att); - } else if (term instanceof KAs kas) { - return KAs(internalAddSortInjections(kas.pattern(), actualSort), kas.alias(), att); + Production substitutedFresh = prod.substitute(immutable(fresh)); + if (prod.params().nonEmpty()) { + Map> subst = new HashMap<>(); + for (int i = 0; i < prod.nonterminals().size(); i++) { + Sort declaredSort = prod.nonterminals().apply(i).sort(); + Sort actual = getSort.apply(i, substitutedFresh); + match(prod, declaredSort, actual, subst); + } + int i = 0; + matchExpected(prod, expectedSort, subst); + for (Sort param : iterable(prod.params())) { + if (subst.get(param) == null) { + args.add(fresh.get(i)); } else { - throw KEMException.internalError("Invalid category of k found.", term); + args.add(lub(subst.get(param), fresh.get(i), loc, mod)); } + i++; + } + substituted = prod.substitute(immutable(args)); } - - /** - * Generate the substituted production with its sort parameters added for a parametric production. - * @param prod The production to add sort parameters to. - * @param expectedSort the sort context where the term with the specified production appears. - * @param getSort a function taking the 0-based index of the child of this production and the substitutedFresh production and returning the sort of the child. - * @param loc The location to report upon an error. - * @return The production substituted with the least upper bounds of its sort parameters based on its children's sorts. - */ - public Production substituteProd(Production prod, Sort expectedSort, BiFunction getSort, HasLocation loc) { - Production substituted = prod; - List args = new ArrayList<>(); - List fresh = new ArrayList<>(); - for (int i = 0; i < prod.params().size(); i++) { - if (prod.params().apply(i).equals(prod.sort())) { - fresh.add(expectedSort); - } else { - fresh.add(freshSortParam()); - } - } - Production substitutedFresh = prod.substitute(immutable(fresh)); - if (prod.params().nonEmpty()) { - Map> subst = new HashMap<>(); - for (int i = 0; i < prod.nonterminals().size(); i++) { - Sort declaredSort = prod.nonterminals().apply(i).sort(); - Sort actual = getSort.apply(i, substitutedFresh); - match(prod, declaredSort, actual, subst); - } - int i = 0; - matchExpected(prod, expectedSort, subst); - for (Sort param : iterable(prod.params())) { - if (subst.get(param) == null) { - args.add(fresh.get(i)); - } else { - args.add(lub(subst.get(param), fresh.get(i), loc, mod)); - } - i++; - } - substituted = prod.substitute(immutable(args)); + return substituted; + } + + private void match( + Production prod, Sort declaredSort, Sort actualSort, Map> subst) { + if (prod.isSortVariable(declaredSort)) { + subst.computeIfAbsent(declaredSort, s -> new ArrayList<>()).add(actualSort); + return; + } + if (actualSort.params().size() == declaredSort.params().size() + && actualSort.head().equals(declaredSort.head())) { + for (int i = 0; i < declaredSort.head().params(); i++) { + match(prod, declaredSort.params().apply(i), actualSort.params().apply(i), subst); + } + } + for (Sort s : iterable(mod.allSorts())) { + if (mod.subsorts().lessThanEq(s, actualSort)) { + if (s.params().size() == declaredSort.params().size() + && s.head().equals(declaredSort.head())) { + for (int i = 0; i < declaredSort.head().params(); i++) { + match(prod, declaredSort.params().apply(i), s.params().apply(i), subst); + } } - return substituted; + } } - - private void match(Production prod, Sort declaredSort, Sort actualSort, Map> subst) { - if (prod.isSortVariable(declaredSort)) { - subst.computeIfAbsent(declaredSort, s -> new ArrayList<>()).add(actualSort); - return; + } + + private Set getPositions(Sort param, Production prod) { + return IntStream.range(0, prod.nonterminals().size()) + .mapToObj(i -> Tuple2.apply(prod.nonterminals().apply(i), i)) + .filter(t -> t._1().sort().equals(param)) + .map(t -> t._2()) + .collect(Collectors.toSet()); + } + + /** + * Compute the sort of a term in a context where there is no expected sort, i.e., at the top of a + * rule body. + */ + public Sort topSort(K term) { + return sort(term, freshSortParam()); + } + + /** Compute the sort of a term with a particular expected sort. */ + public Sort sort(K term, Sort expectedSort) { + if (term instanceof KApply kapp) { + if (kapp.klabel().name().equals("inj")) { + return kapp.klabel().params().apply(1); + } + if (kapp.klabel().name().startsWith("#SemanticCastTo")) { + return Outer.parseSort(kapp.klabel().name().substring("#SemanticCastTo".length())); + } + if (kapp.klabel().name().equals("#fun2")) { + return sort(kapp.items().get(0), expectedSort); + } + if (kapp.klabel().name().equals("#fun3")) { + return sort(kapp.items().get(1), expectedSort); + } + if (kapp.klabel().name().equals("#let")) { + return sort(kapp.items().get(2), expectedSort); + } + if (kapp.klabel().name().equals("_:=K_")) { + return Sorts.Bool(); + } + if (kapp.klabel().name().equals("_:/=K_")) { + return Sorts.Bool(); + } + if (kapp.att().contains(Sort.class)) { + expectedSort = kapp.att().get(Sort.class); + } + Production prod = production(kapp); + Production substituted = prod; + List args = new ArrayList<>(); + List fresh = new ArrayList<>(); + for (int i = 0; i < prod.params().size(); i++) { + if (prod.params().apply(i).equals(prod.sort())) { + fresh.add(expectedSort); + } else { + fresh.add(freshSortParam()); } - if (actualSort.params().size() == declaredSort.params().size() && actualSort.head().equals(declaredSort.head())) { - for (int i = 0; i < declaredSort.head().params(); i++) { - match(prod, declaredSort.params().apply(i), actualSort.params().apply(i), subst); - } + } + Production substitutedFresh = prod.substitute(immutable(fresh)); + if (prod.params().nonEmpty()) { + Map> subst = new HashMap<>(); + for (int i = 0; i < prod.nonterminals().size(); i++) { + Sort declaredSort = prod.nonterminals().apply(i).sort(); + Sort actual = sort(kapp.items().get(i), substitutedFresh.nonterminals().apply(i).sort()); + match(prod, declaredSort, actual, subst); } - for (Sort s : iterable(mod.allSorts())) { - if (mod.subsorts().lessThanEq(s, actualSort)) { - if (s.params().size() == declaredSort.params().size() && s.head().equals(declaredSort.head())) { - for (int i = 0; i < declaredSort.head().params(); i++) { - match(prod, declaredSort.params().apply(i), s.params().apply(i), subst); - } - } - } + int i = 0; + matchExpected(prod, expectedSort, subst); + for (Sort param : iterable(prod.params())) { + if (subst.get(param) == null) { + args.add(fresh.get(i)); + } else { + args.add(lub(subst.get(param), fresh.get(i), kapp, mod)); + } + i++; } + substituted = prod.substitute(immutable(args)); + } + return substituted.sort(); + } else if (term instanceof KRewrite rew) { + Sort leftSort = sort(rew.left(), expectedSort); + Sort rightSort = sort(rew.right(), expectedSort); + return lubSort(leftSort, rightSort, expectedSort, term, mod); + } else if (term instanceof KToken) { + return ((KToken) term).sort(); + } else if (term instanceof KVariable) { + return term.att().getOptional(Sort.class).orElse(Sorts.K()); + } else if (term instanceof KSequence) { + return Sorts.K(); + } else if (term instanceof InjectedKLabel) { + return Sorts.KItem(); + } else if (term instanceof KAs as) { + Sort patternSort = sort(as.pattern(), expectedSort); + Sort rightSort = sort(as.alias(), expectedSort); + return lubSort(patternSort, rightSort, expectedSort, term, mod); + } else { + throw KEMException.internalError("Invalid category of k found.", term); } - - private Set getPositions(Sort param, Production prod) { - return IntStream.range(0, prod.nonterminals().size()) - .mapToObj(i -> Tuple2.apply(prod.nonterminals().apply(i), i)) - .filter(t -> t._1().sort().equals(param)) - .map(t -> t._2()) - .collect(Collectors.toSet()); + } + + private void matchExpected(Production prod, Sort expectedSort, Map> subst) { + boolean found = false; + outer: + for (Sort param : iterable(prod.params())) { + if (!prod.sort().contains(param)) { + continue; + } + for (NonTerminal nt : iterable(prod.nonterminals())) { + if (nt.sort().contains(param)) { + continue outer; + } + } + found = true; } - - /** - * Compute the sort of a term in a context where there is no expected sort, i.e., at the top of a rule body. - */ - public Sort topSort(K term) { - return sort(term, freshSortParam()); + if (found) { + match(prod, prod.sort(), expectedSort, subst); } - - /** - * Compute the sort of a term with a particular expected sort. - */ - public Sort sort(K term, Sort expectedSort) { - if (term instanceof KApply kapp) { - if (kapp.klabel().name().equals("inj")) { - return kapp.klabel().params().apply(1); - } - if (kapp.klabel().name().startsWith("#SemanticCastTo")) { - return Outer.parseSort(kapp.klabel().name().substring("#SemanticCastTo".length())); - } - if (kapp.klabel().name().equals("#fun2")) { - return sort(kapp.items().get(0), expectedSort); - } - if (kapp.klabel().name().equals("#fun3")) { - return sort(kapp.items().get(1), expectedSort); - } - if (kapp.klabel().name().equals("#let")) { - return sort(kapp.items().get(2), expectedSort); - } - if (kapp.klabel().name().equals("_:=K_")) { - return Sorts.Bool(); - } - if (kapp.klabel().name().equals("_:/=K_")) { - return Sorts.Bool(); - } - if (kapp.att().contains(Sort.class)) { - expectedSort = kapp.att().get(Sort.class); - } - Production prod = production(kapp); - Production substituted = prod; - List args = new ArrayList<>(); - List fresh = new ArrayList<>(); - for (int i = 0; i < prod.params().size(); i++) { - if (prod.params().apply(i).equals(prod.sort())) { - fresh.add(expectedSort); - } else { - fresh.add(freshSortParam()); - } - } - Production substitutedFresh = prod.substitute(immutable(fresh)); - if (prod.params().nonEmpty()) { - Map> subst = new HashMap<>(); - for (int i = 0; i < prod.nonterminals().size(); i++) { - Sort declaredSort = prod.nonterminals().apply(i).sort(); - Sort actual = sort(kapp.items().get(i), substitutedFresh.nonterminals().apply(i).sort()); - match(prod, declaredSort, actual, subst); - } - int i = 0; - matchExpected(prod, expectedSort, subst); - for (Sort param : iterable(prod.params())) { - if (subst.get(param) == null) { - args.add(fresh.get(i)); - } else { - args.add(lub(subst.get(param), fresh.get(i), kapp, mod)); - } - i++; - } - substituted = prod.substitute(immutable(args)); - } - return substituted.sort(); - } else if (term instanceof KRewrite rew) { - Sort leftSort = sort(rew.left(), expectedSort); - Sort rightSort = sort(rew.right(), expectedSort); - return lubSort(leftSort, rightSort, expectedSort, term, mod); - } else if (term instanceof KToken) { - return ((KToken) term).sort(); - } else if (term instanceof KVariable) { - return term.att().getOptional(Sort.class).orElse(Sorts.K()); - } else if (term instanceof KSequence) { - return Sorts.K(); - } else if (term instanceof InjectedKLabel) { - return Sorts.KItem(); - } else if (term instanceof KAs as) { - Sort patternSort = sort(as.pattern(), expectedSort); - Sort rightSort = sort(as.alias(), expectedSort); - return lubSort(patternSort, rightSort, expectedSort, term, mod); - } else { - throw KEMException.internalError("Invalid category of k found.", term); - } + } + + public static Sort lubSort( + Sort leftSort, Sort rightSort, Sort expectedSort, HasLocation loc, Module mod) { + if (leftSort == null && rightSort == null) { + return expectedSort; + } else if (leftSort == null) { + return rightSort; + } else if (rightSort == null) { + return leftSort; } + return lub(Arrays.asList(leftSort, rightSort), expectedSort, loc, mod); + } - private void matchExpected(Production prod, Sort expectedSort, Map> subst) { - boolean found = false; - outer: - for (Sort param : iterable(prod.params())) { - if (!prod.sort().contains(param)) { - continue; - } - for (NonTerminal nt : iterable(prod.nonterminals())) { - if (nt.sort().contains(param)) { - continue outer; - } - } - found = true; - } - if (found) { - match(prod, prod.sort(), expectedSort, subst); - } + private Production production(KApply term) { + if (term.klabel() instanceof KVariable) { + throw KEMException.internalError("KORE does not yet support KLabel variables.", term); } - - public static Sort lubSort(Sort leftSort, Sort rightSort, Sort expectedSort, HasLocation loc, Module mod) { - if (leftSort == null && rightSort == null) { - return expectedSort; - } else if (leftSort == null) { - return rightSort; - } else if (rightSort == null) { - return leftSort; - } - return lub(Arrays.asList(leftSort, rightSort), expectedSort, loc, mod); + Option> prods = mod.productionsFor().get(term.klabel().head()); + if (prods.isEmpty()) { + throw KEMException.compilerError( + "Could not find productions for KApply with label " + + term.klabel() + + " in module " + + mod.name(), + term); } - - private Production production(KApply term) { - if (term.klabel() instanceof KVariable) { - throw KEMException.internalError("KORE does not yet support KLabel variables.", term); - } - Option> prods = mod.productionsFor().get(term.klabel().head()); - if (prods.isEmpty()) { - throw KEMException.compilerError("Could not find productions for KApply with label " - + term.klabel() + " in module " + mod.name(), term); - } - return prods.get().head(); + return prods.get().head(); + } + + private static Sort lub( + Collection entries, Sort expectedSort, HasLocation loc, Module mod) { + assert !entries.isEmpty(); + entries = new HashSet<>(entries); + Collection filteredEntries = + entries.stream() + .filter(s -> s != null && !s.name().equals(SORTPARAM_NAME)) + .collect(Collectors.toList()); + if (filteredEntries.isEmpty()) { // if all sorts are parameters, take the first + return entries.iterator().next(); } - private static Sort lub(Collection entries, Sort expectedSort, HasLocation loc, Module mod) { - assert !entries.isEmpty(); - entries = new HashSet<>(entries); - Collection filteredEntries = entries.stream().filter(s -> s != null && !s.name().equals(SORTPARAM_NAME)).collect(Collectors.toList()); - if (filteredEntries.isEmpty()) { // if all sorts are parameters, take the first - return entries.iterator().next(); - } - - Set nonParametric = - filteredEntries.stream().filter(s -> s.params().isEmpty()).collect(Collectors.toSet()); - Set bounds = mutable(mod.subsorts().upperBounds(immutable(nonParametric))); - // Anything less than KBott or greater than K is a syntactic sort from kast.md which should not be considered - bounds.removeIf(s -> mod.subsorts().lessThanEq(s, Sorts.KBott()) || mod.subsorts().greaterThan(s, Sorts.K())); - if (expectedSort != null && !expectedSort.name().equals(SORTPARAM_NAME)) { - bounds.removeIf(s -> !mod.subsorts().lessThanEq(s, expectedSort)); - } + Set nonParametric = + filteredEntries.stream().filter(s -> s.params().isEmpty()).collect(Collectors.toSet()); + Set bounds = mutable(mod.subsorts().upperBounds(immutable(nonParametric))); + // Anything less than KBott or greater than K is a syntactic sort from kast.md which should not + // be considered + bounds.removeIf( + s -> + mod.subsorts().lessThanEq(s, Sorts.KBott()) + || mod.subsorts().greaterThan(s, Sorts.K())); + if (expectedSort != null && !expectedSort.name().equals(SORTPARAM_NAME)) { + bounds.removeIf(s -> !mod.subsorts().lessThanEq(s, expectedSort)); + } - // For parametric sorts, each bound must bound at least one instantiation - Set parametric = - filteredEntries.stream().filter(s -> ! s.params().isEmpty()).collect(Collectors.toSet()); - bounds.removeIf(bound -> - parametric.stream().anyMatch(param -> + // For parametric sorts, each bound must bound at least one instantiation + Set parametric = + filteredEntries.stream().filter(s -> !s.params().isEmpty()).collect(Collectors.toSet()); + bounds.removeIf( + bound -> + parametric.stream() + .anyMatch( + param -> stream(mod.definedInstantiations().apply(param.head())) - .noneMatch(inst -> mod.subsorts().lessThanEq(inst, bound)))); + .noneMatch(inst -> mod.subsorts().lessThanEq(inst, bound)))); - Set lub = mod.subsorts().minimal(bounds); - if (lub.size() != 1) { - throw KEMException.internalError("Could not compute least upper bound for rewrite sort. Possible candidates: " + lub, loc); - } - return lub.iterator().next(); + Set lub = mod.subsorts().minimal(bounds); + if (lub.size() != 1) { + throw KEMException.internalError( + "Could not compute least upper bound for rewrite sort. Possible candidates: " + lub, loc); } + return lub.iterator().next(); + } - private Sort freshSortParam() { - return Sort(SORTPARAM_NAME, Sort("Q" + freshSortParamCounter++)); - } + private Sort freshSortParam() { + return Sort(SORTPARAM_NAME, Sort("Q" + freshSortParamCounter++)); + } } diff --git a/kernel/src/main/java/org/kframework/compile/AddTopCellToRules.java b/kernel/src/main/java/org/kframework/compile/AddTopCellToRules.java index 5b8aff5f585..d05e8f5ac74 100644 --- a/kernel/src/main/java/org/kframework/compile/AddTopCellToRules.java +++ b/kernel/src/main/java/org/kframework/compile/AddTopCellToRules.java @@ -1,9 +1,14 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.Collections.*; +import static org.kframework.kore.KORE.*; + +import java.util.ArrayList; +import java.util.List; import org.kframework.attributes.Att; -import org.kframework.builtin.KLabels; import org.kframework.backend.kore.ConstructorChecks; +import org.kframework.builtin.KLabels; import org.kframework.definition.Context; import org.kframework.definition.Module; import org.kframework.definition.Production; @@ -15,119 +20,104 @@ import org.kframework.kore.KRewrite; import org.kframework.kore.Sort; -import java.util.ArrayList; -import java.util.List; - -import static org.kframework.Collections.*; -import static org.kframework.kore.KORE.*; - /** - * This pass adds the implicit top and k cells to - * the bodies of rules and contexts. - * A K cell is added only if the body is a single item, - * which is not already a cell or a rewrite on cells. - * The top cell is added unless the body is already an - * instance of the top cell. - * Rules with the anywhere attribute are not modified. + * This pass adds the implicit top and k cells to the bodies of rules and contexts. A K cell is + * added only if the body is a single item, which is not already a cell or a rewrite on cells. The + * top cell is added unless the body is already an instance of the top cell. Rules with the anywhere + * attribute are not modified. */ // TODO: rules defining functions shouldn't be wrapped public record AddTopCellToRules(ConfigurationInfo cfg, LabelInfo labelInfo) { - public K addImplicitCells(K term, Module m) { - if (m.isFunction(term)) return term; - return addRootCell(term); - } - - private K addRootCell(K term) { - KLabel root; - root = KLabels.GENERATED_TOP_CELL; + public K addImplicitCells(K term, Module m) { + if (m.isFunction(term)) return term; + return addRootCell(term); + } - // KApply instance - if (term instanceof KApply) { - KLabel kLabel = ((KApply) term).klabel(); - if (ConstructorChecks.isBuiltinLabel(kLabel)) { - // builtin-labels (ML connectives) - Production prod = labelInfo.getProduction(kLabel.name()); - if(prod.params().nonEmpty()) { - for (Sort param : iterable(prod.params())) { - if (prod.sort().equals(param)) { - if (stream(prod.nonterminals()).anyMatch(nt -> nt.sort().equals(param))) { - // recursively call addRoot on the children whose type is the same as the return type - List oldChildren = ((KApply) term).klist().items(); - List newChildren = new ArrayList<>(); - for (int i = 0; i < oldChildren.size(); i++) { - if (prod.nonterminals().apply(i).sort().equals(param)) { - newChildren.add(addRootCell(oldChildren.get(i))); - } else { - newChildren.add(oldChildren.get(i)); - } + private K addRootCell(K term) { + KLabel root; + root = KLabels.GENERATED_TOP_CELL; - } - return KApply(kLabel, KList(newChildren)); - } else { - // only one group can contain 0 - return term; - } - } - } - // if 0 doesn't appear in the poly attribute - return term; - } else { - // connectives that don't have poly attribute - return term; - } - } else { - if (kLabel.equals(root)) { - return term; + // KApply instance + if (term instanceof KApply) { + KLabel kLabel = ((KApply) term).klabel(); + if (ConstructorChecks.isBuiltinLabel(kLabel)) { + // builtin-labels (ML connectives) + Production prod = labelInfo.getProduction(kLabel.name()); + if (prod.params().nonEmpty()) { + for (Sort param : iterable(prod.params())) { + if (prod.sort().equals(param)) { + if (stream(prod.nonterminals()).anyMatch(nt -> nt.sort().equals(param))) { + // recursively call addRoot on the children whose type is the same as the return + // type + List oldChildren = ((KApply) term).klist().items(); + List newChildren = new ArrayList<>(); + for (int i = 0; i < oldChildren.size(); i++) { + if (prod.nonterminals().apply(i).sort().equals(param)) { + newChildren.add(addRootCell(oldChildren.get(i))); + } else { + newChildren.add(oldChildren.get(i)); + } } + return KApply(kLabel, KList(newChildren)); + } else { + // only one group can contain 0 + return term; + } } + } + // if 0 doesn't appear in the poly attribute + return term; + } else { + // connectives that don't have poly attribute + return term; } - - // KRewrite instance - if (term instanceof KRewrite rew) { - K left = addRootCell(rew.left()); - if (left == rew.left()) { - return KRewrite(rew.left(), rew.right()); - } else { - return IncompleteCellUtils.make(root, true, term, true); - } + } else { + if (kLabel.equals(root)) { + return term; } + } + } - // default + // KRewrite instance + if (term instanceof KRewrite rew) { + K left = addRootCell(rew.left()); + if (left == rew.left()) { + return KRewrite(rew.left(), rew.right()); + } else { return IncompleteCellUtils.make(root, true, term, true); + } } - public RuleOrClaim addImplicitCells(RuleOrClaim rule, Module m) { - return rule.newInstance( - addImplicitCells(rule.body(), m), - rule.requires(), - rule.ensures(), - rule.att()); - } + // default + return IncompleteCellUtils.make(root, true, term, true); + } - public Context addImplicitCells(Context context, Module m) { - return new Context( - addImplicitCells(context.body(), m), - context.requires(), - context.att()); - } + public RuleOrClaim addImplicitCells(RuleOrClaim rule, Module m) { + return rule.newInstance( + addImplicitCells(rule.body(), m), rule.requires(), rule.ensures(), rule.att()); + } - public Sentence addImplicitCells(Sentence s, Module m) { - if (skipSentence(s)) { - return s; - } - if (s instanceof RuleOrClaim) { - return addImplicitCells((RuleOrClaim) s, m); - } else if (s instanceof Context) { - return addImplicitCells((Context) s, m); - } else { - return s; - } - } + public Context addImplicitCells(Context context, Module m) { + return new Context(addImplicitCells(context.body(), m), context.requires(), context.att()); + } - private boolean skipSentence(Sentence s) { - return ExpandMacros.isMacro(s) - || s.att().contains(Att.ANYWHERE()) - || s.att().contains(Att.SIMPLIFICATION()); + public Sentence addImplicitCells(Sentence s, Module m) { + if (skipSentence(s)) { + return s; + } + if (s instanceof RuleOrClaim) { + return addImplicitCells((RuleOrClaim) s, m); + } else if (s instanceof Context) { + return addImplicitCells((Context) s, m); + } else { + return s; } + } + + private boolean skipSentence(Sentence s) { + return ExpandMacros.isMacro(s) + || s.att().contains(Att.ANYWHERE()) + || s.att().contains(Att.SIMPLIFICATION()); + } } diff --git a/kernel/src/main/java/org/kframework/compile/Backend.java b/kernel/src/main/java/org/kframework/compile/Backend.java index a5323023217..24b3d87aa1c 100644 --- a/kernel/src/main/java/org/kframework/compile/Backend.java +++ b/kernel/src/main/java/org/kframework/compile/Backend.java @@ -1,44 +1,44 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import java.util.List; +import java.util.Set; +import java.util.function.Function; +import javax.annotation.Nullable; import org.kframework.attributes.Att; import org.kframework.definition.Definition; import org.kframework.definition.Module; import org.kframework.definition.ModuleTransformer; import org.kframework.kompile.CompiledDefinition; -import javax.annotation.Nullable; -import java.util.List; -import java.util.Set; -import java.util.function.Function; - -/** - * Created by dwightguth on 9/1/15. - */ +/** Created by dwightguth on 9/1/15. */ public interface Backend { - class Holder { - public CompiledDefinition def; + class Holder { + public CompiledDefinition def; - public Holder(CompiledDefinition def) { - this.def = def; - } + public Holder(CompiledDefinition def) { + this.def = def; } + } - void accept(Holder def); + void accept(Holder def); - Function steps(); + Function steps(); - Function proofDefinitionNonCachedSteps(@Nullable List extraConcreteRuleLabels); + Function proofDefinitionNonCachedSteps( + @Nullable List extraConcreteRuleLabels); - Function specificationSteps(Definition def); + Function specificationSteps(Definition def); - Set excludedModuleTags(); + Set excludedModuleTags(); - default ModuleTransformer restoreDefinitionModulesTransformer(Definition kompiledDefinition) { - return ModuleTransformer.from(mod -> kompiledDefinition.getModule(mod.name()).isDefined() - ? kompiledDefinition.getModule(mod.name()).get() - : mod, - "restore definition modules to same state as in definition"); - } + default ModuleTransformer restoreDefinitionModulesTransformer(Definition kompiledDefinition) { + return ModuleTransformer.from( + mod -> + kompiledDefinition.getModule(mod.name()).isDefined() + ? kompiledDefinition.getModule(mod.name()).get() + : mod, + "restore definition modules to same state as in definition"); + } } diff --git a/kernel/src/main/java/org/kframework/compile/CloseCells.java b/kernel/src/main/java/org/kframework/compile/CloseCells.java index 0c433c5211a..fa3c21f67d6 100644 --- a/kernel/src/main/java/org/kframework/compile/CloseCells.java +++ b/kernel/src/main/java/org/kframework/compile/CloseCells.java @@ -1,7 +1,13 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.kore.KORE.*; + import com.google.common.collect.Sets; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import org.kframework.attributes.Att; import org.kframework.builtin.Sorts; import org.kframework.definition.Context; @@ -10,281 +16,290 @@ import org.kframework.kore.*; import org.kframework.utils.errorsystem.KEMException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import static org.kframework.kore.KORE.*; - /** * Remove any use of dots in cells, by replacing them with variables and appropriate connectives. * This expects parent cells to have been added by earlier passes, it will only add variables * - * The input to this pass is represents cells as described by {@link IncompleteCellUtils}. - * In the output cells no longer have dots. Leaf cells have a single argument which is - * the body, and parent cells are applied directly to the child cells and variables - * as arguments of the KApply (though not necessarily in the right order) as - * expected by {@link SortCells}. + *

The input to this pass is represents cells as described by {@link IncompleteCellUtils}. In the + * output cells no longer have dots. Leaf cells have a single argument which is the body, and parent + * cells are applied directly to the child cells and variables as arguments of the KApply (though + * not necessarily in the right order) as expected by {@link SortCells}. */ public class CloseCells { - private final ConcretizationInfo cfg; - private final SortInfo sortInfo; - private final LabelInfo labelInfo; + private final ConcretizationInfo cfg; + private final SortInfo sortInfo; + private final LabelInfo labelInfo; - public CloseCells(ConfigurationInfo cfg, SortInfo sortInfo, LabelInfo labelInfo) { - this.cfg = new ConcretizationInfo(cfg, labelInfo); - this.sortInfo = sortInfo; - this.labelInfo = labelInfo; - } + public CloseCells(ConfigurationInfo cfg, SortInfo sortInfo, LabelInfo labelInfo) { + this.cfg = new ConcretizationInfo(cfg, labelInfo); + this.sortInfo = sortInfo; + this.labelInfo = labelInfo; + } - public synchronized K close(K term) { - resetVars(); - gatherVars(term); - return transform(term); - } + public synchronized K close(K term) { + resetVars(); + gatherVars(term); + return transform(term); + } - private RuleOrClaim close(RuleOrClaim rule) { - resetVars(); - gatherVars(rule.body()); - gatherVars(rule.requires()); - gatherVars(rule.ensures()); - return rule.newInstance( - transform(rule.body()), - transform(rule.requires()), - transform(rule.ensures()), - rule.att()); - } + private RuleOrClaim close(RuleOrClaim rule) { + resetVars(); + gatherVars(rule.body()); + gatherVars(rule.requires()); + gatherVars(rule.ensures()); + return rule.newInstance( + transform(rule.body()), transform(rule.requires()), transform(rule.ensures()), rule.att()); + } - private Context close(Context context) { - resetVars(); - gatherVars(context.body()); - gatherVars(context.requires()); - return new Context( - transform(context.body()), - transform(context.requires()), - context.att()); - } + private Context close(Context context) { + resetVars(); + gatherVars(context.body()); + gatherVars(context.requires()); + return new Context(transform(context.body()), transform(context.requires()), context.att()); + } - public synchronized Sentence close(Sentence s) { - if (s instanceof RuleOrClaim) { - return close((RuleOrClaim) s); - } else if (s instanceof Context) { - return close((Context)s); - } else { - return s; - } + public synchronized Sentence close(Sentence s) { + if (s instanceof RuleOrClaim) { + return close((RuleOrClaim) s); + } else if (s instanceof Context) { + return close((Context) s); + } else { + return s; } + } - private int counter = 0; - private final Set vars = Sets.newHashSet(); - private KRewrite rhsOf = null; - - void resetVars() { - counter = 0; - vars.clear(); - rhsOf = null; - } + private int counter = 0; + private final Set vars = Sets.newHashSet(); + private KRewrite rhsOf = null; - KVariable newDotVariable(Sort s) { - KVariable newLabel; - do { - if (s == null) { - newLabel = KVariable("_DotVar" + (counter++), Att().add(Att.ANONYMOUS())); - } else { - newLabel = KVariable("_DotVar" + (counter++), Att().add(Att.ANONYMOUS()).add(Sort.class, s)); - } - } while (vars.contains(newLabel)); - vars.add(newLabel); - return newLabel; - } + void resetVars() { + counter = 0; + vars.clear(); + rhsOf = null; + } - void gatherVars(K term) { - new VisitK() { - @Override - public void apply(KVariable v) { - vars.add(v); - } - }.apply(term); - } + KVariable newDotVariable(Sort s) { + KVariable newLabel; + do { + if (s == null) { + newLabel = KVariable("_DotVar" + (counter++), Att().add(Att.ANONYMOUS())); + } else { + newLabel = + KVariable("_DotVar" + (counter++), Att().add(Att.ANONYMOUS()).add(Sort.class, s)); + } + } while (vars.contains(newLabel)); + vars.add(newLabel); + return newLabel; + } - K transform(K term) { - return new TransformK() { - @Override - public K apply(KApply k) { - return super.apply(closeCell(k)); - } + void gatherVars(K term) { + new VisitK() { + @Override + public void apply(KVariable v) { + vars.add(v); + } + }.apply(term); + } - @Override - public K apply(KRewrite k) { - K l = apply(k.left()); - rhsOf = k; - K r = apply(k.right()); - rhsOf = null; - if (l != k.left() || r != k.right()) { - return KRewrite(l, r, k.att()); - } else { - return k; - } - } - }.apply(term); - } + K transform(K term) { + return new TransformK() { + @Override + public K apply(KApply k) { + return super.apply(closeCell(k)); + } - /** - * Close an individual cell. - */ - protected KApply closeCell(KApply cell) { - KLabel label = cell.klabel(); - if (!cfg.isCell(label)) { - return cell; + @Override + public K apply(KRewrite k) { + K l = apply(k.left()); + rhsOf = k; + K r = apply(k.right()); + rhsOf = null; + if (l != k.left() || r != k.right()) { + return KRewrite(l, r, k.att()); + } else { + return k; } + } + }.apply(term); + } - boolean openLeft = IncompleteCellUtils.isOpenLeft(cell); - boolean openRight = IncompleteCellUtils.isOpenRight(cell); - List contents = IncompleteCellUtils.getChildren(cell); - - if (cfg.isParentCell(label)) { - Set requiredLeft = new HashSet<>(); - Set requiredRight; - for (Sort child : cfg.getChildren(label)) { - if (cfg.getMultiplicity(child) == ConfigurationInfo.Multiplicity.ONE) { - requiredLeft.add(child); - } - } - requiredRight = new HashSet<>(requiredLeft); - for (K item : contents) { - if (item instanceof KRewrite rw) { - for (K leftItem : IncompleteCellUtils.flattenCells(rw.left())) { - filterRequired(requiredLeft, leftItem); - } - for (K rightItem : IncompleteCellUtils.flattenCells(rw.right())) { - filterRequired(requiredRight, rightItem); - } - } else { - filterRequired(requiredLeft, item); - filterRequired(requiredRight, item); - } - } + /** Close an individual cell. */ + protected KApply closeCell(KApply cell) { + KLabel label = cell.klabel(); + if (!cfg.isCell(label)) { + return cell; + } - if (!openLeft && !openRight) { - if (requiredLeft.isEmpty() && requiredRight.isEmpty()) { - return KApply(label, KList(contents)); - } else { - if (requiredLeft.equals(requiredRight)) { - throw KEMException.compilerError("Closed parent cell missing " + - "required children " + requiredLeft, cell); - } else { - throw KEMException.compilerError("Closed parent cell missing " + - "required children " + requiredLeft + " on left hand side and " + requiredRight + " on right hand side.", cell); - } - } - } + boolean openLeft = IncompleteCellUtils.isOpenLeft(cell); + boolean openRight = IncompleteCellUtils.isOpenRight(cell); + List contents = IncompleteCellUtils.getChildren(cell); - if (rhsOf == null) { - // close with variable - List newItems = new ArrayList<>(contents.size() + 1); - newItems.addAll(contents); - newItems.add(newDotVariable(null)); - return KApply(label, KList(newItems)); - } else { - // close by adding default cells - // since we know we are on the right hand side of a rewrite, we assume that - // the cell cannot contain a rewrite and therefore requiredLeft will always equal - // requiredRight. Hence we just pick one. - List newContents = new ArrayList<>(contents.size() + requiredLeft.size()); - newContents.addAll(contents); - for (Sort reqChild : requiredLeft) { - if (!cfg.cfg().isConstantInitializer(reqChild)) - throw KEMException.compilerError("Cannot close cell on right hand side because the initializer for " + reqChild.toString() + " requires configuration variables."); - newContents.add(cfg.getDefaultCell(reqChild)); - } - return (KApply(label, KList(newContents))); - } + if (cfg.isParentCell(label)) { + Set requiredLeft = new HashSet<>(); + Set requiredRight; + for (Sort child : cfg.getChildren(label)) { + if (cfg.getMultiplicity(child) == ConfigurationInfo.Multiplicity.ONE) { + requiredLeft.add(child); } - - // Is a leaf cell - if (contents.size() != 1) { - throw KEMException.criticalError("Leaf cells should contain exactly 1 body term," - + " but there are " + contents.size() + " in " + cell); + } + requiredRight = new HashSet<>(requiredLeft); + for (K item : contents) { + if (item instanceof KRewrite rw) { + for (K leftItem : IncompleteCellUtils.flattenCells(rw.left())) { + filterRequired(requiredLeft, leftItem); + } + for (K rightItem : IncompleteCellUtils.flattenCells(rw.right())) { + filterRequired(requiredRight, rightItem); + } + } else { + filterRequired(requiredLeft, item); + filterRequired(requiredRight, item); } + } - if (!openLeft && !openRight) { - return KApply(label, KList(contents.get(0))); - } - if (rhsOf != null) { - throw KEMException.criticalError("Leaf cells on right hand side of a rewrite" + - " may not be open, but " + cell + " is right of " + rhsOf.toString()); + if (!openLeft && !openRight) { + if (requiredLeft.isEmpty() && requiredRight.isEmpty()) { + return KApply(label, KList(contents)); + } else { + if (requiredLeft.equals(requiredRight)) { + throw KEMException.compilerError( + "Closed parent cell missing " + "required children " + requiredLeft, cell); + } else { + throw KEMException.compilerError( + "Closed parent cell missing " + + "required children " + + requiredLeft + + " on left hand side and " + + requiredRight + + " on right hand side.", + cell); + } } + } - K body = contents.get(0); - Sort cellType = cfg.leafCellType(label); - if (cellType.equals(Sorts.K())) { - // Need special handling to make a KSequence. - int bodyLength; - if (body instanceof KSequence) { - bodyLength = ((KSequence) body).items().size(); - } else { - bodyLength = 1; - } - List newItems = new ArrayList<>((openLeft ? 1 : 0) + bodyLength + (openRight ? 1 : 0)); - if (openLeft) { - newItems.add(newDotVariable(cellType)); - } - if (body instanceof KSequence) { - newItems.addAll(((KSequence) body).items()); - } else { - newItems.add(body); - } - if (openRight) { - newItems.add(newDotVariable(cellType)); - } - return KApply(label, KList(KSequence(newItems))); - } else { - KLabel closeOperator = sortInfo.getCloseOperator(cellType); - if (closeOperator == null) { - throw KEMException.criticalError("No operator registered for closing cells of sort " - + cellType + " when closing cell " + cell); - } - LabelInfo.AssocInfo info = labelInfo.getAssocInfo(closeOperator); - if (!info.isAssoc() && openLeft && openRight) { - throw KEMException.criticalError( - "Ambiguity closing a cell. Operator " + closeOperator - + " for sort " + cellType + " is not associative, " - + "but the cell has ellipses on both sides " + cell); - } - if (info.isComm() && (!openLeft || !openRight || info.isAssoc())) { - openLeft = false; - openRight = true; - } - KVariable leftVar = null; - if (openLeft) { - leftVar = newDotVariable(cellType); - } - if (openRight) { - body = KApply(closeOperator, KList(body, newDotVariable(cellType))); - } - if (openLeft) { - body = KApply(closeOperator, KList(leftVar, body)); - } - return KApply(label, KList(body)); + if (rhsOf == null) { + // close with variable + List newItems = new ArrayList<>(contents.size() + 1); + newItems.addAll(contents); + newItems.add(newDotVariable(null)); + return KApply(label, KList(newItems)); + } else { + // close by adding default cells + // since we know we are on the right hand side of a rewrite, we assume that + // the cell cannot contain a rewrite and therefore requiredLeft will always equal + // requiredRight. Hence we just pick one. + List newContents = new ArrayList<>(contents.size() + requiredLeft.size()); + newContents.addAll(contents); + for (Sort reqChild : requiredLeft) { + if (!cfg.cfg().isConstantInitializer(reqChild)) + throw KEMException.compilerError( + "Cannot close cell on right hand side because the initializer for " + + reqChild.toString() + + " requires configuration variables."); + newContents.add(cfg.getDefaultCell(reqChild)); } + return (KApply(label, KList(newContents))); + } + } + + // Is a leaf cell + if (contents.size() != 1) { + throw KEMException.criticalError( + "Leaf cells should contain exactly 1 body term," + + " but there are " + + contents.size() + + " in " + + cell); + } + + if (!openLeft && !openRight) { + return KApply(label, KList(contents.get(0))); + } + if (rhsOf != null) { + throw KEMException.criticalError( + "Leaf cells on right hand side of a rewrite" + + " may not be open, but " + + cell + + " is right of " + + rhsOf.toString()); } - private void filterRequired(Set required, K item) { - if (item instanceof KApply) { - required.remove(labelInfo.getCodomain(((KApply) item).klabel())); - } else if (item instanceof KVariable) { - if (item.att().contains(Sort.class)) { - Sort sort = item.att().get(Sort.class); - if (cfg.cfg().isCell(sort)) { - required.remove(sort); - } else { - required.clear(); - } - } else { - required.clear(); - } + K body = contents.get(0); + Sort cellType = cfg.leafCellType(label); + if (cellType.equals(Sorts.K())) { + // Need special handling to make a KSequence. + int bodyLength; + if (body instanceof KSequence) { + bodyLength = ((KSequence) body).items().size(); + } else { + bodyLength = 1; + } + List newItems = new ArrayList<>((openLeft ? 1 : 0) + bodyLength + (openRight ? 1 : 0)); + if (openLeft) { + newItems.add(newDotVariable(cellType)); + } + if (body instanceof KSequence) { + newItems.addAll(((KSequence) body).items()); + } else { + newItems.add(body); + } + if (openRight) { + newItems.add(newDotVariable(cellType)); + } + return KApply(label, KList(KSequence(newItems))); + } else { + KLabel closeOperator = sortInfo.getCloseOperator(cellType); + if (closeOperator == null) { + throw KEMException.criticalError( + "No operator registered for closing cells of sort " + + cellType + + " when closing cell " + + cell); + } + LabelInfo.AssocInfo info = labelInfo.getAssocInfo(closeOperator); + if (!info.isAssoc() && openLeft && openRight) { + throw KEMException.criticalError( + "Ambiguity closing a cell. Operator " + + closeOperator + + " for sort " + + cellType + + " is not associative, " + + "but the cell has ellipses on both sides " + + cell); + } + if (info.isComm() && (!openLeft || !openRight || info.isAssoc())) { + openLeft = false; + openRight = true; + } + KVariable leftVar = null; + if (openLeft) { + leftVar = newDotVariable(cellType); + } + if (openRight) { + body = KApply(closeOperator, KList(body, newDotVariable(cellType))); + } + if (openLeft) { + body = KApply(closeOperator, KList(leftVar, body)); + } + return KApply(label, KList(body)); + } + } + + private void filterRequired(Set required, K item) { + if (item instanceof KApply) { + required.remove(labelInfo.getCodomain(((KApply) item).klabel())); + } else if (item instanceof KVariable) { + if (item.att().contains(Sort.class)) { + Sort sort = item.att().get(Sort.class); + if (cfg.cfg().isCell(sort)) { + required.remove(sort); + } else { + required.clear(); } + } else { + required.clear(); + } } + } } diff --git a/kernel/src/main/java/org/kframework/compile/ComputeTransitiveFunctionDependencies.java b/kernel/src/main/java/org/kframework/compile/ComputeTransitiveFunctionDependencies.java index f11ee68875e..889eeefcdea 100644 --- a/kernel/src/main/java/org/kframework/compile/ComputeTransitiveFunctionDependencies.java +++ b/kernel/src/main/java/org/kframework/compile/ComputeTransitiveFunctionDependencies.java @@ -1,19 +1,10 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; -import org.kframework.attributes.Att; -import org.kframework.builtin.KLabels; -import org.kframework.definition.Module; -import org.kframework.definition.Rule; -import org.kframework.kore.K; -import org.kframework.kore.KLabel; -import org.kframework.kore.KApply; -import org.kframework.kore.VisitK; +import static org.kframework.Collections.*; import edu.uci.ics.jung.graph.DirectedGraph; import edu.uci.ics.jung.graph.DirectedSparseGraph; -import scala.Tuple2; - import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -21,90 +12,100 @@ import java.util.LinkedList; import java.util.Queue; import java.util.Set; - -import static org.kframework.Collections.*; +import org.kframework.attributes.Att; +import org.kframework.builtin.KLabels; +import org.kframework.definition.Module; +import org.kframework.definition.Rule; +import org.kframework.kore.K; +import org.kframework.kore.KApply; +import org.kframework.kore.KLabel; +import org.kframework.kore.VisitK; +import scala.Tuple2; public class ComputeTransitiveFunctionDependencies { - public ComputeTransitiveFunctionDependencies(Module module) { - dependencies = new DirectedSparseGraph<>(); + public ComputeTransitiveFunctionDependencies(Module module) { + dependencies = new DirectedSparseGraph<>(); - Set anywhereKLabels = new HashSet<>(); - stream(module.rules()).filter(r -> !ExpandMacros.isMacro(r)).forEach(r -> { - K left = RewriteToTop.toLeft(r.body()); - if (left instanceof KApply kapp) { + Set anywhereKLabels = new HashSet<>(); + stream(module.rules()) + .filter(r -> !ExpandMacros.isMacro(r)) + .forEach( + r -> { + K left = RewriteToTop.toLeft(r.body()); + if (left instanceof KApply kapp) { if (r.att().contains(Att.ANYWHERE())) { - if (kapp.klabel().name().equals(KLabels.INJ)) { - K k = kapp.items().get(0); - if (k instanceof KApply) { - anywhereKLabels.add(((KApply)k).klabel()); - } - } else { - anywhereKLabels.add(kapp.klabel()); + if (kapp.klabel().name().equals(KLabels.INJ)) { + K k = kapp.items().get(0); + if (k instanceof KApply) { + anywhereKLabels.add(((KApply) k).klabel()); } + } else { + anywhereKLabels.add(kapp.klabel()); + } } - } - }); + } + }); - class GetPredecessors extends VisitK { - private final KLabel current; + class GetPredecessors extends VisitK { + private final KLabel current; - private GetPredecessors(KLabel current) { - this.current = current; - } + private GetPredecessors(KLabel current) { + this.current = current; + } - @Override - public void apply(KApply k) { - if (k.klabel().name().equals(KLabels.INJ)) { - super.apply(k); - return; - } - if (module.attributesFor().getOrElse(k.klabel(), () -> Att.empty()).contains(Att.FUNCTION()) || anywhereKLabels.contains(k.klabel())) { - dependencies.addEdge(new Object(), current, k.klabel()); - } - super.apply(k); - } + @Override + public void apply(KApply k) { + if (k.klabel().name().equals(KLabels.INJ)) { + super.apply(k); + return; } - - for (Tuple2> entry : iterable(module.rulesFor())) { - for (Rule rule : iterable(entry._2())) { - if (module.attributesFor().getOrElse(entry._1(), () -> Att.empty()).contains(Att.FUNCTION())) { - GetPredecessors visitor = new GetPredecessors(entry._1()); - visitor.apply(rule.body()); - visitor.apply(rule.requires()); - } - } + if (module.attributesFor().getOrElse(k.klabel(), () -> Att.empty()).contains(Att.FUNCTION()) + || anywhereKLabels.contains(k.klabel())) { + dependencies.addEdge(new Object(), current, k.klabel()); } + super.apply(k); + } } - private static Set ancestors( - Collection startNodes, DirectedGraph graph) - { - Queue queue = new LinkedList(); - queue.addAll(startNodes); - Set visited = new LinkedHashSet(startNodes); - while(!queue.isEmpty()) - { - V v = queue.poll(); - Collection neighbors = graph.getPredecessors(v); - for (V n : neighbors) - { - if (!visited.contains(n)) - { - queue.offer(n); - visited.add(n); - } - } + for (Tuple2> entry : iterable(module.rulesFor())) { + for (Rule rule : iterable(entry._2())) { + if (module + .attributesFor() + .getOrElse(entry._1(), () -> Att.empty()) + .contains(Att.FUNCTION())) { + GetPredecessors visitor = new GetPredecessors(entry._1()); + visitor.apply(rule.body()); + visitor.apply(rule.requires()); } - return visited; + } } + } - public Set ancestors(KLabel label) { - return ancestors(Collections.singleton(label), dependencies); + private static Set ancestors( + Collection startNodes, DirectedGraph graph) { + Queue queue = new LinkedList(); + queue.addAll(startNodes); + Set visited = new LinkedHashSet(startNodes); + while (!queue.isEmpty()) { + V v = queue.poll(); + Collection neighbors = graph.getPredecessors(v); + for (V n : neighbors) { + if (!visited.contains(n)) { + queue.offer(n); + visited.add(n); + } + } } + return visited; + } - public Set ancestors(Set labels) { - return ancestors(labels, dependencies); - } + public Set ancestors(KLabel label) { + return ancestors(Collections.singleton(label), dependencies); + } + + public Set ancestors(Set labels) { + return ancestors(labels, dependencies); + } - private final DirectedGraph dependencies; + private final DirectedGraph dependencies; } diff --git a/kernel/src/main/java/org/kframework/compile/ConcretizationInfo.java b/kernel/src/main/java/org/kframework/compile/ConcretizationInfo.java index 696da3ca5c5..329efa91faf 100644 --- a/kernel/src/main/java/org/kframework/compile/ConcretizationInfo.java +++ b/kernel/src/main/java/org/kframework/compile/ConcretizationInfo.java @@ -1,6 +1,9 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.kore.KORE.*; + +import java.util.List; import org.kframework.kore.K; import org.kframework.kore.KApply; import org.kframework.kore.KLabel; @@ -8,93 +11,94 @@ import org.kframework.kore.Sort; import scala.Option; -import java.util.List; - -import static org.kframework.kore.KORE.*; - -/** - * Created by brandon on 3/31/15. - */ +/** Created by brandon on 3/31/15. */ public record ConcretizationInfo(ConfigurationInfo cfg, LabelInfo labels) { - public Sort getCellSort(K k) { - if (k instanceof KApply) { - return labels.getCodomain(((KApply) k).klabel()); - } else if (k instanceof KVariable) { - return k.att().get(Sort.class); - } else { - throw new AssertionError("expected KApply or KVariable, found " + k.getClass().getSimpleName()); - } - } - - public ConfigurationInfo.Multiplicity getMultiplicity(KLabel label) { - return cfg.getMultiplicity(labels.getCodomain(label)); - } - public ConfigurationInfo.Multiplicity getMultiplicity(Sort sort) { - return cfg.getMultiplicity(sort); - } - - public int getLevel(KLabel label) { - return cfg.getLevel(labels.getCodomain(label)); - } - - public KLabel getParent(KLabel klabel) { - return getParent(labels.getCodomain(klabel)); - } - - public KLabel getParent(Sort sort) { - return cfg.getCellLabel(cfg.getParent(sort)); - } - - public Sort getCellSort(KLabel cellLabel) { - Sort s = labels.getCodomain(cellLabel); - return cfg.isCell(s) ? s : null; - } - - /** If {@code label} is a label making a cell collection, return the - * Sort of the cells in that collection. - */ - public Sort getCellCollectionCell(KLabel label) { - Option result = cfg.getCellForConcat(label); - if (result.isEmpty()) { - result = cfg.getCellForUnit(label); - } - return result.isDefined() ? result.get() : null; - } - public KLabel getCellFragmentLabel(KLabel cellLabel) { - Sort s = labels.getCodomain(cellLabel); - return cfg.getCellFragmentLabel(s); - } - - public K getCellAbsentTerm(Sort cellSort) { - KLabel l = cfg.getCellAbsentLabel(cellSort); - return l == null ? null : KApply(l); - } - - public boolean isCellCollection(KLabel klabel) { - Sort s = labels.getCodomain(klabel); - return cfg.isCellCollection(s); - } - - public boolean isCell(KLabel klabel) { - Sort s = labels.getCodomain(klabel); - return cfg.isCell(s) && cfg.getCellLabel(s).equals(klabel); - } - public boolean isLeafCell(KLabel klabel) { - return cfg.isLeafCell(labels.getCodomain(klabel)); - } - public boolean isParentCell(KLabel klabel) { - return isCell(klabel) && cfg.isParentCell(labels.getCodomain(klabel)); - } - - public Sort leafCellType(KLabel label) { - return cfg.leafCellType(labels.getCodomain(label)); - } - public List getChildren(KLabel label) { - return cfg.getChildren(labels.getCodomain(label)); - } - - public K getDefaultCell(Sort sort) { - return cfg.getDefaultCell(sort); - } + public Sort getCellSort(K k) { + if (k instanceof KApply) { + return labels.getCodomain(((KApply) k).klabel()); + } else if (k instanceof KVariable) { + return k.att().get(Sort.class); + } else { + throw new AssertionError( + "expected KApply or KVariable, found " + k.getClass().getSimpleName()); + } + } + + public ConfigurationInfo.Multiplicity getMultiplicity(KLabel label) { + return cfg.getMultiplicity(labels.getCodomain(label)); + } + + public ConfigurationInfo.Multiplicity getMultiplicity(Sort sort) { + return cfg.getMultiplicity(sort); + } + + public int getLevel(KLabel label) { + return cfg.getLevel(labels.getCodomain(label)); + } + + public KLabel getParent(KLabel klabel) { + return getParent(labels.getCodomain(klabel)); + } + + public KLabel getParent(Sort sort) { + return cfg.getCellLabel(cfg.getParent(sort)); + } + + public Sort getCellSort(KLabel cellLabel) { + Sort s = labels.getCodomain(cellLabel); + return cfg.isCell(s) ? s : null; + } + + /** + * If {@code label} is a label making a cell collection, return the Sort of the cells in that + * collection. + */ + public Sort getCellCollectionCell(KLabel label) { + Option result = cfg.getCellForConcat(label); + if (result.isEmpty()) { + result = cfg.getCellForUnit(label); + } + return result.isDefined() ? result.get() : null; + } + + public KLabel getCellFragmentLabel(KLabel cellLabel) { + Sort s = labels.getCodomain(cellLabel); + return cfg.getCellFragmentLabel(s); + } + + public K getCellAbsentTerm(Sort cellSort) { + KLabel l = cfg.getCellAbsentLabel(cellSort); + return l == null ? null : KApply(l); + } + + public boolean isCellCollection(KLabel klabel) { + Sort s = labels.getCodomain(klabel); + return cfg.isCellCollection(s); + } + + public boolean isCell(KLabel klabel) { + Sort s = labels.getCodomain(klabel); + return cfg.isCell(s) && cfg.getCellLabel(s).equals(klabel); + } + + public boolean isLeafCell(KLabel klabel) { + return cfg.isLeafCell(labels.getCodomain(klabel)); + } + + public boolean isParentCell(KLabel klabel) { + return isCell(klabel) && cfg.isParentCell(labels.getCodomain(klabel)); + } + + public Sort leafCellType(KLabel label) { + return cfg.leafCellType(labels.getCodomain(label)); + } + + public List getChildren(KLabel label) { + return cfg.getChildren(labels.getCodomain(label)); + } + + public K getDefaultCell(Sort sort) { + return cfg.getDefaultCell(sort); + } } diff --git a/kernel/src/main/java/org/kframework/compile/ConcretizeCells.java b/kernel/src/main/java/org/kframework/compile/ConcretizeCells.java index 420154b3b65..a38dc4027c4 100644 --- a/kernel/src/main/java/org/kframework/compile/ConcretizeCells.java +++ b/kernel/src/main/java/org/kframework/compile/ConcretizeCells.java @@ -1,79 +1,72 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; -import org.kframework.compile.ConfigurationInfo; -import org.kframework.compile.ConfigurationInfoFromModule; -import org.kframework.compile.LabelInfo; -import org.kframework.compile.LabelInfoFromModule; import org.kframework.definition.*; import org.kframework.definition.Module; /** - * Apply the configuration concretization process. - * The implicit {@code } cell is added by another stage, AddImplicitComputationCell. - *

- * The input may freely use various configuration abstractions - * and Full K flexibilites. See {@link IncompleteCellUtils} for a - * description of the expected term structure. - * The output will represent cells in - * strict accordance with their declared fixed-arity productions. - *

- * This is a simple composition of the - * {@link AddTopCellToRules}, {@link AddParentCells}, - * {@link CloseCells}, and {@link SortCells} passes, - * see their documentation for details on the transformations. + * Apply the configuration concretization process. The implicit {@code } cell is added by another + * stage, AddImplicitComputationCell. + * + *

The input may freely use various configuration abstractions and Full K flexibilites. See + * {@link IncompleteCellUtils} for a description of the expected term structure. The output will + * represent cells in strict accordance with their declared fixed-arity productions. + * + *

This is a simple composition of the {@link AddTopCellToRules}, {@link AddParentCells}, {@link + * CloseCells}, and {@link SortCells} passes, see their documentation for details on the + * transformations. */ public class ConcretizeCells { - final ConfigurationInfo configurationInfo; - final LabelInfo labelInfo; - final SortInfo sortInfo; - final Module module; + final ConfigurationInfo configurationInfo; + final LabelInfo labelInfo; + final SortInfo sortInfo; + final Module module; - final AddParentCells addParentCells; - final CloseCells closeCells; - final SortCells sortCells; - private final AddTopCellToRules addRootCell; + final AddParentCells addParentCells; + final CloseCells closeCells; + final SortCells sortCells; + private final AddTopCellToRules addRootCell; - public static Definition transformDefinition(Definition input) { - ConfigurationInfoFromModule configInfo = new ConfigurationInfoFromModule(input.mainModule()); - LabelInfo labelInfo = new LabelInfoFromModule(input.mainModule()); - SortInfo sortInfo = SortInfo.fromModule(input.mainModule()); - return DefinitionTransformer.fromSentenceTransformer( - new ConcretizeCells(configInfo, labelInfo, sortInfo, input.mainModule())::concretize, - "concretizing configuration" - ).apply(input); - } + public static Definition transformDefinition(Definition input) { + ConfigurationInfoFromModule configInfo = new ConfigurationInfoFromModule(input.mainModule()); + LabelInfo labelInfo = new LabelInfoFromModule(input.mainModule()); + SortInfo sortInfo = SortInfo.fromModule(input.mainModule()); + return DefinitionTransformer.fromSentenceTransformer( + new ConcretizeCells(configInfo, labelInfo, sortInfo, input.mainModule())::concretize, + "concretizing configuration") + .apply(input); + } + public static Module transformModule(Module mod) { + ConfigurationInfoFromModule configInfo = new ConfigurationInfoFromModule(mod); + LabelInfo labelInfo = new LabelInfoFromModule(mod); + SortInfo sortInfo = SortInfo.fromModule(mod); + return ModuleTransformer.fromSentenceTransformer( + new ConcretizeCells(configInfo, labelInfo, sortInfo, mod)::concretize, + "concretizing configuration") + .apply(mod); + } - public static Module transformModule(Module mod) { - ConfigurationInfoFromModule configInfo = new ConfigurationInfoFromModule(mod); - LabelInfo labelInfo = new LabelInfoFromModule(mod); - SortInfo sortInfo = SortInfo.fromModule(mod); - return ModuleTransformer.fromSentenceTransformer( - new ConcretizeCells(configInfo, labelInfo, sortInfo, mod)::concretize, - "concretizing configuration").apply(mod); - } + public ConcretizeCells( + ConfigurationInfo configurationInfo, LabelInfo labelInfo, SortInfo sortInfo, Module module) { + this.configurationInfo = configurationInfo; + this.labelInfo = labelInfo; + this.sortInfo = sortInfo; + this.module = module; + addRootCell = new AddTopCellToRules(configurationInfo, labelInfo); + addParentCells = new AddParentCells(configurationInfo, labelInfo); + closeCells = new CloseCells(configurationInfo, sortInfo, labelInfo); + sortCells = new SortCells(configurationInfo, labelInfo, module); + } + public Sentence concretize(Module m, Sentence s) { + s = addRootCell.addImplicitCells(s, m); + s = addParentCells.concretize(s); + s = closeCells.close(s); - public ConcretizeCells(ConfigurationInfo configurationInfo, LabelInfo labelInfo, SortInfo sortInfo, Module module) { - this.configurationInfo = configurationInfo; - this.labelInfo = labelInfo; - this.sortInfo = sortInfo; - this.module = module; - addRootCell = new AddTopCellToRules(configurationInfo, labelInfo); - addParentCells = new AddParentCells(configurationInfo, labelInfo); - closeCells = new CloseCells(configurationInfo, sortInfo, labelInfo); - sortCells = new SortCells(configurationInfo, labelInfo, module); - } - - public Sentence concretize(Module m, Sentence s) { - s = addRootCell.addImplicitCells(s, m); - s = addParentCells.concretize(s); - s = closeCells.close(s); - - s = sortCells.preprocess(s); - s = sortCells.sortCells(s); - s = sortCells.postprocess(s); - return s; - } + s = sortCells.preprocess(s); + s = sortCells.sortCells(s); + s = sortCells.postprocess(s); + return s; + } } diff --git a/kernel/src/main/java/org/kframework/compile/ConstantFolding.java b/kernel/src/main/java/org/kframework/compile/ConstantFolding.java index c0efd55e332..0387eae4d08 100644 --- a/kernel/src/main/java/org/kframework/compile/ConstantFolding.java +++ b/kernel/src/main/java/org/kframework/compile/ConstantFolding.java @@ -1,6 +1,17 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import org.apache.commons.lang3.StringUtils; import org.kframework.attributes.Att; import org.kframework.builtin.Hooks; @@ -11,28 +22,15 @@ import org.kframework.kore.KApply; import org.kframework.kore.KToken; import org.kframework.kore.Sort; -import org.kframework.kore.TransformK; -import org.kframework.utils.errorsystem.KEMException; -import org.kframework.utils.StringUtil; import org.kframework.mpfr.BigFloat; import org.kframework.mpfr.BinaryMathContext; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.math.BigInteger; -import java.math.RoundingMode; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; - -import static org.kframework.Collections.*; -import static org.kframework.kore.KORE.*; -import static org.kframework.definition.Constructors.*; +import org.kframework.utils.StringUtil; +import org.kframework.utils.errorsystem.KEMException; public class ConstantFolding { - private static final List hookNamespaces = Arrays.asList(Hooks.BOOL, Hooks.FLOAT, Hooks.INT, Hooks.STRING); + private static final List hookNamespaces = + Arrays.asList(Hooks.BOOL, Hooks.FLOAT, Hooks.INT, Hooks.STRING); private K loc; @@ -42,7 +40,11 @@ void setLoc(K loc) { public Sentence fold(Module module, Sentence sentence) { if (sentence instanceof Rule r) { - return Rule(fold(module, r.body(), true), fold(module, r.requires(), false), fold(module, r.ensures(), false), r.att()); + return Rule( + fold(module, r.body(), true), + fold(module, r.requires(), false), + fold(module, r.ensures(), false), + r.att()); } return sentence; } @@ -72,9 +74,19 @@ public K apply(KApply k) { } try { loc = k; - return doFolding(hook, args, module.productionsFor().apply(k.klabel().head()).head().substitute(k.klabel().params()).sort(), module); + return doFolding( + hook, + args, + module + .productionsFor() + .apply(k.klabel().head()) + .head() + .substitute(k.klabel().params()) + .sort(), + module); } catch (NoSuchMethodException e) { - throw KEMException.internalError("Missing constant-folding implementation for hook " + hook, e); + throw KEMException.internalError( + "Missing constant-folding implementation for hook " + hook, e); } } } @@ -104,42 +116,48 @@ private Object unwrap(String token, String hook) { } private K wrap(Object result, Sort sort, Module module) { - String resultHookName = module.sortAttributesFor().apply(sort.head()).getOptional(Att.HOOK()).orElse(""); - boolean hasStringHook = resultHookName.equals("STRING.String") || resultHookName.equals("BYTES.Bytes"); + String resultHookName = + module.sortAttributesFor().apply(sort.head()).getOptional(Att.HOOK()).orElse(""); + boolean hasStringHook = + resultHookName.equals("STRING.String") || resultHookName.equals("BYTES.Bytes"); if (result instanceof Boolean) { return KToken(result.toString(), sort); } else if (result instanceof FloatBuiltin) { - return KToken(((FloatBuiltin)result).value(), sort); + return KToken(((FloatBuiltin) result).value(), sort); } else if (result instanceof BigInteger) { return KToken(result.toString(), sort); } else if (result instanceof String && hasStringHook) { - return KToken(StringUtil.enquoteKString((String)result), sort); + return KToken(StringUtil.enquoteKString((String) result), sort); } else { return KToken(result.toString(), sort); } } - private K doFolding(String hook, List args, Sort resultSort, Module module) throws NoSuchMethodException { + private K doFolding(String hook, List args, Sort resultSort, Module module) + throws NoSuchMethodException { String renamedHook = hook.replace('.', '_'); List> paramTypes = new ArrayList<>(); List unwrappedArgs = new ArrayList<>(); for (K arg : args) { - KToken tok = (KToken)arg; + KToken tok = (KToken) arg; Sort sort = tok.sort(); - String argHook = module.sortAttributesFor().apply(sort.head()).getOptional(Att.HOOK()).orElse(""); + String argHook = + module.sortAttributesFor().apply(sort.head()).getOptional(Att.HOOK()).orElse(""); paramTypes.add(classOf(argHook)); unwrappedArgs.add(unwrap(tok.s(), argHook)); } try { - Method m = ConstantFolding.class.getDeclaredMethod(renamedHook, paramTypes.toArray(new Class[args.size()])); + Method m = + ConstantFolding.class.getDeclaredMethod( + renamedHook, paramTypes.toArray(new Class[args.size()])); Object result = m.invoke(this, unwrappedArgs.toArray(new Object[args.size()])); return wrap(result, resultSort, module); } catch (IllegalAccessException e) { throw KEMException.internalError("Error invoking constant folding function", e); } catch (InvocationTargetException e) { if (e.getCause() instanceof KEMException) { - throw (KEMException)e.getCause(); + throw (KEMException) e.getCause(); } else { throw KEMException.internalError("Error invoking constant folding function", e); } @@ -147,7 +165,7 @@ private K doFolding(String hook, List args, Sort resultSort, Module module) t } boolean BOOL_not(boolean a) { - return ! a; + return !a; } boolean BOOL_and(boolean a, boolean b) { @@ -171,7 +189,7 @@ boolean BOOL_orElse(boolean a, boolean b) { } boolean BOOL_implies(boolean a, boolean b) { - return ! a || b; + return !a || b; } boolean BOOL_eq(boolean a, boolean b) { @@ -193,28 +211,35 @@ BigInteger STRING_length(String a) { String STRING_chr(BigInteger a) { // unicode code points range from 0x0 to 0x10ffff if (a.compareTo(BigInteger.ZERO) < 0 || a.compareTo(BigInteger.valueOf(0x10ffff)) > 0) { - throw KEMException.compilerError("Argument to hook STRING.chr out of range. Expected a number between 0 and 1114111.", loc); + throw KEMException.compilerError( + "Argument to hook STRING.chr out of range. Expected a number between 0 and 1114111.", + loc); } - int[] codePoint = new int[] { a.intValue() }; + int[] codePoint = new int[] {a.intValue()}; return new String(codePoint, 0, 1); } BigInteger STRING_ord(String a) { if (a.codePointCount(0, a.length()) != 1) { - throw KEMException.compilerError("Argument to hook STRING.ord out of range. Expected a single character."); + throw KEMException.compilerError( + "Argument to hook STRING.ord out of range. Expected a single character."); } return BigInteger.valueOf(a.codePointAt(0)); } private void throwIfNotInt(BigInteger i, String hook) { - if (i.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0 || i.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) { - throw KEMException.compilerError("Argument to hook " + hook + " out of range. Expected a 32-bit signed integer.", loc); + if (i.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0 + || i.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) { + throw KEMException.compilerError( + "Argument to hook " + hook + " out of range. Expected a 32-bit signed integer.", loc); } } private void throwIfNotUnsignedInt(BigInteger i, String hook) { - if (i.compareTo(BigInteger.ZERO) < 0 || i.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) { - throw KEMException.compilerError("Argument to hook " + hook + " out of range. Expected a 32-bit unsigned integer.", loc); + if (i.compareTo(BigInteger.ZERO) < 0 + || i.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) { + throw KEMException.compilerError( + "Argument to hook " + hook + " out of range. Expected a 32-bit unsigned integer.", loc); } } @@ -222,9 +247,14 @@ String STRING_substr(String s, BigInteger start, BigInteger end) { throwIfNotUnsignedInt(start, "STRING.substr"); throwIfNotUnsignedInt(end, "STRING.substr"); try { - return s.substring(s.offsetByCodePoints(0, start.intValue()), s.offsetByCodePoints(0, end.intValue())); + return s.substring( + s.offsetByCodePoints(0, start.intValue()), s.offsetByCodePoints(0, end.intValue())); } catch (IndexOutOfBoundsException e) { - throw KEMException.compilerError("Argument to hook STRING.substr out of range. Expected two indices >= 0 and <= the length of the string.", e, loc); + throw KEMException.compilerError( + "Argument to hook STRING.substr out of range. Expected two indices >= 0 and <= the length" + + " of the string.", + e, + loc); } } @@ -232,10 +262,14 @@ BigInteger STRING_find(String haystack, String needle, BigInteger idx) { throwIfNotUnsignedInt(idx, "STRING.find"); try { int offset = haystack.offsetByCodePoints(0, idx.intValue()); - int foundOffset = haystack.indexOf(needle, offset); + int foundOffset = haystack.indexOf(needle, offset); return BigInteger.valueOf((foundOffset == -1 ? -1 : haystack.codePointCount(0, foundOffset))); - } catch(IndexOutOfBoundsException e) { - throw KEMException.compilerError("Argument to hook STRING.find out of range. Expected an index >= 0 and <= the length of the string to search.", e, loc); + } catch (IndexOutOfBoundsException e) { + throw KEMException.compilerError( + "Argument to hook STRING.find out of range. Expected an index >= 0 and <= the length of" + + " the string to search.", + e, + loc); } } @@ -245,8 +279,12 @@ BigInteger STRING_rfind(String haystack, String needle, BigInteger idx) { int offset = haystack.offsetByCodePoints(0, idx.intValue()); int foundOffset = haystack.lastIndexOf(needle, offset); return BigInteger.valueOf((foundOffset == -1 ? -1 : haystack.codePointCount(0, foundOffset))); - } catch(IndexOutOfBoundsException e) { - throw KEMException.compilerError("Argument to hook STRING.rfind out of range. Expected an index >= 0 and <= the length of the string to search.", e, loc); + } catch (IndexOutOfBoundsException e) { + throw KEMException.compilerError( + "Argument to hook STRING.rfind out of range. Expected an index >= 0 and <= the length of" + + " the string to search.", + e, + loc); } } @@ -256,8 +294,12 @@ BigInteger STRING_findChar(String haystack, String needles, BigInteger idx) { int offset = haystack.offsetByCodePoints(0, idx.intValue()); int foundOffset = StringUtil.indexOfAny(haystack, needles, offset); return BigInteger.valueOf((foundOffset == -1 ? -1 : haystack.codePointCount(0, foundOffset))); - } catch(IndexOutOfBoundsException e) { - throw KEMException.compilerError("Argument to hook STRING.findChar out of range. Expected an index >= 0 and <= the length of the string to search.", e, loc); + } catch (IndexOutOfBoundsException e) { + throw KEMException.compilerError( + "Argument to hook STRING.findChar out of range. Expected an index >= 0 and <= the length" + + " of the string to search.", + e, + loc); } } @@ -267,8 +309,12 @@ BigInteger STRING_rfindChar(String haystack, String needles, BigInteger idx) { int offset = haystack.offsetByCodePoints(0, idx.intValue()); int foundOffset = StringUtil.lastIndexOfAny(haystack, needles, offset); return BigInteger.valueOf((foundOffset == -1 ? -1 : haystack.codePointCount(0, foundOffset))); - } catch(IndexOutOfBoundsException e) { - throw KEMException.compilerError("Argument to hook STRING.rfindChar out of range. Expected an index >= 0 and <= the length of the string to search.", e, loc); + } catch (IndexOutOfBoundsException e) { + throw KEMException.compilerError( + "Argument to hook STRING.rfindChar out of range. Expected an index >= 0 and <= the length" + + " of the string to search.", + e, + loc); } } @@ -284,7 +330,10 @@ FloatBuiltin STRING_string2float(String s) { try { return FloatBuiltin.of(s); } catch (NumberFormatException e) { - throw KEMException.compilerError("Argument to hook STRING.string2float invalid. Expected a valid floating point nuwber.", e, loc); + throw KEMException.compilerError( + "Argument to hook STRING.string2float invalid. Expected a valid floating point nuwber.", + e, + loc); } } @@ -292,7 +341,8 @@ BigInteger STRING_string2int(String s) { try { return new BigInteger(s, 10); } catch (NumberFormatException e) { - throw KEMException.compilerError("Argument to hook STRING.string2int invalid. Expected a valid integer.", e, loc); + throw KEMException.compilerError( + "Argument to hook STRING.string2int invalid. Expected a valid integer.", e, loc); } } @@ -302,18 +352,27 @@ String STRING_int2string(BigInteger i) { BigInteger STRING_string2base(String s, BigInteger base) { if (base.compareTo(BigInteger.valueOf(2)) < 0 || base.compareTo(BigInteger.valueOf(36)) > 0) { - throw KEMException.compilerError("Argument to hook STRING.string2base out of range. Expected a number between 2 and 36.", loc); + throw KEMException.compilerError( + "Argument to hook STRING.string2base out of range. Expected a number between 2 and 36.", + loc); } try { return new BigInteger(s, base.intValue()); } catch (NumberFormatException e) { - throw KEMException.compilerError("Argument to hook STRING.string2base invalid. Expected a valid integer in base " + base.intValue() + ".", e, loc); + throw KEMException.compilerError( + "Argument to hook STRING.string2base invalid. Expected a valid integer in base " + + base.intValue() + + ".", + e, + loc); } } String STRING_base2string(BigInteger i, BigInteger base) { if (base.compareTo(BigInteger.valueOf(2)) < 0 || base.compareTo(BigInteger.valueOf(36)) > 0) { - throw KEMException.compilerError("Argument to hook STRING.string2base out of range. Expected a number between 2 and 36.", loc); + throw KEMException.compilerError( + "Argument to hook STRING.string2base out of range. Expected a number between 2 and 36.", + loc); } return i.toString(base.intValue()); } @@ -379,8 +438,12 @@ BigInteger INT_pow(BigInteger a, BigInteger b) { BigInteger INT_powmod(BigInteger a, BigInteger b, BigInteger c) { try { return a.modPow(b, c); - } catch(ArithmeticException e) { - throw KEMException.compilerError("Argument to hook INT.powmod is invalid. Modulus must be positive and negative exponents are only allowed when value and modulus are relatively prime.", e, loc); + } catch (ArithmeticException e) { + throw KEMException.compilerError( + "Argument to hook INT.powmod is invalid. Modulus must be positive and negative exponents" + + " are only allowed when value and modulus are relatively prime.", + e, + loc); } } @@ -464,7 +527,8 @@ BigInteger INT_abs(BigInteger a) { BigInteger INT_log2(BigInteger a) { if (a.compareTo(BigInteger.ZERO) <= 0) { - throw KEMException.compilerError("Argument to hook INT.log2 out of range. Expected a positive integer.", loc); + throw KEMException.compilerError( + "Argument to hook INT.log2 out of range. Expected a positive integer.", loc); } int log2 = 0; while (a.compareTo(BigInteger.ONE) > 0) { @@ -477,7 +541,12 @@ BigInteger INT_log2(BigInteger a) { BigInteger INT_bitRange(BigInteger i, BigInteger index, BigInteger length) { throwIfNotUnsignedInt(index, "INT.bitRange"); throwIfNotUnsignedInt(length, "INT.bitRange"); - return i.and(BigInteger.ONE.shiftLeft(length.intValue()).subtract(BigInteger.ONE).shiftLeft(index.intValue())).shiftRight(index.intValue()); + return i.and( + BigInteger.ONE + .shiftLeft(length.intValue()) + .subtract(BigInteger.ONE) + .shiftLeft(index.intValue())) + .shiftRight(index.intValue()); } BigInteger INT_signExtendBitRange(BigInteger i, BigInteger index, BigInteger length) { @@ -549,38 +618,45 @@ FloatBuiltin FLOAT_neg(FloatBuiltin f) { private void throwIfNotMatched(FloatBuiltin a, FloatBuiltin b, String hook) { if (!a.getMathContext().equals(b.getMathContext())) { - throw KEMException.compilerError("Arguments to hook " + hook + " do not match in exponent bits and precision.", loc); + throw KEMException.compilerError( + "Arguments to hook " + hook + " do not match in exponent bits and precision.", loc); } } FloatBuiltin FLOAT_pow(FloatBuiltin a, FloatBuiltin b) { throwIfNotMatched(a, b, "FLOAT.pow"); - return FloatBuiltin.of(a.bigFloatValue().pow(b.bigFloatValue(), a.getMathContext()), a.exponent()); + return FloatBuiltin.of( + a.bigFloatValue().pow(b.bigFloatValue(), a.getMathContext()), a.exponent()); } FloatBuiltin FLOAT_mul(FloatBuiltin a, FloatBuiltin b) { throwIfNotMatched(a, b, "FLOAT.mul"); - return FloatBuiltin.of(a.bigFloatValue().multiply(b.bigFloatValue(), a.getMathContext()), a.exponent()); + return FloatBuiltin.of( + a.bigFloatValue().multiply(b.bigFloatValue(), a.getMathContext()), a.exponent()); } FloatBuiltin FLOAT_div(FloatBuiltin a, FloatBuiltin b) { throwIfNotMatched(a, b, "FLOAT.div"); - return FloatBuiltin.of(a.bigFloatValue().divide(b.bigFloatValue(), a.getMathContext()), a.exponent()); + return FloatBuiltin.of( + a.bigFloatValue().divide(b.bigFloatValue(), a.getMathContext()), a.exponent()); } FloatBuiltin FLOAT_rem(FloatBuiltin a, FloatBuiltin b) { throwIfNotMatched(a, b, "FLOAT.rem"); - return FloatBuiltin.of(a.bigFloatValue().remainder(b.bigFloatValue(), a.getMathContext()), a.exponent()); + return FloatBuiltin.of( + a.bigFloatValue().remainder(b.bigFloatValue(), a.getMathContext()), a.exponent()); } FloatBuiltin FLOAT_add(FloatBuiltin a, FloatBuiltin b) { throwIfNotMatched(a, b, "FLOAT.add"); - return FloatBuiltin.of(a.bigFloatValue().add(b.bigFloatValue(), a.getMathContext()), a.exponent()); + return FloatBuiltin.of( + a.bigFloatValue().add(b.bigFloatValue(), a.getMathContext()), a.exponent()); } FloatBuiltin FLOAT_sub(FloatBuiltin a, FloatBuiltin b) { throwIfNotMatched(a, b, "FLOAT.sub"); - return FloatBuiltin.of(a.bigFloatValue().subtract(b.bigFloatValue(), a.getMathContext()), a.exponent()); + return FloatBuiltin.of( + a.bigFloatValue().subtract(b.bigFloatValue(), a.getMathContext()), a.exponent()); } FloatBuiltin FLOAT_root(FloatBuiltin a, BigInteger b) { @@ -596,17 +672,26 @@ FloatBuiltin FLOAT_round(FloatBuiltin a, BigInteger prec, BigInteger exp) { throwIfNotUnsignedInt(prec, "FLOAT.round"); throwIfNotUnsignedInt(exp, "FLOAT.round"); if (prec.intValue() < 2 || exp.intValue() < 2) { - throw KEMException.compilerError("Arguments to hook FLOAT.round are too small. Precision and exponent bits must both be at least 2.", loc); + throw KEMException.compilerError( + "Arguments to hook FLOAT.round are too small. Precision and exponent bits must both be at" + + " least 2.", + loc); } - return FloatBuiltin.of(a.bigFloatValue().round(new BinaryMathContext(prec.intValue(), exp.intValue())), exp.intValue()); + return FloatBuiltin.of( + a.bigFloatValue().round(new BinaryMathContext(prec.intValue(), exp.intValue())), + exp.intValue()); } FloatBuiltin FLOAT_floor(FloatBuiltin a) { - return FloatBuiltin.of(a.bigFloatValue().rint(a.getMathContext().withRoundingMode(RoundingMode.FLOOR)), a.exponent()); + return FloatBuiltin.of( + a.bigFloatValue().rint(a.getMathContext().withRoundingMode(RoundingMode.FLOOR)), + a.exponent()); } FloatBuiltin FLOAT_ceil(FloatBuiltin a) { - return FloatBuiltin.of(a.bigFloatValue().rint(a.getMathContext().withRoundingMode(RoundingMode.CEILING)), a.exponent()); + return FloatBuiltin.of( + a.bigFloatValue().rint(a.getMathContext().withRoundingMode(RoundingMode.CEILING)), + a.exponent()); } FloatBuiltin FLOAT_exp(FloatBuiltin a) { @@ -643,24 +728,30 @@ FloatBuiltin FLOAT_atan(FloatBuiltin a) { FloatBuiltin FLOAT_atan2(FloatBuiltin a, FloatBuiltin b) { throwIfNotMatched(a, b, "FLOAT.atan2"); - return FloatBuiltin.of(BigFloat.atan2(a.bigFloatValue(), b.bigFloatValue(), a.getMathContext()), a.exponent()); + return FloatBuiltin.of( + BigFloat.atan2(a.bigFloatValue(), b.bigFloatValue(), a.getMathContext()), a.exponent()); } FloatBuiltin FLOAT_max(FloatBuiltin a, FloatBuiltin b) { throwIfNotMatched(a, b, "FLOAT.max"); - return FloatBuiltin.of(BigFloat.max(a.bigFloatValue(), b.bigFloatValue(), a.getMathContext()), a.exponent()); + return FloatBuiltin.of( + BigFloat.max(a.bigFloatValue(), b.bigFloatValue(), a.getMathContext()), a.exponent()); } FloatBuiltin FLOAT_min(FloatBuiltin a, FloatBuiltin b) { throwIfNotMatched(a, b, "FLOAT.min"); - return FloatBuiltin.of(BigFloat.min(a.bigFloatValue(), b.bigFloatValue(), a.getMathContext()), a.exponent()); + return FloatBuiltin.of( + BigFloat.min(a.bigFloatValue(), b.bigFloatValue(), a.getMathContext()), a.exponent()); } FloatBuiltin FLOAT_maxValue(BigInteger prec, BigInteger exp) { throwIfNotUnsignedInt(prec, "FLOAT.maxValue"); throwIfNotUnsignedInt(exp, "FLOAT.maxValue"); if (prec.intValue() < 2 || exp.intValue() < 2) { - throw KEMException.compilerError("Arguments to hook FLOAT.maxValue are too small. Precision and exponent bits must both be at least 2.", loc); + throw KEMException.compilerError( + "Arguments to hook FLOAT.maxValue are too small. Precision and exponent bits must both be" + + " at least 2.", + loc); } BinaryMathContext mc = new BinaryMathContext(prec.intValue(), exp.intValue()); return FloatBuiltin.of(BigFloat.maxValue(mc.precision, mc.maxExponent), exp.intValue()); @@ -670,7 +761,10 @@ FloatBuiltin FLOAT_minValue(BigInteger prec, BigInteger exp) { throwIfNotUnsignedInt(prec, "FLOAT.minValue"); throwIfNotUnsignedInt(exp, "FLOAT.minValue"); if (prec.intValue() < 2 || exp.intValue() < 2) { - throw KEMException.compilerError("Arguments to hook FLOAT.minValue are too small. Precision and exponent bits must both be at least 2.", loc); + throw KEMException.compilerError( + "Arguments to hook FLOAT.minValue are too small. Precision and exponent bits must both be" + + " at least 2.", + loc); } BinaryMathContext mc = new BinaryMathContext(prec.intValue(), exp.intValue()); return FloatBuiltin.of(BigFloat.minValue(mc.precision, mc.minExponent), exp.intValue()); @@ -704,7 +798,10 @@ FloatBuiltin FLOAT_int2float(BigInteger a, BigInteger prec, BigInteger exp) { throwIfNotUnsignedInt(prec, "FLOAT.int2float"); throwIfNotUnsignedInt(exp, "FLOAT.int2float"); if (prec.intValue() < 2 || exp.intValue() < 2) { - throw KEMException.compilerError("Arguments to hook FLOAT.int2float are too small. Precision and exponent bits must both be at least 2.", loc); + throw KEMException.compilerError( + "Arguments to hook FLOAT.int2float are too small. Precision and exponent bits must both" + + " be at least 2.", + loc); } BinaryMathContext mc = new BinaryMathContext(prec.intValue(), exp.intValue()); return FloatBuiltin.of(new BigFloat(a, mc), exp.intValue()); @@ -714,7 +811,8 @@ BigInteger FLOAT_float2int(FloatBuiltin a) { try { return a.bigFloatValue().rint(a.getMathContext()).toBigIntegerExact(); } catch (ArithmeticException e) { - throw KEMException.compilerError("Argument to hook FLOAT.float2int cannot be rounded to an integer.", e, loc); + throw KEMException.compilerError( + "Argument to hook FLOAT.float2int cannot be rounded to an integer.", e, loc); } } } diff --git a/kernel/src/main/java/org/kframework/compile/ConvertDataStructureToLookup.java b/kernel/src/main/java/org/kframework/compile/ConvertDataStructureToLookup.java index e07f5ec062a..cc769ec2a4d 100644 --- a/kernel/src/main/java/org/kframework/compile/ConvertDataStructureToLookup.java +++ b/kernel/src/main/java/org/kframework/compile/ConvertDataStructureToLookup.java @@ -1,9 +1,24 @@ // Copyright (c) Runtime Verification, Inc. All Rights Reserved. package org.kframework.compile; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + import com.google.common.collect.HashMultiset; import com.google.common.collect.Lists; import com.google.common.collect.Multiset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.kframework.TopologicalSort; import org.kframework.attributes.Att; import org.kframework.builtin.BooleanUtils; @@ -22,616 +37,668 @@ import org.kframework.kore.KVariable; import org.kframework.kore.TransformK; import org.kframework.kore.VisitK; -import org.kframework.compile.RewriteToTop; import org.kframework.utils.errorsystem.KEMException; import scala.Tuple2; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.kframework.Collections.*; -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; - /** - * Convert all operations involving associativity to use the standard data types - * List,Set,Map,Bag. This algorithm is currently incomplete and may not correctly - * handle all cases. However, it should be capable of handling most cases - * that were handleable in the old KIL framework, including multiplicity * cells, - * although there may be some incorrect behaviors because it has not been thoroughly - * tested and there are several known issues that will cause some patterns to behave - * incorrectly. - * In some cases it may also be more inefficient than the old framework because - * it will sometimes choose a backtracking choice operation instead of a set or map - * lookup. + * Convert all operations involving associativity to use the standard data types List,Set,Map,Bag. + * This algorithm is currently incomplete and may not correctly handle all cases. However, it should + * be capable of handling most cases that were handleable in the old KIL framework, including + * multiplicity * cells, although there may be some incorrect behaviors because it has not been + * thoroughly tested and there are several known issues that will cause some patterns to behave + * incorrectly. In some cases it may also be more inefficient than the old framework because it will + * sometimes choose a backtracking choice operation instead of a set or map lookup. * - * In addition to standard maps, a filtered map is also available. A filtered - * map is meant to be used for cases in which structural equality is too strong - * for checking key equality. - * The proposed solution internally encodes a map with entries {@code K |-> V} - * as a "filtered" map whose entries are of the form {@code FK |-> (K, V)}. - * The implementation of the standard map operations (including computing FK) - * is left to the user. For example, - * {@code $RV_MATCH/c-semantics/semantics/cpp14/language/common/map.k} - * defines a filtered map having {@code CPPType} as a key, but filtered - * through the {@code stripType} function. - * This option is selected by using {@code #filterMapChoice} as argument for - * the {@code choice} attribute. Then the {@code filterElement} attribute - * is used to specify the actual map constructor (which takes as arguments - * the filtered key and the pair), while the {@code element} attribute - * specifies the production that will be used to match or construct an - * entry in terms of an unfiltered key and the value. - * Currently for construction this production must be a function, which - * the user must define to rewrite into a filtered entry. - * The compiler translates matching using this production, so in the - * end construction and matching will be transparent to the user and - * allow syntax identical to an ordinary map. + *

In addition to standard maps, a filtered map is also available. A filtered map is meant to be + * used for cases in which structural equality is too strong for checking key equality. The proposed + * solution internally encodes a map with entries {@code K |-> V} as a "filtered" map whose entries + * are of the form {@code FK |-> (K, V)}. The implementation of the standard map operations + * (including computing FK) is left to the user. For example, {@code + * $RV_MATCH/c-semantics/semantics/cpp14/language/common/map.k} defines a filtered map having {@code + * CPPType} as a key, but filtered through the {@code stripType} function. This option is selected + * by using {@code #filterMapChoice} as argument for the {@code choice} attribute. Then the {@code + * filterElement} attribute is used to specify the actual map constructor (which takes as arguments + * the filtered key and the pair), while the {@code element} attribute specifies the production that + * will be used to match or construct an entry in terms of an unfiltered key and the value. + * Currently for construction this production must be a function, which the user must define to + * rewrite into a filtered entry. The compiler translates matching using this production, so in the + * end construction and matching will be transparent to the user and allow syntax identical to an + * ordinary map. */ public class ConvertDataStructureToLookup { - - private final Set state = new HashSet<>(); - private final Multiset vars = HashMultiset.create(); - - void reset() { - state.clear(); - vars.clear(); - counter = 0; - } - - private final Module m; - private final Map collectionFor; - private final boolean matchOnConsList; - - public ConvertDataStructureToLookup(Module m, boolean matchOnConsList) { - this.m = m; - collectionFor = collectionFor(m); - this.matchOnConsList = matchOnConsList; - } - - public ConvertDataStructureToLookup(ConvertDataStructureToLookup copy) { - this.m = copy.m; - this.collectionFor = copy.collectionFor; - this.matchOnConsList = copy.matchOnConsList; + private final Set state = new HashSet<>(); + private final Multiset vars = HashMultiset.create(); + + void reset() { + state.clear(); + vars.clear(); + counter = 0; + } + + private final Module m; + private final Map collectionFor; + private final boolean matchOnConsList; + + public ConvertDataStructureToLookup(Module m, boolean matchOnConsList) { + this.m = m; + collectionFor = collectionFor(m); + this.matchOnConsList = matchOnConsList; + } + + public ConvertDataStructureToLookup(ConvertDataStructureToLookup copy) { + this.m = copy.m; + this.collectionFor = copy.collectionFor; + this.matchOnConsList = copy.matchOnConsList; + } + + public static Map collectionFor(Module m) { + return stream(m.productions()) + .filter(p -> p.att().contains(Att.ASSOC()) && p.att().contains(Att.ELEMENT())) + .flatMap( + p -> { + Set> set = new HashSet<>(); + set.add(Tuple2.apply(p.klabel().get(), p.klabel().get())); + if (p.att().contains(Att.UNIT())) { + set.add(Tuple2.apply(KLabel(p.att().get(Att.UNIT())), p.klabel().get())); + } + if (p.att().contains(Att.ELEMENT())) { + set.add(Tuple2.apply(KLabel(p.att().get(Att.ELEMENT())), p.klabel().get())); + } + if (p.att().contains(Att.FILTER_ELEMENT())) { + set.add(Tuple2.apply(KLabel(p.att().get(Att.FILTER_ELEMENT())), p.klabel().get())); + } + if (p.att().contains(Att.WRAP_ELEMENT())) { + set.add(Tuple2.apply(KLabel(p.att().get(Att.WRAP_ELEMENT())), p.klabel().get())); + } + return set.stream(); + }) + .distinct() + .collect(Collectors.toMap(Tuple2::_1, Tuple2::_2)); + } + + public static Set filteredMapConstructors(Module m) { + return stream(m.productions()) + .filter(p -> p.att().contains(Att.ASSOC()) && p.att().contains(Att.FILTER_ELEMENT())) + .map(p -> p.klabel().get()) + .distinct() + .collect(Collectors.toSet()); + } + + private Rule convert(Rule rule) { + reset(); + gatherVars(rule.body(), vars); + gatherVars(rule.requires(), vars); + gatherVars(rule.ensures(), vars); + K body = transform(rule.body()); + return Rule(body, addSideCondition(rule.requires()), rule.ensures(), rule.att()); + } + + private Context convert(Context context) { + reset(); + gatherVars(context.body(), vars); + gatherVars(context.requires(), vars); + K body = transform(context.body()); + return new Context(body, addSideCondition(context.requires()), context.att()); + } + + /** Collects all the variables in {@code term} into {@code vars}. */ + void gatherVars(K term, Multiset vars) { + new VisitK() { + @Override + public void apply(KVariable v) { + vars.add(v); + super.apply(v); + } + }.apply(term); + } + + /** + * Adds lookups to the side condition in sorted order in which they must be performed. Lookups are + * sorted based on dependencies between each other, but non-lookup operations appear in no + * particular order with respect to the lookups. + * + * @param requires Previous side condition, if any. + * @return Side condition generated by this compiler pass + previous side condition. + */ + K addSideCondition(K requires) { + Optional sideCondition = getSortedLookups().reduce(BooleanUtils::and); + if (!sideCondition.isPresent()) { + return requires; + } else if (requires.equals(BooleanUtils.TRUE) && sideCondition.isPresent()) { + return sideCondition.get(); + } else { + // we order lookups before the requires clause so that the fresh constant + // matching side condition remains last. This is necessary in order to + // ensure that fresh constants in rule RHSs are consecutive + return BooleanUtils.and(sideCondition.get(), requires); } - - public static Map collectionFor(Module m) { - return stream(m.productions()) - .filter(p -> p.att().contains(Att.ASSOC()) && p.att().contains(Att.ELEMENT())) - .flatMap(p -> { - Set> set = new HashSet<>(); - set.add(Tuple2.apply(p.klabel().get(), p.klabel().get())); - if (p.att().contains(Att.UNIT())) { - set.add(Tuple2.apply(KLabel(p.att().get(Att.UNIT())), p.klabel().get())); - } - if (p.att().contains(Att.ELEMENT())) { - set.add(Tuple2.apply(KLabel(p.att().get(Att.ELEMENT())), p.klabel().get())); - } - if (p.att().contains(Att.FILTER_ELEMENT())) { - set.add(Tuple2.apply(KLabel(p.att().get(Att.FILTER_ELEMENT())), p.klabel().get())); - } - if (p.att().contains(Att.WRAP_ELEMENT())) { - set.add(Tuple2.apply(KLabel(p.att().get(Att.WRAP_ELEMENT())), p.klabel().get())); - } - return set.stream(); - }).distinct().collect(Collectors.toMap(Tuple2::_1, Tuple2::_2)); - } - - public static Set filteredMapConstructors(Module m) { - return stream(m.productions()) - .filter(p -> p.att().contains(Att.ASSOC()) && p.att().contains(Att.FILTER_ELEMENT())) - .map(p -> p.klabel().get()) - .distinct() - .collect(Collectors.toSet()); - } - - private Rule convert(Rule rule) { - reset(); - gatherVars(rule.body(), vars); - gatherVars(rule.requires(), vars); - gatherVars(rule.ensures(), vars); - K body = transform(rule.body()); - return Rule( - body, - addSideCondition(rule.requires()), - rule.ensures(), - rule.att()); - } - - private Context convert(Context context) { - reset(); - gatherVars(context.body(), vars); - gatherVars(context.requires(), vars); - K body = transform(context.body()); - return new Context( - body, - addSideCondition(context.requires()), - context.att()); - } - - - /** - * Collects all the variables in {@code term} into {@code vars}. - */ - void gatherVars(K term, Multiset vars) { - new VisitK() { - @Override - public void apply(KVariable v) { - vars.add(v); - super.apply(v); + } + + /** + * Sorts lookups based on their dependencies with each other. Non-lookups (i.e. everything except + * #match, #setChoice, and #mapChoice) are in no particular order in this ordering, since they can + * always be inferred later to occur at the final step after all other variables are bound. + * + * @return + */ + private Stream getSortedLookups() { + List> edges = new ArrayList<>(); + for (KApply k1 : state) { + Multiset rhsVars = HashMultiset.create(); + if (k1.klabel().name().equals("Set:in")) { + continue; + } + gatherVars(k1.klist().items().get(1), rhsVars); + for (KApply k2 : state) { + Multiset lhsVars = HashMultiset.create(); + if (k2.klabel().name().equals("Set:in")) { + continue; + } + gatherVars(k2.klist().items().get(0), lhsVars); + for (KVariable var : rhsVars) { + if (lhsVars.contains(var)) { + if (k1 != k2) { + edges.add(Tuple2.apply(k2, k1)); + break; } - }.apply(term); - } - - /** - * Adds lookups to the side condition in sorted order in which they must be performed. - * Lookups are sorted based on dependencies between each other, - * but non-lookup operations appear in no particular order with respect to the lookups. - * @param requires Previous side condition, if any. - * @return Side condition generated by this compiler pass + previous side condition. - */ - K addSideCondition(K requires) { - Optional sideCondition = getSortedLookups().reduce(BooleanUtils::and); - if (!sideCondition.isPresent()) { - return requires; - } else if (requires.equals(BooleanUtils.TRUE) && sideCondition.isPresent()) { - return sideCondition.get(); - } else { - // we order lookups before the requires clause so that the fresh constant - // matching side condition remains last. This is necessary in order to - // ensure that fresh constants in rule RHSs are consecutive - return BooleanUtils.and(sideCondition.get(), requires); + } } + } } - - /** - * Sorts lookups based on their dependencies with each other. Non-lookups (i.e. - * everything except #match, #setChoice, and #mapChoice) are in no particular - * order in this ordering, since they can always be inferred later to occur - * at the final step after all other variables are bound. - * @return - */ - private Stream getSortedLookups() { - List> edges = new ArrayList<>(); - for (KApply k1 : state) { - Multiset rhsVars = HashMultiset.create(); - if (k1.klabel().name().equals("Set:in")) { - continue; + List topologicalSorted = mutable(TopologicalSort.tsort(immutable(edges)).toList()); + return state.stream() + .sorted((k1, k2) -> (topologicalSorted.indexOf(k1) - topologicalSorted.indexOf(k2))); + } + + private int counter = 0; + + KVariable newDotVariable() { + KVariable newLabel; + do { + newLabel = KVariable("_Gen" + (counter++)); + } while (vars.contains(newLabel)); + vars.add(newLabel); + return newLabel; + } + + /** + * For the cell bag sorts with multiplicity *, add the single-element wrapper around individual + * cells. + */ + private K infer(K term) { + return new TransformK() { + @Override + public K apply(KApply k) { + for (KLabel collectionLabel : collectionFor.keySet()) { + Optional wrapElement = + m.attributesFor().apply(collectionLabel).getOptional(Att.WRAP_ELEMENT()); + if (wrapElement.isPresent()) { + KLabel wrappedLabel = KLabel(wrapElement.get()); + KLabel elementLabel = + KLabel(m.attributesFor().apply(collectionLabel).get(Att.ELEMENT())); + if (k.klabel().equals(elementLabel)) { + return k; } - gatherVars(k1.klist().items().get(1), rhsVars); - for (KApply k2 : state) { - Multiset lhsVars = HashMultiset.create(); - if (k2.klabel().name().equals("Set:in")) { - continue; - } - gatherVars(k2.klist().items().get(0), lhsVars); - for (KVariable var : rhsVars) { - if (lhsVars.contains(var)) { - if (k1 != k2) { - edges.add(Tuple2.apply(k2, k1)); - break; - } - } - } + if (k.klabel().equals(wrappedLabel)) { + if (collectionIsMap(collectionLabel)) { + // Map + return KApply(elementLabel, super.apply(k.klist().items().get(0)), super.apply(k)); + } else { + return KApply(elementLabel, super.apply(k)); + } } + } } - List topologicalSorted = mutable(TopologicalSort.tsort(immutable(edges)).toList()); - return state.stream().sorted((k1, k2) -> (topologicalSorted.indexOf(k1) - topologicalSorted.indexOf(k2))); - } - - private int counter = 0; - KVariable newDotVariable() { - KVariable newLabel; - do { - newLabel = KVariable("_Gen" + (counter++)); - } while (vars.contains(newLabel)); - vars.add(newLabel); - return newLabel; + return super.apply(k); + } + }.apply(term); + } + + public boolean collectionIsMap(KLabel collectionLabel) { + return m.attributesFor().apply(collectionLabel).contains(Att.COMM()) + && !m.attributesFor().apply(collectionLabel).contains(Att.IDEM()) + && !m.attributesFor().apply(collectionLabel).contains(Att.BAG()); + } + + private boolean isThread = false; + + private K transform(K body) { + if (body instanceof KRewrite + && ((KRewrite) body).left() instanceof KApply + && collectionFor.containsKey(((KApply) ((KRewrite) body).left()).klabel())) { + // this is a rule intended to implement one of the collection operations; do not transform it. + return body; } + // maintain the list of variables in the term so that we can deduce that a particular variable + // is unconstrained + Multiset varConstraints = HashMultiset.create(); + gatherVars(RewriteToTop.toLeft(body), varConstraints); + return new TransformK() { + public K apply(KApply k) { + if (KLabels.KSEQ.equals(k.klabel())) return super.apply(k); + if (k.klabel().name().equals("#Thread")) { + K global = apply(k.klist().items().get(0)); + K id = apply(k.klist().items().get(1)); + isThread = true; + K thread = apply(k.klist().items().get(2)); + isThread = false; + K otherThreads = apply(k.klist().items().get(3)); + return KApply(k.klabel(), global, id, thread, otherThreads); + } - /** - * For the cell bag sorts with multiplicity *, add the single-element wrapper around individual cells. - */ - private K infer(K term) { - return new TransformK() { - @Override - public K apply(KApply k) { - for (KLabel collectionLabel : collectionFor.keySet()) { - Optional wrapElement = m.attributesFor().apply(collectionLabel).getOptional(Att.WRAP_ELEMENT()); - if (wrapElement.isPresent()) { - KLabel wrappedLabel = KLabel(wrapElement.get()); - KLabel elementLabel = KLabel(m.attributesFor().apply(collectionLabel).get(Att.ELEMENT())); - if (k.klabel().equals(elementLabel)) { - return k; - } - if (k.klabel().equals(wrappedLabel)) { - if (collectionIsMap(collectionLabel)) { - // Map - return KApply(elementLabel, super.apply(k.klist().items().get(0)), super.apply(k)); - } else { - return KApply(elementLabel, super.apply(k)); - } - } - } - } - return super.apply(k); + if (collectionFor.containsKey(k.klabel())) { + if (isThread) { + isThread = false; + K res = super.apply(k); + isThread = true; + return res; + } else { + KLabel collectionLabel = collectionFor.get(k.klabel()); + Att att = m.attributesFor().apply(collectionLabel); + // assumed assoc + KApply left = (KApply) RewriteToTop.toLeft(k); + List components = Assoc.flatten(collectionLabel, Collections.singletonList(left), m); + if (att.contains(Att.COMM())) { + if (att.contains(Att.IDEM())) { + // Set + return convertSet(k, collectionLabel, components); + } else { + if (att.contains(Att.BAG())) + // Bag + // TODO(dwightguth): handle bags + return super.apply(k); + else + // Map + return convertMap(k, collectionLabel, components, varConstraints); + } + } else { + // List + return convertList(k, collectionLabel, components); } - }.apply(term); - } - - public boolean collectionIsMap(KLabel collectionLabel) { - return m.attributesFor().apply(collectionLabel).contains(Att.COMM()) - && !m.attributesFor().apply(collectionLabel).contains(Att.IDEM()) - && !m.attributesFor().apply(collectionLabel).contains(Att.BAG()); - } - - private boolean isThread = false; - - private K transform(K body) { - if (body instanceof KRewrite && ((KRewrite) body).left() instanceof KApply && collectionFor.containsKey(((KApply) ((KRewrite) body).left()).klabel())) { - // this is a rule intended to implement one of the collection operations; do not transform it. - return body; + } + } else { + return super.apply(k); } - //maintain the list of variables in the term so that we can deduce that a particular variable is unconstrained - Multiset varConstraints = HashMultiset.create(); - gatherVars(RewriteToTop.toLeft(body), varConstraints); - return new TransformK() { - public K apply(KApply k) { - if (KLabels.KSEQ.equals(k.klabel())) - return super.apply(k); - if (k.klabel().name().equals("#Thread")) { - K global = apply(k.klist().items().get(0)); - K id = apply(k.klist().items().get(1)); - isThread = true; - K thread = apply(k.klist().items().get(2)); - isThread = false; - K otherThreads = apply(k.klist().items().get(3)); - return KApply(k.klabel(), global, id, thread, otherThreads); - } - - if (collectionFor.containsKey(k.klabel())) { - if (isThread) { - isThread = false; - K res = super.apply(k); - isThread = true; - return res; - } else { - KLabel collectionLabel = collectionFor.get(k.klabel()); - Att att = m.attributesFor().apply(collectionLabel); - //assumed assoc - KApply left = (KApply) RewriteToTop.toLeft(k); - List components = Assoc.flatten(collectionLabel, Collections.singletonList(left), m); - if (att.contains(Att.COMM())) { - if (att.contains(Att.IDEM())) { - // Set - return convertSet(k, collectionLabel, components); - } else { - if (att.contains(Att.BAG())) - // Bag - // TODO(dwightguth): handle bags - return super.apply(k); - else - // Map - return convertMap(k, collectionLabel, components, varConstraints); - } - } else { - // List - return convertList(k, collectionLabel, components); - } - } - } else { - return super.apply(k); + } + + /** + * Convert a list pattern, requiring that there is at most one list variable component. + * Individual items may appear before and after the frame variable, which can be translated + * into efficient operatations at the beginning and end of the list. + */ + private K convertList(KApply k, KLabel collectionLabel, List components) { + if (rhsOf == null) { + // left hand side + KVariable frame = null; + List elementsLeft = new ArrayList(); + List elementsRight = new ArrayList(); + KLabel elementLabel = KLabel(m.attributesFor().apply(collectionLabel).get(Att.ELEMENT())); + boolean isRight = false; // true for components later than the frame variable. + // build the components of the list from the flattened KApply. + for (K component : components) { + if (component instanceof KVariable) { + if (frame != null) { + throw KEMException.internalError( + "Unsupported associative matching on List. Found variables " + + component + + " and " + + frame, + k); + } + frame = (KVariable) component; + isRight = true; + } else if (component instanceof KApply kapp) { + boolean needsWrapper = false; + if (kapp.klabel().equals(elementLabel) + || (needsWrapper = kapp.klabel().equals(getWrapElement(collectionLabel)))) { + if (kapp.klist().size() != 1 && !needsWrapper) { + throw KEMException.internalError( + "Unexpected arity of list element: " + kapp.klist().size(), kapp); } + K stack = lhsOf; + // setting lhsOf prevents inner lists from being translated to rewrites, + lhsOf = kapp; + + // overloading means the following two apply functions are actually different + // methods + (isRight ? elementsRight : elementsLeft) + .add( + needsWrapper + ? super.apply(kapp) + : super.apply(kapp.klist().items().get(0))); + + lhsOf = stack; + } else { + throw KEMException.internalError( + "Unexpected term in list, not a list element.", kapp); + } } - - /** - * Convert a list pattern, requiring that there is at most one list variable component. - * Individual items may appear before and after the frame variable, which can be - * translated into efficient operatations at the beginning and end of the list. - */ - private K convertList(KApply k, KLabel collectionLabel, List components) { - if (rhsOf == null) { - //left hand side - KVariable frame = null; - List elementsLeft = new ArrayList(); - List elementsRight = new ArrayList(); - KLabel elementLabel = KLabel(m.attributesFor().apply(collectionLabel).get(Att.ELEMENT())); - boolean isRight = false; // true for components later than the frame variable. - //build the components of the list from the flattened KApply. - for (K component : components) { - if (component instanceof KVariable) { - if (frame != null) { - throw KEMException.internalError("Unsupported associative matching on List. Found variables " + component + " and " + frame, k); - } - frame = (KVariable) component; - isRight = true; - } else if (component instanceof KApply kapp) { - boolean needsWrapper = false; - if (kapp.klabel().equals(elementLabel) - || (needsWrapper = kapp.klabel().equals(getWrapElement(collectionLabel)))) { - if (kapp.klist().size() != 1 && !needsWrapper) { - throw KEMException.internalError("Unexpected arity of list element: " + kapp.klist().size(), kapp); - } - K stack = lhsOf; - // setting lhsOf prevents inner lists from being translated to rewrites, - lhsOf = kapp; - - // overloading means the following two apply functions are actually different methods - (isRight ? elementsRight : elementsLeft).add(needsWrapper ? super.apply(kapp) : super.apply(kapp.klist().items().get(0))); - - lhsOf = stack; - } else { - throw KEMException.internalError("Unexpected term in list, not a list element.", kapp); - } - } - } - K list; - if (elementsRight.size() == 0 && matchOnConsList) { - K tail; - if (frame == null) { - tail = KApply(KLabel(m.attributesFor().apply(collectionLabel).get(Att.UNIT()))); - } else { - tail = frame; - } - list = Lists.reverse(elementsLeft).stream().map(e -> (K)KApply(elementLabel, e)).reduce(tail, (res, el) -> KApply(collectionLabel, el, res)); - } else { - list = newDotVariable(); - // Ctx[ListItem(5) Frame ListItem(X) ListItem(foo(Y))] => Ctx [L] - // requires Frame := range(L, 1, 2) - // andBool 5 := L[0] - // andBool X := L[-2] - // andBool foo(Y) := L[-1] - if (frame != null) { - state.add(KApply(KLabel("#match"), frame, KApply(KLabel("List:range"), list, - KToken(Integer.toString(elementsLeft.size()), Sorts.Int()), - KToken(Integer.toString(elementsRight.size()), Sorts.Int())))); - } else { - KLabel unit = KLabel(m.attributesFor().apply(collectionLabel).get(Att.UNIT())); - // Ctx[.List] => Ctx[L] requires L ==K range(L, 0, 0) - state.add(KApply(KLabel("_==K_"), KApply(unit), KApply(KLabel("List:range"), list, - KToken(Integer.toString(elementsLeft.size()), Sorts.Int()), - KToken(Integer.toString(elementsRight.size()), Sorts.Int())))); - } - for (int i = 0; i < elementsLeft.size(); i++) { - K element = elementsLeft.get(i); - state.add(KApply(KLabel("#match"), element, KApply(KLabel("List:get"), list, KToken(Integer.toString(i), Sorts.Int())))); - } - for (int i = 0; i < elementsRight.size(); i++) { - K element = elementsRight.get(i); - state.add(KApply(KLabel("#match"), element, KApply(KLabel("List:get"), list, KToken(Integer.toString(i - elementsRight.size()), Sorts.Int())))); - } - } - if (lhsOf == null) { - if (!hasRewrite(k)) { - return list; - } - // An outermost list may contain nested rewrites, so the term - // is translated into a rewrite from compiled match into the original right-hand side. - return KRewrite(list, infer(RewriteToTop.toRight(k))); - } else { - return list; - } - } else { - return infer(super.apply(k)); - } + } + K list; + if (elementsRight.size() == 0 && matchOnConsList) { + K tail; + if (frame == null) { + tail = KApply(KLabel(m.attributesFor().apply(collectionLabel).get(Att.UNIT()))); + } else { + tail = frame; } - - - /** - * Convert a map pattern, requiring that there is at most one map variable component. - * Map keys must either be a variable, or bound elsewhere in the rule. - * Map value patterns become additional matching constraints on lookups in the map. - */ - private K convertMap(KApply k, KLabel collectionLabel, List components, Multiset varConstraints) { - if (rhsOf == null) { - //left hand side - KVariable frame = null; - Map elements = new LinkedHashMap<>(); - //build the components of the map from the flattened KApply. - for (K component : components) { - if (component instanceof KVariable) { - if (frame != null) { - throw KEMException.internalError("Unsupported associative matching on Map. Found variables " + component + " and " + frame, k); - } - frame = (KVariable) component; - } else if (component instanceof KApply kapp) { - - boolean needsWrapper = false; - if (kapp.klabel().equals(KLabel(m.attributesFor().apply(collectionLabel).get(Att.ELEMENT()))) - || (needsWrapper = kapp.klabel().equals(getWrapElement(collectionLabel)))) { - if (kapp.klist().size() != 2 && !needsWrapper) { - throw KEMException.internalError("Unexpected arity of map element: " + kapp.klist().size(), kapp); - } - K stack = lhsOf; - // setting lhsOf prevents inner lists from being translated to rewrites, - lhsOf = kapp; - - elements.put(super.apply(kapp.klist().items().get(0)), - needsWrapper ? super.apply(kapp) : super.apply(kapp.klist().items().get(1))); - - lhsOf = stack; - } else { - throw KEMException.internalError("Unexpected term in map, not a map element.", kapp); - } - } - } - KVariable map = newDotVariable(); - // K1,Ctx[K1 |-> K2 K3] => K1,Ctx[M] requires K3 := M[K1<-undef] andBool K1 := choice(M) andBool K2 := M[K1] - KLabel remove = KLabel(m.attributesFor().apply(collectionLabel).getOptional(Att.REMOVE()).orElse("_[_<-undef]")); - if (frame != null) { - state.add(KApply(KLabel("#match"), frame, elements.keySet().stream().reduce(map, (a1, a2) -> KApply(remove, a1, a2)))); - } else { - KLabel unit = KLabel(m.attributesFor().apply(collectionLabel).get(Att.UNIT())); - state.add(KApply(KLabel("_==K_"), KApply(unit), elements.keySet().stream().reduce(map, (a1, a2) -> KApply(remove, a1, a2)))); - } - for (Map.Entry element : elements.entrySet()) { - // TODO(dwightguth): choose better between lookup and choice. - if (element.getKey() instanceof KVariable && varConstraints.count(element.getKey()) == 1) { - state.add(KApply(KLabel("#mapChoice"), element.getKey(), map)); - } - state.add(KApply(KLabel("#match"), element.getValue(), KApply(KLabel("Map:lookup"), map, element.getKey()))); - } - if (lhsOf == null) { - if (!hasRewrite(k)) { - return map; - } - // An outermost map may contain nested rewrites, so the term - // is translated into a rewrite from compiled match into the original right-hand side. - return KRewrite(map, infer(RewriteToTop.toRight(k))); - } else { - return map; - } - } else { - return infer(super.apply(k)); - } + list = + Lists.reverse(elementsLeft).stream() + .map(e -> (K) KApply(elementLabel, e)) + .reduce(tail, (res, el) -> KApply(collectionLabel, el, res)); + } else { + list = newDotVariable(); + // Ctx[ListItem(5) Frame ListItem(X) ListItem(foo(Y))] => Ctx [L] + // requires Frame := range(L, 1, 2) + // andBool 5 := L[0] + // andBool X := L[-2] + // andBool foo(Y) := L[-1] + if (frame != null) { + state.add( + KApply( + KLabel("#match"), + frame, + KApply( + KLabel("List:range"), + list, + KToken(Integer.toString(elementsLeft.size()), Sorts.Int()), + KToken(Integer.toString(elementsRight.size()), Sorts.Int())))); + } else { + KLabel unit = KLabel(m.attributesFor().apply(collectionLabel).get(Att.UNIT())); + // Ctx[.List] => Ctx[L] requires L ==K range(L, 0, 0) + state.add( + KApply( + KLabel("_==K_"), + KApply(unit), + KApply( + KLabel("List:range"), + list, + KToken(Integer.toString(elementsLeft.size()), Sorts.Int()), + KToken(Integer.toString(elementsRight.size()), Sorts.Int())))); } - - private KLabel getWrapElement(KLabel collectionLabel) { - return KLabel(m.attributesFor().apply(collectionLabel).get(Att.WRAP_ELEMENT())); + for (int i = 0; i < elementsLeft.size(); i++) { + K element = elementsLeft.get(i); + state.add( + KApply( + KLabel("#match"), + element, + KApply(KLabel("List:get"), list, KToken(Integer.toString(i), Sorts.Int())))); } - - - /** - * Convert a set pattern, requiring that there is at most one set variable component. - * Set elements without variables become membership checks in the map, whereas Set elements - * with variables trigger iteration over the set with matching on each element. - */ - private K convertSet(KApply k, KLabel collectionLabel, List components) { - if (rhsOf == null) { - //left hand side - KVariable frame = null; - Set elements = new LinkedHashSet<>(); - KLabel elementLabel = KLabel(m.attributesFor().apply(collectionLabel).get(Att.ELEMENT())); - //build the components of the set from the flattened KApply. - for (K component : components) { - if (component instanceof KVariable) { - if (frame != null) { - throw KEMException.internalError("Unsupported associative matching on Set. Found variables " + component + " and " + frame, k); - } - frame = (KVariable) component; - } else if (component instanceof KApply kapp) { - - boolean needsWrapper = false; - if (kapp.klabel().equals(elementLabel) - || (needsWrapper = kapp.klabel().equals(getWrapElement(collectionLabel)))) { - if (kapp.klist().size() != 1 && !needsWrapper) { - throw KEMException.internalError("Unexpected arity of set element: " + kapp.klist().size(), kapp); - } - K stack = lhsOf; - // setting lhsOf prevents inner lists from being translated to rewrites, - lhsOf = kapp; - - // overloading means the following two apply functions are actually different methods - elements.add(needsWrapper ? super.apply(kapp) : super.apply(kapp.klist().items().get(0))); - - lhsOf = stack; - } else { - throw KEMException.internalError("Unexpected term in set, not a set element.", kapp); - } - } - } - KVariable set = newDotVariable(); - K accum = set; - // Ctx[SetItem(K1) K2] => Ctx[S] requires K1 := choice(S) andBool K2 := S -Set SetItem(K1) - // Ctx[SetItem(5) SetItem(6) K] => Ctx[S] requires 5 in S andBool 6 in S andBool K := S -Set SetItem(5) SetItem(6) - for (K element : elements) { - // TODO(dwightguth): choose better between lookup and choice. - Multiset vars = HashMultiset.create(); - gatherVars(element, vars); - if (vars.isEmpty()) { - state.add(KApply(KLabel("Set:in"), element, accum)); - } else { - //set choice - state.add(KApply(KLabel("#setChoice"), element, accum)); - } - accum = KApply(KLabel("Set:difference"), accum, KApply(elementLabel, element)); - } - KLabel unit = KLabel(m.attributesFor().apply(collectionLabel).get(Att.UNIT())); - if (frame != null) { - state.add(KApply(KLabel("#match"), frame, accum)); - } else { - state.add(KApply(KLabel("_==K_"), KApply(unit), accum)); - } - if (lhsOf == null) { - if (!hasRewrite(k)) { - return set; - } - // An outermost set may contain nested rewrites, so the term - // is translated into a rewrite from compiled match into the original right-hand side. - return KRewrite(set, infer(RewriteToTop.toRight(k))); - } else { - return set; - } - } else { - return infer(super.apply(k)); - } + for (int i = 0; i < elementsRight.size(); i++) { + K element = elementsRight.get(i); + state.add( + KApply( + KLabel("#match"), + element, + KApply( + KLabel("List:get"), + list, + KToken(Integer.toString(i - elementsRight.size()), Sorts.Int())))); } - - private K lhsOf; - private K rhsOf; - - @Override - public K apply(KRewrite k) { - lhsOf = k; - K l = apply(k.left()); - lhsOf = null; - rhsOf = k; - K r = apply(k.right()); - rhsOf = null; - if (l != k.left() || r != k.right()) { - return KRewrite(l, r, k.att()); - } else { - return k; + } + if (lhsOf == null) { + if (!hasRewrite(k)) { + return list; + } + // An outermost list may contain nested rewrites, so the term + // is translated into a rewrite from compiled match into the original right-hand side. + return KRewrite(list, infer(RewriteToTop.toRight(k))); + } else { + return list; + } + } else { + return infer(super.apply(k)); + } + } + + /** + * Convert a map pattern, requiring that there is at most one map variable component. Map keys + * must either be a variable, or bound elsewhere in the rule. Map value patterns become + * additional matching constraints on lookups in the map. + */ + private K convertMap( + KApply k, + KLabel collectionLabel, + List components, + Multiset varConstraints) { + if (rhsOf == null) { + // left hand side + KVariable frame = null; + Map elements = new LinkedHashMap<>(); + // build the components of the map from the flattened KApply. + for (K component : components) { + if (component instanceof KVariable) { + if (frame != null) { + throw KEMException.internalError( + "Unsupported associative matching on Map. Found variables " + + component + + " and " + + frame, + k); + } + frame = (KVariable) component; + } else if (component instanceof KApply kapp) { + + boolean needsWrapper = false; + if (kapp.klabel() + .equals(KLabel(m.attributesFor().apply(collectionLabel).get(Att.ELEMENT()))) + || (needsWrapper = kapp.klabel().equals(getWrapElement(collectionLabel)))) { + if (kapp.klist().size() != 2 && !needsWrapper) { + throw KEMException.internalError( + "Unexpected arity of map element: " + kapp.klist().size(), kapp); } + K stack = lhsOf; + // setting lhsOf prevents inner lists from being translated to rewrites, + lhsOf = kapp; + + elements.put( + super.apply(kapp.klist().items().get(0)), + needsWrapper ? super.apply(kapp) : super.apply(kapp.klist().items().get(1))); + + lhsOf = stack; + } else { + throw KEMException.internalError( + "Unexpected term in map, not a map element.", kapp); + } } - }.apply(body); - } - - private Boolean hasRewrite(KApply k) { - return new FoldK() { - public Boolean unit() { - return false; + } + KVariable map = newDotVariable(); + // K1,Ctx[K1 |-> K2 K3] => K1,Ctx[M] requires K3 := M[K1<-undef] andBool K1 := choice(M) + // andBool K2 := M[K1] + KLabel remove = + KLabel( + m.attributesFor() + .apply(collectionLabel) + .getOptional(Att.REMOVE()) + .orElse("_[_<-undef]")); + if (frame != null) { + state.add( + KApply( + KLabel("#match"), + frame, + elements.keySet().stream().reduce(map, (a1, a2) -> KApply(remove, a1, a2)))); + } else { + KLabel unit = KLabel(m.attributesFor().apply(collectionLabel).get(Att.UNIT())); + state.add( + KApply( + KLabel("_==K_"), + KApply(unit), + elements.keySet().stream().reduce(map, (a1, a2) -> KApply(remove, a1, a2)))); + } + for (Map.Entry element : elements.entrySet()) { + // TODO(dwightguth): choose better between lookup and choice. + if (element.getKey() instanceof KVariable + && varConstraints.count(element.getKey()) == 1) { + state.add(KApply(KLabel("#mapChoice"), element.getKey(), map)); } - - public Boolean merge(Boolean a, Boolean b) { - return a || b; + state.add( + KApply( + KLabel("#match"), + element.getValue(), + KApply(KLabel("Map:lookup"), map, element.getKey()))); + } + if (lhsOf == null) { + if (!hasRewrite(k)) { + return map; } - - public Boolean apply(KRewrite rew) { - return true; + // An outermost map may contain nested rewrites, so the term + // is translated into a rewrite from compiled match into the original right-hand side. + return KRewrite(map, infer(RewriteToTop.toRight(k))); + } else { + return map; + } + } else { + return infer(super.apply(k)); + } + } + + private KLabel getWrapElement(KLabel collectionLabel) { + return KLabel(m.attributesFor().apply(collectionLabel).get(Att.WRAP_ELEMENT())); + } + + /** + * Convert a set pattern, requiring that there is at most one set variable component. Set + * elements without variables become membership checks in the map, whereas Set elements with + * variables trigger iteration over the set with matching on each element. + */ + private K convertSet(KApply k, KLabel collectionLabel, List components) { + if (rhsOf == null) { + // left hand side + KVariable frame = null; + Set elements = new LinkedHashSet<>(); + KLabel elementLabel = KLabel(m.attributesFor().apply(collectionLabel).get(Att.ELEMENT())); + // build the components of the set from the flattened KApply. + for (K component : components) { + if (component instanceof KVariable) { + if (frame != null) { + throw KEMException.internalError( + "Unsupported associative matching on Set. Found variables " + + component + + " and " + + frame, + k); + } + frame = (KVariable) component; + } else if (component instanceof KApply kapp) { + + boolean needsWrapper = false; + if (kapp.klabel().equals(elementLabel) + || (needsWrapper = kapp.klabel().equals(getWrapElement(collectionLabel)))) { + if (kapp.klist().size() != 1 && !needsWrapper) { + throw KEMException.internalError( + "Unexpected arity of set element: " + kapp.klist().size(), kapp); + } + K stack = lhsOf; + // setting lhsOf prevents inner lists from being translated to rewrites, + lhsOf = kapp; + + // overloading means the following two apply functions are actually different + // methods + elements.add( + needsWrapper ? super.apply(kapp) : super.apply(kapp.klist().items().get(0))); + + lhsOf = stack; + } else { + throw KEMException.internalError( + "Unexpected term in set, not a set element.", kapp); + } } - }.apply(k); - } - - - public Sentence convert(Sentence s) { - if (s.att().contains(Att.SMT_LEMMA()) - || s.att().contains(Att.PATTERN_FOLDING())) { - return s; - } else if (s instanceof Rule) { - return convert((Rule) s); - } else if (s instanceof Context) { - return convert((Context) s); + } + KVariable set = newDotVariable(); + K accum = set; + // Ctx[SetItem(K1) K2] => Ctx[S] requires K1 := choice(S) andBool K2 := S -Set SetItem(K1) + // Ctx[SetItem(5) SetItem(6) K] => Ctx[S] requires 5 in S andBool 6 in S andBool K := S + // -Set SetItem(5) SetItem(6) + for (K element : elements) { + // TODO(dwightguth): choose better between lookup and choice. + Multiset vars = HashMultiset.create(); + gatherVars(element, vars); + if (vars.isEmpty()) { + state.add(KApply(KLabel("Set:in"), element, accum)); + } else { + // set choice + state.add(KApply(KLabel("#setChoice"), element, accum)); + } + accum = KApply(KLabel("Set:difference"), accum, KApply(elementLabel, element)); + } + KLabel unit = KLabel(m.attributesFor().apply(collectionLabel).get(Att.UNIT())); + if (frame != null) { + state.add(KApply(KLabel("#match"), frame, accum)); + } else { + state.add(KApply(KLabel("_==K_"), KApply(unit), accum)); + } + if (lhsOf == null) { + if (!hasRewrite(k)) { + return set; + } + // An outermost set may contain nested rewrites, so the term + // is translated into a rewrite from compiled match into the original right-hand side. + return KRewrite(set, infer(RewriteToTop.toRight(k))); + } else { + return set; + } } else { - return s; + return infer(super.apply(k)); } + } + + private K lhsOf; + private K rhsOf; + + @Override + public K apply(KRewrite k) { + lhsOf = k; + K l = apply(k.left()); + lhsOf = null; + rhsOf = k; + K r = apply(k.right()); + rhsOf = null; + if (l != k.left() || r != k.right()) { + return KRewrite(l, r, k.att()); + } else { + return k; + } + } + }.apply(body); + } + + private Boolean hasRewrite(KApply k) { + return new FoldK() { + public Boolean unit() { + return false; + } + + public Boolean merge(Boolean a, Boolean b) { + return a || b; + } + + public Boolean apply(KRewrite rew) { + return true; + } + }.apply(k); + } + + public Sentence convert(Sentence s) { + if (s.att().contains(Att.SMT_LEMMA()) || s.att().contains(Att.PATTERN_FOLDING())) { + return s; + } else if (s instanceof Rule) { + return convert((Rule) s); + } else if (s instanceof Context) { + return convert((Context) s); + } else { + return s; } - - public static boolean isLookupKLabel(KLabel k) { - return k.name().equals("#match") || k.name().equals("#mapChoice") || k.name().equals("#filterMapChoice") || k.name().equals("#setChoice"); - } - - public static boolean isLookupKLabel(KApply k) { - return isLookupKLabel(k.klabel()); - } - + } + + public static boolean isLookupKLabel(KLabel k) { + return k.name().equals("#match") + || k.name().equals("#mapChoice") + || k.name().equals("#filterMapChoice") + || k.name().equals("#setChoice"); + } + + public static boolean isLookupKLabel(KApply k) { + return isLookupKLabel(k.klabel()); + } } diff --git a/kernel/src/main/java/org/kframework/compile/DeconstructIntegerAndFloatLiterals.java b/kernel/src/main/java/org/kframework/compile/DeconstructIntegerAndFloatLiterals.java index d66152214a0..d2206735f0d 100644 --- a/kernel/src/main/java/org/kframework/compile/DeconstructIntegerAndFloatLiterals.java +++ b/kernel/src/main/java/org/kframework/compile/DeconstructIntegerAndFloatLiterals.java @@ -1,6 +1,12 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; import org.kframework.builtin.BooleanUtils; import org.kframework.builtin.Sorts; import org.kframework.definition.Context; @@ -8,163 +14,152 @@ import org.kframework.definition.Sentence; import org.kframework.kore.*; -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; - -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; - /** - * Transforms patterns in the LHS of rules which have tokens of sort Int or Float - * into side conditions generating equality over a reconstructed value. - * Thus, + * Transforms patterns in the LHS of rules which have tokens of sort Int or Float into side + * conditions generating equality over a reconstructed value. Thus, * - * rule 5 => .K + *

rule 5 => .K * - * becomes + *

becomes * - * rule I:Int => .K when I ==K 5 + *

rule I:Int => .K when I ==K 5 */ public class DeconstructIntegerAndFloatLiterals { - private final Set state = new HashSet<>(); - private final Set vars = new HashSet<>(); - - void reset() { - state.clear(); - vars.clear(); + private final Set state = new HashSet<>(); + private final Set vars = new HashSet<>(); + + void reset() { + state.clear(); + vars.clear(); + } + + void gatherVars(K term) { + new VisitK() { + @Override + public void apply(KVariable v) { + vars.add(v); + super.apply(v); + } + }.apply(term); + } + + public Sentence convert(Sentence s) { + if (ExpandMacros.isMacro(s)) { + return s; } - - void gatherVars(K term) { - new VisitK() { - @Override - public void apply(KVariable v) { - vars.add(v); - super.apply(v); - } - }.apply(term); + if (s instanceof Rule) { + return convert((Rule) s); + } else if (s instanceof Context) { + return convert((Context) s); + } else { + return s; } - - public Sentence convert(Sentence s) { - if (ExpandMacros.isMacro(s)) { - return s; - } - if (s instanceof Rule) { - return convert((Rule) s); - } else if (s instanceof Context) { - return convert((Context) s); - } else { - return s; - } + } + + private Rule convert(Rule rule) { + reset(); + gatherVars(rule.body()); + gatherVars(rule.requires()); + gatherVars(rule.ensures()); + K body = convert(rule.body()); + K requires = convertLookups(rule.requires()); + return Rule(body, addSideCondition(requires), rule.ensures(), rule.att()); + } + + private K convertLookups(K requires) { + return new Transformer(false).apply(requires); + } + + private Context convert(Context context) { + reset(); + gatherVars(context.body()); + gatherVars(context.requires()); + K body = convert(context.body()); + return Context(body, addSideCondition(context.requires()), context.att()); + } + + private int counter = 0; + + KVariable newDotVariable(Sort sort) { + KVariable newLabel; + do { + newLabel = KVariable("_Gen" + (counter++), Att().add(Sort.class, sort)); + } while (vars.contains(newLabel)); + vars.add(newLabel); + return newLabel; + } + + K addSideCondition(K requires) { + Optional sideCondition = state.stream().reduce(BooleanUtils::and); + if (!sideCondition.isPresent()) { + return requires; + } else if (requires.equals(BooleanUtils.TRUE) && sideCondition.isPresent()) { + return sideCondition.get(); + } else { + return BooleanUtils.and(requires, sideCondition.get()); } + } - private Rule convert(Rule rule) { - reset(); - gatherVars(rule.body()); - gatherVars(rule.requires()); - gatherVars(rule.ensures()); - K body = convert(rule.body()); - K requires = convertLookups(rule.requires()); - return Rule( - body, - addSideCondition(requires), - rule.ensures(), - rule.att()); - } + private K convert(K term) { + return new Transformer(true).apply(term); + } - private K convertLookups(K requires) { - return new Transformer(false).apply(requires); - } + private class Transformer extends TransformK { - private Context convert(Context context) { - reset(); - gatherVars(context.body()); - gatherVars(context.requires()); - K body = convert(context.body()); - return Context( - body, - addSideCondition(context.requires()), - context.att()); + @Override + public K apply(KToken k) { + if (lhs) { + if (k.sort().equals(Sorts.Int()) || k.sort().equals(Sorts.Float())) { + KVariable var = newDotVariable(k.sort()); + state.add(KApply(KLabel("_==" + k.sort().name() + "_"), var, k)); + return var; + } + } + return super.apply(k); } - private int counter = 0; - KVariable newDotVariable(Sort sort) { - KVariable newLabel; - do { - newLabel = KVariable("_Gen" + (counter++), Att().add(Sort.class, sort)); - } while (vars.contains(newLabel)); - vars.add(newLabel); - return newLabel; - } + private boolean lhs; - K addSideCondition(K requires) { - Optional sideCondition = state.stream().reduce(BooleanUtils::and); - if (!sideCondition.isPresent()) { - return requires; - } else if (requires.equals(BooleanUtils.TRUE) && sideCondition.isPresent()) { - return sideCondition.get(); - } else { - return BooleanUtils.and(requires, sideCondition.get()); - } + public Transformer(boolean lhs) { + this.lhs = lhs; } - private K convert(K term) { - return new Transformer(true).apply(term); + public boolean isLookupKLabel(KLabel k) { + return k.name().equals("#match") + || k.name().equals("#mapChoice") + || k.name().equals("#filterMapChoice") + || k.name().equals("#setChoice"); } - private class Transformer extends TransformK { - - @Override - public K apply(KToken k) { - if (lhs) { - if (k.sort().equals(Sorts.Int()) || k.sort().equals(Sorts.Float())) { - KVariable var = newDotVariable(k.sort()); - state.add(KApply(KLabel("_==" + k.sort().name() + "_"), var, k)); - return var; - } - } - return super.apply(k); - } - - private boolean lhs; - - public Transformer(boolean lhs) { - this.lhs = lhs; - } - - public boolean isLookupKLabel(KLabel k) { - return k.name().equals("#match") || k.name().equals("#mapChoice") || k.name().equals("#filterMapChoice") || k.name().equals("#setChoice"); - } - - @Override - public K apply(KApply k) { - if (isLookupKLabel(k.klabel())) { - assert k.klist().size() == 2; - K r = apply(k.klist().items().get(1)); - lhs = true; - K l = apply(k.klist().items().get(0)); - lhs = false; - if (l != k.klist().items().get(0) || r != k.klist().items().get(1)) { - return KApply(k.klabel(), l, r); - } else { - return k; - } - } - return super.apply(k); + @Override + public K apply(KApply k) { + if (isLookupKLabel(k.klabel())) { + assert k.klist().size() == 2; + K r = apply(k.klist().items().get(1)); + lhs = true; + K l = apply(k.klist().items().get(0)); + lhs = false; + if (l != k.klist().items().get(0) || r != k.klist().items().get(1)) { + return KApply(k.klabel(), l, r); + } else { + return k; } + } + return super.apply(k); + } - @Override - public K apply(KRewrite k) { - K l = apply(k.left()); - lhs = false; - K r = apply(k.right()); - lhs = true; - if (l != k.left() || r != k.right()) { - return KRewrite(l, r, k.att()); - } else { - return k; - } - } + @Override + public K apply(KRewrite k) { + K l = apply(k.left()); + lhs = false; + K r = apply(k.right()); + lhs = true; + if (l != k.left() || r != k.right()) { + return KRewrite(l, r, k.att()); + } else { + return k; + } } + } } diff --git a/kernel/src/main/java/org/kframework/compile/ExpandMacros.java b/kernel/src/main/java/org/kframework/compile/ExpandMacros.java index d860de110cf..e27a885686c 100644 --- a/kernel/src/main/java/org/kframework/compile/ExpandMacros.java +++ b/kernel/src/main/java/org/kframework/compile/ExpandMacros.java @@ -1,6 +1,26 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + +import java.io.BufferedWriter; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; import org.kframework.attributes.Att; import org.kframework.backend.kore.ModuleToKORE; import org.kframework.builtin.BooleanUtils; @@ -20,7 +40,6 @@ import org.kframework.kore.KApply; import org.kframework.kore.KAs; import org.kframework.kore.KLabel; -import org.kframework.kore.KRewrite; import org.kframework.kore.KSequence; import org.kframework.kore.KToken; import org.kframework.kore.KVariable; @@ -32,380 +51,417 @@ import org.kframework.utils.file.FileUtil; import scala.Option; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.FileOutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; - -import java.nio.channels.FileChannel; -import java.nio.channels.FileLock; - -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; - -import static org.kframework.Collections.*; -import static org.kframework.kore.KORE.*; -import static org.kframework.definition.Constructors.*; - /** - * Expands all the macros in a particular module. A macro is a rule (without a side condition) - * which is tagged with the "macro" attribute. This class assumes that all such macros perform no complex - * matching on the left hand side of the rule, and generates a simple substitution and recursively applies it. + * Expands all the macros in a particular module. A macro is a rule (without a side condition) which + * is tagged with the "macro" attribute. This class assumes that all such macros perform no complex + * matching on the left hand side of the rule, and generates a simple substitution and recursively + * applies it. */ public class ExpandMacros { - private final Map> macros; - private final Map> macrosBySort; - private final Module mod; - private final boolean cover; - private final PrintWriter coverage; - private final FileChannel channel; - private final boolean reverse; - private final ResolveFunctionWithConfig transformer; - private final KompileOptions kompileOptions; - private final KExceptionManager kem; - - public static ExpandMacros fromMainModule(Module mod, FileUtil files, KExceptionManager kem, KompileOptions kompileOptions, boolean reverse) { - return new ExpandMacros(mod, files, kem, kompileOptions, reverse, true); - } - - public static ExpandMacros forNonSentences(Module mod, FileUtil files, KompileOptions kompileOptions, boolean reverse) { - return new ExpandMacros(mod, files, null, kompileOptions, reverse, false); + private final Map> macros; + private final Map> macrosBySort; + private final Module mod; + private final boolean cover; + private final PrintWriter coverage; + private final FileChannel channel; + private final boolean reverse; + private final ResolveFunctionWithConfig transformer; + private final KompileOptions kompileOptions; + private final KExceptionManager kem; + + public static ExpandMacros fromMainModule( + Module mod, + FileUtil files, + KExceptionManager kem, + KompileOptions kompileOptions, + boolean reverse) { + return new ExpandMacros(mod, files, kem, kompileOptions, reverse, true); + } + + public static ExpandMacros forNonSentences( + Module mod, FileUtil files, KompileOptions kompileOptions, boolean reverse) { + return new ExpandMacros(mod, files, null, kompileOptions, reverse, false); + } + + private ExpandMacros( + Module mod, + FileUtil files, + KExceptionManager kem, + KompileOptions kompileOptions, + boolean reverse, + boolean sentences) { + this( + sentences ? new ResolveFunctionWithConfig(mod) : null, + mod, + files, + kem, + kompileOptions, + reverse); + } + + public ExpandMacros( + ResolveFunctionWithConfig transformer, + Module mod, + FileUtil files, + KExceptionManager kem, + KompileOptions kompileOptions, + boolean reverse) { + this.mod = mod; + this.reverse = reverse; + this.cover = kompileOptions.coverage; + this.kompileOptions = kompileOptions; + this.kem = kem; + files.resolveKompiled(".").mkdirs(); + List allMacros = + stream(mod.rules()) + .filter(r -> isMacro(r.att(), reverse)) + .sorted(Comparator.comparingInt(r -> ModuleToKORE.getPriority(r.att()))) + .collect(Collectors.toList()); + macros = + allMacros.stream() + .filter(r -> getLeft(r, reverse) instanceof KApply) + .collect(Collectors.groupingBy(r -> ((KApply) getLeft(r, reverse)).klabel())); + macrosBySort = + stream(mod.allSorts()) + .collect( + Collectors.toMap( + s -> s, + s -> + allMacros.stream() + .filter( + r -> { + K left = getLeft(r, reverse); + if (left instanceof KToken || left instanceof KVariable) { + return sort(left, r).contains(s); + } else { + return false; + } + }) + .collect(Collectors.toList()))); + this.transformer = transformer; + if (cover) { + try { + FileOutputStream os = new FileOutputStream(files.resolveKompiled("coverage.txt"), true); + channel = os.getChannel(); + coverage = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os))); + } catch (IOException e) { + throw KEMException.internalError("Could not write list of rules to coverage document.", e); + } + } else { + channel = null; + coverage = null; } + } - private ExpandMacros(Module mod, FileUtil files, KExceptionManager kem, KompileOptions kompileOptions, boolean reverse, boolean sentences) { - this(sentences ? new ResolveFunctionWithConfig(mod) : null, mod, files, kem, kompileOptions, reverse); + private K getLeft(Rule r, boolean reverse) { + if (reverse) { + return RewriteToTop.toRight(r.body()); } - - public ExpandMacros(ResolveFunctionWithConfig transformer, Module mod, FileUtil files, KExceptionManager kem, KompileOptions kompileOptions, boolean reverse) { - this.mod = mod; - this.reverse = reverse; - this.cover = kompileOptions.coverage; - this.kompileOptions = kompileOptions; - this.kem = kem; - files.resolveKompiled(".").mkdirs(); - List allMacros = stream(mod.rules()).filter(r -> isMacro(r.att(), reverse)).sorted(Comparator.comparingInt(r -> ModuleToKORE.getPriority(r.att()))).collect(Collectors.toList()); - macros = allMacros.stream().filter(r -> getLeft(r, reverse) instanceof KApply).collect(Collectors.groupingBy(r -> ((KApply)getLeft(r, reverse)).klabel())); - macrosBySort = stream(mod.allSorts()).collect(Collectors.toMap(s -> s, s -> allMacros.stream().filter(r -> { - K left = getLeft(r, reverse); - if (left instanceof KToken || left instanceof KVariable) { - return sort(left, r).contains(s); - } else { - return false; - } - }).collect(Collectors.toList()))); - this.transformer = transformer; - if (cover) { - try { - FileOutputStream os = new FileOutputStream(files.resolveKompiled("coverage.txt"), true); - channel = os.getChannel(); - coverage = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os))); - } catch (IOException e) { - throw KEMException.internalError("Could not write list of rules to coverage document.", e); + return RewriteToTop.toLeft(r.body()); + } + + private boolean isMacro(Att att, boolean reverse) { + return att.contains(Att.ALIAS_REC()) + || att.contains(Att.ALIAS()) + || (!reverse && (att.contains(Att.MACRO()) || att.contains(Att.MACRO_REC()))); + } + + private final Set vars = new HashSet<>(); + + void resetVars() { + vars.clear(); + } + + void gatherVars(K term) { + new VisitK() { + @Override + public void apply(KVariable v) { + vars.add(v); + super.apply(v); + } + }.apply(term); + } + + private int counter = 0; + + KVariable newDotVariable(Att att) { + KVariable newLabel; + do { + newLabel = KVariable("_Gen" + (counter++), att.add(Att.ANONYMOUS())); + } while (vars.contains(newLabel)); + vars.add(newLabel); + return newLabel; + } + + private RuleOrClaim expand(RuleOrClaim rule) { + resetVars(); + gatherVars(rule.body()); + gatherVars(rule.requires()); + gatherVars(rule.ensures()); + RuleOrClaim result = + rule.newInstance( + expand(rule.body()), expand(rule.requires()), expand(rule.ensures()), rule.att()); + return (RuleOrClaim) check(result); + } + + private Context expand(Context context) { + resetVars(); + gatherVars(context.body()); + gatherVars(context.requires()); + Context result = Context(expand(context.body()), expand(context.requires()), context.att()); + return (Context) check(result); + } + + private Sentence check(Sentence s) { + Set errors = new HashSet<>(); + new CheckFunctions(errors, mod).check(s); + new CheckSmtLemmas(errors, mod).check(s); + + /** + * At this point, all macros inside rules should have been expanded. However, a user could have + * accidentally left out a rule that expands the macro or incorrectly programmed this rule. If + * this is the case, rules may contain macros that have not been expanded. + */ + if (s instanceof RuleOrClaim) { + VisitK visitor = + new VisitK() { + @Override + public void apply(KApply k) { + Option atts = mod.attributesFor().get(k.klabel()); + if (atts.isDefined() + && mod.attributesFor().apply(k.klabel()).getMacro().isDefined()) { + errors.add( + KEMException.compilerError( + "Rule contains macro symbol that was not expanded", s)); + } + k.klist().items().forEach(super::apply); } - } else { - channel = null; - coverage = null; - } - } + }; - private K getLeft(Rule r, boolean reverse) { - if (reverse) { - return RewriteToTop.toRight(r.body()); - } - return RewriteToTop.toLeft(r.body()); + visitor.accept(((RuleOrClaim) s).body()); + visitor.accept(((RuleOrClaim) s).requires()); + visitor.accept(((RuleOrClaim) s).ensures()); } - private boolean isMacro(Att att, boolean reverse) { - return att.contains(Att.ALIAS_REC()) || att.contains(Att.ALIAS()) || (!reverse && (att.contains(Att.MACRO()) || att.contains(Att.MACRO_REC()))); + if (!errors.isEmpty()) { + kem.addAllKException(errors.stream().map(e -> e.exception).collect(Collectors.toList())); + throw KEMException.compilerError( + "Had " + errors.size() + " structural errors after macro expansion."); } - - private final Set vars = new HashSet<>(); - - void resetVars() { - vars.clear(); + return s; + } + + public K expand(K term) { + if (macros.size() == 0 && macrosBySort.size() == 0) return term; + FileLock lock = null; + if (cover) { + try { + lock = channel.lock(); + } catch (IOException e) { + throw KEMException.internalError("Could not lock coverage file", e); + } } + try { + K result = + new TransformK() { + private Set appliedRules = new HashSet<>(); - void gatherVars(K term) { - new VisitK() { @Override - public void apply(KVariable v) { - vars.add(v); - super.apply(v); + public K apply(KApply k) { + List rules = macros.get(k.klabel()); + return applyMacros(k, rules, super::apply); } - }.apply(term); - } - - private int counter = 0; - KVariable newDotVariable(Att att) { - KVariable newLabel; - do { - newLabel = KVariable("_Gen" + (counter++), att.add(Att.ANONYMOUS())); - } while (vars.contains(newLabel)); - vars.add(newLabel); - return newLabel; - } - private RuleOrClaim expand(RuleOrClaim rule) { - resetVars(); - gatherVars(rule.body()); - gatherVars(rule.requires()); - gatherVars(rule.ensures()); - RuleOrClaim result = rule.newInstance(expand(rule.body()), - expand(rule.requires()), - expand(rule.ensures()), - rule.att()); - return (RuleOrClaim) check(result); - } - - private Context expand(Context context) { - resetVars(); - gatherVars(context.body()); - gatherVars(context.requires()); - Context result = Context( - expand(context.body()), - expand(context.requires()), - context.att()); - return (Context)check(result); - } - - private Sentence check(Sentence s) { - Set errors = new HashSet<>(); - new CheckFunctions(errors, mod).check(s); - new CheckSmtLemmas(errors, mod).check(s); - - /** - * At this point, all macros inside rules should have been expanded. However, a user could have accidentally - * left out a rule that expands the macro or incorrectly programmed this rule. If this is the case, rules may - * contain macros that have not been expanded. - */ - if (s instanceof RuleOrClaim){ - VisitK visitor = new VisitK() { - @Override - public void apply(KApply k) { - Option atts = mod.attributesFor().get(k.klabel()); - if (atts.isDefined() && mod.attributesFor().apply(k.klabel()).getMacro().isDefined()) { - errors.add(KEMException.compilerError("Rule contains macro symbol that was not expanded", s)); - } - k.klist().items().forEach(super::apply); + private K applyMacros(T k, List rules, Function superApply) { + if (rules == null) return superApply.apply(k); + K applied = superApply.apply(k); + for (Rule r : rules) { + if (!r.requires().equals(BooleanUtils.TRUE)) { + throw KEMException.compilerError( + "Cannot compute macros with side conditions.", r); } - }; - - visitor.accept(((RuleOrClaim) s).body()); - visitor.accept(((RuleOrClaim) s).requires()); - visitor.accept(((RuleOrClaim) s).ensures()); - } - - if (!errors.isEmpty()) { - kem.addAllKException(errors.stream().map(e -> e.exception).collect(Collectors.toList())); - throw KEMException.compilerError("Had " + errors.size() + " structural errors after macro expansion."); - } - return s; - } - - public K expand(K term) { - if (macros.size() == 0 && macrosBySort.size() == 0) - return term; - FileLock lock = null; - if (cover) { - try { - lock = channel.lock(); - } catch (IOException e) { - throw KEMException.internalError("Could not lock coverage file", e); - } - } - try { - K result = new TransformK() { - private Set appliedRules = new HashSet<>(); - - @Override - public K apply(KApply k) { - List rules = macros.get(k.klabel()); - return applyMacros(k, rules, super::apply); + K left = RewriteToTop.toLeft(r.body()); + K right = RewriteToTop.toRight(r.body()); + if (reverse) { + K tmp = left; + left = right; + right = tmp; } - - private K applyMacros(T k, List rules, Function superApply) { - if (rules == null) - return superApply.apply(k); - K applied = superApply.apply(k); - for (Rule r : rules) { - if (!r.requires().equals(BooleanUtils.TRUE)) { - throw KEMException.compilerError("Cannot compute macros with side conditions.", r); - } - K left = RewriteToTop.toLeft(r.body()); - K right = RewriteToTop.toRight(r.body()); - if (reverse) { - K tmp = left; - left = right; - right = tmp; - } - final Map subst = new HashMap<>(); - if (match(subst, left, applied, r) && (r.att().contains(Att.MACRO_REC()) || r.att().contains(Att.ALIAS_REC()) || !appliedRules.contains(r))) { - if (cover) { - if (!r.att().contains(Att.UNIQUE_ID())) System.out.println(r); - coverage.println(r.att().get(Att.UNIQUE_ID())); - } - Set oldAppliedRules = appliedRules; - appliedRules = new HashSet<>(appliedRules); - appliedRules.add(r); - K result = apply(new TransformK() { - @Override - public K apply(KVariable k) { - K result = subst.get(k); - if (result == null) { - if (k.name().equals("#Configuration")) { - return k; - } - result = newDotVariable(k.att()); - subst.put(k, result); - } - return result; + final Map subst = new HashMap<>(); + if (match(subst, left, applied, r) + && (r.att().contains(Att.MACRO_REC()) + || r.att().contains(Att.ALIAS_REC()) + || !appliedRules.contains(r))) { + if (cover) { + if (!r.att().contains(Att.UNIQUE_ID())) System.out.println(r); + coverage.println(r.att().get(Att.UNIQUE_ID())); + } + Set oldAppliedRules = appliedRules; + appliedRules = new HashSet<>(appliedRules); + appliedRules.add(r); + K result = + apply( + new TransformK() { + @Override + public K apply(KVariable k) { + K result = subst.get(k); + if (result == null) { + if (k.name().equals("#Configuration")) { + return k; } - }.apply(right)); - appliedRules = oldAppliedRules; - return result; - } - } - return applied; - } - - @Override - public K apply(KToken k) { - List rules = macrosBySort.get(k.sort()); - return applyMacros(k, rules, super::apply); + result = newDotVariable(k.att()); + subst.put(k, result); + } + return result; + } + }.apply(right)); + appliedRules = oldAppliedRules; + return result; } + } + return applied; + } - }.apply(term); - return result; - } finally { - if (cover) { - coverage.flush(); - try { - lock.close(); - } catch (IOException e) { - throw KEMException.internalError("Could not unlock coverage file", e); - } + @Override + public K apply(KToken k) { + List rules = macrosBySort.get(k.sort()); + return applyMacros(k, rules, super::apply); } + }.apply(term); + return result; + } finally { + if (cover) { + coverage.flush(); + try { + lock.close(); + } catch (IOException e) { + throw KEMException.internalError("Could not unlock coverage file", e); } + } } + } - private boolean hasPolyAtt(Production prod, int idx) { - if (prod.params().isEmpty()) { - return false; - } - for (Sort param : iterable(prod.params())) { - if (prod.sort().equals(param) && prod.nonterminals().apply(idx).sort().equals(param)) { - return true; - } - } + private boolean hasPolyAtt(Production prod, int idx) { + if (prod.params().isEmpty()) { return false; } - - - private Set sort(K k, RuleOrClaim r) { - if (k instanceof KVariable) { - return Collections.singleton(k.att().getOptional(Sort.class).orElse(null)); - } else if (k instanceof KToken) { - return Collections.singleton(((KToken)k).sort()); - } else if (k instanceof KApply kapp) { - if (kapp.klabel() instanceof KVariable) { - throw KEMException.compilerError("Cannot compute macros with klabel variables.", r); - } - Set prods = new HashSet<>(mutable(mod.productionsFor().apply(kapp.klabel()))); - prods.removeIf(p -> p.arity() != kapp.items().size()); - Set polySorts = new HashSet<>(); - for (int i = 0; i < kapp.items().size(); i++) { - final int idx = i; - Set sorts = sort(kapp.items().get(idx), r); - if (prods.stream().anyMatch(p -> hasPolyAtt(p, idx))) { - polySorts.addAll(sorts); - } - if (!sorts.contains(null)) { - prods.removeIf(p -> !hasPolyAtt(p, idx) && sorts.stream().noneMatch(s -> mod.subsorts().lessThanEq(s, p.nonterminal(idx).sort()))); - } - } - Set candidates = prods.stream().map(Production::sort).collect(Collectors.toSet()); - candidates.addAll(polySorts); - return candidates; - } else if (k instanceof KSequence) { - return Collections.singleton(Sorts.K()); - } else if (k instanceof KAs) { - return sort(((KAs)k).pattern(), r); - } else { - throw KEMException.compilerError("Cannot compute macros with sort check on terms that are not KApply, KToken, or KVariable.", r); - } + for (Sort param : iterable(prod.params())) { + if (prod.sort().equals(param) && prod.nonterminals().apply(idx).sort().equals(param)) { + return true; + } } - - private boolean match(Map subst, K pattern, K subject, RuleOrClaim r) { - if (pattern instanceof KVariable) { - if (subst.containsKey(pattern)) { - return subst.get(pattern).equals(subject); - } else { - if (pattern.att().contains(Sort.class)) { - Sort patternSort = pattern.att().get(Sort.class); - if (sort(subject, r).stream().anyMatch(s -> s == null || mod.subsorts().lessThanEq(s, patternSort))) { - subst.put((KVariable)pattern, subject); - return true; - } else { - return false; - } - } else { - subst.put((KVariable)pattern, subject); - return true; - } - } - } - if (pattern instanceof KApply p && subject instanceof KApply s) { - if (p.klabel() instanceof KVariable || s.klabel() instanceof KVariable) { - throw KEMException.compilerError("Cannot compute macros with klabel variables.", r); - } - if (!p.klabel().name().equals(s.klabel().name())) { - if (!mod.overloads().greaterThan(mod.productionsFor().apply(p.klabel()).head(), mod.productionsFor().apply(s.klabel()).head())) { - return false; - } - } - if (p.klist().size() != s.klist().size()) { - return false; - } - boolean match = true; - for (int i = 0; i < p.klist().size(); i++) { - match = match && match(subst, p.klist().items().get(i), s.klist().items().get(i), r); - } - return match; + return false; + } + + private Set sort(K k, RuleOrClaim r) { + if (k instanceof KVariable) { + return Collections.singleton(k.att().getOptional(Sort.class).orElse(null)); + } else if (k instanceof KToken) { + return Collections.singleton(((KToken) k).sort()); + } else if (k instanceof KApply kapp) { + if (kapp.klabel() instanceof KVariable) { + throw KEMException.compilerError("Cannot compute macros with klabel variables.", r); + } + Set prods = new HashSet<>(mutable(mod.productionsFor().apply(kapp.klabel()))); + prods.removeIf(p -> p.arity() != kapp.items().size()); + Set polySorts = new HashSet<>(); + for (int i = 0; i < kapp.items().size(); i++) { + final int idx = i; + Set sorts = sort(kapp.items().get(idx), r); + if (prods.stream().anyMatch(p -> hasPolyAtt(p, idx))) { + polySorts.addAll(sorts); } - if (subject instanceof KToken && pattern instanceof KToken) { - return subject.equals(pattern); + if (!sorts.contains(null)) { + prods.removeIf( + p -> + !hasPolyAtt(p, idx) + && sorts.stream() + .noneMatch(s -> mod.subsorts().lessThanEq(s, p.nonterminal(idx).sort()))); } - if (subject instanceof KVariable) { + } + Set candidates = prods.stream().map(Production::sort).collect(Collectors.toSet()); + candidates.addAll(polySorts); + return candidates; + } else if (k instanceof KSequence) { + return Collections.singleton(Sorts.K()); + } else if (k instanceof KAs) { + return sort(((KAs) k).pattern(), r); + } else { + throw KEMException.compilerError( + "Cannot compute macros with sort check on terms that are not KApply, KToken, or" + + " KVariable.", + r); + } + } + + private boolean match(Map subst, K pattern, K subject, RuleOrClaim r) { + if (pattern instanceof KVariable) { + if (subst.containsKey(pattern)) { + return subst.get(pattern).equals(subject); + } else { + if (pattern.att().contains(Sort.class)) { + Sort patternSort = pattern.att().get(Sort.class); + if (sort(subject, r).stream() + .anyMatch(s -> s == null || mod.subsorts().lessThanEq(s, patternSort))) { + subst.put((KVariable) pattern, subject); + return true; + } else { return false; + } + } else { + subst.put((KVariable) pattern, subject); + return true; } - if (subject instanceof KToken || pattern instanceof KToken) { - return false; + } + } + if (pattern instanceof KApply p && subject instanceof KApply s) { + if (p.klabel() instanceof KVariable || s.klabel() instanceof KVariable) { + throw KEMException.compilerError("Cannot compute macros with klabel variables.", r); + } + if (!p.klabel().name().equals(s.klabel().name())) { + if (!mod.overloads() + .greaterThan( + mod.productionsFor().apply(p.klabel()).head(), + mod.productionsFor().apply(s.klabel()).head())) { + return false; } - throw KEMException.compilerError("Cannot compute macros with terms that are not KApply, KToken, or KVariable.", r); + } + if (p.klist().size() != s.klist().size()) { + return false; + } + boolean match = true; + for (int i = 0; i < p.klist().size(); i++) { + match = match && match(subst, p.klist().items().get(i), s.klist().items().get(i), r); + } + return match; } - - public static boolean isMacro(HasAtt s) { - return s.isMacro(); + if (subject instanceof KToken && pattern instanceof KToken) { + return subject.equals(pattern); } - - public Sentence expand(Sentence s) { - if (s instanceof Rule && !isMacro(s)) { - return transformer.resolve(expand((Rule) s), mod); - } else if (s instanceof Claim) { - return transformer.resolve(expand((Claim) s), mod); - } else if (s instanceof Context) { - return transformer.resolve(expand((Context) s), mod); - } else { - return s; - } + if (subject instanceof KVariable) { + return false; + } + if (subject instanceof KToken || pattern instanceof KToken) { + return false; + } + throw KEMException.compilerError( + "Cannot compute macros with terms that are not KApply, KToken, or KVariable.", r); + } + + public static boolean isMacro(HasAtt s) { + return s.isMacro(); + } + + public Sentence expand(Sentence s) { + if (s instanceof Rule && !isMacro(s)) { + return transformer.resolve(expand((Rule) s), mod); + } else if (s instanceof Claim) { + return transformer.resolve(expand((Claim) s), mod); + } else if (s instanceof Context) { + return transformer.resolve(expand((Context) s), mod); + } else { + return s; } + } } diff --git a/kernel/src/main/java/org/kframework/compile/FloatBuiltin.java b/kernel/src/main/java/org/kframework/compile/FloatBuiltin.java index 026c5b2c04c..91183c5edb9 100644 --- a/kernel/src/main/java/org/kframework/compile/FloatBuiltin.java +++ b/kernel/src/main/java/org/kframework/compile/FloatBuiltin.java @@ -1,197 +1,191 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; -import org.apache.commons.lang3.tuple.Pair; -import org.kframework.mpfr.BigFloat; -import org.kframework.mpfr.BinaryMathContext; - import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.commons.lang3.tuple.Pair; +import org.kframework.mpfr.BigFloat; +import org.kframework.mpfr.BinaryMathContext; public class FloatBuiltin { - /* Token cache */ - private static final Map, FloatBuiltin> tokenCache = new HashMap<>(); - - /** - * Returns a {@link FloatBuiltin} representing the given {@link BigFloat} value - * and the given exponent range, in bits. - * - * @param value - * @return - */ - public static FloatBuiltin of(BigFloat value, Integer exponent) { - assert value != null; - Pair key = Pair.of(value, exponent); - FloatBuiltin floatBuiltin = tokenCache.get(key); - if (floatBuiltin == null) { - floatBuiltin = new FloatBuiltin(value, exponent); - tokenCache.put(key, floatBuiltin); - } - return floatBuiltin; - } - - /** - * Returns a {@link FloatBuiltin} representing the given {@link double} value. - * - * @param value - * @return - */ - public static FloatBuiltin of(double value) { - return of(new BigFloat(value, BinaryMathContext.BINARY64), BinaryMathContext.BINARY64_EXPONENT_BITS); - } - - /** - * Returns a {@link FloatBuiltin} representing the given {@link float} value. - * - * @param value - * @return - */ - public static FloatBuiltin of(float value) { - return of(new BigFloat(value, BinaryMathContext.BINARY32), BinaryMathContext.BINARY32_EXPONENT_BITS); - } - - /** - * Returns a {@link FloatBuiltin} representing the given {@link String} value. - * - * @param value - * @return - */ - public static FloatBuiltin of(String value) { - Pair pair = FloatBuiltin.parseKFloat(value); - return of(pair.getLeft(), pair.getRight()); - } - - private final BigFloat value; - private final int exponent; - - private FloatBuiltin(BigFloat value, int exponent) { - this.value = value; - this.exponent = exponent; - } - - private static final Pattern precisionAndExponent = Pattern.compile("(.*)[pP](\\d+)[xX](\\d+)"); - public static Pair parseKFloat(String s) { - try { - Matcher m = precisionAndExponent.matcher(s); - int precision, exponent; - String value; - if (m.matches()) { - precision = Integer.parseInt(m.group(2)); - exponent = Integer.parseInt(m.group(3)); - value = m.group(1); - } else if (s.endsWith("f") || s.endsWith("F")) { - precision = BinaryMathContext.BINARY32.precision; - exponent = BinaryMathContext.BINARY32_EXPONENT_BITS; - value = s.substring(0, s.length() - 1); - } else { - precision = BinaryMathContext.BINARY64.precision; - exponent = BinaryMathContext.BINARY64_EXPONENT_BITS; - if (s.endsWith("d") || s.endsWith("D")) { - value = s.substring(0, s.length() - 1); - } else { - value = s; - } - } - BinaryMathContext mc = new BinaryMathContext(precision, exponent); - BigFloat result = new BigFloat(value, mc); - return Pair.of(result, exponent); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - throw new NumberFormatException(); - } - } - - /** - * Returns a {@link BigFloat} representing the (interpreted) value of the float token. - */ - public BigFloat bigFloatValue() { - return value; - } - - public float floatValue() { - return value.floatValue(); - } - - public double doubleValue() { - return value.doubleValue(); - } - - /** - * Returns a {@link BinaryMathContext} representing the context to perform arithmetic under. - */ - public int exponent() { - return exponent; - } - - public BinaryMathContext getMathContext() { - return new BinaryMathContext(precision(), exponent()); - } - - public int precision() { - return value.precision(); - } - - /** - * Returns a {@link String} representing the (uninterpreted) value of the float token. - */ - public String value() { - return printKFloat(value) + printKFloatSuffix(value, exponent); - } - - /** - * Return a {@link String} representing the (uninterpreted) value of the numerical - * float corresponding to the value of the float token. - */ - - public static String printKFloat(BigFloat value) { - return printKFloat(value, value::toString); - } - - - public static String printKFloat(BigFloat value, Supplier toString) { - if (value.isInfinite()) { - if (value.sign()) { - return "-Infinity"; - } else { - return "Infinity"; - } - } else if (value.isNaN()) { - return "NaN"; + /* Token cache */ + private static final Map, FloatBuiltin> tokenCache = new HashMap<>(); + + /** + * Returns a {@link FloatBuiltin} representing the given {@link BigFloat} value and the given + * exponent range, in bits. + * + * @param value + * @return + */ + public static FloatBuiltin of(BigFloat value, Integer exponent) { + assert value != null; + Pair key = Pair.of(value, exponent); + FloatBuiltin floatBuiltin = tokenCache.get(key); + if (floatBuiltin == null) { + floatBuiltin = new FloatBuiltin(value, exponent); + tokenCache.put(key, floatBuiltin); + } + return floatBuiltin; + } + + /** + * Returns a {@link FloatBuiltin} representing the given {@link double} value. + * + * @param value + * @return + */ + public static FloatBuiltin of(double value) { + return of( + new BigFloat(value, BinaryMathContext.BINARY64), BinaryMathContext.BINARY64_EXPONENT_BITS); + } + + /** + * Returns a {@link FloatBuiltin} representing the given {@link float} value. + * + * @param value + * @return + */ + public static FloatBuiltin of(float value) { + return of( + new BigFloat(value, BinaryMathContext.BINARY32), BinaryMathContext.BINARY32_EXPONENT_BITS); + } + + /** + * Returns a {@link FloatBuiltin} representing the given {@link String} value. + * + * @param value + * @return + */ + public static FloatBuiltin of(String value) { + Pair pair = FloatBuiltin.parseKFloat(value); + return of(pair.getLeft(), pair.getRight()); + } + + private final BigFloat value; + private final int exponent; + + private FloatBuiltin(BigFloat value, int exponent) { + this.value = value; + this.exponent = exponent; + } + + private static final Pattern precisionAndExponent = Pattern.compile("(.*)[pP](\\d+)[xX](\\d+)"); + + public static Pair parseKFloat(String s) { + try { + Matcher m = precisionAndExponent.matcher(s); + int precision, exponent; + String value; + if (m.matches()) { + precision = Integer.parseInt(m.group(2)); + exponent = Integer.parseInt(m.group(3)); + value = m.group(1); + } else if (s.endsWith("f") || s.endsWith("F")) { + precision = BinaryMathContext.BINARY32.precision; + exponent = BinaryMathContext.BINARY32_EXPONENT_BITS; + value = s.substring(0, s.length() - 1); + } else { + precision = BinaryMathContext.BINARY64.precision; + exponent = BinaryMathContext.BINARY64_EXPONENT_BITS; + if (s.endsWith("d") || s.endsWith("D")) { + value = s.substring(0, s.length() - 1); } else { - return toString.get(); + value = s; } - } - - public static String printKFloatSuffix(BigFloat value, int exponent) { - return "p" + value.precision() + "x" + exponent; - } - - @Override - public int hashCode() { - return value.hashCode() * 31 + exponent; - } - - @Override - public boolean equals(Object object) { - if (object == null) { - return false; - } - if (!object.getClass().equals(FloatBuiltin.class)) { - return false; - } - FloatBuiltin other = (FloatBuiltin)object; - if (!value.equals(other.value)) { - return false; - } - return exponent == other.exponent; - } - - @Override - public String toString() { - return value(); - } + } + BinaryMathContext mc = new BinaryMathContext(precision, exponent); + BigFloat result = new BigFloat(value, mc); + return Pair.of(result, exponent); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + throw new NumberFormatException(); + } + } + + /** Returns a {@link BigFloat} representing the (interpreted) value of the float token. */ + public BigFloat bigFloatValue() { + return value; + } + + public float floatValue() { + return value.floatValue(); + } + + public double doubleValue() { + return value.doubleValue(); + } + + /** Returns a {@link BinaryMathContext} representing the context to perform arithmetic under. */ + public int exponent() { + return exponent; + } + + public BinaryMathContext getMathContext() { + return new BinaryMathContext(precision(), exponent()); + } + + public int precision() { + return value.precision(); + } + + /** Returns a {@link String} representing the (uninterpreted) value of the float token. */ + public String value() { + return printKFloat(value) + printKFloatSuffix(value, exponent); + } + + /** + * Return a {@link String} representing the (uninterpreted) value of the numerical float + * corresponding to the value of the float token. + */ + public static String printKFloat(BigFloat value) { + return printKFloat(value, value::toString); + } + + public static String printKFloat(BigFloat value, Supplier toString) { + if (value.isInfinite()) { + if (value.sign()) { + return "-Infinity"; + } else { + return "Infinity"; + } + } else if (value.isNaN()) { + return "NaN"; + } else { + return toString.get(); + } + } + + public static String printKFloatSuffix(BigFloat value, int exponent) { + return "p" + value.precision() + "x" + exponent; + } + + @Override + public int hashCode() { + return value.hashCode() * 31 + exponent; + } + + @Override + public boolean equals(Object object) { + if (object == null) { + return false; + } + if (!object.getClass().equals(FloatBuiltin.class)) { + return false; + } + FloatBuiltin other = (FloatBuiltin) object; + if (!value.equals(other.value)) { + return false; + } + return exponent == other.exponent; + } + + @Override + public String toString() { + return value(); + } } diff --git a/kernel/src/main/java/org/kframework/compile/GatherVarsVisitor.java b/kernel/src/main/java/org/kframework/compile/GatherVarsVisitor.java index c5c044a69e5..707c3fcd285 100644 --- a/kernel/src/main/java/org/kframework/compile/GatherVarsVisitor.java +++ b/kernel/src/main/java/org/kframework/compile/GatherVarsVisitor.java @@ -1,63 +1,61 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import java.util.Set; import org.kframework.builtin.KLabels; import org.kframework.kore.InjectedKLabel; import org.kframework.kore.KApply; import org.kframework.kore.KVariable; import org.kframework.utils.errorsystem.KEMException; -import java.util.Set; - -/** - * Created by dwightguth on 3/6/17. - */ +/** Created by dwightguth on 3/6/17. */ public class GatherVarsVisitor extends RewriteAwareVisitor { - private final Set vars; - private final Set errors; - private final boolean errorExistential; - private boolean isInMLBinderLhs = false; + private final Set vars; + private final Set errors; + private final boolean errorExistential; + private boolean isInMLBinderLhs = false; - public GatherVarsVisitor(boolean isBody, Set errors, Set vars, boolean errorExistential) { - super(isBody, errors); - this.errors = errors; - this.vars = vars; - this.errorExistential = errorExistential; - } + public GatherVarsVisitor( + boolean isBody, Set errors, Set vars, boolean errorExistential) { + super(isBody, errors); + this.errors = errors; + this.vars = vars; + this.errorExistential = errorExistential; + } - @Override - public void apply(KVariable v) { - if (isLHS() && !ResolveAnonVar.isAnonVar(v)) - vars.add(v); - if (isRHS() && isInMLBinderLhs) - vars.add(v); - if (errorExistential && v.name().startsWith("?")) { - errors.add(KEMException.compilerError("Found existential variable not supported by concrete backend.", v)); - } - super.apply(v); + @Override + public void apply(KVariable v) { + if (isLHS() && !ResolveAnonVar.isAnonVar(v)) vars.add(v); + if (isRHS() && isInMLBinderLhs) vars.add(v); + if (errorExistential && v.name().startsWith("?")) { + errors.add( + KEMException.compilerError( + "Found existential variable not supported by concrete backend.", v)); } + super.apply(v); + } - @Override - public void apply(KApply k) { - if (k.klabel() instanceof KVariable) { - apply((KVariable) k.klabel()); - } - if (k.klabel().equals(KLabels.ML_EXISTS) || k.klabel().equals(KLabels.ML_FORALL)) { - boolean tmp = isInMLBinderLhs; - isInMLBinderLhs = true; - apply(k.items().get(0)); - isInMLBinderLhs = tmp; - apply(k.items().get(1)); - return; - } - super.apply(k); + @Override + public void apply(KApply k) { + if (k.klabel() instanceof KVariable) { + apply((KVariable) k.klabel()); + } + if (k.klabel().equals(KLabels.ML_EXISTS) || k.klabel().equals(KLabels.ML_FORALL)) { + boolean tmp = isInMLBinderLhs; + isInMLBinderLhs = true; + apply(k.items().get(0)); + isInMLBinderLhs = tmp; + apply(k.items().get(1)); + return; } + super.apply(k); + } - @Override - public void apply(InjectedKLabel k) { - if (k.klabel() instanceof KVariable) { - apply((KVariable) k.klabel()); - } - super.apply(k); + @Override + public void apply(InjectedKLabel k) { + if (k.klabel() instanceof KVariable) { + apply((KVariable) k.klabel()); } + super.apply(k); + } } diff --git a/kernel/src/main/java/org/kframework/compile/GenerateCoverage.java b/kernel/src/main/java/org/kframework/compile/GenerateCoverage.java index 28c601891a9..5c5c9845dfa 100644 --- a/kernel/src/main/java/org/kframework/compile/GenerateCoverage.java +++ b/kernel/src/main/java/org/kframework/compile/GenerateCoverage.java @@ -1,6 +1,8 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.kore.KORE.*; + import org.kframework.attributes.Att; import org.kframework.attributes.Source; import org.kframework.builtin.Sorts; @@ -11,33 +13,37 @@ import org.kframework.utils.StringUtil; import org.kframework.utils.file.FileUtil; -import java.io.File; - -import static org.kframework.kore.KORE.*; - public record GenerateCoverage(boolean cover, FileUtil files) { - public K gen(RuleOrClaim r, K body, Module mod) { - if (!cover || r.att().getOptional(Source.class).isEmpty()) { - return body; - } - K left = RewriteToTop.toLeft(body); - K right = RewriteToTop.toRight(body); - String id = r.att().get(Att.UNIQUE_ID()); + public K gen(RuleOrClaim r, K body, Module mod) { + if (!cover || r.att().getOptional(Source.class).isEmpty()) { + return body; + } + K left = RewriteToTop.toLeft(body); + K right = RewriteToTop.toRight(body); + String id = r.att().get(Att.UNIQUE_ID()); - if (ExpandMacros.isMacro(r)) { - //handled by macro expander - return body; - } + if (ExpandMacros.isMacro(r)) { + // handled by macro expander + return body; + } - AddSortInjections inj = new AddSortInjections(mod); + AddSortInjections inj = new AddSortInjections(mod); - Sort s = inj.topSort(body); + Sort s = inj.topSort(body); - K k = KApply(KLabel("sideEffect:" + s.toString()), KApply(KLabel("#logToFile"), - KToken(StringUtil.enquoteKString(files.resolveKompiled("coverage.txt").getAbsolutePath()), Sorts.String()), - KToken(StringUtil.enquoteKString(id + '\n'), Sorts.String())), right); + K k = + KApply( + KLabel("sideEffect:" + s.toString()), + KApply( + KLabel("#logToFile"), + KToken( + StringUtil.enquoteKString( + files.resolveKompiled("coverage.txt").getAbsolutePath()), + Sorts.String()), + KToken(StringUtil.enquoteKString(id + '\n'), Sorts.String())), + right); - return KRewrite(left, k); - } + return KRewrite(left, k); + } } diff --git a/kernel/src/main/java/org/kframework/compile/GenerateSentencesFromConfigDecl.java b/kernel/src/main/java/org/kframework/compile/GenerateSentencesFromConfigDecl.java index 8b7273f7c87..3a675ead82d 100644 --- a/kernel/src/main/java/org/kframework/compile/GenerateSentencesFromConfigDecl.java +++ b/kernel/src/main/java/org/kframework/compile/GenerateSentencesFromConfigDecl.java @@ -1,7 +1,16 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + import com.google.common.collect.Lists; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.kframework.Collections; import org.kframework.attributes.Att; import org.kframework.attributes.Location; @@ -24,620 +33,808 @@ import scala.Tuple4; import scala.collection.Set; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.kframework.Collections.*; -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; - -/** - * Created by dwightguth on 3/27/15. - */ +/** Created by dwightguth on 3/27/15. */ public class GenerateSentencesFromConfigDecl { - /** - * Takes a configuration declaration and returns the sentences that it desugars into. - * - * Cells of multiplicity 1 desugar into an initializer production, an initializer rule, and a cell production. - * Cells of multiplicity * desugar into an initializer production, an initializer rule, a cell production, and a bag - * sort to represent a bag of those cells. - * Cells of multiplicity ? desugar into an initializer production, an initializer rule, a cell production, and an - * empty production indicating the absence of that cell. - * Cells with children additionally generate a *CellFragment sort with the same arity as the cell production, - * but the arguments made optional by generating additional sorts. - * Cells which have parents and are not multiplicity * generate a CellOpt sort which is a supersort of the cell sort - * and has an additional production name like {@code -absent}. (For a cell with multiplicitly ? this is - * necessary to distinguish a fragment that did capture the state of the cell when it wasn't present, from - * a cell fragment that didn't even try to capture the cell). - * - * Currently the implementation does not handle initializer rules; we will address this eventually. - * @param body The body of the configuration declaration. - * @param ensures The ensures clause of the configuration declaration. - * @param att The attributes of the configuration declaration. - * @param m The module the configuration declaration exists in. - * @return A set of sentences representing the configuration declaration. - */ - public static Set gen(K body, K ensures, Att att, Module m) { - return genInternal(body, ensures, att, m)._1(); - } - - /** - * Recurses over a cell and computes all the sentences corresponding to its children, and then generates - * the sentences for itself. - * @param term The term to be processed. Can be either a cell, in which case it processes that cell, - * a list of cells, in which case it processes each of those cells, or a noncell, in which case - * its parent is treated as a leaf cell. - * @param ensures The ensures clause from the configuration declaration. This is appended to the initializer of - * the top cell, but not any other cells. The algorithm assumes this will be null if and only if - * it is not the top cell of a configuration declaration. - * @param cfgAtt The attributes of the configuration declaration. Appended to all cell productions generated. - * @param m The module the configuration declaration is in. Used to get the sort of leaf cells. - * @return A tuple of the sentences generated, a list of the sorts of the children of the cell, and the body of the initializer. - */ - private static Tuple4, List, K, Boolean> genInternal(K term, K ensures, Att cfgAtt, Module m) { - if (term instanceof KApply kapp) { - if (kapp.klabel().name().equals("#configCell")) { - // is a single cell - if (kapp.klist().size() == 4) { - K startLabel = kapp.klist().items().get(0); - K endLabel = kapp.klist().items().get(3); - if (startLabel.equals(endLabel)) { - if (startLabel instanceof KToken label) { - if (label.sort().equals(Sort("#CellName"))) { - String cellName = label.s(); - Att cellProperties = getCellPropertiesAsAtt(kapp.klist().items().get(1), cellName, ensures); - Multiplicity multiplicity = convertStringMultiplicity( - cellProperties.getOption(Att.MULTIPLICITY()), term); - boolean isStream = cellProperties.getOption(Att.STREAM()).isDefined(); - - K cellContents = kapp.klist().items().get(2); - Att att = cfgAtt; - if (kapp.att().contains(Location.class)) - att = cfgAtt.add(Location.class, kapp.att().get(Location.class)); - Tuple4, List, K, Boolean> childResult = genInternal( - cellContents, null, att, m); - - boolean isLeafCell = childResult._4(); - Tuple4, Sort, K, Boolean> myResult = computeSentencesOfWellFormedCell(isLeafCell, isStream, multiplicity, att, m, cellName, cellProperties, - childResult._2(), childResult._3(), ensures, hasConfigOrRegularVariable(cellContents, m)); - return Tuple4.apply((Set)childResult._1().$bar(myResult._1()), Lists.newArrayList(myResult._2()), myResult._3(), false); - } - } - } - } - throw KEMException.compilerError("Malformed cell in configuration declaration.", term); - } else if (kapp.klabel().name().equals("#externalCell")) { - if (kapp.klist().size() == 1) { - K startLabel = kapp.klist().items().get(0); - if (startLabel instanceof KToken label) { - if (label.sort().equals(Sort("#CellName"))) { - String cellName = label.s(); - Sort sort = Sort(getSortOfCell(cellName)); - Option> initializerProduction = m.productionsFor().get(KLabel(getInitLabel(sort))); - if (initializerProduction.isDefined()) { - Set realProds = stream(initializerProduction.get()) - .filter(p -> !p.att().contains(Att.RECORD_PRD(), Production.class)) - .collect(Collections.toSet()); - if (realProds.size() == 1) { // should be only a single initializer - if (realProds.head().items().size() == 1) { - // XCell ::= "initXCell" - return Tuple4.apply(Set(), Lists.newArrayList(sort), KApply(KLabel(getInitLabel(sort))), true); - } else if (realProds.head().items().size() == 4) { - // XCell ::= "initXCell" "(" Map ")" - return Tuple4.apply(Set(), Lists.newArrayList(sort), KApply(KLabel(getInitLabel(sort)), INIT), true); - } - } - } - } - } - } - throw KEMException.compilerError("Malformed external cell in configuration declaration.", term); - } else if (KLabels.CELLS.equals(kapp.klabel())) { - //is a cell bag, and thus represents the multiple children of its parent cell - if (ensures != null) { - //top level cell, therefore, should be the children of the generatedTop cell - KToken cellLabel = KToken(KLabels.GENERATED_TOP_CELL_NAME, Sort("#CellName")); - K generatedTop = KApply(KLabel("#configCell"), cellLabel, KApply(KLabel("#cellPropertyListTerminator")), term, cellLabel); - return genInternal(generatedTop, ensures, cfgAtt, m); - } - List cells = Assoc.flatten(kapp.klabel(), kapp.klist().items(), m); - Set accumSentences = Set(); - List sorts = Lists.newArrayList(); - List initializers = Lists.newArrayList(); - for (K cell : cells) { - //for each cell, generate the child and inform the parent of the children it contains - Tuple4, List, K, Boolean> childResult = genInternal(cell, null, cfgAtt, m); - accumSentences = (Set)accumSentences.$bar(childResult._1()); - sorts.addAll(childResult._2()); - initializers.add(childResult._3()); + /** + * Takes a configuration declaration and returns the sentences that it desugars into. + * + *

Cells of multiplicity 1 desugar into an initializer production, an initializer rule, and a + * cell production. Cells of multiplicity * desugar into an initializer production, an initializer + * rule, a cell production, and a bag sort to represent a bag of those cells. Cells of + * multiplicity ? desugar into an initializer production, an initializer rule, a cell production, + * and an empty production indicating the absence of that cell. Cells with children additionally + * generate a *CellFragment sort with the same arity as the cell production, but the arguments + * made optional by generating additional sorts. Cells which have parents and are not multiplicity + * * generate a CellOpt sort which is a supersort of the cell sort and has an additional + * production name like {@code -absent}. (For a cell with multiplicitly ? this is necessary + * to distinguish a fragment that did capture the state of the cell when it wasn't present, from a + * cell fragment that didn't even try to capture the cell). + * + *

Currently the implementation does not handle initializer rules; we will address this + * eventually. + * + * @param body The body of the configuration declaration. + * @param ensures The ensures clause of the configuration declaration. + * @param att The attributes of the configuration declaration. + * @param m The module the configuration declaration exists in. + * @return A set of sentences representing the configuration declaration. + */ + public static Set gen(K body, K ensures, Att att, Module m) { + return genInternal(body, ensures, att, m)._1(); + } + + /** + * Recurses over a cell and computes all the sentences corresponding to its children, and then + * generates the sentences for itself. + * + * @param term The term to be processed. Can be either a cell, in which case it processes that + * cell, a list of cells, in which case it processes each of those cells, or a noncell, in + * which case its parent is treated as a leaf cell. + * @param ensures The ensures clause from the configuration declaration. This is appended to the + * initializer of the top cell, but not any other cells. The algorithm assumes this will be + * null if and only if it is not the top cell of a configuration declaration. + * @param cfgAtt The attributes of the configuration declaration. Appended to all cell productions + * generated. + * @param m The module the configuration declaration is in. Used to get the sort of leaf cells. + * @return A tuple of the sentences generated, a list of the sorts of the children of the cell, + * and the body of the initializer. + */ + private static Tuple4, List, K, Boolean> genInternal( + K term, K ensures, Att cfgAtt, Module m) { + if (term instanceof KApply kapp) { + if (kapp.klabel().name().equals("#configCell")) { + // is a single cell + if (kapp.klist().size() == 4) { + K startLabel = kapp.klist().items().get(0); + K endLabel = kapp.klist().items().get(3); + if (startLabel.equals(endLabel)) { + if (startLabel instanceof KToken label) { + if (label.sort().equals(Sort("#CellName"))) { + String cellName = label.s(); + Att cellProperties = + getCellPropertiesAsAtt(kapp.klist().items().get(1), cellName, ensures); + Multiplicity multiplicity = + convertStringMultiplicity(cellProperties.getOption(Att.MULTIPLICITY()), term); + boolean isStream = cellProperties.getOption(Att.STREAM()).isDefined(); + + K cellContents = kapp.klist().items().get(2); + Att att = cfgAtt; + if (kapp.att().contains(Location.class)) + att = cfgAtt.add(Location.class, kapp.att().get(Location.class)); + Tuple4, List, K, Boolean> childResult = + genInternal(cellContents, null, att, m); + + boolean isLeafCell = childResult._4(); + Tuple4, Sort, K, Boolean> myResult = + computeSentencesOfWellFormedCell( + isLeafCell, + isStream, + multiplicity, + att, + m, + cellName, + cellProperties, + childResult._2(), + childResult._3(), + ensures, + hasConfigOrRegularVariable(cellContents, m)); + return Tuple4.apply( + (Set) childResult._1().$bar(myResult._1()), + Lists.newArrayList(myResult._2()), + myResult._3(), + false); + } + } + } + } + throw KEMException.compilerError("Malformed cell in configuration declaration.", term); + } else if (kapp.klabel().name().equals("#externalCell")) { + if (kapp.klist().size() == 1) { + K startLabel = kapp.klist().items().get(0); + if (startLabel instanceof KToken label) { + if (label.sort().equals(Sort("#CellName"))) { + String cellName = label.s(); + Sort sort = Sort(getSortOfCell(cellName)); + Option> initializerProduction = + m.productionsFor().get(KLabel(getInitLabel(sort))); + if (initializerProduction.isDefined()) { + Set realProds = + stream(initializerProduction.get()) + .filter(p -> !p.att().contains(Att.RECORD_PRD(), Production.class)) + .collect(Collections.toSet()); + if (realProds.size() == 1) { // should be only a single initializer + if (realProds.head().items().size() == 1) { + // XCell ::= "initXCell" + return Tuple4.apply( + Set(), Lists.newArrayList(sort), KApply(KLabel(getInitLabel(sort))), true); + } else if (realProds.head().items().size() == 4) { + // XCell ::= "initXCell" "(" Map ")" + return Tuple4.apply( + Set(), + Lists.newArrayList(sort), + KApply(KLabel(getInitLabel(sort)), INIT), + true); + } } - return Tuple4.apply(accumSentences, sorts, KApply(KLabels.CELLS, immutable(initializers)), false); + } } - //TODO: call generic getSort method of some kind - // child of a leaf cell. Generate no productions, but inform parent that it has a child of a particular sort. - // A leaf cell initializes to the value specified in the configuration declaration. - Sort sort = kapp.att().get(Production.class).sort(); - Tuple2> res = getLeafInitializer(term, m); - return Tuple4.apply(res._2(), Lists.newArrayList(sort), res._1(), true); - } else if (term instanceof KToken ktoken) { - // child of a leaf cell. Generate no productions, but inform parent that it has a child of a particular sort. - // A leaf cell initializes to the value specified in the configuration declaration. - Tuple2> res = getLeafInitializer(term, m); - return Tuple4.apply(res._2(), Lists.newArrayList(ktoken.sort()), res._1(), true); - } else if (term instanceof KSequence || term instanceof KVariable || term instanceof InjectedKLabel) { - // child of a leaf cell. Generate no productions, but inform parent that it has a child of a particular sort. - // A leaf cell initializes to the value specified in the configuration declaration. - Tuple2> res = getLeafInitializer(term, m); - return Tuple4.apply(res._2(), Lists.newArrayList(Sorts.K()), res._1(), true); - } else { - throw KEMException.compilerError("Unexpected value found in configuration declaration, expected KToken, KSequence, or KApply", term); + } + } + throw KEMException.compilerError( + "Malformed external cell in configuration declaration.", term); + } else if (KLabels.CELLS.equals(kapp.klabel())) { + // is a cell bag, and thus represents the multiple children of its parent cell + if (ensures != null) { + // top level cell, therefore, should be the children of the generatedTop cell + KToken cellLabel = KToken(KLabels.GENERATED_TOP_CELL_NAME, Sort("#CellName")); + K generatedTop = + KApply( + KLabel("#configCell"), + cellLabel, + KApply(KLabel("#cellPropertyListTerminator")), + term, + cellLabel); + return genInternal(generatedTop, ensures, cfgAtt, m); } + List cells = Assoc.flatten(kapp.klabel(), kapp.klist().items(), m); + Set accumSentences = Set(); + List sorts = Lists.newArrayList(); + List initializers = Lists.newArrayList(); + for (K cell : cells) { + // for each cell, generate the child and inform the parent of the children it contains + Tuple4, List, K, Boolean> childResult = + genInternal(cell, null, cfgAtt, m); + accumSentences = (Set) accumSentences.$bar(childResult._1()); + sorts.addAll(childResult._2()); + initializers.add(childResult._3()); + } + return Tuple4.apply( + accumSentences, sorts, KApply(KLabels.CELLS, immutable(initializers)), false); + } + // TODO: call generic getSort method of some kind + // child of a leaf cell. Generate no productions, but inform parent that it has a child of a + // particular sort. + // A leaf cell initializes to the value specified in the configuration declaration. + Sort sort = kapp.att().get(Production.class).sort(); + Tuple2> res = getLeafInitializer(term, m); + return Tuple4.apply(res._2(), Lists.newArrayList(sort), res._1(), true); + } else if (term instanceof KToken ktoken) { + // child of a leaf cell. Generate no productions, but inform parent that it has a child of a + // particular sort. + // A leaf cell initializes to the value specified in the configuration declaration. + Tuple2> res = getLeafInitializer(term, m); + return Tuple4.apply(res._2(), Lists.newArrayList(ktoken.sort()), res._1(), true); + } else if (term instanceof KSequence + || term instanceof KVariable + || term instanceof InjectedKLabel) { + // child of a leaf cell. Generate no productions, but inform parent that it has a child of a + // particular sort. + // A leaf cell initializes to the value specified in the configuration declaration. + Tuple2> res = getLeafInitializer(term, m); + return Tuple4.apply(res._2(), Lists.newArrayList(Sorts.K()), res._1(), true); + } else { + throw KEMException.compilerError( + "Unexpected value found in configuration declaration, expected KToken, KSequence, or" + + " KApply", + term); } - - public static String getInitLabel(Sort sort) { - return "init" + sort.toString(); + } + + public static String getInitLabel(Sort sort) { + return "init" + sort.toString(); + } + + /** + * Returns true if the specified term has a configuration or regular variable + * + * @param contents + */ + private static boolean hasConfigOrRegularVariable(K contents, Module m) { + FindConfigOrRegularVar visitor = new FindConfigOrRegularVar(m); + visitor.apply(contents); + return visitor.hasConfigVar; + } + + private static class FindConfigOrRegularVar extends VisitK { + + private final Module m; + boolean hasConfigVar; + + public FindConfigOrRegularVar(Module m) { + this.m = m; } - /** - * Returns true if the specified term has a configuration or regular variable - * @param contents - */ - private static boolean hasConfigOrRegularVariable(K contents, Module m) { - FindConfigOrRegularVar visitor = new FindConfigOrRegularVar(m); - visitor.apply(contents); - return visitor.hasConfigVar; + @Override + public void apply(KToken k) { + if (k.sort().equals(Sorts.KConfigVar())) { + hasConfigVar = true; + } } - private static class FindConfigOrRegularVar extends VisitK { - - private final Module m; - boolean hasConfigVar; - - public FindConfigOrRegularVar(Module m) { - this.m = m; - } - - @Override - public void apply(KToken k) { - if (k.sort().equals(Sorts.KConfigVar())) { - hasConfigVar = true; - } - } - - @Override - public void apply(KApply kapp) { - if (kapp.klabel().name().equals("#externalCell")) { - if (kapp.klist().size() == 1) { - K startLabel = kapp.klist().items().get(0); - if (startLabel instanceof KToken label) { - if (label.sort().equals(Sort("#CellName"))) { - String cellName = label.s(); - Sort sort = Sort(getSortOfCell(cellName)); - Option> initializerProduction = m.productionsFor().get(KLabel(getInitLabel(sort))); - if (initializerProduction.isDefined()) { - Set realProds = stream(initializerProduction.get()) - .filter(p -> !p.att().contains(Att.RECORD_PRD(), Production.class)) - .collect(Collections.toSet()); - if (realProds.size() == 1) { // should be only a single initializer - if (realProds.head().items().size() == 4) { - hasConfigVar = true; - } - } - } - } - } + @Override + public void apply(KApply kapp) { + if (kapp.klabel().name().equals("#externalCell")) { + if (kapp.klist().size() == 1) { + K startLabel = kapp.klist().items().get(0); + if (startLabel instanceof KToken label) { + if (label.sort().equals(Sort("#CellName"))) { + String cellName = label.s(); + Sort sort = Sort(getSortOfCell(cellName)); + Option> initializerProduction = + m.productionsFor().get(KLabel(getInitLabel(sort))); + if (initializerProduction.isDefined()) { + Set realProds = + stream(initializerProduction.get()) + .filter(p -> !p.att().contains(Att.RECORD_PRD(), Production.class)) + .collect(Collections.toSet()); + if (realProds.size() == 1) { // should be only a single initializer + if (realProds.head().items().size() == 4) { + hasConfigVar = true; + } } + } } - super.apply(kapp); - } - - @Override - public void apply(KVariable k) { - hasConfigVar = true; + } } + } + super.apply(kapp); } - /** - * Returns the body of an initializer for a leaf cell: replaces any configuration variables - * with map lookups in the initialization map. - * @param leafContents - * @return - */ - private static Tuple2> getLeafInitializer(K leafContents, Module m) { - class Holder { - Set sentences = Set(); - } - Holder h = new Holder(); - return Tuple2.apply(new TransformK() { - private Sort sort; - - @Override - public K apply(KApply k) { - if (k.klabel().name().startsWith("#SemanticCastTo")) { - sort = k.att().get(Production.class).sort(); - } - return super.apply(k); + @Override + public void apply(KVariable k) { + hasConfigVar = true; + } + } + + /** + * Returns the body of an initializer for a leaf cell: replaces any configuration variables with + * map lookups in the initialization map. + * + * @param leafContents + * @return + */ + private static Tuple2> getLeafInitializer(K leafContents, Module m) { + class Holder { + Set sentences = Set(); + } + Holder h = new Holder(); + return Tuple2.apply( + new TransformK() { + private Sort sort; + + @Override + public K apply(KApply k) { + if (k.klabel().name().startsWith("#SemanticCastTo")) { + sort = k.att().get(Production.class).sort(); } + return super.apply(k); + } - @Override - public K apply(KToken k) { - if (k.sort().equals(Sorts.KConfigVar())) { - if (sort == null || sort.equals(Sorts.K())) { - return KApply(GenerateSortProjections.getProjectLbl(Sorts.KItem(), m), KApply(KLabel("Map:lookup"), INIT, k)); - } else { - return KApply(GenerateSortProjections.getProjectLbl(sort, m), KApply(KLabel("Map:lookup"), INIT, k)); - } - } - return k; + @Override + public K apply(KToken k) { + if (k.sort().equals(Sorts.KConfigVar())) { + if (sort == null || sort.equals(Sorts.K())) { + return KApply( + GenerateSortProjections.getProjectLbl(Sorts.KItem(), m), + KApply(KLabel("Map:lookup"), INIT, k)); + } else { + return KApply( + GenerateSortProjections.getProjectLbl(sort, m), + KApply(KLabel("Map:lookup"), INIT, k)); + } } - }.apply(leafContents), h.sentences); + return k; + } + }.apply(leafContents), + h.sentences); + } + + private static final KVariable INIT = KVariable("Init", Att.empty().add(Sort.class, Sorts.Map())); + + /** + * Generates the sentences associated with a particular cell. + * + *

As a special case, cells with the maincell attribute (usually just the {@code } cell) are + * generated with contents of sort K, rather than a narrower sort calculated from the contents. + * + * @param isLeaf true if this cell has no child cells. + * @param isStream true if this cell has a stream attribute. + * @param multiplicity The multiplicity of the cell + * @param configAtt The attributes on the configuration declaration. + * @param m The module containing the configuration. + * @param cellName The name of the cell being generated. + * @param cellProperties The attributes on the configuration cell (<cell + * foo="bar"></cell> + * @param childSorts The list of sorts computed via recursion of the children of the current cell. + * @param childInitializer The contents of the cell being processed, converted into the right hand + * side of an initializer. + * @param ensures The ensures clause to be used; null if the cell is not a top cell. + * @param hasConfigurationOrRegularVariable true if the initializer for the cell requires a + * configuration variable or a regular variable (when refering directly to configuration + * initializers). This causes cells of multiplicity * or ? to be initialized to a non-empty + * bag, and the initializer to take a Map argument containing the values of the configuration + * variables. + * @return A tuple containing the sentences associated with the cell, the sort of the cell, and + * the term to be used to initialize this cell in the initializer of its parent cell. + */ + private static Tuple4, Sort, K, Boolean> computeSentencesOfWellFormedCell( + boolean isLeaf, + boolean isStream, + Multiplicity multiplicity, + Att configAtt, + Module m, + String cellName, + Att cellProperties, + List childSorts, + K childInitializer, + K ensures, + boolean hasConfigurationOrRegularVariable) { + String sortName = getSortOfCell(cellName); + Sort sort = Sort(sortName); + + if (cellProperties.contains(Att.MAINCELL())) { + assert isLeaf; + assert childSorts.size() == 1; + childSorts = Lists.newArrayList(Sorts.K()); } - private static final KVariable INIT = KVariable("Init", Att.empty().add(Sort.class, Sorts.Map())); - - /** - * Generates the sentences associated with a particular cell. - * - * As a special case, cells with the maincell attribute (usually just the {@code } cell) - * are generated with contents of sort K, rather than a narrower sort calculated from the contents. - * @param isLeaf true if this cell has no child cells. - * @param isStream true if this cell has a stream attribute. - * @param multiplicity The multiplicity of the cell - * @param configAtt The attributes on the configuration declaration. - * @param m The module containing the configuration. - * @param cellName The name of the cell being generated. - * @param cellProperties The attributes on the configuration cell (<cell foo="bar"></cell> - * @param childSorts The list of sorts computed via recursion of the children of the current cell. - * @param childInitializer The contents of the cell being processed, converted into the right hand side of an initializer. - * @param ensures The ensures clause to be used; null if the cell is not a top cell. - * @param hasConfigurationOrRegularVariable true if the initializer for the cell requires a configuration variable or a - * regular variable (when refering directly to configuration initializers). - * This causes cells of multiplicity * or ? to be initialized to a non-empty bag, - * and the initializer to take a Map argument containing the values of the configuration - * variables. - * @return A tuple containing the sentences associated with the cell, the sort of the cell, and the term to be used to initialize - * this cell in the initializer of its parent cell. - */ - private static Tuple4, Sort, K, Boolean> computeSentencesOfWellFormedCell( - boolean isLeaf, - boolean isStream, - Multiplicity multiplicity, - Att configAtt, - Module m, - String cellName, - Att cellProperties, - List childSorts, - K childInitializer, - K ensures, - boolean hasConfigurationOrRegularVariable) { - String sortName = getSortOfCell(cellName); - Sort sort = Sort(sortName); - - if (cellProperties.contains(Att.MAINCELL())) { - assert isLeaf; - assert childSorts.size() == 1; - childSorts = Lists.newArrayList(Sorts.K()); - } - - List items = Stream.concat(Stream.concat(Stream.of( - Terminal("<" + cellName + ">")), childSorts.stream() - .map(Constructors::NonTerminal)), Stream.of(Terminal(""))) - .collect(Collectors.toList()); - - java.util.Set sentences = new HashSet(); - - String klabel = "<" + cellName + ">"; - Att att = cellProperties.addAll(configAtt); - - StringBuilder format = new StringBuilder(); - if (!cellProperties.contains(Att.FORMAT())) { - format.append("%1%i"); - int i; - for (i = 2; i < 2 + childSorts.size(); i++) { - format.append("%n%").append(i); - } - format.append("%d%n%").append(i); - att = att.add(Att.FORMAT(), format.toString()); - } - - // syntax Cell ::= "" Children... "" [cell, cellProperties, configDeclAttributes] - if(!m.definedKLabels().contains(KLabel(klabel)) && multiplicity != Multiplicity.OPTIONAL) { - Production cellProduction = Production(KLabel(klabel), sort, immutable(items), att); - sentences.add(cellProduction); - } + List items = + Stream.concat( + Stream.concat( + Stream.of(Terminal("<" + cellName + ">")), + childSorts.stream().map(Constructors::NonTerminal)), + Stream.of(Terminal(""))) + .collect(Collectors.toList()); + + java.util.Set sentences = new HashSet(); + + String klabel = "<" + cellName + ">"; + Att att = cellProperties.addAll(configAtt); + + StringBuilder format = new StringBuilder(); + if (!cellProperties.contains(Att.FORMAT())) { + format.append("%1%i"); + int i; + for (i = 2; i < 2 + childSorts.size(); i++) { + format.append("%n%").append(i); + } + format.append("%d%n%").append(i); + att = att.add(Att.FORMAT(), format.toString()); + } - // syntax Cell ::= "initCell" [initializer, function] - // -or- - // syntax Cell ::= initCell(Map) [initializer, function] - String initLabel = getInitLabel(sort); - Sentence initializer; - Rule initializerRule; - Sort initSort = sort; - - if (multiplicity == Multiplicity.STAR) { - String type = cellProperties.getOptional(Att.TYPE()).orElse("Bag"); - initSort = Sort(sortName + type); - } + // syntax Cell ::= "" Children... "" [cell, cellProperties, configDeclAttributes] + if (!m.definedKLabels().contains(KLabel(klabel)) && multiplicity != Multiplicity.OPTIONAL) { + Production cellProduction = Production(KLabel(klabel), sort, immutable(items), att); + sentences.add(cellProduction); + } - if (hasConfigurationOrRegularVariable || isStream) { - initializer = Production(KLabel(initLabel), initSort, Seq(Terminal(initLabel), Terminal("("), NonTerminal(Sorts.Map()), Terminal(")")), Att().add(Att.INITIALIZER()).add(Att.FUNCTION())); - initializerRule = Rule(KRewrite(KApply(KLabel(initLabel), INIT), IncompleteCellUtils.make(KLabel("<" + cellName + ">"), false, childInitializer, false)), BooleanUtils.TRUE, ensures == null ? BooleanUtils.TRUE : ensures, Att().add(Att.INITIALIZER())); - } else { - initializer = Production(KLabel(initLabel), initSort, Seq(Terminal(initLabel)), Att().add(Att.INITIALIZER()).add(Att.FUNCTION())); - initializerRule = Rule(KRewrite(KApply(KLabel(initLabel)), IncompleteCellUtils.make(KLabel("<" + cellName + ">"), false, childInitializer, false)), BooleanUtils.TRUE, ensures == null ? BooleanUtils.TRUE : ensures, Att().add(Att.INITIALIZER())); - } - if (!m.definedKLabels().contains(KLabel(initLabel))) { - sentences.add(initializer); - } - sentences.add(initializerRule); - - if (!isLeaf) { - // syntax CellFragment ::= -fragment Child1CellOpt Child2CellOpt ... ChildNCellOpt -fragment [cellFragment(Cell)] - // syntax Child1CellOpt[cellFragmentOpt(Child1Cell)] ::= Child1Cell | "noChild1Cell"[cellFragmentOptAbsent] - // ... - // syntax ChildNCellOpt[cellFragmentOpt(ChildNCell)] ::= ChildNCell | "noChildNCell"[cellFragmentOptAbsent] - - // The "CellOpt" sorts are added for cells of multiplicitly other than * to allow representing fragments - // that didn't try to capture the corresponding cell, from a cell fragment variable written next to - // an explicit pattern for some cells. - // We don't need to add those sorts for cells of multiplicitly *, because explicit patterns in the - // context of a cell fragment variable can never be sure to capture all copies of such a cell. - Sort fragmentSort = Sort(sortName+"Fragment"); - List fragmentItems = new ArrayList(2+childSorts.size()); - fragmentItems.add(Terminal("<"+cellName+">-fragment")); - for (Sort childSort : childSorts) { - if (!childSort.name().endsWith("Cell")) { - // child was a multiplicity * List/Bag/Set - fragmentItems.add(NonTerminal(childSort)); - } else { - Sort childOptSort = Sort(childSort.name()+"Opt", childSort.params()); - fragmentItems.add(NonTerminal(childOptSort)); - - sentences.add(Production(Seq(), childOptSort, List(NonTerminal(childSort)))); - if (!m.definedKLabels().contains(KLabel("no" + childSort))) { - sentences.add(Production(KLabel("no" + childSort), childOptSort, List(Terminal("no" + childSort)), - Att().add(Att.CELL_OPT_ABSENT(),Sort.class,childSort))); - } - } - } - fragmentItems.add(Terminal("-fragment")); - if (!m.definedKLabels().contains(KLabel("<" + cellName + ">-fragment"))) { - sentences.add(Production(KLabel("<" + cellName + ">-fragment"), fragmentSort, immutable(fragmentItems), - Att().add(Att.CELL_FRAGMENT(), Sort.class, Sort(sortName)))); - } - } + // syntax Cell ::= "initCell" [initializer, function] + // -or- + // syntax Cell ::= initCell(Map) [initializer, function] + String initLabel = getInitLabel(sort); + Sentence initializer; + Rule initializerRule; + Sort initSort = sort; + + if (multiplicity == Multiplicity.STAR) { + String type = cellProperties.getOptional(Att.TYPE()).orElse("Bag"); + initSort = Sort(sortName + type); + } - Sort cellsSort; - K rhs; - if (multiplicity == Multiplicity.STAR) { - // syntax CellBag [hook(BAG.Bag)] - // syntax CellBag ::= Cell - // syntax CellBag ::= ".CellBag" [hook(BAG.unit), function] - // syntax CellBag ::= CellBagItem(Cell) [hook(BAG.element), function] - // syntax CellBag ::= CellBag CellBag [assoc, comm, unit(.CellBag), element(CellBagItem), wrapElement(), hook(BAG.concat), avoid, function] - // -or- - // syntax CellSet [hook(SET.Set)] - // syntax CellSet ::= Cell - // syntax CellSet ::= ".CellSet" [hook(SET.unit), function] - // syntax CellSet ::= CellSetItem(Cell) [hook(SET.element), function] - // syntax CellSet ::= CellSet CellSet [assoc, comm, idem, unit(.CellSet), element(CellSetItem), wrapElement(), hook(SET.concat), avoid, function] - // -or- - // syntax CellMap [hook(MAP.Map)] - // syntax CellMap ::= Cell - // syntax CellMap ::= ".CellMap" [hook(MAP.unit), function] - // syntax CellMap ::= CellMapItem(KeyCell, Cell) [hook(MAP.element), function] - // syntax CellMap ::= CellMap CellMap [assoc, comm, unit(.CellMap), element(CellMapItem), wrapElement(), hook(MAP.concat), avoid, function] - // -or- - // syntax CellList [hook(LIST.List)] - // syntax CellList ::= Cell - // syntax CellList ::= ".CellList" [hook(LIST.unit), function] - // syntax CellList ::= CellListItem(Cell) [hook(LIST.element), function] - // syntax CellList ::= CellList CellList [assoc, unit(.CellList), element(CellListItem), wrapElement(), hook(LIST.concat), avoid, function] - String type = cellProperties.getOptional(Att.TYPE()).orElse("Bag"); - Sort bagSort = Sort(sortName + type); - Att bagAtt = Att() - .add(Att.ASSOC(), "") - .add(Att.CELL_COLLECTION()) - .add(Att.ELEMENT(), bagSort.name() + "Item") - .add(Att.WRAP_ELEMENT(), "<" + cellName + ">") - .add(Att.UNIT(), "." + bagSort.name()) - .add(Att.HOOK(), type.toUpperCase() + ".concat") - .add(Att.AVOID()) // needed to ensure cell collections are parsed as Bag instead of CellBag - .add(Att.FUNCTION()); - String unitHook = type.toUpperCase() + ".unit", elementHook = type.toUpperCase() + ".element"; - switch(type) { - case "Set": - bagAtt = bagAtt.add(Att.IDEM(), ""); - // fall through - case "Map": - bagAtt = bagAtt.add(Att.COMM(), ""); - break; - case "Bag": - bagAtt = bagAtt.add(Att.COMM(), "").add(Att.BAG()); - break; - case "List": - break; - default: - throw KEMException.compilerError("Unexpected type for multiplicity * cell: " + cellName - + ". Should be one of: Set, Bag, List, Map", KApply(KLabel("#EmptyK"), Seq(), configAtt)); - } - SyntaxSort sortDecl = SyntaxSort(Seq(), bagSort, Att().add(Att.HOOK(), type.toUpperCase() + '.' + type).add(Att.CELL_COLLECTION())); - Sentence bagSubsort = Production(Seq(), bagSort, Seq(NonTerminal(sort))); - Sentence bagElement; - if (type.equals("Map")) { - if (childSorts.isEmpty()) { - throw KEMException.compilerError("Cells of type Map expect at least one child cell as their key", - KApply(KLabel("#EmptyK"), Seq(), configAtt)); - } - bagElement = Production(KLabel(bagSort.name() + "Item"), bagSort, Seq( - Terminal(bagSort.name() + "Item"), - Terminal("("), - NonTerminal(childSorts.get(0)), - Terminal(","), - NonTerminal(sort), - Terminal(")")), Att().add(Att.HOOK(), elementHook).add(Att.FUNCTION()).add(Att.FORMAT(), "%5")); - } else { - bagElement = Production(KLabel(bagSort.name() + "Item"), bagSort, Seq( - Terminal(bagSort.name() + "Item"), - Terminal("("), - NonTerminal(sort), - Terminal(")")), Att().add(Att.HOOK(), elementHook).add(Att.FUNCTION()).add(Att.FORMAT(), "%3")); - } - Sentence bagUnit = Production(KLabel("." + bagSort.name()), bagSort, Seq(Terminal("." + bagSort.name())), Att().add(Att.HOOK(), unitHook).add(Att.FUNCTION())); - Sentence bag = Production(KLabel("_" + bagSort + "_"), bagSort, Seq(NonTerminal(bagSort), NonTerminal(bagSort)), - bagAtt); - sentences.add(sortDecl); - sentences.add(bagSubsort); - sentences.add(bagElement); - sentences.add(bagUnit); - sentences.add(bag); - if (type.equals("Map")) { - // syntax Bool ::= KeyCell "in_keys" "(" CellMap ")" [function, total, hook(MAP.in_keys)] - sentences.add(Production(KLabel(bagSort.name() + ":in_keys"), Sorts.Bool(), Seq(NonTerminal(childSorts.get(0)), Terminal("in_keys"), Terminal("("), NonTerminal(bagSort), Terminal(")")), Att().add(Att.HOOK(), "MAP.in_keys").add(Att.FUNCTION()).add(Att.TOTAL()))); - - // syntax KeyCell ::= CellMapKey(Cell) [function, total] - // rule CellMapKey( K ...<\cell>) => K - KLabel cellMapKeyLabel = KLabel(bagSort.name() + "Key"); - Production cellMapKeyProduction = Production(cellMapKeyLabel, childSorts.get(0), Seq(Terminal(bagSort.name() + "Key"), Terminal("("), NonTerminal(sort), Terminal(")")), Att().add(Att.FUNCTION()).add(Att.TOTAL())); - KVariable key = KVariable("Key", Att.empty().add(Sort.class, childSorts.get(0))); - Rule cellMapKeyRule = Rule(KRewrite(KApply(cellMapKeyLabel, IncompleteCellUtils.make(KLabel(klabel), false, key, true)), key), BooleanUtils.TRUE, BooleanUtils.TRUE); - sentences.add(cellMapKeyProduction); - sentences.add(cellMapKeyRule); - } - // rule initCell => .CellBag - // -or- - // rule initCell(Init) => Context[$var] - cellsSort = bagSort; - rhs = optionalCellInitializer(hasConfigurationOrRegularVariable, cellProperties, initLabel); - } else if (multiplicity == Multiplicity.OPTIONAL) { - // syntax Cell ::= ".Cell" - Production cellUnit = Production(KLabel("." + sortName), sort, Seq(Terminal("." + sortName))); - sentences.add(cellUnit); - // add UNIT attribute to cell production. - if(!m.definedKLabels().contains(KLabel(klabel))) { - Production cellProduction = Production(KLabel(klabel), sort, immutable(items), att.add(Att.UNIT(), cellUnit.klabel().get().name())); - sentences.add(cellProduction); - } - // rule initCell => .CellBag - // -or- - // rule initCell(Init) => Context[$var] - cellsSort = sort; - rhs = optionalCellInitializer(hasConfigurationOrRegularVariable, cellProperties, initLabel); + if (hasConfigurationOrRegularVariable || isStream) { + initializer = + Production( + KLabel(initLabel), + initSort, + Seq(Terminal(initLabel), Terminal("("), NonTerminal(Sorts.Map()), Terminal(")")), + Att().add(Att.INITIALIZER()).add(Att.FUNCTION())); + initializerRule = + Rule( + KRewrite( + KApply(KLabel(initLabel), INIT), + IncompleteCellUtils.make( + KLabel("<" + cellName + ">"), false, childInitializer, false)), + BooleanUtils.TRUE, + ensures == null ? BooleanUtils.TRUE : ensures, + Att().add(Att.INITIALIZER())); + } else { + initializer = + Production( + KLabel(initLabel), + initSort, + Seq(Terminal(initLabel)), + Att().add(Att.INITIALIZER()).add(Att.FUNCTION())); + initializerRule = + Rule( + KRewrite( + KApply(KLabel(initLabel)), + IncompleteCellUtils.make( + KLabel("<" + cellName + ">"), false, childInitializer, false)), + BooleanUtils.TRUE, + ensures == null ? BooleanUtils.TRUE : ensures, + Att().add(Att.INITIALIZER())); + } + if (!m.definedKLabels().contains(KLabel(initLabel))) { + sentences.add(initializer); + } + sentences.add(initializerRule); + + if (!isLeaf) { + // syntax CellFragment ::= -fragment Child1CellOpt Child2CellOpt ... ChildNCellOpt + // -fragment [cellFragment(Cell)] + // syntax Child1CellOpt[cellFragmentOpt(Child1Cell)] ::= Child1Cell | + // "noChild1Cell"[cellFragmentOptAbsent] + // ... + // syntax ChildNCellOpt[cellFragmentOpt(ChildNCell)] ::= ChildNCell | + // "noChildNCell"[cellFragmentOptAbsent] + + // The "CellOpt" sorts are added for cells of multiplicitly other than * to allow representing + // fragments + // that didn't try to capture the corresponding cell, from a cell fragment variable written + // next to + // an explicit pattern for some cells. + // We don't need to add those sorts for cells of multiplicitly *, because explicit patterns in + // the + // context of a cell fragment variable can never be sure to capture all copies of such a cell. + Sort fragmentSort = Sort(sortName + "Fragment"); + List fragmentItems = new ArrayList(2 + childSorts.size()); + fragmentItems.add(Terminal("<" + cellName + ">-fragment")); + for (Sort childSort : childSorts) { + if (!childSort.name().endsWith("Cell")) { + // child was a multiplicity * List/Bag/Set + fragmentItems.add(NonTerminal(childSort)); } else { - // rule initCell => initChildren... - // -or- - // rule initCell(Init) => initChildren(Init)... - cellsSort = sort; - if (hasConfigurationOrRegularVariable || isStream) { - rhs = KApply(KLabel(initLabel), INIT); - } else { - rhs = KApply(KLabel(initLabel)); - } + Sort childOptSort = Sort(childSort.name() + "Opt", childSort.params()); + fragmentItems.add(NonTerminal(childOptSort)); + + sentences.add(Production(Seq(), childOptSort, List(NonTerminal(childSort)))); + if (!m.definedKLabels().contains(KLabel("no" + childSort))) { + sentences.add( + Production( + KLabel("no" + childSort), + childOptSort, + List(Terminal("no" + childSort)), + Att().add(Att.CELL_OPT_ABSENT(), Sort.class, childSort))); + } } - - if (cellProperties.contains(Att.EXIT())) { - KLabel getExitCodeLabel = KLabel("getExitCode"); - Production getExitCode = Production(getExitCodeLabel, Sorts.Int(), Seq(Terminal("getExitCode"), Terminal("("), NonTerminal(Sorts.GeneratedTopCell()), Terminal(")")), Att.empty().add(Att.FUNCTION())); - sentences.add(getExitCode); - KVariable var = KVariable("Exit", Att.empty().add(Sort.class, Sorts.Int())); - Rule getExitCodeRule = Rule(KRewrite(KApply(getExitCodeLabel, IncompleteCellUtils.make(KLabels.GENERATED_TOP_CELL, true, IncompleteCellUtils.make(KLabel(klabel), false, var, false), true)), var), BooleanUtils.TRUE, BooleanUtils.TRUE); - sentences.add(SyntaxSort.apply(Seq(), Sorts.GeneratedTopCell(), Att.empty())); - sentences.add(getExitCodeRule); - } - return Tuple4.apply(immutable(sentences),cellsSort,rhs, false); + } + fragmentItems.add(Terminal("-fragment")); + if (!m.definedKLabels().contains(KLabel("<" + cellName + ">-fragment"))) { + sentences.add( + Production( + KLabel("<" + cellName + ">-fragment"), + fragmentSort, + immutable(fragmentItems), + Att().add(Att.CELL_FRAGMENT(), Sort.class, Sort(sortName)))); + } } - /** - * Returns the term used to initialize an optinoal cell. An optional cell is initialized to the empty bag if - * it contains no configuration variables, and to a single cell if it contains configuration variables. - */ - private static KApply optionalCellInitializer(boolean initializeOptionalCell, Att cellProperties, String initLabel) { - if (initializeOptionalCell) { - return KApply(KLabel(initLabel), INIT); - } else if (cellProperties.contains(Att.INITIAL())) { - return KApply(KLabel(initLabel)); - } else { - return KApply(KLabels.CELLS); + Sort cellsSort; + K rhs; + if (multiplicity == Multiplicity.STAR) { + // syntax CellBag [hook(BAG.Bag)] + // syntax CellBag ::= Cell + // syntax CellBag ::= ".CellBag" [hook(BAG.unit), function] + // syntax CellBag ::= CellBagItem(Cell) [hook(BAG.element), function] + // syntax CellBag ::= CellBag CellBag [assoc, comm, unit(.CellBag), element(CellBagItem), + // wrapElement(), hook(BAG.concat), avoid, function] + // -or- + // syntax CellSet [hook(SET.Set)] + // syntax CellSet ::= Cell + // syntax CellSet ::= ".CellSet" [hook(SET.unit), function] + // syntax CellSet ::= CellSetItem(Cell) [hook(SET.element), function] + // syntax CellSet ::= CellSet CellSet [assoc, comm, idem, unit(.CellSet), + // element(CellSetItem), wrapElement(), hook(SET.concat), avoid, function] + // -or- + // syntax CellMap [hook(MAP.Map)] + // syntax CellMap ::= Cell + // syntax CellMap ::= ".CellMap" [hook(MAP.unit), function] + // syntax CellMap ::= CellMapItem(KeyCell, Cell) [hook(MAP.element), function] + // syntax CellMap ::= CellMap CellMap [assoc, comm, unit(.CellMap), element(CellMapItem), + // wrapElement(), hook(MAP.concat), avoid, function] + // -or- + // syntax CellList [hook(LIST.List)] + // syntax CellList ::= Cell + // syntax CellList ::= ".CellList" [hook(LIST.unit), function] + // syntax CellList ::= CellListItem(Cell) [hook(LIST.element), function] + // syntax CellList ::= CellList CellList [assoc, unit(.CellList), element(CellListItem), + // wrapElement(), hook(LIST.concat), avoid, function] + String type = cellProperties.getOptional(Att.TYPE()).orElse("Bag"); + Sort bagSort = Sort(sortName + type); + Att bagAtt = + Att() + .add(Att.ASSOC(), "") + .add(Att.CELL_COLLECTION()) + .add(Att.ELEMENT(), bagSort.name() + "Item") + .add(Att.WRAP_ELEMENT(), "<" + cellName + ">") + .add(Att.UNIT(), "." + bagSort.name()) + .add(Att.HOOK(), type.toUpperCase() + ".concat") + .add( + Att + .AVOID()) // needed to ensure cell collections are parsed as Bag instead of + // CellBag + .add(Att.FUNCTION()); + String unitHook = type.toUpperCase() + ".unit", elementHook = type.toUpperCase() + ".element"; + switch (type) { + case "Set": + bagAtt = bagAtt.add(Att.IDEM(), ""); + // fall through + case "Map": + bagAtt = bagAtt.add(Att.COMM(), ""); + break; + case "Bag": + bagAtt = bagAtt.add(Att.COMM(), "").add(Att.BAG()); + break; + case "List": + break; + default: + throw KEMException.compilerError( + "Unexpected type for multiplicity * cell: " + + cellName + + ". Should be one of: Set, Bag, List, Map", + KApply(KLabel("#EmptyK"), Seq(), configAtt)); + } + SyntaxSort sortDecl = + SyntaxSort( + Seq(), + bagSort, + Att().add(Att.HOOK(), type.toUpperCase() + '.' + type).add(Att.CELL_COLLECTION())); + Sentence bagSubsort = Production(Seq(), bagSort, Seq(NonTerminal(sort))); + Sentence bagElement; + if (type.equals("Map")) { + if (childSorts.isEmpty()) { + throw KEMException.compilerError( + "Cells of type Map expect at least one child cell as their key", + KApply(KLabel("#EmptyK"), Seq(), configAtt)); } + bagElement = + Production( + KLabel(bagSort.name() + "Item"), + bagSort, + Seq( + Terminal(bagSort.name() + "Item"), + Terminal("("), + NonTerminal(childSorts.get(0)), + Terminal(","), + NonTerminal(sort), + Terminal(")")), + Att().add(Att.HOOK(), elementHook).add(Att.FUNCTION()).add(Att.FORMAT(), "%5")); + } else { + bagElement = + Production( + KLabel(bagSort.name() + "Item"), + bagSort, + Seq( + Terminal(bagSort.name() + "Item"), + Terminal("("), + NonTerminal(sort), + Terminal(")")), + Att().add(Att.HOOK(), elementHook).add(Att.FUNCTION()).add(Att.FORMAT(), "%3")); + } + Sentence bagUnit = + Production( + KLabel("." + bagSort.name()), + bagSort, + Seq(Terminal("." + bagSort.name())), + Att().add(Att.HOOK(), unitHook).add(Att.FUNCTION())); + Sentence bag = + Production( + KLabel("_" + bagSort + "_"), + bagSort, + Seq(NonTerminal(bagSort), NonTerminal(bagSort)), + bagAtt); + sentences.add(sortDecl); + sentences.add(bagSubsort); + sentences.add(bagElement); + sentences.add(bagUnit); + sentences.add(bag); + if (type.equals("Map")) { + // syntax Bool ::= KeyCell "in_keys" "(" CellMap ")" [function, total, hook(MAP.in_keys)] + sentences.add( + Production( + KLabel(bagSort.name() + ":in_keys"), + Sorts.Bool(), + Seq( + NonTerminal(childSorts.get(0)), + Terminal("in_keys"), + Terminal("("), + NonTerminal(bagSort), + Terminal(")")), + Att().add(Att.HOOK(), "MAP.in_keys").add(Att.FUNCTION()).add(Att.TOTAL()))); + + // syntax KeyCell ::= CellMapKey(Cell) [function, total] + // rule CellMapKey( K ...<\cell>) => K + KLabel cellMapKeyLabel = KLabel(bagSort.name() + "Key"); + Production cellMapKeyProduction = + Production( + cellMapKeyLabel, + childSorts.get(0), + Seq( + Terminal(bagSort.name() + "Key"), + Terminal("("), + NonTerminal(sort), + Terminal(")")), + Att().add(Att.FUNCTION()).add(Att.TOTAL())); + KVariable key = KVariable("Key", Att.empty().add(Sort.class, childSorts.get(0))); + Rule cellMapKeyRule = + Rule( + KRewrite( + KApply( + cellMapKeyLabel, + IncompleteCellUtils.make(KLabel(klabel), false, key, true)), + key), + BooleanUtils.TRUE, + BooleanUtils.TRUE); + sentences.add(cellMapKeyProduction); + sentences.add(cellMapKeyRule); + } + // rule initCell => .CellBag + // -or- + // rule initCell(Init) => Context[$var] + cellsSort = bagSort; + rhs = optionalCellInitializer(hasConfigurationOrRegularVariable, cellProperties, initLabel); + } else if (multiplicity == Multiplicity.OPTIONAL) { + // syntax Cell ::= ".Cell" + Production cellUnit = Production(KLabel("." + sortName), sort, Seq(Terminal("." + sortName))); + sentences.add(cellUnit); + // add UNIT attribute to cell production. + if (!m.definedKLabels().contains(KLabel(klabel))) { + Production cellProduction = + Production( + KLabel(klabel), + sort, + immutable(items), + att.add(Att.UNIT(), cellUnit.klabel().get().name())); + sentences.add(cellProduction); + } + // rule initCell => .CellBag + // -or- + // rule initCell(Init) => Context[$var] + cellsSort = sort; + rhs = optionalCellInitializer(hasConfigurationOrRegularVariable, cellProperties, initLabel); + } else { + // rule initCell => initChildren... + // -or- + // rule initCell(Init) => initChildren(Init)... + cellsSort = sort; + if (hasConfigurationOrRegularVariable || isStream) { + rhs = KApply(KLabel(initLabel), INIT); + } else { + rhs = KApply(KLabel(initLabel)); + } } - private static Att getCellPropertiesAsAtt(K k, String cellName, K ensures) { - Att att = Att(); - if (cellName.equals("k")) { - att = att.add(Att.MAINCELL()); - } - att = att.add(Att.CELL()).add(Att.CELL_NAME(), cellName); - return att.addAll(getCellPropertiesAsAtt(k)); + if (cellProperties.contains(Att.EXIT())) { + KLabel getExitCodeLabel = KLabel("getExitCode"); + Production getExitCode = + Production( + getExitCodeLabel, + Sorts.Int(), + Seq( + Terminal("getExitCode"), + Terminal("("), + NonTerminal(Sorts.GeneratedTopCell()), + Terminal(")")), + Att.empty().add(Att.FUNCTION())); + sentences.add(getExitCode); + KVariable var = KVariable("Exit", Att.empty().add(Sort.class, Sorts.Int())); + Rule getExitCodeRule = + Rule( + KRewrite( + KApply( + getExitCodeLabel, + IncompleteCellUtils.make( + KLabels.GENERATED_TOP_CELL, + true, + IncompleteCellUtils.make(KLabel(klabel), false, var, false), + true)), + var), + BooleanUtils.TRUE, + BooleanUtils.TRUE); + sentences.add(SyntaxSort.apply(Seq(), Sorts.GeneratedTopCell(), Att.empty())); + sentences.add(getExitCodeRule); + } + return Tuple4.apply(immutable(sentences), cellsSort, rhs, false); + } + + /** + * Returns the term used to initialize an optinoal cell. An optional cell is initialized to the + * empty bag if it contains no configuration variables, and to a single cell if it contains + * configuration variables. + */ + private static KApply optionalCellInitializer( + boolean initializeOptionalCell, Att cellProperties, String initLabel) { + if (initializeOptionalCell) { + return KApply(KLabel(initLabel), INIT); + } else if (cellProperties.contains(Att.INITIAL())) { + return KApply(KLabel(initLabel)); + } else { + return KApply(KLabels.CELLS); } + } - private static Att getCellPropertiesAsAtt(K k) { - if (k instanceof KApply kapp) { - if (kapp.klabel().name().equals("#cellPropertyListTerminator")) { - return Att(); - } else if (kapp.klabel().name().equals("#cellPropertyList")) { - if (kapp.klist().size() == 2) { - Tuple2 attribute = getCellProperty(kapp.klist().items().get(0)); - return ProcessGroupAttributes.getProcessedAtt( - Att().add(attribute._1(), attribute._2()) - .addAll(getCellPropertiesAsAtt(kapp.klist().items().get(1))), - k); - } - } + private static Att getCellPropertiesAsAtt(K k, String cellName, K ensures) { + Att att = Att(); + if (cellName.equals("k")) { + att = att.add(Att.MAINCELL()); + } + att = att.add(Att.CELL()).add(Att.CELL_NAME(), cellName); + return att.addAll(getCellPropertiesAsAtt(k)); + } + + private static Att getCellPropertiesAsAtt(K k) { + if (k instanceof KApply kapp) { + if (kapp.klabel().name().equals("#cellPropertyListTerminator")) { + return Att(); + } else if (kapp.klabel().name().equals("#cellPropertyList")) { + if (kapp.klist().size() == 2) { + Tuple2 attribute = getCellProperty(kapp.klist().items().get(0)); + return ProcessGroupAttributes.getProcessedAtt( + Att() + .add(attribute._1(), attribute._2()) + .addAll(getCellPropertiesAsAtt(kapp.klist().items().get(1))), + k); } - throw KEMException.compilerError("Malformed cell properties", k); + } } - - private static Tuple2 getCellProperty(K k) { - if (k instanceof KApply kapp) { - if (kapp.klabel().name().equals("#cellProperty")) { - if (kapp.klist().size() == 2) { - if (kapp.klist().items().get(0) instanceof KToken keyToken) { - if (keyToken.sort().equals(Sort("#CellName"))) { - Att.Key key = Att.getBuiltinKeyOptional(keyToken.s()) - .orElseThrow(() -> - KEMException.compilerError("Unrecognized attribute: " + keyToken.s() + - "\nHint: User-defined groups can be added with the group=\"...\" attribute.", k)); - if (kapp.klist().items().get(0) instanceof KToken) { - KToken valueToken = (KToken) kapp.klist().items().get(1); - if (valueToken.sort().equals(Sorts.KString())) { - String value = StringUtil.unquoteKString(valueToken.s()); - return Tuple2.apply(key, value); - } - } - } - } + throw KEMException.compilerError("Malformed cell properties", k); + } + + private static Tuple2 getCellProperty(K k) { + if (k instanceof KApply kapp) { + if (kapp.klabel().name().equals("#cellProperty")) { + if (kapp.klist().size() == 2) { + if (kapp.klist().items().get(0) instanceof KToken keyToken) { + if (keyToken.sort().equals(Sort("#CellName"))) { + Att.Key key = + Att.getBuiltinKeyOptional(keyToken.s()) + .orElseThrow( + () -> + KEMException.compilerError( + "Unrecognized attribute: " + + keyToken.s() + + "\n" + + "Hint: User-defined groups can be added with the" + + " group=\"...\" attribute.", + k)); + if (kapp.klist().items().get(0) instanceof KToken) { + KToken valueToken = (KToken) kapp.klist().items().get(1); + if (valueToken.sort().equals(Sorts.KString())) { + String value = StringUtil.unquoteKString(valueToken.s()); + return Tuple2.apply(key, value); } + } } + } } - throw KEMException.compilerError("Malformed cell property", k); + } } - - public static String getSortOfCell(String cellName) { - char[] chars = cellName.toCharArray(); - StringBuilder sb = new StringBuilder(); - sb.append(Character.toUpperCase(chars[0])); - for (int i = 1; i < chars.length; i++) { - if (chars[i] == '-' && i + 1 < chars.length && Character.isLowerCase(chars[i+1])) { - chars[i+1] = Character.toUpperCase(chars[i+1]); - } else if (chars[i] != '-') { - sb.append(chars[i]); - } - } - sb.append("Cell"); - return sb.toString(); + throw KEMException.compilerError("Malformed cell property", k); + } + + public static String getSortOfCell(String cellName) { + char[] chars = cellName.toCharArray(); + StringBuilder sb = new StringBuilder(); + sb.append(Character.toUpperCase(chars[0])); + for (int i = 1; i < chars.length; i++) { + if (chars[i] == '-' && i + 1 < chars.length && Character.isLowerCase(chars[i + 1])) { + chars[i + 1] = Character.toUpperCase(chars[i + 1]); + } else if (chars[i] != '-') { + sb.append(chars[i]); + } } - - private static Multiplicity convertStringMultiplicity(Option multiplicity, K body) { - if (multiplicity.isEmpty()) - return Multiplicity.ONE; - try { - return Multiplicity.of(multiplicity.get()); - } catch (IllegalArgumentException x) { - throw KEMException.compilerError("Invalid multiplicity found in cell: " + multiplicity.get(), body); - } + sb.append("Cell"); + return sb.toString(); + } + + private static Multiplicity convertStringMultiplicity(Option multiplicity, K body) { + if (multiplicity.isEmpty()) return Multiplicity.ONE; + try { + return Multiplicity.of(multiplicity.get()); + } catch (IllegalArgumentException x) { + throw KEMException.compilerError( + "Invalid multiplicity found in cell: " + multiplicity.get(), body); } + } } diff --git a/kernel/src/main/java/org/kframework/compile/GenerateSortPredicateRules.java b/kernel/src/main/java/org/kframework/compile/GenerateSortPredicateRules.java index 925f63ec8c8..ed6558f8c01 100644 --- a/kernel/src/main/java/org/kframework/compile/GenerateSortPredicateRules.java +++ b/kernel/src/main/java/org/kframework/compile/GenerateSortPredicateRules.java @@ -1,17 +1,20 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; -import org.apache.commons.lang3.mutable.MutableBoolean; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; import org.kframework.Collections; import org.kframework.attributes.Att; import org.kframework.builtin.BooleanUtils; import org.kframework.builtin.Sorts; import org.kframework.definition.Module; -import org.kframework.definition.NonTerminal; -import org.kframework.definition.Production; import org.kframework.definition.Rule; import org.kframework.definition.Sentence; -import org.kframework.kore.K; import org.kframework.kore.KApply; import org.kframework.kore.KLabel; import org.kframework.kore.KRewrite; @@ -19,43 +22,48 @@ import org.kframework.kore.Sort; import scala.collection.Set; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.kframework.Collections.*; -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; - /** - * Generates sort predicates from the subsort hierarchy of the module. This module assumes that the backend implements - * the following rules: - * isSort(#token(Sort, _)) => true - * isK(K) => true - * isKItem(K1(K2)) => true - * isKItem(#token(_, _)) => true + * Generates sort predicates from the subsort hierarchy of the module. This module assumes that the + * backend implements the following rules: isSort(#token(Sort, _)) => true isK(K) => true + * isKItem(K1(K2)) => true isKItem(#token(_, _)) => true * - * plus one sort membership function for each builtin-hooked sort. + *

plus one sort membership function for each builtin-hooked sort. */ public class GenerateSortPredicateRules { - public Module gen(Module mod) { - return Module(mod.name(), mod.imports(), (Set) mod.localSentences().$bar(stream(mod.allSorts()) - .flatMap(this::gen).collect(Collections.toSet())), mod.att()); - } + public Module gen(Module mod) { + return Module( + mod.name(), + mod.imports(), + (Set) + mod.localSentences() + .$bar(stream(mod.allSorts()).flatMap(this::gen).collect(Collections.toSet())), + mod.att()); + } - private Stream gen(Sort sort) { - if (sort.equals(Sorts.K())) { - return Stream.of(Rule(KRewrite(KApply(KLabel("is" + sort), KVariable("K")), BooleanUtils.TRUE), BooleanUtils.TRUE, BooleanUtils.TRUE)); - } else { - List res = new ArrayList<>(); - res.add(Rule(KRewrite(KApply(KLabel("is" + sort), KVariable(sort.name(), Att().add(Sort.class, sort))), BooleanUtils.TRUE), BooleanUtils.TRUE, BooleanUtils.TRUE)); - res.add(Rule(KRewrite(KApply(KLabel("is" + sort), KVariable("K")), BooleanUtils.FALSE), BooleanUtils.TRUE, BooleanUtils.TRUE, Att().add(Att.OWISE()))); - return res.stream(); - } + private Stream gen(Sort sort) { + if (sort.equals(Sorts.K())) { + return Stream.of( + Rule( + KRewrite(KApply(KLabel("is" + sort), KVariable("K")), BooleanUtils.TRUE), + BooleanUtils.TRUE, + BooleanUtils.TRUE)); + } else { + List res = new ArrayList<>(); + res.add( + Rule( + KRewrite( + KApply(KLabel("is" + sort), KVariable(sort.name(), Att().add(Sort.class, sort))), + BooleanUtils.TRUE), + BooleanUtils.TRUE, + BooleanUtils.TRUE)); + res.add( + Rule( + KRewrite(KApply(KLabel("is" + sort), KVariable("K")), BooleanUtils.FALSE), + BooleanUtils.TRUE, + BooleanUtils.TRUE, + Att().add(Att.OWISE()))); + return res.stream(); } - + } } diff --git a/kernel/src/main/java/org/kframework/compile/GenerateSortPredicateSyntax.java b/kernel/src/main/java/org/kframework/compile/GenerateSortPredicateSyntax.java index bbc405f8de1..d213025a2ba 100644 --- a/kernel/src/main/java/org/kframework/compile/GenerateSortPredicateSyntax.java +++ b/kernel/src/main/java/org/kframework/compile/GenerateSortPredicateSyntax.java @@ -1,6 +1,13 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; import org.kframework.attributes.Att; import org.kframework.builtin.Sorts; import org.kframework.definition.Module; @@ -8,36 +15,32 @@ import org.kframework.definition.Sentence; import org.kframework.kore.Sort; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import static org.kframework.kore.KORE.*; -import static org.kframework.Collections.*; -import static org.kframework.definition.Constructors.*; - -/** - * Created by dwightguth on 5/28/15. - */ +/** Created by dwightguth on 5/28/15. */ public class GenerateSortPredicateSyntax { - public Module gen(Module mod) { - Set res = new HashSet<>(); - for (Sort sort : iterable(mod.allSorts())) { - res.addAll(gen(mod, sort)); - } - if (!res.isEmpty()) { - res.add(SyntaxSort(Seq(), Sorts.K())); - } - return Module(mod.name(), mod.imports(), (scala.collection.Set) mod.localSentences().$bar(immutable(res)), mod.att()); + public Module gen(Module mod) { + Set res = new HashSet<>(); + for (Sort sort : iterable(mod.allSorts())) { + res.addAll(gen(mod, sort)); } - - public Set gen(Module mod, Sort sort) { - Production prod = Production(KLabel("is" + sort.toString()), Sorts.Bool(), - Seq(Terminal("is" + sort), Terminal("("), NonTerminal(Sorts.K()), Terminal(")")), - Att().add(Att.FUNCTION()).add(Att.TOTAL()).add(Att.PREDICATE(), Sort.class, sort)); - if (!mod.productions().contains(prod)) - return Collections.singleton(prod); - return Collections.emptySet(); + if (!res.isEmpty()) { + res.add(SyntaxSort(Seq(), Sorts.K())); } + return Module( + mod.name(), + mod.imports(), + (scala.collection.Set) mod.localSentences().$bar(immutable(res)), + mod.att()); + } + + public Set gen(Module mod, Sort sort) { + Production prod = + Production( + KLabel("is" + sort.toString()), + Sorts.Bool(), + Seq(Terminal("is" + sort), Terminal("("), NonTerminal(Sorts.K()), Terminal(")")), + Att().add(Att.FUNCTION()).add(Att.TOTAL()).add(Att.PREDICATE(), Sort.class, sort)); + if (!mod.productions().contains(prod)) return Collections.singleton(prod); + return Collections.emptySet(); + } } diff --git a/kernel/src/main/java/org/kframework/compile/GenerateSortProjections.java b/kernel/src/main/java/org/kframework/compile/GenerateSortProjections.java index 89ff228a0e0..6a0c8f6bbca 100644 --- a/kernel/src/main/java/org/kframework/compile/GenerateSortProjections.java +++ b/kernel/src/main/java/org/kframework/compile/GenerateSortProjections.java @@ -1,8 +1,16 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; -import org.kframework.attributes.Att; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.stream.Stream; import org.kframework.Collections; +import org.kframework.attributes.Att; import org.kframework.builtin.BooleanUtils; import org.kframework.builtin.Sorts; import org.kframework.definition.Module; @@ -17,94 +25,137 @@ import org.kframework.parser.inner.RuleGrammarGenerator; import scala.collection.Set; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.stream.Stream; +public class GenerateSortProjections { -import static org.kframework.Collections.*; -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; + private Module mod; + private final boolean cover; -public class GenerateSortProjections { + public GenerateSortProjections(boolean cover) { + this.cover = cover; + } - private Module mod; - private final boolean cover; + public GenerateSortProjections(Module mod) { + this.mod = mod; + this.cover = false; + } - public GenerateSortProjections(boolean cover) { - this.cover = cover; - } + public Module gen(Module mod) { + this.mod = mod; + return Module( + mod.name(), + mod.imports(), + (Set) + mod.localSentences() + .$bar( + Stream.concat( + stream(mod.allSorts()).flatMap(this::gen), + stream(mod.localProductions()).flatMap(this::gen)) + .collect(Collections.toSet())), + mod.att()); + } - public GenerateSortProjections(Module mod) { - this.mod = mod; - this.cover = false; - } + public static KLabel getProjectLbl(Sort sort, Module m) { + KLabel lbl; + lbl = KLabel("project:" + sort.toString()); + return lbl; + } - public Module gen(Module mod) { - this.mod = mod; - return Module(mod.name(), mod.imports(), (Set) mod.localSentences().$bar( - Stream.concat(stream(mod.allSorts()).flatMap(this::gen), - stream(mod.localProductions()).flatMap(this::gen)).collect(Collections.toSet())), mod.att()); - } + public static KLabel getProjectLbl(String klabel, String name) { + return KLabel("project:" + klabel + ":" + name); + } - public static KLabel getProjectLbl(Sort sort, Module m) { - KLabel lbl; - lbl = KLabel("project:" + sort.toString()); - return lbl; + public Stream gen(Sort sort) { + if (RuleGrammarGenerator.isParserSort(sort) + && !sort.equals(Sorts.KItem()) + && !sort.equals(Sorts.K())) { + return Stream.empty(); } - - public static KLabel getProjectLbl(String klabel, String name) { - return KLabel("project:" + klabel + ":" + name); + KLabel lbl = getProjectLbl(sort, mod); + KVariable var = KVariable("K", Att.empty().add(Sort.class, sort)); + Rule r = + Rule( + KRewrite(KApply(lbl, var), var), + BooleanUtils.TRUE, + BooleanUtils.TRUE, + Att().add(Att.PROJECTION())); + if (mod.definedKLabels().contains(lbl)) { + return Stream.empty(); } - - public Stream gen(Sort sort) { - if (RuleGrammarGenerator.isParserSort(sort) && !sort.equals(Sorts.KItem()) && !sort.equals(Sorts.K())) { - return Stream.empty(); - } - KLabel lbl = getProjectLbl(sort, mod); - KVariable var = KVariable("K", Att.empty().add(Sort.class, sort)); - Rule r = Rule(KRewrite(KApply(lbl, var), var), BooleanUtils.TRUE, BooleanUtils.TRUE, Att().add(Att.PROJECTION())); - if (mod.definedKLabels().contains(lbl)) { - return Stream.empty(); - } - Production prod = Production(lbl, sort, Seq(Terminal(lbl.name()), Terminal("("), NonTerminal(Sorts.K()), Terminal(")")), Att().add(Att.FUNCTION()).add(Att.PROJECTION())); - if (cover) { - KLabel sideEffectLbl = KLabel("sideEffect:" + sort); - Production sideEffect = Production(sideEffectLbl, sort, Seq(Terminal(sideEffectLbl.name()), Terminal("("), NonTerminal(Sorts.K()), Terminal(","), NonTerminal(sort), Terminal(")")), Att().add(Att.FUNCTION())); - Rule sideEffectR = Rule(KRewrite(KApply(sideEffectLbl, KVariable("K2", Att.empty().add(Sort.class, Sorts.K())), var), var), BooleanUtils.TRUE, BooleanUtils.TRUE); - return stream(Set(prod, r, sideEffect, sideEffectR)); - } else { - return stream(Set(prod, r)); - } + Production prod = + Production( + lbl, + sort, + Seq(Terminal(lbl.name()), Terminal("("), NonTerminal(Sorts.K()), Terminal(")")), + Att().add(Att.FUNCTION()).add(Att.PROJECTION())); + if (cover) { + KLabel sideEffectLbl = KLabel("sideEffect:" + sort); + Production sideEffect = + Production( + sideEffectLbl, + sort, + Seq( + Terminal(sideEffectLbl.name()), + Terminal("("), + NonTerminal(Sorts.K()), + Terminal(","), + NonTerminal(sort), + Terminal(")")), + Att().add(Att.FUNCTION())); + Rule sideEffectR = + Rule( + KRewrite( + KApply( + sideEffectLbl, KVariable("K2", Att.empty().add(Sort.class, Sorts.K())), var), + var), + BooleanUtils.TRUE, + BooleanUtils.TRUE); + return stream(Set(prod, r, sideEffect, sideEffectR)); + } else { + return stream(Set(prod, r)); } + } - public Stream gen(Production prod) { - if (prod.att().contains(Att.FUNCTION()) || (prod.klabel().isDefined() && mod.macroKLabels().contains(prod.klabel().get()))) { - return Stream.empty(); - } - java.util.Set sentences = new HashSet<>(); - List vars = new ArrayList<>(); - int i = 0; - boolean hasName = false; - for (NonTerminal nt : iterable(prod.nonterminals())) { - vars.add(KVariable("K" + i++, Att.empty().add(Sort.class, nt.sort()))); - hasName = hasName || nt.name().isDefined(); - } - if (!hasName) { - return Stream.empty(); - } - i = 0; - for (NonTerminal nt : iterable(prod.nonterminals())) { - if (nt.name().isDefined()) { - KLabel lbl = getProjectLbl(prod.klabel().get().name(), nt.name().get()); - if (mod.definedKLabels().contains(lbl)) { - return Stream.empty(); - } - sentences.add(Production(lbl, nt.sort(), Seq(Terminal(nt.name().get()), Terminal("("), NonTerminal(prod.sort()), Terminal(")")), Att().add(Att.FUNCTION()))); - sentences.add(Rule(KRewrite(KApply(lbl, KApply(prod.klabel().get(), KList(vars))), vars.get(i)), BooleanUtils.TRUE, BooleanUtils.TRUE)); + public Stream gen(Production prod) { + if (prod.att().contains(Att.FUNCTION()) + || (prod.klabel().isDefined() && mod.macroKLabels().contains(prod.klabel().get()))) { + return Stream.empty(); + } + java.util.Set sentences = new HashSet<>(); + List vars = new ArrayList<>(); + int i = 0; + boolean hasName = false; + for (NonTerminal nt : iterable(prod.nonterminals())) { + vars.add(KVariable("K" + i++, Att.empty().add(Sort.class, nt.sort()))); + hasName = hasName || nt.name().isDefined(); + } + if (!hasName) { + return Stream.empty(); + } + i = 0; + for (NonTerminal nt : iterable(prod.nonterminals())) { + if (nt.name().isDefined()) { + KLabel lbl = getProjectLbl(prod.klabel().get().name(), nt.name().get()); + if (mod.definedKLabels().contains(lbl)) { + return Stream.empty(); } - i++; + sentences.add( + Production( + lbl, + nt.sort(), + Seq( + Terminal(nt.name().get()), + Terminal("("), + NonTerminal(prod.sort()), + Terminal(")")), + Att().add(Att.FUNCTION()))); + sentences.add( + Rule( + KRewrite(KApply(lbl, KApply(prod.klabel().get(), KList(vars))), vars.get(i)), + BooleanUtils.TRUE, + BooleanUtils.TRUE)); } - return sentences.stream(); + i++; } + return sentences.stream(); + } } diff --git a/kernel/src/main/java/org/kframework/compile/GuardOrPatterns.java b/kernel/src/main/java/org/kframework/compile/GuardOrPatterns.java index d15b075f20d..bfc2c5d0cac 100644 --- a/kernel/src/main/java/org/kframework/compile/GuardOrPatterns.java +++ b/kernel/src/main/java/org/kframework/compile/GuardOrPatterns.java @@ -1,110 +1,107 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.kore.KORE.*; + +import java.util.HashSet; +import java.util.Set; import org.kframework.attributes.Att; import org.kframework.builtin.KLabels; import org.kframework.builtin.Sorts; import org.kframework.definition.Context; import org.kframework.definition.Module; -import org.kframework.definition.Production; import org.kframework.definition.Rule; import org.kframework.definition.Sentence; import org.kframework.kore.*; -import java.util.HashSet; -import java.util.Set; +public class GuardOrPatterns { -import static org.kframework.kore.KORE.*; + private final Set vars = new HashSet<>(); -public class GuardOrPatterns { + void resetVars() { + vars.clear(); + } - private final Set vars = new HashSet<>(); + private Rule resolve(Module m, Rule rule) { + resetVars(); + gatherVars(rule.body()); + gatherVars(rule.requires()); + gatherVars(rule.ensures()); + return new Rule( + transform(rule.body(), m), + transform(rule.requires(), m), + transform(rule.ensures(), m), + rule.att()); + } - void resetVars() { - vars.clear(); - } + private Context resolve(Module m, Context context) { + resetVars(); + gatherVars(context.body()); + gatherVars(context.requires()); + return new Context( + transform(context.body(), m), transform(context.requires(), m), context.att()); + } - private Rule resolve(Module m, Rule rule) { - resetVars(); - gatherVars(rule.body()); - gatherVars(rule.requires()); - gatherVars(rule.ensures()); - return new Rule( - transform(rule.body(), m), - transform(rule.requires(), m), - transform(rule.ensures(), m), - rule.att()); - } + public K resolveK(Module m, K k) { + resetVars(); + gatherVars(k); + return transform(k, m); + } - private Context resolve(Module m, Context context) { - resetVars(); - gatherVars(context.body()); - gatherVars(context.requires()); - return new Context( - transform(context.body(), m), - transform(context.requires(), m), - context.att()); + public synchronized Sentence resolve(Module m, Sentence s) { + if (s instanceof Rule) { + return resolve(m, (Rule) s); + } else if (s instanceof Context) { + return resolve(m, (Context) s); + } else { + return s; } + } - public K resolveK(Module m, K k) { - resetVars(); - gatherVars(k); - return transform(k, m); - } + void gatherVars(K term) { + new VisitK() { + @Override + public void apply(KVariable v) { + vars.add(v); + super.apply(v); + } + }.apply(term); + } - public synchronized Sentence resolve(Module m, Sentence s) { - if (s instanceof Rule) { - return resolve(m, (Rule) s); - } else if (s instanceof Context) { - return resolve(m, (Context) s); - } else { - return s; + K transform(K term, Module m) { + return new TransformK() { + @Override + public K apply(KApply k) { + if (k.klabel().head().equals(KLabels.ML_OR)) { + AddSortInjections inj = new AddSortInjections(m); + return KAs(k, newDotVariable(inj.sort(k, null))); } - } + return super.apply(k); + } - void gatherVars(K term) { - new VisitK() { - @Override - public void apply(KVariable v) { - vars.add(v); - super.apply(v); - } - }.apply(term); - } - - K transform(K term, Module m) { - return new TransformK() { - @Override - public K apply(KApply k) { - if (k.klabel().head().equals(KLabels.ML_OR)) { - AddSortInjections inj = new AddSortInjections(m); - return KAs(k, newDotVariable(inj.sort(k, null))); - } - return super.apply(k); - } + @Override + public K apply(KAs k) { + return k; + } - @Override - public K apply(KAs k) { - return k; - } + @Override + public K apply(KRewrite k) { + return k; + } + }.apply(term); + } - @Override - public K apply(KRewrite k) { - return k; - } - }.apply(term); - } + private int counter = 0; - private int counter = 0; - KVariable newDotVariable(Sort s) { - if (s == null) { - s = Sorts.K(); - } - KVariable newLabel; - do { - newLabel = KVariable("_Gen" + (counter++), Att().add(Att.ANONYMOUS()).add(Sort.class, s)); - } while (vars.contains(newLabel)); - vars.add(newLabel); - return newLabel; + KVariable newDotVariable(Sort s) { + if (s == null) { + s = Sorts.K(); } + KVariable newLabel; + do { + newLabel = KVariable("_Gen" + (counter++), Att().add(Att.ANONYMOUS()).add(Sort.class, s)); + } while (vars.contains(newLabel)); + vars.add(newLabel); + return newLabel; + } } diff --git a/kernel/src/main/java/org/kframework/compile/IncompleteCellUtils.java b/kernel/src/main/java/org/kframework/compile/IncompleteCellUtils.java index 3a93e84bca5..2827257d0d3 100644 --- a/kernel/src/main/java/org/kframework/compile/IncompleteCellUtils.java +++ b/kernel/src/main/java/org/kframework/compile/IncompleteCellUtils.java @@ -1,82 +1,82 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.kore.KORE.*; + +import java.util.ArrayList; +import java.util.List; import org.kframework.builtin.KLabels; import org.kframework.kore.K; import org.kframework.kore.KApply; import org.kframework.kore.KLabel; import org.kframework.utils.errorsystem.KEMException; -import java.util.ArrayList; -import java.util.List; - -import static org.kframework.kore.KORE.*; - /** - * Utility methods for dealing with cells as seen in Full K. - * These are represented in kast by applying the cell label - * to three argument - (left dots, body, right dots) - * rather than the arity declared in their production. - * dots are represented with an application #dots() or #noDots() - * Multiple cells, rewrites, or other items are joined into - * a single argument by using the label #cells - * (which this class will allow with arbitrary arity). + * Utility methods for dealing with cells as seen in Full K. These are represented in kast by + * applying the cell label to three argument - (left dots, body, right dots) rather than the arity + * declared in their production. dots are represented with an application #dots() or #noDots() + * Multiple cells, rewrites, or other items are joined into a single argument by using the label + * #cells (which this class will allow with arbitrary arity). */ public class IncompleteCellUtils { - private final static KApply dots = KApply(KLabels.DOTS); - private final static KApply noDots = KApply(KLabels.NO_DOTS); + private static final KApply dots = KApply(KLabels.DOTS); + private static final KApply noDots = KApply(KLabels.NO_DOTS); - private static boolean isOpen(K flag) { - if (dots.equals(flag)) { - return true; - } else if (noDots.equals(flag)) { - return false; - } else { - throw KEMException.criticalError("Instead of #dots() or #noDots(), got " + flag); - } + private static boolean isOpen(K flag) { + if (dots.equals(flag)) { + return true; + } else if (noDots.equals(flag)) { + return false; + } else { + throw KEMException.criticalError("Instead of #dots() or #noDots(), got " + flag); } + } - public static boolean isOpenLeft(KApply cell) { - return isOpen(cell.klist().items().get(0)); - } - public static boolean isOpenRight(KApply cell) { - return isOpen(cell.klist().items().get(2)); - } + public static boolean isOpenLeft(KApply cell) { + return isOpen(cell.klist().items().get(0)); + } - private static void flattenCells(List children, K item) { - if (item instanceof KApply && KLabels.CELLS.equals(((KApply) item).klabel())) { - for (K deeper : ((KApply) item).klist().items()) { - flattenCells(children, deeper); - } - } else { - children.add(item); - } - } - public static List flattenCells(K cells) { - List children = new ArrayList(); - flattenCells(children, cells); - return children; - } + public static boolean isOpenRight(KApply cell) { + return isOpen(cell.klist().items().get(2)); + } - public static List getChildren(KApply cell) { - return flattenCells(cell.klist().items().get(1)); + private static void flattenCells(List children, K item) { + if (item instanceof KApply && KLabels.CELLS.equals(((KApply) item).klabel())) { + for (K deeper : ((KApply) item).klist().items()) { + flattenCells(children, deeper); + } + } else { + children.add(item); } + } - private static KApply makeDots(boolean isOpen) { - return isOpen ? dots : noDots; - } - static K makeBody(List children) { - if (children.size() == 1) { - return children.get(0); - } else { - return KApply(KLabels.CELLS, KList(children)); - } - } + public static List flattenCells(K cells) { + List children = new ArrayList(); + flattenCells(children, cells); + return children; + } - public static KApply make(KLabel label, boolean openLeft, K child, boolean openRight) { - return KApply(label, KList(makeDots(openLeft), child, makeDots(openRight))); - } - public static KApply make(KLabel label, boolean openLeft, List children, boolean openRight) { - return KApply(label, KList(makeDots(openLeft), makeBody(children), makeDots(openRight))); + public static List getChildren(KApply cell) { + return flattenCells(cell.klist().items().get(1)); + } + + private static KApply makeDots(boolean isOpen) { + return isOpen ? dots : noDots; + } + + static K makeBody(List children) { + if (children.size() == 1) { + return children.get(0); + } else { + return KApply(KLabels.CELLS, KList(children)); } + } + + public static KApply make(KLabel label, boolean openLeft, K child, boolean openRight) { + return KApply(label, KList(makeDots(openLeft), child, makeDots(openRight))); + } + + public static KApply make(KLabel label, boolean openLeft, List children, boolean openRight) { + return KApply(label, KList(makeDots(openLeft), makeBody(children), makeDots(openRight))); + } } diff --git a/kernel/src/main/java/org/kframework/compile/LiftToKSequence.java b/kernel/src/main/java/org/kframework/compile/LiftToKSequence.java index c951c9508bd..143ae4a3f4d 100644 --- a/kernel/src/main/java/org/kframework/compile/LiftToKSequence.java +++ b/kernel/src/main/java/org/kframework/compile/LiftToKSequence.java @@ -1,92 +1,85 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; -import org.kframework.builtin.Sorts; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + +import java.util.ArrayList; +import java.util.List; import org.kframework.builtin.KLabels; +import org.kframework.builtin.Sorts; import org.kframework.definition.Context; import org.kframework.definition.Rule; import org.kframework.definition.Sentence; import org.kframework.kore.*; -import java.util.ArrayList; -import java.util.List; - -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; - /** * Raises all rules into a form with a KSequence in every position that expects a term of sort K. */ public class LiftToKSequence { - - public Sentence lift(Sentence s) { - if (s instanceof Rule) { - return lift((Rule) s); - } else if (s instanceof Context) { - return lift((Context) s); - } else { - return s; - } + public Sentence lift(Sentence s) { + if (s instanceof Rule) { + return lift((Rule) s); + } else if (s instanceof Context) { + return lift((Context) s); + } else { + return s; } + } - private Rule lift(Rule rule) { - return Rule( - lift(rule.body()), - lift(rule.requires()), - lift(rule.ensures()), - rule.att()); - } + private Rule lift(Rule rule) { + return Rule(lift(rule.body()), lift(rule.requires()), lift(rule.ensures()), rule.att()); + } - private Context lift(Context context) { - return Context( - lift(context.body()), - lift(context.requires()), - context.att()); - } + private Context lift(Context context) { + return Context(lift(context.body()), lift(context.requires()), context.att()); + } - public K lift(K term) { - K result = new TransformK() { - @Override - public K apply(KApply k) { - List children = new ArrayList<>(); - for (K child : k.klist().items()) { - K res = apply(child); - if (res instanceof KSequence || k.klabel().equals(KLabels.ML_OR)) { - children.add(res); - } else { - children.add(KSequence(res)); - } - } - return KApply(k.klabel(), KList(children), k.att()); + public K lift(K term) { + K result = + new TransformK() { + @Override + public K apply(KApply k) { + List children = new ArrayList<>(); + for (K child : k.klist().items()) { + K res = apply(child); + if (res instanceof KSequence || k.klabel().equals(KLabels.ML_OR)) { + children.add(res); + } else { + children.add(KSequence(res)); + } } + return KApply(k.klabel(), KList(children), k.att()); + } - @Override - public K apply(KAs k) { - K res = apply(k.pattern()); - KVariable var = (KVariable) k.alias(); - if (!(res instanceof KSequence) && var.att().getOptional(Sort.class).orElse(Sorts.K()).equals(Sorts.K())) { - res = KSequence(res); - } - return KAs(res, k.alias(), k.att()); + @Override + public K apply(KAs k) { + K res = apply(k.pattern()); + KVariable var = (KVariable) k.alias(); + if (!(res instanceof KSequence) + && var.att().getOptional(Sort.class).orElse(Sorts.K()).equals(Sorts.K())) { + res = KSequence(res); } + return KAs(res, k.alias(), k.att()); + } }.apply(term); - if (result instanceof KSequence) { - return result; - } else { - return KSequence(result); - } + if (result instanceof KSequence) { + return result; + } else { + return KSequence(result); } + } - public K lower(K term) { - return new TransformK() { - @Override - public K apply(KSequence k) { - if (k.items().size() == 1) { - return super.apply(k.items().get(0)); - } - return super.apply(k); - } - }.apply(term); - } + public K lower(K term) { + return new TransformK() { + @Override + public K apply(KSequence k) { + if (k.items().size() == 1) { + return super.apply(k.items().get(0)); + } + return super.apply(k); + } + }.apply(term); + } } diff --git a/kernel/src/main/java/org/kframework/compile/MarkExtraConcreteRules.java b/kernel/src/main/java/org/kframework/compile/MarkExtraConcreteRules.java index 1ec0c8c2607..62b144016d9 100644 --- a/kernel/src/main/java/org/kframework/compile/MarkExtraConcreteRules.java +++ b/kernel/src/main/java/org/kframework/compile/MarkExtraConcreteRules.java @@ -1,40 +1,43 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import java.util.HashSet; +import java.util.List; +import javax.annotation.Nullable; import org.kframework.attributes.Att; import org.kframework.definition.Definition; import org.kframework.definition.DefinitionTransformer; import org.kframework.definition.Rule; import org.kframework.utils.errorsystem.KEMException; -import javax.annotation.Nullable; -import java.util.HashSet; -import java.util.List; - public class MarkExtraConcreteRules { - /** - * Mark with [concrete] rules with labels enumerated in `--concrete-rules`. - */ - public static Definition mark(Definition def, @Nullable List extraConcreteRuleLabels) { - if (extraConcreteRuleLabels == null) { - return def; - } - HashSet concreteLabelsSet = new HashSet<>(extraConcreteRuleLabels); - Definition result = DefinitionTransformer.fromSentenceTransformer((mod, s) -> { - if (s instanceof Rule r) { - String label = r.att().getOption(Att.LABEL()).getOrElse(() -> null); - if (label != null && concreteLabelsSet.contains(label)) { - // rule labels must be unique, so it's safe to remove from the set as we iterate - concreteLabelsSet.remove(label); - return Rule.apply(r.body(), r.requires(), r.ensures(), r.att().add(Att.CONCRETE())); - } - } - return s; - }, "mark extra concrete rules").apply(def); - if (!concreteLabelsSet.isEmpty()) { - throw KEMException.criticalError("Unused concrete rule labels: " + concreteLabelsSet); - } - return result; + /** Mark with [concrete] rules with labels enumerated in `--concrete-rules`. */ + public static Definition mark(Definition def, @Nullable List extraConcreteRuleLabels) { + if (extraConcreteRuleLabels == null) { + return def; + } + HashSet concreteLabelsSet = new HashSet<>(extraConcreteRuleLabels); + Definition result = + DefinitionTransformer.fromSentenceTransformer( + (mod, s) -> { + if (s instanceof Rule r) { + String label = r.att().getOption(Att.LABEL()).getOrElse(() -> null); + if (label != null && concreteLabelsSet.contains(label)) { + // rule labels must be unique, so it's safe to remove from the set as we + // iterate + concreteLabelsSet.remove(label); + return Rule.apply( + r.body(), r.requires(), r.ensures(), r.att().add(Att.CONCRETE())); + } + } + return s; + }, + "mark extra concrete rules") + .apply(def); + if (!concreteLabelsSet.isEmpty()) { + throw KEMException.criticalError("Unused concrete rule labels: " + concreteLabelsSet); } + return result; + } } diff --git a/kernel/src/main/java/org/kframework/compile/MinimizeTermConstruction.java b/kernel/src/main/java/org/kframework/compile/MinimizeTermConstruction.java index 64d8aea9ec7..68088633ad5 100644 --- a/kernel/src/main/java/org/kframework/compile/MinimizeTermConstruction.java +++ b/kernel/src/main/java/org/kframework/compile/MinimizeTermConstruction.java @@ -1,211 +1,226 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.kore.KORE.*; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import org.kframework.attributes.Att; +import org.kframework.builtin.BooleanUtils; import org.kframework.builtin.KLabels; import org.kframework.builtin.Sorts; -import org.kframework.builtin.BooleanUtils; import org.kframework.definition.Context; import org.kframework.definition.Module; import org.kframework.definition.Rule; import org.kframework.definition.Sentence; import org.kframework.kore.*; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import static org.kframework.kore.KORE.*; - public class MinimizeTermConstruction { - private final Set vars = new HashSet<>(); - private final Map cache = new HashMap<>(); - private final Set usedOnRhs = new HashSet<>(); + private final Set vars = new HashSet<>(); + private final Map cache = new HashMap<>(); + private final Set usedOnRhs = new HashSet<>(); - private final Module module; - private final AddSortInjections sorts; + private final Module module; + private final AddSortInjections sorts; - public MinimizeTermConstruction(Module module) { - this.module = module; - this.sorts = new AddSortInjections(module); - } + public MinimizeTermConstruction(Module module) { + this.module = module; + this.sorts = new AddSortInjections(module); + } - void resetVars() { - vars.clear(); - cache.clear(); - usedOnRhs.clear(); - counter = 0; - } + void resetVars() { + vars.clear(); + cache.clear(); + usedOnRhs.clear(); + counter = 0; + } - private Rule resolve(Rule rule) { - if (rule.att().contains(Att.SIMPLIFICATION())) { - return rule; - } - resetVars(); - gatherVars(rule.body()); - gatherVars(rule.requires()); - gatherVars(rule.ensures()); - gatherTerms(rule.body(), true); - gatherTerms(rule.requires(), false); - gatherTerms(rule.ensures(), false); - filterTerms(rule.body(), true); - filterTerms(rule.requires(), false); - filterTerms(rule.ensures(), false); - return new Rule( - transform(rule.body(), true), - transform(rule.requires(), false), - transform(rule.ensures(), false), - rule.att()); + private Rule resolve(Rule rule) { + if (rule.att().contains(Att.SIMPLIFICATION())) { + return rule; } - - private Context resolve(Context context) { - resetVars(); - gatherVars(context.body()); - gatherVars(context.requires()); - gatherTerms(context.body(), true); - gatherTerms(context.requires(), false); - filterTerms(context.body(), true); - filterTerms(context.requires(), false); - return new Context( - transform(context.body(), true), - transform(context.requires(), false), - context.att()); + resetVars(); + gatherVars(rule.body()); + gatherVars(rule.requires()); + gatherVars(rule.ensures()); + gatherTerms(rule.body(), true); + gatherTerms(rule.requires(), false); + gatherTerms(rule.ensures(), false); + filterTerms(rule.body(), true); + filterTerms(rule.requires(), false); + filterTerms(rule.ensures(), false); + return new Rule( + transform(rule.body(), true), + transform(rule.requires(), false), + transform(rule.ensures(), false), + rule.att()); + } + + private Context resolve(Context context) { + resetVars(); + gatherVars(context.body()); + gatherVars(context.requires()); + gatherTerms(context.body(), true); + gatherTerms(context.requires(), false); + filterTerms(context.body(), true); + filterTerms(context.requires(), false); + return new Context( + transform(context.body(), true), transform(context.requires(), false), context.att()); + } + + public synchronized Sentence resolve(Sentence s) { + if (s instanceof Rule) { + return resolve((Rule) s); + } else if (s instanceof Context) { + return resolve((Context) s); + } else { + return s; } - - public synchronized Sentence resolve(Sentence s) { - if (s instanceof Rule) { - return resolve((Rule) s); - } else if (s instanceof Context) { - return resolve((Context) s); - } else { - return s; + } + + void gatherVars(K term) { + new VisitK() { + @Override + public void apply(KVariable v) { + vars.add(v); + super.apply(v); + } + }.apply(term); + } + + void gatherTerms(K term, boolean body) { + new RewriteAwareVisitor(body, new HashSet<>()) { + @Override + public void apply(K k) { + if (isLHS() + && !isRHS() + && !(k instanceof KVariable) + && !atTop + && !k.equals(BooleanUtils.TRUE)) { + cache.put(k, newDotVariable(sorts.sort(k, Sorts.K()))); } - } + atTop = false; + super.apply(k); + } - void gatherVars(K term) { - new VisitK() { - @Override - public void apply(KVariable v) { - vars.add(v); - super.apply(v); - } - }.apply(term); - } - - void gatherTerms(K term, boolean body) { - new RewriteAwareVisitor(body, new HashSet<>()) { - @Override - public void apply(K k) { - if (isLHS() && !isRHS() && !(k instanceof KVariable) && !atTop && !k.equals(BooleanUtils.TRUE)) { - cache.put(k, newDotVariable(sorts.sort(k, Sorts.K()))); - } - atTop = false; - super.apply(k); - } - - boolean atTop = false; - - @Override - public void apply(KRewrite rew) { - if (rew == term) { - atTop = true; - } - super.apply(rew); - } - - @Override - public void apply(KApply k) { - if (k.klabel().head().equals(KLabels.ML_OR)) { - return; - } - String hook = module.attributesFor().get(k.klabel()).getOrElse(() -> Att.empty()).getOptional(Att.HOOK()).orElse(""); - if (hook.equals("SET.element") - || hook.equals("LIST.element") - || hook.equals("LIST.concat") - || hook.equals("MAP.concat") - || hook.equals("SET.concat")) { - return; - } - if (hook.equals("MAP.element")) { - apply(k.items().get(1)); - return; - } - super.apply(k); - } - }.apply(term); - } + boolean atTop = false; - void filterTerms(K term, boolean body) { - new RewriteAwareVisitor(body, new HashSet<>()) { - @Override - public void apply(K k) { - if (isRHS() && !isLHS() && cache.containsKey(k)) { - usedOnRhs.add(k); - return; - } - super.apply(k); - } - }.apply(term); - } + @Override + public void apply(KRewrite rew) { + if (rew == term) { + atTop = true; + } + super.apply(rew); + } - K transform(K term, boolean body) { - return new RewriteAwareTransformer(body) { - @Override - public K apply(K k) { - if (isRHS() && !isLHS()) { - if (cache.containsKey(k)) { - return cache.get(k); - } - } - if (isLHS() && !isRHS() && !inBad) { - if (usedOnRhs.contains(k)) { - return KAs(super.apply(k), cache.get(k), Att.empty().add(Sort.class, cache.get(k).att().get(Sort.class))); - } - } - return super.apply(k); - } - - boolean inBad = false; - - @Override - public K apply(KApply k) { - boolean stack = inBad; - if (k.klabel().head().equals(KLabels.ML_OR)) { - inBad = true; - } - String hook = module.attributesFor().get(k.klabel()).getOrElse(() -> Att.empty()).getOptional(Att.HOOK()).orElse(""); - if (hook.equals("SET.element") - || hook.equals("LIST.element") - || hook.equals("LIST.concat") - || hook.equals("MAP.concat") - || hook.equals("SET.concat")) { - inBad = true; - } - if (hook.equals("MAP.element")) { - inBad = true; - K key = apply(k.items().get(0)); - inBad = stack; - K val = apply(k.items().get(1)); - return KApply(k.klabel(), KList(key, val), k.att()); - } - K result = super.apply(k); - inBad = stack; - return result; - } - - }.apply(term); - } + @Override + public void apply(KApply k) { + if (k.klabel().head().equals(KLabels.ML_OR)) { + return; + } + String hook = + module + .attributesFor() + .get(k.klabel()) + .getOrElse(() -> Att.empty()) + .getOptional(Att.HOOK()) + .orElse(""); + if (hook.equals("SET.element") + || hook.equals("LIST.element") + || hook.equals("LIST.concat") + || hook.equals("MAP.concat") + || hook.equals("SET.concat")) { + return; + } + if (hook.equals("MAP.element")) { + apply(k.items().get(1)); + return; + } + super.apply(k); + } + }.apply(term); + } + + void filterTerms(K term, boolean body) { + new RewriteAwareVisitor(body, new HashSet<>()) { + @Override + public void apply(K k) { + if (isRHS() && !isLHS() && cache.containsKey(k)) { + usedOnRhs.add(k); + return; + } + super.apply(k); + } + }.apply(term); + } + + K transform(K term, boolean body) { + return new RewriteAwareTransformer(body) { + @Override + public K apply(K k) { + if (isRHS() && !isLHS()) { + if (cache.containsKey(k)) { + return cache.get(k); + } + } + if (isLHS() && !isRHS() && !inBad) { + if (usedOnRhs.contains(k)) { + return KAs( + super.apply(k), + cache.get(k), + Att.empty().add(Sort.class, cache.get(k).att().get(Sort.class))); + } + } + return super.apply(k); + } - private int counter = 0; - KVariable newDotVariable(Sort sort) { - KVariable newLabel; - do { - newLabel = KVariable("_Gen" + (counter++), Att().add(Sort.class, sort)); - } while (vars.contains(newLabel)); - vars.add(newLabel); - return newLabel; - } + boolean inBad = false; + @Override + public K apply(KApply k) { + boolean stack = inBad; + if (k.klabel().head().equals(KLabels.ML_OR)) { + inBad = true; + } + String hook = + module + .attributesFor() + .get(k.klabel()) + .getOrElse(() -> Att.empty()) + .getOptional(Att.HOOK()) + .orElse(""); + if (hook.equals("SET.element") + || hook.equals("LIST.element") + || hook.equals("LIST.concat") + || hook.equals("MAP.concat") + || hook.equals("SET.concat")) { + inBad = true; + } + if (hook.equals("MAP.element")) { + inBad = true; + K key = apply(k.items().get(0)); + inBad = stack; + K val = apply(k.items().get(1)); + return KApply(k.klabel(), KList(key, val), k.att()); + } + K result = super.apply(k); + inBad = stack; + return result; + } + }.apply(term); + } + + private int counter = 0; + + KVariable newDotVariable(Sort sort) { + KVariable newLabel; + do { + newLabel = KVariable("_Gen" + (counter++), Att().add(Sort.class, sort)); + } while (vars.contains(newLabel)); + vars.add(newLabel); + return newLabel; + } } diff --git a/kernel/src/main/java/org/kframework/compile/NormalizeVariables.java b/kernel/src/main/java/org/kframework/compile/NormalizeVariables.java index 096669daa58..f4dc3f7d75d 100644 --- a/kernel/src/main/java/org/kframework/compile/NormalizeVariables.java +++ b/kernel/src/main/java/org/kframework/compile/NormalizeVariables.java @@ -1,130 +1,122 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; -import org.kframework.attributes.Att; -import org.kframework.definition.Context; -import org.kframework.definition.Rule; -import org.kframework.definition.Sentence; -import org.kframework.kore.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.stream.Stream; - -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; +import org.kframework.attributes.Att; +import org.kframework.definition.Context; +import org.kframework.definition.Rule; +import org.kframework.definition.Sentence; +import org.kframework.kore.*; /** - * Normalizes variable names in terms and sentences according to alpha equivalence. - * Variables that have previously been normalized are not normalized on succeeding passes, - * allowing the user to fine-tune the normalization such that arbitrary subterms can share - * a common prefix. + * Normalizes variable names in terms and sentences according to alpha equivalence. Variables that + * have previously been normalized are not normalized on succeeding passes, allowing the user to + * fine-tune the normalization such that arbitrary subterms can share a common prefix. */ public class NormalizeVariables { - private int counter = 0; - private Map vars = new HashMap<>(); - private KVariable normalize(KVariable var) { - if (var.att().contains(Att.DENORMAL())) - return var; - if (!vars.containsKey(var)) { - vars.put(var, "_" + counter++); - } - return KVariable(vars.get(var), var.att().add(Att.DENORMAL(), var.name())); - } + private int counter = 0; + private Map vars = new HashMap<>(); - /** - * Applies the normalization existing in a particular set of normalized terms to a denormal term - * @param denormal The term to be normalized. Only variables which exist in the specified - * {@code normals} are normalized. - * @param normals A list of terms that have previously been normalized using this class, or which - * have been constructed manually with all variables given the "denormal" - * attribute specifying their denormal name. The term to be normalized - * will be normalized according to the same normalization as these terms. - * @return The normalized version of {@code denormal}, in which each variable present in - * the denormal version of the specified {@code normals} is replaced with its normalized - * name. - */ - public K applyNormalization(K denormal, K... normals) { - Map normalization = inferNormalizationFromTerm(normals); - return new TransformK() { - @Override - public K apply(KVariable k) { - if (normalization.containsKey(k)) { - return KVariable(normalization.get(k), k.att().add(Att.DENORMAL(), k.name())); - } - return super.apply(k); - } - }.apply(denormal); + private KVariable normalize(KVariable var) { + if (var.att().contains(Att.DENORMAL())) return var; + if (!vars.containsKey(var)) { + vars.put(var, "_" + counter++); } + return KVariable(vars.get(var), var.att().add(Att.DENORMAL(), var.name())); + } - private Map inferNormalizationFromTerm(K[] normals) { - Map normalization = new HashMap<>(); - for (K normal : normals) { - new VisitK() { - @Override - public void apply(KVariable k) { - if (k.att().contains(Att.DENORMAL())) { - normalization.put(KVariable(k.att().get(Att.DENORMAL())), k.name()); - } - } - }.apply(normal); + /** + * Applies the normalization existing in a particular set of normalized terms to a denormal term + * + * @param denormal The term to be normalized. Only variables which exist in the specified {@code + * normals} are normalized. + * @param normals A list of terms that have previously been normalized using this class, or which + * have been constructed manually with all variables given the "denormal" attribute specifying + * their denormal name. The term to be normalized will be normalized according to the same + * normalization as these terms. + * @return The normalized version of {@code denormal}, in which each variable present in the + * denormal version of the specified {@code normals} is replaced with its normalized name. + */ + public K applyNormalization(K denormal, K... normals) { + Map normalization = inferNormalizationFromTerm(normals); + return new TransformK() { + @Override + public K apply(KVariable k) { + if (normalization.containsKey(k)) { + return KVariable(normalization.get(k), k.att().add(Att.DENORMAL(), k.name())); } - return normalization; - } + return super.apply(k); + } + }.apply(denormal); + } - public Rule applyNormalization(Rule denormal, K... normals) { - return Rule( - applyNormalization(denormal.body(), normals), - applyNormalization(denormal.requires(), normals), - applyNormalization(denormal.ensures(), normals), - denormal.att()); + private Map inferNormalizationFromTerm(K[] normals) { + Map normalization = new HashMap<>(); + for (K normal : normals) { + new VisitK() { + @Override + public void apply(KVariable k) { + if (k.att().contains(Att.DENORMAL())) { + normalization.put(KVariable(k.att().get(Att.DENORMAL())), k.name()); + } + } + }.apply(normal); } + return normalization; + } - public K normalize(K term, K... normals) { - resetVars(Stream.concat(Stream.of(term), Arrays.stream(normals)).toArray(K[]::new)); - return transform(term); - } + public Rule applyNormalization(Rule denormal, K... normals) { + return Rule( + applyNormalization(denormal.body(), normals), + applyNormalization(denormal.requires(), normals), + applyNormalization(denormal.ensures(), normals), + denormal.att()); + } - public K transform(K term) { - return new TransformK() { - @Override - public K apply(KVariable k) { - return normalize(k); - } - }.apply(term); - } + public K normalize(K term, K... normals) { + resetVars(Stream.concat(Stream.of(term), Arrays.stream(normals)).toArray(K[]::new)); + return transform(term); + } - private void resetVars(K... normals) { - vars = inferNormalizationFromTerm(normals); - counter = vars.size(); - } + public K transform(K term) { + return new TransformK() { + @Override + public K apply(KVariable k) { + return normalize(k); + } + }.apply(term); + } - public Rule normalize(Rule rule) { - resetVars(rule.body(), rule.requires(), rule.ensures()); - return Rule( - transform(rule.body()), - transform(rule.requires()), - transform(rule.ensures()), - rule.att()); - } + private void resetVars(K... normals) { + vars = inferNormalizationFromTerm(normals); + counter = vars.size(); + } - private Context normalize(Context context) { - resetVars(context.body(), context.requires()); - return new Context( - transform(context.body()), - transform(context.requires()), - context.att()); - } + public Rule normalize(Rule rule) { + resetVars(rule.body(), rule.requires(), rule.ensures()); + return Rule( + transform(rule.body()), transform(rule.requires()), transform(rule.ensures()), rule.att()); + } - public Sentence normalize(Sentence s) { - if (s instanceof Rule) { - return normalize((Rule) s); - } else if (s instanceof Context) { - return normalize((Context) s); - } else { - return s; - } + private Context normalize(Context context) { + resetVars(context.body(), context.requires()); + return new Context(transform(context.body()), transform(context.requires()), context.att()); + } + + public Sentence normalize(Sentence s) { + if (s instanceof Rule) { + return normalize((Rule) s); + } else if (s instanceof Context) { + return normalize((Context) s); + } else { + return s; } + } } diff --git a/kernel/src/main/java/org/kframework/compile/NumberSentences.java b/kernel/src/main/java/org/kframework/compile/NumberSentences.java index c659d038aee..52867ddddf5 100644 --- a/kernel/src/main/java/org/kframework/compile/NumberSentences.java +++ b/kernel/src/main/java/org/kframework/compile/NumberSentences.java @@ -4,24 +4,24 @@ import org.bouncycastle.jcajce.provider.digest.SHA3; import org.bouncycastle.util.encoders.Hex; import org.kframework.attributes.Att; -import org.kframework.definition.Sentence; import org.kframework.definition.RuleOrClaim; +import org.kframework.definition.Sentence; public class NumberSentences { - public static Sentence number(Sentence s) { - if (!(s instanceof RuleOrClaim) || s.att().contains(Att.UNIQUE_ID())) { - return s; - } - String id = ruleHash(s.withAtt(Att.empty())); - return s.withAtt(s.att().add(Att.UNIQUE_ID(), id)); + public static Sentence number(Sentence s) { + if (!(s instanceof RuleOrClaim) || s.att().contains(Att.UNIQUE_ID())) { + return s; } + String id = ruleHash(s.withAtt(Att.empty())); + return s.withAtt(s.att().add(Att.UNIQUE_ID(), id)); + } - private static String ruleHash(Sentence s) { - String text = new NormalizeVariables().normalize(s).toString(); - SHA3.Digest256 sha3engine = new SHA3.Digest256(); - byte[] digest = sha3engine.digest(text.getBytes()); - String digestString = Hex.toHexString(digest); - return digestString; - } + private static String ruleHash(Sentence s) { + String text = new NormalizeVariables().normalize(s).toString(); + SHA3.Digest256 sha3engine = new SHA3.Digest256(); + byte[] digest = sha3engine.digest(text.getBytes()); + String digestString = Hex.toHexString(digest); + return digestString; + } } diff --git a/kernel/src/main/java/org/kframework/compile/PatternValueAwareVisitor.java b/kernel/src/main/java/org/kframework/compile/PatternValueAwareVisitor.java index cfaad75cbfa..50d32536f66 100644 --- a/kernel/src/main/java/org/kframework/compile/PatternValueAwareVisitor.java +++ b/kernel/src/main/java/org/kframework/compile/PatternValueAwareVisitor.java @@ -1,91 +1,93 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import java.util.Set; import org.kframework.kore.KApply; import org.kframework.kore.KRewrite; import org.kframework.kore.KVariable; import org.kframework.kore.VisitK; import org.kframework.utils.errorsystem.KEMException; -import java.util.Set; - /** * A visitor designed to track whether we are currently in Pattern or Value position. * - * outside of rewrite = LHS&RHS, value&pattern - * lhs of rewrite = LHS, pattern - * rhs of rewrite = RHS, value - * requires = LHS, value - * ensures = RHS, value + *

outside of rewrite = LHS&RHS, value&pattern lhs of rewrite = LHS, pattern rhs of rewrite = + * RHS, value requires = LHS, value ensures = RHS, value */ public class PatternValueAwareVisitor extends VisitK { - private final Set errors; - public PatternValueAwareVisitor(boolean isBody, Set errors) { - this.errors = errors; - if (isBody) { - isValue = true; - isPattern = true; - } else { - isValue = true; - isPattern = false; - } + private final Set errors; + + public PatternValueAwareVisitor(boolean isBody, Set errors) { + this.errors = errors; + if (isBody) { + isValue = true; + isPattern = true; + } else { + isValue = true; + isPattern = false; } + } + private boolean isPattern; + private boolean isValue; - private boolean isPattern; - private boolean isValue; + @Override + public void apply(KRewrite k) { + isValue = false; + apply(k.left()); + isValue = true; + isPattern = false; + apply(k.right()); + isPattern = true; + } - @Override - public void apply(KRewrite k) { - isValue = false; - apply(k.left()); + @Override + public void apply(KApply k) { + if (!(k.klabel() instanceof KVariable) && k.klabel().name().equals("#fun2") + || k.klabel().name().equals("#fun3") + || k.klabel().name().equals("#let")) { + boolean wasValue = isValue; + boolean wasPattern = isPattern; + if (isPattern) { + errors.add( + KEMException.compilerError( + "Found #fun expression in a pattern location (LHS and outside of rewrite).", k)); + } + if (k.klabel().name().equals("#fun2")) { isValue = true; - isPattern = false; - apply(k.right()); isPattern = true; + apply(k.items().get(0)); + // in well formed programs this should always reset to true and false, but we want to make + // sure we don't + // create spurious reports if this constraint was violated by the user. + isValue = wasValue; + isPattern = wasPattern; + apply(k.items().get(1)); + } else { + isPattern = true; + isValue = false; + apply(k.items().get(0)); + isPattern = false; + isValue = true; + apply(k.items().get(1)); + // in well formed programs this should always reset to true and false, but we want to make + // sure we don't + // create spurious reports if this constraint was violated by the user. + isValue = wasValue; + isPattern = wasPattern; + apply(k.items().get(2)); + } + } else { + super.apply(k); } + } - @Override - public void apply(KApply k) { - if (!(k.klabel() instanceof KVariable) && k.klabel().name().equals("#fun2") || k.klabel().name().equals("#fun3") || k.klabel().name().equals("#let")) { - boolean wasValue = isValue; - boolean wasPattern = isPattern; - if (isPattern) { - errors.add(KEMException.compilerError("Found #fun expression in a pattern location (LHS and outside of rewrite).", k)); - } - if (k.klabel().name().equals("#fun2")) { - isValue = true; - isPattern = true; - apply(k.items().get(0)); - // in well formed programs this should always reset to true and false, but we want to make sure we don't - // create spurious reports if this constraint was violated by the user. - isValue = wasValue; - isPattern = wasPattern; - apply(k.items().get(1)); - } else { - isPattern = true; - isValue = false; - apply(k.items().get(0)); - isPattern = false; - isValue = true; - apply(k.items().get(1)); - // in well formed programs this should always reset to true and false, but we want to make sure we don't - // create spurious reports if this constraint was violated by the user. - isValue = wasValue; - isPattern = wasPattern; - apply(k.items().get(2)); - } - } else { - super.apply(k); - } - } - - public boolean isPattern() { - return isPattern; - } + public boolean isPattern() { + return isPattern; + } - public boolean isValue() { - return isValue; - } + public boolean isValue() { + return isValue; + } } diff --git a/kernel/src/main/java/org/kframework/compile/ProcessGroupAttributes.java b/kernel/src/main/java/org/kframework/compile/ProcessGroupAttributes.java index 6877a2a4a39..f479d367a3f 100644 --- a/kernel/src/main/java/org/kframework/compile/ProcessGroupAttributes.java +++ b/kernel/src/main/java/org/kframework/compile/ProcessGroupAttributes.java @@ -10,31 +10,31 @@ import scala.util.Either; /** - * A pass which handles all "user group" attributes. Specifically, - * the attribute [group(att1,...,attN)] is replaced with the underlying attributes [att1,...,attN]. + * A pass which handles all "user group" attributes. Specifically, the attribute + * [group(att1,...,attN)] is replaced with the underlying attributes [att1,...,attN]. */ public class ProcessGroupAttributes { - public static Att getProcessedAtt(Att att, HasLocation node) { - Either newAttOrError = att.withGroupAttAsUserGroups(); - if (newAttOrError.isLeft()) { - throw KEMException.compilerError(newAttOrError.left().get(), node); - } - Att newAtt = newAttOrError.right().get(); - return newAtt; + public static Att getProcessedAtt(Att att, HasLocation node) { + Either newAttOrError = att.withGroupAttAsUserGroups(); + if (newAttOrError.isLeft()) { + throw KEMException.compilerError(newAttOrError.left().get(), node); } + Att newAtt = newAttOrError.right().get(); + return newAtt; + } - public static void apply(Module m) { - m.setAttributes(getProcessedAtt(m.getAttributes(), m)); - m.getItems().stream() - .filter((modItem) -> modItem instanceof Syntax) - .flatMap((s) -> ((Syntax) s).getPriorityBlocks().stream()) - .flatMap((pb) -> pb.getProductions().stream()) - .forEach((p) -> p.setAttributes(getProcessedAtt(p.getAttributes(), p))); - } + public static void apply(Module m) { + m.setAttributes(getProcessedAtt(m.getAttributes(), m)); + m.getItems().stream() + .filter((modItem) -> modItem instanceof Syntax) + .flatMap((s) -> ((Syntax) s).getPriorityBlocks().stream()) + .flatMap((pb) -> pb.getProductions().stream()) + .forEach((p) -> p.setAttributes(getProcessedAtt(p.getAttributes(), p))); + } - public static void apply(Definition d) { - d.getItems().stream() - .filter((item) -> item instanceof Module) - .forEach((m) -> apply((Module) m)); - } + public static void apply(Definition d) { + d.getItems().stream() + .filter((item) -> item instanceof Module) + .forEach((m) -> apply((Module) m)); + } } diff --git a/kernel/src/main/java/org/kframework/compile/PropagateMacro.java b/kernel/src/main/java/org/kframework/compile/PropagateMacro.java index 7eeb05b5432..2d6d7ce72e7 100644 --- a/kernel/src/main/java/org/kframework/compile/PropagateMacro.java +++ b/kernel/src/main/java/org/kframework/compile/PropagateMacro.java @@ -7,18 +7,24 @@ import org.kframework.definition.Sentence; /** - * Propagate macro, macro-rec, alias, and alias-rec labels from productions to rules that only contain that klabel on the LHS - * This prepares rules for macro expansion in ExpandMacros. - * There is one exception: simplification rules are meant to be used for the haskell backend and macros should not be propagated - * to these rules. + * Propagate macro, macro-rec, alias, and alias-rec labels from productions to rules that only + * contain that klabel on the LHS This prepares rules for macro expansion in ExpandMacros. There is + * one exception: simplification rules are meant to be used for the haskell backend and macros + * should not be propagated to these rules. */ public record PropagateMacro(Module m) { - public Sentence propagate(Sentence s) { - if (s instanceof Rule && m.ruleLhsHasMacroKLabel((Rule) s) && !s.att().contains(Att.SIMPLIFICATION())) { - Att macroAtt = m.attributesFor().apply(m.matchKLabel((Rule) s)); - return Rule.apply(((Rule) s).body(), ((Rule) s).requires(), ((Rule) s).ensures(), s.att().add(macroAtt.getMacro().get())); - } - return s; + public Sentence propagate(Sentence s) { + if (s instanceof Rule + && m.ruleLhsHasMacroKLabel((Rule) s) + && !s.att().contains(Att.SIMPLIFICATION())) { + Att macroAtt = m.attributesFor().apply(m.matchKLabel((Rule) s)); + return Rule.apply( + ((Rule) s).body(), + ((Rule) s).requires(), + ((Rule) s).ensures(), + s.att().add(macroAtt.getMacro().get())); } + return s; + } } diff --git a/kernel/src/main/java/org/kframework/compile/RefreshRules.java b/kernel/src/main/java/org/kframework/compile/RefreshRules.java index d7b6f5622f2..c7328079f82 100644 --- a/kernel/src/main/java/org/kframework/compile/RefreshRules.java +++ b/kernel/src/main/java/org/kframework/compile/RefreshRules.java @@ -1,11 +1,8 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; -import org.kframework.attributes.Att; -import org.kframework.definition.Rule; -import org.kframework.kore.K; -import org.kframework.kore.KVariable; -import org.kframework.kore.TransformK; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; import java.util.Collection; import java.util.HashMap; @@ -14,77 +11,78 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; - -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; +import org.kframework.attributes.Att; +import org.kframework.definition.Rule; +import org.kframework.kore.K; +import org.kframework.kore.KVariable; +import org.kframework.kore.TransformK; /** - * refreshes variable names in terms and sentences according to alpha equivalence. - * Variables that have previously been refreshed are not normalized on succeeding passes, - * allowing the user to fine-tune the applyRefresh process such that arbitrary subterms can share - * a common prefix. + * refreshes variable names in terms and sentences according to alpha equivalence. Variables that + * have previously been refreshed are not normalized on succeeding passes, allowing the user to + * fine-tune the applyRefresh process such that arbitrary subterms can share a common prefix. */ public class RefreshRules { - private final Set avoidVars; - private int counter = 0; - private final Map vars = new HashMap<>(); - - public RefreshRules(Set avoidVars) { - this.avoidVars = avoidVars; - } - - /** - * Refreshes a rule - * @param rule The rule to be refreshed. - * @return The refreshed version of {@code rule}, in which each variable - * is alpha-renamed to fresh variable. - * name. - */ - public static Rule refresh(Rule rule) { - return new RefreshRules(new HashSet<>()).applyRefresh(rule); - } + private final Set avoidVars; + private int counter = 0; + private final Map vars = new HashMap<>(); - /** - * Refreshes a set of rules such that no two rules would have variables in common. - * @param rules The rules to be refreshed. - * @return The refreshed version of {@code rules} - */ - public static Collection refresh(Collection rules, Set avoidVars) { - RefreshRules refreshRules = new RefreshRules(avoidVars); - return rules.stream().map(refreshRules::applyRefreshResetVars).collect(Collectors.toCollection(LinkedList::new)); - } + public RefreshRules(Set avoidVars) { + this.avoidVars = avoidVars; + } - private Rule applyRefresh(Rule rule) { - return Rule( - applyRefresh(rule.body()), - applyRefresh(rule.requires()), - applyRefresh(rule.ensures()), - rule.att()); - } + /** + * Refreshes a rule + * + * @param rule The rule to be refreshed. + * @return The refreshed version of {@code rule}, in which each variable is alpha-renamed to fresh + * variable. name. + */ + public static Rule refresh(Rule rule) { + return new RefreshRules(new HashSet<>()).applyRefresh(rule); + } - private Rule applyRefreshResetVars(Rule rule) { - vars.clear(); - return applyRefresh(rule); - } + /** + * Refreshes a set of rules such that no two rules would have variables in common. + * + * @param rules The rules to be refreshed. + * @return The refreshed version of {@code rules} + */ + public static Collection refresh(Collection rules, Set avoidVars) { + RefreshRules refreshRules = new RefreshRules(avoidVars); + return rules.stream() + .map(refreshRules::applyRefreshResetVars) + .collect(Collectors.toCollection(LinkedList::new)); + } - private K applyRefresh(K term) { - return new TransformK() { - @Override - public K apply(KVariable var) { - if (var.att().contains(Att.REFRESHED())) - return var; - if (!vars.containsKey(var)) { - String newVarName; - do { - newVarName = "_Gen" + counter++; - } while (avoidVars.contains(newVarName)); - vars.put(var, newVarName); - } - return KVariable(vars.get(var), var.att().add(Att.REFRESHED(), var.name())); - } - }.apply(term); - } + private Rule applyRefresh(Rule rule) { + return Rule( + applyRefresh(rule.body()), + applyRefresh(rule.requires()), + applyRefresh(rule.ensures()), + rule.att()); + } + private Rule applyRefreshResetVars(Rule rule) { + vars.clear(); + return applyRefresh(rule); + } + private K applyRefresh(K term) { + return new TransformK() { + @Override + public K apply(KVariable var) { + if (var.att().contains(Att.REFRESHED())) return var; + if (!vars.containsKey(var)) { + String newVarName; + do { + newVarName = "_Gen" + counter++; + } while (avoidVars.contains(newVarName)); + vars.put(var, newVarName); + } + return KVariable(vars.get(var), var.att().add(Att.REFRESHED(), var.name())); + } + }.apply(term); + } } diff --git a/kernel/src/main/java/org/kframework/compile/RemoveUnit.java b/kernel/src/main/java/org/kframework/compile/RemoveUnit.java index 4a9c3e96caa..7287e4f0e1b 100644 --- a/kernel/src/main/java/org/kframework/compile/RemoveUnit.java +++ b/kernel/src/main/java/org/kframework/compile/RemoveUnit.java @@ -1,6 +1,11 @@ // Copyright (c) Runtime Verification, Inc. All Rights Reserved. package org.kframework.compile; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + +import java.util.stream.Stream; import org.kframework.Collections; import org.kframework.attributes.Att; import org.kframework.definition.Module; @@ -9,23 +14,19 @@ import org.kframework.definition.Sentence; import org.kframework.kore.*; import org.kframework.utils.errorsystem.KEMException; - -import java.util.HashSet; -import java.util.stream.Stream; - import scala.collection.Set; -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; -import static org.kframework.Collections.*; - public class RemoveUnit { private Module m; public Module apply(Module module) { m = module; - return Module(module.name(), module.imports(), stream(module.localSentences()).flatMap(this::gen).collect(Collections.toSet()), module.att()); + return Module( + module.name(), + module.imports(), + stream(module.localSentences()).flatMap(this::gen).collect(Collections.toSet()), + module.att()); } private Stream gen(Sentence s) { @@ -44,16 +45,16 @@ private K flattenLists(K k) { public K apply(KApply k) { Production p; if (m.productionsFor().contains(k.klabel())) { - Set s = m.productionsFor().get(k.klabel()).get(); - assert s.size() == 1; // There should only be one production for this label - p = s.head(); + Set s = m.productionsFor().get(k.klabel()).get(); + assert s.size() == 1; // There should only be one production for this label + p = s.head(); } else { - return super.apply(k); + return super.apply(k); } Att att = p.att(); // Ignore optional cells, which have a unit attribute but no assoc - if ( att.contains(Att.CELL()) + if (att.contains(Att.CELL()) && att.contains(Att.MULTIPLICITY()) && att.get(Att.MULTIPLICITY()).equals("?")) { return super.apply(k); @@ -61,9 +62,13 @@ public K apply(KApply k) { if (att.contains(Att.UNIT())) { if (!att.contains(Att.ASSOC())) { - throw KEMException.internalError("Unimplemented case when removing unit applications: unit attribute but no assoc", p); + throw KEMException.internalError( + "Unimplemented case when removing unit applications: unit attribute but no assoc", + p); } - return Assoc.flatten(k.klabel(), k.items(), m).stream().reduce((k1, k2) -> KApply(k.klabel(), k1, k2)).orElse(KApply(KLabel(att.get(Att.UNIT())))); + return Assoc.flatten(k.klabel(), k.items(), m).stream() + .reduce((k1, k2) -> KApply(k.klabel(), k1, k2)) + .orElse(KApply(KLabel(att.get(Att.UNIT())))); } return super.apply(k); } diff --git a/kernel/src/main/java/org/kframework/compile/ResolveAnonVar.java b/kernel/src/main/java/org/kframework/compile/ResolveAnonVar.java index bfaef719619..ff89efaceee 100644 --- a/kernel/src/main/java/org/kframework/compile/ResolveAnonVar.java +++ b/kernel/src/main/java/org/kframework/compile/ResolveAnonVar.java @@ -1,6 +1,11 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.kore.KORE.*; + +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; import org.kframework.attributes.Att; import org.kframework.attributes.Location; import org.kframework.attributes.Source; @@ -10,134 +15,125 @@ import org.kframework.definition.Sentence; import org.kframework.kore.*; -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; - -import static org.kframework.kore.KORE.*; - public class ResolveAnonVar { - public static KVariable ANON_VAR = KVariable("_"); - public static KVariable FRESH_ANON_VAR = KVariable("?_"); - public static KVariable FRESH_ANON_CONSTANT = KVariable("!_"); - public static KVariable FRESH_LIST_VAR = KVariable("@_"); - - public static boolean isAnonVar(KVariable var) { - return var.equals(ANON_VAR) || var.equals(FRESH_ANON_VAR) || var.equals(FRESH_ANON_CONSTANT) || var.equals(FRESH_LIST_VAR); - } - - public static boolean isAnonVarOrNamedAnonVar(KVariable var) { - return var.name().startsWith(ANON_VAR.name()) - || var.name().startsWith(FRESH_ANON_VAR.name()) - || var.name().startsWith(FRESH_ANON_CONSTANT.name()) - || var.name().startsWith(FRESH_LIST_VAR.name()); - } - - - private final Set vars = new HashSet<>(); - - void resetVars() { - vars.clear(); - counter = 0; + public static KVariable ANON_VAR = KVariable("_"); + public static KVariable FRESH_ANON_VAR = KVariable("?_"); + public static KVariable FRESH_ANON_CONSTANT = KVariable("!_"); + public static KVariable FRESH_LIST_VAR = KVariable("@_"); + + public static boolean isAnonVar(KVariable var) { + return var.equals(ANON_VAR) + || var.equals(FRESH_ANON_VAR) + || var.equals(FRESH_ANON_CONSTANT) + || var.equals(FRESH_LIST_VAR); + } + + public static boolean isAnonVarOrNamedAnonVar(KVariable var) { + return var.name().startsWith(ANON_VAR.name()) + || var.name().startsWith(FRESH_ANON_VAR.name()) + || var.name().startsWith(FRESH_ANON_CONSTANT.name()) + || var.name().startsWith(FRESH_LIST_VAR.name()); + } + + private final Set vars = new HashSet<>(); + + void resetVars() { + vars.clear(); + counter = 0; + } + + private RuleOrClaim resolve(RuleOrClaim rule) { + resetVars(); + gatherVars(rule.body()); + gatherVars(rule.requires()); + gatherVars(rule.ensures()); + return rule.newInstance( + transform(rule.body()), transform(rule.requires()), transform(rule.ensures()), rule.att()); + } + + private Context resolve(Context context) { + resetVars(); + gatherVars(context.body()); + gatherVars(context.requires()); + return new Context(transform(context.body()), transform(context.requires()), context.att()); + } + + private ContextAlias resolve(ContextAlias context) { + resetVars(); + gatherVars(context.body()); + gatherVars(context.requires()); + return new ContextAlias( + transform(context.body()), transform(context.requires()), context.att()); + } + + public K resolveK(K k) { + resetVars(); + gatherVars(k); + return transform(k); + } + + public synchronized Sentence resolve(Sentence s) { + if (s instanceof RuleOrClaim) { + return resolve((RuleOrClaim) s); + } else if (s instanceof Context) { + return resolve((Context) s); + } else if (s instanceof ContextAlias) { + return resolve((ContextAlias) s); + } else { + return s; } - - private RuleOrClaim resolve(RuleOrClaim rule) { - resetVars(); - gatherVars(rule.body()); - gatherVars(rule.requires()); - gatherVars(rule.ensures()); - return rule.newInstance( - transform(rule.body()), - transform(rule.requires()), - transform(rule.ensures()), - rule.att()); - } - - private Context resolve(Context context) { - resetVars(); - gatherVars(context.body()); - gatherVars(context.requires()); - return new Context( - transform(context.body()), - transform(context.requires()), - context.att()); - } - - private ContextAlias resolve(ContextAlias context) { - resetVars(); - gatherVars(context.body()); - gatherVars(context.requires()); - return new ContextAlias( - transform(context.body()), - transform(context.requires()), - context.att()); - } - - public K resolveK(K k) { - resetVars(); - gatherVars(k); - return transform(k); - } - - public synchronized Sentence resolve(Sentence s) { - if (s instanceof RuleOrClaim) { - return resolve((RuleOrClaim) s); - } else if (s instanceof Context) { - return resolve((Context) s); - } else if (s instanceof ContextAlias) { - return resolve((ContextAlias) s); - } else { - return s; + } + + void gatherVars(K term) { + new VisitK() { + @Override + public void apply(KVariable v) { + vars.add(v); + super.apply(v); + } + }.apply(term); + } + + K transform(K term) { + return new TransformK() { + @Override + public K apply(KVariable k) { + if (ANON_VAR.equals(k)) { + return newDotVariable("", k); } - } - - void gatherVars(K term) { - new VisitK() { - @Override - public void apply(KVariable v) { - vars.add(v); - super.apply(v); - } - }.apply(term); - } + if (FRESH_ANON_VAR.equals(k)) { + return newDotVariable("?", k); + } + if (FRESH_ANON_CONSTANT.equals(k)) { + return newDotVariable("!", k); + } + if (FRESH_LIST_VAR.equals(k)) { + return newDotVariable("@", k); + } + return super.apply(k); + } + }.apply(term); + } - K transform(K term) { - return new TransformK() { - @Override - public K apply(KVariable k) { - if (ANON_VAR.equals(k)) { - return newDotVariable("", k); - } - if (FRESH_ANON_VAR.equals(k)) { - return newDotVariable("?", k); - } - if (FRESH_ANON_CONSTANT.equals(k)) { - return newDotVariable("!", k); - } - if (FRESH_LIST_VAR.equals(k)) { - return newDotVariable("@", k); - } - return super.apply(k); - } - }.apply(term); - } + private int counter = 0; - private int counter = 0; - KVariable newDotVariable(String prefix, K k) { - KVariable newLabel; - Att locInfo = Optional.of(Att()) + KVariable newDotVariable(String prefix, K k) { + KVariable newLabel; + Att locInfo = + Optional.of(Att()) .flatMap(att -> k.att().getOptional(Source.class).map(s -> att.add(Source.class, s))) - .flatMap(att -> k.att().getOptional(Location.class).map(l -> att.add(Location.class, l))).orElse(Att()); - Att att = Att().add(Att.ANONYMOUS()).addAll(locInfo); - if (prefix.equals("?")) { - att = att.add(Att.FRESH()); - } - do { - newLabel = KVariable(prefix + "_Gen" + (counter++), att); - } while (vars.contains(newLabel)); - vars.add(newLabel); - return newLabel; + .flatMap( + att -> k.att().getOptional(Location.class).map(l -> att.add(Location.class, l))) + .orElse(Att()); + Att att = Att().add(Att.ANONYMOUS()).addAll(locInfo); + if (prefix.equals("?")) { + att = att.add(Att.FRESH()); } - + do { + newLabel = KVariable(prefix + "_Gen" + (counter++), att); + } while (vars.contains(newLabel)); + vars.add(newLabel); + return newLabel; + } } diff --git a/kernel/src/main/java/org/kframework/compile/ResolveComm.java b/kernel/src/main/java/org/kframework/compile/ResolveComm.java index d393b267e5b..b5ed68806c4 100644 --- a/kernel/src/main/java/org/kframework/compile/ResolveComm.java +++ b/kernel/src/main/java/org/kframework/compile/ResolveComm.java @@ -1,7 +1,14 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.Collections.immutable; +import static org.kframework.Collections.stream; +import static org.kframework.definition.Constructors.Module; + import com.google.common.collect.Lists; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.kframework.attributes.Att; import org.kframework.attributes.Location; import org.kframework.attributes.Source; @@ -15,60 +22,75 @@ import org.kframework.utils.errorsystem.KException; import org.kframework.utils.errorsystem.KExceptionManager; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.kframework.Collections.immutable; -import static org.kframework.Collections.stream; -import static org.kframework.definition.Constructors.Module; - public record ResolveComm(KExceptionManager kem) { - - public Module resolve(Module m) { - // generate a duplicate simplification rule for symbols that are labeled as `comm` - // remove this attribute from the rules because the Haskell Backend has a different meaning for it - Set commSimpRules = stream(m.localSentences()) - .filter(s -> s instanceof Rule && s.att().contains(Att.SIMPLIFICATION()) && s.att().contains(Att.COMM())) - .collect(Collectors.toSet()); - Set commRulesToAdd = commSimpRules.stream() - .flatMap(s -> { - Rule r = (Rule) s; - K newBody = genCommRule(r.body(), m); - if (!newBody.equals(r.body())) - return Stream.of( - Rule.apply(newBody, r.requires(), r.ensures(), r.att().remove(Att.COMM())), - Rule.apply(r.body(), r.requires(), r.ensures(), r.att().remove(Att.COMM()))); - return Stream.of(Rule.apply(r.body(), r.requires(), r.ensures(), r.att().remove(Att.COMM()))); + public Module resolve(Module m) { + // generate a duplicate simplification rule for symbols that are labeled as `comm` + // remove this attribute from the rules because the Haskell Backend has a different meaning for + // it + Set commSimpRules = + stream(m.localSentences()) + .filter( + s -> + s instanceof Rule + && s.att().contains(Att.SIMPLIFICATION()) + && s.att().contains(Att.COMM())) + .collect(Collectors.toSet()); + Set commRulesToAdd = + commSimpRules.stream() + .flatMap( + s -> { + Rule r = (Rule) s; + K newBody = genCommRule(r.body(), m); + if (!newBody.equals(r.body())) + return Stream.of( + Rule.apply(newBody, r.requires(), r.ensures(), r.att().remove(Att.COMM())), + Rule.apply( + r.body(), r.requires(), r.ensures(), r.att().remove(Att.COMM()))); + return Stream.of( + Rule.apply(r.body(), r.requires(), r.ensures(), r.att().remove(Att.COMM()))); }) - .collect(Collectors.toSet()); - return Module(m.name(), m.imports(), m.localSentences().$minus$minus(immutable(commSimpRules)).$bar(immutable(commRulesToAdd)).seq(), m.att()); - } + .collect(Collectors.toSet()); + return Module( + m.name(), + m.imports(), + m.localSentences() + .$minus$minus(immutable(commSimpRules)) + .$bar(immutable(commRulesToAdd)) + .seq(), + m.att()); + } - public K genCommRule(K body, Module m) { - return new RewriteAwareTransformer(true) { - @Override - public K apply(KApply k) { - if (k.klabel().name().equals("#withConfig")) { - return super.apply(k); - } - if ((isRHS() && !isLHS()) || k.klabel() instanceof KVariable || !m.attributesFor().contains(k.klabel())) { - return k; - } - Att attributes = m.attributesFor().apply(k.klabel()); - if (attributes.contains(Att.COMM())) { - return KORE.KApply( - k.klabel(), - KORE.KList(Lists.newArrayList(k.klist().items().get(1), k.klist().items().get(0))), - k.att()); - } else - kem.addKException(new KException(KException.ExceptionType.ERROR, KException.KExceptionGroup.COMPILER, - "Used 'comm' attribute on simplification rule but " + k.klabel().name() + " is not comm.", - k.att().getOptional(Source.class).orElse(null), - k.att().getOptional(Location.class).orElse(null))); - return k; - } - }.apply(body); - } + public K genCommRule(K body, Module m) { + return new RewriteAwareTransformer(true) { + @Override + public K apply(KApply k) { + if (k.klabel().name().equals("#withConfig")) { + return super.apply(k); + } + if ((isRHS() && !isLHS()) + || k.klabel() instanceof KVariable + || !m.attributesFor().contains(k.klabel())) { + return k; + } + Att attributes = m.attributesFor().apply(k.klabel()); + if (attributes.contains(Att.COMM())) { + return KORE.KApply( + k.klabel(), + KORE.KList(Lists.newArrayList(k.klist().items().get(1), k.klist().items().get(0))), + k.att()); + } else + kem.addKException( + new KException( + KException.ExceptionType.ERROR, + KException.KExceptionGroup.COMPILER, + "Used 'comm' attribute on simplification rule but " + + k.klabel().name() + + " is not comm.", + k.att().getOptional(Source.class).orElse(null), + k.att().getOptional(Location.class).orElse(null))); + return k; + } + }.apply(body); + } } diff --git a/kernel/src/main/java/org/kframework/compile/ResolveContexts.java b/kernel/src/main/java/org/kframework/compile/ResolveContexts.java index ba21995981b..f4dabc8edae 100644 --- a/kernel/src/main/java/org/kframework/compile/ResolveContexts.java +++ b/kernel/src/main/java/org/kframework/compile/ResolveContexts.java @@ -1,6 +1,19 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.kframework.Collections; import org.kframework.attributes.Att; import org.kframework.builtin.BooleanUtils; @@ -19,307 +32,320 @@ import org.kframework.kore.KLabel; import org.kframework.kore.KRewrite; import org.kframework.kore.KVariable; -import org.kframework.kore.VisitK; import org.kframework.kore.TransformK; +import org.kframework.kore.VisitK; import org.kframework.utils.errorsystem.KEMException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.kframework.Collections.*; -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; - public class ResolveContexts { - private final KompileOptions kompileOptions; - - public ResolveContexts(KompileOptions kompileOptions) { - this.kompileOptions = kompileOptions; - } - - public Definition resolve(Definition d) { - klabels = new HashSet<>(); - Module transformedMainModule = resolve(d.mainModule()); - return Definition.apply(transformedMainModule, add(transformedMainModule, minus(d.mainModule(), d.entryModules())), d.att()); + private final KompileOptions kompileOptions; + + public ResolveContexts(KompileOptions kompileOptions) { + this.kompileOptions = kompileOptions; + } + + public Definition resolve(Definition d) { + klabels = new HashSet<>(); + Module transformedMainModule = resolve(d.mainModule()); + return Definition.apply( + transformedMainModule, + add(transformedMainModule, minus(d.mainModule(), d.entryModules())), + d.att()); + } + + public Module resolve(Module input) { + Set rulesToAdd = + stream(input.sentences()) + .filter(s -> s instanceof Context) + .map(s -> (Context) s) + .flatMap(c -> this.resolve(c, input)) + .collect(Collectors.toCollection(HashSet::new)); + if (!rulesToAdd.isEmpty()) { + rulesToAdd.add(SyntaxSort(Seq(), Sorts.K())); } - - public Module resolve(Module input) { - Set rulesToAdd = stream(input.sentences()) - .filter(s -> s instanceof Context) - .map(s -> (Context) s) - .flatMap(c -> this.resolve(c, input)).collect(Collectors.toCollection(HashSet::new)); - if (!rulesToAdd.isEmpty()) { - rulesToAdd.add(SyntaxSort(Seq(), Sorts.K())); - } - return Module(input.name(), input.imports(), (scala.collection.Set) stream(input.localSentences()).filter(s -> !(s instanceof Context)).collect(Collections.toSet()).$bar(immutable(rulesToAdd)), input.att()); + return Module( + input.name(), + input.imports(), + (scala.collection.Set) + stream(input.localSentences()) + .filter(s -> !(s instanceof Context)) + .collect(Collections.toSet()) + .$bar(immutable(rulesToAdd)), + input.att()); + } + + private Set klabels; + + private KLabel getUniqueFreezerLabel(Module input, String nameHint) { + if (klabels.isEmpty()) { + klabels.addAll(mutable(input.definedKLabels())); } - - private Set klabels; - - private KLabel getUniqueFreezerLabel(Module input, String nameHint) { - if (klabels.isEmpty()) { - klabels.addAll(mutable(input.definedKLabels())); - } - int counter = 0; - KLabel freezer; - do { - freezer = KLabel("#freezer" + nameHint + "_" + (counter++ == 0 ? "" : counter)); - } while (klabels.contains(freezer)); - klabels.add(freezer); - return freezer; + int counter = 0; + KLabel freezer; + do { + freezer = KLabel("#freezer" + nameHint + "_" + (counter++ == 0 ? "" : counter)); + } while (klabels.contains(freezer)); + klabels.add(freezer); + return freezer; + } + + private Att addSuffixToLabel(Att a, String suffix) { + if (!a.contains(Att.LABEL())) { + return a; } - - private Att addSuffixToLabel(Att a, String suffix) { - if (!a.contains(Att.LABEL())) { - return a; - } - return a.add(Att.LABEL(), a.get(Att.LABEL()) + suffix); - } - - private Stream resolve(Context context, Module input) { - checkContextValidity(context); - final SortedMap vars = new TreeMap<>((v1, v2) -> v1.name().compareTo(v2.name())); - K body = context.body(); - K requiresHeat = context.requires(); - K requiresCool = BooleanUtils.TRUE; - - int[] currentHolePosition = new int[] { 0 }; - int[] finalHolePosition = new int[] { 0 }; - // does this context have a main cell? - boolean hasMainCell = new FoldK() { - @Override - public Boolean apply(KApply k) { - if (input.attributesFor().getOrElse(k.klabel(), () -> Att()).contains(Att.MAINCELL())) { - return true; - } - return super.apply(k); + return a.add(Att.LABEL(), a.get(Att.LABEL()) + suffix); + } + + private Stream resolve(Context context, Module input) { + checkContextValidity(context); + final SortedMap vars = new TreeMap<>((v1, v2) -> v1.name().compareTo(v2.name())); + K body = context.body(); + K requiresHeat = context.requires(); + K requiresCool = BooleanUtils.TRUE; + + int[] currentHolePosition = new int[] {0}; + int[] finalHolePosition = new int[] {0}; + // does this context have a main cell? + boolean hasMainCell = + new FoldK() { + @Override + public Boolean apply(KApply k) { + if (input.attributesFor().getOrElse(k.klabel(), () -> Att()).contains(Att.MAINCELL())) { + return true; } + return super.apply(k); + } - @Override - public Boolean unit() { - return false; - } + @Override + public Boolean unit() { + return false; + } - @Override - public Boolean merge(Boolean a, Boolean b) { - return a || b; - } + @Override + public Boolean merge(Boolean a, Boolean b) { + return a || b; + } }.apply(body); - // Find a heated hole - // e.g., context ++(HOLE => lvalue(HOLE)) - K heated = new VisitK() { - K heated; - KVariable holeVar; - boolean inMainCell = false; - - public K process(K k) { - apply(k); - if (heated != null) - return heated; - else - return holeVar; - } + // Find a heated hole + // e.g., context ++(HOLE => lvalue(HOLE)) + K heated = + new VisitK() { + K heated; + KVariable holeVar; + boolean inMainCell = false; + + public K process(K k) { + apply(k); + if (heated != null) return heated; + else return holeVar; + } - @Override - public void apply(KRewrite k) { - heated = k.right(); - super.apply(k); - } + @Override + public void apply(KRewrite k) { + heated = k.right(); + super.apply(k); + } - @Override - public void apply(KVariable k) { - if (inMainCell || !hasMainCell) { - if (!k.name().equals("HOLE")) { - vars.put(k, k); - currentHolePosition[0]++; - } else { - holeVar = k; - finalHolePosition[0] = currentHolePosition[0]; - } - } - super.apply(k); + @Override + public void apply(KVariable k) { + if (inMainCell || !hasMainCell) { + if (!k.name().equals("HOLE")) { + vars.put(k, k); + currentHolePosition[0]++; + } else { + holeVar = k; + finalHolePosition[0] = currentHolePosition[0]; + } } + super.apply(k); + } - @Override - public void apply(KApply k) { - if (input.attributesFor().getOrElse(k.klabel(), () -> Att()).contains(Att.MAINCELL())) { - inMainCell = true; - } - if (k.klabel() instanceof KVariable && (inMainCell || !hasMainCell)) - vars.put((KVariable) k.klabel(), InjectedKLabel(k.klabel())); - super.apply(k); - if (input.attributesFor().getOrElse(k.klabel(), () -> Att()).contains(Att.MAINCELL())) { - inMainCell = false; - } + @Override + public void apply(KApply k) { + if (input.attributesFor().getOrElse(k.klabel(), () -> Att()).contains(Att.MAINCELL())) { + inMainCell = true; } - }.process(body); - K cooled = new VisitK() { - K cooled; - public K process(K k) { - apply(k); - if (cooled != null) - return cooled; - else - return k; + if (k.klabel() instanceof KVariable && (inMainCell || !hasMainCell)) + vars.put((KVariable) k.klabel(), InjectedKLabel(k.klabel())); + super.apply(k); + if (input.attributesFor().getOrElse(k.klabel(), () -> Att()).contains(Att.MAINCELL())) { + inMainCell = false; } + } + }.process(body); + K cooled = + new VisitK() { + K cooled; + + public K process(K k) { + apply(k); + if (cooled != null) return cooled; + else return k; + } - @Override - public void apply(KApply k) { - if (input.attributesFor().getOrElse(k.klabel(), () -> Att()).contains(Att.MAINCELL())) { - cooled = k.items().get(1); - } - super.apply(k); + @Override + public void apply(KApply k) { + if (input.attributesFor().getOrElse(k.klabel(), () -> Att()).contains(Att.MAINCELL())) { + cooled = k.items().get(1); } + super.apply(k); + } }.process(RewriteToTop.toLeft(body)); - // TODO(dwightguth): generate freezers better for pretty-printing purposes - List items = new ArrayList<>(); - KLabel freezerLabel; - if (cooled instanceof KApply kApply) { - String name = kApply.klabel().name(); - if (name.equals("#SemanticCastToK")) { - K firstArg = kApply.klist().items().get(0); - if (firstArg instanceof KApply) - name = ((KApply)firstArg).klabel().name(); - } - freezerLabel = getUniqueFreezerLabel(input, name + finalHolePosition[0]); - } else { - freezerLabel = getUniqueFreezerLabel(input, ""); - } - items.add(Terminal(freezerLabel.name())); - items.add(Terminal("(")); - for (int i = 0; i < vars.size(); i++) { - items.add(NonTerminal(Sorts.K())); - items.add(Terminal(",")); - } - if (vars.size() > 0) { - items.remove(items.size() - 1); - } - items.add(Terminal(")")); - Production freezer = Production(freezerLabel, Sorts.KItem(), immutable(items), Att()); - K frozen = KApply(freezerLabel, vars.values().stream().collect(Collections.toList())); - - Att heatAtt = addSuffixToLabel(context.att().add(Att.HEAT()), "-heat"); - Att coolAtt = addSuffixToLabel(context.att().add(Att.COOL()), "-cool"); - - Function throwException = label -> { - Sentence loc = input.labeled().get(label).get().head(); - throw KEMException.compilerError("The generated label for a context rule conflicts with a user-defined label at " - + loc.source().get() + " and " - + loc.location().get() + ". Please consider renaming.", context); + // TODO(dwightguth): generate freezers better for pretty-printing purposes + List items = new ArrayList<>(); + KLabel freezerLabel; + if (cooled instanceof KApply kApply) { + String name = kApply.klabel().name(); + if (name.equals("#SemanticCastToK")) { + K firstArg = kApply.klist().items().get(0); + if (firstArg instanceof KApply) name = ((KApply) firstArg).klabel().name(); + } + freezerLabel = getUniqueFreezerLabel(input, name + finalHolePosition[0]); + } else { + freezerLabel = getUniqueFreezerLabel(input, ""); + } + items.add(Terminal(freezerLabel.name())); + items.add(Terminal("(")); + for (int i = 0; i < vars.size(); i++) { + items.add(NonTerminal(Sorts.K())); + items.add(Terminal(",")); + } + if (vars.size() > 0) { + items.remove(items.size() - 1); + } + items.add(Terminal(")")); + Production freezer = Production(freezerLabel, Sorts.KItem(), immutable(items), Att()); + K frozen = KApply(freezerLabel, vars.values().stream().collect(Collections.toList())); + + Att heatAtt = addSuffixToLabel(context.att().add(Att.HEAT()), "-heat"); + Att coolAtt = addSuffixToLabel(context.att().add(Att.COOL()), "-cool"); + + Function throwException = + label -> { + Sentence loc = input.labeled().get(label).get().head(); + throw KEMException.compilerError( + "The generated label for a context rule conflicts with a user-defined label at " + + loc.source().get() + + " and " + + loc.location().get() + + ". Please consider renaming.", + context); }; - if (heatAtt.contains(Att.LABEL())) { - String label = heatAtt.get(Att.LABEL()); - if (input.labeled().contains(label)) { - throwException.apply(label); - } - } - - if (coolAtt.contains(Att.LABEL())) { - String label = coolAtt.get(Att.LABEL()); - if (input.labeled().contains(label)) { - throwException.apply(label); - } - } - - - return Stream.of(freezer, - Rule(insert(body, KRewrite(cooled, KSequence(heated, frozen)), input), requiresHeat, BooleanUtils.TRUE, heatAtt), - Rule(insert(body, KRewrite(KSequence(heated, frozen), cooled), input), requiresCool, BooleanUtils.TRUE, coolAtt)); + if (heatAtt.contains(Att.LABEL())) { + String label = heatAtt.get(Att.LABEL()); + if (input.labeled().contains(label)) { + throwException.apply(label); + } } - private K insert(K body, K rewrite, Module mod) { - class Holder { - boolean found = false; + if (coolAtt.contains(Att.LABEL())) { + String label = coolAtt.get(Att.LABEL()); + if (input.labeled().contains(label)) { + throwException.apply(label); } - Holder h = new Holder(); - K inserted = new TransformK() { + } + + return Stream.of( + freezer, + Rule( + insert(body, KRewrite(cooled, KSequence(heated, frozen)), input), + requiresHeat, + BooleanUtils.TRUE, + heatAtt), + Rule( + insert(body, KRewrite(KSequence(heated, frozen), cooled), input), + requiresCool, + BooleanUtils.TRUE, + coolAtt)); + } + + private K insert(K body, K rewrite, Module mod) { + class Holder { + boolean found = false; + } + Holder h = new Holder(); + K inserted = + new TransformK() { @Override public K apply(KApply k) { - if (mod.attributesFor().getOrElse(k.klabel(), () -> Att()).contains(Att.MAINCELL())) { - h.found = true; - return KApply(k.klabel(), k.items().get(0), rewrite, k.items().get(2)); - } - return super.apply(k); + if (mod.attributesFor().getOrElse(k.klabel(), () -> Att()).contains(Att.MAINCELL())) { + h.found = true; + return KApply(k.klabel(), k.items().get(0), rewrite, k.items().get(2)); + } + return super.apply(k); } - }.apply(body); - if (h.found) { - return inserted; - } else { - return rewrite; - } + }.apply(body); + if (h.found) { + return inserted; + } else { + return rewrite; } - - /** - * Check validity of context. - *

- * Currently the following conditions are checked: - * - Contexts must have at least one HOLE. - * - Contexts must have a single rewrite. - * - Only the HOLE can be rewritten in a context definition. - * - * @param context to be checked - */ - public static void checkContextValidity(Context context) { - K body = context.body(); - - int cntHoles = new FindK() { - @Override - public scala.collection.Set apply(KVariable k) { - if (k.name().equals("HOLE")) { - return org.kframework.Collections.Set(k); - } else { - return super.apply(k); - } + } + + /** + * Check validity of context. + * + *

Currently the following conditions are checked: - Contexts must have at least one HOLE. - + * Contexts must have a single rewrite. - Only the HOLE can be rewritten in a context definition. + * + * @param context to be checked + */ + public static void checkContextValidity(Context context) { + K body = context.body(); + + int cntHoles = + new FindK() { + @Override + public scala.collection.Set apply(KVariable k) { + if (k.name().equals("HOLE")) { + return org.kframework.Collections.Set(k); + } else { + return super.apply(k); } + } }.apply(body).size(); - if (cntHoles < 1) { - throw KEMException.compilerError("Contexts must have at least one HOLE.", context); - } + if (cntHoles < 1) { + throw KEMException.compilerError("Contexts must have at least one HOLE.", context); + } - int cntRewrites = new FindK() { - @Override - public scala.collection.Set apply(KRewrite k) { - return this.merge(org.kframework.Collections.Set(k), super.apply(k)); - } + int cntRewrites = + new FindK() { + @Override + public scala.collection.Set apply(KRewrite k) { + return this.merge(org.kframework.Collections.Set(k), super.apply(k)); + } }.apply(body).size(); - if (cntRewrites > 1) { - throw KEMException.compilerError("Cannot compile a context with multiple rewrites.", context); - } + if (cntRewrites > 1) { + throw KEMException.compilerError("Cannot compile a context with multiple rewrites.", context); + } - new VisitK() { - @Override - public void apply(KRewrite k) { - if (!isHOLE(k.left())) { - throw KEMException.compilerError("Only the HOLE can be rewritten in a context definition", context); - } - super.apply(k); - } + new VisitK() { + @Override + public void apply(KRewrite k) { + if (!isHOLE(k.left())) { + throw KEMException.compilerError( + "Only the HOLE can be rewritten in a context definition", context); + } + super.apply(k); + } - // return true when k is either HOLE or #SemanticCastToX(HOLE) - private boolean isHOLE(K k) { - if (k instanceof KApply kapp) { - return kapp.klabel().name().startsWith("#SemanticCastTo") && - kapp.klist().size() == 1 && - isHOLEVar(kapp.klist().items().get(0)); - } else { - return isHOLEVar(k); - } - } + // return true when k is either HOLE or #SemanticCastToX(HOLE) + private boolean isHOLE(K k) { + if (k instanceof KApply kapp) { + return kapp.klabel().name().startsWith("#SemanticCastTo") + && kapp.klist().size() == 1 + && isHOLEVar(kapp.klist().items().get(0)); + } else { + return isHOLEVar(k); + } + } - private boolean isHOLEVar(K k) { - return k instanceof KVariable && ((KVariable) k).name().equals("HOLE"); - } - }.apply(body); - } + private boolean isHOLEVar(K k) { + return k instanceof KVariable && ((KVariable) k).name().equals("HOLE"); + } + }.apply(body); + } } diff --git a/kernel/src/main/java/org/kframework/compile/ResolveFreshConfigConstants.java b/kernel/src/main/java/org/kframework/compile/ResolveFreshConfigConstants.java index 8f90f346b9c..60e1ebff8e2 100644 --- a/kernel/src/main/java/org/kframework/compile/ResolveFreshConfigConstants.java +++ b/kernel/src/main/java/org/kframework/compile/ResolveFreshConfigConstants.java @@ -1,6 +1,10 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.kore.KORE.*; + +import java.util.HashMap; +import java.util.Map; import org.kframework.attributes.Att; import org.kframework.builtin.Sorts; import org.kframework.definition.Module; @@ -15,76 +19,72 @@ import org.kframework.kore.TransformK; import org.kframework.utils.errorsystem.KEMException; -import java.util.HashMap; -import java.util.Map; - -import static org.kframework.kore.KORE.*; - public class ResolveFreshConfigConstants { - private int currentFresh = 0; - private final Map freshMap = new HashMap<>(); + private int currentFresh = 0; + private final Map freshMap = new HashMap<>(); - /** - * Replaces fresh variables in the RHS of cell initializer rules with a fresh constant. - * - * There is a question of whether fresh config variables with the same name but in - * different modules should generate different constants. For the sake of simplicity, and - * to allow the use case, no distinction is made between different modules. - * - * @param r The rule that initializes the cell - * @param body The KRewrite for the rule - * @return The body is returned with any fresh variables in the RHS replaced with a - * fresh constant - */ - private K transform(RuleOrClaim r, K body) { - if (!(r instanceof Rule rule)) { - return body; - } + /** + * Replaces fresh variables in the RHS of cell initializer rules with a fresh constant. + * + *

There is a question of whether fresh config variables with the same name but in different + * modules should generate different constants. For the sake of simplicity, and to allow the use + * case, no distinction is made between different modules. + * + * @param r The rule that initializes the cell + * @param body The KRewrite for the rule + * @return The body is returned with any fresh variables in the RHS replaced with a fresh constant + */ + private K transform(RuleOrClaim r, K body) { + if (!(r instanceof Rule rule)) { + return body; + } - if (!rule.att().contains(Att.INITIALIZER())) { - return body; - } + if (!rule.att().contains(Att.INITIALIZER())) { + return body; + } - return new TransformK() { - @Override - public K apply(KVariable k) { - if (k.name().startsWith("!")) { - if (!k.att().get(Sort.class).equals(Sorts.Int())) { - throw KEMException.compilerError("Can't resolve fresh configuration variable not of sort Int", k); - } - if (k.att().contains(Att.ANONYMOUS())) { - return KToken(Integer.toString(currentFresh++), Sorts.Int()); - } - if (!freshMap.containsKey(k)) { - freshMap.put(k, currentFresh++); - } - return KToken(Integer.toString(freshMap.get(k)), Sorts.Int()); - } - return k; - } + return new TransformK() { + @Override + public K apply(KVariable k) { + if (k.name().startsWith("!")) { + if (!k.att().get(Sort.class).equals(Sorts.Int())) { + throw KEMException.compilerError( + "Can't resolve fresh configuration variable not of sort Int", k); + } + if (k.att().contains(Att.ANONYMOUS())) { + return KToken(Integer.toString(currentFresh++), Sorts.Int()); + } + if (!freshMap.containsKey(k)) { + freshMap.put(k, currentFresh++); + } + return KToken(Integer.toString(freshMap.get(k)), Sorts.Int()); + } + return k; + } - @Override - public K apply(KRewrite k) { - return KRewrite(k.left(), apply(k.right()), k.att()); - } - }.apply(body); - } + @Override + public K apply(KRewrite k) { + return KRewrite(k.left(), apply(k.right()), k.att()); + } + }.apply(body); + } - public Module resolve(Module m) { - return ModuleTransformer.fromRuleBodyTransformerWithRule((r, body) -> transform(r, body), "Resolve fresh variables in cell initializers").apply(m); - } + public Module resolve(Module m) { + return ModuleTransformer.fromRuleBodyTransformerWithRule( + (r, body) -> transform(r, body), "Resolve fresh variables in cell initializers") + .apply(m); + } - /** - * Gets the number of fresh constants that were generated. - * - * This is used in the next stage of the pipeline where the generatedCounter cell is added - * to the configuration. The initializer rule for the generatedCounter will use this value - * so any fresh values during execution do not conflict with the values in the initial - * configuration. - * - * @return What the generatedCounter should be initialized to. - */ - public int getCurrentFresh() { - return currentFresh; - } + /** + * Gets the number of fresh constants that were generated. + * + *

This is used in the next stage of the pipeline where the generatedCounter cell is added to + * the configuration. The initializer rule for the generatedCounter will use this value so any + * fresh values during execution do not conflict with the values in the initial configuration. + * + * @return What the generatedCounter should be initialized to. + */ + public int getCurrentFresh() { + return currentFresh; + } } diff --git a/kernel/src/main/java/org/kframework/compile/ResolveFreshConstants.java b/kernel/src/main/java/org/kframework/compile/ResolveFreshConstants.java index 41b6de448f0..aea8af8c32b 100644 --- a/kernel/src/main/java/org/kframework/compile/ResolveFreshConstants.java +++ b/kernel/src/main/java/org/kframework/compile/ResolveFreshConstants.java @@ -1,13 +1,23 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; import org.kframework.attributes.Att; import org.kframework.builtin.BooleanUtils; import org.kframework.builtin.KLabels; import org.kframework.builtin.Sorts; import org.kframework.definition.Context; import org.kframework.definition.Definition; -import org.kframework.definition.Import; import org.kframework.definition.Module; import org.kframework.definition.NonTerminal; import org.kframework.definition.Production; @@ -25,255 +35,299 @@ import org.kframework.parser.inner.ParseInModule; import org.kframework.parser.inner.RuleGrammarGenerator; import org.kframework.parser.outer.Outer; -import org.kframework.utils.StringUtil; -import org.kframework.utils.file.FileUtil; import org.kframework.utils.errorsystem.KEMException; +import org.kframework.utils.file.FileUtil; +import scala.Option; import scala.collection.JavaConverters; import scala.collection.Set; -import scala.Option; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; - -import static org.kframework.Collections.*; -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; public class ResolveFreshConstants { - private final Definition def; - private final FileUtil files; - private Module m; - private final java.util.Set freshVars = new HashSet<>(); - private final Map offsets = new HashMap<>(); - private final String manualTopCell; - private final int initialFresh; + private final Definition def; + private final FileUtil files; + private Module m; + private final java.util.Set freshVars = new HashSet<>(); + private final Map offsets = new HashMap<>(); + private final String manualTopCell; + private final int initialFresh; - private void reset() { - freshVars.clear(); - offsets.clear(); - } + private void reset() { + freshVars.clear(); + offsets.clear(); + } - private Rule resolve(Rule rule) { - reset(); - analyze(rule.body()); - analyze(rule.requires()); - analyze(rule.ensures()); - finishAnalysis(); - Rule withFresh = Rule( - addFreshCell(transform(rule.body())), - transform(rule.requires()), - transform(rule.ensures()), - rule.att()); - if (rule.att().contains(Att.INITIALIZER())) { - K left = RewriteToTop.toLeft(withFresh.body()); - if (left instanceof KApply kapp) { - if (kapp.klabel().equals(KLabels.INIT_GENERATED_TOP_CELL)) { - KApply right = (KApply)RewriteToTop.toRight(withFresh.body()); - KApply cells = (KApply)right.items().get(1); - List items = new ArrayList<>(cells.items()); - items.add(KApply(KLabels.INIT_GENERATED_COUNTER_CELL)); - KApply newCells = KApply(cells.klabel(), immutable(items)); - List rightItems = new ArrayList<>(right.items()); - rightItems.set(1, newCells); - return Rule( - KRewrite(left, KApply(right.klabel(), immutable(rightItems))), - withFresh.requires(), - withFresh.ensures(), - withFresh.att()); - } - } - } - K left = RewriteToTop.toLeft(rule.body()); - if (left instanceof KApply kapp) { - if (kapp.klabel().name().equals("#withConfig")) { - left = kapp.items().get(0); - } - if (left instanceof KApply) { - kapp = (KApply)left; - if (m.attributesFor().get(kapp.klabel()).getOrElse(() -> Att()).contains(Att.FUNCTION())) { - return rule; - } - } + private Rule resolve(Rule rule) { + reset(); + analyze(rule.body()); + analyze(rule.requires()); + analyze(rule.ensures()); + finishAnalysis(); + Rule withFresh = + Rule( + addFreshCell(transform(rule.body())), + transform(rule.requires()), + transform(rule.ensures()), + rule.att()); + if (rule.att().contains(Att.INITIALIZER())) { + K left = RewriteToTop.toLeft(withFresh.body()); + if (left instanceof KApply kapp) { + if (kapp.klabel().equals(KLabels.INIT_GENERATED_TOP_CELL)) { + KApply right = (KApply) RewriteToTop.toRight(withFresh.body()); + KApply cells = (KApply) right.items().get(1); + List items = new ArrayList<>(cells.items()); + items.add(KApply(KLabels.INIT_GENERATED_COUNTER_CELL)); + KApply newCells = KApply(cells.klabel(), immutable(items)); + List rightItems = new ArrayList<>(right.items()); + rightItems.set(1, newCells); + return Rule( + KRewrite(left, KApply(right.klabel(), immutable(rightItems))), + withFresh.requires(), + withFresh.ensures(), + withFresh.att()); } - return withFresh; + } } - - private void analyze(K term) { - new VisitK() { - @Override - public void apply(KVariable k) { - if (k.name().startsWith("!")) { - freshVars.add(k); - } - super.apply(k); - } - }.apply(term); - } - - private void finishAnalysis() { - int i = 0; - for (KVariable v : freshVars) { - offsets.put(v, i++); + K left = RewriteToTop.toLeft(rule.body()); + if (left instanceof KApply kapp) { + if (kapp.klabel().name().equals("#withConfig")) { + left = kapp.items().get(0); + } + if (left instanceof KApply) { + kapp = (KApply) left; + if (m.attributesFor().get(kapp.klabel()).getOrElse(() -> Att()).contains(Att.FUNCTION())) { + return rule; } + } } + return withFresh; + } - private static final KVariable FRESH = KVariable("#Fresh", Att.empty().add(Sort.class, Sorts.Int())); + private void analyze(K term) { + new VisitK() { + @Override + public void apply(KVariable k) { + if (k.name().startsWith("!")) { + freshVars.add(k); + } + super.apply(k); + } + }.apply(term); + } - private K transform(K term) { - return new TransformK() { - @Override - public K apply(KVariable k) { - if (freshVars.contains(k)) { - Optional s = k.att().getOptional(Sort.class); - if (!s.isPresent()) { - throw KEMException.compilerError("Fresh constant used without a declared sort.", k); - } - Option lbl = m.freshFunctionFor().get(s.get()); - if (!lbl.isDefined()) { - throw KEMException.compilerError("No fresh generator defined for sort " + s, k); - } - return KApply(lbl.get(), KApply(KLabel("_+Int_"), FRESH, KToken(offsets.get(k).toString(), Sorts.Int()))); - } - return super.apply(k); - } - }.apply(term); + private void finishAnalysis() { + int i = 0; + for (KVariable v : freshVars) { + offsets.put(v, i++); } + } + + private static final KVariable FRESH = + KVariable("#Fresh", Att.empty().add(Sort.class, Sorts.Int())); - private K addFreshCell(K body) { - if (freshVars.size() == 0) { - return body; + private K transform(K term) { + return new TransformK() { + @Override + public K apply(KVariable k) { + if (freshVars.contains(k)) { + Optional s = k.att().getOptional(Sort.class); + if (!s.isPresent()) { + throw KEMException.compilerError("Fresh constant used without a declared sort.", k); + } + Option lbl = m.freshFunctionFor().get(s.get()); + if (!lbl.isDefined()) { + throw KEMException.compilerError("No fresh generator defined for sort " + s, k); + } + return KApply( + lbl.get(), + KApply(KLabel("_+Int_"), FRESH, KToken(offsets.get(k).toString(), Sorts.Int()))); } - KApply cellTerm = IncompleteCellUtils.make(KLabels.GENERATED_COUNTER_CELL, false, KRewrite(FRESH, KApply(KLabel("_+Int_"), FRESH, KToken(Integer.toString(freshVars.size()), Sorts.Int()))), false); - return KApply(KLabels.CELLS, body, cellTerm); - } + return super.apply(k); + } + }.apply(term); + } - private Context resolve(Context context) { - reset(); - analyze(context.body()); - analyze(context.requires()); - finishAnalysis(); - return new Context( - addFreshCell(transform(context.body())), - transform(context.requires()), - context.att()); + private K addFreshCell(K body) { + if (freshVars.size() == 0) { + return body; } + KApply cellTerm = + IncompleteCellUtils.make( + KLabels.GENERATED_COUNTER_CELL, + false, + KRewrite( + FRESH, + KApply( + KLabel("_+Int_"), + FRESH, + KToken(Integer.toString(freshVars.size()), Sorts.Int()))), + false); + return KApply(KLabels.CELLS, body, cellTerm); + } - private Production resolve(Production prod) { - if (prod.klabel().isDefined() && prod.klabel().get().equals(KLabels.GENERATED_TOP_CELL)) { - List pis = stream(prod.items()).collect(Collectors.toCollection(ArrayList::new)); - // expecting a production of the form C1 C2 Cx.. - // insert the GeneratedCounterCell as the last cell - pis.add(prod.items().size() - 1, NonTerminal(Sorts.GeneratedCounterCell())); - return Production(prod.klabel().get(), prod.sort(), immutable(pis), prod.att()); - } - return prod; - } + private Context resolve(Context context) { + reset(); + analyze(context.body()); + analyze(context.requires()); + finishAnalysis(); + return new Context( + addFreshCell(transform(context.body())), transform(context.requires()), context.att()); + } - private Sentence resolve(Sentence s) { - if (s instanceof Rule) { - return resolve((Rule) s); - } else if (s instanceof Context) { - return resolve((Context) s); - } else if (s instanceof Production) { - return resolve((Production) s); - } - return s; + private Production resolve(Production prod) { + if (prod.klabel().isDefined() && prod.klabel().get().equals(KLabels.GENERATED_TOP_CELL)) { + List pis = + stream(prod.items()).collect(Collectors.toCollection(ArrayList::new)); + // expecting a production of the form C1 C2 Cx.. + // insert the GeneratedCounterCell as the last cell + pis.add(prod.items().size() - 1, NonTerminal(Sorts.GeneratedCounterCell())); + return Production(prod.klabel().get(), prod.sort(), immutable(pis), prod.att()); } + return prod; + } - public ResolveFreshConstants(Definition def, String manualTopCell, FileUtil files) { - this(def, manualTopCell, files, 0); + private Sentence resolve(Sentence s) { + if (s instanceof Rule) { + return resolve((Rule) s); + } else if (s instanceof Context) { + return resolve((Context) s); + } else if (s instanceof Production) { + return resolve((Production) s); } + return s; + } - public ResolveFreshConstants(Definition def, String manualTopCell, FileUtil files, int initialFresh) { - this.def = def; - this.manualTopCell = manualTopCell; - this.files = files; - this.initialFresh = initialFresh; - } + public ResolveFreshConstants(Definition def, String manualTopCell, FileUtil files) { + this(def, manualTopCell, files, 0); + } - public Module resolve(Module m) { - this.m = m; - Set sentences = map(this::resolve, m.localSentences()); - KToken counterCellLabel = KToken("generatedCounter", Sort("#CellName")); - KApply freshCell = KApply(KLabel("#configCell"), counterCellLabel, KApply(KLabel("#cellPropertyListTerminator")), KToken(Integer.toString(initialFresh), Sorts.Int()), counterCellLabel); + public ResolveFreshConstants( + Definition def, String manualTopCell, FileUtil files, int initialFresh) { + this.def = def; + this.manualTopCell = manualTopCell; + this.files = files; + this.initialFresh = initialFresh; + } - java.util.Set counterSentences = new HashSet<>(); - counterSentences.add(Production(KLabel("getGeneratedCounterCell"), Sorts.GeneratedCounterCell(), Seq(Terminal("getGeneratedCounterCell"), Terminal("("), NonTerminal(Sorts.GeneratedTopCell()), Terminal(")")), Att.empty().add(Att.FUNCTION()))); - counterSentences.add(Rule(KRewrite(KApply(KLabel("getGeneratedCounterCell"), IncompleteCellUtils.make(KLabels.GENERATED_TOP_CELL, true, KVariable("Cell", Att.empty().add(Sort.class, Sorts.GeneratedCounterCell())), true)), KVariable("Cell", Att.empty().add(Sort.class, Sorts.GeneratedCounterCell()))), BooleanUtils.TRUE, BooleanUtils.TRUE)); + public Module resolve(Module m) { + this.m = m; + Set sentences = map(this::resolve, m.localSentences()); + KToken counterCellLabel = KToken("generatedCounter", Sort("#CellName")); + KApply freshCell = + KApply( + KLabel("#configCell"), + counterCellLabel, + KApply(KLabel("#cellPropertyListTerminator")), + KToken(Integer.toString(initialFresh), Sorts.Int()), + counterCellLabel); - if (m.name().equals(def.mainModule().name())) { - if (!m.definedKLabels().contains(KLabels.GENERATED_TOP_CELL)) { - RuleGrammarGenerator gen = new RuleGrammarGenerator(def); - ParseInModule mod = RuleGrammarGenerator.getCombinedGrammar(gen.getConfigGrammar(m), true, files); - ConfigurationInfoFromModule configInfo = new ConfigurationInfoFromModule(m); - Sort topCellSort; - try { - topCellSort = configInfo.getRootCell(); - } catch (KEMException e) { - if (manualTopCell != null) { - topCellSort = Outer.parseSort(manualTopCell); - } else { - throw e; - } - } - KLabel topCellLabel = configInfo.getCellLabel(topCellSort); - Production prod = m.productionsFor().apply(topCellLabel).head(); - KToken cellName = KToken(prod.att().get(Att.CELL_NAME()), Sort("#CellName")); + java.util.Set counterSentences = new HashSet<>(); + counterSentences.add( + Production( + KLabel("getGeneratedCounterCell"), + Sorts.GeneratedCounterCell(), + Seq( + Terminal("getGeneratedCounterCell"), + Terminal("("), + NonTerminal(Sorts.GeneratedTopCell()), + Terminal(")")), + Att.empty().add(Att.FUNCTION()))); + counterSentences.add( + Rule( + KRewrite( + KApply( + KLabel("getGeneratedCounterCell"), + IncompleteCellUtils.make( + KLabels.GENERATED_TOP_CELL, + true, + KVariable( + "Cell", Att.empty().add(Sort.class, Sorts.GeneratedCounterCell())), + true)), + KVariable("Cell", Att.empty().add(Sort.class, Sorts.GeneratedCounterCell()))), + BooleanUtils.TRUE, + BooleanUtils.TRUE)); - KToken topCellToken = KToken(KLabels.GENERATED_TOP_CELL_NAME, Sort("#CellName")); - K generatedTop = KApply(KLabel("#configCell"), topCellToken, KApply(KLabel("#cellPropertyListTerminator")), KApply(KLabels.CELLS, KApply(KLabel("#externalCell"), cellName), freshCell), topCellToken); - Set newSentences = GenerateSentencesFromConfigDecl.gen(generatedTop, BooleanUtils.TRUE, Att.empty(), mod.getExtensionModule()); - sentences = (Set) sentences.$bar(newSentences); - sentences = (Set) sentences.$bar(immutable(counterSentences)); - } - } - if (m.localKLabels().contains(KLabels.GENERATED_TOP_CELL)) { - RuleGrammarGenerator gen = new RuleGrammarGenerator(def); - ParseInModule mod = RuleGrammarGenerator.getCombinedGrammar(gen.getConfigGrammar(m), true, files); - Set newSentences = GenerateSentencesFromConfigDecl.gen(freshCell, BooleanUtils.TRUE, Att.empty(), mod.getExtensionModule()); - sentences = (Set) sentences.$bar(newSentences); - sentences = (Set) sentences.$bar(immutable(counterSentences)); - } - if (sentences.equals(m.localSentences())) { - return m; + if (m.name().equals(def.mainModule().name())) { + if (!m.definedKLabels().contains(KLabels.GENERATED_TOP_CELL)) { + RuleGrammarGenerator gen = new RuleGrammarGenerator(def); + ParseInModule mod = + RuleGrammarGenerator.getCombinedGrammar(gen.getConfigGrammar(m), true, files); + ConfigurationInfoFromModule configInfo = new ConfigurationInfoFromModule(m); + Sort topCellSort; + try { + topCellSort = configInfo.getRootCell(); + } catch (KEMException e) { + if (manualTopCell != null) { + topCellSort = Outer.parseSort(manualTopCell); + } else { + throw e; + } } - // fix the format after inserting the GeneratedCounterCell - sentences = immutable(stream(sentences).map(s -> s instanceof Production ? fixFormat((Production) s) : s).collect(Collectors.toSet())); - return Module(m.name(), m.imports(), sentences, m.att()); + KLabel topCellLabel = configInfo.getCellLabel(topCellSort); + Production prod = m.productionsFor().apply(topCellLabel).head(); + KToken cellName = KToken(prod.att().get(Att.CELL_NAME()), Sort("#CellName")); + + KToken topCellToken = KToken(KLabels.GENERATED_TOP_CELL_NAME, Sort("#CellName")); + K generatedTop = + KApply( + KLabel("#configCell"), + topCellToken, + KApply(KLabel("#cellPropertyListTerminator")), + KApply(KLabels.CELLS, KApply(KLabel("#externalCell"), cellName), freshCell), + topCellToken); + Set newSentences = + GenerateSentencesFromConfigDecl.gen( + generatedTop, BooleanUtils.TRUE, Att.empty(), mod.getExtensionModule()); + sentences = (Set) sentences.$bar(newSentences); + sentences = (Set) sentences.$bar(immutable(counterSentences)); + } + } + if (m.localKLabels().contains(KLabels.GENERATED_TOP_CELL)) { + RuleGrammarGenerator gen = new RuleGrammarGenerator(def); + ParseInModule mod = + RuleGrammarGenerator.getCombinedGrammar(gen.getConfigGrammar(m), true, files); + Set newSentences = + GenerateSentencesFromConfigDecl.gen( + freshCell, BooleanUtils.TRUE, Att.empty(), mod.getExtensionModule()); + sentences = (Set) sentences.$bar(newSentences); + sentences = (Set) sentences.$bar(immutable(counterSentences)); + } + if (sentences.equals(m.localSentences())) { + return m; } + // fix the format after inserting the GeneratedCounterCell + sentences = + immutable( + stream(sentences) + .map(s -> s instanceof Production ? fixFormat((Production) s) : s) + .collect(Collectors.toSet())); + return Module(m.name(), m.imports(), sentences, m.att()); + } - private static Production fixFormat(Production prod) { - if (prod.klabel().isDefined() && prod.klabel().get().equals(KLabels.GENERATED_TOP_CELL)) { - List cellPositions = new ArrayList(); - int i = 1; - for (ProductionItem p: JavaConverters.seqAsJavaList(prod.items())) { - if (p instanceof NonTerminal nt) { - if (! nt.sort().equals(Sorts.GeneratedCounterCell())) { - cellPositions.add(i); - } - } - i++; - } - StringBuilder format = new StringBuilder(); - if (cellPositions.size() == 1) { - format.append("%").append(cellPositions.get(0)); - } else { - format.append("%1%i"); - int j; - for (j = 0; j < cellPositions.size(); j++) { - format.append("%n%").append(cellPositions.get(j)); - } - format.append("%d%n%").append(cellPositions.get(j - 1) + 2); - } - return prod.withAtt(prod.att().add(Att.FORMAT(), format.toString())); + private static Production fixFormat(Production prod) { + if (prod.klabel().isDefined() && prod.klabel().get().equals(KLabels.GENERATED_TOP_CELL)) { + List cellPositions = new ArrayList(); + int i = 1; + for (ProductionItem p : JavaConverters.seqAsJavaList(prod.items())) { + if (p instanceof NonTerminal nt) { + if (!nt.sort().equals(Sorts.GeneratedCounterCell())) { + cellPositions.add(i); + } } - return prod; + i++; + } + StringBuilder format = new StringBuilder(); + if (cellPositions.size() == 1) { + format.append("%").append(cellPositions.get(0)); + } else { + format.append("%1%i"); + int j; + for (j = 0; j < cellPositions.size(); j++) { + format.append("%n%").append(cellPositions.get(j)); + } + format.append("%d%n%").append(cellPositions.get(j - 1) + 2); + } + return prod.withAtt(prod.att().add(Att.FORMAT(), format.toString())); } + return prod; + } } - diff --git a/kernel/src/main/java/org/kframework/compile/ResolveFun.java b/kernel/src/main/java/org/kframework/compile/ResolveFun.java index 9f0580a0d25..5b2dfdf7127 100644 --- a/kernel/src/main/java/org/kframework/compile/ResolveFun.java +++ b/kernel/src/main/java/org/kframework/compile/ResolveFun.java @@ -1,10 +1,21 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; import org.kframework.attributes.Att; import org.kframework.builtin.BooleanUtils; import org.kframework.builtin.KLabels; import org.kframework.builtin.Sorts; +import org.kframework.compile.checks.ComputeUnboundVariables; import org.kframework.definition.Context; import org.kframework.definition.ContextAlias; import org.kframework.definition.Module; @@ -23,234 +34,219 @@ import org.kframework.kore.KVariable; import org.kframework.kore.Sort; import org.kframework.kore.TransformK; -import org.kframework.compile.checks.ComputeUnboundVariables; import org.kframework.utils.errorsystem.KEMException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.UnaryOperator; -import java.util.stream.Collectors; - -import static org.kframework.Collections.*; -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; - /** * Resolves #fun KApplies. * - * The rule Ctx[#fun(Pattern)(Expression)] is equivalent to the following sentences assuming some completely unique KLabel #lambda1 not used in any token: + *

The rule Ctx[#fun(Pattern)(Expression)] is equivalent to the following sentences assuming some + * completely unique KLabel #lambda1 not used in any token: * - * rule Ctx[#lambda1(Expression)] - * syntax K ::= #lambda1(K) [function] - * rule #lambda1(LHS) => RHS + *

rule Ctx[#lambda1(Expression)] syntax K ::= #lambda1(K) [function] rule #lambda1(LHS) => RHS * - * Where LHS is the LHS of Pattern and RHS is the RHS of Pattern. + *

Where LHS is the LHS of Pattern and RHS is the RHS of Pattern. * - * Note that if a variable is used in the rhs of the fun expression which is not bound in its lhs, it is added as a - * closure parameter to the generated function. + *

Note that if a variable is used in the rhs of the fun expression which is not bound in its + * lhs, it is added as a closure parameter to the generated function. * - * We purposely resolve this construct as early as possible in the pipeline so that later stages which insert implicit - * side conditions into the rule insert them into the correct rule. + *

We purposely resolve this construct as early as possible in the pipeline so that later stages + * which insert implicit side conditions into the rule insert them into the correct rule. */ public class ResolveFun { - private final Set funProds = new HashSet<>(); - private final Set funRules = new HashSet<>(); - private Module module; - private AddSortInjections inj; - private final Set klabels = new HashSet<>(); - - private KLabel getUniqueLambdaLabel(String nameHint1, String nameHint2) { - if (klabels.isEmpty()) { - klabels.addAll(mutable(module.definedKLabels())); + private final Set funProds = new HashSet<>(); + private final Set funRules = new HashSet<>(); + private Module module; + private AddSortInjections inj; + private final Set klabels = new HashSet<>(); + + private KLabel getUniqueLambdaLabel(String nameHint1, String nameHint2) { + if (klabels.isEmpty()) { + klabels.addAll(mutable(module.definedKLabels())); + } + int counter = 0; + KLabel freezer; + do { + freezer = + KLabel("#lambda" + nameHint1 + "_" + nameHint2 + "_" + (counter++ == 0 ? "" : counter)); + } while (klabels.contains(freezer)); + klabels.add(freezer); + return freezer; + } + + private Rule resolve(Rule rule) { + return new Rule( + transform(rule.body()), transform(rule.requires()), transform(rule.ensures()), rule.att()); + } + + private K transform(K body) { + return new TransformK() { + @Override + public K apply(KApply k) { + KLabel lbl = k.klabel(); + if (!(lbl instanceof KVariable) && lbl.name().equals("#fun2") + || lbl.name().equals("#fun3") + || lbl.name().equals("#let") + || lbl.equals(KLabels.IN_K) + || lbl.equals(KLabels.NOT_IN_K)) { + String nameHint1 = "", nameHint2 = ""; + K arg, body; + if (lbl.name().equals("#fun3")) { + body = KRewrite(k.items().get(0), k.items().get(1)); + arg = k.items().get(2); + } else if (lbl.name().equals("#let")) { + body = KRewrite(k.items().get(0), k.items().get(2)); + arg = k.items().get(1); + } else { + body = k.items().get(0); + arg = k.items().get(1); + } + if (arg instanceof KVariable) { + nameHint1 = ((KVariable) arg).name(); + } else if (arg instanceof KApply + && ((KApply) arg).klabel().name().startsWith("#SemanticCastTo") + && ((KApply) arg).items().get(0) instanceof KVariable) { + nameHint1 = ((KVariable) ((KApply) arg).items().get(0)).name(); + } + if (body instanceof KApply) { + nameHint2 = ((KApply) body).klabel().name(); + } + KLabel fun = getUniqueLambdaLabel(nameHint1, nameHint2); + Sort lhsSort = sort(RewriteToTop.toLeft(body)); + Sort argSort = sort(arg); + Sort lubSort = AddSortInjections.lubSort(lhsSort, argSort, null, body, module); + if (lbl.name().equals("#fun3") + || lbl.name().equals("#fun2") + || lbl.name().equals("#let")) { + funProds.add(funProd(fun, body, lubSort)); + funRules.add(funRule(fun, body, k.att())); + } else { + funProds.add(predProd(fun, body, lubSort)); + funRules.add(predRule(fun, body, k.att())); + funRules.add(owiseRule(fun, body, lubSort, k.att())); + } + List klist = new ArrayList<>(); + klist.add(apply(arg)); + klist.addAll(closure(body)); + K funCall = KApply(fun, KList(klist)); + if (lbl.equals(KLabels.NOT_IN_K)) { + return BooleanUtils.not(funCall); + } + return funCall; } - int counter = 0; - KLabel freezer; - do { - freezer = KLabel("#lambda" + nameHint1 + "_" + nameHint2 + "_" + (counter++ == 0 ? "" : counter)); - } while (klabels.contains(freezer)); - klabels.add(freezer); - return freezer; - } - - private Rule resolve(Rule rule) { - return new Rule( - transform(rule.body()), - transform(rule.requires()), - transform(rule.ensures()), - rule.att()); - } - - private K transform(K body) { - return new TransformK() { - @Override - public K apply(KApply k) { - KLabel lbl = k.klabel(); - if (!(lbl instanceof KVariable) && lbl.name().equals("#fun2") || lbl.name().equals("#fun3") || lbl.name().equals("#let") || lbl.equals(KLabels.IN_K) || lbl.equals(KLabels.NOT_IN_K)) { - String nameHint1 = "", nameHint2 = ""; - K arg, body; - if (lbl.name().equals("#fun3")) { - body = KRewrite(k.items().get(0), k.items().get(1)); - arg = k.items().get(2); - } else if (lbl.name().equals("#let")) { - body = KRewrite(k.items().get(0), k.items().get(2)); - arg = k.items().get(1); - } else { - body = k.items().get(0); - arg = k.items().get(1); - } - if (arg instanceof KVariable) { - nameHint1 = ((KVariable) arg).name(); - } else if (arg instanceof KApply - && ((KApply) arg).klabel().name().startsWith("#SemanticCastTo") - && ((KApply) arg).items().get(0) instanceof KVariable) { - nameHint1 = ((KVariable) ((KApply) arg).items().get(0)).name(); - } - if (body instanceof KApply) { - nameHint2 = ((KApply) body).klabel().name(); - } - KLabel fun = getUniqueLambdaLabel(nameHint1, nameHint2); - Sort lhsSort = sort(RewriteToTop.toLeft(body)); - Sort argSort = sort(arg); - Sort lubSort = AddSortInjections.lubSort(lhsSort, argSort, null, body, module); - if (lbl.name().equals("#fun3") || lbl.name().equals("#fun2") || lbl.name().equals("#let")) { - funProds.add(funProd(fun, body, lubSort)); - funRules.add(funRule(fun, body, k.att())); - } else { - funProds.add(predProd(fun, body, lubSort)); - funRules.add(predRule(fun, body, k.att())); - funRules.add(owiseRule(fun, body, lubSort, k.att())); - } - List klist = new ArrayList<>(); - klist.add(apply(arg)); - klist.addAll(closure(body)); - K funCall = KApply(fun, KList(klist)); - if (lbl.equals(KLabels.NOT_IN_K)) { - return BooleanUtils.not(funCall); - } - return funCall; - } - return super.apply(k); - } - }.apply(body); - } - - private Rule funRule(KLabel fun, K k, Att att) { - return lambdaRule(fun, k, k, att, RewriteToTop::toRight); - } - - private Rule predRule(KLabel fun, K k, Att att) { - return lambdaRule(fun, k, k, att, x -> BooleanUtils.TRUE); - } - - private Rule owiseRule(KLabel fun, K k, Sort arg, Att att) { - return lambdaRule(fun, KApply(KLabel("#SemanticCastTo" + arg.toString()), KVariable("#Owise")), k, att.add(Att.OWISE()), x -> BooleanUtils.FALSE); - } - - private Rule lambdaRule(KLabel fun, K body, K closure, Att att, UnaryOperator getRHS) { - K resolved = transform(body); - K withAnonVars = new ResolveAnonVar().resolveK(resolved); - List klist = new ArrayList<>(); - klist.add(RewriteToTop.toLeft(withAnonVars)); - klist.addAll(closure(closure)); - K rewrite = KRewrite(KApply(fun, KList(klist)), getRHS.apply(withAnonVars)); - K renamed = new TransformK() { - public K apply(KVariable k) { - if (k.name().startsWith("!")) { - return KVariable("#_" + k.name().substring(1), k.att()); - } - return k; + return super.apply(k); + } + }.apply(body); + } + + private Rule funRule(KLabel fun, K k, Att att) { + return lambdaRule(fun, k, k, att, RewriteToTop::toRight); + } + + private Rule predRule(KLabel fun, K k, Att att) { + return lambdaRule(fun, k, k, att, x -> BooleanUtils.TRUE); + } + + private Rule owiseRule(KLabel fun, K k, Sort arg, Att att) { + return lambdaRule( + fun, + KApply(KLabel("#SemanticCastTo" + arg.toString()), KVariable("#Owise")), + k, + att.add(Att.OWISE()), + x -> BooleanUtils.FALSE); + } + + private Rule lambdaRule(KLabel fun, K body, K closure, Att att, UnaryOperator getRHS) { + K resolved = transform(body); + K withAnonVars = new ResolveAnonVar().resolveK(resolved); + List klist = new ArrayList<>(); + klist.add(RewriteToTop.toLeft(withAnonVars)); + klist.addAll(closure(closure)); + K rewrite = KRewrite(KApply(fun, KList(klist)), getRHS.apply(withAnonVars)); + K renamed = + new TransformK() { + public K apply(KVariable k) { + if (k.name().startsWith("!")) { + return KVariable("#_" + k.name().substring(1), k.att()); } + return k; + } }.apply(rewrite); - return Rule(renamed, - BooleanUtils.TRUE, BooleanUtils.TRUE, att); - } - - private List closure(K k) { - Set errors = new HashSet<>(); - Set vars = new HashSet<>(); - List result = new ArrayList<>(); - new GatherVarsVisitor(true, errors, vars, false).apply(k); - new ComputeUnboundVariables(true, true, errors, vars, result::add).apply(k); - return result; - } - - private Production funProd(KLabel fun, K k, Sort arg) { - return lambdaProd(fun, k, arg, sort(RewriteToTop.toRight(k))); - } - - private Production predProd(KLabel fun, K k, Sort arg) { - return lambdaProd(fun, k, arg, Sorts.Bool()); - } - - private Production lambdaProd(KLabel fun, K k, Sort arg, Sort rhs) { - List pis = new ArrayList<>(); - pis.add(Terminal(fun.name())); - pis.add(Terminal("(")); - pis.add(NonTerminal(arg)); - for (KVariable var : closure(k)) { - pis.add(Terminal(",")); - pis.add(NonTerminal(var.att().getOptional(Sort.class).orElse(Sorts.K()))); - } - pis.add(Terminal(")")); - return Production(fun, rhs, - immutable(pis), - Att().add(Att.FUNCTION())); - } - - private Sort sort(K k) { - if (k instanceof KSequence) - return Sorts.K(); - if (k instanceof KAs) - return sort(((KAs) k).pattern()); - if (k instanceof InjectedKLabel) - return Sorts.KItem(); - if (k instanceof KToken) - return ((KToken) k).sort(); - if (k instanceof KApply) { - return inj.sort(k, Sorts.K()); - } - if (k instanceof KVariable) - return Sorts.K(); - throw KEMException.compilerError("Could not compute sort of term", k); - } - - private Context resolve(Context context) { - return new Context( - transform(context.body()), - transform(context.requires()), - context.att()); - } - - private ContextAlias resolve(ContextAlias context) { - return new ContextAlias( - transform(context.body()), - transform(context.requires()), - context.att()); - } - - public Sentence resolve(Sentence s) { - if (s instanceof Rule) { - return resolve((Rule) s); - } else if (s instanceof Context) { - return resolve((Context) s); - } else if (s instanceof ContextAlias) { - return resolve((ContextAlias) s); - } else { - return s; - } - } - - public Module resolve(Module m) { - module = Kompile.subsortKItem(m); - inj = new AddSortInjections(module); - funProds.clear(); - funRules.clear(); - Set newSentences = stream(m.localSentences()).map(this::resolve).collect(Collectors.toSet()); - newSentences.addAll(funProds); - newSentences.addAll(funRules); - return Module(m.name(), m.imports(), immutable(newSentences), m.att()); - } + return Rule(renamed, BooleanUtils.TRUE, BooleanUtils.TRUE, att); + } + + private List closure(K k) { + Set errors = new HashSet<>(); + Set vars = new HashSet<>(); + List result = new ArrayList<>(); + new GatherVarsVisitor(true, errors, vars, false).apply(k); + new ComputeUnboundVariables(true, true, errors, vars, result::add).apply(k); + return result; + } + + private Production funProd(KLabel fun, K k, Sort arg) { + return lambdaProd(fun, k, arg, sort(RewriteToTop.toRight(k))); + } + + private Production predProd(KLabel fun, K k, Sort arg) { + return lambdaProd(fun, k, arg, Sorts.Bool()); + } + + private Production lambdaProd(KLabel fun, K k, Sort arg, Sort rhs) { + List pis = new ArrayList<>(); + pis.add(Terminal(fun.name())); + pis.add(Terminal("(")); + pis.add(NonTerminal(arg)); + for (KVariable var : closure(k)) { + pis.add(Terminal(",")); + pis.add(NonTerminal(var.att().getOptional(Sort.class).orElse(Sorts.K()))); + } + pis.add(Terminal(")")); + return Production(fun, rhs, immutable(pis), Att().add(Att.FUNCTION())); + } + + private Sort sort(K k) { + if (k instanceof KSequence) return Sorts.K(); + if (k instanceof KAs) return sort(((KAs) k).pattern()); + if (k instanceof InjectedKLabel) return Sorts.KItem(); + if (k instanceof KToken) return ((KToken) k).sort(); + if (k instanceof KApply) { + return inj.sort(k, Sorts.K()); + } + if (k instanceof KVariable) return Sorts.K(); + throw KEMException.compilerError("Could not compute sort of term", k); + } + + private Context resolve(Context context) { + return new Context(transform(context.body()), transform(context.requires()), context.att()); + } + + private ContextAlias resolve(ContextAlias context) { + return new ContextAlias( + transform(context.body()), transform(context.requires()), context.att()); + } + + public Sentence resolve(Sentence s) { + if (s instanceof Rule) { + return resolve((Rule) s); + } else if (s instanceof Context) { + return resolve((Context) s); + } else if (s instanceof ContextAlias) { + return resolve((ContextAlias) s); + } else { + return s; + } + } + + public Module resolve(Module m) { + module = Kompile.subsortKItem(m); + inj = new AddSortInjections(module); + funProds.clear(); + funRules.clear(); + Set newSentences = + stream(m.localSentences()).map(this::resolve).collect(Collectors.toSet()); + newSentences.addAll(funProds); + newSentences.addAll(funRules); + return Module(m.name(), m.imports(), immutable(newSentences), m.att()); + } } diff --git a/kernel/src/main/java/org/kframework/compile/ResolveFunctionWithConfig.java b/kernel/src/main/java/org/kframework/compile/ResolveFunctionWithConfig.java index 4d438c92cff..6111413625b 100644 --- a/kernel/src/main/java/org/kframework/compile/ResolveFunctionWithConfig.java +++ b/kernel/src/main/java/org/kframework/compile/ResolveFunctionWithConfig.java @@ -1,6 +1,14 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.kframework.Collections; import org.kframework.attributes.Att; import org.kframework.builtin.KLabels; @@ -14,226 +22,245 @@ import org.kframework.definition.Rule; import org.kframework.definition.RuleOrClaim; import org.kframework.definition.Sentence; +import org.kframework.kore.FoldK; import org.kframework.kore.K; import org.kframework.kore.KApply; import org.kframework.kore.KLabel; import org.kframework.kore.KRewrite; import org.kframework.kore.KVariable; import org.kframework.kore.Sort; -import org.kframework.kore.FoldK; import org.kframework.kore.TransformK; import org.kframework.utils.errorsystem.KEMException; - -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - import scala.collection.immutable.List; -import static org.kframework.Collections.*; -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; - public class ResolveFunctionWithConfig { - private final Set withConfigFunctions = new HashSet<>(); - private final Sort topCell; - private final KLabel topCellLabel; + private final Set withConfigFunctions = new HashSet<>(); + private final Sort topCell; + private final KLabel topCellLabel; - public ResolveFunctionWithConfig(Definition d) { - this(d.mainModule()); - } + public ResolveFunctionWithConfig(Definition d) { + this(d.mainModule()); + } - public ResolveFunctionWithConfig(Module mod) { - ComputeTransitiveFunctionDependencies deps = new ComputeTransitiveFunctionDependencies(mod); - Set functions = stream(mod.productions()).filter(p -> p.att().contains(Att.FUNCTION())).map(p -> p.klabel().get()).collect(Collectors.toSet()); - withConfigFunctions.addAll(functions.stream().filter(f -> stream(mod.rulesFor().getOrElse(f, () -> Collections.Set())).anyMatch(r -> ruleNeedsConfig(r))).collect(Collectors.toSet())); - withConfigFunctions.addAll(deps.ancestors(withConfigFunctions)); - ConfigurationInfoFromModule info = new ConfigurationInfoFromModule(mod); - topCell = Sorts.GeneratedTopCell(); - topCellLabel = KLabels.GENERATED_TOP_CELL; - CONFIG_VAR = KVariable("#Configuration", Att().add(Sort.class, topCell).add(Att.WITH_CONFIG())); + public ResolveFunctionWithConfig(Module mod) { + ComputeTransitiveFunctionDependencies deps = new ComputeTransitiveFunctionDependencies(mod); + Set functions = + stream(mod.productions()) + .filter(p -> p.att().contains(Att.FUNCTION())) + .map(p -> p.klabel().get()) + .collect(Collectors.toSet()); + withConfigFunctions.addAll( + functions.stream() + .filter( + f -> + stream(mod.rulesFor().getOrElse(f, () -> Collections.Set())) + .anyMatch(r -> ruleNeedsConfig(r))) + .collect(Collectors.toSet())); + withConfigFunctions.addAll(deps.ancestors(withConfigFunctions)); + ConfigurationInfoFromModule info = new ConfigurationInfoFromModule(mod); + topCell = Sorts.GeneratedTopCell(); + topCellLabel = KLabels.GENERATED_TOP_CELL; + CONFIG_VAR = KVariable("#Configuration", Att().add(Sort.class, topCell).add(Att.WITH_CONFIG())); + } + + private boolean ruleNeedsConfig(RuleOrClaim r) { + if (r.body() instanceof KApply && ((KApply) r.body()).klabel().name().equals("#withConfig")) { + return true; } + FoldK hasVarNeedsConfig = + new FoldK() { + @Override + public Boolean unit() { + return false; + } - private boolean ruleNeedsConfig(RuleOrClaim r) { - if (r.body() instanceof KApply && ((KApply)r.body()).klabel().name().equals("#withConfig")) { - return true; - } - FoldK hasVarNeedsConfig = new FoldK() { - @Override - public Boolean unit() { - return false; - } - - @Override - public Boolean merge(Boolean a, Boolean b) { - return a || b; - } - - @Override - public Boolean apply(KVariable k) { - return k.name().startsWith("!") || k.name().equals("#Configuration"); - } + @Override + public Boolean merge(Boolean a, Boolean b) { + return a || b; + } + + @Override + public Boolean apply(KVariable k) { + return k.name().startsWith("!") || k.name().equals("#Configuration"); + } }; - return hasVarNeedsConfig.apply(RewriteToTop.toRight(r.body())) || hasVarNeedsConfig.apply(r.requires()) || hasVarNeedsConfig.apply(r.ensures()); - } + return hasVarNeedsConfig.apply(RewriteToTop.toRight(r.body())) + || hasVarNeedsConfig.apply(r.requires()) + || hasVarNeedsConfig.apply(r.ensures()); + } - public RuleOrClaim resolve(RuleOrClaim rule, Module m) { - return rule.newInstance( - transform(resolve(rule.body(), m), m), - transform(rule.requires(), m), - transform(rule.ensures(), m), - rule.att()); - } + public RuleOrClaim resolve(RuleOrClaim rule, Module m) { + return rule.newInstance( + transform(resolve(rule.body(), m), m), + transform(rule.requires(), m), + transform(rule.ensures(), m), + rule.att()); + } - public Context resolve(Context context, Module m) { - return new Context( - transform(context.body(), m), - transform(context.requires(), m), - context.att()); - } + public Context resolve(Context context, Module m) { + return new Context( + transform(context.body(), m), transform(context.requires(), m), context.att()); + } - public ContextAlias resolve(ContextAlias context, Module m) { - return new ContextAlias( - transform(context.body(), m), - transform(context.requires(), m), - context.att()); - } + public ContextAlias resolve(ContextAlias context, Module m) { + return new ContextAlias( + transform(context.body(), m), transform(context.requires(), m), context.att()); + } - public final KVariable CONFIG_VAR; + public final KVariable CONFIG_VAR; - private K transform(K term, Module module) { - return new TransformK() { - @Override - public K apply(KApply kapp) { - if (!kapp.items().isEmpty() && kapp.items().get(kapp.items().size() - 1).att().contains(Att.WITH_CONFIG())) { - return super.apply(kapp); - } - if (withConfigFunctions.contains(kapp.klabel())) { - return KApply(kapp.klabel(), KList(Stream.concat(kapp.items().stream().map(this::apply), Stream.of(CONFIG_VAR)).collect(Collections.toList())), kapp.att()); - } + private K transform(K term, Module module) { + return new TransformK() { + @Override + public K apply(KApply kapp) { + if (!kapp.items().isEmpty() + && kapp.items().get(kapp.items().size() - 1).att().contains(Att.WITH_CONFIG())) { return super.apply(kapp); } - }.apply(term); - } + if (withConfigFunctions.contains(kapp.klabel())) { + return KApply( + kapp.klabel(), + KList( + Stream.concat(kapp.items().stream().map(this::apply), Stream.of(CONFIG_VAR)) + .collect(Collections.toList())), + kapp.att()); + } + return super.apply(kapp); + } + }.apply(term); + } - private K resolve(K body, Module module) { - if (body instanceof KApply kapp) { - if (kapp.klabel().name().equals("#withConfig")) { - K fun = kapp.items().get(0); - K cell = kapp.items().get(1); - K rhs = null; - KRewrite rew = null; - if (fun instanceof KRewrite) { - rew = (KRewrite)fun; - fun = rew.left(); - rhs = rew.right(); - } - if (!(fun instanceof KApply funKApp)) { - throw KEMException.compilerError("Found term that is not a cell or a function at the top of a rule.", fun); - } - if (!module.attributesFor().apply(funKApp.klabel()).contains(Att.FUNCTION())) { - throw KEMException.compilerError("Found term that is not a cell or a function at the top of a rule.", fun); - } - if (!(cell instanceof KApply cellKApp)) { - throw KEMException.compilerError("Found term that is not a cell in the context of a function rule.", cell); - } - K secondChild; - if (cellKApp.klabel().equals(topCellLabel)) { - secondChild = cell; - } else { - secondChild = IncompleteCellUtils.make(topCellLabel, true, cell, true); - } - List items = Stream.concat(funKApp.items().stream(), Stream.of(KAs(secondChild, CONFIG_VAR, Att().add(Att.WITH_CONFIG())))).collect(Collections.toList()); - K result = KApply(funKApp.klabel(), KList(items), funKApp.att()); - if (rhs == null) { - return result; - } else { - return KRewrite(result, rhs, rew.att()); - } + private K resolve(K body, Module module) { + if (body instanceof KApply kapp) { + if (kapp.klabel().name().equals("#withConfig")) { + K fun = kapp.items().get(0); + K cell = kapp.items().get(1); + K rhs = null; + KRewrite rew = null; + if (fun instanceof KRewrite) { + rew = (KRewrite) fun; + fun = rew.left(); + rhs = rew.right(); + } + if (!(fun instanceof KApply funKApp)) { + throw KEMException.compilerError( + "Found term that is not a cell or a function at the top of a rule.", fun); + } + if (!module.attributesFor().apply(funKApp.klabel()).contains(Att.FUNCTION())) { + throw KEMException.compilerError( + "Found term that is not a cell or a function at the top of a rule.", fun); + } + if (!(cell instanceof KApply cellKApp)) { + throw KEMException.compilerError( + "Found term that is not a cell in the context of a function rule.", cell); + } + K secondChild; + if (cellKApp.klabel().equals(topCellLabel)) { + secondChild = cell; + } else { + secondChild = IncompleteCellUtils.make(topCellLabel, true, cell, true); + } + List items = + Stream.concat( + funKApp.items().stream(), + Stream.of(KAs(secondChild, CONFIG_VAR, Att().add(Att.WITH_CONFIG())))) + .collect(Collections.toList()); + K result = KApply(funKApp.klabel(), KList(items), funKApp.att()); + if (rhs == null) { + return result; + } else { + return KRewrite(result, rhs, rew.att()); } } - return body; } + return body; + } - private Production resolve(Production prod) { - if (prod.klabel().isDefined() && withConfigFunctions.contains(prod.klabel().get())) { - List pis = Stream.concat(stream(prod.items()), Stream.of(NonTerminal(topCell))).collect(Collections.toList()); - return Production(prod.klabel(), prod.params(), prod.sort(), pis, prod.att()); - } - return prod; + private Production resolve(Production prod) { + if (prod.klabel().isDefined() && withConfigFunctions.contains(prod.klabel().get())) { + List pis = + Stream.concat(stream(prod.items()), Stream.of(NonTerminal(topCell))) + .collect(Collections.toList()); + return Production(prod.klabel(), prod.params(), prod.sort(), pis, prod.att()); } + return prod; + } - public K resolveConfigVar(K body, K requires, K ensures) { - FoldK hasConfig = new FoldK() { + public K resolveConfigVar(K body, K requires, K ensures) { + FoldK hasConfig = + new FoldK() { @Override public Boolean unit() { - return false; + return false; } @Override public Boolean apply(KVariable k) { - return k.name().equals("#Configuration"); + return k.name().equals("#Configuration"); } @Override public Boolean merge(Boolean a, Boolean b) { - return a || b; + return a || b; + } + }; + if (new FoldK() { + @Override + public Boolean unit() { + return false; + } + + @Override + public Boolean apply(KRewrite k) { + return true; } - }; - if (new FoldK() { - @Override - public Boolean unit() { - return false; - } - - @Override - public Boolean apply(KRewrite k) { - return true; - } - - @Override - public Boolean merge(Boolean a, Boolean b) { - return a || b; - } - }.apply(body) && (hasConfig.apply(body) || hasConfig.apply(requires) || hasConfig.apply(ensures))) { - K left = RewriteToTop.toLeft(body); - if (left instanceof KApply && ((KApply)left).klabel().equals(topCellLabel)) { - body = KRewrite(KAs(RewriteToTop.toLeft(body), CONFIG_VAR), RewriteToTop.toRight(body)); - } - } - return body; - } - public Sentence resolveConfigVar(Sentence s) { - if (s instanceof RuleOrClaim r) { - return r.newInstance(resolveConfigVar(r.body(), r.requires(), r.ensures()), r.requires(), r.ensures(), r.att()); + @Override + public Boolean merge(Boolean a, Boolean b) { + return a || b; + } + }.apply(body) + && (hasConfig.apply(body) || hasConfig.apply(requires) || hasConfig.apply(ensures))) { + K left = RewriteToTop.toLeft(body); + if (left instanceof KApply && ((KApply) left).klabel().equals(topCellLabel)) { + body = KRewrite(KAs(RewriteToTop.toLeft(body), CONFIG_VAR), RewriteToTop.toRight(body)); } - return s; } + return body; + } - public Module moduleResolve(Module m) { - Set newSentences = new HashSet<>(); - for (Sentence s : mutable(m.localSentences())) { - if (s instanceof RuleOrClaim) { - newSentences.add(resolve((RuleOrClaim) s, m)); - } else if (s instanceof Context) { - newSentences.add(resolve((Context) s, m)); - } else if (s instanceof ContextAlias) { - newSentences.add(resolve((ContextAlias) s, m)); - } else if (s instanceof Production) { - Production prd = resolve((Production) s); - newSentences.add(prd); - // topCell introduces a new sort. Make sure it's declared - if (!prd.equals(s) && !m.definedSorts().contains(topCell.head())) - newSentences.add(SyntaxSort(Seq(), topCell)); - } else { - newSentences.add(s); - } - } - if (newSentences.equals(mutable(m.localSentences()))) - return m; - return Module.apply(m.name(), m.imports(), immutable(newSentences), m.att()); + public Sentence resolveConfigVar(Sentence s) { + if (s instanceof RuleOrClaim r) { + return r.newInstance( + resolveConfigVar(r.body(), r.requires(), r.ensures()), + r.requires(), + r.ensures(), + r.att()); + } + return s; + } + + public Module moduleResolve(Module m) { + Set newSentences = new HashSet<>(); + for (Sentence s : mutable(m.localSentences())) { + if (s instanceof RuleOrClaim) { + newSentences.add(resolve((RuleOrClaim) s, m)); + } else if (s instanceof Context) { + newSentences.add(resolve((Context) s, m)); + } else if (s instanceof ContextAlias) { + newSentences.add(resolve((ContextAlias) s, m)); + } else if (s instanceof Production) { + Production prd = resolve((Production) s); + newSentences.add(prd); + // topCell introduces a new sort. Make sure it's declared + if (!prd.equals(s) && !m.definedSorts().contains(topCell.head())) + newSentences.add(SyntaxSort(Seq(), topCell)); + } else { + newSentences.add(s); + } } + if (newSentences.equals(mutable(m.localSentences()))) return m; + return Module.apply(m.name(), m.imports(), immutable(newSentences), m.att()); + } } diff --git a/kernel/src/main/java/org/kframework/compile/ResolveHeatCoolAttribute.java b/kernel/src/main/java/org/kframework/compile/ResolveHeatCoolAttribute.java index 7c26aa291f7..e1f41dba853 100644 --- a/kernel/src/main/java/org/kframework/compile/ResolveHeatCoolAttribute.java +++ b/kernel/src/main/java/org/kframework/compile/ResolveHeatCoolAttribute.java @@ -1,6 +1,12 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + +import java.util.Optional; +import java.util.Set; import org.kframework.attributes.Att; import org.kframework.builtin.BooleanUtils; import org.kframework.definition.Context; @@ -12,63 +18,59 @@ import org.kframework.kore.KLabel; import org.kframework.utils.errorsystem.KEMException; -import java.util.Optional; -import java.util.Set; +public record ResolveHeatCoolAttribute(Set unrestrictedRules) { -import static org.kframework.Collections.*; -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; + private Rule resolve(Module m, Rule rule) { + return Rule(rule.body(), transform(m, rule.requires(), rule.att()), rule.ensures(), rule.att()); + } -public record ResolveHeatCoolAttribute(Set unrestrictedRules) { + private Context resolve(Module m, Context context) { + return new Context( + context.body(), transform(m, context.requires(), context.att()), context.att()); + } - private Rule resolve(Module m, Rule rule) { - return Rule( - rule.body(), - transform(m, rule.requires(), rule.att()), - rule.ensures(), - rule.att()); + private K transform(Module m, K requires, Att att) { + String sort = att.getOptional(Att.RESULT()).orElse("KResult"); + KLabel lbl = KLabel("is" + sort); + if (!m.productionsFor().contains(lbl) + && stream(m.allSorts()).noneMatch(s -> s.toString().equals(sort))) { + throw KEMException.compilerError( + "Definition is missing function " + + lbl.name() + + " required for strictness. Please either declare sort " + + sort + + " or declare 'syntax Bool ::= " + + lbl.name() + + "(K) [symbol, function]'", + requires); } - - private Context resolve(Module m, Context context) { - return new Context( - context.body(), - transform(m, context.requires(), context.att()), - context.att()); + KApply predicate = KApply(lbl, KVariable("HOLE")); + if (att.contains(Att.HEAT())) { + return BooleanUtils.and(requires, BooleanUtils.not(predicate)); } - - private K transform(Module m, K requires, Att att) { - String sort = att.getOptional(Att.RESULT()).orElse("KResult"); - KLabel lbl = KLabel("is" + sort); - if (!m.productionsFor().contains(lbl) && stream(m.allSorts()).noneMatch(s -> s.toString().equals(sort))) { - throw KEMException.compilerError("Definition is missing function " + lbl.name() + - " required for strictness. Please either declare sort " + sort + - " or declare 'syntax Bool ::= " + lbl.name() + "(K) [symbol, function]'", requires); - } - KApply predicate = KApply(lbl, KVariable("HOLE")); - if (att.contains(Att.HEAT())) { - return BooleanUtils.and(requires, BooleanUtils.not(predicate)); - } - if (att.contains(Att.COOL())) { - if (unrestrictedRules.stream() - .map(Att::getUserGroupOptional).flatMap(Optional::stream).anyMatch(att::contains)) { - return requires; - } - return BooleanUtils.and(requires, predicate); - } - throw new AssertionError("Called ResolveHeatCoolAttribute::transform on rule without " + - "heat or cool attribute"); + if (att.contains(Att.COOL())) { + if (unrestrictedRules.stream() + .map(Att::getUserGroupOptional) + .flatMap(Optional::stream) + .anyMatch(att::contains)) { + return requires; + } + return BooleanUtils.and(requires, predicate); } + throw new AssertionError( + "Called ResolveHeatCoolAttribute::transform on rule without " + "heat or cool attribute"); + } - public Sentence resolve(Module m, Sentence s) { - if (!s.att().contains(Att.HEAT()) && !s.att().contains(Att.COOL())) { - return s; - } - if (s instanceof Rule r) { - return resolve(m, r); - } else if (s instanceof Context c) { - return resolve(m, c); - } else { - return s; - } + public Sentence resolve(Module m, Sentence s) { + if (!s.att().contains(Att.HEAT()) && !s.att().contains(Att.COOL())) { + return s; + } + if (s instanceof Rule r) { + return resolve(m, r); + } else if (s instanceof Context c) { + return resolve(m, c); + } else { + return s; } + } } diff --git a/kernel/src/main/java/org/kframework/compile/ResolveIOStreams.java b/kernel/src/main/java/org/kframework/compile/ResolveIOStreams.java index dbea035d9a9..1401f398d5b 100644 --- a/kernel/src/main/java/org/kframework/compile/ResolveIOStreams.java +++ b/kernel/src/main/java/org/kframework/compile/ResolveIOStreams.java @@ -1,6 +1,13 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.stream.Collectors; import org.kframework.attributes.Att; import org.kframework.attributes.Location; import org.kframework.builtin.KLabels; @@ -26,392 +33,415 @@ import scala.Tuple2; import scala.collection.Set; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.stream.Collectors; - -import static org.kframework.Collections.*; -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; - -/** - * Created by daejunpark on 9/6/15. - */ -public record ResolveIOStreams(Definition definition, - KExceptionManager kem) { - - /** - * Update modules that declare stream cells in configuration, - * by using builtin *-STREAM modules. - *

- * Steps: - * 1. Update the init rules of the stream cells. - * 2. Update rules that refer to 'stdin' stream. - * 3. Import rules from *-STREAM modules (with modification of cell names). - */ - public Module resolve(Module m) { - java.util.Set streamProductions = getStreamProductions(m.sentences()); - if (streamProductions.isEmpty()) { - return m; - } else { - java.util.Set sentences = mutable(m.localSentences()); - // NOTE: it may seem inefficient to have duplicated for-loops here, - // but such a duplication makes each step much simpler; - // moreover the number of `streamProductions` is at most two (or possibly three later on), - // so that the duplication effect is not that much. - // Step 1. - for (Production p : streamProductions) { - sentences = sentences.stream().map(s -> resolveInitRule(p, s)).collect(Collectors.toSet()); - } - // Step 2. - for (Production p : streamProductions) { - if (p.att().get(Att.STREAM()).equals("stdin")) { - sentences.addAll(getStdinStreamUnblockingRules(p, sentences)); - } - } - // Step 3. - if (!getStreamProductions(m.localSentences()).isEmpty()) { - for (Production p : streamProductions) { - sentences.addAll(getStreamModuleSentences(p)); - } - } - return Module(m.name(), (Set) m.imports(). - $bar(Set(Import(definition.getModule("K-IO").get(), true))). - $bar(Set(Import(definition.getModule("K-REFLECTION").get(), true))), - immutable(sentences), m.att()); - } - } - - // Collect productions that have 'stream' attribute - private java.util.Set getStreamProductions(Set sentences) { - java.util.Set productions = new HashSet<>(); - for (Sentence s : mutable(sentences)) { - if (s instanceof Production p) { - if (p.att().getOption(Att.STREAM()).isDefined()) { - checkStreamName(p.att().get(Att.STREAM())); - productions.add(p); - } - } - } - return productions; - } - - private void checkStreamName(String streamName) { - ArrayList streams = new ArrayList(); - streams.add("stdin"); - streams.add("stdout"); - - if (!streams.contains(streamName)) { - throw KEMException.compilerError("Make sure you give the correct stream names: " + streamName + - "\nIt should be one of " + streams); +/** Created by daejunpark on 9/6/15. */ +public record ResolveIOStreams(Definition definition, KExceptionManager kem) { + + /** + * Update modules that declare stream cells in configuration, by using builtin *-STREAM modules. + * + *

Steps: 1. Update the init rules of the stream cells. 2. Update rules that refer to 'stdin' + * stream. 3. Import rules from *-STREAM modules (with modification of cell names). + */ + public Module resolve(Module m) { + java.util.Set streamProductions = getStreamProductions(m.sentences()); + if (streamProductions.isEmpty()) { + return m; + } else { + java.util.Set sentences = mutable(m.localSentences()); + // NOTE: it may seem inefficient to have duplicated for-loops here, + // but such a duplication makes each step much simpler; + // moreover the number of `streamProductions` is at most two (or possibly three later + // on), + // so that the duplication effect is not that much. + // Step 1. + for (Production p : streamProductions) { + sentences = sentences.stream().map(s -> resolveInitRule(p, s)).collect(Collectors.toSet()); + } + // Step 2. + for (Production p : streamProductions) { + if (p.att().get(Att.STREAM()).equals("stdin")) { + sentences.addAll(getStdinStreamUnblockingRules(p, sentences)); } - } - - private Sentence resolveInitRule(Production p, Sentence s) { - if (s instanceof Rule) { - return resolveInitRule(p, (Rule) s); - } else { - return s; + } + // Step 3. + if (!getStreamProductions(m.localSentences()).isEmpty()) { + for (Production p : streamProductions) { + sentences.addAll(getStreamModuleSentences(p)); } + } + return Module( + m.name(), + (Set) + m.imports() + .$bar(Set(Import(definition.getModule("K-IO").get(), true))) + .$bar(Set(Import(definition.getModule("K-REFLECTION").get(), true))), + immutable(sentences), + m.att()); } - - // Step 1. - private Rule resolveInitRule(Production streamProduction, Rule rule) { - Sort streamSort = streamProduction.sort(); // InCell, OutCell - String initLabel = GenerateSentencesFromConfigDecl.getInitLabel(streamSort); // initInCell, initOutCell - KLabel cellLabel = streamProduction.klabel().get(); // , - - // rule initInCell(Init) => ... - if (isInitRule(initLabel, cellLabel.name(), rule)) { - KRewrite body = (KRewrite) rule.body(); - KApply right = (KApply) body.right(); - KList klist = getContentsOfInitRule(streamProduction); - right = KApply(right.klabel(), klist, right.att()); - body = KRewrite(body.left(), right, body.att()); - return Rule(body, rule.requires(), rule.ensures(), rule.att()); + } + + // Collect productions that have 'stream' attribute + private java.util.Set getStreamProductions(Set sentences) { + java.util.Set productions = new HashSet<>(); + for (Sentence s : mutable(sentences)) { + if (s instanceof Production p) { + if (p.att().getOption(Att.STREAM()).isDefined()) { + checkStreamName(p.att().get(Att.STREAM())); + productions.add(p); } - return rule; + } } - - // TODO(Daejun): rule should have contained an initializer attribute. - private boolean isInitRule(String initLabel, String cellLabel, Sentence s) { - try { - // rule initXCell(Init) => ... - KRewrite body = (KRewrite) ((Rule) s).body(); - KApply left = (KApply) body.left(); - KApply right = (KApply) body.right(); - return left.klabel().name().equals(initLabel) // initXCell - && right.klabel().name().equals(cellLabel); // - } catch (ClassCastException ignored) { - return false; - } + return productions; + } + + private void checkStreamName(String streamName) { + ArrayList streams = new ArrayList(); + streams.add("stdin"); + streams.add("stdout"); + + if (!streams.contains(streamName)) { + throw KEMException.compilerError( + "Make sure you give the correct stream names: " + + streamName + + "\nIt should be one of " + + streams); } + } - // Get an initializer rule from the built-in *-STREAM module - private KList getContentsOfInitRule(Production streamProduction) { - String streamName = streamProduction.att().get(Att.STREAM()); // stdin, stdout - String initLabel = GenerateSentencesFromConfigDecl.getInitLabel( - Sort(GenerateSentencesFromConfigDecl.getSortOfCell(streamName))); // initStdinCell, initStdoutCell - String cellLabel = "<" + streamName + ">"; // , - - java.util.List initRules = - stream(getStreamModule(streamName).localSentences()) - .filter(s -> isInitRule(initLabel, cellLabel, s)).toList(); - assert initRules.size() == 1; - Sentence initRule = initRules.get(0); - - // rule initXCell(Init) => ... - KRewrite body = (KRewrite) ((Rule) initRule).body(); - KApply right = (KApply) body.right(); - return right.klist(); + private Sentence resolveInitRule(Production p, Sentence s) { + if (s instanceof Rule) { + return resolveInitRule(p, (Rule) s); + } else { + return s; } - - // Step 3. - // get sentences from stream module: - // - productions whose sort is `Stream` - // - rules that have `stream` attribute, but changing cell names according to user-defined one. - private java.util.Set getStreamModuleSentences(Production streamProduction) { - String streamName = streamProduction.att().get(Att.STREAM()); // stdin, stdout - String builtinCellLabel = "<" + streamName + ">"; // , - KLabel userCellLabel = streamProduction.klabel().get(); // , - - java.util.Set sentences = new HashSet<>(); - for (Sentence s : mutable(getStreamModule(streamName).localSentences())) { - if (s instanceof Rule rule) { - if (rule.att().contains(Att.STREAM())) { - // Update cell names - K body = new TransformK() { - @Override - public K apply(KApply k) { - k = (KApply) super.apply(k); - return KApply(apply(k.klabel()), k.klist(), k.att()); - } - - private KLabel apply(KLabel klabel) { - if (klabel.name().equals(builtinCellLabel)) { - return userCellLabel; - } else { - return klabel; - } - } - }.apply(rule.body()); - - rule = Rule(body, rule.requires(), rule.ensures(), rule.att()); - sentences.add(rule); - } else if (rule.att().contains(Att.PROJECTION())) { - sentences.add(rule); - } - } else if (s instanceof Production production) { - if (production.sort().toString().equals("Stream") || production.att().contains(Att.PROJECTION())) { - sentences.add(production); - } - } - } - return sentences; + } + + // Step 1. + private Rule resolveInitRule(Production streamProduction, Rule rule) { + Sort streamSort = streamProduction.sort(); // InCell, OutCell + String initLabel = + GenerateSentencesFromConfigDecl.getInitLabel(streamSort); // initInCell, initOutCell + KLabel cellLabel = streamProduction.klabel().get(); // , + + // rule initInCell(Init) => ... + if (isInitRule(initLabel, cellLabel.name(), rule)) { + KRewrite body = (KRewrite) rule.body(); + KApply right = (KApply) body.right(); + KList klist = getContentsOfInitRule(streamProduction); + right = KApply(right.klabel(), klist, right.att()); + body = KRewrite(body.left(), right, body.att()); + return Rule(body, rule.requires(), rule.ensures(), rule.att()); } - - private Module getStreamModule(String streamName) { - // TODO(Daejun): fix hard-coded stream module naming convention - String moduleName = streamName.toUpperCase() + "-STREAM"; - Option module = definition.getModule(moduleName); - if (module.isDefined()) { - return module.get(); - } else { - throw KEMException.compilerError("no such module: " + moduleName); - } + return rule; + } + + // TODO(Daejun): rule should have contained an initializer attribute. + private boolean isInitRule(String initLabel, String cellLabel, Sentence s) { + try { + // rule initXCell(Init) => ... + KRewrite body = (KRewrite) ((Rule) s).body(); + KApply left = (KApply) body.left(); + KApply right = (KApply) body.right(); + return left.klabel().name().equals(initLabel) // initXCell + && right.klabel().name().equals(cellLabel); // + } catch (ClassCastException ignored) { + return false; } - - // Step 2. - /* - * From the following stdin stream reference rule: - * rule read() => V ... - * - * ListItem(V:Int) => .List - * ... - * - * - * Generate the following auxiliary rule: - * rule read() ... - * - * `.List => ListItem(#parseInput("Int", " \n\t\r"))` - * ListItem(#buffer(_:String)) - * ... - * - */ - private java.util.Set getStdinStreamUnblockingRules(Production streamProduction, - java.util.Set sentences) { - KLabel userCellLabel = streamProduction.klabel().get(); // - - // find rules with currently supported matching patterns - java.util.Set> rules = new HashSet<>(); - for (Sentence s : sentences) { - if (s instanceof Rule rule) { - java.util.List sorts = isSupportingRulePatternAndGetSortNameOfCast(streamProduction, rule); - assert sorts.size() <= 1; - if (sorts.size() == 1) { - rules.add(Tuple2.apply(rule, sorts.get(0))); - } - } - } - - // generate additional unblocking rules for each of the above rules - java.util.Set newSentences = new HashSet<>(); - for (Tuple2 r : rules) { - Rule rule = r._1(); - String sort = r._2(); - - K body = new TransformK() { + } + + // Get an initializer rule from the built-in *-STREAM module + private KList getContentsOfInitRule(Production streamProduction) { + String streamName = streamProduction.att().get(Att.STREAM()); // stdin, stdout + String initLabel = + GenerateSentencesFromConfigDecl.getInitLabel( + Sort( + GenerateSentencesFromConfigDecl.getSortOfCell( + streamName))); // initStdinCell, initStdoutCell + String cellLabel = "<" + streamName + ">"; // , + + java.util.List initRules = + stream(getStreamModule(streamName).localSentences()) + .filter(s -> isInitRule(initLabel, cellLabel, s)) + .toList(); + assert initRules.size() == 1; + Sentence initRule = initRules.get(0); + + // rule initXCell(Init) => ... + KRewrite body = (KRewrite) ((Rule) initRule).body(); + KApply right = (KApply) body.right(); + return right.klist(); + } + + // Step 3. + // get sentences from stream module: + // - productions whose sort is `Stream` + // - rules that have `stream` attribute, but changing cell names according to user-defined one. + private java.util.Set getStreamModuleSentences(Production streamProduction) { + String streamName = streamProduction.att().get(Att.STREAM()); // stdin, stdout + String builtinCellLabel = "<" + streamName + ">"; // , + KLabel userCellLabel = streamProduction.klabel().get(); // , + + java.util.Set sentences = new HashSet<>(); + for (Sentence s : mutable(getStreamModule(streamName).localSentences())) { + if (s instanceof Rule rule) { + if (rule.att().contains(Att.STREAM())) { + // Update cell names + K body = + new TransformK() { @Override public K apply(KApply k) { - if (k.klabel().name().equals(userCellLabel.name())) { - return getUnblockRuleBody(streamProduction, sort); - } else { - return super.apply(k); - } + k = (KApply) super.apply(k); + return KApply(apply(k.klabel()), k.klist(), k.att()); } - @Override - public K apply(KRewrite k) { - // drop rhs - return apply(k.left()); + private KLabel apply(KLabel klabel) { + if (klabel.name().equals(builtinCellLabel)) { + return userCellLabel; + } else { + return klabel; + } } - }.apply(rule.body()); + }.apply(rule.body()); - rule = Rule(body, rule.requires(), rule.ensures(), rule.att()); - newSentences.add(rule); + rule = Rule(body, rule.requires(), rule.ensures(), rule.att()); + sentences.add(rule); + } else if (rule.att().contains(Att.PROJECTION())) { + sentences.add(rule); } - - return newSentences; + } else if (s instanceof Production production) { + if (production.sort().toString().equals("Stream") + || production.att().contains(Att.PROJECTION())) { + sentences.add(production); + } + } + } + return sentences; + } + + private Module getStreamModule(String streamName) { + // TODO(Daejun): fix hard-coded stream module naming convention + String moduleName = streamName.toUpperCase() + "-STREAM"; + Option module = definition.getModule(moduleName); + if (module.isDefined()) { + return module.get(); + } else { + throw KEMException.compilerError("no such module: " + moduleName); + } + } + + // Step 2. + /* + * From the following stdin stream reference rule: + * rule read() => V ... + * + * ListItem(V:Int) => .List + * ... + * + * + * Generate the following auxiliary rule: + * rule read() ... + * + * `.List => ListItem(#parseInput("Int", " \n\t\r"))` + * ListItem(#buffer(_:String)) + * ... + * + */ + private java.util.Set getStdinStreamUnblockingRules( + Production streamProduction, java.util.Set sentences) { + KLabel userCellLabel = streamProduction.klabel().get(); // + + // find rules with currently supported matching patterns + java.util.Set> rules = new HashSet<>(); + for (Sentence s : sentences) { + if (s instanceof Rule rule) { + java.util.List sorts = + isSupportingRulePatternAndGetSortNameOfCast(streamProduction, rule); + assert sorts.size() <= 1; + if (sorts.size() == 1) { + rules.add(Tuple2.apply(rule, sorts.get(0))); + } + } } - // return (a list of) sort names of cast if the given rule has the supported pattern matching over input stream cell, - // otherwise return empty. - // currently, the list of sort names of cast should be a singleton. - /* - * Currently supported rule pattern is: - * rule read() => V ... - * - * ListItem(V:Int) => .List - * ... - * - */ - private java.util.List isSupportingRulePatternAndGetSortNameOfCast(Production streamProduction, Rule rule) { - KLabel userCellLabel = streamProduction.klabel().get(); // - - java.util.List sorts = new ArrayList<>(); - new VisitK() { + // generate additional unblocking rules for each of the above rules + java.util.Set newSentences = new HashSet<>(); + for (Tuple2 r : rules) { + Rule rule = r._1(); + String sort = r._2(); + + K body = + new TransformK() { @Override - public void apply(KApply k) { - if (k.klabel().name().equals(userCellLabel.name())) { - String sort = wellformedAndGetSortNameOfCast(k.klist()); - if (!sort.isEmpty()) { - sorts.add(sort); - } else { - if (k.att().getOption(Location.class).isDefined()) { // warning only for user-provided rules - throw KEMException.compilerError("Unsupported matching pattern in stdin stream cell." + - "\nThe currently supported pattern is: ListItem(V:Sort) => .List ... ", k); - } - } - } - super.apply(k); + public K apply(KApply k) { + if (k.klabel().name().equals(userCellLabel.name())) { + return getUnblockRuleBody(streamProduction, sort); + } else { + return super.apply(k); + } } - // TODO(Daejun): it support only pattern matching on the top of stream. - // more patterns need to be supported as well. - /* - * Return cast sort name if well formed, otherwise empty string. - * - * klist is well formed if it consists of: - * #noDots(.KList) - * ListItem(#SemanticCastToInt(V)) => .List - * #dots(.KList) - * - * which comes from, e.g.,: - * ListItem(V:Int) => .List ... - */ - private String wellformedAndGetSortNameOfCast(KList klist) { - try { - if (klist.size() == 3) { - KApply k1 = (KApply) klist.items().get(0); - KApply k3 = (KApply) klist.items().get(2); - if (KLabels.NO_DOTS.equals(k1.klabel()) && k1.klist().size() == 0 && - KLabels.DOTS.equals(k3.klabel()) && k3.klist().size() == 0) { - - KRewrite k2 = (KRewrite) klist.items().get(1); - KApply k2l = (KApply) k2.left(); - KApply k2r = (KApply) k2.right(); - if (k2l.klabel().name().equals("ListItem") && k2l.klist().size() == 1 && - k2r.klabel().name().equals(".List") && k2r.klist().size() == 0) { - - KApply k2li = (KApply) k2l.klist().items().get(0); - if (k2li.klabel().name().startsWith("#SemanticCastTo") && k2li.klist().size() == 1 && - k2li.klist().items().get(0) instanceof KVariable) { - return ResolveSemanticCasts.getSortNameOfCast(k2li); // k2li.klabel().name().substring("#SemanticCastTo".length()); - } - } - } - } - } catch (ClassCastException ignored) { - } - return ""; + @Override + public K apply(KRewrite k) { + // drop rhs + return apply(k.left()); } - }.apply(rule.body()); + }.apply(rule.body()); - return sorts; + rule = Rule(body, rule.requires(), rule.ensures(), rule.att()); + newSentences.add(rule); } - // get rule body of the `[unblock]` rule (it should exist an unique one), - // instantiating with proper `Sort` and `Delimiters` values. - // this method should be called with stdin stream production, not with stdout stream. - /* - * Currently supporting generated rule would be: - * rule read() ... - * - * `.List => ListItem(#parseInput("Int", " \n\t\r"))` - * ListItem(#buffer(_:String)) - * ... - * - */ - private K getUnblockRuleBody(Production streamProduction, String sort) { - String streamName = streamProduction.att().get(Att.STREAM()); - assert streamName.equals("stdin"); // stdin - String builtinCellLabel = "<" + streamName + ">"; // - KLabel userCellLabel = streamProduction.klabel().get(); // - - java.util.List unblockRules = stream(getStreamModule(streamName).localSentences()) - .filter(s -> s instanceof Rule && s.att().getOptional(Att.LABEL()).map(lbl -> lbl.equals("STDIN-STREAM.stdinUnblock")).orElse(false)).toList(); - assert unblockRules.size() == 1; - Rule unblockRule = (Rule) unblockRules.get(0); - - return new TransformK() { - @Override - public K apply(KApply k) { - if (k.klabel().name().equals("#SemanticCastToString") && k.klist().size() == 1) { - K i = k.klist().items().get(0); - if (i instanceof KVariable x) { - switch (x.name()) { - case "?Sort": - return KToken("\"" + sort + "\"", Sorts.String()); - case "?Delimiters": - // TODO(Daejun): support `delimiter` attribute in stream cell - return KToken("\" \\n\\t\\r\"", Sorts.String()); - default: - // fall through - } - } - } - k = (KApply) super.apply(k); - return KApply(apply(k.klabel()), k.klist(), k.att()); + return newSentences; + } + + // return (a list of) sort names of cast if the given rule has the supported pattern matching over + // input stream cell, + // otherwise return empty. + // currently, the list of sort names of cast should be a singleton. + /* + * Currently supported rule pattern is: + * rule read() => V ... + * + * ListItem(V:Int) => .List + * ... + * + */ + private java.util.List isSupportingRulePatternAndGetSortNameOfCast( + Production streamProduction, Rule rule) { + KLabel userCellLabel = streamProduction.klabel().get(); // + + java.util.List sorts = new ArrayList<>(); + new VisitK() { + @Override + public void apply(KApply k) { + if (k.klabel().name().equals(userCellLabel.name())) { + String sort = wellformedAndGetSortNameOfCast(k.klist()); + if (!sort.isEmpty()) { + sorts.add(sort); + } else { + if (k.att() + .getOption(Location.class) + .isDefined()) { // warning only for user-provided rules + throw KEMException.compilerError( + "Unsupported matching pattern in stdin stream cell.\n" + + "The currently supported pattern is: ListItem(V:Sort) => .List ..." + + " ", + k); } - - private KLabel apply(KLabel klabel) { - if (klabel.name().equals(builtinCellLabel)) { - return userCellLabel; - } else { - return klabel; + } + } + super.apply(k); + } + + // TODO(Daejun): it support only pattern matching on the top of stream. + // more patterns need to be supported as well. + /* + * Return cast sort name if well formed, otherwise empty string. + * + * klist is well formed if it consists of: + * #noDots(.KList) + * ListItem(#SemanticCastToInt(V)) => .List + * #dots(.KList) + * + * which comes from, e.g.,: + * ListItem(V:Int) => .List ... + */ + private String wellformedAndGetSortNameOfCast(KList klist) { + try { + if (klist.size() == 3) { + KApply k1 = (KApply) klist.items().get(0); + KApply k3 = (KApply) klist.items().get(2); + if (KLabels.NO_DOTS.equals(k1.klabel()) + && k1.klist().size() == 0 + && KLabels.DOTS.equals(k3.klabel()) + && k3.klist().size() == 0) { + + KRewrite k2 = (KRewrite) klist.items().get(1); + KApply k2l = (KApply) k2.left(); + KApply k2r = (KApply) k2.right(); + if (k2l.klabel().name().equals("ListItem") + && k2l.klist().size() == 1 + && k2r.klabel().name().equals(".List") + && k2r.klist().size() == 0) { + + KApply k2li = (KApply) k2l.klist().items().get(0); + if (k2li.klabel().name().startsWith("#SemanticCastTo") + && k2li.klist().size() == 1 + && k2li.klist().items().get(0) instanceof KVariable) { + return ResolveSemanticCasts.getSortNameOfCast( + k2li); // k2li.klabel().name().substring("#SemanticCastTo".length()); } + } } - }.apply(unblockRule.body()); - } + } + } catch (ClassCastException ignored) { + } + return ""; + } + }.apply(rule.body()); + + return sorts; + } + + // get rule body of the `[unblock]` rule (it should exist an unique one), + // instantiating with proper `Sort` and `Delimiters` values. + // this method should be called with stdin stream production, not with stdout stream. + /* + * Currently supporting generated rule would be: + * rule read() ... + * + * `.List => ListItem(#parseInput("Int", " \n\t\r"))` + * ListItem(#buffer(_:String)) + * ... + * + */ + private K getUnblockRuleBody(Production streamProduction, String sort) { + String streamName = streamProduction.att().get(Att.STREAM()); + assert streamName.equals("stdin"); // stdin + String builtinCellLabel = "<" + streamName + ">"; // + KLabel userCellLabel = streamProduction.klabel().get(); // + + java.util.List unblockRules = + stream(getStreamModule(streamName).localSentences()) + .filter( + s -> + s instanceof Rule + && s.att() + .getOptional(Att.LABEL()) + .map(lbl -> lbl.equals("STDIN-STREAM.stdinUnblock")) + .orElse(false)) + .toList(); + assert unblockRules.size() == 1; + Rule unblockRule = (Rule) unblockRules.get(0); + + return new TransformK() { + @Override + public K apply(KApply k) { + if (k.klabel().name().equals("#SemanticCastToString") && k.klist().size() == 1) { + K i = k.klist().items().get(0); + if (i instanceof KVariable x) { + switch (x.name()) { + case "?Sort": + return KToken("\"" + sort + "\"", Sorts.String()); + case "?Delimiters": + // TODO(Daejun): support `delimiter` attribute in stream cell + return KToken("\" \\n\\t\\r\"", Sorts.String()); + default: + // fall through + } + } + } + k = (KApply) super.apply(k); + return KApply(apply(k.klabel()), k.klist(), k.att()); + } + private KLabel apply(KLabel klabel) { + if (klabel.name().equals(builtinCellLabel)) { + return userCellLabel; + } else { + return klabel; + } + } + }.apply(unblockRule.body()); + } } diff --git a/kernel/src/main/java/org/kframework/compile/ResolveSemanticCasts.java b/kernel/src/main/java/org/kframework/compile/ResolveSemanticCasts.java index abf21c3ab5a..ced3d3f2e5e 100644 --- a/kernel/src/main/java/org/kframework/compile/ResolveSemanticCasts.java +++ b/kernel/src/main/java/org/kframework/compile/ResolveSemanticCasts.java @@ -1,153 +1,160 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.kore.KORE.*; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import org.kframework.builtin.BooleanUtils; import org.kframework.definition.Context; import org.kframework.definition.RuleOrClaim; import org.kframework.definition.Sentence; +import org.kframework.kore.AddAtt; import org.kframework.kore.K; import org.kframework.kore.KApply; import org.kframework.kore.KVariable; import org.kframework.kore.Sort; -import org.kframework.kore.AddAtt; import org.kframework.kore.TransformK; import org.kframework.kore.VisitK; import org.kframework.parser.outer.Outer; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -import static org.kframework.kore.KORE.*; - -/** - * Created by dwightguth on 4/17/15. - */ +/** Created by dwightguth on 4/17/15. */ public class ResolveSemanticCasts { - private final boolean skipSortPredicates; - private final Set casts = new HashSet<>(); - private final Map varToTypedVar = new HashMap<>(); - - public ResolveSemanticCasts(boolean skipSortPredicates) { - - this.skipSortPredicates = skipSortPredicates; - } - - void resetCasts() { - casts.clear(); - varToTypedVar.clear(); - } - - public K resolve(K k) { - resetCasts(); - gatherCasts(k); - return transform(k); - } - - private RuleOrClaim resolve(RuleOrClaim rule) { - resetCasts(); - gatherCasts(rule.body()); - gatherCasts(rule.requires()); - gatherCasts(rule.ensures()); - return rule.newInstance( - transform(rule.body()), - addSideCondition(transform(rule.requires()), ExpandMacros.isMacro(rule)), - transform(rule.ensures()), - rule.att()); - } - - private Context resolve(Context context) { - resetCasts(); - gatherCasts(context.body()); - gatherCasts(context.requires()); - return new Context( - transform(context.body()), - addSideCondition(transform(context.requires()), false), - context.att()); - } - - K addSideCondition(K requires, boolean macro) { - if (skipSortPredicates || macro) - return requires; - else { - Optional sideCondition = casts.stream().map(k -> { - return new TransformK() { - @Override - public K apply(KVariable k) { + private final boolean skipSortPredicates; + private final Set casts = new HashSet<>(); + private final Map varToTypedVar = new HashMap<>(); + + public ResolveSemanticCasts(boolean skipSortPredicates) { + + this.skipSortPredicates = skipSortPredicates; + } + + void resetCasts() { + casts.clear(); + varToTypedVar.clear(); + } + + public K resolve(K k) { + resetCasts(); + gatherCasts(k); + return transform(k); + } + + private RuleOrClaim resolve(RuleOrClaim rule) { + resetCasts(); + gatherCasts(rule.body()); + gatherCasts(rule.requires()); + gatherCasts(rule.ensures()); + return rule.newInstance( + transform(rule.body()), + addSideCondition(transform(rule.requires()), ExpandMacros.isMacro(rule)), + transform(rule.ensures()), + rule.att()); + } + + private Context resolve(Context context) { + resetCasts(); + gatherCasts(context.body()); + gatherCasts(context.requires()); + return new Context( + transform(context.body()), + addSideCondition(transform(context.requires()), false), + context.att()); + } + + K addSideCondition(K requires, boolean macro) { + if (skipSortPredicates || macro) return requires; + else { + Optional sideCondition = + casts.stream() + .map( + k -> { + return new TransformK() { + @Override + public K apply(KVariable k) { if (varToTypedVar.containsKey(k)) { - return varToTypedVar.get(k); + return varToTypedVar.get(k); } return k; - } - }.apply(k); - }).map(k -> KApply(KLabel("is" + getSortNameOfCast((KApply) k)), transform(k))).reduce(BooleanUtils::and); - if (!sideCondition.isPresent()) { - return requires; - } else if (requires.equals(BooleanUtils.TRUE) && sideCondition.isPresent()) { - return sideCondition.get(); - } else { - return BooleanUtils.and(sideCondition.get(), requires); - } - } + } + }.apply(k); + }) + .map(k -> KApply(KLabel("is" + getSortNameOfCast((KApply) k)), transform(k))) + .reduce(BooleanUtils::and); + if (!sideCondition.isPresent()) { + return requires; + } else if (requires.equals(BooleanUtils.TRUE) && sideCondition.isPresent()) { + return sideCondition.get(); + } else { + return BooleanUtils.and(sideCondition.get(), requires); + } } - - public static String getSortNameOfCast(KApply kapp) { - return kapp.klabel().name().substring("#SemanticCastTo".length()); - } - - void gatherCasts(K term) { - new VisitK() { - @Override - public void apply(KApply v) { - if (v.klabel().name().startsWith("#SemanticCastTo")) { - casts.add(v); - K child = v.klist().items().get(0); - if (child instanceof KVariable var) { - varToTypedVar.put(var, KVariable(var.name(), var.att().contains(Sort.class) ? var.att() : var.att().add(Sort.class, Outer.parseSort(getSortNameOfCast(v))))); - } - } - super.apply(v); - } - }.apply(term); - } - - K transform(K term) { - return new TransformK() { - private Sort sort; - - @Override - public K apply(K k) { - final Sort oldSort = sort; - K applied; - if (casts.contains(k)) { - KApply kapp = (KApply)k; - sort = Outer.parseSort(getSortNameOfCast(kapp)); - applied = apply(kapp.items().get(0)); - } else { - sort = null; - applied = super.apply(k); - } - if (oldSort != null) { - return new AddAtt(a -> a.add(Sort.class, oldSort)).apply(applied); - } - if (varToTypedVar.containsKey(applied)) { - return varToTypedVar.get(applied); - } - return applied; - } - }.apply(term); - } - - public synchronized Sentence resolve(Sentence s) { - if (s instanceof RuleOrClaim) { - return resolve((RuleOrClaim) s); - } else if (s instanceof Context) { - return resolve((Context) s); + } + + public static String getSortNameOfCast(KApply kapp) { + return kapp.klabel().name().substring("#SemanticCastTo".length()); + } + + void gatherCasts(K term) { + new VisitK() { + @Override + public void apply(KApply v) { + if (v.klabel().name().startsWith("#SemanticCastTo")) { + casts.add(v); + K child = v.klist().items().get(0); + if (child instanceof KVariable var) { + varToTypedVar.put( + var, + KVariable( + var.name(), + var.att().contains(Sort.class) + ? var.att() + : var.att().add(Sort.class, Outer.parseSort(getSortNameOfCast(v))))); + } + } + super.apply(v); + } + }.apply(term); + } + + K transform(K term) { + return new TransformK() { + private Sort sort; + + @Override + public K apply(K k) { + final Sort oldSort = sort; + K applied; + if (casts.contains(k)) { + KApply kapp = (KApply) k; + sort = Outer.parseSort(getSortNameOfCast(kapp)); + applied = apply(kapp.items().get(0)); } else { - return s; + sort = null; + applied = super.apply(k); + } + if (oldSort != null) { + return new AddAtt(a -> a.add(Sort.class, oldSort)).apply(applied); + } + if (varToTypedVar.containsKey(applied)) { + return varToTypedVar.get(applied); } + return applied; + } + }.apply(term); + } + + public synchronized Sentence resolve(Sentence s) { + if (s instanceof RuleOrClaim) { + return resolve((RuleOrClaim) s); + } else if (s instanceof Context) { + return resolve((Context) s); + } else { + return s; } + } } diff --git a/kernel/src/main/java/org/kframework/compile/ResolveStrict.java b/kernel/src/main/java/org/kframework/compile/ResolveStrict.java index bda4d83e756..e626d1f33a8 100644 --- a/kernel/src/main/java/org/kframework/compile/ResolveStrict.java +++ b/kernel/src/main/java/org/kframework/compile/ResolveStrict.java @@ -1,10 +1,22 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.kframework.Collections; import org.kframework.attributes.Att; import org.kframework.builtin.BooleanUtils; import org.kframework.builtin.Hooks; -import org.kframework.builtin.Sorts; import org.kframework.definition.*; import org.kframework.definition.Module; import org.kframework.kompile.KompileOptions; @@ -17,234 +29,274 @@ import org.kframework.parser.outer.Outer; import org.kframework.utils.StringUtil; import org.kframework.utils.errorsystem.KEMException; -import org.kframework.Collections; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - import scala.Option; -import static org.kframework.Collections.*; -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; - public class ResolveStrict { - private final KompileOptions kompileOptions; - private final Definition d; + private final KompileOptions kompileOptions; + private final Definition d; - private Module currentModule; + private Module currentModule; - public ResolveStrict(KompileOptions kompileOptions, Definition d) { - this.kompileOptions = kompileOptions; - this.d = d; - } + public ResolveStrict(KompileOptions kompileOptions, Definition d) { + this.kompileOptions = kompileOptions; + this.d = d; + } - public Set resolve(Set strictProductions) { - Set sentences = new HashSet<>(); - for (Production prod : strictProductions) { - if (!prod.klabel().isDefined()) { - throw KEMException.compilerError("Only productions with a KLabel can be strict.", prod); - } - if (prod.att().contains(Att.STRICT())) { - sentences.addAll(resolve(prod, false)); - } - if (prod.att().contains(Att.SEQSTRICT())) { - sentences.addAll(resolve(prod, true)); - } - } - return sentences; + public Set resolve(Set strictProductions) { + Set sentences = new HashSet<>(); + for (Production prod : strictProductions) { + if (!prod.klabel().isDefined()) { + throw KEMException.compilerError("Only productions with a KLabel can be strict.", prod); + } + if (prod.att().contains(Att.STRICT())) { + sentences.addAll(resolve(prod, false)); + } + if (prod.att().contains(Att.SEQSTRICT())) { + sentences.addAll(resolve(prod, true)); + } } + return sentences; + } - private static KApply cast(Sort sort, K k) { - return KApply(KLabel("#SemanticCastTo" + sort.toString()), k); - } + private static KApply cast(Sort sort, K k) { + return KApply(KLabel("#SemanticCastTo" + sort.toString()), k); + } - public static void setPositions(String attribute, List strictnessPositions, long arity, Production loc) { - String[] strictAttrs = attribute.split(","); - for (String strictAttr : strictAttrs) { - try { - int pos = Integer.parseInt(strictAttr.trim()); - if (pos < 1 || pos > arity) - throw new IndexOutOfBoundsException(); - strictnessPositions.add(pos); - } catch (NumberFormatException | IndexOutOfBoundsException e) { - if (arity == 0) { - throw KEMException.compilerError("Cannot put a strict attribute on a production with no nonterminals", loc); - } else { - throw KEMException.compilerError( - "Expecting a number between 1 and " + arity + ", but found " + strictAttr + " as a" + - " strict position in " + Arrays.toString(strictAttrs), - loc); - } - } + public static void setPositions( + String attribute, List strictnessPositions, long arity, Production loc) { + String[] strictAttrs = attribute.split(","); + for (String strictAttr : strictAttrs) { + try { + int pos = Integer.parseInt(strictAttr.trim()); + if (pos < 1 || pos > arity) throw new IndexOutOfBoundsException(); + strictnessPositions.add(pos); + } catch (NumberFormatException | IndexOutOfBoundsException e) { + if (arity == 0) { + throw KEMException.compilerError( + "Cannot put a strict attribute on a production with no nonterminals", loc); + } else { + throw KEMException.compilerError( + "Expecting a number between 1 and " + + arity + + ", but found " + + strictAttr + + " as a" + + " strict position in " + + Arrays.toString(strictAttrs), + loc); } + } } + } - private void setAliases(String attribute, Set aliases, Production prod) { - String[] strictAttrs = attribute.split(","); - for (String strictAttr : strictAttrs) { - String label = strictAttr.trim(); - Option> ss = d.mainModule().labeled().get(label); - if (ss.isDefined()) { - for (Sentence s : iterable(ss.get())) { - if (s instanceof ContextAlias) { - aliases.add((ContextAlias)s); - } else { - throw KEMException.compilerError("Found rule label \"" + label + "\" in strictness attribute of production " + prod + " which does not refer to a context alias.", s); - } - } - } else { - throw KEMException.compilerError("Found rule label \"" + label + "\" in strictness attribute which did not refer to any sentence.", prod); - } + private void setAliases(String attribute, Set aliases, Production prod) { + String[] strictAttrs = attribute.split(","); + for (String strictAttr : strictAttrs) { + String label = strictAttr.trim(); + Option> ss = d.mainModule().labeled().get(label); + if (ss.isDefined()) { + for (Sentence s : iterable(ss.get())) { + if (s instanceof ContextAlias) { + aliases.add((ContextAlias) s); + } else { + throw KEMException.compilerError( + "Found rule label \"" + + label + + "\" in strictness attribute of production " + + prod + + " which does not refer to a context alias.", + s); + } } - + } else { + throw KEMException.compilerError( + "Found rule label \"" + + label + + "\" in strictness attribute which did not refer to any sentence.", + prod); + } } + } + + private static final ContextAlias DEFAULT_ALIAS = + ContextAlias(KVariable("HERE"), BooleanUtils.TRUE, Att.empty()); + + private void resolve( + boolean sequential, + Set sentences, + long arity, + List strictnessPositions, + List allPositions, + Set aliases, + Production production) { + for (int i = 0; i < strictnessPositions.size(); i++) { + List items = new ArrayList<>(); + for (int j = 0; j < arity; j++) { + // Preserve sort information of the production + items.add(cast(production.nonterminal(j).sort(), KVariable("K" + j))); + } + int strictnessPosition = strictnessPositions.get(i) - 1; + K hole; + // Preserve sort information of the production + hole = cast(production.nonterminal(strictnessPosition).sort(), KVariable("HOLE")); - private static final ContextAlias DEFAULT_ALIAS = ContextAlias(KVariable("HERE"), BooleanUtils.TRUE, Att.empty()); - - private void resolve(boolean sequential, Set sentences, long arity, List strictnessPositions, List allPositions, Set aliases, Production production) { - for (int i = 0; i < strictnessPositions.size(); i++) { - List items = new ArrayList<>(); - for (int j = 0; j < arity; j++) { - // Preserve sort information of the production - items.add(cast(production.nonterminal(j).sort(), KVariable("K" + j))); - } - int strictnessPosition = strictnessPositions.get(i) - 1; - K hole; - // Preserve sort information of the production - hole = cast(production.nonterminal(strictnessPosition).sort(), KVariable("HOLE")); - - for (ContextAlias alias : aliases) { - K body = new TransformK() { - @Override - public K apply(KVariable var) { - if (var.name().equals("HERE")) { - K thisHole = hole; - if (alias.att().contains(Att.CONTEXT())) { - KLabel contextLabel = KLabel(alias.att().get(Att.CONTEXT())); - thisHole = KRewrite(hole, KApply(contextLabel, hole)); - } - items.set(strictnessPosition, thisHole); - return KApply(production.klabel().get(), KList(items)); - } - return var; - } - }.apply(alias.body()); - - Sort result = Outer.parseSort(alias.att().getOptional(Att.RESULT()).orElse("KResult")); - - // is seqstrict the elements before the argument should be KResult - Optional sideCondition = Stream.concat(allPositions.stream(), strictnessPositions.subList(0, i).stream()).map(j -> KApply(KLabel("is" + result.toString()), KVariable("K" + (j - 1)))).reduce(BooleanUtils::and); - K requires; - if (!sideCondition.isPresent() || !sequential) { - requires = BooleanUtils.TRUE; - } else { - requires = sideCondition.get(); + for (ContextAlias alias : aliases) { + K body = + new TransformK() { + @Override + public K apply(KVariable var) { + if (var.name().equals("HERE")) { + K thisHole = hole; + if (alias.att().contains(Att.CONTEXT())) { + KLabel contextLabel = KLabel(alias.att().get(Att.CONTEXT())); + thisHole = KRewrite(hole, KApply(contextLabel, hole)); + } + items.set(strictnessPosition, thisHole); + return KApply(production.klabel().get(), KList(items)); } + return var; + } + }.apply(alias.body()); - String label = currentModule.name() + "." + production.klabelAtt().get().replace("`", "").replaceAll("\\s", "") + (strictnessPosition+1); + Sort result = Outer.parseSort(alias.att().getOptional(Att.RESULT()).orElse("KResult")); - Context ctx = Context(body, BooleanUtils.and(requires, alias.requires()), production.att().addAll(alias.att()).add(Att.LABEL(), label)); - sentences.add(ctx); - } + // is seqstrict the elements before the argument should be KResult + Optional sideCondition = + Stream.concat(allPositions.stream(), strictnessPositions.subList(0, i).stream()) + .map(j -> KApply(KLabel("is" + result.toString()), KVariable("K" + (j - 1)))) + .reduce(BooleanUtils::and); + K requires; + if (!sideCondition.isPresent() || !sequential) { + requires = BooleanUtils.TRUE; + } else { + requires = sideCondition.get(); } + + String label = + currentModule.name() + + "." + + production.klabelAtt().get().replace("`", "").replaceAll("\\s", "") + + (strictnessPosition + 1); + + Context ctx = + Context( + body, + BooleanUtils.and(requires, alias.requires()), + production.att().addAll(alias.att()).add(Att.LABEL(), label)); + sentences.add(ctx); + } } + } - public Set resolve(Production production, boolean sequential) { - long arity = production.nonterminals().size(); - List strictnessPositions = new ArrayList<>(); - List allPositions = new ArrayList<>(); - Set aliases = new HashSet<>(); - String attribute; - Set sentences = new HashSet<>(); - if (sequential) { - attribute = production.att().get(Att.SEQSTRICT()); + public Set resolve(Production production, boolean sequential) { + long arity = production.nonterminals().size(); + List strictnessPositions = new ArrayList<>(); + List allPositions = new ArrayList<>(); + Set aliases = new HashSet<>(); + String attribute; + Set sentences = new HashSet<>(); + if (sequential) { + attribute = production.att().get(Att.SEQSTRICT()); + } else { + attribute = production.att().get(Att.STRICT()); + } + if (attribute.isEmpty()) { + for (int i = 1; i <= arity; i++) { + strictnessPositions.add(i); + } + aliases.add(DEFAULT_ALIAS); + resolve(sequential, sentences, arity, strictnessPositions, allPositions, aliases, production); + allPositions.addAll(strictnessPositions); + } else { + String[] components = attribute.split(";"); + if (components.length == 1) { + if (Character.isDigit(components[0].trim().charAt(0))) { + aliases.add(DEFAULT_ALIAS); + setPositions(components[0].trim(), strictnessPositions, arity, production); } else { - attribute = production.att().get(Att.STRICT()); + for (int i = 1; i <= arity; i++) { + strictnessPositions.add(i); + } + setAliases(components[0].trim(), aliases, production); } - if (attribute.isEmpty()) { - for (int i = 1; i <= arity; i++) { - strictnessPositions.add(i); - } - aliases.add(DEFAULT_ALIAS); - resolve(sequential, sentences, arity, strictnessPositions, allPositions, aliases, production); - allPositions.addAll(strictnessPositions); - } else { - String[] components = attribute.split(";"); - if (components.length == 1) { - if (Character.isDigit(components[0].trim().charAt(0))) { - aliases.add(DEFAULT_ALIAS); - setPositions(components[0].trim(), strictnessPositions, arity, production); - } else { - for (int i = 1; i <= arity; i++) { - strictnessPositions.add(i); - } - setAliases(components[0].trim(), aliases, production); - } - resolve(sequential, sentences, arity, strictnessPositions, allPositions, aliases, production); - allPositions.addAll(strictnessPositions); - } else if (components.length % 2 == 0) { - for (int i = 0; i < components.length; i+=2) { - setAliases(components[i].trim(), aliases, production); - setPositions(components[i+1].trim(), strictnessPositions, arity, production); - resolve(sequential, sentences, arity, strictnessPositions, allPositions, aliases, production); - aliases.clear(); - allPositions.addAll(strictnessPositions); - strictnessPositions.clear(); - } - } else { - throw KEMException.compilerError("Invalid strict attribute containing multiple semicolons.", production); - } + resolve( + sequential, sentences, arity, strictnessPositions, allPositions, aliases, production); + allPositions.addAll(strictnessPositions); + } else if (components.length % 2 == 0) { + for (int i = 0; i < components.length; i += 2) { + setAliases(components[i].trim(), aliases, production); + setPositions(components[i + 1].trim(), strictnessPositions, arity, production); + resolve( + sequential, sentences, arity, strictnessPositions, allPositions, aliases, production); + aliases.clear(); + allPositions.addAll(strictnessPositions); + strictnessPositions.clear(); } + } else { + throw KEMException.compilerError( + "Invalid strict attribute containing multiple semicolons.", production); + } + } - if (production.att().contains(Att.HYBRID())) { - List results = new ArrayList<>(); - if (!production.att().get(Att.HYBRID()).equals("")) { - String[] sorts = StringUtil.splitOneDimensionalAtt(production.att().get(Att.HYBRID())); - for (String sort : sorts) { - results.add(KLabel("is" + sort)); - } - } else { - results.add(KLabel("isKResult")); - } - for (KLabel result : results) { - List items = new ArrayList<>(); - for (int j = 0; j < arity; j++) { - // Preserve sort information of the production - items.add(cast(production.nonterminal(j).sort(), KVariable("K" + j))); - } - K term = KApply(production.klabel().get(), KList(items)); - Optional sideCondition = allPositions.stream().map(j -> KApply(result, KVariable("K" + (j - 1)))).reduce(BooleanUtils::and); - K requires; - if (!sideCondition.isPresent()) { - requires = BooleanUtils.TRUE; - } else { - requires = sideCondition.get(); - } - Rule hybrid = Rule(KRewrite(KApply(result, term), BooleanUtils.TRUE), requires, BooleanUtils.TRUE); - sentences.add(hybrid); - } + if (production.att().contains(Att.HYBRID())) { + List results = new ArrayList<>(); + if (!production.att().get(Att.HYBRID()).equals("")) { + String[] sorts = StringUtil.splitOneDimensionalAtt(production.att().get(Att.HYBRID())); + for (String sort : sorts) { + results.add(KLabel("is" + sort)); + } + } else { + results.add(KLabel("isKResult")); + } + for (KLabel result : results) { + List items = new ArrayList<>(); + for (int j = 0; j < arity; j++) { + // Preserve sort information of the production + items.add(cast(production.nonterminal(j).sort(), KVariable("K" + j))); } - return sentences; + K term = KApply(production.klabel().get(), KList(items)); + Optional sideCondition = + allPositions.stream() + .map(j -> KApply(result, KVariable("K" + (j - 1)))) + .reduce(BooleanUtils::and); + K requires; + if (!sideCondition.isPresent()) { + requires = BooleanUtils.TRUE; + } else { + requires = sideCondition.get(); + } + Rule hybrid = + Rule(KRewrite(KApply(result, term), BooleanUtils.TRUE), requires, BooleanUtils.TRUE); + sentences.add(hybrid); + } } + return sentences; + } - public Module resolve(Module input) { - currentModule = input; - Set contextsToAdd = resolve(stream(input.localSentences()) + public Module resolve(Module input) { + currentModule = input; + Set contextsToAdd = + resolve( + stream(input.localSentences()) .filter(s -> s instanceof Production) .map(s -> (Production) s) - .filter(p -> p.att().contains(Att.STRICT()) || p.att().contains(Att.SEQSTRICT())).collect(Collectors.toSet())); - scala.collection.Set imports = input.imports(); - // strictness makes use _andBool_ found in the BOOL module. Make sure it's imported - if (!contextsToAdd.isEmpty() && !input.importedModuleNames().contains(Hooks.BOOL)) - imports = (scala.collection.Set) Set(Import.apply(d.getModule(Hooks.BOOL).get(), false)).$bar(imports); - return Module(input.name(), imports, (scala.collection.Set) stream(input.localSentences()).filter(s -> !(s instanceof ContextAlias)).collect(Collections.toSet()).$bar(immutable(contextsToAdd)), input.att()); - } + .filter(p -> p.att().contains(Att.STRICT()) || p.att().contains(Att.SEQSTRICT())) + .collect(Collectors.toSet())); + scala.collection.Set imports = input.imports(); + // strictness makes use _andBool_ found in the BOOL module. Make sure it's imported + if (!contextsToAdd.isEmpty() && !input.importedModuleNames().contains(Hooks.BOOL)) + imports = + (scala.collection.Set) + Set(Import.apply(d.getModule(Hooks.BOOL).get(), false)).$bar(imports); + return Module( + input.name(), + imports, + (scala.collection.Set) + stream(input.localSentences()) + .filter(s -> !(s instanceof ContextAlias)) + .collect(Collections.toSet()) + .$bar(immutable(contextsToAdd)), + input.att()); + } } diff --git a/kernel/src/main/java/org/kframework/compile/RewriteAwareTransformer.java b/kernel/src/main/java/org/kframework/compile/RewriteAwareTransformer.java index a4e90e08dfe..3e1301113c7 100644 --- a/kernel/src/main/java/org/kframework/compile/RewriteAwareTransformer.java +++ b/kernel/src/main/java/org/kframework/compile/RewriteAwareTransformer.java @@ -1,55 +1,55 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.kore.KORE.*; + import org.kframework.kore.K; import org.kframework.kore.KRewrite; import org.kframework.kore.TransformK; -import static org.kframework.kore.KORE.*; - /** - * A visitor designed to track whether we are currently in the left hand side or right hand side of a term. + * A visitor designed to track whether we are currently in the left hand side or right hand side of + * a term. * - * This visitor provides two boolean methods, isRHS() and isLHS(). Outside of a rewrite, both are true, signifying - * that the term being visited is part of both the LHS and the RHS. Inside a rewrite, only one is true. It is an error - * for both to be false. + *

This visitor provides two boolean methods, isRHS() and isLHS(). Outside of a rewrite, both are + * true, signifying that the term being visited is part of both the LHS and the RHS. Inside a + * rewrite, only one is true. It is an error for both to be false. */ public class RewriteAwareTransformer extends TransformK { - public RewriteAwareTransformer(boolean isBody) { - if (isBody) { - isRHS = true; - isLHS = true; - } else { - isRHS = true; - isLHS = false; - } + public RewriteAwareTransformer(boolean isBody) { + if (isBody) { + isRHS = true; + isLHS = true; + } else { + isRHS = true; + isLHS = false; } - - - private boolean isRHS; - private boolean isLHS; - - public boolean isLHS() { - return isLHS; - } - - public boolean isRHS() { - return isRHS; - } - - @Override - public K apply(KRewrite k) { - isRHS = false; - K left = apply(k.left()); - isRHS = true; - isLHS = false; - K right = apply(k.right()); - isLHS = true; - if (left != k.left() || right != k.right()) { - return KRewrite(left, right, k.att()); - } else { - return k; - } + } + + private boolean isRHS; + private boolean isLHS; + + public boolean isLHS() { + return isLHS; + } + + public boolean isRHS() { + return isRHS; + } + + @Override + public K apply(KRewrite k) { + isRHS = false; + K left = apply(k.left()); + isRHS = true; + isLHS = false; + K right = apply(k.right()); + isLHS = true; + if (left != k.left() || right != k.right()) { + return KRewrite(left, right, k.att()); + } else { + return k; } + } } diff --git a/kernel/src/main/java/org/kframework/compile/RewriteAwareVisitor.java b/kernel/src/main/java/org/kframework/compile/RewriteAwareVisitor.java index 06b1269af05..72045be9dd1 100644 --- a/kernel/src/main/java/org/kframework/compile/RewriteAwareVisitor.java +++ b/kernel/src/main/java/org/kframework/compile/RewriteAwareVisitor.java @@ -1,86 +1,90 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import java.util.Set; import org.kframework.kore.KApply; import org.kframework.kore.KRewrite; import org.kframework.kore.KVariable; import org.kframework.kore.VisitK; import org.kframework.utils.errorsystem.KEMException; -import java.util.Set; - /** - * A visitor designed to track whether we are currently in the left hand side or right hand side of a term. + * A visitor designed to track whether we are currently in the left hand side or right hand side of + * a term. * - * This visitor provides two boolean methods, isRHS() and isLHS(). Outside of a rewrite, both are true, signifying - * that the term being visited is part of both the LHS and the RHS. Inside a rewrite, only one is true. It is an error - * for both to be false. + *

This visitor provides two boolean methods, isRHS() and isLHS(). Outside of a rewrite, both are + * true, signifying that the term being visited is part of both the LHS and the RHS. Inside a + * rewrite, only one is true. It is an error for both to be false. */ public class RewriteAwareVisitor extends VisitK { - private final Set errors; - public RewriteAwareVisitor(boolean isBody, Set errors) { - this.errors = errors; - if (isBody) { - isRHS = true; - isLHS = true; - } else { - isRHS = true; - isLHS = false; - } + private final Set errors; + + public RewriteAwareVisitor(boolean isBody, Set errors) { + this.errors = errors; + if (isBody) { + isRHS = true; + isLHS = true; + } else { + isRHS = true; + isLHS = false; } + } + private boolean isRHS; + private boolean isLHS; - private boolean isRHS; - private boolean isLHS; + public boolean isLHS() { + return isLHS; + } - public boolean isLHS() { - return isLHS; - } + public boolean isRHS() { + return isRHS; + } - public boolean isRHS() { - return isRHS; - } + @Override + public void apply(KRewrite k) { + isRHS = false; + apply(k.left()); + isRHS = true; + isLHS = false; + apply(k.right()); + isLHS = true; + } - @Override - public void apply(KRewrite k) { + @Override + public void apply(KApply k) { + if (!(k.klabel() instanceof KVariable) && k.klabel().name().equals("#fun2") + || k.klabel().name().equals("#fun3") + || k.klabel().name().equals("#let")) { + boolean wasRHS = isRHS; + boolean wasLHS = isLHS; + if (k.klabel().name().equals("#fun2")) { + isRHS = true; + isLHS = true; + apply(k.items().get(0)); + // in well formed programs this should always reset to true and false, but we want to make + // sure we don't + // create spurious reports if this constraint was violated by the user. + isRHS = wasRHS; + isLHS = wasLHS; + apply(k.items().get(1)); + } else { isRHS = false; - apply(k.left()); + isLHS = true; + apply(k.items().get(0)); isRHS = true; isLHS = false; - apply(k.right()); - isLHS = true; - } - - @Override - public void apply(KApply k) { - if (!(k.klabel() instanceof KVariable) && k.klabel().name().equals("#fun2") || k.klabel().name().equals("#fun3") || k.klabel().name().equals("#let")) { - boolean wasRHS = isRHS; - boolean wasLHS = isLHS; - if (k.klabel().name().equals("#fun2")) { - isRHS = true; - isLHS = true; - apply(k.items().get(0)); - // in well formed programs this should always reset to true and false, but we want to make sure we don't - // create spurious reports if this constraint was violated by the user. - isRHS = wasRHS; - isLHS = wasLHS; - apply(k.items().get(1)); - } else { - isRHS = false; - isLHS = true; - apply(k.items().get(0)); - isRHS = true; - isLHS = false; - apply(k.items().get(1)); - // in well formed programs this should always reset to true and false, but we want to make sure we don't - // create spurious reports if this constraint was violated by the user. - isRHS = wasRHS; - isLHS = wasLHS; - apply(k.items().get(2)); - } - } else { - super.apply(k); - } + apply(k.items().get(1)); + // in well formed programs this should always reset to true and false, but we want to make + // sure we don't + // create spurious reports if this constraint was violated by the user. + isRHS = wasRHS; + isLHS = wasLHS; + apply(k.items().get(2)); + } + } else { + super.apply(k); } + } } diff --git a/kernel/src/main/java/org/kframework/compile/SortCells.java b/kernel/src/main/java/org/kframework/compile/SortCells.java index faaffb962ec..a5aff6766d2 100644 --- a/kernel/src/main/java/org/kframework/compile/SortCells.java +++ b/kernel/src/main/java/org/kframework/compile/SortCells.java @@ -1,9 +1,24 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.Collections.*; +import static org.kframework.kore.KORE.*; + import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.annotation.Nonnull; import org.kframework.attributes.Att; import org.kframework.builtin.BooleanUtils; import org.kframework.builtin.Sorts; @@ -25,859 +40,910 @@ import scala.collection.JavaConversions; import scala.collection.Seq; -import javax.annotation.Nonnull; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.kframework.Collections.*; -import static org.kframework.kore.KORE.*; - /** - * Arrange cell contents and variables to match the klabels declared for cells. - * In Full K, cell contents can be written in any order, and variables can - * be written that match multiple cells. - *

- * In the input to this pass, parent cells are represented by appling the label directly - * to a klist of all the children, variables, and rewrites under the cell. - * Left cells should already be in their final form. - * In the output each cell will be represented by using the cell labels in agreement - * with the production declaring it, so parent cells will have a fixed arity with separate + * Arrange cell contents and variables to match the klabels declared for cells. In Full K, cell + * contents can be written in any order, and variables can be written that match multiple cells. + * + *

In the input to this pass, parent cells are represented by appling the label directly to a + * klist of all the children, variables, and rewrites under the cell. Left cells should already be + * in their final form. In the output each cell will be represented by using the cell labels in + * agreement with the production declaring it, so parent cells will have a fixed arity with separate * argument positions reserved for different types of child cell. - *

- * The most complicated part of the transformation is dealing with variables - * in cells. An occurrence in a cell that might match child cells of different - * sorts has to be split into several variables in different arguments, and any - * occurrence of the variable outside of a cell replaced by a suitable - * expression involving the split variables, using the cell fragment productions + * + *

The most complicated part of the transformation is dealing with variables in cells. An + * occurrence in a cell that might match child cells of different sorts has to be split into several + * variables in different arguments, and any occurrence of the variable outside of a cell replaced + * by a suitable expression involving the split variables, using the cell fragment productions * introduced along with the cell labels. */ public class SortCells { - private final ConcretizationInfo cfg; - private final LabelInfo labelInfo; - private final Module module; - public SortCells(ConfigurationInfo cfgInfo, LabelInfo labelInfo, Module module) { - this.cfg = new ConcretizationInfo(cfgInfo, labelInfo); - this.labelInfo = labelInfo; - this.module = module; - } - public SortCells(ConfigurationInfo cfgInfo, LabelInfo labelInfo) { - this.cfg = new ConcretizationInfo(cfgInfo, labelInfo); - this.labelInfo = labelInfo; - this.module = null; + private final ConcretizationInfo cfg; + private final LabelInfo labelInfo; + private final Module module; + + public SortCells(ConfigurationInfo cfgInfo, LabelInfo labelInfo, Module module) { + this.cfg = new ConcretizationInfo(cfgInfo, labelInfo); + this.labelInfo = labelInfo; + this.module = module; + } + + public SortCells(ConfigurationInfo cfgInfo, LabelInfo labelInfo) { + this.cfg = new ConcretizationInfo(cfgInfo, labelInfo); + this.labelInfo = labelInfo; + this.module = null; + } + + public synchronized K sortCells(K term) { + resetVars(); + analyzeVars(term); + return processVars(term); + } + + private RuleOrClaim sortCells(RuleOrClaim rule) { + resetVars(); + analyzeVars(rule.body()); + analyzeVars(rule.requires()); + analyzeVars(rule.ensures()); + rule = + rule.newInstance( + processVars(rule.body()), + processVars(rule.requires()), + processVars(rule.ensures()), + rule.att()); + rule = + rule.newInstance( + resolveIncompleteCellFragment(rule.body()), + resolveIncompleteCellFragment(rule.requires()), + resolveIncompleteCellFragment(rule.ensures()), + rule.att()); + return rule; + } + + private Context sortCells(Context context) { + resetVars(); + analyzeVars(context.body()); + analyzeVars(context.requires()); + return new Context(processVars(context.body()), processVars(context.requires()), context.att()); + } + + public synchronized Sentence sortCells(Sentence s) { + if (s instanceof RuleOrClaim) { + return sortCells((RuleOrClaim) s); + } else if (s instanceof Context) { + return sortCells((Context) s); + } else { + return s; } - - public synchronized K sortCells(K term) { - resetVars(); - analyzeVars(term); - return processVars(term); - + } + + // Information on uses of a particular cell fragment variable + private class VarInfo { + KVariable var; + KLabel parentCell; + LinkedHashSet remainingCells; + Map split; + + void addOccurances(KLabel cell, KVariable var, List items) { + assert split == null; // need to add all occurances before getting any split. + this.var = var; + if (parentCell == null) { + parentCell = cell; + } else if (!parentCell.equals(cell)) { + throw KEMException.criticalError( + "Cell variable " + var + " used under two cells, " + parentCell + " and " + cell); + } + if (remainingCells == null) { + remainingCells = new LinkedHashSet<>(cfg.getChildren(cell)); + } + if (var.att().contains(Sort.class)) { + Sort sort = var.att().get(Sort.class); + if (cfg.cfg().isCell(sort)) { + remainingCells.removeIf(s -> !s.equals(sort)); + } + } + for (K item : items) { + if (item instanceof KApply kApply) { + Sort s = cfg.getCellSort(kApply.klabel()); + if (s != null && cfg.getMultiplicity(s) != Multiplicity.STAR) { + remainingCells.remove(s); + } + } else if (item instanceof KVariable && !item.equals(var)) { + if (item.att().contains(Sort.class)) { + Sort sort = item.att().get(Sort.class); + remainingCells.remove(sort); + } + } + } } - private RuleOrClaim sortCells(RuleOrClaim rule) { - resetVars(); - analyzeVars(rule.body()); - analyzeVars(rule.requires()); - analyzeVars(rule.ensures()); - rule = rule.newInstance( - processVars(rule.body()), - processVars(rule.requires()), - processVars(rule.ensures()), - rule.att()); - rule = rule.newInstance( - resolveIncompleteCellFragment(rule.body()), - resolveIncompleteCellFragment(rule.requires()), - resolveIncompleteCellFragment(rule.ensures()), - rule.att()); - return rule; + K replacementTerm() { + getSplit(var); + KLabel fragmentLabel = cfg.getCellFragmentLabel(parentCell); + if (fragmentLabel == null) { + throw KEMException.compilerError( + "Unsupported cell fragment with types: " + remainingCells, var); + } + List children = cfg.getChildren(parentCell); + List arguments = new ArrayList<>(children.size()); + for (Sort child : children) { + K arg = split.get(child); + if (arg == null) { + if (cfg.getMultiplicity(child) == Multiplicity.ONE) { + arg = cfg.getCellAbsentTerm(child); + } else { + arg = cfg.cfg().getUnit(child); + } + } + assert arg != null; + arguments.add(arg); + } + return KApply(fragmentLabel, immutable(arguments)); } - private Context sortCells(Context context) { - resetVars(); - analyzeVars(context.body()); - analyzeVars(context.requires()); - return new Context( - processVars(context.body()), - processVars(context.requires()), - context.att()); + Map getSplit(KVariable var) { + if (split != null) { + return split; + } + if (remainingCells.size() == 0) { + split = Collections.emptyMap(); + } else if (remainingCells.size() == 1) { + Sort s = Iterables.getOnlyElement(remainingCells); + if (cfg.getMultiplicity(s) == Multiplicity.STAR) { + split = + ImmutableMap.of( + s, KVariable(var.name(), var.att().add(Sort.class, getPredicateSort(s)))); + } else { + split = + ImmutableMap.of( + s, KVariable(var.name(), var.att().add(Sort.class, s).add(Att.CELL_SORT()))); + } + } else { + split = new HashMap<>(); + for (Sort cell : remainingCells) { + split.put(cell, newDotVariable(var.att().add(Sort.class, cell).add(Att.CELL_SORT()))); + } + } + return split; } + } + + private int counter = 0; + + KVariable newDotVariable(Att att) { + KVariable newLabel; + do { + newLabel = KVariable("_Gen" + (counter++), att); + } while (variables.containsKey(newLabel) || previousVars.contains(newLabel)); + variables.put(newLabel, null); + return newLabel; + } + + private final Map variables = new HashMap<>(); + private final Map cellVariables = new HashMap<>(); + private final Set previousVars = new HashSet<>(); + + private void resetVars() { + variables.clear(); + cellVariables.clear(); + previousVars.clear(); + counter = 0; + } + + private void analyzeVars(K term) { + new VisitK() { + private boolean inRewrite = false; + private boolean inRhs = false; + + private Stream streamCells(K term) { + return IncompleteCellUtils.flattenCells(term).stream(); + } - public synchronized Sentence sortCells(Sentence s) { - if (s instanceof RuleOrClaim) { - return sortCells((RuleOrClaim) s); - } else if (s instanceof Context) { - return sortCells((Context) s); + private K left(K term) { + if (term instanceof KRewrite) { + return ((KRewrite) term).left(); } else { - return s; + return term; } - } + } - // Information on uses of a particular cell fragment variable - private class VarInfo { - KVariable var; - KLabel parentCell; - LinkedHashSet remainingCells; - Map split; - - void addOccurances(KLabel cell, KVariable var, List items) { - assert split == null; // need to add all occurances before getting any split. - this.var = var; - if (parentCell == null) { - parentCell = cell; - } else if (!parentCell.equals(cell)) { - throw KEMException.criticalError("Cell variable "+var+" used under two cells, " - + parentCell + " and " + cell); - } - if (remainingCells == null) { - remainingCells = new LinkedHashSet<>(cfg.getChildren(cell)); - } - if (var.att().contains(Sort.class)) { - Sort sort = var.att().get(Sort.class); - if (cfg.cfg().isCell(sort)) { - remainingCells.removeIf(s -> !s.equals(sort)); - } - } - for (K item : items) { - if (item instanceof KApply kApply) { - Sort s = cfg.getCellSort(kApply.klabel()); - if (s != null && cfg.getMultiplicity(s) != Multiplicity.STAR) { - remainingCells.remove(s); - } - } else if (item instanceof KVariable && !item.equals(var)) { - if (item.att().contains(Sort.class)) { - Sort sort = item.att().get(Sort.class); - remainingCells.remove(sort); - } - } - } + private K right(K term) { + if (term instanceof KRewrite) { + return ((KRewrite) term).right(); + } else { + return term; } + } - K replacementTerm() { - getSplit(var); - KLabel fragmentLabel = cfg.getCellFragmentLabel(parentCell); - if (fragmentLabel == null) { - throw KEMException.compilerError("Unsupported cell fragment with types: " + remainingCells, var); - } - List children = cfg.getChildren(parentCell); - List arguments = new ArrayList<>(children.size()); - for (Sort child : children) { - K arg = split.get(child); - if (arg == null) { - if (cfg.getMultiplicity(child) == Multiplicity.ONE) { - arg = cfg.getCellAbsentTerm(child); - } else { - arg = cfg.cfg().getUnit(child); - } - } - assert arg != null; - arguments.add(arg); - } - return KApply(fragmentLabel, immutable(arguments)); + @Override + public void apply(KApply k) { + if (cfg.isParentCell(k.klabel())) { + if (inRewrite) { + processSide( + k, + inRhs, + k.klist().stream().flatMap(this::streamCells).collect(Collectors.toList())); + } else { + processSide( + k, + false, + k.klist().stream() + .flatMap(this::streamCells) + .map(this::left) + .flatMap(this::streamCells) + .collect(Collectors.toList())); + + processSide( + k, + true, + k.klist().stream() + .flatMap(this::streamCells) + .map(this::right) + .flatMap(this::streamCells) + .collect(Collectors.toList())); + } } + super.apply(k); + } - Map getSplit(KVariable var) { - if(split != null) { - return split; - } - if (remainingCells.size() == 0) { - split = Collections.emptyMap(); - } else if (remainingCells.size() == 1) { - Sort s = Iterables.getOnlyElement(remainingCells); - if (cfg.getMultiplicity(s) == Multiplicity.STAR) { - split = ImmutableMap.of(s, KVariable( - var.name(), - var.att().add(Sort.class, getPredicateSort(s)))); - } else { - split = ImmutableMap.of(s, KVariable(var.name(), var.att().add(Sort.class, s).add(Att.CELL_SORT()))); + private void processSide(KApply parentCell, boolean allowRhs, List items) { + List bagVars = new ArrayList<>(); + for (K item : items) { + if (item instanceof KVariable var) { + if (var.att().contains(Sort.class)) { + Sort sort = var.att().get(Sort.class); + if (cfg.cfg().isCell(sort)) { + if (!cellVariables.getOrDefault(var, sort).equals(sort)) { + Sort prevSort = cellVariables.get(var); + throw KEMException.compilerError( + "Variable " + + var + + " occurs annotated as multiple cell sorts, " + + sort + + " and " + + prevSort, + item); } - } else { - split = new HashMap<>(); - for (Sort cell : remainingCells) { - split.put(cell, newDotVariable(var.att().add(Sort.class, cell).add(Att.CELL_SORT()))); + if (variables.containsKey(var)) { + throw KEMException.compilerError( + "Variable " + + var + + " occurs with cell sort " + + sort + + " after occurance without cell sort annotation"); + } + cellVariables.put(var, sort); + continue; + } else { + if (cellVariables.containsKey(var)) { + throw KEMException.compilerError( + "Variable " + + var + + " occurs annotated as non-cell sort " + + sort + + " after appearing as cell sort " + + cellVariables.get(var), + item); } + } } - return split; + if (cellVariables.containsKey(var)) { + throw KEMException.compilerError( + "Variable " + + var + + " occurs without sort annotation after appearing as cell sort " + + cellVariables.get(var), + item); + } + bagVars.add(var); + } } - } + if (!allowRhs && bagVars.size() > 1) { + throw KEMException.compilerError( + "AC matching of multiple cell variables not yet supported. " + + "encountered variables " + + bagVars + + " in cell " + + parentCell.klabel(), + parentCell); + } + for (KVariable var : bagVars) { + if (!variables.containsKey(var)) { + variables.put(var, new VarInfo()); + } + variables.get(var).addOccurances(parentCell.klabel(), var, items); + } + } + @Override + public void apply(KRewrite k) { + assert !inRewrite; + inRewrite = true; + apply(k.left()); + inRhs = true; + apply(k.right()); + inRhs = false; + inRewrite = false; + } - private int counter = 0; - KVariable newDotVariable(Att att) { - KVariable newLabel; - do { - newLabel = KVariable("_Gen" + (counter++), att); - } while (variables.containsKey(newLabel) || previousVars.contains(newLabel)); - variables.put(newLabel, null); - return newLabel; + @Override + public void apply(KVariable k) { + previousVars.add(k); + } + }.apply(term); + } + + private Sort getPredicateSort(Sort s) { + if (cfg.getMultiplicity(s) == Multiplicity.STAR) { + scala.collection.Set sorts = cfg.cfg().getCellBagSortsOfCell(s); + if (sorts.size() != 1) { + throw KEMException.compilerError( + "Expected exactly one cell collection sort for the sort " + s + "; found " + sorts); + } + return stream(sorts).findFirst().get(); } - - private final Map variables = new HashMap<>(); - private final Map cellVariables = new HashMap<>(); - private final Set previousVars = new HashSet<>(); - - private void resetVars() { - variables.clear(); cellVariables.clear(); previousVars.clear(); counter = 0; + return s; + } + + private boolean isCellFragmentTest(KApply app) { + if (app.klist().size() != 1) return false; + K argument = app.klist().items().get(0); + if (!(argument instanceof KVariable)) return false; + VarInfo info = variables.get((KVariable) argument); + if (info == null) return false; + KLabel expectedPredicate = + KLabel("is" + cfg.getCellSort(info.parentCell).toString() + "Fragment"); + return app.klabel().equals(expectedPredicate); + } + + private int indexOf(List l, Sort o, KApply loc) { + int idx = l.indexOf(o); + if (idx == -1) { + throw KEMException.compilerError( + "Expected to find sort " + + o.toString() + + " in the children of cell with klabel " + + loc.klabel(), + loc); } - - private void analyzeVars(K term) { - new VisitK() { - private boolean inRewrite = false; - private boolean inRhs = false; - - private Stream streamCells(K term) { - return IncompleteCellUtils.flattenCells(term).stream(); + return idx; + } + + /** + * Expand away cell fragment variables, and correctly order the children of cells. There are three + * significnat contexts for expanding cell fragments - as an argument to a parent cell it splits + * into separate arguments for each of the variables in most other uses, it expands into a term + * applying the appropriate {@code -fragment} label to the split variables, except that + * applications of an {@code isCellFragment} sort predicate to a cell fragment variable decomposes + * into a conjunction of sort predicate tests on the split variables. + */ + private K processVars(K term) { + return new TransformK() { + @Override + public K apply(KApply k) { + if (!cfg.isParentCell(k.klabel())) { + if (isCellFragmentTest(k)) { + return getSplit(k.klist().items().get(0)).entrySet().stream() + .map(e -> (K) KApply(KLabel("is" + getPredicateSort(e.getKey())), e.getValue())) + .reduce(BooleanUtils.TRUE, BooleanUtils::and); + } else if (k.klabel().name().equals("isBag") + && k.klist().size() == 1 + && k.klist().items().get(0) instanceof KVariable var) { + VarInfo info = variables.get(var); + if (info != null) { + return info.getSplit(var).entrySet().stream() + .map(e -> (K) KApply(KLabel("is" + getPredicateSort(e.getKey())), e.getValue())) + .reduce(BooleanUtils.TRUE, BooleanUtils::and); } - - private K left(K term) { - if (term instanceof KRewrite) { - return ((KRewrite)term).left(); - } else { - return term; - } - } - private K right(K term) { - if (term instanceof KRewrite) { - return ((KRewrite)term).right(); - } else { - return term; - } + } + return super.apply(k); + } else { + List order = cfg.getChildren(k.klabel()); + ArrayList ordered = new ArrayList(Collections.nCopies(order.size(), null)); + for (K item : k.klist().items()) { + Map split = getSplit(item); + for (Map.Entry e : split.entrySet()) { + int idx = indexOf(order, e.getKey(), k); + if (ordered.get(idx) != null) { + ordered.set( + idx, + concatenateStarCells( + e.getKey(), Arrays.asList(ordered.get(idx), e.getValue()))); + } else { + ordered.set(idx, e.getValue()); + } } - - @Override - public void apply(KApply k) { - if (cfg.isParentCell(k.klabel())) { - if (inRewrite) { - processSide(k, inRhs, k.klist().stream() - .flatMap(this::streamCells) - .collect(Collectors.toList())); + } + order.stream() + .filter(s -> ordered.get(indexOf(order, s, k)) == null) + .forEach( + sort -> { + if (cfg.getMultiplicity(sort) == Multiplicity.ONE) { + throw KEMException.compilerError( + "Missing cell of multiplicity=\"1\": " + sort, k); } else { - processSide(k, false, k.klist().stream() - .flatMap(this::streamCells).map(this::left).flatMap(this::streamCells) - .collect(Collectors.toList())); - - processSide(k, true, k.klist().stream() - .flatMap(this::streamCells).map(this::right).flatMap(this::streamCells) - .collect(Collectors.toList())); + ordered.set(indexOf(order, sort, k), cfg.cfg().getUnit(sort)); } - } - super.apply(k); - } - - private void processSide(KApply parentCell, boolean allowRhs, List items) { - List bagVars = new ArrayList<>(); - for (K item : items) { - if (item instanceof KVariable var) { - if (var.att().contains(Sort.class)) { - Sort sort = var.att().get(Sort.class); - if (cfg.cfg().isCell(sort)) { - if (!cellVariables.getOrDefault(var, sort).equals(sort)) { - Sort prevSort = cellVariables.get(var); - throw KEMException.compilerError("Variable "+var+" occurs annotated as multiple cell sorts, "+sort+" and "+prevSort, - item); - } - if (variables.containsKey(var)) { - throw KEMException.compilerError("Variable "+var+" occurs with cell sort "+sort+" after occurance without cell sort annotation"); - } - cellVariables.put(var,sort); - continue; - } else { - if (cellVariables.containsKey(var)) { - throw KEMException.compilerError("Variable "+var+" occurs annotated as non-cell sort "+sort+" after appearing as cell sort "+cellVariables.get(var),item); - } - } - } - if (cellVariables.containsKey(var)) { - throw KEMException.compilerError("Variable "+var+" occurs without sort annotation after appearing as cell sort "+cellVariables.get(var),item); - } - bagVars.add(var); - } - } - if (!allowRhs && bagVars.size() > 1) { - throw KEMException.compilerError( - "AC matching of multiple cell variables not yet supported. " - + "encountered variables " + bagVars + " in cell " + parentCell.klabel(), parentCell); - } - for (KVariable var : bagVars) { - if (!variables.containsKey(var)) { - variables.put(var, new VarInfo()); - } - variables.get(var).addOccurances(parentCell.klabel(), var, items); - } - } + }); + return KApply(k.klabel(), KList(ordered), k.att()); + } + } - @Override - public void apply(KRewrite k) { - assert !inRewrite; - inRewrite = true; - apply(k.left()); - inRhs = true; - apply(k.right()); - inRhs = false; - inRewrite = false; + @Nonnull + private Map getSplit(K item) { + if (item instanceof KVariable) { + VarInfo info = variables.get(item); + if (info == null) { + Sort cellSort = cellVariables.get(item); + if (cellSort == null) { + throw new IllegalArgumentException("Unknown variable " + item); + } else { + return ImmutableMap.of(cellSort, item); } - - @Override - public void apply(KVariable k) { - previousVars.add(k); + } + return info.getSplit((KVariable) item); + } else if (item instanceof KApply) { + List children = IncompleteCellUtils.flattenCells(item); + if (children.size() == 1 && children.get(0) == item) { + final KLabel label = ((KApply) item).klabel(); + Sort s = cfg.getCellSort(label); + if (s == null) { + s = cfg.getCellCollectionCell(label); + if (s == null) { + throw KEMException.compilerError("Attempting to split non-cell term " + item, item); + } } - }.apply(term); - } - - private Sort getPredicateSort(Sort s) { - if (cfg.getMultiplicity(s) == Multiplicity.STAR) { - scala.collection.Set sorts = cfg.cfg().getCellBagSortsOfCell(s); - if (sorts.size() != 1) { - throw KEMException.compilerError("Expected exactly one cell collection sort for the sort " + s + "; found " + sorts); - } - return stream(sorts).findFirst().get(); + return Collections.singletonMap(s, apply(item)); + } + // flatten the List> into a Map> + Map> multiMap = + children.stream() + .flatMap(e -> getSplit(e).entrySet().stream()) + .collect( + Collectors.groupingBy( + Map.Entry::getKey, + Collectors.mapping(Map.Entry::getValue, Collectors.toList()))); + return multiMap.entrySet().stream() + .filter(e -> e.getValue().size() > 0) + .collect( + Collectors.toMap( + e -> e.getKey(), + e -> { + if (e.getValue().size() == 1) { + return e.getValue().get(0); + } else { + return concatenateStarCells(e.getKey(), e.getValue()); + } + })); + } else if (item instanceof KRewrite rw) { + Map splitLeft = new HashMap<>(getSplit(rw.left())); + Map splitRight = new HashMap<>(getSplit(rw.right())); + addDefaultCells(item, splitLeft, splitRight); + addDefaultCells(item, splitRight, splitLeft); + assert splitLeft.keySet().equals(splitRight.keySet()); + return splitLeft.keySet().stream() + .collect( + Collectors.toMap( + sort -> sort, + sort -> KRewrite(splitLeft.get(sort), splitRight.get(sort), rw.att()))); + } else { + throw KEMException.compilerError( + "Unexpected kind of term found in cell. Expected variable, " + + "apply, or rewrite; found " + + item.getClass().getSimpleName(), + item); } - return s; - } + } - private boolean isCellFragmentTest(KApply app) { - if (app.klist().size() != 1) return false; - K argument = app.klist().items().get(0); - if (!(argument instanceof KVariable)) return false; - VarInfo info = variables.get((KVariable)argument); - if (info == null) return false; - KLabel expectedPredicate = KLabel("is"+cfg.getCellSort(info.parentCell).toString()+"Fragment"); - return app.klabel().equals(expectedPredicate); - } + private K concatenateStarCells(Sort sort, List children) { + if (cfg.getMultiplicity(sort) != Multiplicity.STAR) { + throw KEMException.compilerError( + "Attempting to concatenate cells not of multiplicity=\"*\" " + + "into a cell collection.", + children.iterator().next()); + } + if (children.size() == 0) { + return cfg.cfg().getUnit(sort); + } + KLabel concat = cfg.cfg().getConcat(sort); + int ix = children.size(); + K result = children.get(--ix); + while (ix > 0) { + result = KApply(concat, children.get(--ix), result); + } + return result; + } - private int indexOf(List l, Sort o, KApply loc) { - int idx = l.indexOf(o); - if (idx == -1) { - throw KEMException.compilerError("Expected to find sort " + o.toString() + " in the children of cell with klabel " + loc.klabel(), loc); + private void addDefaultCells(K item, Map splitLeft, Map splitRight) { + for (Sort s : Sets.difference(splitLeft.keySet(), splitRight.keySet())) { + if (cfg.getMultiplicity(s) == Multiplicity.ONE) { + throw KEMException.compilerError( + "Cannot rewrite a multiplicity=\"1\" cell to or from the cell unit.", item); + } else { + splitRight.put(s, cfg.cfg().getUnit(s)); + } + } } - return idx; - } - /** - * Expand away cell fragment variables, and correctly order the children of cells. - * There are three significnat contexts for expanding cell fragments - - * as an argument to a parent cell it splits into separate arguments for each of the variables - * in most other uses, it expands into a term applying the appropriate {@code -fragment} label - * to the split variables, except that applications of an {@code isCellFragment} sort predicate - * to a cell fragment variable decomposes into a conjunction of sort predicate tests on the split - * variables. - */ - private K processVars(K term) { - return new TransformK() { - @Override - public K apply(KApply k) { - if (!cfg.isParentCell(k.klabel())) { - if (isCellFragmentTest(k)) { - return getSplit(k.klist().items().get(0)).entrySet().stream() - .map(e -> (K) KApply(KLabel("is" + getPredicateSort(e.getKey())), e.getValue())) - .reduce(BooleanUtils.TRUE, BooleanUtils::and); - } else if(k.klabel().name().equals("isBag") - && k.klist().size() == 1 - && k.klist().items().get(0) instanceof KVariable var) { - VarInfo info = variables.get(var); - if (info != null) { - return info.getSplit(var).entrySet().stream() - .map(e -> (K) KApply(KLabel("is" + getPredicateSort(e.getKey())), e.getValue())) - .reduce(BooleanUtils.TRUE, BooleanUtils::and); - } + @Override + public K apply(KVariable v) { + VarInfo info = variables.get(v); + if (info != null) { + return info.replacementTerm(); + } else { + return v; + } + } + }.apply(term); + } + + /** + * processVars handles a cell fragment variable when it appears solely in a normal term, e.g, rule + * run => foo(X) ... X:XCellFragment _ but not when it appears with + * other cells, e.g., rule run => foo(X C) ... X:XCellFragment C + * resolveIncompleteCellFragment handles such cases, after processVars is done. An idea is to fix + * invalid cell fragment terms. For example, processVars translates the term `foo(X)` into: + * foo(-fragment A B -fragment C) then resolveIncompleteCellFragment fixes the term + * as: foo(-fragment A B C -fragment) + * + *

Another example: When a cell fragment term consists of only individual cells, processVars + * does nothing. rule run => foo(B C) ... _ B:BCell C:CCell In this + * case, resolveIncompleteCellFragment fixes the term `foo(B C)` into: foo(-fragment noACell B + * C -fragment) + * + *

Another example: When a cell fragment variable appears only in a normal term, but never in + * cells, processVars does nothing. rule foo( _ X:XCellFragment) => bar( 2 X) In + * this case, the cell fragment term in LHS is temporarily decorated by dummy cell label, + * before processVars, which triggers processVars to split the cell fragment variable. Then + * resolveIncompleteCellFragment comes in. After resolveIncompleteCellFragment, the dummy cell + * label is replaced by the corresponding cell fragment label. The pre-/post-processing is done by + * preprocess/prostprocess. + * + *

Potential issue: In the above example, if X has no sort casting, it is considered as + * XCellFragment by default, which may be different with an user's intention. + * + *

Another potential issue: Currently, one cannot represent a fully filled cell fragment term. + * e.g., rule foo(X) => .K ... _ => X The above rule replaces the entire cell + * with the cell fragment term X, but doesn't have a side condition saying that X should be + * fully decorated. It means that X is given by a partially decorated cell fragment, cell + * becomes invalid. e.g., foo(-fragment 1 noBCell noCCell -fragment) results in an + * invalid cell: 1 noBCell noCCell Note that this issue is not specifically + * about resolveIncompleteCellFragment. + */ + private K resolveIncompleteCellFragment(K term) { + return new TransformK() { + @Override + public K apply(KApply k0) { + if (!hasCells(k0)) return super.apply(k0); + + ArrayList klist0 = new ArrayList(Collections.nCopies(k0.klist().size(), null)); + for (int idx = 0; idx < k0.klist().size(); idx++) { + K item0 = k0.klist().items().get(idx); + klist0.set(idx, item0); + if (item0 instanceof KApply k) { + + // incomplete cells remain as #cells(...) after processVars + if (k.klabel().name().equals("#cells")) { + + Sort cellFragmentSort = nthArgSort(k0.klabel(), idx); + if (cellFragmentSort == null) { + throw new IllegalArgumentException( + "Not found " + idx + "th argument sort of " + k0.klabel()); + } + + // a cell fragment term + if (cellFragmentSort.name().endsWith("Fragment")) { + + Sort cellSort = + Sort( + cellFragmentSort + .name() + .substring(0, cellFragmentSort.name().indexOf("Fragment"))); + KLabel cellLabel = cfg.cfg().getCellLabel(cellSort); + + List subcellSorts = cfg.getChildren(cellLabel); + + /* + fix an invalid cell fragment term, e.g., + + Case 1. + from + foo(-fragment A B -fragment C) + into + foo(-fragment A B C -fragment) + + Case 2. + from + foo(B C) + into + foo(-fragment noACell B C -fragment) + */ + + // fill individual cells first, starting with empty + KApply cellFragment = null; + ArrayList klist = + new ArrayList(Collections.nCopies(subcellSorts.size(), null)); + for (K item : + IncompleteCellUtils.flattenCells(k)) { // #cells(#cells(x,y),z) => [x,y,z] + if (item instanceof KApply kapp) { + if (cfg.cfg().isCellLabel(kapp.klabel())) { + Sort sort = cfg.getCellSort(kapp.klabel()); + if (!subcellSorts.contains(sort)) { + throw new IllegalArgumentException( + "No such sub-cell " + sort + " in the cell " + cellLabel); + } + klist.set(subcellSorts.indexOf(sort), item); + } else if (kapp.klabel().name().endsWith("-fragment")) { + cellFragment = kapp; + assert cellFragment.klist().size() == subcellSorts.size(); + assert cellFragment.klabel().name().equals(cellLabel.name() + "-fragment"); + } else { + throw KEMException.compilerError("Unsupported cell fragment element.", item); } - return super.apply(k); - } else { - List order = cfg.getChildren(k.klabel()); - ArrayList ordered = new ArrayList(Collections.nCopies(order.size(), null)); - for (K item : k.klist().items()) { - Map split = getSplit(item); - for (Map.Entry e : split.entrySet()) { - int idx = indexOf(order, e.getKey(), k); - if (ordered.get(idx) != null) { - ordered.set(idx, concatenateStarCells(e.getKey(), Arrays.asList(ordered.get(idx), e.getValue()))); - } else { - ordered.set(idx, e.getValue()); - } - } + } else if (item instanceof KVariable var) { + VarInfo varinfo = null; + if (variables.containsKey(var)) { + varinfo = variables.get(var); } - order.stream().filter(s -> ordered.get(indexOf(order, s, k)) == null).forEach(sort -> { - if (cfg.getMultiplicity(sort) == Multiplicity.ONE) { - throw KEMException.compilerError("Missing cell of multiplicity=\"1\": " + sort, k); - } else { - ordered.set(indexOf(order, sort, k), cfg.cfg().getUnit(sort)); - } - }); - return KApply(k.klabel(), KList(ordered), k.att()); - } - } - - @Nonnull - private Map getSplit(K item) { - if (item instanceof KVariable) { - VarInfo info = variables.get(item); - if (info == null) { - Sort cellSort = cellVariables.get(item); - if (cellSort == null) { - throw new IllegalArgumentException("Unknown variable " + item); - } else { - return ImmutableMap.of(cellSort,item); - } + if (!var.att().contains(Sort.class) && varinfo != null) { + if (varinfo.var != null) var = varinfo.var; } - return info.getSplit((KVariable) item); - } else if (item instanceof KApply) { - List children = IncompleteCellUtils.flattenCells(item); - if(children.size() == 1 && children.get(0) == item) { - final KLabel label = ((KApply) item).klabel(); - Sort s = cfg.getCellSort(label); - if (s == null) { - s = cfg.getCellCollectionCell(label); - if (s == null) { - throw KEMException.compilerError("Attempting to split non-cell term " + item, item); + if (var.att().contains(Sort.class)) { + Sort sort = var.att().get(Sort.class); + if (cfg.cfg().isCell(sort)) { + if (!subcellSorts.contains(sort)) { + throw new IllegalArgumentException( + "No such sub-cell " + sort + " in the cell " + cellLabel); + } + klist.set(subcellSorts.indexOf(sort), item); + } else { + // if the variable is not explicitly sort-casted, then its sort information + // should be found in other places + if (varinfo != null + && varinfo.remainingCells != null + && varinfo.remainingCells.size() == 1) { + Sort s = Iterables.getOnlyElement(varinfo.remainingCells); + if (cfg.cfg().isCell(s)) { + if (!subcellSorts.contains(s)) { + throw new IllegalArgumentException( + "No such sub-cell " + s + " in the cell " + cellLabel); } + klist.set(subcellSorts.indexOf(s), item); + continue; + } } - return Collections.singletonMap(s, apply(item)); + throw KEMException.compilerError( + "Unsupported cell fragment element. Not found sort info.", item); + } + } else { + throw KEMException.compilerError( + "Unsupported cell fragment element. Not found sort info.", item); } - // flatten the List> into a Map> - Map> multiMap = children.stream().flatMap(e -> getSplit(e).entrySet().stream()).collect( - Collectors.groupingBy(Map.Entry::getKey, - Collectors.mapping(Map.Entry::getValue, Collectors.toList()))); - return multiMap.entrySet().stream().filter(e -> e.getValue().size() > 0).collect(Collectors.toMap(e -> e.getKey(), e -> { - if (e.getValue().size() == 1) { - return e.getValue().get(0); - } else { - return concatenateStarCells(e.getKey(), e.getValue()); - } - })); - } else if (item instanceof KRewrite rw) { - Map splitLeft = new HashMap<>(getSplit(rw.left())); - Map splitRight = new HashMap<>(getSplit(rw.right())); - addDefaultCells(item, splitLeft, splitRight); - addDefaultCells(item, splitRight, splitLeft); - assert splitLeft.keySet().equals(splitRight.keySet()); - return splitLeft.keySet().stream().collect(Collectors.toMap(sort -> sort, - sort -> KRewrite(splitLeft.get(sort), splitRight.get(sort), rw.att()))); - } else { - throw KEMException.compilerError("Unexpected kind of term found in cell. Expected variable, " - + "apply, or rewrite; found " + item.getClass().getSimpleName(), item); - } - } - - private K concatenateStarCells(Sort sort, List children) { - if (cfg.getMultiplicity(sort) != Multiplicity.STAR) { - throw KEMException.compilerError("Attempting to concatenate cells not of multiplicity=\"*\" " - + "into a cell collection.", children.iterator().next()); - } - if (children.size() == 0) { - return cfg.cfg().getUnit(sort); - } - KLabel concat = cfg.cfg().getConcat(sort); - int ix = children.size(); - K result = children.get(--ix); - while (ix > 0) { - result = KApply(concat,children.get(--ix),result); + } else { + // TODO: support when item instanceof KRewrite + throw KEMException.compilerError("Unsupported cell fragment element.", item); + } } - return result; - } - private void addDefaultCells(K item, Map splitLeft, Map splitRight) { - for (Sort s : Sets.difference(splitLeft.keySet(), splitRight.keySet())) { - if (cfg.getMultiplicity(s) == Multiplicity.ONE) { - throw KEMException.compilerError("Cannot rewrite a multiplicity=\"1\" cell to or from the cell unit.", item); + // fill remaining cells, considering a split cell fragment variable if any + for (int i = 0; i < subcellSorts.size(); i++) { + if (klist.get(i) == null) { + if (cellFragment != null) { + klist.set(i, cellFragment.klist().items().get(i)); } else { - splitRight.put(s, cfg.cfg().getUnit(s)); + if (cfg.getMultiplicity(subcellSorts.get(i)) == Multiplicity.ONE) { + klist.set(i, cfg.getCellAbsentTerm(subcellSorts.get(i))); + } else { // Multiplicity.OPTIONAL || Multiplicity.STAR + klist.set(i, cfg.cfg().getUnit(subcellSorts.get(i))); + } } + } } - } - @Override - public K apply(KVariable v) { - VarInfo info = variables.get(v); - if (info != null) { - return info.replacementTerm(); - } else { - return v; - } + klist0.set(idx, KApply(KLabel(cellLabel.name() + "-fragment"), KList(klist))); + } } - }.apply(term); + } + } + return KApply(k0.klabel(), KList(klist0), k0.att()); + } + }.apply(term); + } + + /** Pre-process terms before processVar */ + public synchronized Sentence preprocess(Sentence s) { + if (s instanceof RuleOrClaim) { + return preprocess((RuleOrClaim) s); + } else { + return s; } - - - /** - * processVars handles a cell fragment variable when it appears solely in a normal term, e.g, - * rule run => foo(X) ... - * - * X:XCellFragment - * _ - * - * but not when it appears with other cells, e.g., - * rule run => foo(X C) ... - * - * X:XCellFragment - * C - * - * resolveIncompleteCellFragment handles such cases, after processVars is done. - * An idea is to fix invalid cell fragment terms. For example, processVars translates the term `foo(X)` into: - * foo(-fragment A B -fragment C) - * then resolveIncompleteCellFragment fixes the term as: - * foo(-fragment A B C -fragment) - * - * Another example: - * When a cell fragment term consists of only individual cells, processVars does nothing. - * rule run => foo(B C) ... - * - * _ - * B:BCell - * C:CCell - * - * In this case, resolveIncompleteCellFragment fixes the term `foo(B C)` into: - * foo(-fragment noACell B C -fragment) - * - * Another example: - * When a cell fragment variable appears only in a normal term, but never in cells, processVars does nothing. - * rule foo( _ X:XCellFragment) => bar( 2 X) - * In this case, the cell fragment term in LHS is temporarily decorated by dummy cell label, before processVars, - * which triggers processVars to split the cell fragment variable. Then resolveIncompleteCellFragment comes in. - * After resolveIncompleteCellFragment, the dummy cell label is replaced by the corresponding cell fragment label. - * The pre-/post-processing is done by preprocess/prostprocess. - * - * Potential issue: - * In the above example, if X has no sort casting, it is considered as XCellFragment by default, - * which may be different with an user's intention. - * - * Another potential issue: - * Currently, one cannot represent a fully filled cell fragment term. e.g., - * rule foo(X) => .K ... - * _ => X - * The above rule replaces the entire cell with the cell fragment term X, but doesn't have a side condition - * saying that X should be fully decorated. It means that X is given by a partially decorated cell fragment, - * cell becomes invalid. e.g., - * foo(-fragment 1 noBCell noCCell -fragment) - * results in an invalid cell: - * 1 noBCell noCCell - * Note that this issue is not specifically about resolveIncompleteCellFragment. - */ - private K resolveIncompleteCellFragment(K term) { - return new TransformK() { - @Override - public K apply(KApply k0) { - if (!hasCells(k0)) return super.apply(k0); - - ArrayList klist0 = new ArrayList(Collections.nCopies(k0.klist().size(), null)); - for (int idx = 0; idx < k0.klist().size(); idx++) { - K item0 = k0.klist().items().get(idx); - klist0.set(idx, item0); - if (item0 instanceof KApply k) { - - // incomplete cells remain as #cells(...) after processVars - if (k.klabel().name().equals("#cells")) { - - Sort cellFragmentSort = nthArgSort(k0.klabel(), idx); - if (cellFragmentSort == null) { - throw new IllegalArgumentException("Not found " + idx + "th argument sort of " + k0.klabel()); - } - - // a cell fragment term - if (cellFragmentSort.name().endsWith("Fragment")) { - - Sort cellSort = Sort(cellFragmentSort.name().substring(0,cellFragmentSort.name().indexOf("Fragment"))); - KLabel cellLabel = cfg.cfg().getCellLabel(cellSort); - - List subcellSorts = cfg.getChildren(cellLabel); - - /* - fix an invalid cell fragment term, e.g., - - Case 1. - from - foo(-fragment A B -fragment C) - into - foo(-fragment A B C -fragment) - - Case 2. - from - foo(B C) - into - foo(-fragment noACell B C -fragment) - */ - - // fill individual cells first, starting with empty - KApply cellFragment = null; - ArrayList klist = new ArrayList(Collections.nCopies(subcellSorts.size(), null)); - for (K item : IncompleteCellUtils.flattenCells(k)) { // #cells(#cells(x,y),z) => [x,y,z] - if (item instanceof KApply kapp) { - if (cfg.cfg().isCellLabel(kapp.klabel())) { - Sort sort = cfg.getCellSort(kapp.klabel()); - if (!subcellSorts.contains(sort)) { - throw new IllegalArgumentException("No such sub-cell " + sort + " in the cell " + cellLabel); - } - klist.set(subcellSorts.indexOf(sort), item); - } else if (kapp.klabel().name().endsWith("-fragment")) { - cellFragment = kapp; - assert cellFragment.klist().size() == subcellSorts.size(); - assert cellFragment.klabel().name().equals(cellLabel.name() + "-fragment"); - } else { - throw KEMException.compilerError("Unsupported cell fragment element.", item); - } - } else if (item instanceof KVariable var) { - VarInfo varinfo = null; - if (variables.containsKey(var)) { - varinfo = variables.get(var); - } - if (!var.att().contains(Sort.class) && varinfo != null) { - if (varinfo.var != null) - var = varinfo.var; - } - if (var.att().contains(Sort.class)) { - Sort sort = var.att().get(Sort.class); - if (cfg.cfg().isCell(sort)) { - if (!subcellSorts.contains(sort)) { - throw new IllegalArgumentException("No such sub-cell " + sort + " in the cell " + cellLabel); - } - klist.set(subcellSorts.indexOf(sort), item); - } else { - // if the variable is not explicitly sort-casted, then its sort information should be found in other places - if (varinfo != null && varinfo.remainingCells != null && varinfo.remainingCells.size() == 1) { - Sort s = Iterables.getOnlyElement(varinfo.remainingCells); - if (cfg.cfg().isCell(s)) { - if (!subcellSorts.contains(s)) { - throw new IllegalArgumentException("No such sub-cell " + s + " in the cell " + cellLabel); - } - klist.set(subcellSorts.indexOf(s), item); - continue; - } - } - throw KEMException.compilerError("Unsupported cell fragment element. Not found sort info.", item); - } - } else { - throw KEMException.compilerError("Unsupported cell fragment element. Not found sort info.", item); - } - } else { - // TODO: support when item instanceof KRewrite - throw KEMException.compilerError("Unsupported cell fragment element.", item); - } - } - - // fill remaining cells, considering a split cell fragment variable if any - for (int i = 0; i < subcellSorts.size(); i++) { - if (klist.get(i) == null) { - if (cellFragment != null) { - klist.set(i, cellFragment.klist().items().get(i)); - } else { - if (cfg.getMultiplicity(subcellSorts.get(i)) == Multiplicity.ONE) { - klist.set(i, cfg.getCellAbsentTerm(subcellSorts.get(i))); - } else { // Multiplicity.OPTIONAL || Multiplicity.STAR - klist.set(i, cfg.cfg().getUnit(subcellSorts.get(i))); - } - } - - } - } - - klist0.set(idx, KApply(KLabel(cellLabel.name() + "-fragment"), KList(klist))); - } - } + } + + /** Post-process terms after processVar */ + public synchronized Sentence postprocess(Sentence s) { + if (s instanceof RuleOrClaim) { + return postprocess((RuleOrClaim) s); + } else { + return s; + } + } + + private RuleOrClaim preprocess(RuleOrClaim rule) { + return rule.newInstance( + preprocess(rule.body()), + preprocess(rule.requires()), + preprocess(rule.ensures()), + rule.att()); + } + + private RuleOrClaim postprocess(RuleOrClaim rule) { + return rule.newInstance( + postprocess(rule.body()), + postprocess(rule.requires()), + postprocess(rule.ensures()), + rule.att()); + } + + /** + * When a cell fragment variable appears only in cell, e.g., rule foo( _ + * X:XCellFragment) => bar( 2 X) preprocess temporarily arguments the cell fragment term + * in LHS using its parent cell label, e.g., rule foo( _ X:XCellFragment ) => + * bar( 2 X) Now, the cell fragment variable X will be split by processVars. + */ + private K preprocess(K term) { + // find all of cell fragment variables + HashMap> cellFragmentVars = new HashMap<>(); + new VisitK() { + @Override + public void apply(KApply k) { + if (k.klabel().name().equals("#cells")) { + for (int i = 0; i < k.klist().size(); i++) { + K item = k.klist().items().get(i); + if (item instanceof KVariable var) { + if (var.att().contains(Sort.class)) { + Sort sort = var.att().get(Sort.class); + if (!cfg.cfg().isCell(sort)) { + if (!cellFragmentVars.containsKey(var)) { + cellFragmentVars.put(var, new HashSet<>()); + } + cellFragmentVars.get(var).add(k); + } + } else { + if (!cellFragmentVars.containsKey(var)) { + cellFragmentVars.put(var, new HashSet<>()); } + cellFragmentVars.get(var).add(k); + } } - return KApply(k0.klabel(), KList(klist0), k0.att()); + } + } else { + super.apply(k); } - }.apply(term); + } + }.apply(term); + + if (cellFragmentVars.isEmpty()) { + return term; } - /** - * Pre-process terms before processVar - */ - public synchronized Sentence preprocess(Sentence s) { - if (s instanceof RuleOrClaim) { - return preprocess((RuleOrClaim) s); - } else { - return s; + // drop cell fragment variables that appear outside cell, in non-function rules + if (!labelInfo.isFunction(term)) { + new VisitK() { + private boolean inKCell = false; + + @Override + public void apply(KApply k) { + if (k.klabel().name().equals("")) { + assert !inKCell; + inKCell = true; + super.apply(k); + inKCell = false; + } else { + super.apply(k); + } } - } - /** - * Post-process terms after processVar - */ - public synchronized Sentence postprocess(Sentence s) { - if (s instanceof RuleOrClaim) { - return postprocess((RuleOrClaim) s); - } else { - return s; + @Override + public void apply(KVariable var) { + if (!inKCell) { + cellFragmentVars.remove(var); + } } + }.apply(term); } - private RuleOrClaim preprocess(RuleOrClaim rule) { - return rule.newInstance( - preprocess(rule.body()), - preprocess(rule.requires()), - preprocess(rule.ensures()), - rule.att()); + if (cellFragmentVars.isEmpty()) { + return term; } - private RuleOrClaim postprocess(RuleOrClaim rule) { - return rule.newInstance( - postprocess(rule.body()), - postprocess(rule.requires()), - postprocess(rule.ensures()), - rule.att()); + HashSet cellFragmentVarsCell = new HashSet<>(); + for (HashSet cells : cellFragmentVars.values()) { + cellFragmentVarsCell.addAll(cells); } - /** - * When a cell fragment variable appears only in cell, e.g., - * rule foo( _ X:XCellFragment) => bar( 2 X) - * preprocess temporarily arguments the cell fragment term in LHS using its parent cell label, e.g., - * rule foo( _ X:XCellFragment ) => bar( 2 X) - * Now, the cell fragment variable X will be split by processVars. - */ - private K preprocess(K term) { - // find all of cell fragment variables - HashMap> cellFragmentVars = new HashMap<>(); - new VisitK() { - @Override - public void apply(KApply k) { - if (k.klabel().name().equals("#cells")) { - for (int i = 0; i < k.klist().size(); i++) { - K item = k.klist().items().get(i); - if (item instanceof KVariable var) { - if (var.att().contains(Sort.class)) { - Sort sort = var.att().get(Sort.class); - if (!cfg.cfg().isCell(sort)) { - if (!cellFragmentVars.containsKey(var)) { - cellFragmentVars.put(var, new HashSet<>()); - } - cellFragmentVars.get(var).add(k); - } - } else { - if (!cellFragmentVars.containsKey(var)) { - cellFragmentVars.put(var, new HashSet<>()); - } - cellFragmentVars.get(var).add(k); - } - } - } - } else { - super.apply(k); + // decorate such cell fragment terms with their parent cell label + return new TransformK() { + @Override + public K apply(KApply k0) { + if (hasCells(k0)) { + ArrayList klist0 = new ArrayList(Collections.nCopies(k0.klist().size(), null)); + for (int idx = 0; idx < k0.klist().size(); idx++) { + K item0 = k0.klist().items().get(idx); + klist0.set(idx, item0); + if (item0 instanceof KApply k) { + if (k.klabel().name().equals("#cells")) { + if (cellFragmentVarsCell.contains(k)) { + Sort cellFragmentSort = nthArgSort(k0.klabel(), idx); + if (cellFragmentSort == null) { + throw new IllegalArgumentException( + "Not found " + idx + "th argument sort of " + k0.klabel()); + } + if (cellFragmentSort.name().endsWith("Fragment")) { + Sort cellSort = + Sort( + cellFragmentSort + .name() + .substring(0, cellFragmentSort.name().indexOf("Fragment"))); + KLabel cellLabel = cfg.cfg().getCellLabel(cellSort); + klist0.set(idx, KApply(cellLabel, KList(item0), Att().add(Att.DUMMY_CELL()))); + } } + } } - }.apply(term); - - if (cellFragmentVars.isEmpty()) { - return term; - } - - // drop cell fragment variables that appear outside cell, in non-function rules - if (!labelInfo.isFunction(term)) { - new VisitK() { - private boolean inKCell = false; - @Override - public void apply(KApply k) { - if (k.klabel().name().equals("")) { - assert !inKCell; - inKCell = true; - super.apply(k); - inKCell = false; - } else { - super.apply(k); - } - } - @Override - public void apply(KVariable var) { - if (!inKCell) { - cellFragmentVars.remove(var); - } - } - }.apply(term); - } - - if (cellFragmentVars.isEmpty()) { - return term; + } + return KApply(k0.klabel(), KList(klist0), k0.att()); } + return super.apply(k0); + } - HashSet cellFragmentVarsCell = new HashSet<>(); - for (HashSet cells : cellFragmentVars.values()) { - cellFragmentVarsCell.addAll(cells); + @Override + public K apply(KRewrite k) { + K left = super.apply(k.left()); + return KRewrite(left, k.right(), k.att()); + } + }.apply(term); + } + + // remove the dummy cell decoration introduced by preprocess + private K postprocess(K term) { + return new TransformK() { + @Override + public K apply(KApply k) { + if (k.att().contains(Att.DUMMY_CELL())) { + KLabel klabel = KLabel(k.klabel().name() + "-fragment"); + return KApply(klabel, k.klist(), k.att()); } - - // decorate such cell fragment terms with their parent cell label - return new TransformK() { - @Override - public K apply(KApply k0) { - if (hasCells(k0)) { - ArrayList klist0 = new ArrayList(Collections.nCopies(k0.klist().size(), null)); - for (int idx = 0; idx < k0.klist().size(); idx++) { - K item0 = k0.klist().items().get(idx); - klist0.set(idx, item0); - if (item0 instanceof KApply k) { - if (k.klabel().name().equals("#cells")) { - if (cellFragmentVarsCell.contains(k)) { - Sort cellFragmentSort = nthArgSort(k0.klabel(), idx); - if (cellFragmentSort == null) { - throw new IllegalArgumentException("Not found " + idx + "th argument sort of " + k0.klabel()); - } - if (cellFragmentSort.name().endsWith("Fragment")) { - Sort cellSort = Sort(cellFragmentSort.name().substring(0,cellFragmentSort.name().indexOf("Fragment"))); - KLabel cellLabel = cfg.cfg().getCellLabel(cellSort); - klist0.set(idx, KApply(cellLabel, KList(item0), Att().add(Att.DUMMY_CELL()))); - } - } - } - } - } - return KApply(k0.klabel(), KList(klist0), k0.att()); - } - return super.apply(k0); - } - @Override - public K apply(KRewrite k) { - K left = super.apply(k.left()); - return KRewrite(left, k.right(), k.att()); - } - }.apply(term); - } - - // remove the dummy cell decoration introduced by preprocess - private K postprocess(K term) { - return new TransformK() { - @Override - public K apply(KApply k) { - if (k.att().contains(Att.DUMMY_CELL())) { - KLabel klabel = KLabel(k.klabel().name() + "-fragment"); - return KApply(klabel, k.klist(), k.att()); - } - return super.apply(k); - } - }.apply(term); + return super.apply(k); + } + }.apply(term); + } + + private boolean hasCells(KApply k) { + for (int i = 0; i < k.klist().size(); i++) { + K item = k.klist().items().get(i); + if (item instanceof KApply && ((KApply) item).klabel().name().equals("#cells")) { + return true; + } } - - private boolean hasCells(KApply k) { - for (int i = 0; i < k.klist().size(); i++) { - K item = k.klist().items().get(i); - if (item instanceof KApply && ((KApply) item).klabel().name().equals("#cells")) { - return true; - } - } - return false; + return false; + } + + // find nth argument sort for a given klabel + // if multiple signiture exist, then return arbitrary one of them that is not K + private Sort nthArgSort(KLabel klabel, int n) { + java.util.Set, Sort>> sigs = + mutable(JavaConversions.mapAsJavaMap(module.signatureFor()).get(klabel)); + if (sigs == null) { + throw new IllegalArgumentException("Not found signature for label: " + klabel); } - - // find nth argument sort for a given klabel - // if multiple signiture exist, then return arbitrary one of them that is not K - private Sort nthArgSort(KLabel klabel, int n) { - java.util.Set,Sort>> sigs = - mutable(JavaConversions.mapAsJavaMap(module.signatureFor()).get(klabel)); - if (sigs == null) { - throw new IllegalArgumentException("Not found signature for label: " + klabel); - } - Sort sort = null; - for (Tuple2,Sort> sig : sigs) { - List sorts = JavaConversions.seqAsJavaList(sig._1()); - if (n >= sorts.size()) continue; - sort = sorts.get(n); - if (!sort.equals(Sorts.K())) { - return sort; - } - } + Sort sort = null; + for (Tuple2, Sort> sig : sigs) { + List sorts = JavaConversions.seqAsJavaList(sig._1()); + if (n >= sorts.size()) continue; + sort = sorts.get(n); + if (!sort.equals(Sorts.K())) { return sort; + } } + return sort; + } } diff --git a/kernel/src/main/java/org/kframework/compile/SortInfo.java b/kernel/src/main/java/org/kframework/compile/SortInfo.java index 472cb2f6b7d..6632b53a5a4 100644 --- a/kernel/src/main/java/org/kframework/compile/SortInfo.java +++ b/kernel/src/main/java/org/kframework/compile/SortInfo.java @@ -1,10 +1,13 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.kore.KORE.*; + import com.google.common.collect.HashMultimap; import com.google.common.collect.Iterators; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; +import java.util.Map; import org.kframework.Collections; import org.kframework.attributes.Att; import org.kframework.definition.Module; @@ -12,47 +15,51 @@ import org.kframework.kore.KLabel; import org.kframework.kore.Sort; -import java.util.Map; - -import static org.kframework.kore.KORE.*; - -/** - * Information about sorts which is used in cell completion - */ +/** Information about sorts which is used in cell completion */ public class SortInfo { - private final Map closeOperators = Maps.newHashMap(); + private final Map closeOperators = Maps.newHashMap(); - protected void addOp(Sort sort, String label) { - closeOperators.put(sort, KLabel(label)); - } + protected void addOp(Sort sort, String label) { + closeOperators.put(sort, KLabel(label)); + } - public SortInfo() { - } + public SortInfo() {} - /** - * If s is the sort of the contents of a leaf cell, this returns the label to be used as - * the union/append operator when turning dots into an explicit variable - */ - public KLabel getCloseOperator(Sort s) { - return closeOperators.get(s); - } + /** + * If s is the sort of the contents of a leaf cell, this returns the label to be used as the + * union/append operator when turning dots into an explicit variable + */ + public KLabel getCloseOperator(Sort s) { + return closeOperators.get(s); + } - /** - * Gather sort information from a module. - * Populates the close operators only for sorts which only have a single associative production. - */ - public static SortInfo fromModule(Module module) { - Multimap joinOps = HashMultimap.create(); - Collections.stream(module.productionsFor()).forEach(x -> { - if (Collections.stream(x._2()).anyMatch(p -> p.att().contains(Att.ASSOC()) - && Collections.stream(p.items()).filter(i -> i instanceof NonTerminal).count() == 2)) { + /** + * Gather sort information from a module. Populates the close operators only for sorts which only + * have a single associative production. + */ + public static SortInfo fromModule(Module module) { + Multimap joinOps = HashMultimap.create(); + Collections.stream(module.productionsFor()) + .forEach( + x -> { + if (Collections.stream(x._2()) + .anyMatch( + p -> + p.att().contains(Att.ASSOC()) + && Collections.stream(p.items()) + .filter(i -> i instanceof NonTerminal) + .count() + == 2)) { joinOps.put(x._2().head().sort(), x._1()); - } - }); - SortInfo info = new SortInfo(); - joinOps.asMap().forEach((sort, labels) -> { - info.closeOperators.put(sort, Iterators.getNext(labels.iterator(), null)); - }); - return info; - } + } + }); + SortInfo info = new SortInfo(); + joinOps + .asMap() + .forEach( + (sort, labels) -> { + info.closeOperators.put(sort, Iterators.getNext(labels.iterator(), null)); + }); + return info; + } } diff --git a/kernel/src/main/java/org/kframework/compile/checks/CheckAnonymous.java b/kernel/src/main/java/org/kframework/compile/checks/CheckAnonymous.java index 0cf1e94e395..c6dbb67d32f 100644 --- a/kernel/src/main/java/org/kframework/compile/checks/CheckAnonymous.java +++ b/kernel/src/main/java/org/kframework/compile/checks/CheckAnonymous.java @@ -1,8 +1,13 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile.checks; +import static org.kframework.kore.KORE.*; + import com.google.common.collect.HashMultiset; import com.google.common.collect.Multiset; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; import org.kframework.attributes.Att; import org.kframework.compile.ResolveAnonVar; import org.kframework.definition.Context; @@ -19,92 +24,105 @@ import org.kframework.utils.errorsystem.KException.ExceptionType; import org.kframework.utils.errorsystem.KExceptionManager; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import static org.kframework.kore.KORE.*; - public class CheckAnonymous { - private final Set errors; - private final KExceptionManager kem; - private final Module module; + private final Set errors; + private final KExceptionManager kem; + private final Module module; - public CheckAnonymous(Set errors, Module module, KExceptionManager kem) { - this.errors = errors; - this.kem = kem; - this.module = module; - } + public CheckAnonymous(Set errors, Module module, KExceptionManager kem) { + this.errors = errors; + this.kem = kem; + this.module = module; + } - private final Multiset vars = HashMultiset.create(); - private final Map loc = new HashMap<>(); + private final Multiset vars = HashMultiset.create(); + private final Map loc = new HashMap<>(); - private void resetVars() { - vars.clear(); - loc.clear(); - } + private void resetVars() { + vars.clear(); + loc.clear(); + } - private void gatherVars(K k) { - new VisitK() { - @Override - public void apply(KVariable var) { - vars.add(var.name()); - loc.put(var.name(), var); - } + private void gatherVars(K k) { + new VisitK() { + @Override + public void apply(KVariable var) { + vars.add(var.name()); + loc.put(var.name(), var); + } - @Override - public void apply(KApply k) { - if (k.klabel() instanceof KVariable) { - apply((KVariable) k.klabel()); - } - super.apply(k); - } - - @Override - public void apply(InjectedKLabel k) { - if (k.klabel() instanceof KVariable) { - apply((KVariable) k.klabel()); - } - } - }.apply(k); - } + @Override + public void apply(KApply k) { + if (k.klabel() instanceof KVariable) { + apply((KVariable) k.klabel()); + } + super.apply(k); + } - public void check(Sentence s) { - if (s.att().getOptional(Att.LABEL()).orElse("").equals("STDIN-STREAM.stdinUnblock")) { - return; + @Override + public void apply(InjectedKLabel k) { + if (k.klabel() instanceof KVariable) { + apply((KVariable) k.klabel()); } - resetVars(); - if (s instanceof RuleOrClaim r) { - gatherVars(r.body()); - gatherVars(r.requires()); - gatherVars(r.ensures()); - } else if (s instanceof Context c) { - gatherVars(c.body()); - gatherVars(c.requires()); - } else if (s instanceof ContextAlias c) { - gatherVars(c.body()); - gatherVars(c.requires()); + } + }.apply(k); + } + + public void check(Sentence s) { + if (s.att().getOptional(Att.LABEL()).orElse("").equals("STDIN-STREAM.stdinUnblock")) { + return; + } + resetVars(); + if (s instanceof RuleOrClaim r) { + gatherVars(r.body()); + gatherVars(r.requires()); + gatherVars(r.ensures()); + } else if (s instanceof Context c) { + gatherVars(c.body()); + gatherVars(c.requires()); + } else if (s instanceof ContextAlias c) { + gatherVars(c.body()); + gatherVars(c.requires()); + } + for (Multiset.Entry entry : vars.entrySet()) { + if (entry.getCount() == 1) { + if (!(entry.getElement().startsWith("_") + || entry.getElement().startsWith("?_") + || entry.getElement().startsWith("!_") + || entry.getElement().startsWith("@_"))) { + if (s instanceof ContextAlias && entry.getElement().equals("HERE")) { + continue; + } + if ((s instanceof Context || s instanceof ContextAlias) + && entry.getElement().equals("HOLE")) { + continue; + } + if (loc.get(entry.getElement()).location().isPresent()) // ignore generated variables + kem.registerCompilerWarning( + ExceptionType.UNUSED_VAR, + errors, + "Variable '" + + entry.getElement() + + "' defined but not used. Prefix variable name with underscore if this is" + + " intentional.", + loc.get(entry.getElement())); } - for (Multiset.Entry entry : vars.entrySet()) { - if (entry.getCount() == 1) { - if (!(entry.getElement().startsWith("_") || entry.getElement().startsWith("?_") || entry.getElement().startsWith("!_") || entry.getElement().startsWith("@_"))) { - if (s instanceof ContextAlias && entry.getElement().equals("HERE")) { - continue; - } - if ((s instanceof Context || s instanceof ContextAlias) && entry.getElement().equals("HOLE")) { - continue; - } - if (loc.get(entry.getElement()).location().isPresent()) // ignore generated variables - kem.registerCompilerWarning(ExceptionType.UNUSED_VAR, errors, "Variable '" + entry.getElement() + "' defined but not used. Prefix variable name with underscore if this is intentional.", - loc.get(entry.getElement())); - } - } else if (entry.getCount() > 1) { - if ((entry.getElement().startsWith("_") || entry.getElement().startsWith("?_") || entry.getElement().startsWith("!_") || entry.getElement().startsWith("@_")) && !ResolveAnonVar.isAnonVar(KVariable(entry.getElement()))) { - errors.add(KEMException.compilerError("Variable '" + entry.getElement() + "' declared as unused, but it is used. Remove underscore from variable name if this is intentional.", - loc.get(entry.getElement()))); - } - } + } else if (entry.getCount() > 1) { + if ((entry.getElement().startsWith("_") + || entry.getElement().startsWith("?_") + || entry.getElement().startsWith("!_") + || entry.getElement().startsWith("@_")) + && !ResolveAnonVar.isAnonVar(KVariable(entry.getElement()))) { + errors.add( + KEMException.compilerError( + "Variable '" + + entry.getElement() + + "' declared as unused, but it is used. Remove underscore from variable name" + + " if this is intentional.", + loc.get(entry.getElement()))); } + } } + } } diff --git a/kernel/src/main/java/org/kframework/compile/checks/CheckAssoc.java b/kernel/src/main/java/org/kframework/compile/checks/CheckAssoc.java index eea4f51f14e..d746b7b9621 100644 --- a/kernel/src/main/java/org/kframework/compile/checks/CheckAssoc.java +++ b/kernel/src/main/java/org/kframework/compile/checks/CheckAssoc.java @@ -1,54 +1,77 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile.checks; +import java.util.Set; import org.kframework.attributes.Att; import org.kframework.definition.Module; import org.kframework.definition.Production; import org.kframework.definition.Sentence; import org.kframework.kore.Sort; import org.kframework.utils.errorsystem.KEMException; -import org.kframework.utils.errorsystem.KExceptionManager; - -import java.util.Set; /** - * Ensure that left, right, and non-assoc are only applied to productions with sorting which permits associativity. - * That is, if we have `syntax A ::= B "op" C`, then check the following: - * - if left, then A <= C - * - if right, then A <= B - * - if non-assoc, then A <= B and A <= C + * Ensure that left, right, and non-assoc are only applied to productions with sorting which permits + * associativity. That is, if we have `syntax A ::= B "op" C`, then check the following: - if left, + * then A <= C - if right, then A <= B - if non-assoc, then A <= B and A <= C */ public record CheckAssoc(Set errors, Module module) { - public void check(Sentence s) { - if (s instanceof Production p) { - if (p.arity() != 2) { - return; - } - Sort pSort = p.sort(); - Sort leftSort = p.nonterminals().head().sort(); - Sort rightSort = p.nonterminals().last().sort(); - boolean leqLeft = module.subsorts().lessThanEq(pSort, leftSort); - boolean leqRight = module.subsorts().lessThanEq(pSort, rightSort); - if (p.att().contains(Att.LEFT()) && !leqRight) { - errors.add(KEMException.compilerError(Att.LEFT() + - " attribute not permitted on non-associative production.\n" + - "Hint: The sub-sorting relation " + pSort + " <= " + rightSort + - " does not hold, so the " + Att.LEFT() + " attribute has no effect.", p)); - } - if (p.att().contains(Att.RIGHT()) && !leqLeft) { - errors.add(KEMException.compilerError(Att.RIGHT() + - " attribute not permitted on non-associative production.\n" + - "Hint: The sub-sorting relation " + pSort + " <= " + leftSort + - " does not hold, so the " + Att.RIGHT() + " attribute has no effect.", p)); - } - if (p.att().contains(Att.NON_ASSOC()) && !(leqLeft && leqRight)) { - errors.add(KEMException.compilerError(Att.NON_ASSOC() + - " attribute not permitted on non-associative production.\n" + - "Hint: One of the sub-sorting relations " + pSort + " <= " + leftSort + " or " + - pSort + " <= " + rightSort + " does not hold, so the " + Att.NON_ASSOC() + - " attribute has no effect.", p)); - } - } + public void check(Sentence s) { + if (s instanceof Production p) { + if (p.arity() != 2) { + return; + } + Sort pSort = p.sort(); + Sort leftSort = p.nonterminals().head().sort(); + Sort rightSort = p.nonterminals().last().sort(); + boolean leqLeft = module.subsorts().lessThanEq(pSort, leftSort); + boolean leqRight = module.subsorts().lessThanEq(pSort, rightSort); + if (p.att().contains(Att.LEFT()) && !leqRight) { + errors.add( + KEMException.compilerError( + Att.LEFT() + + " attribute not permitted on non-associative production.\n" + + "Hint: The sub-sorting relation " + + pSort + + " <= " + + rightSort + + " does not hold, so the " + + Att.LEFT() + + " attribute has no effect.", + p)); + } + if (p.att().contains(Att.RIGHT()) && !leqLeft) { + errors.add( + KEMException.compilerError( + Att.RIGHT() + + " attribute not permitted on non-associative production.\n" + + "Hint: The sub-sorting relation " + + pSort + + " <= " + + leftSort + + " does not hold, so the " + + Att.RIGHT() + + " attribute has no effect.", + p)); + } + if (p.att().contains(Att.NON_ASSOC()) && !(leqLeft && leqRight)) { + errors.add( + KEMException.compilerError( + Att.NON_ASSOC() + + " attribute not permitted on non-associative production.\n" + + "Hint: One of the sub-sorting relations " + + pSort + + " <= " + + leftSort + + " or " + + pSort + + " <= " + + rightSort + + " does not hold, so the " + + Att.NON_ASSOC() + + " attribute has no effect.", + p)); + } } + } } diff --git a/kernel/src/main/java/org/kframework/compile/checks/CheckAtt.java b/kernel/src/main/java/org/kframework/compile/checks/CheckAtt.java index 66ebcd5df50..92cb8ec434f 100644 --- a/kernel/src/main/java/org/kframework/compile/checks/CheckAtt.java +++ b/kernel/src/main/java/org/kframework/compile/checks/CheckAtt.java @@ -1,6 +1,12 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile.checks; +import static org.kframework.Collections.*; + +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.kframework.attributes.Att; import org.kframework.attributes.Att.Key; import org.kframework.attributes.HasLocation; @@ -16,223 +22,306 @@ import org.kframework.utils.errorsystem.KException.ExceptionType; import org.kframework.utils.errorsystem.KExceptionManager; -import java.util.Comparator; -import java.util.List; -import java.util.Set; -import java.util.regex.Pattern; -import java.util.stream.Collectors; +/** Created by dwightguth on 1/25/16. */ +public class CheckAtt { + private final scala.collection.Set macros; + private final Set errors; + private final KExceptionManager kem; + private final Module m; + private final boolean isSymbolicKast; -import static org.kframework.Collections.*; + public CheckAtt( + Set errors, KExceptionManager kem, Module m, boolean isSymbolicKast) { + this.errors = errors; + this.kem = kem; + this.m = m; + this.isSymbolicKast = isSymbolicKast; + this.macros = m.macroKLabels(); + } -/** - * Created by dwightguth on 1/25/16. - */ -public class CheckAtt { - private final scala.collection.Set macros; - private final Set errors; - private final KExceptionManager kem; - private final Module m; - private final boolean isSymbolicKast; - - public CheckAtt(Set errors, KExceptionManager kem, Module m, boolean isSymbolicKast) { - this.errors = errors; - this.kem = kem; - this.m = m; - this.isSymbolicKast = isSymbolicKast; - this.macros = m.macroKLabels(); - } - - public void checkUnrecognizedModuleAtts() { - if (!m.att().unrecognizedKeys().isEmpty()) { - errors.add(KEMException.compilerError("Unrecognized attributes on module " + m.name() + ": " + - stream(m.att().unrecognizedKeys()).map(Key::toString).sorted().collect(Collectors.toList()) + - "\nHint: User-defined groups can be added with the group(_) attribute.")); - } + public void checkUnrecognizedModuleAtts() { + if (!m.att().unrecognizedKeys().isEmpty()) { + errors.add( + KEMException.compilerError( + "Unrecognized attributes on module " + + m.name() + + ": " + + stream(m.att().unrecognizedKeys()) + .map(Key::toString) + .sorted() + .collect(Collectors.toList()) + + "\nHint: User-defined groups can be added with the group(_) attribute.")); } + } - public void check(Sentence sentence) { - checkUnrecognizedAtts(sentence); - checkRestrictedAtts(sentence); - if (sentence instanceof Rule) { - check(sentence.att(), sentence); - check((Rule) sentence); - } else if (sentence instanceof Production) { - check((Production) sentence); - } - checkLabel(sentence); + public void check(Sentence sentence) { + checkUnrecognizedAtts(sentence); + checkRestrictedAtts(sentence); + if (sentence instanceof Rule) { + check(sentence.att(), sentence); + check((Rule) sentence); + } else if (sentence instanceof Production) { + check((Production) sentence); } + checkLabel(sentence); + } - private static final Pattern whitespace = Pattern.compile("\\s"); + private static final Pattern whitespace = Pattern.compile("\\s"); - private void checkLabel(Sentence sentence) { - if (sentence.att().contains(Att.LABEL())) { - String label = sentence.att().get(Att.LABEL()); - if (label.contains("`") || whitespace.matcher(label).find()) { - errors.add(KEMException.compilerError("Rule label '" + label + "' cannot contain whitespace or backticks.", sentence)); - } + private void checkLabel(Sentence sentence) { + if (sentence.att().contains(Att.LABEL())) { + String label = sentence.att().get(Att.LABEL()); + if (label.contains("`") || whitespace.matcher(label).find()) { + errors.add( + KEMException.compilerError( + "Rule label '" + label + "' cannot contain whitespace or backticks.", sentence)); } } + } - private void checkUnrecognizedAtts(Sentence sentence) { - if (!sentence.att().unrecognizedKeys().isEmpty()) { - errors.add(KEMException.compilerError("Unrecognized attributes: " + - stream(sentence.att().unrecognizedKeys()).map(Key::toString).sorted().collect(Collectors.toList()) + - "\nHint: User-defined groups can be added with the group(_) attribute.", sentence)); - } + private void checkUnrecognizedAtts(Sentence sentence) { + if (!sentence.att().unrecognizedKeys().isEmpty()) { + errors.add( + KEMException.compilerError( + "Unrecognized attributes: " + + stream(sentence.att().unrecognizedKeys()) + .map(Key::toString) + .sorted() + .collect(Collectors.toList()) + + "\nHint: User-defined groups can be added with the group(_) attribute.", + sentence)); } + } - private void checkRestrictedAtts(Sentence sentence) { - Class cls = sentence.getClass(); - Att att = sentence.att(); - Set keys = stream(att.att().keySet()).map(k -> k._1()).collect(Collectors.toSet()); - keys.removeIf(k -> k.allowedSentences().exists(c -> c.isAssignableFrom(cls))); - if (!keys.isEmpty()) { - List sortedKeys = keys.stream().map(k -> k.toString()).sorted().collect(Collectors.toList()); - errors.add(KEMException.compilerError(cls.getSimpleName() + " sentences can not have the following attributes: " + sortedKeys, sentence)); - } + private void checkRestrictedAtts(Sentence sentence) { + Class cls = sentence.getClass(); + Att att = sentence.att(); + Set keys = stream(att.att().keySet()).map(k -> k._1()).collect(Collectors.toSet()); + keys.removeIf(k -> k.allowedSentences().exists(c -> c.isAssignableFrom(cls))); + if (!keys.isEmpty()) { + List sortedKeys = + keys.stream().map(k -> k.toString()).sorted().collect(Collectors.toList()); + errors.add( + KEMException.compilerError( + cls.getSimpleName() + + " sentences can not have the following attributes: " + + sortedKeys, + sentence)); } + } - private void check(Production prod) { - if (!prod.sort().equals(Sorts.KItem())) { - Att sortAtt = m.sortAttributesFor().getOrElse(prod.sort().head(), () -> Att.empty()); - if (sortAtt.contains(Att.HOOK()) && !sortAtt.get(Att.HOOK()).equals("ARRAY.Array") && !(sortAtt.get(Att.HOOK()).equals("KVAR.KVar") && isSymbolicKast)) { - if (!prod.att().contains(Att.FUNCTION()) && !prod.att().contains(Att.BRACKET()) && - !prod.att().contains(Att.TOKEN()) && !prod.att().contains(Att.MACRO()) && !(prod.klabel().isDefined() && macros.contains(prod.klabel().get()))) { - if (!(prod.sort().equals(Sorts.K()) && ((prod.klabel().isDefined() && (prod.klabel().get().name().equals("#EmptyK") || prod.klabel().get().name().equals("#KSequence"))) || prod.isSubsort()))) { - if (!(sortAtt.contains(Att.CELL_COLLECTION()) && prod.isSubsort())) { - errors.add(KEMException.compilerError("Cannot add new constructors to hooked sort " + prod.sort(), prod)); - } - } - } - } - } - if (prod.att().contains(Att.BINDER()) && !isSymbolicKast) { - if (!prod.att().get(Att.BINDER()).equals("")) { - errors.add(KEMException.compilerError("Attribute value for 'binder' attribute is not supported.", prod)); + private void check(Production prod) { + if (!prod.sort().equals(Sorts.KItem())) { + Att sortAtt = m.sortAttributesFor().getOrElse(prod.sort().head(), () -> Att.empty()); + if (sortAtt.contains(Att.HOOK()) + && !sortAtt.get(Att.HOOK()).equals("ARRAY.Array") + && !(sortAtt.get(Att.HOOK()).equals("KVAR.KVar") && isSymbolicKast)) { + if (!prod.att().contains(Att.FUNCTION()) + && !prod.att().contains(Att.BRACKET()) + && !prod.att().contains(Att.TOKEN()) + && !prod.att().contains(Att.MACRO()) + && !(prod.klabel().isDefined() && macros.contains(prod.klabel().get()))) { + if (!(prod.sort().equals(Sorts.K()) + && ((prod.klabel().isDefined() + && (prod.klabel().get().name().equals("#EmptyK") + || prod.klabel().get().name().equals("#KSequence"))) + || prod.isSubsort()))) { + if (!(sortAtt.contains(Att.CELL_COLLECTION()) && prod.isSubsort())) { + errors.add( + KEMException.compilerError( + "Cannot add new constructors to hooked sort " + prod.sort(), prod)); } - if (prod.nonterminals().size() < 2) { - errors.add(KEMException.compilerError("Binder productions must have at least two nonterminals.", prod)); - } else if (!m.sortAttributesFor().get(prod.nonterminals().apply(0).sort().head()).getOrElse(() -> Att.empty()).getOptional(Att.HOOK()).orElse("").equals("KVAR.KVar")) { - errors.add(KEMException.compilerError("First child of binder must have a sort with the 'KVAR.KVar' hook attribute.", prod)); - } - } - boolean hasColors = false; - int ncolors = 0; - if (prod.att().contains(Att.COLORS())) { - hasColors = true; - ncolors = prod.att().get(Att.COLORS()).split(",").length; + } } - int nterminals = prod.items().size() - prod.nonterminals().size(); - int nescapes = 0; - if (prod.att().contains(Att.FORMAT())) { - String format = prod.att().get(Att.FORMAT()); - for (int i = 0; i < format.length(); i++) { - char c = format.charAt(i); - if (c == '%') { - if (i == format.length() - 1) { - errors.add(KEMException.compilerError("Invalid format attribute: unfinished escape sequence.", prod)); - break; - } - char c2 = format.charAt(i + 1); - i++; - switch(c2) { - case 'n': - case 'i': - case 'd': - case 'r': - break; - case 'c': - nescapes++; - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - StringBuilder sb = new StringBuilder(); - for (; i < format.length() && format.charAt(i) >= '0' && format.charAt(i) <= '9'; i++) { - sb.append(format.charAt(i)); - } - i--; - int idx = Integer.parseInt(sb.toString()); - if (idx == 0 || idx > prod.items().size()) { - errors.add(KEMException.compilerError("Invalid format escape sequence '%" + sb + "'. Expected a number between 1 and " + prod.items().size(), prod)); - } else { - ProductionItem pi = prod.items().apply(idx-1); - if (pi instanceof RegexTerminal) { - errors.add(KEMException.compilerError("Invalid format escape sequence referring to regular expression terminal '" + pi + "'.", prod)); - } - } - break; - } - } - } - } else if (!prod.att().contains(Att.TOKEN()) && !prod.sort().equals(Sorts.Layout()) && !prod.sort().equals(Sorts.LineMarker())) { - for (ProductionItem pi : iterable(prod.items())) { + } + } + if (prod.att().contains(Att.BINDER()) && !isSymbolicKast) { + if (!prod.att().get(Att.BINDER()).equals("")) { + errors.add( + KEMException.compilerError( + "Attribute value for 'binder' attribute is not supported.", prod)); + } + if (prod.nonterminals().size() < 2) { + errors.add( + KEMException.compilerError( + "Binder productions must have at least two nonterminals.", prod)); + } else if (!m.sortAttributesFor() + .get(prod.nonterminals().apply(0).sort().head()) + .getOrElse(() -> Att.empty()) + .getOptional(Att.HOOK()) + .orElse("") + .equals("KVAR.KVar")) { + errors.add( + KEMException.compilerError( + "First child of binder must have a sort with the 'KVAR.KVar' hook attribute.", + prod)); + } + } + boolean hasColors = false; + int ncolors = 0; + if (prod.att().contains(Att.COLORS())) { + hasColors = true; + ncolors = prod.att().get(Att.COLORS()).split(",").length; + } + int nterminals = prod.items().size() - prod.nonterminals().size(); + int nescapes = 0; + if (prod.att().contains(Att.FORMAT())) { + String format = prod.att().get(Att.FORMAT()); + for (int i = 0; i < format.length(); i++) { + char c = format.charAt(i); + if (c == '%') { + if (i == format.length() - 1) { + errors.add( + KEMException.compilerError( + "Invalid format attribute: unfinished escape sequence.", prod)); + break; + } + char c2 = format.charAt(i + 1); + i++; + switch (c2) { + case 'n': + case 'i': + case 'd': + case 'r': + break; + case 'c': + nescapes++; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + StringBuilder sb = new StringBuilder(); + for (; + i < format.length() && format.charAt(i) >= '0' && format.charAt(i) <= '9'; + i++) { + sb.append(format.charAt(i)); + } + i--; + int idx = Integer.parseInt(sb.toString()); + if (idx == 0 || idx > prod.items().size()) { + errors.add( + KEMException.compilerError( + "Invalid format escape sequence '%" + + sb + + "'. Expected a number between 1 and " + + prod.items().size(), + prod)); + } else { + ProductionItem pi = prod.items().apply(idx - 1); if (pi instanceof RegexTerminal) { - if (prod.items().size() == 1) { - errors.add(KEMException.compilerError( - "Expected format attribute on production with regular expression terminal. Did you forget the 'token' attribute?", prod)); - } else { - errors.add(KEMException.compilerError( - "Expected format attribute on production with regular expression terminal.", prod)); - } + errors.add( + KEMException.compilerError( + "Invalid format escape sequence referring to regular expression terminal" + + " '" + + pi + + "'.", + prod)); } - } - } - if (hasColors && nescapes + nterminals != ncolors) { - errors.add(KEMException.compilerError("Invalid colors attribute: expected " + (nescapes + nterminals) + " colors, found " + ncolors + " colors instead.", prod)); + } + break; + } } - if (prod.att().contains(Att.FUNCTIONAL())) { - kem.registerCompilerWarning(ExceptionType.FUTURE_ERROR, errors, - "The attribute 'functional' has been deprecated on symbols. Use the combination of attributes 'function' and 'total' instead.", prod); - } - if (prod.att().contains(Att.TOTAL()) && !prod.att().contains(Att.FUNCTION())) { - errors.add(KEMException.compilerError( - "The attribute 'total' cannot be applied to a production which does not have the 'function' attribute.", prod)); + } + } else if (!prod.att().contains(Att.TOKEN()) + && !prod.sort().equals(Sorts.Layout()) + && !prod.sort().equals(Sorts.LineMarker())) { + for (ProductionItem pi : iterable(prod.items())) { + if (pi instanceof RegexTerminal) { + if (prod.items().size() == 1) { + errors.add( + KEMException.compilerError( + "Expected format attribute on production with regular expression terminal. Did" + + " you forget the 'token' attribute?", + prod)); + } else { + errors.add( + KEMException.compilerError( + "Expected format attribute on production with regular expression terminal.", + prod)); + } } + } } + if (hasColors && nescapes + nterminals != ncolors) { + errors.add( + KEMException.compilerError( + "Invalid colors attribute: expected " + + (nescapes + nterminals) + + " colors, found " + + ncolors + + " colors instead.", + prod)); + } + if (prod.att().contains(Att.FUNCTIONAL())) { + kem.registerCompilerWarning( + ExceptionType.FUTURE_ERROR, + errors, + "The attribute 'functional' has been deprecated on symbols. Use the combination of" + + " attributes 'function' and 'total' instead.", + prod); + } + if (prod.att().contains(Att.TOTAL()) && !prod.att().contains(Att.FUNCTION())) { + errors.add( + KEMException.compilerError( + "The attribute 'total' cannot be applied to a production which does not have the" + + " 'function' attribute.", + prod)); + } + } - private void check(Rule rule) { - if (rule.isMacro()) { - kem.registerCompilerWarning(ExceptionType.FUTURE_ERROR, errors, - "The attribute [" + rule.att().getMacro().get() + "] has been deprecated on rules. Use this label on syntax declarations instead.", rule); - } - - checkNonExecutable(rule); + private void check(Rule rule) { + if (rule.isMacro()) { + kem.registerCompilerWarning( + ExceptionType.FUTURE_ERROR, + errors, + "The attribute [" + + rule.att().getMacro().get() + + "] has been deprecated on rules. Use this label on syntax declarations instead.", + rule); } - private void checkNonExecutable(Rule rule) { - boolean isNonExecutable = rule.att().contains(Att.NON_EXECUTABLE()); - boolean isFunction = m.attributesFor() - .getOrElse(m.matchKLabel(rule), Att::empty) - .contains(Att.FUNCTION()); + checkNonExecutable(rule); + } - if(isNonExecutable && !isFunction) { - errors.add( - KEMException.compilerError( - "non-executable attribute is only supported on function rules.", - rule)); - } + private void checkNonExecutable(Rule rule) { + boolean isNonExecutable = rule.att().contains(Att.NON_EXECUTABLE()); + boolean isFunction = + m.attributesFor().getOrElse(m.matchKLabel(rule), Att::empty).contains(Att.FUNCTION()); + + if (isNonExecutable && !isFunction) { + errors.add( + KEMException.compilerError( + "non-executable attribute is only supported on function rules.", rule)); } + } - private void check(Att att, HasLocation loc) { - if (att.contains(Att.OWISE()) && att.contains(Att.SIMPLIFICATION())) { - errors.add(KEMException.compilerError("owise attribute is not supported on simplification rules.", loc)); - } - if (att.contains(Att.PRIORITY()) && att.contains(Att.SIMPLIFICATION())) { - errors.add(KEMException.compilerError("priority attribute is not supported on simplification rules.", loc)); - } - if(att.contains(Att.ANYWHERE()) && att.contains(Att.SIMPLIFICATION())) { - errors.add(KEMException.compilerError("anywhere attribute is not supported on simplification rules.", loc)); - } - if(att.contains(Att.ANYWHERE()) && att.contains(Att.SYMBOLIC())) { - errors.add(KEMException.compilerError("anywhere attribute is not supported on symbolic rules.", loc)); - } + private void check(Att att, HasLocation loc) { + if (att.contains(Att.OWISE()) && att.contains(Att.SIMPLIFICATION())) { + errors.add( + KEMException.compilerError( + "owise attribute is not supported on simplification rules.", loc)); + } + if (att.contains(Att.PRIORITY()) && att.contains(Att.SIMPLIFICATION())) { + errors.add( + KEMException.compilerError( + "priority attribute is not supported on simplification rules.", loc)); + } + if (att.contains(Att.ANYWHERE()) && att.contains(Att.SIMPLIFICATION())) { + errors.add( + KEMException.compilerError( + "anywhere attribute is not supported on simplification rules.", loc)); + } + if (att.contains(Att.ANYWHERE()) && att.contains(Att.SYMBOLIC())) { + errors.add( + KEMException.compilerError( + "anywhere attribute is not supported on symbolic rules.", loc)); } + } } diff --git a/kernel/src/main/java/org/kframework/compile/checks/CheckBracket.java b/kernel/src/main/java/org/kframework/compile/checks/CheckBracket.java index 9b826a49aa2..c2c0864ee15 100644 --- a/kernel/src/main/java/org/kframework/compile/checks/CheckBracket.java +++ b/kernel/src/main/java/org/kframework/compile/checks/CheckBracket.java @@ -1,6 +1,8 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile.checks; +import java.util.List; +import java.util.stream.Collectors; import org.kframework.attributes.Att; import org.kframework.kil.Module; import org.kframework.kil.ModuleItem; @@ -11,26 +13,31 @@ import org.kframework.kil.Syntax; import org.kframework.utils.errorsystem.KEMException; -import java.util.List; -import java.util.stream.Collectors; - public class CheckBracket { - public static void check(Module m) { - m.getItems().forEach(CheckBracket::check); // i -> check(i) - } + public static void check(Module m) { + m.getItems().forEach(CheckBracket::check); // i -> check(i) + } - private static void check(ModuleItem i) { - if (i instanceof Syntax s) { - for (PriorityBlock b : s.getPriorityBlocks()) { - for (Production p : b.getProductions()) { - if (p.containsAttribute(Att.BRACKET())) { - List nts = p.getItems().stream().filter(x -> x instanceof NonTerminal).collect(Collectors.toList()); - if (nts.size() != 1 || !((NonTerminal) nts.get(0)).getSort().equals(s.getDeclaredSort().getSort())) - throw KEMException.outerParserError("bracket productions should have exactly one non-terminal of the same sort as the production.", p.getSource(), p.getLocation()); - } - } - } + private static void check(ModuleItem i) { + if (i instanceof Syntax s) { + for (PriorityBlock b : s.getPriorityBlocks()) { + for (Production p : b.getProductions()) { + if (p.containsAttribute(Att.BRACKET())) { + List nts = + p.getItems().stream() + .filter(x -> x instanceof NonTerminal) + .collect(Collectors.toList()); + if (nts.size() != 1 + || !((NonTerminal) nts.get(0)).getSort().equals(s.getDeclaredSort().getSort())) + throw KEMException.outerParserError( + "bracket productions should have exactly one non-terminal of the same sort as the" + + " production.", + p.getSource(), + p.getLocation()); + } } + } } + } } diff --git a/kernel/src/main/java/org/kframework/compile/checks/CheckConfigurationCells.java b/kernel/src/main/java/org/kframework/compile/checks/CheckConfigurationCells.java index e3700ad27a1..c07eb2197fe 100644 --- a/kernel/src/main/java/org/kframework/compile/checks/CheckConfigurationCells.java +++ b/kernel/src/main/java/org/kframework/compile/checks/CheckConfigurationCells.java @@ -1,6 +1,10 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile.checks; +import static org.kframework.Collections.*; + +import java.util.HashSet; +import java.util.Set; import org.kframework.attributes.Att; import org.kframework.compile.ConfigurationInfoFromModule; import org.kframework.definition.Module; @@ -11,56 +15,61 @@ import org.kframework.kore.Sort; import org.kframework.utils.errorsystem.KEMException; -import java.util.HashSet; -import java.util.Set; - -import static org.kframework.Collections.*; - -/** - * Checks that no two cells with the same name are declared in the configuration. - */ +/** Checks that no two cells with the same name are declared in the configuration. */ public class CheckConfigurationCells { - private final Set errors; + private final Set errors; - private final Module module; + private final Module module; - private final boolean isSymbolicKast; + private final boolean isSymbolicKast; - public CheckConfigurationCells(Set errors, Module module, boolean isSymbolicKast) { - this.errors = errors; - this.module = module; - this.isSymbolicKast = isSymbolicKast; - } + public CheckConfigurationCells(Set errors, Module module, boolean isSymbolicKast) { + this.errors = errors; + this.module = module; + this.isSymbolicKast = isSymbolicKast; + } - public void check(Sentence s) { - if (s instanceof Production) { - check((Production) s); - } + public void check(Sentence s) { + if (s instanceof Production) { + check((Production) s); } + } - private final Set cells = new HashSet<>(); + private final Set cells = new HashSet<>(); - private void check(Production p) { - if (p.att().contains(Att.CELL())) { - for (ProductionItem i : mutable(p.items())) { - if (i instanceof NonTerminal) { - Sort sort = ((NonTerminal) i).sort(); - if (sort.name().endsWith("Cell")) { - if (cells.contains(sort)) { - Production cell = new ConfigurationInfoFromModule(module).cellProductionsFor().get(sort).get().head(); - errors.add(KEMException.compilerError("Cell " + cell.klabel().get() + " found twice in configuration.", p)); - } - cells.add(sort); - } - } - } - if (p.att().getOptional(Att.MULTIPLICITY()).orElse("").equals("*") && p.att().getOptional(Att.TYPE()).orElse("Bag").equals("Bag")) { - if (!isSymbolicKast) { - errors.add(KEMException.compilerError("Cell bags are only supported on the Java backend. If you want " - + "this feature, comment on https://github.com/runtimeverification/k/issues/1419 . As a workaround, you can add the attribute " - + "type=\"Set\" and add a unique identifier to each element in the set.", p)); - } + private void check(Production p) { + if (p.att().contains(Att.CELL())) { + for (ProductionItem i : mutable(p.items())) { + if (i instanceof NonTerminal) { + Sort sort = ((NonTerminal) i).sort(); + if (sort.name().endsWith("Cell")) { + if (cells.contains(sort)) { + Production cell = + new ConfigurationInfoFromModule(module) + .cellProductionsFor() + .get(sort) + .get() + .head(); + errors.add( + KEMException.compilerError( + "Cell " + cell.klabel().get() + " found twice in configuration.", p)); } + cells.add(sort); + } + } + } + if (p.att().getOptional(Att.MULTIPLICITY()).orElse("").equals("*") + && p.att().getOptional(Att.TYPE()).orElse("Bag").equals("Bag")) { + if (!isSymbolicKast) { + errors.add( + KEMException.compilerError( + "Cell bags are only supported on the Java backend. If you want this feature," + + " comment on https://github.com/runtimeverification/k/issues/1419 . As a" + + " workaround, you can add the attribute type=\"Set\" and add a unique" + + " identifier to each element in the set.", + p)); } + } } + } } diff --git a/kernel/src/main/java/org/kframework/compile/checks/CheckFunctions.java b/kernel/src/main/java/org/kframework/compile/checks/CheckFunctions.java index 8c727c51037..ce019ff0fac 100644 --- a/kernel/src/main/java/org/kframework/compile/checks/CheckFunctions.java +++ b/kernel/src/main/java/org/kframework/compile/checks/CheckFunctions.java @@ -1,6 +1,7 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile.checks; +import java.util.Set; import org.kframework.attributes.Att; import org.kframework.compile.RewriteAwareVisitor; import org.kframework.definition.Claim; @@ -14,101 +15,158 @@ import org.kframework.kore.KVariable; import org.kframework.utils.errorsystem.KEMException; -import java.util.Set; - -/** - * Check that functions are not used on LHS in places that should be performing matching. - */ +/** Check that functions are not used on LHS in places that should be performing matching. */ public record CheckFunctions(Set errors, Module m) { - public void check(Sentence sentence) { - if (sentence instanceof Rule rl) { - checkFuncAtt(rl); - if (!rl.att().contains(Att.SIMPLIFICATION())) - // functions are allowed on the LHS of simplification rules - check(rl.body()); - } else if (sentence instanceof Claim c) { - // functions are allowed on LHS of claims - if (c.att().contains(Att.MACRO()) || c.att().contains(Att.MACRO_REC()) - || c.att().contains(Att.ALIAS()) || c.att().contains(Att.ALIAS_REC())) - errors.add(KEMException.compilerError( - "Attributes " + Att.MACRO() + "|" + Att.MACRO_REC() + "|" - + Att.ALIAS() + "|" + Att.ALIAS_REC() + " are not allowed on claims.", c)); - } else if (sentence instanceof Context ctx) { - check(ctx.body()); - if (ctx.att().contains(Att.MACRO()) || ctx.att().contains(Att.MACRO_REC()) - || ctx.att().contains(Att.ALIAS()) || ctx.att().contains(Att.ALIAS_REC())) - errors.add(KEMException.compilerError( - "Attributes " + Att.MACRO() + "|" + Att.MACRO_REC() + "|" - + Att.ALIAS() + "|" + Att.ALIAS_REC() + " are not allowed on contexts.", ctx)); - } else if (sentence instanceof ContextAlias ctx) { - check(ctx.body()); - if (ctx.att().contains(Att.MACRO()) || ctx.att().contains(Att.MACRO_REC()) - || ctx.att().contains(Att.ALIAS()) || ctx.att().contains(Att.ALIAS_REC())) - errors.add(KEMException.compilerError( - "Attributes " + Att.MACRO() + "|" + Att.MACRO_REC() + "|" - + Att.ALIAS() + "|" + Att.ALIAS_REC() + " are not allowed on contexts.", ctx)); - } + public void check(Sentence sentence) { + if (sentence instanceof Rule rl) { + checkFuncAtt(rl); + if (!rl.att().contains(Att.SIMPLIFICATION())) + // functions are allowed on the LHS of simplification rules + check(rl.body()); + } else if (sentence instanceof Claim c) { + // functions are allowed on LHS of claims + if (c.att().contains(Att.MACRO()) + || c.att().contains(Att.MACRO_REC()) + || c.att().contains(Att.ALIAS()) + || c.att().contains(Att.ALIAS_REC())) + errors.add( + KEMException.compilerError( + "Attributes " + + Att.MACRO() + + "|" + + Att.MACRO_REC() + + "|" + + Att.ALIAS() + + "|" + + Att.ALIAS_REC() + + " are not allowed on claims.", + c)); + } else if (sentence instanceof Context ctx) { + check(ctx.body()); + if (ctx.att().contains(Att.MACRO()) + || ctx.att().contains(Att.MACRO_REC()) + || ctx.att().contains(Att.ALIAS()) + || ctx.att().contains(Att.ALIAS_REC())) + errors.add( + KEMException.compilerError( + "Attributes " + + Att.MACRO() + + "|" + + Att.MACRO_REC() + + "|" + + Att.ALIAS() + + "|" + + Att.ALIAS_REC() + + " are not allowed on contexts.", + ctx)); + } else if (sentence instanceof ContextAlias ctx) { + check(ctx.body()); + if (ctx.att().contains(Att.MACRO()) + || ctx.att().contains(Att.MACRO_REC()) + || ctx.att().contains(Att.ALIAS()) + || ctx.att().contains(Att.ALIAS_REC())) + errors.add( + KEMException.compilerError( + "Attributes " + + Att.MACRO() + + "|" + + Att.MACRO_REC() + + "|" + + Att.ALIAS() + + "|" + + Att.ALIAS_REC() + + " are not allowed on contexts.", + ctx)); } + } - public void check(K body) { - new RewriteAwareVisitor(true, errors) { - boolean atTop = true; - @Override - public void apply(KApply k) { - if (k.klabel().name().equals("#withConfig")) { - super.apply(k); - return; - } - if (k.klabel() instanceof KVariable || CheckKLabels.isInternalKLabel(k.klabel().name(), m) || !m.attributesFor().contains(k.klabel()) ) { - atTop = false; - super.apply(k); - return; - } - Att attributes = m.attributesFor().apply(k.klabel()); - String hook = attributes.getOptional(Att.HOOK()).orElse(""); - if (attributes.contains(Att.FUNCTION()) - && isLHS() - && !atTop - && !(hook.equals("LIST.element") || hook.equals("LIST.concat") || hook.equals("LIST.unit") - || hook.equals("SET.element") || hook.equals("SET.concat") || hook.equals("SET.unit") - || hook.equals("MAP.element") || hook.equals("MAP.concat") || hook.equals("MAP.unit") - || hook.equals("BAG.element") || hook.equals("BAG.concat") || hook.equals("BAG.unit"))) { - errors.add(KEMException.compilerError("Illegal function symbol " + k.klabel().name() + " on LHS of rule." + - " Consider adding `simplification` attribute to the rule if this is intended.", k)); - } - atTop = false; - if (hook.equals("SET.element")) return; - if (hook.equals("MAP.element")) { - apply(k.items().get(1)); - return; - } - super.apply(k); - } - }.apply(body); - } + public void check(K body) { + new RewriteAwareVisitor(true, errors) { + boolean atTop = true; - public void checkFuncAtt(Rule r) { - new RewriteAwareVisitor(true, errors) { - @Override - public void apply(KApply k) { - if (k.klabel().name().equals("#withConfig")) { - super.apply(k); - return; - } - if ((isRHS() && !isLHS()) || k.klabel() instanceof KVariable || !m.attributesFor().contains(k.klabel())) { - return; - } - Att attributes = m.attributesFor().apply(k.klabel()); - if (attributes.contains(Att.FUNCTION()) && (r.att().contains(Att.MACRO()) - || r.att().contains(Att.MACRO_REC()) - || r.att().contains(Att.ALIAS()) - || r.att().contains(Att.ALIAS_REC()))) { - errors.add(KEMException.compilerError( - "Attributes " + Att.MACRO() + "|" + Att.MACRO_REC() + "|" - + Att.ALIAS() + "|" + Att.ALIAS_REC() + " are not allowed on function rules.", r)); - } - } - }.apply(r.body()); - } + @Override + public void apply(KApply k) { + if (k.klabel().name().equals("#withConfig")) { + super.apply(k); + return; + } + if (k.klabel() instanceof KVariable + || CheckKLabels.isInternalKLabel(k.klabel().name(), m) + || !m.attributesFor().contains(k.klabel())) { + atTop = false; + super.apply(k); + return; + } + Att attributes = m.attributesFor().apply(k.klabel()); + String hook = attributes.getOptional(Att.HOOK()).orElse(""); + if (attributes.contains(Att.FUNCTION()) + && isLHS() + && !atTop + && !(hook.equals("LIST.element") + || hook.equals("LIST.concat") + || hook.equals("LIST.unit") + || hook.equals("SET.element") + || hook.equals("SET.concat") + || hook.equals("SET.unit") + || hook.equals("MAP.element") + || hook.equals("MAP.concat") + || hook.equals("MAP.unit") + || hook.equals("BAG.element") + || hook.equals("BAG.concat") + || hook.equals("BAG.unit"))) { + errors.add( + KEMException.compilerError( + "Illegal function symbol " + + k.klabel().name() + + " on LHS of rule. Consider adding `simplification` attribute to the rule if" + + " this is intended.", + k)); + } + atTop = false; + if (hook.equals("SET.element")) return; + if (hook.equals("MAP.element")) { + apply(k.items().get(1)); + return; + } + super.apply(k); + } + }.apply(body); + } + + public void checkFuncAtt(Rule r) { + new RewriteAwareVisitor(true, errors) { + @Override + public void apply(KApply k) { + if (k.klabel().name().equals("#withConfig")) { + super.apply(k); + return; + } + if ((isRHS() && !isLHS()) + || k.klabel() instanceof KVariable + || !m.attributesFor().contains(k.klabel())) { + return; + } + Att attributes = m.attributesFor().apply(k.klabel()); + if (attributes.contains(Att.FUNCTION()) + && (r.att().contains(Att.MACRO()) + || r.att().contains(Att.MACRO_REC()) + || r.att().contains(Att.ALIAS()) + || r.att().contains(Att.ALIAS_REC()))) { + errors.add( + KEMException.compilerError( + "Attributes " + + Att.MACRO() + + "|" + + Att.MACRO_REC() + + "|" + + Att.ALIAS() + + "|" + + Att.ALIAS_REC() + + " are not allowed on function rules.", + r)); + } + } + }.apply(r.body()); + } } diff --git a/kernel/src/main/java/org/kframework/compile/checks/CheckHOLE.java b/kernel/src/main/java/org/kframework/compile/checks/CheckHOLE.java index ca443f43708..8739d609542 100644 --- a/kernel/src/main/java/org/kframework/compile/checks/CheckHOLE.java +++ b/kernel/src/main/java/org/kframework/compile/checks/CheckHOLE.java @@ -1,6 +1,11 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile.checks; +import static org.kframework.kore.KORE.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; import org.kframework.attributes.Att; import org.kframework.builtin.Sorts; import org.kframework.compile.ResolveStrict; @@ -8,84 +13,85 @@ import org.kframework.definition.Module; import org.kframework.definition.Production; import org.kframework.definition.Sentence; -import org.kframework.kore.K; import org.kframework.kore.KApply; import org.kframework.kore.KVariable; import org.kframework.kore.VisitK; import org.kframework.utils.errorsystem.KEMException; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import static org.kframework.kore.KORE.*; - public record CheckHOLE(Set errors, Module m) { - public void check(Sentence sentence) { - if (sentence instanceof Production) { - check((Production) sentence); - } else if (sentence instanceof Context) { - check((Context) sentence); - } + public void check(Sentence sentence) { + if (sentence instanceof Production) { + check((Production) sentence); + } else if (sentence instanceof Context) { + check((Context) sentence); } + } - private void check(Production prod) { - if (prod.att().contains(Att.STRICT())) { - check(prod, prod.att().get(Att.STRICT())); - } - if (prod.att().contains(Att.SEQSTRICT())) { - check(prod, prod.att().get(Att.SEQSTRICT())); - } + private void check(Production prod) { + if (prod.att().contains(Att.STRICT())) { + check(prod, prod.att().get(Att.STRICT())); } + if (prod.att().contains(Att.SEQSTRICT())) { + check(prod, prod.att().get(Att.SEQSTRICT())); + } + } - private void check(Production prod, String att) { - long arity = prod.nonterminals().size(); - List strictnessPositions = new ArrayList<>(); - if (att.isEmpty()) { + private void check(Production prod, String att) { + long arity = prod.nonterminals().size(); + List strictnessPositions = new ArrayList<>(); + if (att.isEmpty()) { + for (int i = 1; i <= arity; i++) { + strictnessPositions.add(i); + } + } else { + try { + String[] components = att.split(";"); + if (components.length == 1) { + if (Character.isDigit(components[0].trim().charAt(0))) { + ResolveStrict.setPositions(components[0].trim(), strictnessPositions, arity, prod); + } else { for (int i = 1; i <= arity; i++) { - strictnessPositions.add(i); + strictnessPositions.add(i); } + } + } else if (components.length % 2 == 0) { + for (int i = 0; i < components.length; i += 2) { + ResolveStrict.setPositions(components[i + 1].trim(), strictnessPositions, arity, prod); + } } else { - try { - String[] components = att.split(";"); - if (components.length == 1) { - if (Character.isDigit(components[0].trim().charAt(0))) { - ResolveStrict.setPositions(components[0].trim(), strictnessPositions, arity, prod); - } else { - for (int i = 1; i <= arity; i++) { - strictnessPositions.add(i); - } - } - } else if (components.length % 2 == 0) { - for (int i = 0; i < components.length; i += 2) { - ResolveStrict.setPositions(components[i+1].trim(), strictnessPositions, arity, prod); - } - } else { - errors.add(KEMException.compilerError("Invalid strict attribute containing invalid semicolons. Must contain 0, 1, 2, or an even number of components.", prod)); - } - } catch (KEMException e) { - errors.add(e); - } - } - for (int pos : strictnessPositions) { - if (prod.nonterminals().apply(pos-1).sort().equals(Sorts.K())) { - errors.add(KEMException.compilerError("Cannot heat a nonterminal of sort K. Did you mean KItem?", prod)); - } + errors.add( + KEMException.compilerError( + "Invalid strict attribute containing invalid semicolons. Must contain 0, 1, 2, or" + + " an even number of components.", + prod)); } + } catch (KEMException e) { + errors.add(e); + } + } + for (int pos : strictnessPositions) { + if (prod.nonterminals().apply(pos - 1).sort().equals(Sorts.K())) { + errors.add( + KEMException.compilerError( + "Cannot heat a nonterminal of sort K. Did you mean KItem?", prod)); + } } + } - private static final KApply K_HOLE = KApply(KLabel("#SemanticCastToK"), KVariable("HOLE")); + private static final KApply K_HOLE = KApply(KLabel("#SemanticCastToK"), KVariable("HOLE")); - private void check(Context ctx) { - new VisitK() { - @Override - public void apply(KApply k) { - if (k.equals(K_HOLE)) { - errors.add(KEMException.compilerError("Cannot heat a HOLE of sort K. Did you mean to sort it to KItem?", k)); - } - super.apply(k); - } - }.apply(ctx.body()); - } + private void check(Context ctx) { + new VisitK() { + @Override + public void apply(KApply k) { + if (k.equals(K_HOLE)) { + errors.add( + KEMException.compilerError( + "Cannot heat a HOLE of sort K. Did you mean to sort it to KItem?", k)); + } + super.apply(k); + } + }.apply(ctx.body()); + } } diff --git a/kernel/src/main/java/org/kframework/compile/checks/CheckK.java b/kernel/src/main/java/org/kframework/compile/checks/CheckK.java index 35d619a0bdf..d987845a23d 100644 --- a/kernel/src/main/java/org/kframework/compile/checks/CheckK.java +++ b/kernel/src/main/java/org/kframework/compile/checks/CheckK.java @@ -1,6 +1,7 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile.checks; +import java.util.Set; import org.kframework.definition.Context; import org.kframework.definition.ContextAlias; import org.kframework.definition.RuleOrClaim; @@ -12,42 +13,44 @@ import org.kframework.kore.VisitK; import org.kframework.utils.errorsystem.KEMException; -import java.util.Set; - public record CheckK(Set errors) { - private void check(K k) { - new VisitK() { - @Override - public void apply(KAs as) { - boolean error = false; - if (!(as.alias() instanceof KVariable)) { - error = true; - if (as.alias() instanceof KApply app) { - if (app.klabel().name().startsWith("#SemanticCastTo") && app.items().size() == 1 && app.items().get(0) instanceof KVariable) { - error = false; - } - } - } - if (error) { - errors.add(KEMException.compilerError("Found #as pattern where the right side is not a variable.", as)); - } - super.apply(as); + private void check(K k) { + new VisitK() { + @Override + public void apply(KAs as) { + boolean error = false; + if (!(as.alias() instanceof KVariable)) { + error = true; + if (as.alias() instanceof KApply app) { + if (app.klabel().name().startsWith("#SemanticCastTo") + && app.items().size() == 1 + && app.items().get(0) instanceof KVariable) { + error = false; } - }.apply(k); - } - - public void check(Sentence s) { - if (s instanceof RuleOrClaim r) { - check(r.body()); - check(r.requires()); - check(r.ensures()); - } else if (s instanceof Context c) { - check(c.body()); - check(c.requires()); - } else if (s instanceof ContextAlias c) { - check(c.body()); - check(c.requires()); + } + } + if (error) { + errors.add( + KEMException.compilerError( + "Found #as pattern where the right side is not a variable.", as)); } + super.apply(as); + } + }.apply(k); + } + + public void check(Sentence s) { + if (s instanceof RuleOrClaim r) { + check(r.body()); + check(r.requires()); + check(r.ensures()); + } else if (s instanceof Context c) { + check(c.body()); + check(c.requires()); + } else if (s instanceof ContextAlias c) { + check(c.body()); + check(c.requires()); } + } } diff --git a/kernel/src/main/java/org/kframework/compile/checks/CheckKLabels.java b/kernel/src/main/java/org/kframework/compile/checks/CheckKLabels.java index a7947a43dbc..1fb6905597b 100644 --- a/kernel/src/main/java/org/kframework/compile/checks/CheckKLabels.java +++ b/kernel/src/main/java/org/kframework/compile/checks/CheckKLabels.java @@ -1,7 +1,13 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile.checks; +import static org.kframework.Collections.*; + import com.google.common.collect.ImmutableSet; +import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; import org.apache.commons.collections4.CollectionUtils; import org.kframework.Collections; import org.kframework.attributes.Att; @@ -22,184 +28,261 @@ import org.kframework.utils.errorsystem.KException.ExceptionType; import org.kframework.utils.errorsystem.KExceptionManager; import org.kframework.utils.file.FileUtil; - -import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.stream.Collectors; import scala.Tuple2; -import static org.kframework.Collections.*; - /** - * Checks to ensure that KLabels in the definition obey rules relating to their use. First, klabels used in rules must - * be defined by a production in one of the modules imported by the module the rule is in. Second, any given klabel - * can only be defined in one module. This ensures that klabels don't mix across modules without strictly enforcing - * the requirement that all klabels be unique, or that all klabels be qualified by their module name. + * Checks to ensure that KLabels in the definition obey rules relating to their use. First, klabels + * used in rules must be defined by a production in one of the modules imported by the module the + * rule is in. Second, any given klabel can only be defined in one module. This ensures that klabels + * don't mix across modules without strictly enforcing the requirement that all klabels be unique, + * or that all klabels be qualified by their module name. */ public class CheckKLabels { - private final Set errors; - private final KExceptionManager kem; - private final FileUtil files; - private final List extraConcreteRules; - public CheckKLabels(Set errors, KExceptionManager kem, FileUtil files, List extraConcreteRules) { - this.errors = errors; - this.kem = kem; - this.files = files; - this.extraConcreteRules = extraConcreteRules; - } + private final Set errors; + private final KExceptionManager kem; + private final FileUtil files; + private final List extraConcreteRules; - private final Map klabels = new HashMap<>(); - private final Map klabelProds = new HashMap<>(); - private final Set usedLabels = new HashSet<>(); + public CheckKLabels( + Set errors, + KExceptionManager kem, + FileUtil files, + List extraConcreteRules) { + this.errors = errors; + this.kem = kem; + this.files = files; + this.extraConcreteRules = extraConcreteRules; + } - public void check(Sentence sentence, Module m) { - VisitK checkKLabels = new VisitK() { - @Override - public void apply(InjectedKLabel k) { - apply(k.klabel(), k); - super.apply(k); - } + private final Map klabels = new HashMap<>(); + private final Map klabelProds = new HashMap<>(); + private final Set usedLabels = new HashSet<>(); - @Override - public void apply(KApply k) { - apply(k.klabel(), k); - super.apply(k); - } + public void check(Sentence sentence, Module m) { + VisitK checkKLabels = + new VisitK() { + @Override + public void apply(InjectedKLabel k) { + apply(k.klabel(), k); + super.apply(k); + } - private void apply(KLabel klabel, K k) { - if (klabel instanceof KVariable) - return; - Optional s = k.att().getOptional(Source.class); - if (s.isPresent()) { - usedLabels.add(klabel.name()); - if (m.definedKLabels().apply(klabel)) { - for (Production prod : iterable(m.productionsFor().apply(klabel.head()))) { - if (prod.source().isPresent() && prod.location().isPresent()) { - usedLabels.addAll(stream(m.productionsForLoc().apply(Tuple2.apply(prod.source().get(), prod.location().get()))) - .filter(p -> p.klabel().isDefined()).map(p -> p.klabel().get().name()).collect(Collectors.toSet())); - } - } - } - } - if (!m.definedKLabels().apply(klabel) && !isInternalKLabel(klabel.name(), m)) { - errors.add(KEMException.compilerError("Found klabel " + klabel.name() + " not defined in any production.", k)); + @Override + public void apply(KApply k) { + apply(k.klabel(), k); + super.apply(k); + } + + private void apply(KLabel klabel, K k) { + if (klabel instanceof KVariable) return; + Optional s = k.att().getOptional(Source.class); + if (s.isPresent()) { + usedLabels.add(klabel.name()); + if (m.definedKLabels().apply(klabel)) { + for (Production prod : iterable(m.productionsFor().apply(klabel.head()))) { + if (prod.source().isPresent() && prod.location().isPresent()) { + usedLabels.addAll( + stream( + m.productionsForLoc() + .apply( + Tuple2.apply(prod.source().get(), prod.location().get()))) + .filter(p -> p.klabel().isDefined()) + .map(p -> p.klabel().get().name()) + .collect(Collectors.toSet())); + } } + } } - }; - if (sentence instanceof Rule rl) { - checkKLabels.apply(rl.body()); - checkKLabels.apply(rl.requires()); - checkKLabels.apply(rl.ensures()); - } else if (sentence instanceof Context ctx) { - checkKLabels.apply(ctx.body()); - checkKLabels.apply(ctx.requires()); - } else if (sentence instanceof ContextAlias ctx) { - checkKLabels.apply(ctx.body()); - checkKLabels.apply(ctx.requires()); - } else if (sentence instanceof Production prod) { - if (prod.klabel().isDefined()) { - KLabel klabel = prod.klabel().get(); - if (klabelProds.containsKey(klabel.name()) && !internalDuplicates.contains(klabel.name())) { - errors.add(KEMException.compilerError("Symbol " + klabel.name() + " is not unique. Previously defined as: " + klabelProds.get(klabel.name()), prod)); - } - klabels.put(klabel.name(), m); - klabelProds.put(klabel.name(), prod); + if (!m.definedKLabels().apply(klabel) && !isInternalKLabel(klabel.name(), m)) { + errors.add( + KEMException.compilerError( + "Found klabel " + klabel.name() + " not defined in any production.", k)); } + } + }; + if (sentence instanceof Rule rl) { + checkKLabels.apply(rl.body()); + checkKLabels.apply(rl.requires()); + checkKLabels.apply(rl.ensures()); + } else if (sentence instanceof Context ctx) { + checkKLabels.apply(ctx.body()); + checkKLabels.apply(ctx.requires()); + } else if (sentence instanceof ContextAlias ctx) { + checkKLabels.apply(ctx.body()); + checkKLabels.apply(ctx.requires()); + } else if (sentence instanceof Production prod) { + if (prod.klabel().isDefined()) { + KLabel klabel = prod.klabel().get(); + if (klabelProds.containsKey(klabel.name()) && !internalDuplicates.contains(klabel.name())) { + errors.add( + KEMException.compilerError( + "Symbol " + + klabel.name() + + " is not unique. Previously defined as: " + + klabelProds.get(klabel.name()), + prod)); } + klabels.put(klabel.name(), m); + klabelProds.put(klabel.name(), prod); + } } + } - private boolean isExtraConcreteRule(Rule r) { - return r.label().isPresent() && extraConcreteRules.contains(r.label().get()); - } + private boolean isExtraConcreteRule(Rule r) { + return r.label().isPresent() && extraConcreteRules.contains(r.label().get()); + } - private boolean hasSymbolicAttWithNoArg(Rule r) { - return r.att().contains(Att.SYMBOLIC()) && r.att().get(Att.SYMBOLIC()).equals(""); - } + private boolean hasSymbolicAttWithNoArg(Rule r) { + return r.att().contains(Att.SYMBOLIC()) && r.att().get(Att.SYMBOLIC()).equals(""); + } - private boolean hasConcreteAttWithNoArg(Rule r) { - return isExtraConcreteRule(r) || (r.att().contains(Att.CONCRETE()) && r.att().get(Att.CONCRETE()).equals("")); - } + private boolean hasConcreteAttWithNoArg(Rule r) { + return isExtraConcreteRule(r) + || (r.att().contains(Att.CONCRETE()) && r.att().get(Att.CONCRETE()).equals("")); + } - private boolean hasConcreteAtt(Rule r) { - return isExtraConcreteRule(r) || r.att().contains(Att.CONCRETE()); - } + private boolean hasConcreteAtt(Rule r) { + return isExtraConcreteRule(r) || r.att().contains(Att.CONCRETE()); + } - public void check(Module mainMod) { - Set definedButNotUsed = new HashSet<>(klabelProds.keySet()); - definedButNotUsed.removeAll(usedLabels); - File includeDir = files.resolveKInclude("."); - String canonicalPath; - try { - canonicalPath = includeDir.getCanonicalPath(); - if (!canonicalPath.endsWith(File.separator)) { - canonicalPath = canonicalPath + File.separator; - } - } catch (IOException e) { - canonicalPath = null; + public void check(Module mainMod) { + Set definedButNotUsed = new HashSet<>(klabelProds.keySet()); + definedButNotUsed.removeAll(usedLabels); + File includeDir = files.resolveKInclude("."); + String canonicalPath; + try { + canonicalPath = includeDir.getCanonicalPath(); + if (!canonicalPath.endsWith(File.separator)) { + canonicalPath = canonicalPath + File.separator; + } + } catch (IOException e) { + canonicalPath = null; + } + for (String symbol : definedButNotUsed) { + Production prod = klabelProds.get(symbol); + Optional s = prod.source(); + if (prod.att().contains(Att.MAINCELL()) + || prod.att().contains(Att.UNUSED()) + || symbol.equals("") + || !s.isPresent() + || (prod.att().contains(Att.CELL()) + && stream(prod.nonterminals()) + .filter( + nt -> + klabels + .get(symbol) + .sortAttributesFor() + .get(nt.sort().head()) + .getOrElse(() -> Att.empty()) + .contains(Att.CELL_COLLECTION())) + .findAny() + .isPresent())) { + continue; + } + if (canonicalPath == null || !s.get().source().contains(canonicalPath)) { + kem.registerCompilerWarning( + ExceptionType.UNUSED_SYMBOL, + errors, + "Symbol '" + + symbol + + "' defined but not used. Add the 'unused' attribute if this is intentional.", + klabelProds.get(symbol)); + } + } + for (KLabel function : iterable(mainMod.functions())) { + boolean allConcrete = true; + boolean allSymbolic = true; + for (Rule rule : + iterable(mainMod.rulesFor().get(function).getOrElse(() -> Collections.Set()))) { + if ((hasConcreteAttWithNoArg(rule) && rule.att().contains(Att.SYMBOLIC())) + || (hasSymbolicAttWithNoArg(rule) && hasConcreteAtt(rule))) { + errors.add( + KEMException.compilerError( + "Rule cannot be both concrete and symbolic in the same variable.", rule)); } - for (String symbol : definedButNotUsed) { - Production prod = klabelProds.get(symbol); - Optional s = prod.source(); - if (prod.att().contains(Att.MAINCELL()) || - prod.att().contains(Att.UNUSED()) || - symbol.equals("") || - !s.isPresent() || - (prod.att().contains(Att.CELL()) && stream(prod.nonterminals()).filter(nt -> klabels.get(symbol).sortAttributesFor().get(nt.sort().head()).getOrElse(() -> Att.empty()).contains(Att.CELL_COLLECTION())).findAny().isPresent())) { - continue; - } - if (canonicalPath == null || !s.get().source().contains(canonicalPath)) { - kem.registerCompilerWarning(ExceptionType.UNUSED_SYMBOL, errors, "Symbol '" + symbol + "' defined but not used. Add the 'unused' attribute if this is intentional.", klabelProds.get(symbol)); - } + if (!hasConcreteAttWithNoArg(rule) && !rule.att().contains(Att.SIMPLIFICATION())) { + allConcrete = false; } - for (KLabel function : iterable(mainMod.functions())) { - boolean allConcrete = true; - boolean allSymbolic = true; - for (Rule rule : iterable(mainMod.rulesFor().get(function).getOrElse(() -> Collections.Set()))) { - if ((hasConcreteAttWithNoArg(rule) && rule.att().contains(Att.SYMBOLIC())) || - (hasSymbolicAttWithNoArg(rule) && hasConcreteAtt(rule))) { - errors.add(KEMException.compilerError("Rule cannot be both concrete and symbolic in the same variable.", rule)); - } - if (!hasConcreteAttWithNoArg(rule) && !rule.att().contains(Att.SIMPLIFICATION())) { - allConcrete = false; - } - if (!hasSymbolicAttWithNoArg(rule) && !rule.att().contains(Att.SIMPLIFICATION())) { - allSymbolic = false; - } - } - // If concrete or symbolic is used on the rule for a function, force all of them to have the same attribute - // to keep the soundness of the definition. Exception are simplification rules which need to be sound by themselves. - // https://github.com/runtimeverification/k/issues/1591 - for (Rule rule : iterable(mainMod.rulesFor().get(function).getOrElse(() -> Collections.Set()))) { - if (hasConcreteAtt(rule) && !allConcrete && !rule.att().contains(Att.SIMPLIFICATION())) { - errors.add(KEMException.compilerError("Found concrete attribute without simplification attribute on function with one or more non-concrete rules.", rule)); - } - if (rule.att().contains(Att.SYMBOLIC()) && !allSymbolic && !rule.att().contains(Att.SIMPLIFICATION())) { - errors.add(KEMException.compilerError("Found symbolic attribute without simplification attribute on function with one or more non-symbolic rules.", rule)); - } - } + if (!hasSymbolicAttWithNoArg(rule) && !rule.att().contains(Att.SIMPLIFICATION())) { + allSymbolic = false; } - for (Rule rule : iterable(mainMod.rules())) { - Att att = rule.att(); - if (att.contains(Att.SIMPLIFICATION()) && hasConcreteAtt(rule) && att.contains(Att.SYMBOLIC())) { - Collection concreteVars = Arrays.stream(att.getOptional(Att.CONCRETE()).orElse("").split(",")) - .map(String::trim).collect(Collectors.toList()); - Collection symbolicVars = Arrays.stream(att.get(Att.SYMBOLIC()).split(",")) - .map(String::trim).collect(Collectors.toList()); - if (concreteVars.isEmpty() || symbolicVars.isEmpty()) - errors.add(KEMException.compilerError("Rule cannot be both concrete and symbolic in the same variable.", rule)); - Collection intersection = CollectionUtils.intersection(concreteVars, symbolicVars); - if (!intersection.isEmpty()) - errors.add(KEMException.compilerError("Rule cannot be both concrete and symbolic in the same variable: " + intersection, rule)); - } + } + // If concrete or symbolic is used on the rule for a function, force all of them to have the + // same attribute + // to keep the soundness of the definition. Exception are simplification rules which need to + // be sound by themselves. + // https://github.com/runtimeverification/k/issues/1591 + for (Rule rule : + iterable(mainMod.rulesFor().get(function).getOrElse(() -> Collections.Set()))) { + if (hasConcreteAtt(rule) && !allConcrete && !rule.att().contains(Att.SIMPLIFICATION())) { + errors.add( + KEMException.compilerError( + "Found concrete attribute without simplification attribute on function with one" + + " or more non-concrete rules.", + rule)); + } + if (rule.att().contains(Att.SYMBOLIC()) + && !allSymbolic + && !rule.att().contains(Att.SIMPLIFICATION())) { + errors.add( + KEMException.compilerError( + "Found symbolic attribute without simplification attribute on function with one" + + " or more non-symbolic rules.", + rule)); } + } } + for (Rule rule : iterable(mainMod.rules())) { + Att att = rule.att(); + if (att.contains(Att.SIMPLIFICATION()) + && hasConcreteAtt(rule) + && att.contains(Att.SYMBOLIC())) { + Collection concreteVars = + Arrays.stream(att.getOptional(Att.CONCRETE()).orElse("").split(",")) + .map(String::trim) + .collect(Collectors.toList()); + Collection symbolicVars = + Arrays.stream(att.get(Att.SYMBOLIC()).split(",")) + .map(String::trim) + .collect(Collectors.toList()); + if (concreteVars.isEmpty() || symbolicVars.isEmpty()) + errors.add( + KEMException.compilerError( + "Rule cannot be both concrete and symbolic in the same variable.", rule)); + Collection intersection = CollectionUtils.intersection(concreteVars, symbolicVars); + if (!intersection.isEmpty()) + errors.add( + KEMException.compilerError( + "Rule cannot be both concrete and symbolic in the same variable: " + intersection, + rule)); + } + } + } - private static final ImmutableSet internalDuplicates = ImmutableSet.of("#EmptyKList", "#EmptyK", "#ruleRequires", "#ruleRequiresEnsures", "#Top", "#Bottom"); + private static final ImmutableSet internalDuplicates = + ImmutableSet.of( + "#EmptyKList", "#EmptyK", "#ruleRequires", "#ruleRequiresEnsures", "#Top", "#Bottom"); - private static final ImmutableSet internalNames = ImmutableSet.of("#cells", "#dots", "#noDots", "#Or", "#fun2", "#fun3", "#let", "#withConfig", "", "#SemanticCastToBag", "_:=K_", "_:/=K_"); + private static final ImmutableSet internalNames = + ImmutableSet.of( + "#cells", + "#dots", + "#noDots", + "#Or", + "#fun2", + "#fun3", + "#let", + "#withConfig", + "", + "#SemanticCastToBag", + "_:=K_", + "_:/=K_"); - public static boolean isInternalKLabel(String name, Module m) { - return m.semanticCasts().apply(name) || internalNames.contains(name)|| m.recordProjections().apply(name) || m.sortPredicates().apply(name) || m.sortProjections().apply(name); - } + public static boolean isInternalKLabel(String name, Module m) { + return m.semanticCasts().apply(name) + || internalNames.contains(name) + || m.recordProjections().apply(name) + || m.sortPredicates().apply(name) + || m.sortProjections().apply(name); + } } diff --git a/kernel/src/main/java/org/kframework/compile/checks/CheckLabels.java b/kernel/src/main/java/org/kframework/compile/checks/CheckLabels.java index 2c55fcd94eb..62318e75687 100644 --- a/kernel/src/main/java/org/kframework/compile/checks/CheckLabels.java +++ b/kernel/src/main/java/org/kframework/compile/checks/CheckLabels.java @@ -1,36 +1,31 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile.checks; +import java.util.Set; +import java.util.TreeSet; import org.kframework.definition.ContextAlias; -import org.kframework.definition.Module; import org.kframework.definition.Sentence; import org.kframework.utils.errorsystem.KEMException; -import java.util.Set; -import java.util.TreeSet; - -/** - * Check that all sentence labels in a module are unique. - */ +/** Check that all sentence labels in a module are unique. */ public class CheckLabels { - private final Set errors; + private final Set errors; - public CheckLabels(Set errors) { - this.errors = errors; - } + public CheckLabels(Set errors) { + this.errors = errors; + } - private final Set labels = new TreeSet<>(); + private final Set labels = new TreeSet<>(); - public void check(Sentence sentence) { - if (sentence instanceof ContextAlias) { - return; - } - if (sentence.label().isPresent()) { - String label = sentence.label().get(); - if (!labels.add(label)) { - errors.add(KEMException.compilerError("Found duplicate sentence label " + label, sentence)); - } - } + public void check(Sentence sentence) { + if (sentence instanceof ContextAlias) { + return; } + if (sentence.label().isPresent()) { + String label = sentence.label().get(); + if (!labels.add(label)) { + errors.add(KEMException.compilerError("Found duplicate sentence label " + label, sentence)); + } + } + } } - diff --git a/kernel/src/main/java/org/kframework/compile/checks/CheckListDecl.java b/kernel/src/main/java/org/kframework/compile/checks/CheckListDecl.java index fd58b25c86e..49e1127e73f 100644 --- a/kernel/src/main/java/org/kframework/compile/checks/CheckListDecl.java +++ b/kernel/src/main/java/org/kframework/compile/checks/CheckListDecl.java @@ -2,6 +2,7 @@ package org.kframework.compile.checks; import com.google.common.collect.ImmutableSet; +import java.util.Set; import org.kframework.builtin.Sorts; import org.kframework.kil.Module; import org.kframework.kil.ModuleItem; @@ -12,52 +13,49 @@ import org.kframework.kil.UserList; import org.kframework.kore.Sort; import org.kframework.utils.errorsystem.KEMException; -import org.kframework.utils.errorsystem.KExceptionManager; - -import java.util.Set; /** - * Checks user list declarations: - * - Base sorts cannot be extended with list. - * - Circular lists are not allowed. - * - Inline lists are not allowed. + * Checks user list declarations: - Base sorts cannot be extended with list. - Circular lists are + * not allowed. - Inline lists are not allowed. */ public class CheckListDecl { - public static void check(Module m) { - m.getItems().stream().forEach(CheckListDecl::check); // i -> check(i) - } - - private static final Set BASE_SORTS = ImmutableSet.of(Sorts.K(), Sorts.KResult(), Sorts.KItem(), - Sorts.KList(), Sorts.Bag(), Sorts.KLabel()); - - private static boolean isBaseSort(Sort sort) { - return BASE_SORTS.contains(sort); - } - - - private static void check(ModuleItem i) { - if (i instanceof Syntax s) { - for (PriorityBlock b : s.getPriorityBlocks()) { - for (Production p : b.getProductions()) { - if (p.getItems().size() == 1 && p.getItems().get(0) instanceof UserList) { // Syntax Es ::= List{E,""} - Sort listSort = s.getDeclaredSort().getSort(); // Es - Sort elemSort = ((UserList) p.getItems().get(0)).getSort(); // E - if (isBaseSort(listSort)) { - throw KEMException.compilerError(listSort + " can not be extended to be a list sort.", p); - } - if (listSort.equals(elemSort)) { - throw KEMException.compilerError("Circular lists are not allowed.", p); - } - } else { - for (ProductionItem it : p.getItems()) { - if (it instanceof UserList) { // Syntax Es ::= ... List{E,""} ... - throw KEMException.compilerError("Inline list declarations are not allowed.", it); - } - } - } - } + public static void check(Module m) { + m.getItems().stream().forEach(CheckListDecl::check); // i -> check(i) + } + + private static final Set BASE_SORTS = + ImmutableSet.of( + Sorts.K(), Sorts.KResult(), Sorts.KItem(), Sorts.KList(), Sorts.Bag(), Sorts.KLabel()); + + private static boolean isBaseSort(Sort sort) { + return BASE_SORTS.contains(sort); + } + + private static void check(ModuleItem i) { + if (i instanceof Syntax s) { + for (PriorityBlock b : s.getPriorityBlocks()) { + for (Production p : b.getProductions()) { + if (p.getItems().size() == 1 + && p.getItems().get(0) instanceof UserList) { // Syntax Es ::= List{E,""} + Sort listSort = s.getDeclaredSort().getSort(); // Es + Sort elemSort = ((UserList) p.getItems().get(0)).getSort(); // E + if (isBaseSort(listSort)) { + throw KEMException.compilerError( + listSort + " can not be extended to be a list sort.", p); + } + if (listSort.equals(elemSort)) { + throw KEMException.compilerError("Circular lists are not allowed.", p); + } + } else { + for (ProductionItem it : p.getItems()) { + if (it instanceof UserList) { // Syntax Es ::= ... List{E,""} ... + throw KEMException.compilerError("Inline list declarations are not allowed.", it); + } } + } } + } } + } } diff --git a/kernel/src/main/java/org/kframework/compile/checks/CheckRHSVariables.java b/kernel/src/main/java/org/kframework/compile/checks/CheckRHSVariables.java index 19c80ae5177..319f950866c 100644 --- a/kernel/src/main/java/org/kframework/compile/checks/CheckRHSVariables.java +++ b/kernel/src/main/java/org/kframework/compile/checks/CheckRHSVariables.java @@ -2,6 +2,8 @@ package org.kframework.compile.checks; import com.google.common.collect.Sets; +import java.util.HashSet; +import java.util.Set; import org.kframework.attributes.Att; import org.kframework.backend.Backends; import org.kframework.compile.GatherVarsVisitor; @@ -17,108 +19,117 @@ import org.kframework.utils.errorsystem.KEMException; import scala.Option; -import java.util.HashSet; -import java.util.Set; - /** - * Checks a sentence to determine if it declares any variables in the RHS that are not bound in the LHS. + * Checks a sentence to determine if it declares any variables in the RHS that are not bound in the + * LHS. * - * More specifically, it performs the following checks: for each anonymous variable, if it is on the right hand side - * of a rewrite operator, and does not signify a fresh variable or constant, it is an error. For each non-anonymous - * variable, if it is on the right hand side of a rewrite operator, it is an error if it does not appear anywhere on the - * left hand side of the rule, and does not signify a fresh variable or constant. + *

More specifically, it performs the following checks: for each anonymous variable, if it is on + * the right hand side of a rewrite operator, and does not signify a fresh variable or constant, it + * is an error. For each non-anonymous variable, if it is on the right hand side of a rewrite + * operator, it is an error if it does not appear anywhere on the left hand side of the rule, and + * does not signify a fresh variable or constant. * - * Exception: this check takes into account the {@code Att.UNBOUND_VARIABLES()} attribute and allows the - * variables whose names are specified by the attribute to be unbound in the LHS. + *

Exception: this check takes into account the {@code Att.UNBOUND_VARIABLES()} attribute and + * allows the variables whose names are specified by the attribute to be unbound in the LHS. */ public class CheckRHSVariables { - private final Set errors; - private final boolean errorExistential; - private final String backend; + private final Set errors; + private final boolean errorExistential; + private final String backend; - public CheckRHSVariables(Set errors, boolean errorExistential, String backend) { - this.errors = errors; - this.errorExistential = errorExistential; - this.backend = backend; - } - private void check(RuleOrClaim rule) { - resetVars(); - Set unboundVariableNames = getUnboundVarNames(rule); - boolean errorExistential = this.errorExistential && !(rule.att().contains(Att.LABEL()) && rule.att().get(Att.LABEL()).equals("STDIN-STREAM.stdinUnblock")); - new PatternValueAwareVisitor(true, errors).apply(rule.body()); - new PatternValueAwareVisitor(false, errors).apply(rule.requires()); - new PatternValueAwareVisitor(false, errors).apply(rule.ensures()); - gatherVars(true, rule.body(), errorExistential); - gatherVars(false, rule.ensures(), errorExistential); - if (rule instanceof Claim || (rule instanceof Rule && backend.equals(Backends.HASKELL))) { - gatherVars(true, rule.requires(), errorExistential); - check(rule.body(), true, false, unboundVariableNames); - check(rule.requires(), true, false, unboundVariableNames); - } else { - gatherVars(false, rule.requires(), errorExistential); - check(rule.body(), true, false, unboundVariableNames); - check(rule.requires(), false, false, unboundVariableNames); - } - check(rule.ensures(), false, false, unboundVariableNames); - } + public CheckRHSVariables(Set errors, boolean errorExistential, String backend) { + this.errors = errors; + this.errorExistential = errorExistential; + this.backend = backend; + } - private void check(Context context) { - resetVars(); - gatherVars(true, context.body(), false); - gatherVars(false, context.requires(), false); - check(context.body(), true, false, new HashSet<>()); - check(context.requires(), false, false, new HashSet<>()); + private void check(RuleOrClaim rule) { + resetVars(); + Set unboundVariableNames = getUnboundVarNames(rule); + boolean errorExistential = + this.errorExistential + && !(rule.att().contains(Att.LABEL()) + && rule.att().get(Att.LABEL()).equals("STDIN-STREAM.stdinUnblock")); + new PatternValueAwareVisitor(true, errors).apply(rule.body()); + new PatternValueAwareVisitor(false, errors).apply(rule.requires()); + new PatternValueAwareVisitor(false, errors).apply(rule.ensures()); + gatherVars(true, rule.body(), errorExistential); + gatherVars(false, rule.ensures(), errorExistential); + if (rule instanceof Claim || (rule instanceof Rule && backend.equals(Backends.HASKELL))) { + gatherVars(true, rule.requires(), errorExistential); + check(rule.body(), true, false, unboundVariableNames); + check(rule.requires(), true, false, unboundVariableNames); + } else { + gatherVars(false, rule.requires(), errorExistential); + check(rule.body(), true, false, unboundVariableNames); + check(rule.requires(), false, false, unboundVariableNames); } + check(rule.ensures(), false, false, unboundVariableNames); + } - private void check(ContextAlias context) { - resetVars(); - gatherVars(true, context.body(), false); - gatherVars(false, context.requires(), false); - check(context.body(), true, true, new HashSet<>()); - check(context.requires(), false, true, new HashSet<>()); - } + private void check(Context context) { + resetVars(); + gatherVars(true, context.body(), false); + gatherVars(false, context.requires(), false); + check(context.body(), true, false, new HashSet<>()); + check(context.requires(), false, false, new HashSet<>()); + } + + private void check(ContextAlias context) { + resetVars(); + gatherVars(true, context.body(), false); + gatherVars(false, context.requires(), false); + check(context.body(), true, true, new HashSet<>()); + check(context.requires(), false, true, new HashSet<>()); + } - public void check(Sentence s) { - if (s instanceof RuleOrClaim) { - check((RuleOrClaim) s); - } else if (s instanceof Context) { - check((Context) s); - } else if (s instanceof ContextAlias) { - check((ContextAlias) s); - } + public void check(Sentence s) { + if (s instanceof RuleOrClaim) { + check((RuleOrClaim) s); + } else if (s instanceof Context) { + check((Context) s); + } else if (s instanceof ContextAlias) { + check((ContextAlias) s); } + } - private Set getUnboundVarNames(RuleOrClaim rule) { - Option unboundVariablesString = rule.att().getOption(Att.UNBOUND_VARIABLES()); - Set unboundVariableNames = new HashSet<>(); - if (unboundVariablesString.nonEmpty()) { - String[] components = unboundVariablesString.get().split(","); - for (String component : components) { - unboundVariableNames.add(component.trim()); - } - } - return unboundVariableNames; + private Set getUnboundVarNames(RuleOrClaim rule) { + Option unboundVariablesString = rule.att().getOption(Att.UNBOUND_VARIABLES()); + Set unboundVariableNames = new HashSet<>(); + if (unboundVariablesString.nonEmpty()) { + String[] components = unboundVariablesString.get().split(","); + for (String component : components) { + unboundVariableNames.add(component.trim()); + } } + return unboundVariableNames; + } - private final Set vars = Sets.newHashSet(); + private final Set vars = Sets.newHashSet(); - void resetVars() { - vars.clear(); - } + void resetVars() { + vars.clear(); + } - void gatherVars(boolean isBody, K term, boolean isMacro) { - new GatherVarsVisitor(isBody, errors, vars, isMacro).apply(term); - } + void gatherVars(boolean isBody, K term, boolean isMacro) { + new GatherVarsVisitor(isBody, errors, vars, isMacro).apply(term); + } - private void check(K body, boolean isBody, boolean isAlias, Set unboundVarNames) { - Set unbound = new HashSet<>(); - new ComputeUnboundVariables(isBody, false, errors, vars, unbound::add).apply(body); - for (KVariable k : unbound) { - if (unboundVarNames.contains(k.name())) continue; - if (isAlias && k.name().equals("HOLE")) continue; - errors.add(KEMException.compilerError("Found variable " + k.name() - + " on right hand side of rule, not bound on left hand side." - + " Did you mean \"?" + k.name() + "\"?", k)); - } + private void check(K body, boolean isBody, boolean isAlias, Set unboundVarNames) { + Set unbound = new HashSet<>(); + new ComputeUnboundVariables(isBody, false, errors, vars, unbound::add).apply(body); + for (KVariable k : unbound) { + if (unboundVarNames.contains(k.name())) continue; + if (isAlias && k.name().equals("HOLE")) continue; + errors.add( + KEMException.compilerError( + "Found variable " + + k.name() + + " on right hand side of rule, not bound on left hand side." + + " Did you mean \"?" + + k.name() + + "\"?", + k)); } + } } diff --git a/kernel/src/main/java/org/kframework/compile/checks/CheckRewrite.java b/kernel/src/main/java/org/kframework/compile/checks/CheckRewrite.java index a6e7536df6f..b5f3a18401b 100644 --- a/kernel/src/main/java/org/kframework/compile/checks/CheckRewrite.java +++ b/kernel/src/main/java/org/kframework/compile/checks/CheckRewrite.java @@ -1,11 +1,11 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile.checks; +import java.util.Set; import org.kframework.definition.Claim; import org.kframework.definition.Module; import org.kframework.definition.RuleOrClaim; import org.kframework.definition.Sentence; -import org.kframework.kore.K; import org.kframework.kore.KApply; import org.kframework.kore.KAs; import org.kframework.kore.KRewrite; @@ -13,154 +13,169 @@ import org.kframework.kore.VisitK; import org.kframework.utils.errorsystem.KEMException; -import java.util.Set; - -/** - * Created by dwightguth on 1/25/16. - */ +/** Created by dwightguth on 1/25/16. */ public record CheckRewrite(Set errors, Module m) { - public void check(Sentence sentence) { - if (sentence instanceof RuleOrClaim) { - check((RuleOrClaim) sentence, sentence instanceof Claim); - } + public void check(Sentence sentence) { + if (sentence instanceof RuleOrClaim) { + check((RuleOrClaim) sentence, sentence instanceof Claim); } + } - private void check(RuleOrClaim sentence, boolean isClaim) { - class Holder { - boolean hasRewrite = false; - boolean inRewrite = false; - boolean inRewriteRHS = false; - boolean inAs = false; - boolean inFunctionContext = false; - boolean inFunctionBody = false; - } - Holder h = new Holder(); - VisitK visitor = new VisitK() { - @Override - public void apply(KRewrite k) { - boolean inRewrite = h.inRewrite; - boolean inRewriteRHS = h.inRewriteRHS; - if (h.inRewrite) { - errors.add(KEMException.compilerError("Rewrites are not allowed to be nested.", k)); - } - if (h.inFunctionContext) { - errors.add(KEMException.compilerError("Rewrites are not allowed in the context of a function rule.", k)); - } - if (h.inAs) { - errors.add(KEMException.compilerError("Rewrites are not allowed inside an #as pattern.", k)); - } - h.hasRewrite = true; - h.inRewrite = true; - super.apply(k.left()); - h.inRewriteRHS = true; - super.apply(k.right()); - h.inRewriteRHS = inRewriteRHS; - h.inRewrite = inRewrite; + private void check(RuleOrClaim sentence, boolean isClaim) { + class Holder { + boolean hasRewrite = false; + boolean inRewrite = false; + boolean inRewriteRHS = false; + boolean inAs = false; + boolean inFunctionContext = false; + boolean inFunctionBody = false; + } + Holder h = new Holder(); + VisitK visitor = + new VisitK() { + @Override + public void apply(KRewrite k) { + boolean inRewrite = h.inRewrite; + boolean inRewriteRHS = h.inRewriteRHS; + if (h.inRewrite) { + errors.add(KEMException.compilerError("Rewrites are not allowed to be nested.", k)); } - - @Override - public void apply(KAs k) { - boolean inAs = h.inAs; - if (h.inRewriteRHS) - errors.add(KEMException.compilerError("#as is not allowed in the RHS of a rule.", k)); - h.inAs = true; - super.apply(k); - h.inAs = inAs; + if (h.inFunctionContext) { + errors.add( + KEMException.compilerError( + "Rewrites are not allowed in the context of a function rule.", k)); + } + if (h.inAs) { + errors.add( + KEMException.compilerError("Rewrites are not allowed inside an #as pattern.", k)); } + h.hasRewrite = true; + h.inRewrite = true; + super.apply(k.left()); + h.inRewriteRHS = true; + super.apply(k.right()); + h.inRewriteRHS = inRewriteRHS; + h.inRewrite = inRewrite; + } - @Override - public void apply(KVariable k) { - if (!h.inRewriteRHS && k.name().startsWith("?")) { - errors.add(KEMException.compilerError( - "Existential variable " + k.name() + " found in LHS. Existential variables are only allowed in the RHS.", k)); - } + @Override + public void apply(KAs k) { + boolean inAs = h.inAs; + if (h.inRewriteRHS) + errors.add(KEMException.compilerError("#as is not allowed in the RHS of a rule.", k)); + h.inAs = true; + super.apply(k); + h.inAs = inAs; + } + + @Override + public void apply(KVariable k) { + if (!h.inRewriteRHS && k.name().startsWith("?")) { + errors.add( + KEMException.compilerError( + "Existential variable " + + k.name() + + " found in LHS. Existential variables are only allowed in the RHS.", + k)); } + } - @Override - public void apply(KApply k) { - if (!(k.klabel() instanceof KVariable) && k.klabel().name().equals("#fun2") || k.klabel().name().equals("#fun3")) { - if (k.klabel().name().equals("#fun2")) { - boolean wasInRewrite = h.inRewrite; - boolean hadRewrite = h.hasRewrite; - boolean wasInFunctionContext = h.inFunctionContext; - boolean wasInFunctionBody = h.inFunctionBody; - h.inRewrite = false; - h.hasRewrite = false; - h.inFunctionContext = false; - h.inFunctionBody = false; - apply(k.items().get(0)); - if (!h.hasRewrite) { - errors.add(KEMException.compilerError("#fun expressions must have at least one rewrite.", k)); - } - // in well formed programs this should always reset to true and true, but we want to make sure we - // don't create spurious reports if this constraint was violated by the user. - h.inRewrite = wasInRewrite; - h.hasRewrite = hadRewrite; - h.inFunctionContext = wasInFunctionContext; - h.inFunctionBody = wasInFunctionBody; - apply(k.items().get(1)); - } else if (k.klabel().name().equals("#fun3")) { - boolean wasInRewrite = h.inRewrite; - boolean hadRewrite = h.hasRewrite; - boolean wasInFunctionContext = h.inFunctionContext; - boolean wasInFunctionBody = h.inFunctionBody; - h.inRewrite = true; - h.hasRewrite = true; - h.inFunctionContext = false; - h.inFunctionBody = false; - apply(k.items().get(0)); - apply(k.items().get(1)); - // in well formed programs this should always reset to true and true, but we want to make sure we - // don't create spurious reports if this constraint was violated by the user. - h.inRewrite = wasInRewrite; - h.hasRewrite = hadRewrite; - h.inFunctionContext = wasInFunctionContext; - h.inFunctionBody = wasInFunctionBody; - apply(k.items().get(2)); - } else { - boolean wasInRewrite = h.inRewrite; - boolean hadRewrite = h.hasRewrite; - boolean wasInFunctionContext = h.inFunctionContext; - boolean wasInFunctionBody = h.inFunctionBody; - h.inRewrite = true; - h.hasRewrite = true; - h.inFunctionContext = false; - h.inFunctionBody = false; - apply(k.items().get(0)); - apply(k.items().get(2)); - // in well formed programs this should always reset to true and true, but we want to make sure we - // don't create spurious reports if this constraint was violated by the user. - h.inRewrite = wasInRewrite; - h.hasRewrite = hadRewrite; - h.inFunctionContext = wasInFunctionContext; - h.inFunctionBody = wasInFunctionBody; - apply(k.items().get(1)); - } - } else if (!(k.klabel() instanceof KVariable) && k.klabel().name().equals("#withConfig")) { - boolean inFunctionContext = h.inFunctionContext; - boolean inFunctionBody = h.inFunctionBody; - if (h.inFunctionContext || h.inFunctionBody) { - errors.add(KEMException.compilerError("Function context is not allowed to be nested.", k)); - } - if (h.inRewrite) { - errors.add(KEMException.compilerError("Function context is not allowed inside a rewrite.", k)); - } - h.inFunctionBody = true; - apply(k.items().get(0)); - h.inFunctionBody = inFunctionBody; - h.inFunctionContext = true; - apply(k.items().get(1)); - h.inFunctionContext = inFunctionContext; - } else { - super.apply(k); + @Override + public void apply(KApply k) { + if (!(k.klabel() instanceof KVariable) && k.klabel().name().equals("#fun2") + || k.klabel().name().equals("#fun3")) { + if (k.klabel().name().equals("#fun2")) { + boolean wasInRewrite = h.inRewrite; + boolean hadRewrite = h.hasRewrite; + boolean wasInFunctionContext = h.inFunctionContext; + boolean wasInFunctionBody = h.inFunctionBody; + h.inRewrite = false; + h.hasRewrite = false; + h.inFunctionContext = false; + h.inFunctionBody = false; + apply(k.items().get(0)); + if (!h.hasRewrite) { + errors.add( + KEMException.compilerError( + "#fun expressions must have at least one rewrite.", k)); } + // in well formed programs this should always reset to true and true, but we want to + // make sure we + // don't create spurious reports if this constraint was violated by the user. + h.inRewrite = wasInRewrite; + h.hasRewrite = hadRewrite; + h.inFunctionContext = wasInFunctionContext; + h.inFunctionBody = wasInFunctionBody; + apply(k.items().get(1)); + } else if (k.klabel().name().equals("#fun3")) { + boolean wasInRewrite = h.inRewrite; + boolean hadRewrite = h.hasRewrite; + boolean wasInFunctionContext = h.inFunctionContext; + boolean wasInFunctionBody = h.inFunctionBody; + h.inRewrite = true; + h.hasRewrite = true; + h.inFunctionContext = false; + h.inFunctionBody = false; + apply(k.items().get(0)); + apply(k.items().get(1)); + // in well formed programs this should always reset to true and true, but we want to + // make sure we + // don't create spurious reports if this constraint was violated by the user. + h.inRewrite = wasInRewrite; + h.hasRewrite = hadRewrite; + h.inFunctionContext = wasInFunctionContext; + h.inFunctionBody = wasInFunctionBody; + apply(k.items().get(2)); + } else { + boolean wasInRewrite = h.inRewrite; + boolean hadRewrite = h.hasRewrite; + boolean wasInFunctionContext = h.inFunctionContext; + boolean wasInFunctionBody = h.inFunctionBody; + h.inRewrite = true; + h.hasRewrite = true; + h.inFunctionContext = false; + h.inFunctionBody = false; + apply(k.items().get(0)); + apply(k.items().get(2)); + // in well formed programs this should always reset to true and true, but we want to + // make sure we + // don't create spurious reports if this constraint was violated by the user. + h.inRewrite = wasInRewrite; + h.hasRewrite = hadRewrite; + h.inFunctionContext = wasInFunctionContext; + h.inFunctionBody = wasInFunctionBody; + apply(k.items().get(1)); + } + } else if (!(k.klabel() instanceof KVariable) + && k.klabel().name().equals("#withConfig")) { + boolean inFunctionContext = h.inFunctionContext; + boolean inFunctionBody = h.inFunctionBody; + if (h.inFunctionContext || h.inFunctionBody) { + errors.add( + KEMException.compilerError("Function context is not allowed to be nested.", k)); + } + if (h.inRewrite) { + errors.add( + KEMException.compilerError( + "Function context is not allowed inside a rewrite.", k)); + } + h.inFunctionBody = true; + apply(k.items().get(0)); + h.inFunctionBody = inFunctionBody; + h.inFunctionContext = true; + apply(k.items().get(1)); + h.inFunctionContext = inFunctionContext; + } else { + super.apply(k); } + } }; - visitor.accept(sentence.requires()); - visitor.accept(sentence.body()); - if (!h.hasRewrite && !isClaim) { - errors.add(KEMException.compilerError("Rules must have at least one rewrite.", sentence.body())); - } + visitor.accept(sentence.requires()); + visitor.accept(sentence.body()); + if (!h.hasRewrite && !isClaim) { + errors.add( + KEMException.compilerError("Rules must have at least one rewrite.", sentence.body())); } + } } diff --git a/kernel/src/main/java/org/kframework/compile/checks/CheckSmtLemmas.java b/kernel/src/main/java/org/kframework/compile/checks/CheckSmtLemmas.java index 343a0454df8..a634d09ccbe 100644 --- a/kernel/src/main/java/org/kframework/compile/checks/CheckSmtLemmas.java +++ b/kernel/src/main/java/org/kframework/compile/checks/CheckSmtLemmas.java @@ -1,37 +1,41 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile.checks; +import java.util.Set; import org.kframework.attributes.Att; import org.kframework.definition.Module; import org.kframework.definition.Rule; import org.kframework.definition.Sentence; import org.kframework.kore.KApply; -import org.kframework.kore.KRewrite; import org.kframework.kore.VisitK; import org.kframework.utils.errorsystem.KEMException; -import java.util.Set; - public record CheckSmtLemmas(Set errors, Module m) { - public void check(Sentence sentence) { - if ((sentence instanceof Rule) && sentence.att().contains(Att.SMT_LEMMA())) { - check((Rule) sentence); - } + public void check(Sentence sentence) { + if ((sentence instanceof Rule) && sentence.att().contains(Att.SMT_LEMMA())) { + check((Rule) sentence); } + } - private void check(Rule rule){ - new VisitK() { - @Override - public void apply(KApply k) { - if (m.productionsFor().isDefinedAt(k.klabel()) && !m.productionsFor().get(k.klabel()).get() - .exists(p -> p.att().contains(Att.SMT_HOOK()) || p.att().contains(Att.SMTLIB()))) { - errors.add(KEMException.compilerError( - "Invalid term in smt-lemma detected. All terms in smt-lemma rules require smt-hook or smtlib labels", k)); - } + private void check(Rule rule) { + new VisitK() { + @Override + public void apply(KApply k) { + if (m.productionsFor().isDefinedAt(k.klabel()) + && !m.productionsFor() + .get(k.klabel()) + .get() + .exists(p -> p.att().contains(Att.SMT_HOOK()) || p.att().contains(Att.SMTLIB()))) { + errors.add( + KEMException.compilerError( + "Invalid term in smt-lemma detected. All terms in smt-lemma rules require" + + " smt-hook or smtlib labels", + k)); + } - k.klist().items().forEach(super::apply); - } - }.accept(rule.body()); - } + k.klist().items().forEach(super::apply); + } + }.accept(rule.body()); + } } diff --git a/kernel/src/main/java/org/kframework/compile/checks/CheckSortTopUniqueness.java b/kernel/src/main/java/org/kframework/compile/checks/CheckSortTopUniqueness.java index c813d08109f..687a17904c3 100644 --- a/kernel/src/main/java/org/kframework/compile/checks/CheckSortTopUniqueness.java +++ b/kernel/src/main/java/org/kframework/compile/checks/CheckSortTopUniqueness.java @@ -1,6 +1,7 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile.checks; +import java.util.Set; import org.kframework.builtin.Sorts; import org.kframework.definition.Module; import org.kframework.definition.Production; @@ -9,38 +10,34 @@ import org.kframework.kore.Sort; import org.kframework.utils.errorsystem.KEMException; -import java.util.Set; - /** - * Check that every sort has an unique top (super) sort. - * For example, the following is not allowed, since A has multiple top sorts, KList and Bag: - * syntax A - * syntax KList ::= A - * syntax Bag ::= A + * Check that every sort has an unique top (super) sort. For example, the following is not allowed, + * since A has multiple top sorts, KList and Bag: syntax A syntax KList ::= A syntax Bag ::= A */ public record CheckSortTopUniqueness(Set errors, Module module) { - public void check(Sentence s) { - if (s instanceof Production) { - check((Production) s); - } else if (s instanceof SyntaxSort) { - check((SyntaxSort) s); - } + public void check(Sentence s) { + if (s instanceof Production) { + check((Production) s); + } else if (s instanceof SyntaxSort) { + check((SyntaxSort) s); } + } - private void check(SyntaxSort p) { - check(p.sort(), p); - } + private void check(SyntaxSort p) { + check(p.sort(), p); + } - private void check(Production p) { - check(p.sort(), p); - } + private void check(Production p) { + check(p.sort(), p); + } - private void check(Sort s, Sentence p) { - if (!s.equals(Sorts.Cell()) && - module.subsorts().lessThan(s, Sorts.KList()) && - module.subsorts().lessThan(s, Sorts.Bag())) { - errors.add(KEMException.compilerError("Multiple top sorts found for " + s + ": KList and Bag.", p)); - } + private void check(Sort s, Sentence p) { + if (!s.equals(Sorts.Cell()) + && module.subsorts().lessThan(s, Sorts.KList()) + && module.subsorts().lessThan(s, Sorts.Bag())) { + errors.add( + KEMException.compilerError("Multiple top sorts found for " + s + ": KList and Bag.", p)); } + } } diff --git a/kernel/src/main/java/org/kframework/compile/checks/CheckStreams.java b/kernel/src/main/java/org/kframework/compile/checks/CheckStreams.java index 239672df519..57a0834f3bb 100644 --- a/kernel/src/main/java/org/kframework/compile/checks/CheckStreams.java +++ b/kernel/src/main/java/org/kframework/compile/checks/CheckStreams.java @@ -1,6 +1,9 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile.checks; +import static org.kframework.Collections.*; + +import java.util.Set; import org.kframework.attributes.Att; import org.kframework.builtin.Sorts; import org.kframework.definition.Module; @@ -11,32 +14,29 @@ import org.kframework.kore.Sort; import org.kframework.utils.errorsystem.KEMException; -import java.util.Set; - -import static org.kframework.Collections.*; - -/** - * Checks that stream cells have contents of List sort. - */ +/** Checks that stream cells have contents of List sort. */ public record CheckStreams(Set errors, Module module) { - public void check(Sentence s) { - if (s instanceof Production) { - check((Production) s); - } + public void check(Sentence s) { + if (s instanceof Production) { + check((Production) s); } + } - private void check(Production p) { - if (p.att().contains(Att.CELL()) && p.att().contains(Att.STREAM())) { - ProductionItem i = mutable(p.items()).get(1); - if (i instanceof NonTerminal) { - Sort sort = ((NonTerminal) i).sort(); - if (!module.subsorts().lessThanEq(sort, Sorts.List())) { - errors.add(KEMException.compilerError("Wrong sort in streaming cell. Expected List, but found " + sort.toString() + ".", p)); - } - } else { - throw KEMException.internalError("Illegal arguments for stream cell."); - } + private void check(Production p) { + if (p.att().contains(Att.CELL()) && p.att().contains(Att.STREAM())) { + ProductionItem i = mutable(p.items()).get(1); + if (i instanceof NonTerminal) { + Sort sort = ((NonTerminal) i).sort(); + if (!module.subsorts().lessThanEq(sort, Sorts.List())) { + errors.add( + KEMException.compilerError( + "Wrong sort in streaming cell. Expected List, but found " + sort.toString() + ".", + p)); } + } else { + throw KEMException.internalError("Illegal arguments for stream cell."); + } } + } } diff --git a/kernel/src/main/java/org/kframework/compile/checks/CheckSyntaxGroups.java b/kernel/src/main/java/org/kframework/compile/checks/CheckSyntaxGroups.java index 015cd8e03de..dd6dcc0d5c4 100644 --- a/kernel/src/main/java/org/kframework/compile/checks/CheckSyntaxGroups.java +++ b/kernel/src/main/java/org/kframework/compile/checks/CheckSyntaxGroups.java @@ -1,37 +1,43 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile.checks; +import static org.kframework.Collections.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import org.kframework.definition.Module; import org.kframework.definition.Sentence; import org.kframework.definition.SyntaxAssociativity; import org.kframework.definition.Tag; import org.kframework.utils.errorsystem.KEMException; import org.kframework.utils.errorsystem.KException; import org.kframework.utils.errorsystem.KExceptionManager; -import org.kframework.definition.Module; -import static org.kframework.Collections.*; - -import java.util.List; -import java.util.ArrayList; -import java.util.Set; public record CheckSyntaxGroups(Set errors, Module module, KExceptionManager kem) { - public void check(Sentence s) { - if(s instanceof SyntaxAssociativity) { - Set tags = mutable(((SyntaxAssociativity) s).tags()); - List tagList = new ArrayList<>(tags); + public void check(Sentence s) { + if (s instanceof SyntaxAssociativity) { + Set tags = mutable(((SyntaxAssociativity) s).tags()); + List tagList = new ArrayList<>(tags); - for(int i = 0; i < tagList.size(); ++i) { - for(int j = i + 1; j < tagList.size(); ++j) { - Tag t1 = tagList.get(i); - Tag t2 = tagList.get(j); + for (int i = 0; i < tagList.size(); ++i) { + for (int j = i + 1; j < tagList.size(); ++j) { + Tag t1 = tagList.get(i); + Tag t2 = tagList.get(j); - if(this.module.priorities().inSomeRelation(t1, t2)) { - String message = "Symbols " + t1 + " and " + t2 + " are in the same associativity group, but have different priorities."; - kem.registerCompilerWarning(KException.ExceptionType.INVALID_ASSOCIATIVITY, errors, message, s); - } - } - } + if (this.module.priorities().inSomeRelation(t1, t2)) { + String message = + "Symbols " + + t1 + + " and " + + t2 + + " are in the same associativity group, but have different priorities."; + kem.registerCompilerWarning( + KException.ExceptionType.INVALID_ASSOCIATIVITY, errors, message, s); + } } + } } + } } diff --git a/kernel/src/main/java/org/kframework/compile/checks/CheckTokens.java b/kernel/src/main/java/org/kframework/compile/checks/CheckTokens.java index 17d2546b5cd..f3b597d5a14 100644 --- a/kernel/src/main/java/org/kframework/compile/checks/CheckTokens.java +++ b/kernel/src/main/java/org/kframework/compile/checks/CheckTokens.java @@ -2,42 +2,50 @@ package org.kframework.compile.checks; import com.google.common.collect.ImmutableSet; +import java.util.Set; import org.kframework.attributes.Att; -import org.kframework.attributes.Att.Key; import org.kframework.definition.Module; import org.kframework.definition.Production; import org.kframework.definition.Sentence; import org.kframework.utils.errorsystem.KEMException; -import java.util.Set; - public record CheckTokens(Set errors, Module m) { - private static final ImmutableSet ignoredSortNames = ImmutableSet.of("KBott", "KLabel"); - private static final ImmutableSet allowedAtts = ImmutableSet.of(Att.FUNCTION(), Att.TOKEN(), Att.BRACKET()); + private static final ImmutableSet ignoredSortNames = ImmutableSet.of("KBott", "KLabel"); + private static final ImmutableSet allowedAtts = + ImmutableSet.of(Att.FUNCTION(), Att.TOKEN(), Att.BRACKET()); - public void check(Sentence sentence) { - if (sentence instanceof Production p) { - check(p); - } + public void check(Sentence sentence) { + if (sentence instanceof Production p) { + check(p); } + } - // This check ensures that sorts containing token declarations only contain syntax declarations that are also tokens (or macros). - public void check(Production p) { - // ignoredSortNames contains special sorts defined in domains.md or kast.md that are special variables that - // contain subsorts and tokens. The codebase relies on the definitions in these files to be unmodified - // so ignore these names. - if (p.sort().name().startsWith("#") - || allowedAtts.stream().anyMatch(l -> p.att().contains(l)) - || ignoredSortNames.contains(p.sort().name())) { - return; - } - - if (!m.tokenSorts().contains(p.sort()) // We only care about sorts that have been declared as tokens. - || p.klabel().isDefined() && m.macroKLabels().contains(p.klabel().get())) { - return; - } + // This check ensures that sorts containing token declarations only contain syntax declarations + // that are also tokens (or macros). + public void check(Production p) { + // ignoredSortNames contains special sorts defined in domains.md or kast.md that are special + // variables that + // contain subsorts and tokens. The codebase relies on the definitions in these files to be + // unmodified + // so ignore these names. + if (p.sort().name().startsWith("#") + || allowedAtts.stream().anyMatch(l -> p.att().contains(l)) + || ignoredSortNames.contains(p.sort().name())) { + return; + } - errors.add(KEMException.compilerError( - "Sort " + p.sort().name() + " was declared as a token. Productions of this sort can only contain [function] or [token] labels.", p)); + if (!m.tokenSorts() + .contains(p.sort()) // We only care about sorts that have been declared as tokens. + || p.klabel().isDefined() && m.macroKLabels().contains(p.klabel().get())) { + return; } + + errors.add( + KEMException.compilerError( + "Sort " + + p.sort().name() + + " was declared as a token. Productions of this sort can only contain [function]" + + " or [token] labels.", + p)); + } } diff --git a/kernel/src/main/java/org/kframework/compile/checks/ComputeUnboundVariables.java b/kernel/src/main/java/org/kframework/compile/checks/ComputeUnboundVariables.java index 2f9f67d24aa..53db71595c2 100644 --- a/kernel/src/main/java/org/kframework/compile/checks/ComputeUnboundVariables.java +++ b/kernel/src/main/java/org/kframework/compile/checks/ComputeUnboundVariables.java @@ -1,81 +1,84 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile.checks; +import static org.kframework.kore.KORE.*; + +import java.util.Set; +import java.util.function.Consumer; import org.kframework.builtin.KLabels; +import org.kframework.compile.ResolveAnonVar; +import org.kframework.compile.RewriteAwareVisitor; import org.kframework.definition.Production; import org.kframework.kore.InjectedKLabel; -import org.kframework.kore.K; import org.kframework.kore.KApply; import org.kframework.kore.KVariable; -import org.kframework.compile.ResolveAnonVar; -import org.kframework.compile.RewriteAwareVisitor; import org.kframework.kore.Sort; import org.kframework.utils.errorsystem.KEMException; -import java.util.Set; -import java.util.function.Consumer; - -import static org.kframework.kore.KORE.*; - -/** - * Created by dwightguth on 3/6/17. - */ +/** Created by dwightguth on 3/6/17. */ public class ComputeUnboundVariables extends RewriteAwareVisitor { - private final Set vars; - private final Consumer reporter; - private Sort context = null; - private boolean isInKLhs = false; - private final boolean lambda; + private final Set vars; + private final Consumer reporter; + private Sort context = null; + private boolean isInKLhs = false; + private final boolean lambda; - public ComputeUnboundVariables(boolean isBody, boolean lambda, Set errors, Set vars, Consumer reporter) { - super(isBody, errors); - this.lambda = lambda; - this.vars = vars; - this.reporter = reporter; - } + public ComputeUnboundVariables( + boolean isBody, + boolean lambda, + Set errors, + Set vars, + Consumer reporter) { + super(isBody, errors); + this.lambda = lambda; + this.vars = vars; + this.reporter = reporter; + } - @Override - public void apply(KVariable k) { - if (context != null) { - k = KVariable(k.name(), k.att().add(Sort.class, context)); - } - if (isRHS() && !isInKLhs) { - if (!k.name().equals(KLabels.THIS_CONFIGURATION) && - ((k.equals(ResolveAnonVar.ANON_VAR) && !isLHS()) - || (!k.equals(ResolveAnonVar.ANON_VAR) && !(k.name().startsWith("?") || (k.name().startsWith("!") && !lambda)) && !vars.contains(k)))) { - reporter.accept(k); - } - } + @Override + public void apply(KVariable k) { + if (context != null) { + k = KVariable(k.name(), k.att().add(Sort.class, context)); + } + if (isRHS() && !isInKLhs) { + if (!k.name().equals(KLabels.THIS_CONFIGURATION) + && ((k.equals(ResolveAnonVar.ANON_VAR) && !isLHS()) + || (!k.equals(ResolveAnonVar.ANON_VAR) + && !(k.name().startsWith("?") || (k.name().startsWith("!") && !lambda)) + && !vars.contains(k)))) { + reporter.accept(k); + } } + } - @Override - public void apply(InjectedKLabel k) { - if (k.klabel() instanceof KVariable) { - apply((KVariable) k.klabel()); - } - super.apply(k); + @Override + public void apply(InjectedKLabel k) { + if (k.klabel() instanceof KVariable) { + apply((KVariable) k.klabel()); } + super.apply(k); + } - @Override - public void apply(KApply k) { - if (k.klabel().equals(KLabels.IN_K) || k.klabel().equals(KLabels.NOT_IN_K)) { - boolean tmp = isInKLhs; - isInKLhs = true; - apply(k.items().get(0)); - isInKLhs = tmp; - apply(k.items().get(1)); - return; - } - if (k.klabel().name().startsWith("#SemanticCastTo")) { - Sort savedContext = context; - context = k.att().get(Production.class).sort(); - apply(k.items().get(0)); - context = savedContext; - return; - } - if (k.klabel() instanceof KVariable) { - apply((KVariable) k.klabel()); - } - super.apply(k); + @Override + public void apply(KApply k) { + if (k.klabel().equals(KLabels.IN_K) || k.klabel().equals(KLabels.NOT_IN_K)) { + boolean tmp = isInKLhs; + isInKLhs = true; + apply(k.items().get(0)); + isInKLhs = tmp; + apply(k.items().get(1)); + return; + } + if (k.klabel().name().startsWith("#SemanticCastTo")) { + Sort savedContext = context; + context = k.att().get(Production.class).sort(); + apply(k.items().get(0)); + context = savedContext; + return; + } + if (k.klabel() instanceof KVariable) { + apply((KVariable) k.klabel()); } + super.apply(k); + } } diff --git a/kernel/src/main/java/org/kframework/kast/KastFrontEnd.java b/kernel/src/main/java/org/kframework/kast/KastFrontEnd.java index 0b8ed8c45fe..668c9e259dd 100644 --- a/kernel/src/main/java/org/kframework/kast/KastFrontEnd.java +++ b/kernel/src/main/java/org/kframework/kast/KastFrontEnd.java @@ -4,6 +4,16 @@ import com.google.common.collect.Lists; import com.google.inject.Inject; import com.google.inject.Provider; +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import org.kframework.attributes.Att; import org.kframework.attributes.Source; import org.kframework.builtin.BooleanUtils; @@ -36,202 +46,246 @@ import scala.Tuple2; import scala.util.Either; -import java.io.File; -import java.io.IOException; -import java.io.Reader; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - public class KastFrontEnd extends FrontEnd { - public static List getModules() { - List modules = new ArrayList<>(); - modules.add(new KastModule()); - modules.add(new JCommanderModule()); - modules.add(new CommonModule()); - return modules; - } + public static List getModules() { + List modules = new ArrayList<>(); + modules.add(new KastModule()); + modules.add(new JCommanderModule()); + modules.add(new CommonModule()); + return modules; + } - private final KastOptions options; - private final Stopwatch sw; - private final KExceptionManager kem; - private final Provider files; - private final Map env; - private final Provider kompiledDir; - private final Provider compiledDef; - private final Provider kprint; - private final DefinitionScope scope; - - @Inject - KastFrontEnd( - KastOptions options, - @Usage String usage, - Stopwatch sw, - KExceptionManager kem, - JarInfo jarInfo, - @Environment Map env, - Provider files, - @KompiledDir Provider kompiledDir, - Provider compiledDef, - Provider kprint, - DefinitionScope scope) { - super(kem, options.global, usage, jarInfo, files); - this.options = options; - this.sw = sw; - this.kem = kem; - this.files = files; - this.env = env; - this.kompiledDir = kompiledDir; - this.compiledDef = compiledDef; - this.kprint = kprint; - this.scope = scope; - } + private final KastOptions options; + private final Stopwatch sw; + private final KExceptionManager kem; + private final Provider files; + private final Map env; + private final Provider kompiledDir; + private final Provider compiledDef; + private final Provider kprint; + private final DefinitionScope scope; + + @Inject + KastFrontEnd( + KastOptions options, + @Usage String usage, + Stopwatch sw, + KExceptionManager kem, + JarInfo jarInfo, + @Environment Map env, + Provider files, + @KompiledDir Provider kompiledDir, + Provider compiledDef, + Provider kprint, + DefinitionScope scope) { + super(kem, options.global, usage, jarInfo, files); + this.options = options; + this.sw = sw; + this.kem = kem; + this.files = files; + this.env = env; + this.kompiledDir = kompiledDir; + this.compiledDef = compiledDef; + this.kprint = kprint; + this.scope = scope; + } - public enum KompileSteps { - help, concretizeCells, anonVars + public enum KompileSteps { + help, + concretizeCells, + anonVars + } + + /** + * @return true if the application terminated normally; false otherwise + */ + @Override + public int run() { + if (options.steps.contains(KompileSteps.help)) { + System.out.println( + "For --input rule, apply these steps, in this order, separated by comma:\n" + + " concretizeCells - configuration concretization\n" + + " anonVars - rename anonymous variables to be unique\n" + + " , - empty list of steps"); + return 0; } + scope.enter(kompiledDir.get()); + try { + CompiledDefinition def = compiledDef.get(); - /** - * - * @return true if the application terminated normally; false otherwise - */ - @Override - public int run() { - if (options.steps.contains(KompileSteps.help)) { - System.out.println( - "For --input rule, apply these steps, in this order, separated by comma:\n" + - " concretizeCells - configuration concretization\n" + - " anonVars - rename anonymous variables to be unique\n" + - " , - empty list of steps"); - return 0; + org.kframework.kore.Sort sort = options.sort; + String startSymbolLocation = "kast CLI --sort"; + if (sort == null) { + if (env.get("KRUN_SORT") != null) { + sort = Outer.parseSort(env.get("KRUN_SORT")); + startSymbolLocation = "kast env KRUN_SORT"; } - scope.enter(kompiledDir.get()); - try { - CompiledDefinition def = compiledDef.get(); - - org.kframework.kore.Sort sort = options.sort; - String startSymbolLocation = "kast CLI --sort"; - if (sort == null) { - if (env.get("KRUN_SORT") != null) { - sort = Outer.parseSort(env.get("KRUN_SORT")); - startSymbolLocation = "kast env KRUN_SORT"; - } - } + } - if (options.input.equals(InputModes.RULE)) { - options.module = def.executionModule().name(); - if (sort == null) { - sort = Sorts.K(); - startSymbolLocation = "kast default K"; - } - Module mod = def.ruleParsingModuleFor(options.module) - .getOrElse(() -> {throw KEMException.innerParserError("Module " + options.module + " not found. Specify a module with -m.");}); - String stringToParse = FileUtil.read(options.stringToParse()); - Source source = options.source(); - - try (ParseInModule parseInModule = RuleGrammarGenerator.getCombinedGrammar(mod, true, null)) { - if (options.debugTokens) - System.out.println(parseInModule.tokenizeString(stringToParse, source)); - else { - Tuple2, K>, Set> res = parseInModule.parseString(stringToParse, sort, startSymbolLocation, source); - kem.addAllKException(res._2().stream().map(KEMException::getKException).collect(Collectors.toSet())); - if (res._1().isLeft()) { - throw res._1().left().get().iterator().next(); - } - // important to get the extension module for unparsing because it contains generated syntax - // like casts, projections and others - Module unparsingMod = parseInModule.getExtensionModule(); - K parsed = new TreeNodesToKORE(Outer::parseSort, true).down(res._1().right().get()); - - if (options.expandMacros) { - parsed = ExpandMacros.forNonSentences(unparsingMod, files.get(), def.kompileOptions, false).expand(parsed); - } - ConfigurationInfoFromModule configInfo = new ConfigurationInfoFromModule(mod); - LabelInfo labelInfo = new LabelInfoFromModule(mod); - SortInfo sortInfo = SortInfo.fromModule(mod); - - Rule r = Rule.apply(parsed, BooleanUtils.TRUE, BooleanUtils.TRUE, Att.empty()); - if (options.steps.contains(KompileSteps.anonVars)) - r = (Rule) new ResolveAnonVar().resolve(r); - r = (Rule) new ResolveSemanticCasts(false).resolve(r); // move casts to side condition predicates - r = (Rule) new ConstantFolding().fold(unparsingMod, r); - r = (Rule) new CloseCells(configInfo, sortInfo, labelInfo).close(r); - if (options.steps.contains(KompileSteps.concretizeCells)) { - r = (Rule) new AddImplicitComputationCell(configInfo, labelInfo).apply(mod, r); - r = (Rule) new ConcretizeCells(configInfo, labelInfo, sortInfo, mod).concretize(mod, r); - } - K result = r.body(); - kprint.get().prettyPrint(def.kompiledDefinition, unparsingMod, s -> kprint.get().outputFile(s), result, sort); - } - } - - sw.printTotal("Total"); - return 0; - } else if (!options.steps.equals(Lists.newArrayList(KompileSteps.anonVars))) { - throw KEMException.innerParserError("Option --steps is available only with --input rule."); - } + if (options.input.equals(InputModes.RULE)) { + options.module = def.executionModule().name(); + if (sort == null) { + sort = Sorts.K(); + startSymbolLocation = "kast default K"; + } + Module mod = + def.ruleParsingModuleFor(options.module) + .getOrElse( + () -> { + throw KEMException.innerParserError( + "Module " + options.module + " not found. Specify a module with -m."); + }); + String stringToParse = FileUtil.read(options.stringToParse()); + Source source = options.source(); - Module unparsingMod; - if (sort == null) { - sort = def.programStartSymbol; - startSymbolLocation = " program default"; + try (ParseInModule parseInModule = + RuleGrammarGenerator.getCombinedGrammar(mod, true, null)) { + if (options.debugTokens) + System.out.println(parseInModule.tokenizeString(stringToParse, source)); + else { + Tuple2, K>, Set> res = + parseInModule.parseString(stringToParse, sort, startSymbolLocation, source); + kem.addAllKException( + res._2().stream().map(KEMException::getKException).collect(Collectors.toSet())); + if (res._1().isLeft()) { + throw res._1().left().get().iterator().next(); } + // important to get the extension module for unparsing because it contains generated + // syntax + // like casts, projections and others + Module unparsingMod = parseInModule.getExtensionModule(); + K parsed = new TreeNodesToKORE(Outer::parseSort, true).down(res._1().right().get()); - if (options.module == null) { - options.module = def.mainSyntaxModuleName(); - unparsingMod = switch (options.input) { - case KORE -> def.languageParsingModule(); - default -> def.kompiledDefinition.getModule(def.mainSyntaxModuleName()).get(); - }; - } else { - Option maybeUnparsingMod = def.kompiledDefinition.getModule(options.module); - if (maybeUnparsingMod.isEmpty()) { - throw KEMException.innerParserError("Module " + options.module + " not found."); - } - unparsingMod = maybeUnparsingMod.get(); + if (options.expandMacros) { + parsed = + ExpandMacros.forNonSentences(unparsingMod, files.get(), def.kompileOptions, false) + .expand(parsed); } + ConfigurationInfoFromModule configInfo = new ConfigurationInfoFromModule(mod); + LabelInfo labelInfo = new LabelInfoFromModule(mod); + SortInfo sortInfo = SortInfo.fromModule(mod); - Option maybeMod = def.programParsingModuleFor(options.module, kem); - if (maybeMod.isEmpty()) { - throw KEMException.innerParserError("Module " + options.module + " not found. Specify a module with -m."); - } - Module parsingMod = maybeMod.get(); - - KRead kread = new KRead(kem, files.get(), options.input, options.global); - if (options.genParser || options.genGlrParser) { - kread.createBisonParser(parsingMod, sort, options.bisonOutputFile(), options.genGlrParser, options.bisonFile, options.bisonStackMaxDepth, false); - try { - Files.copy(options.bisonOutputFile().toPath(), files.get().resolveKompiled("parser_" + sort.name() + "_" + options.module).toPath(), StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) {} - } else { - Reader stringToParse = options.stringToParse(); - Source source = options.source(); - if (options.debugTokens) - System.out.println(kread.showTokens(parsingMod, def, FileUtil.read(stringToParse), source)); - else { - K parsed = kread.prettyRead(parsingMod, sort, startSymbolLocation, def, source, FileUtil.read(stringToParse), options.debugParse); - - if (options.expandMacros) { - parsed = ExpandMacros.forNonSentences(unparsingMod, files.get(), def.kompileOptions, false).expand(parsed); - } - - if (sort.equals(Sorts.K())) { - sort = Sorts.KItem(); - } - - kprint.get().prettyPrint(def.kompiledDefinition, unparsingMod, s -> kprint.get().outputFile(s), parsed, sort); - } + Rule r = Rule.apply(parsed, BooleanUtils.TRUE, BooleanUtils.TRUE, Att.empty()); + if (options.steps.contains(KompileSteps.anonVars)) + r = (Rule) new ResolveAnonVar().resolve(r); + r = + (Rule) + new ResolveSemanticCasts(false) + .resolve(r); // move casts to side condition predicates + r = (Rule) new ConstantFolding().fold(unparsingMod, r); + r = (Rule) new CloseCells(configInfo, sortInfo, labelInfo).close(r); + if (options.steps.contains(KompileSteps.concretizeCells)) { + r = (Rule) new AddImplicitComputationCell(configInfo, labelInfo).apply(mod, r); + r = + (Rule) + new ConcretizeCells(configInfo, labelInfo, sortInfo, mod).concretize(mod, r); } + K result = r.body(); + kprint + .get() + .prettyPrint( + def.kompiledDefinition, + unparsingMod, + s -> kprint.get().outputFile(s), + result, + sort); + } + } + + sw.printTotal("Total"); + return 0; + } else if (!options.steps.equals(Lists.newArrayList(KompileSteps.anonVars))) { + throw KEMException.innerParserError("Option --steps is available only with --input rule."); + } - sw.printTotal("Total"); - return 0; - } finally { - scope.exit(); + Module unparsingMod; + if (sort == null) { + sort = def.programStartSymbol; + startSymbolLocation = " program default"; + } + + if (options.module == null) { + options.module = def.mainSyntaxModuleName(); + unparsingMod = + switch (options.input) { + case KORE -> def.languageParsingModule(); + default -> def.kompiledDefinition.getModule(def.mainSyntaxModuleName()).get(); + }; + } else { + Option maybeUnparsingMod = def.kompiledDefinition.getModule(options.module); + if (maybeUnparsingMod.isEmpty()) { + throw KEMException.innerParserError("Module " + options.module + " not found."); + } + unparsingMod = maybeUnparsingMod.get(); + } + + Option maybeMod = def.programParsingModuleFor(options.module, kem); + if (maybeMod.isEmpty()) { + throw KEMException.innerParserError( + "Module " + options.module + " not found. Specify a module with -m."); + } + Module parsingMod = maybeMod.get(); + + KRead kread = new KRead(kem, files.get(), options.input, options.global); + if (options.genParser || options.genGlrParser) { + kread.createBisonParser( + parsingMod, + sort, + options.bisonOutputFile(), + options.genGlrParser, + options.bisonFile, + options.bisonStackMaxDepth, + false); + try { + Files.copy( + options.bisonOutputFile().toPath(), + files.get().resolveKompiled("parser_" + sort.name() + "_" + options.module).toPath(), + StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { } + } else { + Reader stringToParse = options.stringToParse(); + Source source = options.source(); + if (options.debugTokens) + System.out.println( + kread.showTokens(parsingMod, def, FileUtil.read(stringToParse), source)); + else { + K parsed = + kread.prettyRead( + parsingMod, + sort, + startSymbolLocation, + def, + source, + FileUtil.read(stringToParse), + options.debugParse); + + if (options.expandMacros) { + parsed = + ExpandMacros.forNonSentences(unparsingMod, files.get(), def.kompileOptions, false) + .expand(parsed); + } + + if (sort.equals(Sorts.K())) { + sort = Sorts.KItem(); + } + + kprint + .get() + .prettyPrint( + def.kompiledDefinition, + unparsingMod, + s -> kprint.get().outputFile(s), + parsed, + sort); + } + } + + sw.printTotal("Total"); + return 0; + } finally { + scope.exit(); } + } } diff --git a/kernel/src/main/java/org/kframework/kast/KastModule.java b/kernel/src/main/java/org/kframework/kast/KastModule.java index a28605ce26d..4e34309e370 100644 --- a/kernel/src/main/java/org/kframework/kast/KastModule.java +++ b/kernel/src/main/java/org/kframework/kast/KastModule.java @@ -1,6 +1,9 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.kast; +import com.google.inject.AbstractModule; +import com.google.inject.Provides; +import com.google.inject.multibindings.Multibinder; import org.kframework.kil.loader.Context; import org.kframework.main.FrontEnd; import org.kframework.main.GlobalOptions; @@ -12,39 +15,37 @@ import org.kframework.utils.inject.RequestScoped; import org.kframework.utils.options.DefinitionLoadingOptions; -import com.google.inject.AbstractModule; -import com.google.inject.Provides; -import com.google.inject.TypeLiteral; -import com.google.inject.multibindings.Multibinder; - public class KastModule extends AbstractModule { - @Override - public void configure() { - binder().requireAtInjectOnConstructors(); - bind(FrontEnd.class).to(KastFrontEnd.class); - bind(Tool.class).toInstance(Tool.KAST); - - install(new DefinitionLoadingModule()); - - bind(Context.class).annotatedWith(Main.class).to(Context.class); - - Multibinder optionsBinder = Multibinder.newSetBinder(binder(), Object.class, Options.class); - optionsBinder.addBinding().to(KastOptions.class); - } - - @Provides @RequestScoped - GlobalOptions globalOptions(KastOptions options) { - return options.global; - } - - @Provides @RequestScoped - PrintOptions printOptions(KastOptions options) { - return options.print; - } - - @Provides - DefinitionLoadingOptions defLoadingOptions(KastOptions options) { - return options.definitionLoading; - } + @Override + public void configure() { + binder().requireAtInjectOnConstructors(); + bind(FrontEnd.class).to(KastFrontEnd.class); + bind(Tool.class).toInstance(Tool.KAST); + + install(new DefinitionLoadingModule()); + + bind(Context.class).annotatedWith(Main.class).to(Context.class); + + Multibinder optionsBinder = + Multibinder.newSetBinder(binder(), Object.class, Options.class); + optionsBinder.addBinding().to(KastOptions.class); + } + + @Provides + @RequestScoped + GlobalOptions globalOptions(KastOptions options) { + return options.global; + } + + @Provides + @RequestScoped + PrintOptions printOptions(KastOptions options) { + return options.print; + } + + @Provides + DefinitionLoadingOptions defLoadingOptions(KastOptions options) { + return options.definitionLoading; + } } diff --git a/kernel/src/main/java/org/kframework/kast/KastOptions.java b/kernel/src/main/java/org/kframework/kast/KastOptions.java index aa63d6d8078..2ac46ad9274 100644 --- a/kernel/src/main/java/org/kframework/kast/KastOptions.java +++ b/kernel/src/main/java/org/kframework/kast/KastOptions.java @@ -7,11 +7,17 @@ import com.google.common.collect.Lists; import com.google.inject.Inject; import com.google.inject.Provider; +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import java.util.List; import org.kframework.attributes.Source; import org.kframework.kore.Sort; import org.kframework.main.GlobalOptions; -import org.kframework.parser.outer.Outer; import org.kframework.parser.InputModes; +import org.kframework.parser.outer.Outer; import org.kframework.unparser.OutputModes; import org.kframework.unparser.PrintOptions; import org.kframework.utils.errorsystem.KEMException; @@ -20,148 +26,175 @@ import org.kframework.utils.options.BaseEnumConverter; import org.kframework.utils.options.DefinitionLoadingOptions; -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.io.File; -import java.io.Reader; -import java.io.StringReader; -import java.util.Collections; -import java.util.List; - @RequestScoped public final class KastOptions { - @Inject - public KastOptions() {} - - @Parameter(description="") - private List parameters; - - public Reader stringToParse() { - checkFileExprExclusion(); - if (expression != null) { - return new StringReader(expression); - } - checkSingleFile(); - if (parameters.get(0).equals("-")) { - return new BufferedReader(new InputStreamReader(System.in)); - } - return files.get().readFromWorkingDirectory(parameters.get(0)); - } + @Inject + public KastOptions() {} - private void checkFileExprExclusion() { - if (parameters != null && parameters.size() > 0 && expression != null) { - throw KEMException.criticalError("It is an error to provide both a file and an expression to parse."); - } - } + @Parameter(description = "") + private List parameters; - private void checkSingleFile() { - if (parameters != null && parameters.size() > 1) { - throw KEMException.criticalError("You can only parse one program at a time."); - } - if (parameters == null || parameters.size() != 1) { - throw KEMException.criticalError("You have to provide a file in order to kast a program."); - } + public Reader stringToParse() { + checkFileExprExclusion(); + if (expression != null) { + return new StringReader(expression); } - - private Provider files; - - @Inject - public void setFiles(Provider files) { - this.files = files; + checkSingleFile(); + if (parameters.get(0).equals("-")) { + return new BufferedReader(new InputStreamReader(System.in)); } + return files.get().readFromWorkingDirectory(parameters.get(0)); + } - /** - * Get the source of the string to parse. This method is undefined if it is called before calling - * {@link #stringToParse()}. - * @return A textual description of the source of the string to parse. - */ - public Source source() { - if (expression != null) { - return Source.apply(""); - } else { - return Source.apply(files.get().resolveWorkingDirectory(parameters.get(0)).getAbsolutePath()); - } + private void checkFileExprExclusion() { + if (parameters != null && parameters.size() > 0 && expression != null) { + throw KEMException.criticalError( + "It is an error to provide both a file and an expression to parse."); } + } - @ParametersDelegate - public transient GlobalOptions global = new GlobalOptions(); - - @ParametersDelegate - public DefinitionLoadingOptions definitionLoading = new DefinitionLoadingOptions(); - - @ParametersDelegate - public PrintOptions print = new PrintOptions(OutputModes.KAST); - - @Parameter(names={"--gen-parser"}, description="Generate a Bison/Flex parser for the specified module and sort.") - public boolean genParser; - - @Parameter(names={"--gen-glr-parser"}, description="Generate a Bison/Flex GLR parser for the specified module and sort.") - public boolean genGlrParser; - - public File bisonOutputFile() { - checkSingleFile(); - return files.get().resolveWorkingDirectory(parameters.get(0)); + private void checkSingleFile() { + if (parameters != null && parameters.size() > 1) { + throw KEMException.criticalError("You can only parse one program at a time."); } - - @Parameter(names="--bison-file", descriptionKey = "file", hidden = true, - description="C file containing functions to link into bison parser.") - public String bisonFile; - - @Parameter(names="--bison-stack-max-depth", descriptionKey = "size", hidden = true, - description="Maximum size of bison parsing stack.") - public long bisonStackMaxDepth = 10000; - - @Parameter(names={"--expression", "-e"}, descriptionKey = "expression", - description="An expression to parse passed on the command line. It is an error to provide both this " + - "option and a file to parse.") - private String expression; - - @Parameter(names={"--sort", "-s"}, descriptionKey = "sort", converter=SortTypeConverter.class, - description="The start sort for the default parser. The default is the sort of $PGM from the configuration. " + - "A sort may also be specified with the 'KRUN_SORT' environment variable, in which case it is used " + - "if the option is not specified on the command line.") - public Sort sort; - - public static class SortTypeConverter implements IStringConverter { - // converts the command line argument into a Sort - @Override - public Sort convert(String arg) { - return Outer.parseSort(arg); - } + if (parameters == null || parameters.size() != 1) { + throw KEMException.criticalError("You have to provide a file in order to kast a program."); } - - @Parameter(names={"--module", "-m"}, descriptionKey = "name", - description="Parse text in the specified module. Defaults to the syntax module of the definition.") - public String module; - - @Parameter(names="--expand-macros", description="Also expand macros in the parsed string.") - public boolean expandMacros = false; - - @Parameter(names={"--input", "-i"}, descriptionKey = "mode", converter=InputModeConverter.class, - description="How to read kast input in. is either [program|binary|kast|json|kore|rule].") - public InputModes input = InputModes.PROGRAM; - - @Parameter(names={"--steps"}, descriptionKey = "steps", - description="Apply specified kompilation steps to the parsed term. Only for --input rule. " + - "Use --steps help for a detailed description of available steps.") - public List steps = Lists.newArrayList(KastFrontEnd.KompileSteps.anonVars); - - public static class InputModeConverter extends BaseEnumConverter { - - public InputModeConverter(String optionName) { - super(optionName); - } - - @Override - public Class enumClass() { - return InputModes.class; - } + } + + private Provider files; + + @Inject + public void setFiles(Provider files) { + this.files = files; + } + + /** + * Get the source of the string to parse. This method is undefined if it is called before calling + * {@link #stringToParse()}. + * + * @return A textual description of the source of the string to parse. + */ + public Source source() { + if (expression != null) { + return Source.apply(""); + } else { + return Source.apply(files.get().resolveWorkingDirectory(parameters.get(0)).getAbsolutePath()); + } + } + + @ParametersDelegate public transient GlobalOptions global = new GlobalOptions(); + + @ParametersDelegate + public DefinitionLoadingOptions definitionLoading = new DefinitionLoadingOptions(); + + @ParametersDelegate public PrintOptions print = new PrintOptions(OutputModes.KAST); + + @Parameter( + names = {"--gen-parser"}, + description = "Generate a Bison/Flex parser for the specified module and sort.") + public boolean genParser; + + @Parameter( + names = {"--gen-glr-parser"}, + description = "Generate a Bison/Flex GLR parser for the specified module and sort.") + public boolean genGlrParser; + + public File bisonOutputFile() { + checkSingleFile(); + return files.get().resolveWorkingDirectory(parameters.get(0)); + } + + @Parameter( + names = "--bison-file", + descriptionKey = "file", + hidden = true, + description = "C file containing functions to link into bison parser.") + public String bisonFile; + + @Parameter( + names = "--bison-stack-max-depth", + descriptionKey = "size", + hidden = true, + description = "Maximum size of bison parsing stack.") + public long bisonStackMaxDepth = 10000; + + @Parameter( + names = {"--expression", "-e"}, + descriptionKey = "expression", + description = + "An expression to parse passed on the command line. It is an error to provide both this " + + "option and a file to parse.") + private String expression; + + @Parameter( + names = {"--sort", "-s"}, + descriptionKey = "sort", + converter = SortTypeConverter.class, + description = + "The start sort for the default parser. The default is the sort of $PGM from the" + + " configuration. A sort may also be specified with the 'KRUN_SORT' environment" + + " variable, in which case it is used if the option is not specified on the command" + + " line.") + public Sort sort; + + public static class SortTypeConverter implements IStringConverter { + // converts the command line argument into a Sort + @Override + public Sort convert(String arg) { + return Outer.parseSort(arg); + } + } + + @Parameter( + names = {"--module", "-m"}, + descriptionKey = "name", + description = + "Parse text in the specified module. Defaults to the syntax module of the definition.") + public String module; + + @Parameter(names = "--expand-macros", description = "Also expand macros in the parsed string.") + public boolean expandMacros = false; + + @Parameter( + names = {"--input", "-i"}, + descriptionKey = "mode", + converter = InputModeConverter.class, + description = + "How to read kast input in. is either [program|binary|kast|json|kore|rule].") + public InputModes input = InputModes.PROGRAM; + + @Parameter( + names = {"--steps"}, + descriptionKey = "steps", + description = + "Apply specified kompilation steps to the parsed term. Only for --input rule. " + + "Use --steps help for a detailed description of available steps.") + public List steps = + Lists.newArrayList(KastFrontEnd.KompileSteps.anonVars); + + public static class InputModeConverter extends BaseEnumConverter { + + public InputModeConverter(String optionName) { + super(optionName); } - @Parameter(names="--debug-tokens", description="Print a Markdown table of tokens matched by the scanner. Useful for debugging parsing errors.") - public boolean debugTokens = false; - - @Parameter(names="--debug-parse", description="Print extended diagnostic information and partial ASTs for parse errors.") - public boolean debugParse = false; + @Override + public Class enumClass() { + return InputModes.class; + } + } + + @Parameter( + names = "--debug-tokens", + description = + "Print a Markdown table of tokens matched by the scanner. Useful for debugging parsing" + + " errors.") + public boolean debugTokens = false; + + @Parameter( + names = "--debug-parse", + description = "Print extended diagnostic information and partial ASTs for parse errors.") + public boolean debugParse = false; } diff --git a/kernel/src/main/java/org/kframework/kdep/KDepFrontEnd.java b/kernel/src/main/java/org/kframework/kdep/KDepFrontEnd.java index 8a7c15c3220..212422636d1 100644 --- a/kernel/src/main/java/org/kframework/kdep/KDepFrontEnd.java +++ b/kernel/src/main/java/org/kframework/kdep/KDepFrontEnd.java @@ -1,10 +1,17 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.kdep; -import com.google.inject.Provider; import com.google.common.collect.Lists; import com.google.inject.Inject; import com.google.inject.Module; +import com.google.inject.Provider; +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import org.apache.commons.collections4.ListUtils; import org.kframework.attributes.Source; import org.kframework.kompile.Kompile; @@ -20,23 +27,17 @@ import org.kframework.utils.inject.JCommanderModule.Usage; import org.kframework.utils.options.OuterParsingOptions; -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - /** * Frontend for kdep tool. + * + *

kdep is designed to generate a Makefile that contains the dependencies that kompile has on + * files when you run it. This can be used in order to ensure that if any of the files required by a + * k definition are changed, the makefile will rerun kompile. + * + *

Example Makefile snippet: + * *

- * kdep is designed to generate a Makefile that contains the dependencies - * that kompile has on files when you run it. This can be used in order to ensure that if any - * of the files required by a k definition are changed, the makefile will rerun kompile. - *

- * Example Makefile snippet: - *

+ * *

  *     .depend:
  *             kdep definition.k --output-directory "kompiled directory" -I includes > .depend
@@ -46,89 +47,106 @@
  */
 public class KDepFrontEnd extends FrontEnd {
 
-    private final KDepOptions kdepOptions;
-    private final OuterParsingOptions options;
-    private final KExceptionManager kem;
-    private final Stopwatch sw;
-    private final Provider files;
-    private final GlobalOptions globalOptions;
+  private final KDepOptions kdepOptions;
+  private final OuterParsingOptions options;
+  private final KExceptionManager kem;
+  private final Stopwatch sw;
+  private final Provider files;
+  private final GlobalOptions globalOptions;
 
-    @Inject
-    public KDepFrontEnd(
-            KDepOptions kdepOptions,
-            OuterParsingOptions options,
-            KExceptionManager kem,
-            GlobalOptions globalOptions,
-            @Usage String usage,
-            Stopwatch sw,
-            JarInfo jarInfo,
-            Provider files) {
-        super(kem, globalOptions, usage, jarInfo, files);
-        this.kdepOptions = kdepOptions;
-        this.options = options;
-        this.globalOptions = globalOptions;
-        this.kem = kem;
-        this.sw = sw;
-        this.files = files;
-    }
+  @Inject
+  public KDepFrontEnd(
+      KDepOptions kdepOptions,
+      OuterParsingOptions options,
+      KExceptionManager kem,
+      GlobalOptions globalOptions,
+      @Usage String usage,
+      Stopwatch sw,
+      JarInfo jarInfo,
+      Provider files) {
+    super(kem, globalOptions, usage, jarInfo, files);
+    this.kdepOptions = kdepOptions;
+    this.options = options;
+    this.globalOptions = globalOptions;
+    this.kem = kem;
+    this.sw = sw;
+    this.files = files;
+  }
 
-    public static List getModules() {
-        List modules = new ArrayList<>();
-        modules.add(new KDepModule());
-        modules.add(new JCommanderModule());
-        modules.add(new CommonModule());
-        return modules;
-    }
+  public static List getModules() {
+    List modules = new ArrayList<>();
+    modules.add(new KDepModule());
+    modules.add(new JCommanderModule());
+    modules.add(new CommonModule());
+    return modules;
+  }
 
-    @Override
-    protected int run() {
-        ParserUtils parser = new ParserUtils(files.get(), kem, globalOptions, options);
-        List modules = new ArrayList<>();
-        Source source = Source.apply(options.mainDefinitionFile(files.get()).getAbsolutePath());
-        File currentDirectory = options.mainDefinitionFile(files.get()).getParentFile();
-        List lookupDirectories = ListUtils.union(options.includes.stream()
-                        .map(files.get()::resolveWorkingDirectory).collect(Collectors.toList()),
-                Lists.newArrayList(Kompile.BUILTIN_DIRECTORY));
+  @Override
+  protected int run() {
+    ParserUtils parser = new ParserUtils(files.get(), kem, globalOptions, options);
+    List modules = new ArrayList<>();
+    Source source = Source.apply(options.mainDefinitionFile(files.get()).getAbsolutePath());
+    File currentDirectory = options.mainDefinitionFile(files.get()).getParentFile();
+    List lookupDirectories =
+        ListUtils.union(
+            options.includes.stream()
+                .map(files.get()::resolveWorkingDirectory)
+                .collect(Collectors.toList()),
+            Lists.newArrayList(Kompile.BUILTIN_DIRECTORY));
 
-        Set requiredFiles = new HashSet<>();
-        // load builtin files if needed first
-        if (!options.noPrelude) {
-            modules.addAll(parser.slurp(Kompile.REQUIRE_PRELUDE_K,
-                    source,
-                    currentDirectory,
-                    lookupDirectories,
-                    requiredFiles));
-        }
+    Set requiredFiles = new HashSet<>();
+    // load builtin files if needed first
+    if (!options.noPrelude) {
+      modules.addAll(
+          parser.slurp(
+              Kompile.REQUIRE_PRELUDE_K,
+              source,
+              currentDirectory,
+              lookupDirectories,
+              requiredFiles));
+    }
 
-        modules.addAll(parser.slurp(FileUtil.load(options.mainDefinitionFile(files.get())),
-                source,
-                currentDirectory,
-                lookupDirectories,
-                requiredFiles));
-        Set allFiles = modules.stream().map(m -> new File(m.getSource().source())).collect(Collectors.toSet());
-        System.out.println(files.get().resolveWorkingDirectory(".").toURI().relativize(files.get().resolveKompiled("timestamp").toURI()).getPath() + " : \\");
+    modules.addAll(
+        parser.slurp(
+            FileUtil.load(options.mainDefinitionFile(files.get())),
+            source,
+            currentDirectory,
+            lookupDirectories,
+            requiredFiles));
+    Set allFiles =
+        modules.stream().map(m -> new File(m.getSource().source())).collect(Collectors.toSet());
+    System.out.println(
+        files
+                .get()
+                .resolveWorkingDirectory(".")
+                .toURI()
+                .relativize(files.get().resolveKompiled("timestamp").toURI())
+                .getPath()
+            + " : \\");
 
-        List sortedFiles = new ArrayList(allFiles);
-        Collections.sort(sortedFiles, (File a, File b) -> {
+    List sortedFiles = new ArrayList(allFiles);
+    Collections.sort(
+        sortedFiles,
+        (File a, File b) -> {
           return a.getAbsolutePath().compareTo(b.getAbsolutePath());
         });
 
-        for (File file : sortedFiles) {
-            System.out.println("    " + file.getAbsolutePath() + " \\");
-        }
-        System.out.println();
+    for (File file : sortedFiles) {
+      System.out.println("    " + file.getAbsolutePath() + " \\");
+    }
+    System.out.println();
 
-        if (this.kdepOptions.alsoRemakeDepend) {
-            System.out.println("DEPEND_FILE=$(lastword $(MAKEFILE_LIST))");
-            System.out.println("$(DEPEND_FILE) : " + " \\");
-            System.out.println("    $(wildcard \\");
+    if (this.kdepOptions.alsoRemakeDepend) {
+      System.out.println("DEPEND_FILE=$(lastword $(MAKEFILE_LIST))");
+      System.out.println("$(DEPEND_FILE) : " + " \\");
+      System.out.println("    $(wildcard \\");
 
-            for (File file : sortedFiles) {
-                System.out.println("        " + file.getAbsolutePath() + " \\");
-            }
+      for (File file : sortedFiles) {
+        System.out.println("        " + file.getAbsolutePath() + " \\");
+      }
 
-            System.out.println("    )");
-        }
-        return 0;
+      System.out.println("    )");
     }
+    return 0;
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kdep/KDepModule.java b/kernel/src/main/java/org/kframework/kdep/KDepModule.java
index ed6af4762b9..516e989a87a 100644
--- a/kernel/src/main/java/org/kframework/kdep/KDepModule.java
+++ b/kernel/src/main/java/org/kframework/kdep/KDepModule.java
@@ -3,9 +3,7 @@
 
 import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
-import com.google.inject.TypeLiteral;
 import com.google.inject.multibindings.Multibinder;
-import org.kframework.kompile.KompileOptions;
 import org.kframework.main.FrontEnd;
 import org.kframework.main.GlobalOptions;
 import org.kframework.main.Tool;
@@ -16,30 +14,36 @@
 import org.kframework.utils.options.OutputDirectoryOptions;
 
 /**
- * Guice module for kdep tool. Binds the information needed to compute the kompiled directory as well as the options
- * and frontend.
+ * Guice module for kdep tool. Binds the information needed to compute the kompiled directory as
+ * well as the options and frontend.
  */
 public class KDepModule extends AbstractModule {
-    @Override
-    protected void configure() {
-        binder().requireAtInjectOnConstructors();
-        bind(FrontEnd.class).to(KDepFrontEnd.class);
-        bind(Tool.class).toInstance(Tool.KDEP);
+  @Override
+  protected void configure() {
+    binder().requireAtInjectOnConstructors();
+    bind(FrontEnd.class).to(KDepFrontEnd.class);
+    bind(Tool.class).toInstance(Tool.KDEP);
 
-        install(new OuterParsingModule());
+    install(new OuterParsingModule());
 
-        Multibinder optionsBinder = Multibinder.newSetBinder(binder(), Object.class, Options.class);
-        optionsBinder.addBinding().to(KDepOptions.class);
-    }
+    Multibinder optionsBinder =
+        Multibinder.newSetBinder(binder(), Object.class, Options.class);
+    optionsBinder.addBinding().to(KDepOptions.class);
+  }
 
-    @Provides @RequestScoped
-    GlobalOptions globalOptions(KDepOptions options) {
-        return options.global;
-    }
+  @Provides
+  @RequestScoped
+  GlobalOptions globalOptions(KDepOptions options) {
+    return options.global;
+  }
 
-    @Provides
-    OuterParsingOptions outerParsingOptions(KDepOptions options) { return options.outerParsing; }
+  @Provides
+  OuterParsingOptions outerParsingOptions(KDepOptions options) {
+    return options.outerParsing;
+  }
 
-    @Provides
-    OutputDirectoryOptions outputDirectoryOptions(KDepOptions options) { return options.outputDirectory; }
+  @Provides
+  OutputDirectoryOptions outputDirectoryOptions(KDepOptions options) {
+    return options.outputDirectory;
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kdep/KDepOptions.java b/kernel/src/main/java/org/kframework/kdep/KDepOptions.java
index c65cc98273e..b0129fefc5c 100644
--- a/kernel/src/main/java/org/kframework/kdep/KDepOptions.java
+++ b/kernel/src/main/java/org/kframework/kdep/KDepOptions.java
@@ -10,25 +10,23 @@
 import org.kframework.utils.options.OutputDirectoryOptions;
 
 /**
- * JCommander options for kdep. Essentially, this should contain all the kompile options needed in order to decide what
- * files get slurped by the outer parser.
+ * JCommander options for kdep. Essentially, this should contain all the kompile options needed in
+ * order to decide what files get slurped by the outer parser.
  */
-
 @RequestScoped
 public class KDepOptions {
 
-    @Inject
-    public KDepOptions() {}
+  @Inject
+  public KDepOptions() {}
 
-    @ParametersDelegate
-    public transient GlobalOptions global = new GlobalOptions();
+  @ParametersDelegate public transient GlobalOptions global = new GlobalOptions();
 
-    @ParametersDelegate
-    public OuterParsingOptions outerParsing = new OuterParsingOptions();
+  @ParametersDelegate public OuterParsingOptions outerParsing = new OuterParsingOptions();
 
-    @ParametersDelegate
-    public OutputDirectoryOptions outputDirectory = new OutputDirectoryOptions();
+  @ParametersDelegate public OutputDirectoryOptions outputDirectory = new OutputDirectoryOptions();
 
-    @Parameter(names="--remake-depend", description="Generate an additional rule to remake the dependency file.")
-    public boolean alsoRemakeDepend = false;
+  @Parameter(
+      names = "--remake-depend",
+      description = "Generate an additional rule to remake the dependency file.")
+  public boolean alsoRemakeDepend = false;
 }
diff --git a/kernel/src/main/java/org/kframework/kil/ASTNode.java b/kernel/src/main/java/org/kframework/kil/ASTNode.java
index 5df3e9498ae..228f815809a 100644
--- a/kernel/src/main/java/org/kframework/kil/ASTNode.java
+++ b/kernel/src/main/java/org/kframework/kil/ASTNode.java
@@ -1,165 +1,165 @@
 // Copyright (c) K Team. All Rights Reserved.
 package org.kframework.kil;
 
+import java.io.Serializable;
+import java.util.Optional;
 import org.kframework.attributes.Att;
 import org.kframework.attributes.HasLocation;
 import org.kframework.attributes.Location;
 import org.kframework.attributes.Source;
 import org.kframework.utils.errorsystem.KEMException;
 
-import java.io.Serializable;
-import java.util.Optional;
-
-/**
- * Base class for K AST. Useful for Visitors and Transformers.
- */
+/** Base class for K AST. Useful for Visitors and Transformers. */
 public abstract class ASTNode implements Serializable, HasLocation {
-    /**
-     *
-     */
-    private static final long serialVersionUID = 1L;
-    /**
-     * Used on any node for metadata also used on {@link Rule} and {@link Production} for the attribute list.
-     */
-    private Att att = Att.empty();
-
-    private Source source;
-    private Location location;
-
-    /**
-     * Default constructor (generated at runtime)
-     */
-    public ASTNode() {
-        this(null, null);
-    }
-
-    /**
-     * Constructor with specified location and filename.
-     *
-     * @param loc
-     * @param file
-     */
-    public ASTNode(Location loc, Source source) {
-        setLocation(loc);
-        setSource(source);
-    }
-
-    /**
-     * Retrieves the location of the current ASTNode.
-     *
-     * @return recorded location or null if no recorded location found.
-     */
-    public Location getLocation() {
-        return location;
-    }
-
-    /**
-     * Sets the location or removes it if appropriate.
-     *
-     * @param loc
-     */
-    public void setLocation(Location location) {
-        this.location = location;
-    }
-
-    /**
-     * Retrieves the source of the current ASTNode.
-     *
-     * @return recorded source or null if no recorded source found.
-     */
-    public Source getSource() {
-        return source;
-    }
-
-    /**
-     * Sets the source or removes it if appropriate.
-     *
-     * @param file
-     */
-    public void setSource(Source source) {
-        this.source = source;
+  /** */
+  private static final long serialVersionUID = 1L;
+
+  /**
+   * Used on any node for metadata also used on {@link Rule} and {@link Production} for the
+   * attribute list.
+   */
+  private Att att = Att.empty();
+
+  private Source source;
+  private Location location;
+
+  /** Default constructor (generated at runtime) */
+  public ASTNode() {
+    this(null, null);
+  }
+
+  /**
+   * Constructor with specified location and filename.
+   *
+   * @param loc
+   * @param file
+   */
+  public ASTNode(Location loc, Source source) {
+    setLocation(loc);
+    setSource(source);
+  }
+
+  /**
+   * Retrieves the location of the current ASTNode.
+   *
+   * @return recorded location or null if no recorded location found.
+   */
+  public Location getLocation() {
+    return location;
+  }
+
+  /**
+   * Sets the location or removes it if appropriate.
+   *
+   * @param loc
+   */
+  public void setLocation(Location location) {
+    this.location = location;
+  }
+
+  /**
+   * Retrieves the source of the current ASTNode.
+   *
+   * @return recorded source or null if no recorded source found.
+   */
+  public Source getSource() {
+    return source;
+  }
+
+  /**
+   * Sets the source or removes it if appropriate.
+   *
+   * @param file
+   */
+  public void setSource(Source source) {
+    this.source = source;
+  }
+
+  public Optional location() {
+    return Optional.ofNullable(location);
+  }
+
+  public Optional source() {
+    return Optional.ofNullable(source);
+  }
+
+  /*
+   * methods for easy attributes manipulation
+   */
+
+  /**
+   * Append an attribute to the list of attributes. In particular, - inserting a key from the
+   * attribute whitelist if the attribute is recognized as a built-in - add the source location to
+   * any exceptions (ie. parameter restrictions) thrown when inserting the key - otherwise,
+   * inserting an unrecognized key to be errored on later
+   *
+   * @param key
+   * @param val
+   */
+  public void addBuiltInOrUnrecognizedAttribute(
+      String key, String val, Source source, Location loc) {
+    Att.Key attKey = Att.getBuiltinKeyOptional(key).orElse(Att.unrecognizedKey(key));
+    if (att.contains(attKey)) {
+      throw KEMException.outerParserError("Duplicate attribute: " + key, source, loc);
     }
-
-    public Optional location() { return Optional.ofNullable(location); }
-    public Optional source() { return Optional.ofNullable(source); }
-
-    /*
-     * methods for easy attributes manipulation
-     */
-
-    /**
-     * Append an attribute to the list of attributes. In particular,
-     * - inserting a key from the attribute whitelist if the attribute is recognized as a built-in
-     * - add the source location to any exceptions (ie. parameter restrictions) thrown when inserting the key
-     * - otherwise, inserting an unrecognized key to be errored on later
-     *
-     * @param key
-     * @param val
-     */
-    public void addBuiltInOrUnrecognizedAttribute(String key, String val, Source source, Location loc) {
-        Att.Key attKey = Att.getBuiltinKeyOptional(key).orElse(Att.unrecognizedKey(key));
-        if (att.contains(attKey)) {
-            throw KEMException.outerParserError("Duplicate attribute: " + key, source, loc);
-        }
-        try {
-            att = att.add(attKey, val);
-        } catch (KEMException e) {
-            throw e.withLocation(loc, source);
-        }
-    }
-
-    /**
-     * Appends an attribute to the list of attributes.
-     *
-     * @param key
-     * @param val
-     */
-    public void addAttribute(Att.Key key, String val) {
-        att = att.add(key, val);
-    }
-
-    /**
-     * @param key
-     * @return whether the attribute key occurs in the list of attributes.
-     */
-    public boolean containsAttribute(Att.Key key) {
-        return att.contains(key);
-    }
-
-
-    /**
-     * Retrieves the attribute by key from the list of attributes
-     *
-     * @param key
-     * @return a value for key in the list of attributes or the default value.
-     */
-    public String getAttribute(Att.Key key) {
-        return att.getOptional(key).orElse(null);
-    }
-
-    /**
-     * @return the attributes object associated to this ASTNode. Constructs one if it is
-     * not already created.
-     */
-    public Att getAttributes() {
-        return att;
-    }
-
-    /**
-     * Sets the attributes to the provided Att
-     *
-     * @param att - the new attributes
-     */
-    public void setAttributes(Att att) {
-        this.att = att;
-    }
-
-    public abstract void toString(StringBuilder sb);
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        toString(sb);
-        return sb.toString();
+    try {
+      att = att.add(attKey, val);
+    } catch (KEMException e) {
+      throw e.withLocation(loc, source);
     }
+  }
+
+  /**
+   * Appends an attribute to the list of attributes.
+   *
+   * @param key
+   * @param val
+   */
+  public void addAttribute(Att.Key key, String val) {
+    att = att.add(key, val);
+  }
+
+  /**
+   * @param key
+   * @return whether the attribute key occurs in the list of attributes.
+   */
+  public boolean containsAttribute(Att.Key key) {
+    return att.contains(key);
+  }
+
+  /**
+   * Retrieves the attribute by key from the list of attributes
+   *
+   * @param key
+   * @return a value for key in the list of attributes or the default value.
+   */
+  public String getAttribute(Att.Key key) {
+    return att.getOptional(key).orElse(null);
+  }
+
+  /**
+   * @return the attributes object associated to this ASTNode. Constructs one if it is not already
+   *     created.
+   */
+  public Att getAttributes() {
+    return att;
+  }
+
+  /**
+   * Sets the attributes to the provided Att
+   *
+   * @param att - the new attributes
+   */
+  public void setAttributes(Att att) {
+    this.att = att;
+  }
+
+  public abstract void toString(StringBuilder sb);
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    toString(sb);
+    return sb.toString();
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/Definition.java b/kernel/src/main/java/org/kframework/kil/Definition.java
index 4157a5f8959..9fc3b977c3d 100644
--- a/kernel/src/main/java/org/kframework/kil/Definition.java
+++ b/kernel/src/main/java/org/kframework/kil/Definition.java
@@ -2,62 +2,59 @@
 package org.kframework.kil;
 
 import com.google.inject.Inject;
-
 import java.io.File;
-import java.util.ArrayList;
-import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-
 /**
- * Represents a language definition.
- * Includes contents from all {@code required}-d files.
+ * Represents a language definition. Includes contents from all {@code required}-d files.
+ *
  * @see DefinitionLoader
  */
 public class Definition extends ASTNode {
 
-    private List items;
-    private File mainFile;
-    private String mainModule;
-    /** An index of all modules in {@link #items} by name */
-    private String mainSyntaxModule;
-    public Map locations = new HashMap<>();
+  private List items;
+  private File mainFile;
+  private String mainModule;
 
-    public Definition() {
-        super();
-    }
+  /** An index of all modules in {@link #items} by name */
+  private String mainSyntaxModule;
 
-    @Inject
-    public Definition(Void v) {}
+  public Map locations = new HashMap<>();
 
-    @Override
-    public void toString(StringBuilder sb) {
-        for (DefinitionItem di : items) {
-            di.toString(sb);
-            sb.append("\n\n");
-        }
-    }
+  public Definition() {
+    super();
+  }
 
-    public void setItems(List items) {
-        this.items = items;
-    }
+  @Inject
+  public Definition(Void v) {}
 
-    public List getItems() {
-        return items;
+  @Override
+  public void toString(StringBuilder sb) {
+    for (DefinitionItem di : items) {
+      di.toString(sb);
+      sb.append("\n\n");
     }
+  }
 
-    public void setMainModule(String mainModule) {
-        this.mainModule = mainModule;
-    }
+  public void setItems(List items) {
+    this.items = items;
+  }
 
-    public String getMainModule() {
-        return mainModule;
-    }
+  public List getItems() {
+    return items;
+  }
 
-    public void setMainSyntaxModule(String mainSyntaxModule) {
-        this.mainSyntaxModule = mainSyntaxModule;
-    }
+  public void setMainModule(String mainModule) {
+    this.mainModule = mainModule;
+  }
+
+  public String getMainModule() {
+    return mainModule;
+  }
 
+  public void setMainSyntaxModule(String mainSyntaxModule) {
+    this.mainSyntaxModule = mainSyntaxModule;
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/DefinitionItem.java b/kernel/src/main/java/org/kframework/kil/DefinitionItem.java
index 176f86298ae..ecfa04ddc02 100644
--- a/kernel/src/main/java/org/kframework/kil/DefinitionItem.java
+++ b/kernel/src/main/java/org/kframework/kil/DefinitionItem.java
@@ -3,18 +3,18 @@
 
 public abstract class DefinitionItem extends ASTNode {
 
-    /** set iff the item was read from a file in the standard libraries */
-    private boolean predefined;
+  /** set iff the item was read from a file in the standard libraries */
+  private boolean predefined;
 
-    public DefinitionItem() {
-        super();
-    }
+  public DefinitionItem() {
+    super();
+  }
 
-    public void setPredefined(boolean predefined) {
-        this.predefined = predefined;
-    }
+  public void setPredefined(boolean predefined) {
+    this.predefined = predefined;
+  }
 
-    public boolean isPredefined() {
-        return predefined;
-    }
+  public boolean isPredefined() {
+    return predefined;
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/Import.java b/kernel/src/main/java/org/kframework/kil/Import.java
index 09756eb5c3e..83950d1a609 100644
--- a/kernel/src/main/java/org/kframework/kil/Import.java
+++ b/kernel/src/main/java/org/kframework/kil/Import.java
@@ -7,45 +7,45 @@
 /** An import directive */
 public class Import extends ModuleItem {
 
-    String name;
-    boolean isPublic;
-
-    public Import(String importName, boolean isPublic) {
-        super();
-        name = importName;
-        this.isPublic = isPublic;
+  String name;
+  boolean isPublic;
+
+  public Import(String importName, boolean isPublic) {
+    super();
+    name = importName;
+    this.isPublic = isPublic;
+  }
+
+  public Import(String importName, boolean isPublic, Location loc, Source source) {
+    super(loc, source);
+    this.name = importName;
+    this.isPublic = isPublic;
+  }
+
+  @Override
+  public void toString(StringBuilder sb) {
+    sb.append("  imports ");
+    if (!isPublic) {
+      sb.append("private ");
+    } else {
+      sb.append("public ");
     }
+    sb.append(name);
+  }
 
-    public Import(String importName, boolean isPublic, Location loc, Source source) {
-        super(loc, source);
-        this.name = importName;
-        this.isPublic = isPublic;
-    }
+  public String getName() {
+    return name;
+  }
 
-    @Override
-    public void toString(StringBuilder sb) {
-        sb.append("  imports ");
-        if (!isPublic) {
-          sb.append("private ");
-        } else {
-          sb.append("public ");
-        }
-        sb.append(name);
-    }
+  public void setName(String name) {
+    this.name = name;
+  }
 
-    public String getName() {
-        return name;
-    }
+  public boolean isPublic() {
+    return isPublic;
+  }
 
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public boolean isPublic() {
-      return isPublic;
-    }
-
-    public void setPublic(boolean isPublic) {
-      this.isPublic = isPublic;
-    }
+  public void setPublic(boolean isPublic) {
+    this.isPublic = isPublic;
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/Lexical.java b/kernel/src/main/java/org/kframework/kil/Lexical.java
index 7c65e35be40..09bc6d485b9 100644
--- a/kernel/src/main/java/org/kframework/kil/Lexical.java
+++ b/kernel/src/main/java/org/kframework/kil/Lexical.java
@@ -6,48 +6,44 @@
 /** A terminal in a {@link Production}. */
 public class Lexical extends ProductionItem {
 
-    private final String lexicalRule;
-    private String follow;
-
-    public Lexical(String terminal, String follow) {
-        super();
-        this.lexicalRule = terminal;
-        this.follow = follow;
-    }
-
-    @Override
-    public void toString(StringBuilder sb) {
-        sb.append("r");
-        sb.append(StringUtil.enquoteKString(lexicalRule));
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null)
-            return false;
-        if (obj == this)
-            return true;
-        if (!(obj instanceof Lexical trm))
-            return false;
-
-        return trm.lexicalRule.equals(this.lexicalRule);
-    }
-
-    @Override
-    public int hashCode() {
-        return this.lexicalRule.hashCode();
-    }
-
-    public void setFollow(String follow) {
-        this.follow = follow;
-    }
-
-    public String getFollow() {
-        return follow;
-    }
-
-    public String getLexicalRule() {
-        return lexicalRule;
-    }
-
+  private final String lexicalRule;
+  private String follow;
+
+  public Lexical(String terminal, String follow) {
+    super();
+    this.lexicalRule = terminal;
+    this.follow = follow;
+  }
+
+  @Override
+  public void toString(StringBuilder sb) {
+    sb.append("r");
+    sb.append(StringUtil.enquoteKString(lexicalRule));
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) return false;
+    if (obj == this) return true;
+    if (!(obj instanceof Lexical trm)) return false;
+
+    return trm.lexicalRule.equals(this.lexicalRule);
+  }
+
+  @Override
+  public int hashCode() {
+    return this.lexicalRule.hashCode();
+  }
+
+  public void setFollow(String follow) {
+    this.follow = follow;
+  }
+
+  public String getFollow() {
+    return follow;
+  }
+
+  public String getLexicalRule() {
+    return lexicalRule;
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/Module.java b/kernel/src/main/java/org/kframework/kil/Module.java
index 7d4ed6f10ae..f618c940950 100644
--- a/kernel/src/main/java/org/kframework/kil/Module.java
+++ b/kernel/src/main/java/org/kframework/kil/Module.java
@@ -1,79 +1,81 @@
 // Copyright (c) K Team. All Rights Reserved.
 package org.kframework.kil;
 
-import org.kframework.kore.Sort;
-
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
-import java.util.Comparator;
 import java.util.List;
 import java.util.Set;
+import org.kframework.kore.Sort;
 
 /** A module. */
 public class Module extends DefinitionItem {
-    private String name;
-    private List items = new ArrayList<>();
+  private String name;
+  private List items = new ArrayList<>();
 
-    // lazily computed set of sorts.
-    private Set sorts;
+  // lazily computed set of sorts.
+  private Set sorts;
 
-    public Module() {
-        super();
-    }
+  public Module() {
+    super();
+  }
 
-    public Module(String name) {
-        super();
-        this.name = name;
-    }
+  public Module(String name) {
+    super();
+    this.name = name;
+  }
 
-    public void appendModuleItem(ModuleItem item) {
-        this.items.add(item);
-        this.sorts = null;
-    }
+  public void appendModuleItem(ModuleItem item) {
+    this.items.add(item);
+    this.sorts = null;
+  }
 
-    public void setName(String name) {
-        this.name = name;
-    }
+  public void setName(String name) {
+    this.name = name;
+  }
 
-    public String getName() {
-        return name;
-    }
+  public String getName() {
+    return name;
+  }
 
-    public void setItems(List items) {
-        this.items = items;
-        this.sorts = null;
-    }
+  public void setItems(List items) {
+    this.items = items;
+    this.sorts = null;
+  }
 
-    public List getItems() {
-        return items;
-    }
+  public List getItems() {
+    return items;
+  }
 
-    @Override
-    public void toString(StringBuilder sb) {
-        sb.append("module ").append(name).append(" ").append(getAttributes().withUserGroupsAsGroupAtt()).append("\n");
-        for (ModuleItem i : items) {
-            i.toString(sb);
-            sb.append("\n");
-        }
-        sb.append("endmodule");
+  @Override
+  public void toString(StringBuilder sb) {
+    sb.append("module ")
+        .append(name)
+        .append(" ")
+        .append(getAttributes().withUserGroupsAsGroupAtt())
+        .append("\n");
+    for (ModuleItem i : items) {
+      i.toString(sb);
+      sb.append("\n");
     }
+    sb.append("endmodule");
+  }
 
-    /**
-     * Used to determine if a module was modified.
-     *  GH #2910
-     * This allows for scripts to move files around after compilation but still give error messages
-     * if the contents of modules differ.
-     */
-    public String digest() {
-        try {
-            StringBuilder mod = new StringBuilder();
-            toString(mod);
-            MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
-            messageDigest.update(mod.toString().getBytes());
-            return new String(messageDigest.digest());
-        } catch (NoSuchAlgorithmException e) {
-            throw new RuntimeException(e);
-        }
+  /**
+   * Used to determine if a module was modified. GH #2910 This allows for
+   * scripts to move files around after compilation but still give error messages if the contents of
+   * modules differ.
+   */
+  public String digest() {
+    try {
+      StringBuilder mod = new StringBuilder();
+      toString(mod);
+      MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
+      messageDigest.update(mod.toString().getBytes());
+      return new String(messageDigest.digest());
+    } catch (NoSuchAlgorithmException e) {
+      throw new RuntimeException(e);
     }
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/ModuleItem.java b/kernel/src/main/java/org/kframework/kil/ModuleItem.java
index cb58956c620..6a5ef1373a2 100644
--- a/kernel/src/main/java/org/kframework/kil/ModuleItem.java
+++ b/kernel/src/main/java/org/kframework/kil/ModuleItem.java
@@ -4,23 +4,20 @@
 import org.kframework.attributes.Location;
 import org.kframework.attributes.Source;
 
-import org.w3c.dom.Element;
-
 public abstract class ModuleItem extends ASTNode {
-    public ModuleItem() {
-        super();
-    }
-
-    public ModuleItem(Location loc, Source source) {
-        super(loc, source);
-    }
+  public ModuleItem() {
+    super();
+  }
 
-    public java.util.List getLabels() {
-        return null;
-    }
+  public ModuleItem(Location loc, Source source) {
+    super(loc, source);
+  }
 
-    public java.util.List getKLabels() {
-        return null;
-    }
+  public java.util.List getLabels() {
+    return null;
+  }
 
+  public java.util.List getKLabels() {
+    return null;
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/NonTerminal.java b/kernel/src/main/java/org/kframework/kil/NonTerminal.java
index 64c58213481..e78447f5078 100644
--- a/kernel/src/main/java/org/kframework/kil/NonTerminal.java
+++ b/kernel/src/main/java/org/kframework/kil/NonTerminal.java
@@ -1,66 +1,60 @@
 // Copyright (c) K Team. All Rights Reserved.
 package org.kframework.kil;
 
+import java.util.Optional;
 import org.kframework.kore.Sort;
-
 import scala.Option;
 
-import java.util.Optional;
-
 /** A nonterminal in a {@link Production}. Also abused in some places as a sort identifier */
 public class NonTerminal extends ProductionItem {
 
-    private Sort sort;
-    private final Optional name;
+  private Sort sort;
+  private final Optional name;
 
-    public NonTerminal(Sort sort, Optional name) {
-        super();
-        this.sort = sort;
-        this.name = name;
-    }
+  public NonTerminal(Sort sort, Optional name) {
+    super();
+    this.sort = sort;
+    this.name = name;
+  }
 
-    public Option getName() {
-        if (name.isPresent()) {
-            return Option.apply(name.get());
-        }
-        return Option.empty();
+  public Option getName() {
+    if (name.isPresent()) {
+      return Option.apply(name.get());
     }
+    return Option.empty();
+  }
 
-    public void setSort(Sort sort) {
-        this.sort = sort;
-    }
+  public void setSort(Sort sort) {
+    this.sort = sort;
+  }
 
-    public Sort getSort() {
-        return sort;
-    }
+  public Sort getSort() {
+    return sort;
+  }
 
-    public Sort getRealSort() {
-        return sort;
-    }
+  public Sort getRealSort() {
+    return sort;
+  }
 
-    @Override
-    public void toString(StringBuilder sb) {
-        if (name.isPresent()) {
-          sb.append(name.get()).append(": ");
-        }
-        sb.append(sort);
+  @Override
+  public void toString(StringBuilder sb) {
+    if (name.isPresent()) {
+      sb.append(name.get()).append(": ");
     }
+    sb.append(sort);
+  }
 
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null)
-            return false;
-        if (obj == this)
-            return true;
-        if (!(obj instanceof NonTerminal nt))
-            return false;
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) return false;
+    if (obj == this) return true;
+    if (!(obj instanceof NonTerminal nt)) return false;
 
-        return sort.equals(nt.sort);
-    }
-
-    @Override
-    public int hashCode() {
-        return sort.hashCode();
-    }
+    return sort.equals(nt.sort);
+  }
 
+  @Override
+  public int hashCode() {
+    return sort.hashCode();
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/PriorityBlock.java b/kernel/src/main/java/org/kframework/kil/PriorityBlock.java
index 776ed5c7721..008c31a2227 100644
--- a/kernel/src/main/java/org/kframework/kil/PriorityBlock.java
+++ b/kernel/src/main/java/org/kframework/kil/PriorityBlock.java
@@ -2,94 +2,89 @@
 package org.kframework.kil;
 
 import com.google.common.collect.Lists;
-
 import java.util.ArrayList;
 
 /**
  * A block of productions at the same priority within a syntax declaration.
+ *
  * @see Syntax
  */
 public class PriorityBlock extends ASTNode {
 
-    java.util.List productions = new ArrayList();
-    /** "left", "right", or "non-assoc" if this group of productions had
-     * an explicitly declared associativity, "" otherwise */
-    String assoc;
-
-    public java.util.List getProductions() {
-        return productions;
+  java.util.List productions = new ArrayList();
+
+  /**
+   * "left", "right", or "non-assoc" if this group of productions had an explicitly declared
+   * associativity, "" otherwise
+   */
+  String assoc;
+
+  public java.util.List getProductions() {
+    return productions;
+  }
+
+  public void setProductions(java.util.List productions) {
+    this.productions = productions;
+  }
+
+  public String getAssoc() {
+    return assoc;
+  }
+
+  public void setAssoc(String assoc) {
+    this.assoc = assoc;
+  }
+
+  public PriorityBlock() {
+    super();
+    this.assoc = "";
+  }
+
+  public PriorityBlock(String assoc, java.util.List productions) {
+    super();
+    this.assoc = assoc;
+    this.productions = productions;
+  }
+
+  public PriorityBlock(String assoc, Production... productions) {
+    this(assoc, Lists.newArrayList(productions));
+  }
+
+  @Override
+  public void toString(StringBuilder sb) {
+    if (!assoc.equals("")) {
+      sb.append(assoc).append(": \n    ");
     }
-
-    public void setProductions(java.util.List productions) {
-        this.productions = productions;
+    String conn = "";
+    for (Production production : productions) {
+      sb.append(conn);
+      production.toString(sb);
+      conn = "\n  | ";
     }
+  }
 
-    public String getAssoc() {
-        return assoc;
-    }
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) return false;
+    if (this == obj) return true;
+    if (!(obj instanceof PriorityBlock pb)) return false;
 
-    public void setAssoc(String assoc) {
-        this.assoc = assoc;
-    }
+    if (!pb.getAssoc().equals(this.assoc)) return false;
 
-    public PriorityBlock() {
-        super();
-        this.assoc = "";
-    }
-
-    public PriorityBlock(String assoc, java.util.List productions) {
-        super();
-        this.assoc = assoc;
-        this.productions = productions;
-    }
+    if (pb.productions.size() != productions.size()) return false;
 
-    public PriorityBlock(String assoc, Production... productions) {
-        this(assoc, Lists.newArrayList(productions));
+    for (int i = 0; i < pb.productions.size(); i++) {
+      if (!pb.productions.get(i).equals(productions.get(i))) return false;
     }
 
-    @Override
-    public void toString(StringBuilder sb) {
-        if (!assoc.equals("")) {
-            sb.append(assoc).append(": \n    ");
-        }
-        String conn = "";
-        for (Production production : productions) {
-            sb.append(conn);
-            production.toString(sb);
-            conn = "\n  | ";
-        }
-    }
+    return true;
+  }
 
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null)
-            return false;
-        if (this == obj)
-            return true;
-        if (!(obj instanceof PriorityBlock pb))
-            return false;
-
-        if (!pb.getAssoc().equals(this.assoc))
-            return false;
-
-        if (pb.productions.size() != productions.size())
-            return false;
-
-        for (int i = 0; i < pb.productions.size(); i++) {
-            if (!pb.productions.get(i).equals(productions.get(i)))
-                return false;
-        }
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int hash = assoc.hashCode();
-
-        for (Production prd : productions)
-            hash += prd.hashCode();
-        return hash;
-    }
+  @Override
+  public int hashCode() {
+    int hash = assoc.hashCode();
 
+    for (Production prd : productions) hash += prd.hashCode();
+    return hash;
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/PriorityBlockExtended.java b/kernel/src/main/java/org/kframework/kil/PriorityBlockExtended.java
index ffccf40a51b..34b216d8a18 100644
--- a/kernel/src/main/java/org/kframework/kil/PriorityBlockExtended.java
+++ b/kernel/src/main/java/org/kframework/kil/PriorityBlockExtended.java
@@ -1,64 +1,59 @@
 // Copyright (c) K Team. All Rights Reserved.
 package org.kframework.kil;
 
-import org.kframework.definition.Tag;
-
 import java.util.ArrayList;
+import org.kframework.definition.Tag;
 
-/** A group within a {@code syntax priorities} declaration.
- * @see PriorityExtended */
+/**
+ * A group within a {@code syntax priorities} declaration.
+ *
+ * @see PriorityExtended
+ */
 public class PriorityBlockExtended extends ASTNode {
 
-    java.util.List productions = new ArrayList<>();
+  java.util.List productions = new ArrayList<>();
 
-    public java.util.List getProductions() {
-        return productions;
-    }
+  public java.util.List getProductions() {
+    return productions;
+  }
 
-    public void setProductions(java.util.List productions) {
-        this.productions = productions;
-    }
+  public void setProductions(java.util.List productions) {
+    this.productions = productions;
+  }
 
-    public PriorityBlockExtended(java.util.List productions) {
-        super();
-        this.productions.addAll(productions);
-    }
+  public PriorityBlockExtended(java.util.List productions) {
+    super();
+    this.productions.addAll(productions);
+  }
 
-    @Override
-    public void toString(StringBuilder sb) {
-        for (Tag production : productions) {
-            sb.append(production);
-            sb.append(" ");
-        }
+  @Override
+  public void toString(StringBuilder sb) {
+    for (Tag production : productions) {
+      sb.append(production);
+      sb.append(" ");
     }
+  }
 
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null)
-            return false;
-        if (this == obj)
-            return true;
-        if (!(obj instanceof PriorityBlockExtended pb))
-            return false;
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) return false;
+    if (this == obj) return true;
+    if (!(obj instanceof PriorityBlockExtended pb)) return false;
 
-        if (pb.productions.size() != productions.size())
-            return false;
+    if (pb.productions.size() != productions.size()) return false;
 
-        for (int i = 0; i < pb.productions.size(); i++) {
-            if (!pb.productions.get(i).equals(productions.get(i)))
-                return false;
-        }
-
-        return true;
+    for (int i = 0; i < pb.productions.size(); i++) {
+      if (!pb.productions.get(i).equals(productions.get(i))) return false;
     }
 
-    @Override
-    public int hashCode() {
-        int hash = 0;
+    return true;
+  }
 
-        for (Tag prd : productions)
-            hash += prd.hashCode();
-        return hash;
-    }
+  @Override
+  public int hashCode() {
+    int hash = 0;
 
+    for (Tag prd : productions) hash += prd.hashCode();
+    return hash;
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/PriorityExtended.java b/kernel/src/main/java/org/kframework/kil/PriorityExtended.java
index c65e5e79fba..86f6da122c7 100644
--- a/kernel/src/main/java/org/kframework/kil/PriorityExtended.java
+++ b/kernel/src/main/java/org/kframework/kil/PriorityExtended.java
@@ -1,62 +1,58 @@
 // Copyright (c) K Team. All Rights Reserved.
 package org.kframework.kil;
 
-/** A priority declaration, {@code syntax priorities} labels {@code >} ... {@code >} labels.
+/**
+ * A priority declaration, {@code syntax priorities} labels {@code >} ... {@code >}
+ * labels.
+ *
  * @see PriorityBlockExtended
  */
 public class PriorityExtended extends ModuleItem {
-    /** Highest priority block comes first */
-    java.util.List priorityBlocks;
-
-    public PriorityExtended(java.util.List priorities) {
-        super();
-        this.priorityBlocks = priorities;
-    }
-
-    public java.util.List getPriorityBlocks() {
-        return priorityBlocks;
+  /** Highest priority block comes first */
+  java.util.List priorityBlocks;
+
+  public PriorityExtended(java.util.List priorities) {
+    super();
+    this.priorityBlocks = priorities;
+  }
+
+  public java.util.List getPriorityBlocks() {
+    return priorityBlocks;
+  }
+
+  @Override
+  public void toString(StringBuilder sb) {
+    sb.append("  syntax priorities ");
+    String conn = "";
+    for (PriorityBlockExtended pb : priorityBlocks) {
+      sb.append(conn);
+      pb.toString(sb);
+      conn = " > ";
     }
+    sb.append(" ");
+    sb.append(getAttributes());
+  }
 
-    @Override
-    public void toString(StringBuilder sb) {
-        sb.append("  syntax priorities ");
-        String conn = "";
-        for (PriorityBlockExtended pb : priorityBlocks) {
-            sb.append(conn);
-            pb.toString(sb);
-            conn = " > ";
-        }
-        sb.append(" ");
-        sb.append(getAttributes());
-    }
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) return false;
+    if (this == obj) return true;
+    if (!(obj instanceof PriorityExtended syn)) return false;
 
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null)
-            return false;
-        if (this == obj)
-            return true;
-        if (!(obj instanceof PriorityExtended syn))
-            return false;
+    if (syn.priorityBlocks.size() != priorityBlocks.size()) return false;
 
-        if (syn.priorityBlocks.size() != priorityBlocks.size())
-            return false;
-
-        for (int i = 0; i < syn.priorityBlocks.size(); i++) {
-            if (!syn.priorityBlocks.get(i).equals(priorityBlocks.get(i)))
-                return false;
-        }
-
-        return true;
+    for (int i = 0; i < syn.priorityBlocks.size(); i++) {
+      if (!syn.priorityBlocks.get(i).equals(priorityBlocks.get(i))) return false;
     }
 
-    @Override
-    public int hashCode() {
-        int hash = 0;
+    return true;
+  }
 
-        for (PriorityBlockExtended pb : priorityBlocks)
-            hash += pb.hashCode();
-        return hash;
-    }
+  @Override
+  public int hashCode() {
+    int hash = 0;
 
+    for (PriorityBlockExtended pb : priorityBlocks) hash += pb.hashCode();
+    return hash;
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/PriorityExtendedAssoc.java b/kernel/src/main/java/org/kframework/kil/PriorityExtendedAssoc.java
index 6dce9bdcfe6..aa683d88bc9 100644
--- a/kernel/src/main/java/org/kframework/kil/PriorityExtendedAssoc.java
+++ b/kernel/src/main/java/org/kframework/kil/PriorityExtendedAssoc.java
@@ -4,72 +4,67 @@
 import org.kframework.definition.Tag;
 
 /**
- * An associativity declaration, one of {@code syntax left}, {@code syntax right}, or {@ code syntax non-assoc}.
+ * An associativity declaration, one of {@code syntax left}, {@code syntax right}, or {@ code syntax
+ * non-assoc}.
  */
 public class PriorityExtendedAssoc extends ModuleItem {
-    /** "left", "right", "non-assoc" */
-    String assoc = null;
-    /** The labels getting an associativity. */
-    java.util.List tags;
+  /** "left", "right", "non-assoc" */
+  String assoc = null;
 
-    public String getAssoc() {
-        return assoc;
-    }
+  /** The labels getting an associativity. */
+  java.util.List tags;
 
-    public void setAssoc(String assoc) {
-        this.assoc = assoc;
-    }
+  public String getAssoc() {
+    return assoc;
+  }
 
-    public java.util.List getTags() {
-        return tags;
-    }
+  public void setAssoc(String assoc) {
+    this.assoc = assoc;
+  }
 
-    public void setTags(java.util.List tags) {
-        this.tags = tags;
-    }
+  public java.util.List getTags() {
+    return tags;
+  }
 
-    public PriorityExtendedAssoc(String assoc, java.util.List tags) {
-        super();
-        this.tags = tags;
-        this.assoc = assoc;
-    }
+  public void setTags(java.util.List tags) {
+    this.tags = tags;
+  }
 
-    @Override
-    public void toString(StringBuilder sb) {
-        sb.append("  syntax ").append(assoc).append(" ");
-        for (Tag pb : tags) {
-            sb.append(pb).append(" ");
-        }
-        sb.append(getAttributes());
-    }
+  public PriorityExtendedAssoc(String assoc, java.util.List tags) {
+    super();
+    this.tags = tags;
+    this.assoc = assoc;
+  }
 
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null)
-            return false;
-        if (this == obj)
-            return true;
-        if (!(obj instanceof PriorityExtendedAssoc syn))
-            return false;
+  @Override
+  public void toString(StringBuilder sb) {
+    sb.append("  syntax ").append(assoc).append(" ");
+    for (Tag pb : tags) {
+      sb.append(pb).append(" ");
+    }
+    sb.append(getAttributes());
+  }
 
-        if (syn.tags.size() != tags.size())
-            return false;
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) return false;
+    if (this == obj) return true;
+    if (!(obj instanceof PriorityExtendedAssoc syn)) return false;
 
-        for (int i = 0; i < syn.tags.size(); i++) {
-            if (!syn.tags.get(i).equals(tags.get(i)))
-                return false;
-        }
+    if (syn.tags.size() != tags.size()) return false;
 
-        return true;
+    for (int i = 0; i < syn.tags.size(); i++) {
+      if (!syn.tags.get(i).equals(tags.get(i))) return false;
     }
 
-    @Override
-    public int hashCode() {
-        int hash = assoc.hashCode();
+    return true;
+  }
 
-        for (Tag pb : tags)
-            hash += pb.hashCode();
-        return hash;
-    }
+  @Override
+  public int hashCode() {
+    int hash = assoc.hashCode();
 
+    for (Tag pb : tags) hash += pb.hashCode();
+    return hash;
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/Production.java b/kernel/src/main/java/org/kframework/kil/Production.java
index e0d89b95a83..3021bc9d5fd 100644
--- a/kernel/src/main/java/org/kframework/kil/Production.java
+++ b/kernel/src/main/java/org/kframework/kil/Production.java
@@ -1,252 +1,249 @@
 // Copyright (c) K Team. All Rights Reserved.
 package org.kframework.kil;
 
-import org.kframework.kore.Sort;
-import org.kframework.attributes.Att;
-
 import com.google.common.collect.Multimap;
-import org.kframework.utils.StringUtil;
-
 import java.util.ArrayList;
 import java.util.List;
+import org.kframework.attributes.Att;
+import org.kframework.kore.Sort;
+import org.kframework.utils.StringUtil;
 
 /**
  * A production. Any explicit attributes on the production are stored in {@link ASTNode#attributes}.
  */
 public class Production extends ASTNode {
 
-    /*
-     * Andrei S: It appears that the cons attribute is mandatory for all new production added during compilation, otherwise a null pointer exception can be thrown in one of the later compilation
-     * steps.
-     */
-    protected List items;
-    protected Sort sort;
-    protected List params;
-    protected String ownerModuleName;
-    private Multimap binderMap;
-
-    public boolean isListDecl() {
-        return items.size() == 1 && items.get(0) instanceof UserList;
-    }
-
-    /**
-     * Returns the KLabel for the list terminator.
-     * Constructed as '.List{""}
-     * Should be called only if isListDecl is true.
-     * @return String representation of the separator KLabel.
-     */
-    public String getTerminatorKLabel(boolean kore) {
-        assert isListDecl();
-        return ".List{" + StringUtil.enquoteCString(getKLabel(kore)) + "}" + (kore ? "_" + getSort().name() : "");
-    }
-
-    /**
-     * True if this production consists of a single nonterminal,
-     * even if it has an explicitly assigned label and so is
-     * not semantically a subsort declaration.
-     * @return
-     */
-    public boolean isSyntacticSubsort() {
-        return items.size() == 1 && items.get(0) instanceof NonTerminal;
-    }
-
-    public boolean isLexical() {
-        return items.size() == 1 && items.get(0) instanceof Lexical;
-    }
-
-    /**
-     * Retrieves the {@link Lexical} object of the production if this is a lexical token.
-     * Should not be called on other types of productions.
-     * @return the Lexical object
-     */
-    public Lexical getLexical() {
-        assert isLexical();
-        return (Lexical) items.get(0);
-    }
-
-    /**
-     * Retrieves the {@link Terminal} object of the production if this is a constant.
-     * Should not be called on other types of productions.
-     * @return the Terminal object
-     */
-    public Terminal getConstant() {
-        assert isTerminal(); // should be at least a single terminal
-        return (Terminal) items.get(0);
-    }
-
-    /**
-     * Returns true if this production consists of exactly one terminal.
-     */
-    public boolean isTerminal() {
-        return items.size() == 1 && items.get(0) instanceof Terminal;
-    }
-
-    public Production(NonTerminal sort, java.util.List items) {
-        super();
-        this.items = items;
-        this.sort = sort.getSort();
-    }
-
-    public Production(NonTerminal sort, java.util.List items, String ownerModule) {
-        super();
-        this.items = items;
-        this.sort = sort.getSort();
-        this.ownerModuleName = ownerModule;
-    }
-
-    /**
-     * Gets the KLabel corresponding to this production. A production has a
-     * KLabel if and only if the production flattens in KORE to a term which is of sort
-     * KItem (ie, is a function or a constructor).
-     * @return
-     */
-    public String getKLabel(boolean kore) {
-        String klabel = getAttribute(Att.KLABEL());
-        if (klabel == null && (isSyntacticSubsort() || containsAttribute(Att.TOKEN()) || containsAttribute(Att.BRACKET()))) {
-            return null;
-        } else if (klabel == null || (kore && getAttribute(Att.SYMBOL()) == null)) {
-            klabel = getPrefixLabel(kore);
-        }
-        return klabel.replace(" ", "");
-    }
-
-    public String getBracketLabel(boolean kore) {
-        String klabel = getAttribute(Att.KLABEL());
-        if (klabel == null || (kore && getAttribute(Att.SYMBOL()) == null)) {
-            klabel = getPrefixLabel(kore);
-        }
-        return klabel.replace(" ", "");
-    }
-
-    private String getPrefixLabel(boolean kore) {
-        String label = "";
-        List sorts = new ArrayList<>();
-        for (ProductionItem pi : items) {
-            if (pi instanceof NonTerminal) {
-                label += "_";
-                sorts.add(((NonTerminal) pi).getSort().name());
-            } else if (pi instanceof Terminal) {
-                label += ((Terminal) pi).getTerminal();
-            } else if (pi instanceof UserList) {
-                label += "_" + ((UserList) pi).separator + "_";
-                sorts.add(((UserList) pi).sort.name());
-                sorts.add(sort.name());
-            }
-        }
-        return label + "_" + ownerModuleName + (kore ? "_" + sorts.stream().reduce(sort.name(), (s1, s2) -> s1 + "_" + s2) : "");
-    }
-
-    public java.util.List getItems() {
-        return items;
-    }
-
-    public void setItems(java.util.List items) {
-        this.items = items;
-    }
-
-    /**
-     * Gets the arity of a production. A production's arity is the number of
-     * nonterminals in the syntactic declaration which the production
-     * corresponds to.
-     * @return
-     */
-    public int getArity() {
-        int arity = 0;
-        for (ProductionItem i : items) {
-            if (i instanceof UserList)
-                arity += 2;
-            if (i instanceof NonTerminal)
-                arity++;
-        }
-        return arity;
-    }
-
-    public Sort getSort() {
-        return sort;
-    }
-
-    public void setSort(Sort sort) {
-        this.sort = sort;
-    }
-
-    public List getParams() {
-        return params;
-    }
-
-    public void setParams(List params) {
-        this.params = params;
-    }
-
-    public ASTNode getChildNode(int idx) {
-        int arity = -1;
-        if (items.get(0) instanceof UserList) {
-            if (idx == 0) {
-                return items.get(0);
-            } else {
-                return this;
-            }
-        }
-        for (ProductionItem i : items) {
-            if (!(i instanceof Terminal))
-                arity++;
-            if (arity == idx) {
-                return i;
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj)
-            return true;
-        if (obj == null)
-            return false;
-        if (getClass() != obj.getClass())
-            return false;
-        Production other = (Production) obj;
-        if (items == null) {
-            if (other.items != null)
-                return false;
-        } else if (!items.equals(other.items))
-            return false;
-        if (getAttribute(Att.KLABEL()) == null) {
-            if (other.getAttribute(Att.KLABEL()) != null)
-                return false;
-        } else if (!getAttribute(Att.KLABEL()).equals(other.getAttribute(Att.KLABEL())))
-            return false;
-        if (sort == null) {
-            if (other.sort != null)
-                return false;
-        } else if (!sort.equals(other.sort))
-            return false;
-        if (binderMap == null) {
-            return other.binderMap == null;
-        } else return binderMap.equals(other.binderMap);
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + ((items == null) ? 0 : items.hashCode());
-        result = prime * result
-                + ((getAttribute(Att.KLABEL()) == null) ? 0 : getAttribute(Att.KLABEL()).hashCode());
-        result = prime * result + ((sort == null) ? 0 : sort.hashCode());
-        result = prime * result + ((binderMap == null) ? 0 : binderMap.hashCode());
-        return result;
-    }
-
-    @Override
-    public void toString(StringBuilder sb) {
-        for (ProductionItem i : items) {
-            i.toString(sb);
-            sb.append(" ");
-        }
-        sb.append(getAttributes().withUserGroupsAsGroupAtt());
-    }
-
-    public void setOwnerModuleName(String ownerModuleName) {
-        this.ownerModuleName = ownerModuleName;
-    }
+  /*
+   * Andrei S: It appears that the cons attribute is mandatory for all new production added during compilation, otherwise a null pointer exception can be thrown in one of the later compilation
+   * steps.
+   */
+  protected List items;
+  protected Sort sort;
+  protected List params;
+  protected String ownerModuleName;
+  private Multimap binderMap;
+
+  public boolean isListDecl() {
+    return items.size() == 1 && items.get(0) instanceof UserList;
+  }
+
+  /**
+   * Returns the KLabel for the list terminator. Constructed as '.List{""} Should be
+   * called only if isListDecl is true.
+   *
+   * @return String representation of the separator KLabel.
+   */
+  public String getTerminatorKLabel(boolean kore) {
+    assert isListDecl();
+    return ".List{"
+        + StringUtil.enquoteCString(getKLabel(kore))
+        + "}"
+        + (kore ? "_" + getSort().name() : "");
+  }
+
+  /**
+   * True if this production consists of a single nonterminal, even if it has an explicitly assigned
+   * label and so is not semantically a subsort declaration.
+   *
+   * @return
+   */
+  public boolean isSyntacticSubsort() {
+    return items.size() == 1 && items.get(0) instanceof NonTerminal;
+  }
+
+  public boolean isLexical() {
+    return items.size() == 1 && items.get(0) instanceof Lexical;
+  }
+
+  /**
+   * Retrieves the {@link Lexical} object of the production if this is a lexical token. Should not
+   * be called on other types of productions.
+   *
+   * @return the Lexical object
+   */
+  public Lexical getLexical() {
+    assert isLexical();
+    return (Lexical) items.get(0);
+  }
+
+  /**
+   * Retrieves the {@link Terminal} object of the production if this is a constant. Should not be
+   * called on other types of productions.
+   *
+   * @return the Terminal object
+   */
+  public Terminal getConstant() {
+    assert isTerminal(); // should be at least a single terminal
+    return (Terminal) items.get(0);
+  }
+
+  /** Returns true if this production consists of exactly one terminal. */
+  public boolean isTerminal() {
+    return items.size() == 1 && items.get(0) instanceof Terminal;
+  }
+
+  public Production(NonTerminal sort, java.util.List items) {
+    super();
+    this.items = items;
+    this.sort = sort.getSort();
+  }
+
+  public Production(NonTerminal sort, java.util.List items, String ownerModule) {
+    super();
+    this.items = items;
+    this.sort = sort.getSort();
+    this.ownerModuleName = ownerModule;
+  }
+
+  /**
+   * Gets the KLabel corresponding to this production. A production has a KLabel if and only if the
+   * production flattens in KORE to a term which is of sort KItem (ie, is a function or a
+   * constructor).
+   *
+   * @return
+   */
+  public String getKLabel(boolean kore) {
+    String klabel = getAttribute(Att.KLABEL());
+    if (klabel == null
+        && (isSyntacticSubsort()
+            || containsAttribute(Att.TOKEN())
+            || containsAttribute(Att.BRACKET()))) {
+      return null;
+    } else if (klabel == null || (kore && getAttribute(Att.SYMBOL()) == null)) {
+      klabel = getPrefixLabel(kore);
+    }
+    return klabel.replace(" ", "");
+  }
+
+  public String getBracketLabel(boolean kore) {
+    String klabel = getAttribute(Att.KLABEL());
+    if (klabel == null || (kore && getAttribute(Att.SYMBOL()) == null)) {
+      klabel = getPrefixLabel(kore);
+    }
+    return klabel.replace(" ", "");
+  }
+
+  private String getPrefixLabel(boolean kore) {
+    String label = "";
+    List sorts = new ArrayList<>();
+    for (ProductionItem pi : items) {
+      if (pi instanceof NonTerminal) {
+        label += "_";
+        sorts.add(((NonTerminal) pi).getSort().name());
+      } else if (pi instanceof Terminal) {
+        label += ((Terminal) pi).getTerminal();
+      } else if (pi instanceof UserList) {
+        label += "_" + ((UserList) pi).separator + "_";
+        sorts.add(((UserList) pi).sort.name());
+        sorts.add(sort.name());
+      }
+    }
+    return label
+        + "_"
+        + ownerModuleName
+        + (kore ? "_" + sorts.stream().reduce(sort.name(), (s1, s2) -> s1 + "_" + s2) : "");
+  }
+
+  public java.util.List getItems() {
+    return items;
+  }
+
+  public void setItems(java.util.List items) {
+    this.items = items;
+  }
+
+  /**
+   * Gets the arity of a production. A production's arity is the number of nonterminals in the
+   * syntactic declaration which the production corresponds to.
+   *
+   * @return
+   */
+  public int getArity() {
+    int arity = 0;
+    for (ProductionItem i : items) {
+      if (i instanceof UserList) arity += 2;
+      if (i instanceof NonTerminal) arity++;
+    }
+    return arity;
+  }
+
+  public Sort getSort() {
+    return sort;
+  }
+
+  public void setSort(Sort sort) {
+    this.sort = sort;
+  }
+
+  public List getParams() {
+    return params;
+  }
+
+  public void setParams(List params) {
+    this.params = params;
+  }
+
+  public ASTNode getChildNode(int idx) {
+    int arity = -1;
+    if (items.get(0) instanceof UserList) {
+      if (idx == 0) {
+        return items.get(0);
+      } else {
+        return this;
+      }
+    }
+    for (ProductionItem i : items) {
+      if (!(i instanceof Terminal)) arity++;
+      if (arity == idx) {
+        return i;
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) return true;
+    if (obj == null) return false;
+    if (getClass() != obj.getClass()) return false;
+    Production other = (Production) obj;
+    if (items == null) {
+      if (other.items != null) return false;
+    } else if (!items.equals(other.items)) return false;
+    if (getAttribute(Att.KLABEL()) == null) {
+      if (other.getAttribute(Att.KLABEL()) != null) return false;
+    } else if (!getAttribute(Att.KLABEL()).equals(other.getAttribute(Att.KLABEL()))) return false;
+    if (sort == null) {
+      if (other.sort != null) return false;
+    } else if (!sort.equals(other.sort)) return false;
+    if (binderMap == null) {
+      return other.binderMap == null;
+    } else return binderMap.equals(other.binderMap);
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + ((items == null) ? 0 : items.hashCode());
+    result =
+        prime * result
+            + ((getAttribute(Att.KLABEL()) == null) ? 0 : getAttribute(Att.KLABEL()).hashCode());
+    result = prime * result + ((sort == null) ? 0 : sort.hashCode());
+    result = prime * result + ((binderMap == null) ? 0 : binderMap.hashCode());
+    return result;
+  }
+
+  @Override
+  public void toString(StringBuilder sb) {
+    for (ProductionItem i : items) {
+      i.toString(sb);
+      sb.append(" ");
+    }
+    sb.append(getAttributes().withUserGroupsAsGroupAtt());
+  }
+
+  public void setOwnerModuleName(String ownerModuleName) {
+    this.ownerModuleName = ownerModuleName;
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/ProductionItem.java b/kernel/src/main/java/org/kframework/kil/ProductionItem.java
index f6bd8d16687..3297d1bfa2c 100644
--- a/kernel/src/main/java/org/kframework/kil/ProductionItem.java
+++ b/kernel/src/main/java/org/kframework/kil/ProductionItem.java
@@ -2,7 +2,7 @@
 package org.kframework.kil;
 
 public abstract class ProductionItem extends ASTNode {
-    public ProductionItem() {
-        super();
-    }
+  public ProductionItem() {
+    super();
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/Require.java b/kernel/src/main/java/org/kframework/kil/Require.java
index b243911231e..75a4eb56306 100644
--- a/kernel/src/main/java/org/kframework/kil/Require.java
+++ b/kernel/src/main/java/org/kframework/kil/Require.java
@@ -5,25 +5,25 @@
 
 /** A require directive */
 public class Require extends DefinitionItem {
-    /** The string argument to {@code require}, as written in the input file. */
-    private String value;
+  /** The string argument to {@code require}, as written in the input file. */
+  private String value;
 
-    public Require(String value) {
-        super();
-        this.value = value;
-    }
+  public Require(String value) {
+    super();
+    this.value = value;
+  }
 
-    public void setValue(String value) {
-        this.value = value;
-    }
+  public void setValue(String value) {
+    this.value = value;
+  }
 
-    public String getValue() {
-        return value;
-    }
+  public String getValue() {
+    return value;
+  }
 
-    @Override
-    public void toString(StringBuilder sb) {
-        sb.append("require ");
-        sb.append(StringUtil.enquoteCString(value));
-    }
+  @Override
+  public void toString(StringBuilder sb) {
+    sb.append("require ");
+    sb.append(StringUtil.enquoteCString(value));
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/SortSynonym.java b/kernel/src/main/java/org/kframework/kil/SortSynonym.java
index dc172a4c367..0e985b2ea55 100644
--- a/kernel/src/main/java/org/kframework/kil/SortSynonym.java
+++ b/kernel/src/main/java/org/kframework/kil/SortSynonym.java
@@ -5,41 +5,41 @@
 
 public class SortSynonym extends ModuleItem {
 
-    public final Sort newSort;
-    public final Sort oldSort;
-
-    public SortSynonym(NonTerminal newSort, NonTerminal oldSort) {
-        super();
-        this.newSort = newSort.getSort();
-        this.oldSort = oldSort.getSort();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj)
-            return true;
-        if (obj == null)
-            return false;
-        if (getClass() != obj.getClass())
-            return false;
-        SortSynonym other = (SortSynonym) obj;
-        if (!newSort.equals(other.newSort))
-            return false;
-        return oldSort.equals(other.oldSort);
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + newSort.hashCode();
-        result = prime * result + oldSort.hashCode();
-        return result;
-    }
-
-    @Override
-    public void toString(StringBuilder sb) {
-      sb.append("  syntax ").append(newSort).append(" = ").append(oldSort).append(" ").append(getAttributes());
-    }
-
+  public final Sort newSort;
+  public final Sort oldSort;
+
+  public SortSynonym(NonTerminal newSort, NonTerminal oldSort) {
+    super();
+    this.newSort = newSort.getSort();
+    this.oldSort = oldSort.getSort();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) return true;
+    if (obj == null) return false;
+    if (getClass() != obj.getClass()) return false;
+    SortSynonym other = (SortSynonym) obj;
+    if (!newSort.equals(other.newSort)) return false;
+    return oldSort.equals(other.oldSort);
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + newSort.hashCode();
+    result = prime * result + oldSort.hashCode();
+    return result;
+  }
+
+  @Override
+  public void toString(StringBuilder sb) {
+    sb.append("  syntax ")
+        .append(newSort)
+        .append(" = ")
+        .append(oldSort)
+        .append(" ")
+        .append(getAttributes());
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/StringSentence.java b/kernel/src/main/java/org/kframework/kil/StringSentence.java
index 17c0d31050a..bfbf1de44b2 100644
--- a/kernel/src/main/java/org/kframework/kil/StringSentence.java
+++ b/kernel/src/main/java/org/kframework/kil/StringSentence.java
@@ -5,65 +5,65 @@
  * Used as a container for unparsed sentences like rule, context and configuration.
  *
  * @author Radu
- *
  */
 public class StringSentence extends ModuleItem {
-    private String content;
-    private final int contentStartLine;
-    private final int contentStartColumn;
-    private String label;
-    private String type;
+  private String content;
+  private final int contentStartLine;
+  private final int contentStartColumn;
+  private String label;
+  private String type;
 
-    public StringSentence(String content, int contentStartLine, int contentStartColumn, String type, String label) {
-        this.content = content;
-        this.contentStartLine = contentStartLine;
-        this.contentStartColumn = contentStartColumn;
-        this.type = type;
-        this.label = label;
-    }
+  public StringSentence(
+      String content, int contentStartLine, int contentStartColumn, String type, String label) {
+    this.content = content;
+    this.contentStartLine = contentStartLine;
+    this.contentStartColumn = contentStartColumn;
+    this.type = type;
+    this.label = label;
+  }
 
-    @Override
-    public void toString(StringBuilder sb) {
-        switch (type) {
-        case "config" -> sb.append("  configuration ");
-        case "alias" -> sb.append("  context alias ");
-        default -> sb.append("  ").append(type).append(" ");
-        }
-        if (!label.equals("")) {
-          sb.append("[").append(label).append("]: ");
-        }
-        sb.append(content).append(" ").append(getAttributes());
+  @Override
+  public void toString(StringBuilder sb) {
+    switch (type) {
+      case "config" -> sb.append("  configuration ");
+      case "alias" -> sb.append("  context alias ");
+      default -> sb.append("  ").append(type).append(" ");
     }
-
-    public String getContent() {
-        return content;
+    if (!label.equals("")) {
+      sb.append("[").append(label).append("]: ");
     }
+    sb.append(content).append(" ").append(getAttributes());
+  }
 
-    public void setContent(String content) {
-        this.content = content;
-    }
+  public String getContent() {
+    return content;
+  }
 
-    public void setType(String type) {
-        this.type = type;
-    }
+  public void setContent(String content) {
+    this.content = content;
+  }
 
-    public String getType() {
-        return type;
-    }
+  public void setType(String type) {
+    this.type = type;
+  }
 
-    public String getLabel() {
-        return label;
-    }
+  public String getType() {
+    return type;
+  }
 
-    public void setLabel(String label) {
-        this.label = label;
-    }
+  public String getLabel() {
+    return label;
+  }
 
-    public int getContentStartLine() {
-        return contentStartLine;
-    }
+  public void setLabel(String label) {
+    this.label = label;
+  }
 
-    public int getContentStartColumn() {
-        return contentStartColumn;
-    }
+  public int getContentStartLine() {
+    return contentStartLine;
+  }
+
+  public int getContentStartColumn() {
+    return contentStartColumn;
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/Syntax.java b/kernel/src/main/java/org/kframework/kil/Syntax.java
index a88dda8191c..40a793ab0c2 100644
--- a/kernel/src/main/java/org/kframework/kil/Syntax.java
+++ b/kernel/src/main/java/org/kframework/kil/Syntax.java
@@ -1,111 +1,100 @@
 // Copyright (c) K Team. All Rights Reserved.
 package org.kframework.kil;
 
-import org.kframework.kore.Sort;
-
 import java.util.ArrayList;
 import java.util.List;
+import org.kframework.kore.Sort;
 
 /**
- * A syntax declaration.
- * Contains {@link Production}s, grouped into a list {@link PriorityBlock}
+ * A syntax declaration. Contains {@link Production}s, grouped into a list {@link PriorityBlock}
  * according to precedence marked by {@code >} in the declaration.
  */
 public class Syntax extends ModuleItem {
-    /** The sort being declared. */
-    NonTerminal sort;
-    java.util.List params;
-    java.util.List priorityBlocks;
-
-    public Syntax(NonTerminal sort, List params, List priorities) {
-        super();
-        this.sort = sort;
-        this.params = params;
-        this.priorityBlocks = priorities;
-    }
-
-    public Syntax(NonTerminal sort, List params) {
-        this(sort, params, new ArrayList());
-    }
-
-    /**
-     * The sort being declared.
-     */
-    public NonTerminal getDeclaredSort() {
-        return sort;
-    }
-
-    public List getParams() {
-        return params;
+  /** The sort being declared. */
+  NonTerminal sort;
+
+  java.util.List params;
+  java.util.List priorityBlocks;
+
+  public Syntax(NonTerminal sort, List params, List priorities) {
+    super();
+    this.sort = sort;
+    this.params = params;
+    this.priorityBlocks = priorities;
+  }
+
+  public Syntax(NonTerminal sort, List params) {
+    this(sort, params, new ArrayList());
+  }
+
+  /** The sort being declared. */
+  public NonTerminal getDeclaredSort() {
+    return sort;
+  }
+
+  public List getParams() {
+    return params;
+  }
+
+  public void setSort(NonTerminal sort) {
+    this.sort = sort;
+  }
+
+  public java.util.List getPriorityBlocks() {
+    return priorityBlocks;
+  }
+
+  public void setPriorityBlocks(java.util.List priorityBlocks) {
+    this.priorityBlocks = priorityBlocks;
+  }
+
+  @Override
+  public void toString(StringBuilder sb) {
+    sb.append("  syntax ");
+    if (!params.isEmpty()) {
+      sb.append("{");
+      String conn = "";
+      for (Sort param : params) {
+        sb.append(conn);
+        sb.append(param);
+        conn = ", ";
+      }
+      sb.append("} ");
     }
-
-    public void setSort(NonTerminal sort) {
-        this.sort = sort;
+    sb.append(sort).append(" ").append(getAttributes());
+    if (!priorityBlocks.isEmpty()) {
+      sb.append(" ::=\n    ");
+      String conn = "";
+      for (PriorityBlock pb : priorityBlocks) {
+        sb.append(conn);
+        pb.toString(sb);
+        conn = "\n  > ";
+      }
     }
+  }
 
-    public java.util.List getPriorityBlocks() {
-        return priorityBlocks;
-    }
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) return false;
+    if (this == obj) return true;
+    if (!(obj instanceof Syntax syn)) return false;
 
-    public void setPriorityBlocks(java.util.List priorityBlocks) {
-        this.priorityBlocks = priorityBlocks;
-    }
-
-    @Override
-    public void toString(StringBuilder sb) {
-        sb.append("  syntax ");
-        if(!params.isEmpty()) {
-            sb.append("{");
-            String conn = "";
-            for (Sort param : params) {
-                sb.append(conn);
-                sb.append(param);
-                conn = ", ";
-            }
-            sb.append("} ");
-        }
-        sb.append(sort).append(" ").append(getAttributes());
-        if (!priorityBlocks.isEmpty()) {
-            sb.append(" ::=\n    ");
-            String conn = "";
-            for (PriorityBlock pb : priorityBlocks) {
-                sb.append(conn);
-                pb.toString(sb);
-                conn = "\n  > ";
-            }
-        }
-    }
+    if (!syn.getDeclaredSort().equals(this.sort)) return false;
 
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null)
-            return false;
-        if (this == obj)
-            return true;
-        if (!(obj instanceof Syntax syn))
-            return false;
+    if (syn.priorityBlocks.size() != priorityBlocks.size()) return false;
 
-        if (!syn.getDeclaredSort().equals(this.sort))
-            return false;
-
-        if (syn.priorityBlocks.size() != priorityBlocks.size())
-            return false;
-
-        for (int i = 0; i < syn.priorityBlocks.size(); i++) {
-            if (!syn.priorityBlocks.get(i).equals(priorityBlocks.get(i)))
-                return false;
-        }
-
-        return true;
+    for (int i = 0; i < syn.priorityBlocks.size(); i++) {
+      if (!syn.priorityBlocks.get(i).equals(priorityBlocks.get(i))) return false;
     }
 
-    @Override
-    public int hashCode() {
-        int hash = sort.hashCode();
+    return true;
+  }
 
-        for (PriorityBlock pb : priorityBlocks)
-            hash += pb.hashCode();
-        return hash;
-    }
+  @Override
+  public int hashCode() {
+    int hash = sort.hashCode();
 
+    for (PriorityBlock pb : priorityBlocks) hash += pb.hashCode();
+    return hash;
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/SyntaxLexical.java b/kernel/src/main/java/org/kframework/kil/SyntaxLexical.java
index 7a10192bd21..2f269dd0935 100644
--- a/kernel/src/main/java/org/kframework/kil/SyntaxLexical.java
+++ b/kernel/src/main/java/org/kframework/kil/SyntaxLexical.java
@@ -5,41 +5,41 @@
 
 public class SyntaxLexical extends ModuleItem {
 
-    public final String name;
-    public final String regex;
-
-    public SyntaxLexical(String name, String regex) {
-        super();
-        this.name = name;
-        this.regex = regex;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj)
-            return true;
-        if (obj == null)
-            return false;
-        if (getClass() != obj.getClass())
-            return false;
-        SyntaxLexical other = (SyntaxLexical) obj;
-        if (!name.equals(other.name))
-            return false;
-        return regex.equals(other.regex);
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + name.hashCode();
-        result = prime * result + regex.hashCode();
-        return result;
-    }
-
-    @Override
-    public void toString(StringBuilder sb) {
-      sb.append("  syntax lexical ").append(name).append(" = r").append(StringUtil.enquoteKString(regex)).append(" ").append(getAttributes());
-    }
-
+  public final String name;
+  public final String regex;
+
+  public SyntaxLexical(String name, String regex) {
+    super();
+    this.name = name;
+    this.regex = regex;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) return true;
+    if (obj == null) return false;
+    if (getClass() != obj.getClass()) return false;
+    SyntaxLexical other = (SyntaxLexical) obj;
+    if (!name.equals(other.name)) return false;
+    return regex.equals(other.regex);
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + name.hashCode();
+    result = prime * result + regex.hashCode();
+    return result;
+  }
+
+  @Override
+  public void toString(StringBuilder sb) {
+    sb.append("  syntax lexical ")
+        .append(name)
+        .append(" = r")
+        .append(StringUtil.enquoteKString(regex))
+        .append(" ")
+        .append(getAttributes());
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/Terminal.java b/kernel/src/main/java/org/kframework/kil/Terminal.java
index 3091c35d98e..e8ac10a1de4 100644
--- a/kernel/src/main/java/org/kframework/kil/Terminal.java
+++ b/kernel/src/main/java/org/kframework/kil/Terminal.java
@@ -6,40 +6,37 @@
 /** A terminal in a {@link Production}. */
 public class Terminal extends ProductionItem {
 
-    private String terminal;
-
-    public Terminal(String terminal) {
-        super();
-        this.terminal = terminal;
-    }
-
-    public void setTerminal(String terminal) {
-        this.terminal = terminal;
-    }
-
-    public String getTerminal() {
-        return terminal;
-    }
-
-    @Override
-    public void toString(StringBuilder sb) {
-        sb.append(StringUtil.enquoteCString(terminal));
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null)
-            return false;
-        if (obj == this)
-            return true;
-        if (!(obj instanceof Terminal trm))
-            return false;
-
-        return trm.terminal.equals(this.terminal);
-    }
-
-    @Override
-    public int hashCode() {
-        return this.terminal.hashCode();
-    }
+  private String terminal;
+
+  public Terminal(String terminal) {
+    super();
+    this.terminal = terminal;
+  }
+
+  public void setTerminal(String terminal) {
+    this.terminal = terminal;
+  }
+
+  public String getTerminal() {
+    return terminal;
+  }
+
+  @Override
+  public void toString(StringBuilder sb) {
+    sb.append(StringUtil.enquoteCString(terminal));
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) return false;
+    if (obj == this) return true;
+    if (!(obj instanceof Terminal trm)) return false;
+
+    return trm.terminal.equals(this.terminal);
+  }
+
+  @Override
+  public int hashCode() {
+    return this.terminal.hashCode();
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/UserList.java b/kernel/src/main/java/org/kframework/kil/UserList.java
index 48f4e9abaf6..88820ff4f69 100644
--- a/kernel/src/main/java/org/kframework/kil/UserList.java
+++ b/kernel/src/main/java/org/kframework/kil/UserList.java
@@ -5,77 +5,82 @@
 import org.kframework.utils.StringUtil;
 
 /**
- * A production item for a cons-list with separator, like List{UserSort,";"}. Must be the only item in a {@link Production}.
+ * A production item for a cons-list with separator, like List{UserSort,";"}. Must be the only item
+ * in a {@link Production}.
  */
 public class UserList extends ProductionItem {
-    protected Sort sort;
-    protected String separator;
-    protected String listType;
+  protected Sort sort;
+  protected String separator;
+  protected String listType;
 
-    public static final String ZERO_OR_MORE = "*";
-    public static final String ONE_OR_MORE = "+";
+  public static final String ZERO_OR_MORE = "*";
+  public static final String ONE_OR_MORE = "+";
 
-    public UserList(Sort sort, String separator) {
-        this.sort = sort;
-        this.separator = separator.trim();
-        this.listType = ZERO_OR_MORE;
-    }
+  public UserList(Sort sort, String separator) {
+    this.sort = sort;
+    this.separator = separator.trim();
+    this.listType = ZERO_OR_MORE;
+  }
 
-    public UserList(Sort sort, String separator, String listType) {
-        this.sort = sort;
-        this.separator = separator.trim();
-        this.listType = listType;
-    }
+  public UserList(Sort sort, String separator, String listType) {
+    this.sort = sort;
+    this.separator = separator.trim();
+    this.listType = listType;
+  }
 
-    @Override
-    public void toString(StringBuilder sb) {
-        if (listType.equals(ZERO_OR_MORE)) {
-            sb.append("List{").append(sort).append(",").append(StringUtil.enquoteCString(separator)).append("}");
-        } else {
-            sb.append("NeList{").append(sort).append(",").append(StringUtil.enquoteCString(separator)).append("}");
-        }
+  @Override
+  public void toString(StringBuilder sb) {
+    if (listType.equals(ZERO_OR_MORE)) {
+      sb.append("List{")
+          .append(sort)
+          .append(",")
+          .append(StringUtil.enquoteCString(separator))
+          .append("}");
+    } else {
+      sb.append("NeList{")
+          .append(sort)
+          .append(",")
+          .append(StringUtil.enquoteCString(separator))
+          .append("}");
     }
+  }
 
-    public Sort getSort() {
-        return sort;
-    }
+  public Sort getSort() {
+    return sort;
+  }
 
-    public void setSort(Sort sort) {
-        this.sort = sort;
-    }
+  public void setSort(Sort sort) {
+    this.sort = sort;
+  }
 
-    public String getSeparator() {
-        return separator;
-    }
+  public String getSeparator() {
+    return separator;
+  }
 
-    public void setSeparator(String separator) {
-        this.separator = separator.trim();
-    }
+  public void setSeparator(String separator) {
+    this.separator = separator.trim();
+  }
 
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null)
-            return false;
-        if (obj == this)
-            return true;
-        if (!(obj instanceof UserList srt))
-            return false;
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) return false;
+    if (obj == this) return true;
+    if (!(obj instanceof UserList srt)) return false;
 
-        if (!sort.equals(srt.getSort()))
-            return false;
-        return separator.equals(srt.getSeparator());
-    }
+    if (!sort.equals(srt.getSort())) return false;
+    return separator.equals(srt.getSeparator());
+  }
 
-    @Override
-    public int hashCode() {
-        return this.separator.hashCode() + this.sort.hashCode();
-    }
+  @Override
+  public int hashCode() {
+    return this.separator.hashCode() + this.sort.hashCode();
+  }
 
-    public String getListType() {
-        return listType;
-    }
+  public String getListType() {
+    return listType;
+  }
 
-    public void setListType(String listType) {
-        this.listType = listType;
-    }
+  public void setListType(String listType) {
+    this.listType = listType;
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kil/loader/Context.java b/kernel/src/main/java/org/kframework/kil/loader/Context.java
index a136a3922e6..e8994639f9b 100644
--- a/kernel/src/main/java/org/kframework/kil/loader/Context.java
+++ b/kernel/src/main/java/org/kframework/kil/loader/Context.java
@@ -1,50 +1,48 @@
 // Copyright (c) K Team. All Rights Reserved.
 package org.kframework.kil.loader;
 
+import static org.kframework.Collections.*;
+
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.SetMultimap;
 import com.google.inject.Inject;
+import java.io.Serializable;
 import org.kframework.attributes.Att;
-import org.kframework.attributes.Att.Key;
 import org.kframework.kil.Production;
 import org.kframework.kompile.KompileOptions;
 import org.kframework.main.GlobalOptions;
 import org.kframework.utils.inject.RequestScoped;
 import scala.Tuple2;
 
-import java.io.Serializable;
-
-import static org.kframework.Collections.*;
-
 @RequestScoped
 public class Context implements Serializable {
 
-    @Inject
-    public Context() {}
-
-    /**
-     * Represents a map from all Klabels or attributes in string representation
-     * to sets of corresponding productions.
-     */
-    public SetMultimap tags = HashMultimap.create();
-
-    // TODO(dwightguth): remove these fields and replace with injected dependencies
-    @Deprecated @Inject public transient GlobalOptions globalOptions;
-    public KompileOptions kompileOptions;
-
-    public void addProduction(Production p) {
-        if (p.containsAttribute(Att.GROUP())) {
-            throw new AssertionError(
-                    "Must call ExpandGroupAttribute.apply(Definition) before creating a Context.");
-        }
-
-        if (p.getKLabel(false) != null) {
-            tags.put(p.getKLabel(false), p);
-        } else if (p.getAttributes().contains(Att.BRACKET())) {
-            tags.put(p.getBracketLabel(false), p);
-        }
-        for (Tuple2 a : iterable(p.getAttributes().att().keys())) {
-            tags.put(a._1.key(), p);
-        }
+  @Inject
+  public Context() {}
+
+  /**
+   * Represents a map from all Klabels or attributes in string representation to sets of
+   * corresponding productions.
+   */
+  public SetMultimap tags = HashMultimap.create();
+
+  // TODO(dwightguth): remove these fields and replace with injected dependencies
+  @Deprecated @Inject public transient GlobalOptions globalOptions;
+  public KompileOptions kompileOptions;
+
+  public void addProduction(Production p) {
+    if (p.containsAttribute(Att.GROUP())) {
+      throw new AssertionError(
+          "Must call ExpandGroupAttribute.apply(Definition) before creating a Context.");
+    }
+
+    if (p.getKLabel(false) != null) {
+      tags.put(p.getKLabel(false), p);
+    } else if (p.getAttributes().contains(Att.BRACKET())) {
+      tags.put(p.getBracketLabel(false), p);
+    }
+    for (Tuple2 a : iterable(p.getAttributes().att().keys())) {
+      tags.put(a._1.key(), p);
     }
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kompile/BackendModule.java b/kernel/src/main/java/org/kframework/kompile/BackendModule.java
index 5425cabee5c..5b04657ba3b 100644
--- a/kernel/src/main/java/org/kframework/kompile/BackendModule.java
+++ b/kernel/src/main/java/org/kframework/kompile/BackendModule.java
@@ -4,29 +4,29 @@
 import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
 import com.google.inject.multibindings.MapBinder;
+import java.util.Map;
 import org.kframework.backend.kore.KoreBackend;
 import org.kframework.compile.Backend;
 import org.kframework.utils.errorsystem.KEMException;
 import org.kframework.utils.errorsystem.KExceptionManager;
 
-import java.util.Map;
-
 public class BackendModule extends AbstractModule {
-    @Override
-    protected void configure() {
-        binder().requireAtInjectOnConstructors();
-        MapBinder backendBinder = MapBinder.newMapBinder(
-                binder(), String.class, org.kframework.compile.Backend.class);
-        backendBinder.addBinding("kore").to(KoreBackend.class);
-    }
+  @Override
+  protected void configure() {
+    binder().requireAtInjectOnConstructors();
+    MapBinder backendBinder =
+        MapBinder.newMapBinder(binder(), String.class, org.kframework.compile.Backend.class);
+    backendBinder.addBinding("kore").to(KoreBackend.class);
+  }
 
-    @Provides
-    org.kframework.compile.Backend getKoreBackend(KompileOptions options, Map map, KExceptionManager kem) {
-        org.kframework.compile.Backend backend = map.get(options.backend);
-        if (backend == null) {
-            throw KEMException.criticalError("Invalid backend: " + options.backend
-                    + ". It should be one of " + map.keySet());
-        }
-        return backend;
+  @Provides
+  org.kframework.compile.Backend getKoreBackend(
+      KompileOptions options, Map map, KExceptionManager kem) {
+    org.kframework.compile.Backend backend = map.get(options.backend);
+    if (backend == null) {
+      throw KEMException.criticalError(
+          "Invalid backend: " + options.backend + ". It should be one of " + map.keySet());
     }
+    return backend;
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kompile/CompiledDefinition.java b/kernel/src/main/java/org/kframework/kompile/CompiledDefinition.java
index a7f045feaab..1d6242aacc9 100644
--- a/kernel/src/main/java/org/kframework/kompile/CompiledDefinition.java
+++ b/kernel/src/main/java/org/kframework/kompile/CompiledDefinition.java
@@ -1,6 +1,17 @@
 // Copyright (c) K Team. All Rights Reserved.
 package org.kframework.kompile;
 
+import static org.kframework.Collections.*;
+import static org.kframework.definition.Constructors.*;
+import static org.kframework.kore.KORE.*;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
 import org.kframework.Collections;
 import org.kframework.attributes.Att;
 import org.kframework.attributes.Source;
@@ -23,204 +34,260 @@
 import org.kframework.parser.inner.ParseInModule;
 import org.kframework.parser.inner.RuleGrammarGenerator;
 import org.kframework.parser.outer.Outer;
+import org.kframework.utils.StringUtil;
 import org.kframework.utils.errorsystem.KEMException;
 import org.kframework.utils.errorsystem.KExceptionManager;
 import org.kframework.utils.file.FileUtil;
-import org.kframework.utils.StringUtil;
 import org.kframework.utils.options.InnerParsingOptions;
 import org.kframework.utils.options.OuterParsingOptions;
 import scala.Option;
 import scala.Tuple2;
 import scala.util.Either;
 
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
-
-import static org.kframework.Collections.*;
-import static org.kframework.definition.Constructors.*;
-import static org.kframework.kore.KORE.*;
-
 /**
- * A class representing a compiled definition. It has everything needed for executing and parsing programs.
+ * A class representing a compiled definition. It has everything needed for executing and parsing
+ * programs.
  */
-
 public class CompiledDefinition implements Serializable {
-    public final KompileOptions kompileOptions;
-    private final OuterParsingOptions outerParsingOptions;
-    private transient final GlobalOptions globalOptions;
-    private final InnerParsingOptions innerParsingOptions;
-    private final Definition parsedDefinition;
-    public final Definition kompiledDefinition;
-    public final Sort programStartSymbol;
-    public final HashMap configurationVariableDefaultSorts = new HashMap<>();
-    public final KLabel topCellInitializer;
-    private final Module languageParsingModule;
-    private final Map cachedcompiledPatterns = new ConcurrentHashMap<>();
-    private final Map cachedParsedPatterns = new ConcurrentHashMap<>();
-
-
-    public CompiledDefinition(KompileOptions kompileOptions, OuterParsingOptions outerParsingOptions, InnerParsingOptions innerParsingOptions, GlobalOptions globalOptions, Definition parsedDefinition, Definition kompiledDefinition, FileUtil files, KExceptionManager kem, KLabel topCellInitializer) {
-        this.kompileOptions = kompileOptions;
-        this.outerParsingOptions = outerParsingOptions;
-        this.innerParsingOptions = innerParsingOptions;
-        this.globalOptions = globalOptions;
-        this.parsedDefinition = parsedDefinition;
-        this.kompiledDefinition = kompiledDefinition;
-        initializeConfigurationVariableDefaultSorts(files);
-        this.programStartSymbol = configurationVariableDefaultSorts.getOrDefault("$PGM", Sorts.K());
-        this.topCellInitializer = topCellInitializer;
-        this.languageParsingModule = kompiledDefinition.getModule("LANGUAGE-PARSING").get();
-    }
+  public final KompileOptions kompileOptions;
+  private final OuterParsingOptions outerParsingOptions;
+  private final transient GlobalOptions globalOptions;
+  private final InnerParsingOptions innerParsingOptions;
+  private final Definition parsedDefinition;
+  public final Definition kompiledDefinition;
+  public final Sort programStartSymbol;
+  public final HashMap configurationVariableDefaultSorts = new HashMap<>();
+  public final KLabel topCellInitializer;
+  private final Module languageParsingModule;
+  private final Map cachedcompiledPatterns = new ConcurrentHashMap<>();
+  private final Map cachedParsedPatterns = new ConcurrentHashMap<>();
 
-    private Rule getExitCodeRule(Definition parsedDefinition) {
-        Module mainMod = parsedDefinition.mainModule();
-        Set exitProds = stream(mainMod.productions()).filter(p -> p.att().contains(Att.EXIT())).collect(Collectors.toSet());
-        if (exitProds.size() == 0) {
-            return null;
-        } else if (exitProds.size() > 1) {
-            throw KEMException.compilerError("Found more than one or zero productions with 'exit' attribute. Exactly one production, a cell, must have this attribute, designating the exit code of krun. Found:\n" + exitProds);
-        }
-        Production exitProd = exitProds.iterator().next();
-        return Rule(IncompleteCellUtils.make(exitProd.klabel().get(), false, KApply(KLabel("#SemanticCastToInt"), KVariable("_")), false), BooleanUtils.TRUE, BooleanUtils.TRUE);
+  public CompiledDefinition(
+      KompileOptions kompileOptions,
+      OuterParsingOptions outerParsingOptions,
+      InnerParsingOptions innerParsingOptions,
+      GlobalOptions globalOptions,
+      Definition parsedDefinition,
+      Definition kompiledDefinition,
+      FileUtil files,
+      KExceptionManager kem,
+      KLabel topCellInitializer) {
+    this.kompileOptions = kompileOptions;
+    this.outerParsingOptions = outerParsingOptions;
+    this.innerParsingOptions = innerParsingOptions;
+    this.globalOptions = globalOptions;
+    this.parsedDefinition = parsedDefinition;
+    this.kompiledDefinition = kompiledDefinition;
+    initializeConfigurationVariableDefaultSorts(files);
+    this.programStartSymbol = configurationVariableDefaultSorts.getOrDefault("$PGM", Sorts.K());
+    this.topCellInitializer = topCellInitializer;
+    this.languageParsingModule = kompiledDefinition.getModule("LANGUAGE-PARSING").get();
+  }
+
+  private Rule getExitCodeRule(Definition parsedDefinition) {
+    Module mainMod = parsedDefinition.mainModule();
+    Set exitProds =
+        stream(mainMod.productions())
+            .filter(p -> p.att().contains(Att.EXIT()))
+            .collect(Collectors.toSet());
+    if (exitProds.size() == 0) {
+      return null;
+    } else if (exitProds.size() > 1) {
+      throw KEMException.compilerError(
+          "Found more than one or zero productions with 'exit' attribute. Exactly one production, a"
+              + " cell, must have this attribute, designating the exit code of krun. Found:\n"
+              + exitProds);
     }
+    Production exitProd = exitProds.iterator().next();
+    return Rule(
+        IncompleteCellUtils.make(
+            exitProd.klabel().get(),
+            false,
+            KApply(KLabel("#SemanticCastToInt"), KVariable("_")),
+            false),
+        BooleanUtils.TRUE,
+        BooleanUtils.TRUE);
+  }
 
-    private void initializeConfigurationVariableDefaultSorts(FileUtil files) {
-        StringBuilder sb = new StringBuilder();
-        sb.append("#!/usr/bin/env bash\n\n");
-        StringBuilder arr = new StringBuilder("declaredConfigVars=(\n");
-        // searching for #SemanticCastTo(Map:lookup(_, #token(, KConfigVar)))
-        Collections.stream(kompiledDefinition.mainModule().rules())
-                .forEach(r -> {
-                    new VisitK() {
-                        @Override
-                        public void apply(KApply k) {
-                            if (k.klabel().name().startsWith("project:")
-                                    && k.items().size() == 1 && k.items().get(0) instanceof KApply theMapLookup) {
-                                if (KLabels.MAP_LOOKUP.equals(theMapLookup.klabel())
-                                        && theMapLookup.size() == 2 && theMapLookup.items().get(1) instanceof KToken t) {
-                                    if (t.sort().equals(Sorts.KConfigVar())) {
-                                        Sort sort = Outer.parseSort(k.klabel().name().substring("project:".length()));
-                                        configurationVariableDefaultSorts.put(t.s(), sort);
-                                        if (sort.equals(Sorts.K())) {
-                                          sort = Sorts.KItem();
-                                        }
-                                        String str = "declaredConfigVar_" + t.s().substring(1) + "='" + sort.toString() + "'\n";
-                                        sb.append(str);
-                                        String astr = "    '" + t.s().substring(1) + "'\n";
-                                        arr.append(astr);
-                                    }
-                                }
-                            }
-                            super.apply(k);
+  private void initializeConfigurationVariableDefaultSorts(FileUtil files) {
+    StringBuilder sb = new StringBuilder();
+    sb.append("#!/usr/bin/env bash\n\n");
+    StringBuilder arr = new StringBuilder("declaredConfigVars=(\n");
+    // searching for #SemanticCastTo(Map:lookup(_, #token(, KConfigVar)))
+    Collections.stream(kompiledDefinition.mainModule().rules())
+        .forEach(
+            r -> {
+              new VisitK() {
+                @Override
+                public void apply(KApply k) {
+                  if (k.klabel().name().startsWith("project:")
+                      && k.items().size() == 1
+                      && k.items().get(0) instanceof KApply theMapLookup) {
+                    if (KLabels.MAP_LOOKUP.equals(theMapLookup.klabel())
+                        && theMapLookup.size() == 2
+                        && theMapLookup.items().get(1) instanceof KToken t) {
+                      if (t.sort().equals(Sorts.KConfigVar())) {
+                        Sort sort =
+                            Outer.parseSort(k.klabel().name().substring("project:".length()));
+                        configurationVariableDefaultSorts.put(t.s(), sort);
+                        if (sort.equals(Sorts.K())) {
+                          sort = Sorts.KItem();
                         }
-                    }.apply(r.body());
-                });
-        sb.append(arr);
-        sb.append(")\n");
-
-        for (Production prod : iterable(kompiledDefinition.mainModule().productions())) {
-            if (prod.att().contains(Att.CELL()) && prod.att().contains(Att.PARSER())) {
-                String att = prod.att().get(Att.PARSER());
-                String[][] parts = StringUtil.splitTwoDimensionalAtt(att);
-                for (String[] part : parts) {
-                    if (part.length != 2) {
-                        throw KEMException.compilerError("Invalid value for parser attribute: " + att, prod);
+                        String str =
+                            "declaredConfigVar_"
+                                + t.s().substring(1)
+                                + "='"
+                                + sort.toString()
+                                + "'\n";
+                        sb.append(str);
+                        String astr = "    '" + t.s().substring(1) + "'\n";
+                        arr.append(astr);
+                      }
                     }
-                    String name = part[0];
-                    String module = part[1];
-                    sb.append("declaredConfigVarModule_" + name + "='" + module + "'\n");
+                  }
+                  super.apply(k);
                 }
-            }
-        }
+              }.apply(r.body());
+            });
+    sb.append(arr);
+    sb.append(")\n");
 
-        files.saveToKompiled("configVars.sh", sb.toString());
+    for (Production prod : iterable(kompiledDefinition.mainModule().productions())) {
+      if (prod.att().contains(Att.CELL()) && prod.att().contains(Att.PARSER())) {
+        String att = prod.att().get(Att.PARSER());
+        String[][] parts = StringUtil.splitTwoDimensionalAtt(att);
+        for (String[] part : parts) {
+          if (part.length != 2) {
+            throw KEMException.compilerError("Invalid value for parser attribute: " + att, prod);
+          }
+          String name = part[0];
+          String module = part[1];
+          sb.append("declaredConfigVarModule_" + name + "='" + module + "'\n");
+        }
+      }
     }
 
-    /**
-     * The parsed but uncompiled definition
-     */
-    public Definition getParsedDefinition() {
-        return parsedDefinition;
-    }
+    files.saveToKompiled("configVars.sh", sb.toString());
+  }
 
-    /**
-     * A module containing the compiled definition
-     */
-    public Module executionModule() {
-        return kompiledDefinition.mainModule();
-    }
+  /** The parsed but uncompiled definition */
+  public Definition getParsedDefinition() {
+    return parsedDefinition;
+  }
 
-    public String mainSyntaxModuleName() { return parsedDefinition.att().getOptional(Att.SYNTAX_MODULE()).get(); }
-
-    /**
-     * @return the module used for generating the program (i.e. ground) parser for the module named moduleName
-     * It automatically generates this module unless the user has already defined a module postfixed with
-     * {@link RuleGrammarGenerator#POSTFIX}. In latter case, it uses the user-defined module.
-     */
-    public Option programParsingModuleFor(String moduleName, KExceptionManager kem) {
-        RuleGrammarGenerator gen = new RuleGrammarGenerator(parsedDefinition);
-
-        Option userProgramParsingModule = parsedDefinition.getModule(moduleName + RuleGrammarGenerator.POSTFIX);
-        if (userProgramParsingModule.isDefined()) {
-            return userProgramParsingModule;
-        } else {
-            Option moduleOption = parsedDefinition.getModule(moduleName);
-            Option programParsingModuleOption = moduleOption.isDefined() ?
-                    Option.apply(gen.getProgramsGrammar(moduleOption.get())) :
-                    Option.empty();
-            return programParsingModuleOption;
-        }
-    }
+  /** A module containing the compiled definition */
+  public Module executionModule() {
+    return kompiledDefinition.mainModule();
+  }
 
-    public Option ruleParsingModuleFor(String moduleName) {
-        RuleGrammarGenerator gen = new RuleGrammarGenerator(kompiledDefinition);
+  public String mainSyntaxModuleName() {
+    return parsedDefinition.att().getOptional(Att.SYNTAX_MODULE()).get();
+  }
 
-        Option moduleOption = kompiledDefinition.getModule(moduleName);
-        if (!moduleOption.isDefined())
-            return Option.empty();
-        return Option.apply(gen.getRuleGrammar(moduleOption.get()));
-    }
+  /**
+   * @return the module used for generating the program (i.e. ground) parser for the module named
+   *     moduleName It automatically generates this module unless the user has already defined a
+   *     module postfixed with {@link RuleGrammarGenerator#POSTFIX}. In latter case, it uses the
+   *     user-defined module.
+   */
+  public Option programParsingModuleFor(String moduleName, KExceptionManager kem) {
+    RuleGrammarGenerator gen = new RuleGrammarGenerator(parsedDefinition);
 
-    public Module languageParsingModule() { return languageParsingModule; }
-
-    /**
-     * Creates a parser for a module and use it to parse a term.
-     *
-     * @return the parsed term.
-     */
-
-    public K parseSingleTerm(Module module, Sort programStartSymbol, String startSymbolLocation, KExceptionManager kem, FileUtil files, String s, Source source, boolean partialParseDebug) {
-        try (ParseInModule parseInModule = RuleGrammarGenerator.getCombinedGrammar(module, true, files, partialParseDebug)) {
-            Tuple2, K>, Set> res = parseInModule.parseString(s, programStartSymbol, startSymbolLocation, source);
-            kem.addAllKException(res._2().stream().map(e -> e.getKException()).collect(Collectors.toSet()));
-            if (res._1().isLeft()) {
-                throw res._1().left().get().iterator().next();
-            }
-            return new TreeNodesToKORE(Outer::parseSort, true).down(res._1().right().get());
-        }
+    Option userProgramParsingModule =
+        parsedDefinition.getModule(moduleName + RuleGrammarGenerator.POSTFIX);
+    if (userProgramParsingModule.isDefined()) {
+      return userProgramParsingModule;
+    } else {
+      Option moduleOption = parsedDefinition.getModule(moduleName);
+      Option programParsingModuleOption =
+          moduleOption.isDefined()
+              ? Option.apply(gen.getProgramsGrammar(moduleOption.get()))
+              : Option.empty();
+      return programParsingModuleOption;
     }
+  }
 
-    public String showTokens(Module module, FileUtil files, String s, Source source) {
-        try (ParseInModule parseInModule = RuleGrammarGenerator.getCombinedGrammar(module, true, files)) {
-            return parseInModule.tokenizeString(s, source);
-        }
-    }
+  public Option ruleParsingModuleFor(String moduleName) {
+    RuleGrammarGenerator gen = new RuleGrammarGenerator(kompiledDefinition);
 
-    public Module getExtensionModule(Module module, FileUtil files) {
-        return RuleGrammarGenerator.getCombinedGrammar(module, true, files).getExtensionModule();
-    }
+    Option moduleOption = kompiledDefinition.getModule(moduleName);
+    if (!moduleOption.isDefined()) return Option.empty();
+    return Option.apply(gen.getRuleGrammar(moduleOption.get()));
+  }
+
+  public Module languageParsingModule() {
+    return languageParsingModule;
+  }
 
-    public Rule compilePatternIfAbsent(FileUtil files, KExceptionManager kem, String pattern, Source source) {
-        return cachedcompiledPatterns.computeIfAbsent(pattern, p -> new Kompile(kompileOptions, outerParsingOptions, innerParsingOptions, globalOptions, files, kem).parseAndCompileRule(this, p, source,
-                Optional.of(parsePatternIfAbsent(files, kem, pattern, source))));
+  /**
+   * Creates a parser for a module and use it to parse a term.
+   *
+   * @return the parsed term.
+   */
+  public K parseSingleTerm(
+      Module module,
+      Sort programStartSymbol,
+      String startSymbolLocation,
+      KExceptionManager kem,
+      FileUtil files,
+      String s,
+      Source source,
+      boolean partialParseDebug) {
+    try (ParseInModule parseInModule =
+        RuleGrammarGenerator.getCombinedGrammar(module, true, files, partialParseDebug)) {
+      Tuple2, K>, Set> res =
+          parseInModule.parseString(s, programStartSymbol, startSymbolLocation, source);
+      kem.addAllKException(
+          res._2().stream().map(e -> e.getKException()).collect(Collectors.toSet()));
+      if (res._1().isLeft()) {
+        throw res._1().left().get().iterator().next();
+      }
+      return new TreeNodesToKORE(Outer::parseSort, true).down(res._1().right().get());
     }
+  }
 
-    public Rule parsePatternIfAbsent(FileUtil files, KExceptionManager kem, String pattern, Source source) {
-        return cachedParsedPatterns.computeIfAbsent(pattern, p -> new Kompile(kompileOptions, outerParsingOptions, innerParsingOptions, globalOptions, files, kem).parseRule(this, p, source));
+  public String showTokens(Module module, FileUtil files, String s, Source source) {
+    try (ParseInModule parseInModule =
+        RuleGrammarGenerator.getCombinedGrammar(module, true, files)) {
+      return parseInModule.tokenizeString(s, source);
     }
+  }
+
+  public Module getExtensionModule(Module module, FileUtil files) {
+    return RuleGrammarGenerator.getCombinedGrammar(module, true, files).getExtensionModule();
+  }
+
+  public Rule compilePatternIfAbsent(
+      FileUtil files, KExceptionManager kem, String pattern, Source source) {
+    return cachedcompiledPatterns.computeIfAbsent(
+        pattern,
+        p ->
+            new Kompile(
+                    kompileOptions,
+                    outerParsingOptions,
+                    innerParsingOptions,
+                    globalOptions,
+                    files,
+                    kem)
+                .parseAndCompileRule(
+                    this,
+                    p,
+                    source,
+                    Optional.of(parsePatternIfAbsent(files, kem, pattern, source))));
+  }
+
+  public Rule parsePatternIfAbsent(
+      FileUtil files, KExceptionManager kem, String pattern, Source source) {
+    return cachedParsedPatterns.computeIfAbsent(
+        pattern,
+        p ->
+            new Kompile(
+                    kompileOptions,
+                    outerParsingOptions,
+                    innerParsingOptions,
+                    globalOptions,
+                    files,
+                    kem)
+                .parseRule(this, p, source));
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kompile/DefinitionParsing.java b/kernel/src/main/java/org/kframework/kompile/DefinitionParsing.java
index 9134b70efe3..9c3d537ea02 100644
--- a/kernel/src/main/java/org/kframework/kompile/DefinitionParsing.java
+++ b/kernel/src/main/java/org/kframework/kompile/DefinitionParsing.java
@@ -1,8 +1,27 @@
 // Copyright (c) K Team. All Rights Reserved.
 package org.kframework.kompile;
 
+import static org.kframework.Collections.*;
+import static org.kframework.builtin.KLabels.*;
+import static org.kframework.definition.Constructors.*;
+import static org.kframework.definition.Constructors.Module;
+import static org.kframework.kore.KORE.*;
+
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import org.apache.commons.collections4.ListUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.kframework.Collections;
@@ -53,643 +72,937 @@
 import scala.collection.Set;
 import scala.util.Either;
 
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static org.kframework.Collections.*;
-import static org.kframework.builtin.KLabels.*;
-import static org.kframework.definition.Constructors.Module;
-import static org.kframework.definition.Constructors.*;
-import static org.kframework.kore.KORE.*;
-
 /**
- * A bundle of code doing various aspects of definition parsing.
- * TODO: In major need of refactoring.
- * @cos refactored this code out of Kompile but none (or close to none) of it was originally written by him.
+ * A bundle of code doing various aspects of definition parsing. TODO: In major need of refactoring.
+ *
+ * @cos refactored this code out of Kompile but none (or close to none) of it was originally written
+ *     by him.
  */
 public class DefinitionParsing {
-    public static final Sort START_SYMBOL = Sorts.RuleContent();
-    public static final String rule = "rule";
-    public static final String claim = "claim";
-    public static final String configuration = "config";
-    public static final String alias = "alias";
-    public static final String context = "context";
-    private final File cacheFile;
-    private final boolean autoImportDomains;
-    private final KompileOptions options;
-    private final GlobalOptions globalOptions;
-    private final OuterParsingOptions outerParsingOptions;
-
-    private final KExceptionManager kem;
-    private final FileUtil files;
-    private final ParserUtils parser;
-    private final boolean cacheParses;
-    private final BinaryLoader loader;
-    private final Stopwatch sw;
-
-    public final AtomicInteger parsedBubbles = new AtomicInteger(0);
-    public final AtomicInteger cachedBubbles = new AtomicInteger(0);
-    private final boolean profileRules;
-    private final List lookupDirectories;
-    private final InnerParsingOptions innerParsingOptions;
-
-    public DefinitionParsing(
-            List lookupDirectories,
-            KompileOptions options,
-            OuterParsingOptions outerParsingOptions,
-            InnerParsingOptions innerParsingOptions,
-            GlobalOptions globalOptions,
-            KExceptionManager kem,
-            FileUtil files,
-            ParserUtils parser,
-            boolean cacheParses,
-            File cacheFile,
-            Stopwatch sw) {
-        this.lookupDirectories = lookupDirectories;
-        this.options = options;
-        this.globalOptions = globalOptions;
-        this.outerParsingOptions = outerParsingOptions;
-        this.innerParsingOptions = innerParsingOptions;
-        this.kem = kem;
-        this.files = files;
-        this.parser = parser;
-        this.cacheParses = cacheParses;
-        this.cacheFile = cacheFile;
-        this.autoImportDomains = !outerParsingOptions.noPrelude;
-        this.loader = new BinaryLoader(this.kem);
-        this.profileRules = innerParsingOptions.profileRules != null;
-        this.sw = sw;
+  public static final Sort START_SYMBOL = Sorts.RuleContent();
+  public static final String rule = "rule";
+  public static final String claim = "claim";
+  public static final String configuration = "config";
+  public static final String alias = "alias";
+  public static final String context = "context";
+  private final File cacheFile;
+  private final boolean autoImportDomains;
+  private final KompileOptions options;
+  private final GlobalOptions globalOptions;
+  private final OuterParsingOptions outerParsingOptions;
+
+  private final KExceptionManager kem;
+  private final FileUtil files;
+  private final ParserUtils parser;
+  private final boolean cacheParses;
+  private final BinaryLoader loader;
+  private final Stopwatch sw;
+
+  public final AtomicInteger parsedBubbles = new AtomicInteger(0);
+  public final AtomicInteger cachedBubbles = new AtomicInteger(0);
+  private final boolean profileRules;
+  private final List lookupDirectories;
+  private final InnerParsingOptions innerParsingOptions;
+
+  public DefinitionParsing(
+      List lookupDirectories,
+      KompileOptions options,
+      OuterParsingOptions outerParsingOptions,
+      InnerParsingOptions innerParsingOptions,
+      GlobalOptions globalOptions,
+      KExceptionManager kem,
+      FileUtil files,
+      ParserUtils parser,
+      boolean cacheParses,
+      File cacheFile,
+      Stopwatch sw) {
+    this.lookupDirectories = lookupDirectories;
+    this.options = options;
+    this.globalOptions = globalOptions;
+    this.outerParsingOptions = outerParsingOptions;
+    this.innerParsingOptions = innerParsingOptions;
+    this.kem = kem;
+    this.files = files;
+    this.parser = parser;
+    this.cacheParses = cacheParses;
+    this.cacheFile = cacheFile;
+    this.autoImportDomains = !outerParsingOptions.noPrelude;
+    this.loader = new BinaryLoader(this.kem);
+    this.profileRules = innerParsingOptions.profileRules != null;
+    this.sw = sw;
+  }
+
+  public java.util.Set parseModules(
+      CompiledDefinition definition,
+      String mainModule,
+      String entryPointModule,
+      File definitionFile,
+      java.util.Set excludeModules,
+      boolean readOnlyCache,
+      boolean useCachedScanner) {
+    Definition def =
+        parser.loadDefinition(
+            mainModule,
+            mutable(definition.getParsedDefinition().modules()),
+            FileUtil.load(definitionFile),
+            Source.apply(definitionFile.getAbsolutePath()),
+            definitionFile.getParentFile(),
+            ListUtils.union(Lists.newArrayList(Kompile.BUILTIN_DIRECTORY), lookupDirectories),
+            options.preprocess,
+            options.bisonLists);
+
+    if (!def.getModule(mainModule).isDefined()) {
+      throw KEMException.criticalError("Module " + mainModule + " does not exist.");
     }
-
-    public java.util.Set parseModules(CompiledDefinition definition, String mainModule, String entryPointModule, File definitionFile, java.util.Set excludeModules, boolean readOnlyCache, boolean useCachedScanner) {
-        Definition def = parser.loadDefinition(
-                mainModule,
-                mutable(definition.getParsedDefinition().modules()),
-                FileUtil.load(definitionFile),
-                Source.apply(definitionFile.getAbsolutePath()),
-                definitionFile.getParentFile(),
-                ListUtils.union(Lists.newArrayList(Kompile.BUILTIN_DIRECTORY),
-                  lookupDirectories),
-                options.preprocess,
-                options.bisonLists);
-
-        if (!def.getModule(mainModule).isDefined()) {
-          throw KEMException.criticalError("Module " + mainModule + " does not exist.");
-        }
-        if (!def.getModule(entryPointModule).isDefined()) {
-          throw KEMException.criticalError("Module " + entryPointModule + " does not exist.");
-        }
-        if (profileRules) // create the temp dir ahead of parsing to avoid a race condition
-            files.resolveTemp(".");
-        Stream modules = Stream.of(def.getModule(mainModule).get());
-        modules = Stream.concat(modules, stream(def.getModule(mainModule).get().importedModules()));
-        modules = Stream.concat(modules, Stream.of(def.getModule(entryPointModule).get()));
-        modules = Stream.concat(modules, stream(def.getModule(entryPointModule).get().importedModules()));
-        modules = Stream.concat(modules,
-                stream(def.entryModules()).filter(m -> stream(m.sentences()).noneMatch(s -> s instanceof Bubble)));
-        def = Definition(def.mainModule(), modules.collect(Collections.toSet()), def.att());
-
-        def = Kompile.excludeModulesByTag(excludeModules, entryPointModule).apply(def);
-        sw.printIntermediate("Outer parsing [" + def.modules().size() + " modules]");
-
-        errors = java.util.Collections.synchronizedSet(Sets.newHashSet());
-        caches = loadCaches();
-
-        try {
-          def = resolveConfigBubbles(def);
-        } catch (KEMException e) {
-            errors.add(e);
-            throwExceptionIfThereAreErrors();
-            throw new AssertionError("should not reach this statement");
-        }
-
-        def = resolveNonConfigBubbles(def, false, useCachedScanner);
-        saveTimings();
-        if (! readOnlyCache) {
-            saveCaches();
-        }
-        throwExceptionIfThereAreErrors();
-        return mutable(def.entryModules());
+    if (!def.getModule(entryPointModule).isDefined()) {
+      throw KEMException.criticalError("Module " + entryPointModule + " does not exist.");
     }
-
-    public Map loadCaches() {
-        Map result;
-        //noinspection unchecked
-        result = cacheParses ? loader.loadCache(Map.class, cacheFile) : null;
-        if (result == null) {
-            result = new HashMap<>();
-        }
-        return result;
+    if (profileRules) // create the temp dir ahead of parsing to avoid a race condition
+    files.resolveTemp(".");
+    Stream modules = Stream.of(def.getModule(mainModule).get());
+    modules = Stream.concat(modules, stream(def.getModule(mainModule).get().importedModules()));
+    modules = Stream.concat(modules, Stream.of(def.getModule(entryPointModule).get()));
+    modules =
+        Stream.concat(modules, stream(def.getModule(entryPointModule).get().importedModules()));
+    modules =
+        Stream.concat(
+            modules,
+            stream(def.entryModules())
+                .filter(m -> stream(m.sentences()).noneMatch(s -> s instanceof Bubble)));
+    def = Definition(def.mainModule(), modules.collect(Collections.toSet()), def.att());
+
+    def = Kompile.excludeModulesByTag(excludeModules, entryPointModule).apply(def);
+    sw.printIntermediate("Outer parsing [" + def.modules().size() + " modules]");
+
+    errors = java.util.Collections.synchronizedSet(Sets.newHashSet());
+    caches = loadCaches();
+
+    try {
+      def = resolveConfigBubbles(def);
+    } catch (KEMException e) {
+      errors.add(e);
+      throwExceptionIfThereAreErrors();
+      throw new AssertionError("should not reach this statement");
     }
 
-    private void saveCachesAndReportParsingErrors() {
-        saveCaches();
-        throwExceptionIfThereAreErrors();
+    def = resolveNonConfigBubbles(def, false, useCachedScanner);
+    saveTimings();
+    if (!readOnlyCache) {
+      saveCaches();
     }
-
-    private void saveCaches() {
-        if (cacheParses) {
-            loader.saveOrDie(cacheFile, caches);
-        }
+    throwExceptionIfThereAreErrors();
+    return mutable(def.entryModules());
+  }
+
+  public Map loadCaches() {
+    Map result;
+    //noinspection unchecked
+    result = cacheParses ? loader.loadCache(Map.class, cacheFile) : null;
+    if (result == null) {
+      result = new HashMap<>();
     }
+    return result;
+  }
 
-    public Definition parseDefinitionAndResolveBubbles(File definitionFile, String mainModuleName, String mainProgramsModule, java.util.Set excludedModuleTags) {
-        Definition parsedDefinition = parseDefinition(definitionFile, mainModuleName, mainProgramsModule);
-        Stream modules = Stream.of(parsedDefinition.mainModule());
-        modules = Stream.concat(modules, stream(parsedDefinition.mainModule().importedModules()));
-        Option syntaxModule = parsedDefinition.getModule(mainProgramsModule);
-        if (syntaxModule.isDefined()) {
-            modules = Stream.concat(modules, Stream.of(syntaxModule.get()));
-            modules = Stream.concat(modules, stream(syntaxModule.get().importedModules()));
-        }
-        modules = Stream.concat(modules, Stream.of(parsedDefinition.getModule("K-REFLECTION").get()));
-        modules = Stream.concat(modules, Stream.of(parsedDefinition.getModule("STDIN-STREAM").get()));
-        modules = Stream.concat(modules, Stream.of(parsedDefinition.getModule("STDOUT-STREAM").get()));
-        modules = Stream.concat(modules, Stream.of(parsedDefinition.getModule("MAP").get()));
-        modules = Stream.concat(modules,
-                stream(parsedDefinition.entryModules()).filter(m -> stream(m.sentences()).noneMatch(s -> s instanceof Bubble)));
-        Definition trimmed = Definition(parsedDefinition.mainModule(), modules.collect(Collections.toSet()),
-                parsedDefinition.att());
-        trimmed = Kompile.excludeModulesByTag(excludedModuleTags, mainProgramsModule).apply(trimmed);
-        sw.printIntermediate("Outer parsing [" + trimmed.modules().size() + " modules]");
-        if (profileRules) // create the temp dir ahead of parsing to avoid a race condition
-            files.resolveTemp(".");
-        Definition afterResolvingConfigBubbles = resolveConfigBubbles(trimmed, parsedDefinition.getModule("DEFAULT-CONFIGURATION").get());
-        sw.printIntermediate("Parse configurations [" + parsedBubbles.get() + "/" + (parsedBubbles.get() + cachedBubbles.get()) + " declarations]");
-        parsedBubbles.set(0);
-        cachedBubbles.set(0);
-        Definition afterResolvingAllOtherBubbles = resolveNonConfigBubbles(afterResolvingConfigBubbles, true, false);
-        sw.printIntermediate("Parse rules [" + parsedBubbles.get() + "/" + (parsedBubbles.get() + cachedBubbles.get()) + " rules]");
-        saveTimings();
-        saveCachesAndReportParsingErrors();
-        return afterResolvingAllOtherBubbles;
-    }
+  private void saveCachesAndReportParsingErrors() {
+    saveCaches();
+    throwExceptionIfThereAreErrors();
+  }
 
-    private void throwExceptionIfThereAreErrors() {
-        if (!errors.isEmpty()) {
-            kem.addAllKException(errors.stream().map(e -> e.exception).collect(Collectors.toList()));
-            throw KEMException.compilerError("Had " + errors.size() + " parsing errors.");
-        }
+  private void saveCaches() {
+    if (cacheParses) {
+      loader.saveOrDie(cacheFile, caches);
     }
-
-    public Definition parseDefinition(File definitionFile, String mainModuleName, String mainProgramsModule) {
-        Definition definition = parser.loadDefinition(
-                mainModuleName,
-                mainProgramsModule, FileUtil.load(definitionFile),
-                definitionFile,
-                definitionFile.getParentFile(),
-                ListUtils.union(lookupDirectories,
-                        Lists.newArrayList(Kompile.BUILTIN_DIRECTORY)),
-                autoImportDomains,
-                options.preprocess,
-                options.bisonLists);
-        Module m = definition.mainModule();
-        return options.coverage ? DefinitionTransformer.from(mod -> mod.equals(m) ? Module(m.name(), (Set)m.imports().$bar(Set(Import(definition.getModule("K-IO").get(), true))), m.localSentences(), m.att()) : mod, "add implicit modules").apply(definition) : definition;
+  }
+
+  public Definition parseDefinitionAndResolveBubbles(
+      File definitionFile,
+      String mainModuleName,
+      String mainProgramsModule,
+      java.util.Set excludedModuleTags) {
+    Definition parsedDefinition =
+        parseDefinition(definitionFile, mainModuleName, mainProgramsModule);
+    Stream modules = Stream.of(parsedDefinition.mainModule());
+    modules = Stream.concat(modules, stream(parsedDefinition.mainModule().importedModules()));
+    Option syntaxModule = parsedDefinition.getModule(mainProgramsModule);
+    if (syntaxModule.isDefined()) {
+      modules = Stream.concat(modules, Stream.of(syntaxModule.get()));
+      modules = Stream.concat(modules, stream(syntaxModule.get().importedModules()));
     }
-
-    protected Definition resolveConfigBubbles(Definition definition, Module defaultConfiguration) {
-        Definition definitionWithConfigBubble = DefinitionTransformer.from(mod -> {
-            if (mod.name().equals(definition.mainModule().name())) {
-                boolean hasConfigDecl = stream(mod.sentences())
-                        .anyMatch(s -> s instanceof Bubble && ((Bubble) s).sentenceType().equals(configuration));
-                if (!hasConfigDecl) {
-                    return Module(mod.name(), mod.imports().$bar(Set(Import(defaultConfiguration, true))).seq(), mod.localSentences(), mod.att());
-                }
-            }
-            return mod;
-        }, "adding default configuration").apply(definition);
-
-        Module mapModule = definitionWithConfigBubble.getModule("MAP")
-                .getOrElse(() -> { throw KEMException.compilerError("Module MAP must be visible at the configuration declaration"); });
-        Definition definitionWithMapForConfig = DefinitionTransformer.from(mod -> {
-            boolean hasConfigDecl = stream(mod.localSentences())
-                    .anyMatch(s -> s instanceof Bubble && ((Bubble) s).sentenceType().equals(configuration));
-            if (hasConfigDecl) {
-                return Module(mod.name(), mod.imports().$bar(Set(Import(mapModule, true))).seq(), mod.localSentences(), mod.att());
-            }
-            return mod;
-        }, "adding MAP to modules with configs").apply(definitionWithConfigBubble);
-
-        errors = java.util.Collections.synchronizedSet(Sets.newHashSet());
-        caches = loadCaches();
-
-        Definition result;
-        try {
-            result = resolveConfigBubbles(definitionWithMapForConfig);
-        } catch (KEMException e) {
-            errors.add(e);
-            throwExceptionIfThereAreErrors();
-            throw new AssertionError("should not reach this statement");
-        }
-        throwExceptionIfThereAreErrors();
-        return result;
+    modules = Stream.concat(modules, Stream.of(parsedDefinition.getModule("K-REFLECTION").get()));
+    modules = Stream.concat(modules, Stream.of(parsedDefinition.getModule("STDIN-STREAM").get()));
+    modules = Stream.concat(modules, Stream.of(parsedDefinition.getModule("STDOUT-STREAM").get()));
+    modules = Stream.concat(modules, Stream.of(parsedDefinition.getModule("MAP").get()));
+    modules =
+        Stream.concat(
+            modules,
+            stream(parsedDefinition.entryModules())
+                .filter(m -> stream(m.sentences()).noneMatch(s -> s instanceof Bubble)));
+    Definition trimmed =
+        Definition(
+            parsedDefinition.mainModule(),
+            modules.collect(Collections.toSet()),
+            parsedDefinition.att());
+    trimmed = Kompile.excludeModulesByTag(excludedModuleTags, mainProgramsModule).apply(trimmed);
+    sw.printIntermediate("Outer parsing [" + trimmed.modules().size() + " modules]");
+    if (profileRules) // create the temp dir ahead of parsing to avoid a race condition
+    files.resolveTemp(".");
+    Definition afterResolvingConfigBubbles =
+        resolveConfigBubbles(trimmed, parsedDefinition.getModule("DEFAULT-CONFIGURATION").get());
+    sw.printIntermediate(
+        "Parse configurations ["
+            + parsedBubbles.get()
+            + "/"
+            + (parsedBubbles.get() + cachedBubbles.get())
+            + " declarations]");
+    parsedBubbles.set(0);
+    cachedBubbles.set(0);
+    Definition afterResolvingAllOtherBubbles =
+        resolveNonConfigBubbles(afterResolvingConfigBubbles, true, false);
+    sw.printIntermediate(
+        "Parse rules ["
+            + parsedBubbles.get()
+            + "/"
+            + (parsedBubbles.get() + cachedBubbles.get())
+            + " rules]");
+    saveTimings();
+    saveCachesAndReportParsingErrors();
+    return afterResolvingAllOtherBubbles;
+  }
+
+  private void throwExceptionIfThereAreErrors() {
+    if (!errors.isEmpty()) {
+      kem.addAllKException(errors.stream().map(e -> e.exception).collect(Collectors.toList()));
+      throw KEMException.compilerError("Had " + errors.size() + " parsing errors.");
     }
-
-    Map caches;
-    private java.util.Set errors;
-
-    public java.util.Set errors() {
-        return errors;
+  }
+
+  public Definition parseDefinition(
+      File definitionFile, String mainModuleName, String mainProgramsModule) {
+    Definition definition =
+        parser.loadDefinition(
+            mainModuleName,
+            mainProgramsModule,
+            FileUtil.load(definitionFile),
+            definitionFile,
+            definitionFile.getParentFile(),
+            ListUtils.union(lookupDirectories, Lists.newArrayList(Kompile.BUILTIN_DIRECTORY)),
+            autoImportDomains,
+            options.preprocess,
+            options.bisonLists);
+    Module m = definition.mainModule();
+    return options.coverage
+        ? DefinitionTransformer.from(
+                mod ->
+                    mod.equals(m)
+                        ? Module(
+                            m.name(),
+                            (Set)
+                                m.imports()
+                                    .$bar(Set(Import(definition.getModule("K-IO").get(), true))),
+                            m.localSentences(),
+                            m.att())
+                        : mod,
+                "add implicit modules")
+            .apply(definition)
+        : definition;
+  }
+
+  protected Definition resolveConfigBubbles(Definition definition, Module defaultConfiguration) {
+    Definition definitionWithConfigBubble =
+        DefinitionTransformer.from(
+                mod -> {
+                  if (mod.name().equals(definition.mainModule().name())) {
+                    boolean hasConfigDecl =
+                        stream(mod.sentences())
+                            .anyMatch(
+                                s ->
+                                    s instanceof Bubble
+                                        && ((Bubble) s).sentenceType().equals(configuration));
+                    if (!hasConfigDecl) {
+                      return Module(
+                          mod.name(),
+                          mod.imports().$bar(Set(Import(defaultConfiguration, true))).seq(),
+                          mod.localSentences(),
+                          mod.att());
+                    }
+                  }
+                  return mod;
+                },
+                "adding default configuration")
+            .apply(definition);
+
+    Module mapModule =
+        definitionWithConfigBubble
+            .getModule("MAP")
+            .getOrElse(
+                () -> {
+                  throw KEMException.compilerError(
+                      "Module MAP must be visible at the configuration declaration");
+                });
+    Definition definitionWithMapForConfig =
+        DefinitionTransformer.from(
+                mod -> {
+                  boolean hasConfigDecl =
+                      stream(mod.localSentences())
+                          .anyMatch(
+                              s ->
+                                  s instanceof Bubble
+                                      && ((Bubble) s).sentenceType().equals(configuration));
+                  if (hasConfigDecl) {
+                    return Module(
+                        mod.name(),
+                        mod.imports().$bar(Set(Import(mapModule, true))).seq(),
+                        mod.localSentences(),
+                        mod.att());
+                  }
+                  return mod;
+                },
+                "adding MAP to modules with configs")
+            .apply(definitionWithConfigBubble);
+
+    errors = java.util.Collections.synchronizedSet(Sets.newHashSet());
+    caches = loadCaches();
+
+    Definition result;
+    try {
+      result = resolveConfigBubbles(definitionWithMapForConfig);
+    } catch (KEMException e) {
+      errors.add(e);
+      throwExceptionIfThereAreErrors();
+      throw new AssertionError("should not reach this statement");
     }
-
-    private void checkConfigCells(Definition defWithParsedConfigs) {
-        // check for duplicate  cell declarations
-        List kcells = new ArrayList<>();
-        stream(defWithParsedConfigs.mainModule().sentences())
-                .filter(s -> s instanceof Configuration)
-                .forEach(s -> new VisitK() {
-                    @Override
-                    public void apply(KApply k) {
-                        if (k.klabel().equals(KLabel("#configCell"))) {
-                            KToken kt = (KToken) k.klist().items().get(0);
-                            assert kt.sort().equals(Sorts.CellName());
-                            if (kt.s().equals("k"))
-                                kcells.add(k);
-                            else if (kt.s().equals(GENERATED_TOP_CELL_NAME) || kt.s().equals(GENERATED_COUNTER_CELL_NAME)) {
-                                // check for definitions of generated cell names
-                                errors.add(KEMException.compilerError("Cell name <" + kt.s() + "> is reserved by K.", kt));
-                            }
-                        }
-                        super.apply(k);
+    throwExceptionIfThereAreErrors();
+    return result;
+  }
+
+  Map caches;
+  private java.util.Set errors;
+
+  public java.util.Set errors() {
+    return errors;
+  }
+
+  private void checkConfigCells(Definition defWithParsedConfigs) {
+    // check for duplicate  cell declarations
+    List kcells = new ArrayList<>();
+    stream(defWithParsedConfigs.mainModule().sentences())
+        .filter(s -> s instanceof Configuration)
+        .forEach(
+            s ->
+                new VisitK() {
+                  @Override
+                  public void apply(KApply k) {
+                    if (k.klabel().equals(KLabel("#configCell"))) {
+                      KToken kt = (KToken) k.klist().items().get(0);
+                      assert kt.sort().equals(Sorts.CellName());
+                      if (kt.s().equals("k")) kcells.add(k);
+                      else if (kt.s().equals(GENERATED_TOP_CELL_NAME)
+                          || kt.s().equals(GENERATED_COUNTER_CELL_NAME)) {
+                        // check for definitions of generated cell names
+                        errors.add(
+                            KEMException.compilerError(
+                                "Cell name <" + kt.s() + "> is reserved by K.", kt));
+                      }
                     }
+                    super.apply(k);
+                  }
                 }.apply(((Configuration) s).body()));
-        if (kcells.size() <= 1) {
-            return;
-        }
-        for (K kCellDecl: kcells) {
-            this.errors.add(KEMException.compilerError("Multiple K cell declarations detected. Only one  cell declaration is allowed.", kCellDecl));
-        }
-        throwExceptionIfThereAreErrors();
+    if (kcells.size() <= 1) {
+      return;
     }
-
-    private Definition resolveConfigBubbles(Definition def) {
-        Definition defWithCaches = resolveCachedBubbles(def, false);
-        RuleGrammarGenerator gen = new RuleGrammarGenerator(def);
-
-        // parse config bubbles in parallel
-        // step 1 - use scala parallel streams to generate parsers
-        // step 2 - use java parallel streams to parse sentences
-        // this avoids creation of extra (costly) threads at the cost
-        // of a small thread contention between the two thread pools
-        Map parsed = defWithCaches.parMap(m -> {
-            if (stream(m.localSentences()).noneMatch(s -> s instanceof Bubble && ((Bubble) s).sentenceType().equals(configuration)))
+    for (K kCellDecl : kcells) {
+      this.errors.add(
+          KEMException.compilerError(
+              "Multiple K cell declarations detected. Only one  cell declaration is allowed.",
+              kCellDecl));
+    }
+    throwExceptionIfThereAreErrors();
+  }
+
+  private Definition resolveConfigBubbles(Definition def) {
+    Definition defWithCaches = resolveCachedBubbles(def, false);
+    RuleGrammarGenerator gen = new RuleGrammarGenerator(def);
+
+    // parse config bubbles in parallel
+    // step 1 - use scala parallel streams to generate parsers
+    // step 2 - use java parallel streams to parse sentences
+    // this avoids creation of extra (costly) threads at the cost
+    // of a small thread contention between the two thread pools
+    Map parsed =
+        defWithCaches.parMap(
+            m -> {
+              if (stream(m.localSentences())
+                  .noneMatch(
+                      s ->
+                          s instanceof Bubble && ((Bubble) s).sentenceType().equals(configuration)))
                 return m;
-            Module configParserModule = gen.getConfigGrammar(m);
-            ParseCache cache = loadCache(configParserModule);
-            try (ParseInModule parser = RuleGrammarGenerator.getCombinedGrammar(cache.module(), true, profileRules, files, options.debugTypeInference)) {
-                // each parser gets its own scanner because config labels can conflict with user tokens
+              Module configParserModule = gen.getConfigGrammar(m);
+              ParseCache cache = loadCache(configParserModule);
+              try (ParseInModule parser =
+                  RuleGrammarGenerator.getCombinedGrammar(
+                      cache.module(), true, profileRules, files, options.debugTypeInference)) {
+                // each parser gets its own scanner because config labels can conflict with user
+                // tokens
                 parser.getScanner(globalOptions);
                 parser.initialize();
 
-                java.util.Set parsedSet = stream(m.localSentences())
-                        .filter(s -> s instanceof Bubble && ((Bubble) s).sentenceType().equals(configuration))
+                java.util.Set parsedSet =
+                    stream(m.localSentences())
+                        .filter(
+                            s ->
+                                s instanceof Bubble
+                                    && ((Bubble) s).sentenceType().equals(configuration))
                         .map(b -> (Bubble) b)
                         .parallel()
-                        .flatMap(b -> parseBubble(parser, cache.cache(), b)
-                                .map(p -> upSentence(p, b.sentenceType())))
+                        .flatMap(
+                            b ->
+                                parseBubble(parser, cache.cache(), b)
+                                    .map(p -> upSentence(p, b.sentenceType())))
                         .collect(Collectors.toSet());
-                Set allSent = m.localSentences().$bar(immutable(parsedSet)).filter(s -> !(s instanceof Bubble && ((Bubble) s).sentenceType().equals(configuration))).seq();
+                Set allSent =
+                    m.localSentences()
+                        .$bar(immutable(parsedSet))
+                        .filter(
+                            s ->
+                                !(s instanceof Bubble
+                                    && ((Bubble) s).sentenceType().equals(configuration)))
+                        .seq();
                 return Module(m.name(), m.imports(), allSent, m.att());
-            }
-        });
-
-        Definition defWithParsedConfigs = DefinitionTransformer.from(m ->
-                Module(m.name(), m.imports(), parsed.get(m.name()).localSentences(), m.att()),
-                "replace configs").apply(defWithCaches);
-
-        checkConfigCells(defWithParsedConfigs);
-
-        // replace config bubbles with the generated syntax and rules
-        return DefinitionTransformer.from(m -> {
-            if (stream(m.localSentences()).noneMatch(s -> s instanceof Configuration
-                    || (s instanceof SyntaxSort && s.att().contains(Att.TEMPORARY_CELL_SORT_DECL()))))
-              return m;
-
-            Set importedConfigurationSortsSubsortedToCell = stream(m.productions())
-                  .filter(p -> p.att().contains(Att.CELL()))
-                  .map(p -> Production(Seq(), Sorts.Cell(), Seq(NonTerminal(p.sort())))).collect(toSet());
-
-            Module module = Module(m.name(), m.imports(),
-                  (Set) m.localSentences().$bar(importedConfigurationSortsSubsortedToCell),
-                  m.att());
+              }
+            });
+
+    Definition defWithParsedConfigs =
+        DefinitionTransformer.from(
+                m -> Module(m.name(), m.imports(), parsed.get(m.name()).localSentences(), m.att()),
+                "replace configs")
+            .apply(defWithCaches);
+
+    checkConfigCells(defWithParsedConfigs);
+
+    // replace config bubbles with the generated syntax and rules
+    return DefinitionTransformer.from(
+            m -> {
+              if (stream(m.localSentences())
+                  .noneMatch(
+                      s ->
+                          s instanceof Configuration
+                              || (s instanceof SyntaxSort
+                                  && s.att().contains(Att.TEMPORARY_CELL_SORT_DECL())))) return m;
+
+              Set importedConfigurationSortsSubsortedToCell =
+                  stream(m.productions())
+                      .filter(p -> p.att().contains(Att.CELL()))
+                      .map(p -> Production(Seq(), Sorts.Cell(), Seq(NonTerminal(p.sort()))))
+                      .collect(toSet());
 
-            Module extMod = RuleGrammarGenerator.getCombinedGrammar(gen.getConfigGrammar(module), true, profileRules, files, options.debugTypeInference).getExtensionModule();
-            Set configDeclProductions = stream(module.localSentences())
+              Module module =
+                  Module(
+                      m.name(),
+                      m.imports(),
+                      (Set)
+                          m.localSentences().$bar(importedConfigurationSortsSubsortedToCell),
+                      m.att());
+
+              Module extMod =
+                  RuleGrammarGenerator.getCombinedGrammar(
+                          gen.getConfigGrammar(module),
+                          true,
+                          profileRules,
+                          files,
+                          options.debugTypeInference)
+                      .getExtensionModule();
+              Set configDeclProductions =
+                  stream(module.localSentences())
                       .filter(s -> s instanceof Configuration)
                       .map(b -> (Configuration) b)
-                      .flatMap(configDecl ->
-                              stream(GenerateSentencesFromConfigDecl.gen(configDecl.body(), configDecl.ensures(), configDecl.att(), extMod)))
+                      .flatMap(
+                          configDecl ->
+                              stream(
+                                  GenerateSentencesFromConfigDecl.gen(
+                                      configDecl.body(),
+                                      configDecl.ensures(),
+                                      configDecl.att(),
+                                      extMod)))
                       .collect(toSet());
 
-            Set stc = m.localSentences()
-                    .$bar(configDeclProductions)
-                    .filter(s -> !(s instanceof Configuration))
-                    .filter(s -> !(s instanceof SyntaxSort && s.att().contains(Att.TEMPORARY_CELL_SORT_DECL()))).seq();
-            Module newM = Module(m.name(), m.imports(), stc, m.att());
-            newM.checkSorts(); // ensure all the Cell sorts are defined
-            return newM;
-        }, "expand configs").apply(defWithParsedConfigs);
-    }
-
-    private Definition resolveNonConfigBubbles(Definition defWithConfig, boolean serializeScanner, boolean deserializeScanner) {
-        Definition defWithCaches = resolveCachedBubbles(defWithConfig, true);
-        RuleGrammarGenerator gen = new RuleGrammarGenerator(defWithCaches);
-        Module ruleParserModule = gen.getRuleGrammar(defWithCaches.mainModule());
-        ParseCache cache = loadCache(ruleParserModule);
-        try (ParseInModule parser = RuleGrammarGenerator.getCombinedGrammar(cache.module(), true, profileRules, false, true, files, options.debugTypeInference, false)) {
-            Scanner scanner;
-            if (deserializeScanner) {
-                scanner = new Scanner(parser, globalOptions, files.resolveKompiled("scanner"));
-                parser.setScanner(scanner);
-            } else {
-                scanner = parser.getScanner(globalOptions);
-                if (serializeScanner) {
-                    scanner.serialize(files.resolveKompiled("scanner"));
-                }
-            }
-            final Scanner realScanner = scanner;
-            Map parsed = defWithCaches.parMap(m -> this.resolveNonConfigBubbles(m, realScanner, gen));
-            return DefinitionTransformer.from(m -> Module(m.name(), m.imports(), parsed.get(m.name()).localSentences(), m.att()), "parsing rules").apply(defWithConfig);
+              Set stc =
+                  m.localSentences()
+                      .$bar(configDeclProductions)
+                      .filter(s -> !(s instanceof Configuration))
+                      .filter(
+                          s ->
+                              !(s instanceof SyntaxSort
+                                  && s.att().contains(Att.TEMPORARY_CELL_SORT_DECL())))
+                      .seq();
+              Module newM = Module(m.name(), m.imports(), stc, m.att());
+              newM.checkSorts(); // ensure all the Cell sorts are defined
+              return newM;
+            },
+            "expand configs")
+        .apply(defWithParsedConfigs);
+  }
+
+  private Definition resolveNonConfigBubbles(
+      Definition defWithConfig, boolean serializeScanner, boolean deserializeScanner) {
+    Definition defWithCaches = resolveCachedBubbles(defWithConfig, true);
+    RuleGrammarGenerator gen = new RuleGrammarGenerator(defWithCaches);
+    Module ruleParserModule = gen.getRuleGrammar(defWithCaches.mainModule());
+    ParseCache cache = loadCache(ruleParserModule);
+    try (ParseInModule parser =
+        RuleGrammarGenerator.getCombinedGrammar(
+            cache.module(),
+            true,
+            profileRules,
+            false,
+            true,
+            files,
+            options.debugTypeInference,
+            false)) {
+      Scanner scanner;
+      if (deserializeScanner) {
+        scanner = new Scanner(parser, globalOptions, files.resolveKompiled("scanner"));
+        parser.setScanner(scanner);
+      } else {
+        scanner = parser.getScanner(globalOptions);
+        if (serializeScanner) {
+          scanner.serialize(files.resolveKompiled("scanner"));
         }
+      }
+      final Scanner realScanner = scanner;
+      Map parsed =
+          defWithCaches.parMap(m -> this.resolveNonConfigBubbles(m, realScanner, gen));
+      return DefinitionTransformer.from(
+              m -> Module(m.name(), m.imports(), parsed.get(m.name()).localSentences(), m.att()),
+              "parsing rules")
+          .apply(defWithConfig);
     }
-
-    private Module resolveNonConfigBubbles(Module module, Scanner scanner, RuleGrammarGenerator gen) {
-        if (stream(module.localSentences()).noneMatch(s -> s instanceof Bubble))
-            return module;
-
-        Module ruleParserModule = gen.getRuleGrammar(module);
-        // this scanner is not good for this module, so we must generate a new scanner.
-        boolean needNewScanner = !scanner.getModule().importedModuleNames().contains(module.name());
-
-        ParseCache cache = loadCache(ruleParserModule);
-        try (ParseInModule parser = needNewScanner ?
-                RuleGrammarGenerator.getCombinedGrammar(cache.module(), true, profileRules, files, options.debugTypeInference) :
-                RuleGrammarGenerator.getCombinedGrammar(cache.module(), scanner, true, profileRules, false, files, options.debugTypeInference, false)) {
-            if (needNewScanner)
-                parser.getScanner(globalOptions);
-            parser.initialize();
-
-            Set parsedSet = stream(module.localSentences())
-                    .parallel()
-                    .filter(s -> s instanceof Bubble)
-                    .map(b -> (Bubble) b)
-                    .flatMap(b -> parseBubble(parser, cache.cache(), b)
-                            .map(p -> upSentence(p, b.sentenceType())))
-                    .collect(Collections.toSet());
-
-            if (needNewScanner) {
-                parser.getScanner().close();//required for Windows.
-            }
-
-            return Module(module.name(), module.imports(),
-                    stream((Set) module.localSentences().$bar(parsedSet)).filter(b -> !(b instanceof Bubble)).collect(Collections.toSet()), module.att());
-        }
+  }
+
+  private Module resolveNonConfigBubbles(Module module, Scanner scanner, RuleGrammarGenerator gen) {
+    if (stream(module.localSentences()).noneMatch(s -> s instanceof Bubble)) return module;
+
+    Module ruleParserModule = gen.getRuleGrammar(module);
+    // this scanner is not good for this module, so we must generate a new scanner.
+    boolean needNewScanner = !scanner.getModule().importedModuleNames().contains(module.name());
+
+    ParseCache cache = loadCache(ruleParserModule);
+    try (ParseInModule parser =
+        needNewScanner
+            ? RuleGrammarGenerator.getCombinedGrammar(
+                cache.module(), true, profileRules, files, options.debugTypeInference)
+            : RuleGrammarGenerator.getCombinedGrammar(
+                cache.module(),
+                scanner,
+                true,
+                profileRules,
+                false,
+                files,
+                options.debugTypeInference,
+                false)) {
+      if (needNewScanner) parser.getScanner(globalOptions);
+      parser.initialize();
+
+      Set parsedSet =
+          stream(module.localSentences())
+              .parallel()
+              .filter(s -> s instanceof Bubble)
+              .map(b -> (Bubble) b)
+              .flatMap(
+                  b ->
+                      parseBubble(parser, cache.cache(), b)
+                          .map(p -> upSentence(p, b.sentenceType())))
+              .collect(Collections.toSet());
+
+      if (needNewScanner) {
+        parser.getScanner().close(); // required for Windows.
+      }
+
+      return Module(
+          module.name(),
+          module.imports(),
+          stream((Set) module.localSentences().$bar(parsedSet))
+              .filter(b -> !(b instanceof Bubble))
+              .collect(Collections.toSet()),
+          module.att());
     }
-
-    /**
-     * Replace all the targeted Bubbles from the definition if they can be found in caches.
-     * @param def    The Definition with Bubbles.
-     * @param isRule true if it should target non config Bubbles, false if it should parse only config bubbles
-     * @return A new Definition object with Bubbles replaced by the appropriate Sentence type.
-     */
-    private Definition resolveCachedBubbles(Definition def, boolean isRule) {
-        RuleGrammarGenerator gen = new RuleGrammarGenerator(def);
-        return DefinitionTransformer.from(m -> {
-            if (stream(m.localSentences()).noneMatch(s -> s instanceof Bubble && (isRule || ((Bubble) s).sentenceType().equals(configuration))))
+  }
+
+  /**
+   * Replace all the targeted Bubbles from the definition if they can be found in caches.
+   *
+   * @param def The Definition with Bubbles.
+   * @param isRule true if it should target non config Bubbles, false if it should parse only config
+   *     bubbles
+   * @return A new Definition object with Bubbles replaced by the appropriate Sentence type.
+   */
+  private Definition resolveCachedBubbles(Definition def, boolean isRule) {
+    RuleGrammarGenerator gen = new RuleGrammarGenerator(def);
+    return DefinitionTransformer.from(
+            m -> {
+              if (stream(m.localSentences())
+                  .noneMatch(
+                      s ->
+                          s instanceof Bubble
+                              && (isRule || ((Bubble) s).sentenceType().equals(configuration))))
                 return m;
-            ParseCache cache = isRule ? loadCache(gen.getRuleGrammar(m)) : loadCache(gen.getConfigGrammar(m));
-
-            Map fromCache = stream(m.localSentences())
-                    .filter(s -> s instanceof Bubble && (isRule || ((Bubble) s).sentenceType().equals(configuration)))
-                    .map(b -> (Bubble) b)
-                    .flatMap(b -> {
-                        if (cache.cache().containsKey(b.contents()) && cache.cache().get(b.contents()).parse() != null) {
-                            ParsedSentence parse = updateLocation(cache.cache().get(b.contents()), b);
-                            Att termAtt = parse.parse().att().remove(Source.class).remove(Location.class).remove(Production.class);
-                            Att bubbleAtt = b.att().remove(Source.class).remove(Location.class).remove(Att.CONTENT_START_LINE(), Integer.class).remove(Att.CONTENT_START_COLUMN(), Integer.class);
-                            if (!termAtt.equals(bubbleAtt)) // invalidate cache if attributes changed
-                                return Stream.of();
-                            cachedBubbles.getAndIncrement();
-                            registerWarnings(parse.warnings());
-                            KApply k = (KApply) new TreeNodesToKORE(Outer::parseSort, true).down(parse.parse());
-                            return Stream.of(Pair.of(b, upSentence(k, b.sentenceType())));
-                        }
-                        return Stream.of();
-                    }).collect(Collectors.toMap(Pair::getKey, Pair::getValue));
-
-            if (!fromCache.isEmpty()) {
-                Set stc = m.localSentences()
+              ParseCache cache =
+                  isRule ? loadCache(gen.getRuleGrammar(m)) : loadCache(gen.getConfigGrammar(m));
+
+              Map fromCache =
+                  stream(m.localSentences())
+                      .filter(
+                          s ->
+                              s instanceof Bubble
+                                  && (isRule || ((Bubble) s).sentenceType().equals(configuration)))
+                      .map(b -> (Bubble) b)
+                      .flatMap(
+                          b -> {
+                            if (cache.cache().containsKey(b.contents())
+                                && cache.cache().get(b.contents()).parse() != null) {
+                              ParsedSentence parse =
+                                  updateLocation(cache.cache().get(b.contents()), b);
+                              Att termAtt =
+                                  parse
+                                      .parse()
+                                      .att()
+                                      .remove(Source.class)
+                                      .remove(Location.class)
+                                      .remove(Production.class);
+                              Att bubbleAtt =
+                                  b.att()
+                                      .remove(Source.class)
+                                      .remove(Location.class)
+                                      .remove(Att.CONTENT_START_LINE(), Integer.class)
+                                      .remove(Att.CONTENT_START_COLUMN(), Integer.class);
+                              if (!termAtt.equals(
+                                  bubbleAtt)) // invalidate cache if attributes changed
+                              return Stream.of();
+                              cachedBubbles.getAndIncrement();
+                              registerWarnings(parse.warnings());
+                              KApply k =
+                                  (KApply)
+                                      new TreeNodesToKORE(Outer::parseSort, true)
+                                          .down(parse.parse());
+                              return Stream.of(Pair.of(b, upSentence(k, b.sentenceType())));
+                            }
+                            return Stream.of();
+                          })
+                      .collect(Collectors.toMap(Pair::getKey, Pair::getValue));
+
+              if (!fromCache.isEmpty()) {
+                Set stc =
+                    m.localSentences()
                         .$bar(immutable(Sets.newHashSet(fromCache.values())))
-                        .filter(s -> !(s instanceof Bubble && fromCache.containsKey(s))).seq();
+                        .filter(s -> !(s instanceof Bubble && fromCache.containsKey(s)))
+                        .seq();
                 return Module(m.name(), m.imports(), stc, m.att());
-            }
-            return m;
-        }, "load cached bubbles").apply(def);
-    }
-
-    public static ParsedSentence updateLocation(ParsedSentence parse, Bubble b) {
-        int newStartLine = b.att().get(Att.CONTENT_START_LINE(), Integer.class);
-        int newStartColumn = b.att().get(Att.CONTENT_START_COLUMN(), Integer.class);
-        int oldStartLine = parse.startLine();
-        int oldStartColumn = parse.startColumn();
-        if (oldStartLine != newStartLine || oldStartColumn != newStartColumn || !parse.source().equals(b.source().get())) {
-            int lineOffset = newStartLine - oldStartLine;
-            int columnOffset = newStartColumn - oldStartColumn;
-            K k = parse.parse() != null ? new AddAttRec(a -> {
-                Location loc = a.get(Location.class);
-                Location newLoc = updateLocation(oldStartLine, lineOffset, columnOffset, loc);
-                return a.remove(Source.class).remove(Location.class).add(Location.class, newLoc)
-                        .add(Source.class, b.source().orElseThrow(() -> new AssertionError("Expecting bubble to have source location!")));
-            }).apply(parse.parse()) : null;
-            java.util.Set warnings = parse.warnings().stream().map(ex -> ex.withLocation(updateLocation(oldStartLine, lineOffset, columnOffset, ex.exception.getLocation()),
-                            b.source().orElseThrow(() -> new AssertionError("Expecting bubble to have source location!"))))
-                    .collect(Collectors.toSet());
-            java.util.Set errors = parse.errors().stream().map(ex -> ex.withLocation(updateLocation(oldStartLine, lineOffset, columnOffset, ex.exception.getLocation()),
-                            b.source().orElseThrow(() -> new AssertionError("Expecting bubble to have source location!"))))
-                    .collect(Collectors.toSet());
-            return new ParsedSentence(k, warnings, errors, newStartLine, newStartColumn, parse.source());
-        }
-        return parse;
-    }
-
-    private static Location updateLocation(int oldStartLine, int lineOffset, int columnOffset, Location loc) {
-        return Location.apply(
-                loc.startLine() + lineOffset,
-                // only the first line can have column offset, otherwise it will trigger a cache miss
-                oldStartLine == loc.startLine() ? loc.startColumn() + columnOffset : loc.startColumn(),
-                loc.endLine() + lineOffset,
-                oldStartLine == loc.endLine() ? loc.endColumn() + columnOffset : loc.endColumn()
-        );
-    }
-
-    private void registerWarnings(java.util.Set warnings) {
-        if (kem.options.warnings2errors) {
-            for (KEMException err : warnings) {
-                if (kem.options.includesExceptionType(err.exception.getType())) {
-                    errors.add(KEMException.asError(err));
-                }
-            }
-        } else {
-            kem.addAllKException(warnings.stream().map(KEMException::getKException).collect(Collectors.toList()));
-        }
+              }
+              return m;
+            },
+            "load cached bubbles")
+        .apply(def);
+  }
+
+  public static ParsedSentence updateLocation(ParsedSentence parse, Bubble b) {
+    int newStartLine = b.att().get(Att.CONTENT_START_LINE(), Integer.class);
+    int newStartColumn = b.att().get(Att.CONTENT_START_COLUMN(), Integer.class);
+    int oldStartLine = parse.startLine();
+    int oldStartColumn = parse.startColumn();
+    if (oldStartLine != newStartLine
+        || oldStartColumn != newStartColumn
+        || !parse.source().equals(b.source().get())) {
+      int lineOffset = newStartLine - oldStartLine;
+      int columnOffset = newStartColumn - oldStartColumn;
+      K k =
+          parse.parse() != null
+              ? new AddAttRec(
+                      a -> {
+                        Location loc = a.get(Location.class);
+                        Location newLoc =
+                            updateLocation(oldStartLine, lineOffset, columnOffset, loc);
+                        return a.remove(Source.class)
+                            .remove(Location.class)
+                            .add(Location.class, newLoc)
+                            .add(
+                                Source.class,
+                                b.source()
+                                    .orElseThrow(
+                                        () ->
+                                            new AssertionError(
+                                                "Expecting bubble to have source location!")));
+                      })
+                  .apply(parse.parse())
+              : null;
+      java.util.Set warnings =
+          parse.warnings().stream()
+              .map(
+                  ex ->
+                      ex.withLocation(
+                          updateLocation(
+                              oldStartLine, lineOffset, columnOffset, ex.exception.getLocation()),
+                          b.source()
+                              .orElseThrow(
+                                  () ->
+                                      new AssertionError(
+                                          "Expecting bubble to have source location!"))))
+              .collect(Collectors.toSet());
+      java.util.Set errors =
+          parse.errors().stream()
+              .map(
+                  ex ->
+                      ex.withLocation(
+                          updateLocation(
+                              oldStartLine, lineOffset, columnOffset, ex.exception.getLocation()),
+                          b.source()
+                              .orElseThrow(
+                                  () ->
+                                      new AssertionError(
+                                          "Expecting bubble to have source location!"))))
+              .collect(Collectors.toSet());
+      return new ParsedSentence(k, warnings, errors, newStartLine, newStartColumn, parse.source());
     }
-
-    public Rule parseRule(CompiledDefinition compiledDef, String contents, Source source) {
-        errors = java.util.Collections.synchronizedSet(Sets.newHashSet());
-        RuleGrammarGenerator gen = new RuleGrammarGenerator(compiledDef.kompiledDefinition);
-        try (ParseInModule parser = RuleGrammarGenerator
-                .getCombinedGrammar(gen.getRuleGrammar(compiledDef.getParsedDefinition().mainModule()), true, profileRules, false, true, files, options.debugTypeInference, false)) {
-            parser.setScanner(new Scanner(parser, globalOptions, files.resolveKompiled("scanner")));
-            java.util.Set res = parseBubble(parser, new HashMap<>(),
-                    new Bubble(rule, contents, Att().add(Att.CONTENT_START_LINE(), 1)
-                            .add(Att.CONTENT_START_COLUMN(), 1).add(Source.class, source)))
-                    .collect(Collectors.toSet());
-            if (!errors.isEmpty()) {
-                throw errors.iterator().next();
-            }
-            return upRule(res.iterator().next());
+    return parse;
+  }
+
+  private static Location updateLocation(
+      int oldStartLine, int lineOffset, int columnOffset, Location loc) {
+    return Location.apply(
+        loc.startLine() + lineOffset,
+        // only the first line can have column offset, otherwise it will trigger a cache miss
+        oldStartLine == loc.startLine() ? loc.startColumn() + columnOffset : loc.startColumn(),
+        loc.endLine() + lineOffset,
+        oldStartLine == loc.endLine() ? loc.endColumn() + columnOffset : loc.endColumn());
+  }
+
+  private void registerWarnings(java.util.Set warnings) {
+    if (kem.options.warnings2errors) {
+      for (KEMException err : warnings) {
+        if (kem.options.includesExceptionType(err.exception.getType())) {
+          errors.add(KEMException.asError(err));
         }
+      }
+    } else {
+      kem.addAllKException(
+          warnings.stream().map(KEMException::getKException).collect(Collectors.toList()));
     }
-
-    private Sentence upSentence(K contents, String sentenceType) {
-        switch (sentenceType) {
-        case claim:         return upClaim(contents);
-        case rule:          return upRule(contents);
-        case context:       return upContext(contents);
-        case alias:         return upAlias(contents);
-        case configuration: return upConfiguration(contents);
-        }
-        throw new AssertionError("Unexpected sentence type: " + sentenceType);
+  }
+
+  public Rule parseRule(CompiledDefinition compiledDef, String contents, Source source) {
+    errors = java.util.Collections.synchronizedSet(Sets.newHashSet());
+    RuleGrammarGenerator gen = new RuleGrammarGenerator(compiledDef.kompiledDefinition);
+    try (ParseInModule parser =
+        RuleGrammarGenerator.getCombinedGrammar(
+            gen.getRuleGrammar(compiledDef.getParsedDefinition().mainModule()),
+            true,
+            profileRules,
+            false,
+            true,
+            files,
+            options.debugTypeInference,
+            false)) {
+      parser.setScanner(new Scanner(parser, globalOptions, files.resolveKompiled("scanner")));
+      java.util.Set res =
+          parseBubble(
+                  parser,
+                  new HashMap<>(),
+                  new Bubble(
+                      rule,
+                      contents,
+                      Att()
+                          .add(Att.CONTENT_START_LINE(), 1)
+                          .add(Att.CONTENT_START_COLUMN(), 1)
+                          .add(Source.class, source)))
+              .collect(Collectors.toSet());
+      if (!errors.isEmpty()) {
+        throw errors.iterator().next();
+      }
+      return upRule(res.iterator().next());
     }
-
-    private Claim upClaim(K contents) {
-        KApply ruleContents = (KApply) contents;
-        List items = ruleContents.klist().items();
-        return switch (ruleContents.klabel().name()) {
-            case "#ruleNoConditions" -> Claim(items.get(0), BooleanUtils.TRUE, BooleanUtils.TRUE, ruleContents.att());
-            case "#ruleRequires" -> Claim(items.get(0), items.get(1), BooleanUtils.TRUE, ruleContents.att());
-            case "#ruleEnsures" -> Claim(items.get(0), BooleanUtils.TRUE, items.get(1), ruleContents.att());
-            case "#ruleRequiresEnsures" -> Claim(items.get(0), items.get(1), items.get(2), ruleContents.att());
-            default -> throw new AssertionError("Wrong KLabel for claim content");
-        };
+  }
+
+  private Sentence upSentence(K contents, String sentenceType) {
+    switch (sentenceType) {
+      case claim:
+        return upClaim(contents);
+      case rule:
+        return upRule(contents);
+      case context:
+        return upContext(contents);
+      case alias:
+        return upAlias(contents);
+      case configuration:
+        return upConfiguration(contents);
     }
-
-    private Rule upRule(K contents) {
-        KApply ruleContents = (KApply) contents;
-        List items = ruleContents.klist().items();
-        return switch (ruleContents.klabel().name()) {
-            case "#ruleNoConditions" -> Rule(items.get(0), BooleanUtils.TRUE, BooleanUtils.TRUE, ruleContents.att());
-            case "#ruleRequires" -> Rule(items.get(0), items.get(1), BooleanUtils.TRUE, ruleContents.att());
-            case "#ruleEnsures" -> Rule(items.get(0), BooleanUtils.TRUE, items.get(1), ruleContents.att());
-            case "#ruleRequiresEnsures" -> Rule(items.get(0), items.get(1), items.get(2), ruleContents.att());
-            default -> throw new AssertionError("Wrong KLabel for rule content");
-        };
+    throw new AssertionError("Unexpected sentence type: " + sentenceType);
+  }
+
+  private Claim upClaim(K contents) {
+    KApply ruleContents = (KApply) contents;
+    List items = ruleContents.klist().items();
+    return switch (ruleContents.klabel().name()) {
+      case "#ruleNoConditions" -> Claim(
+          items.get(0), BooleanUtils.TRUE, BooleanUtils.TRUE, ruleContents.att());
+      case "#ruleRequires" -> Claim(
+          items.get(0), items.get(1), BooleanUtils.TRUE, ruleContents.att());
+      case "#ruleEnsures" -> Claim(
+          items.get(0), BooleanUtils.TRUE, items.get(1), ruleContents.att());
+      case "#ruleRequiresEnsures" -> Claim(
+          items.get(0), items.get(1), items.get(2), ruleContents.att());
+      default -> throw new AssertionError("Wrong KLabel for claim content");
+    };
+  }
+
+  private Rule upRule(K contents) {
+    KApply ruleContents = (KApply) contents;
+    List items = ruleContents.klist().items();
+    return switch (ruleContents.klabel().name()) {
+      case "#ruleNoConditions" -> Rule(
+          items.get(0), BooleanUtils.TRUE, BooleanUtils.TRUE, ruleContents.att());
+      case "#ruleRequires" -> Rule(
+          items.get(0), items.get(1), BooleanUtils.TRUE, ruleContents.att());
+      case "#ruleEnsures" -> Rule(
+          items.get(0), BooleanUtils.TRUE, items.get(1), ruleContents.att());
+      case "#ruleRequiresEnsures" -> Rule(
+          items.get(0), items.get(1), items.get(2), ruleContents.att());
+      default -> throw new AssertionError("Wrong KLabel for rule content");
+    };
+  }
+
+  private Context upContext(K contents) {
+    KApply ruleContents = (KApply) contents;
+    List items = ruleContents.klist().items();
+    return switch (ruleContents.klabel().name()) {
+      case "#ruleNoConditions" -> Context(items.get(0), BooleanUtils.TRUE, ruleContents.att());
+      case "#ruleRequires" -> Context(items.get(0), items.get(1), ruleContents.att());
+      default -> throw KEMException.criticalError(
+          "Illegal context with ensures clause detected.", contents);
+    };
+  }
+
+  private ContextAlias upAlias(K contents) {
+    KApply ruleContents = (KApply) contents;
+    List items = ruleContents.klist().items();
+    return switch (ruleContents.klabel().name()) {
+      case "#ruleNoConditions" -> ContextAlias(items.get(0), BooleanUtils.TRUE, ruleContents.att());
+      case "#ruleRequires" -> ContextAlias(items.get(0), items.get(1), ruleContents.att());
+      default -> throw KEMException.criticalError(
+          "Illegal context alias with ensures clause detected.", contents);
+    };
+  }
+
+  private Configuration upConfiguration(K contents) {
+    KApply configContents = (KApply) contents;
+    List items = configContents.klist().items();
+    return switch (configContents.klabel().name()) {
+      case "#ruleNoConditions" -> Configuration(
+          items.get(0), BooleanUtils.TRUE, configContents.att());
+      case "#ruleEnsures" -> Configuration(items.get(0), items.get(1), configContents.att());
+      default -> throw KEMException.compilerError(
+          "Illegal configuration with requires clause detected.", configContents);
+    };
+  }
+
+  private ParseCache loadCache(Module parser) {
+    ParseCache cachedParser = caches.get(parser.name());
+    if (cachedParser == null
+        || !equalsSyntax(cachedParser.module().signature(), parser.signature())) {
+      cachedParser =
+          new ParseCache(parser, true, java.util.Collections.synchronizedMap(new HashMap<>()));
+      caches.put(parser.name(), cachedParser);
     }
-
-    private Context upContext(K contents) {
-        KApply ruleContents = (KApply) contents;
-        List items = ruleContents.klist().items();
-        return switch (ruleContents.klabel().name()) {
-            case "#ruleNoConditions" -> Context(items.get(0), BooleanUtils.TRUE, ruleContents.att());
-            case "#ruleRequires" -> Context(items.get(0), items.get(1), ruleContents.att());
-            default -> throw KEMException.criticalError("Illegal context with ensures clause detected.", contents);
-        };
+    return cachedParser;
+  }
+
+  private boolean equalsSyntax(Module _this, Module that) {
+    if (!_this.productions().equals(that.productions())) return false;
+    if (!_this.priorities().equals(that.priorities())) return false;
+    if (!_this.leftAssoc().equals(that.leftAssoc())) return false;
+    if (!_this.rightAssoc().equals(that.rightAssoc())) return false;
+    return _this.sortDeclarations().equals(that.sortDeclarations());
+  }
+
+  private Stream parseBubble(
+      ParseInModule pim, Map cache, Bubble b) {
+    int startLine = b.att().get(Att.CONTENT_START_LINE(), Integer.class);
+    int startColumn = b.att().get(Att.CONTENT_START_COLUMN(), Integer.class);
+    Source source = b.att().get(Source.class);
+    boolean isAnywhere =
+        b.att().contains(Att.ANYWHERE())
+            || b.att().contains(Att.SIMPLIFICATION())
+            || ExpandMacros.isMacro(b);
+    Tuple2, K>, java.util.Set> result =
+        pim.parseString(
+            b.contents(),
+            START_SYMBOL,
+            "bubble parsing",
+            pim.getScanner(),
+            source,
+            startLine,
+            startColumn,
+            true,
+            isAnywhere);
+    parsedBubbles.getAndIncrement();
+    registerWarnings(result._2());
+    if (result._1().isRight()) {
+      KApply k = (KApply) result._1().right().get();
+      k =
+          KApply(
+              k.klabel(),
+              k.klist(),
+              k.att()
+                  .addAll(
+                      b.att()
+                          .remove(Att.CONTENT_START_LINE(), Integer.class)
+                          .remove(Att.CONTENT_START_COLUMN(), Integer.class)
+                          .remove(Source.class)
+                          .remove(Location.class)));
+      cache.put(
+          b.contents(),
+          new ParsedSentence(
+              k, new HashSet<>(result._2()), new HashSet<>(), startLine, startColumn, source));
+      k = (KApply) new TreeNodesToKORE(Outer::parseSort, true).down(k);
+      return Stream.of(k);
+    } else {
+      cache.put(
+          b.contents(),
+          new ParsedSentence(
+              null,
+              new HashSet<>(result._2()),
+              result._1().left().get(),
+              startLine,
+              startColumn,
+              source));
+      errors.addAll(result._1().left().get());
+      return Stream.empty();
     }
-
-    private ContextAlias upAlias(K contents) {
-        KApply ruleContents = (KApply) contents;
-        List items = ruleContents.klist().items();
-        return switch (ruleContents.klabel().name()) {
-            case "#ruleNoConditions" -> ContextAlias(items.get(0), BooleanUtils.TRUE, ruleContents.att());
-            case "#ruleRequires" -> ContextAlias(items.get(0), items.get(1), ruleContents.att());
-            default -> throw KEMException.criticalError("Illegal context alias with ensures clause detected.", contents);
-        };
-    }
-
-    private Configuration upConfiguration(K contents) {
-        KApply configContents = (KApply) contents;
-        List items = configContents.klist().items();
-        return switch (configContents.klabel().name()) {
-            case "#ruleNoConditions" -> Configuration(items.get(0), BooleanUtils.TRUE, configContents.att());
-            case "#ruleEnsures" -> Configuration(items.get(0), items.get(1), configContents.att());
-            default -> throw KEMException.compilerError("Illegal configuration with requires clause detected.", configContents);
-        };
-    }
-
-    private ParseCache loadCache(Module parser) {
-        ParseCache cachedParser = caches.get(parser.name());
-        if (cachedParser == null || !equalsSyntax(cachedParser.module().signature(), parser.signature())) {
-            cachedParser = new ParseCache(parser, true, java.util.Collections.synchronizedMap(new HashMap<>()));
-            caches.put(parser.name(), cachedParser);
-        }
-        return cachedParser;
-    }
-
-    private boolean equalsSyntax(Module _this, Module that) {
-        if (!_this.productions().equals(that.productions())) return false;
-        if (!_this.priorities().equals(that.priorities())) return false;
-        if (!_this.leftAssoc().equals(that.leftAssoc())) return false;
-        if (!_this.rightAssoc().equals(that.rightAssoc())) return false;
-        return _this.sortDeclarations().equals(that.sortDeclarations());
-    }
-
-    private Stream parseBubble(ParseInModule pim, Map cache, Bubble b) {
-        int startLine = b.att().get(Att.CONTENT_START_LINE(), Integer.class);
-        int startColumn = b.att().get(Att.CONTENT_START_COLUMN(), Integer.class);
-        Source source = b.att().get(Source.class);
-        boolean isAnywhere = b.att().contains(Att.ANYWHERE()) || b.att().contains(Att.SIMPLIFICATION()) || ExpandMacros.isMacro(b);
-        Tuple2, K>, java.util.Set> result =
-                pim.parseString(b.contents(), START_SYMBOL, "bubble parsing", pim.getScanner(), source, startLine, startColumn, true, isAnywhere);
-        parsedBubbles.getAndIncrement();
-        registerWarnings(result._2());
-        if (result._1().isRight()) {
-            KApply k = (KApply) result._1().right().get();
-            k = KApply(k.klabel(), k.klist(), k.att().addAll(b.att().remove(Att.CONTENT_START_LINE(), Integer.class)
-                    .remove(Att.CONTENT_START_COLUMN(), Integer.class).remove(Source.class).remove(Location.class)));
-            cache.put(b.contents(), new ParsedSentence(k, new HashSet<>(result._2()), new HashSet<>(), startLine, startColumn, source));
-            k = (KApply) new TreeNodesToKORE(Outer::parseSort, true).down(k);
-            return Stream.of(k);
-        } else {
-            cache.put(b.contents(), new ParsedSentence(null, new HashSet<>(result._2()), result._1().left().get(), startLine, startColumn, source));
-            errors.addAll(result._1().left().get());
-            return Stream.empty();
-        }
-    }
-
-    // Save all the timing information collected during parsing in a single file specified at command line.
-    // Timing information is expected to have two parts:
-    // 1. the comparable part - path:lineNumber
-    // 2. the printable part which contains the timing information
-    // The comparable part is used to sort each entry to provide a stable output.
-    private void saveTimings() {
-        if (innerParsingOptions.profileRules != null) {
-            try {
-                List> msgs = new ArrayList<>();
-                for (File f : files.resolveTemp(".").listFiles()) {
-                    if (f.getName().matches("timing.+\\.log")) {
-                        BufferedReader br = new BufferedReader(new FileReader(f));
-                        String path = br.readLine();
-                        String msg = br.readLine();
-                        msgs.add(Tuple2.apply(path, msg));
-                    }
-                }
-                msgs.sort(Comparator.comparing(Tuple2::_1));
-                FileUtil.save(new File(innerParsingOptions.profileRules),
-                        msgs.stream().map(Tuple2::_2).collect(Collectors.joining("\n")));
-            } catch (IOException e) {
-                throw KEMException.internalError("Failed to open timing.log", e);
-            }
+  }
+
+  // Save all the timing information collected during parsing in a single file specified at command
+  // line.
+  // Timing information is expected to have two parts:
+  // 1. the comparable part - path:lineNumber
+  // 2. the printable part which contains the timing information
+  // The comparable part is used to sort each entry to provide a stable output.
+  private void saveTimings() {
+    if (innerParsingOptions.profileRules != null) {
+      try {
+        List> msgs = new ArrayList<>();
+        for (File f : files.resolveTemp(".").listFiles()) {
+          if (f.getName().matches("timing.+\\.log")) {
+            BufferedReader br = new BufferedReader(new FileReader(f));
+            String path = br.readLine();
+            String msg = br.readLine();
+            msgs.add(Tuple2.apply(path, msg));
+          }
         }
+        msgs.sort(Comparator.comparing(Tuple2::_1));
+        FileUtil.save(
+            new File(innerParsingOptions.profileRules),
+            msgs.stream().map(Tuple2::_2).collect(Collectors.joining("\n")));
+      } catch (IOException e) {
+        throw KEMException.internalError("Failed to open timing.log", e);
+      }
     }
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kompile/Kompile.java b/kernel/src/main/java/org/kframework/kompile/Kompile.java
index 266c8a8d0b8..f75ab4047c1 100644
--- a/kernel/src/main/java/org/kframework/kompile/Kompile.java
+++ b/kernel/src/main/java/org/kframework/kompile/Kompile.java
@@ -1,18 +1,41 @@
 // Copyright (c) K Team. All Rights Reserved.
 package org.kframework.kompile;
 
+import static org.kframework.Collections.*;
+import static org.kframework.definition.Constructors.*;
+import static org.kframework.kore.KORE.*;
+
 import com.google.inject.Inject;
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import org.apache.commons.io.FileUtils;
 import org.kframework.attributes.Att;
-import org.kframework.attributes.Att.Key;
 import org.kframework.attributes.Location;
 import org.kframework.attributes.Source;
 import org.kframework.backend.Backends;
 import org.kframework.builtin.Sorts;
 import org.kframework.compile.*;
+import org.kframework.compile.checks.CheckAnonymous;
 import org.kframework.compile.checks.CheckAssoc;
 import org.kframework.compile.checks.CheckAtt;
-import org.kframework.compile.checks.CheckAnonymous;
 import org.kframework.compile.checks.CheckConfigurationCells;
 import org.kframework.compile.checks.CheckFunctions;
 import org.kframework.compile.checks.CheckHOLE;
@@ -34,10 +57,10 @@
 import org.kframework.kore.Sort;
 import org.kframework.main.GlobalOptions;
 import org.kframework.parser.InputModes;
-import org.kframework.parser.json.JsonParser;
 import org.kframework.parser.KRead;
 import org.kframework.parser.ParserUtils;
 import org.kframework.parser.inner.RuleGrammarGenerator;
+import org.kframework.parser.json.JsonParser;
 import org.kframework.unparser.ToJson;
 import org.kframework.utils.OS;
 import org.kframework.utils.RunProcess;
@@ -49,537 +72,756 @@
 import org.kframework.utils.errorsystem.KExceptionManager;
 import org.kframework.utils.file.FileUtil;
 import org.kframework.utils.file.JarInfo;
-
 import org.kframework.utils.options.InnerParsingOptions;
 import org.kframework.utils.options.OuterParsingOptions;
-import scala.collection.JavaConverters;
 import scala.Function1;
 import scala.Option;
+import scala.collection.JavaConverters;
 import scala.collection.Seq;
 import scala.collection.Set$;
 
-import java.io.File;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Predicate;
-import java.util.function.UnaryOperator;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static org.kframework.Collections.*;
-import static org.kframework.definition.Constructors.*;
-import static org.kframework.kore.KORE.*;
-
 /**
- * The new compilation pipeline. Everything is just wired together and will need clean-up once we deside on design.
- * Tracked by #1442.
+ * The new compilation pipeline. Everything is just wired together and will need clean-up once we
+ * deside on design. Tracked by #1442.
  */
 public class Kompile {
-    public static final File BUILTIN_DIRECTORY = JarInfo.getKIncludeDir().resolve("builtin").toFile();
-    public static final String REQUIRE_PRELUDE_K = "requires \"prelude.md\"\n";
-
-    public static final String CACHE_FILE_NAME = "cache.bin";
-
-
-    private final KompileOptions kompileOptions;
-    private final GlobalOptions globalOptions;
-    private final FileUtil files;
-    private final KExceptionManager kem;
-    private final ParserUtils parser;
-    private final Stopwatch sw;
-    private final DefinitionParsing definitionParsing;
-    private final OuterParsingOptions outerParsingOptions;
-    private final InnerParsingOptions innerParsingOptions;
-    java.util.Set errors;
-
-    public Kompile(KompileOptions kompileOptions, OuterParsingOptions outerParsingOptions, InnerParsingOptions innerParsingOptions, GlobalOptions globalOptions, FileUtil files, KExceptionManager kem, boolean cacheParses) {
-        this(kompileOptions, outerParsingOptions, innerParsingOptions, globalOptions, files, kem, new Stopwatch(globalOptions), cacheParses);
+  public static final File BUILTIN_DIRECTORY = JarInfo.getKIncludeDir().resolve("builtin").toFile();
+  public static final String REQUIRE_PRELUDE_K = "requires \"prelude.md\"\n";
+
+  public static final String CACHE_FILE_NAME = "cache.bin";
+
+  private final KompileOptions kompileOptions;
+  private final GlobalOptions globalOptions;
+  private final FileUtil files;
+  private final KExceptionManager kem;
+  private final ParserUtils parser;
+  private final Stopwatch sw;
+  private final DefinitionParsing definitionParsing;
+  private final OuterParsingOptions outerParsingOptions;
+  private final InnerParsingOptions innerParsingOptions;
+  java.util.Set errors;
+
+  public Kompile(
+      KompileOptions kompileOptions,
+      OuterParsingOptions outerParsingOptions,
+      InnerParsingOptions innerParsingOptions,
+      GlobalOptions globalOptions,
+      FileUtil files,
+      KExceptionManager kem,
+      boolean cacheParses) {
+    this(
+        kompileOptions,
+        outerParsingOptions,
+        innerParsingOptions,
+        globalOptions,
+        files,
+        kem,
+        new Stopwatch(globalOptions),
+        cacheParses);
+  }
+
+  public Kompile(
+      KompileOptions kompileOptions,
+      OuterParsingOptions outerParsingOptions,
+      InnerParsingOptions innerParsingOptions,
+      GlobalOptions globalOptions,
+      FileUtil files,
+      KExceptionManager kem) {
+    this(kompileOptions, outerParsingOptions, innerParsingOptions, globalOptions, files, kem, true);
+  }
+
+  @Inject
+  public Kompile(
+      KompileOptions kompileOptions,
+      OuterParsingOptions outerParsingOptions,
+      InnerParsingOptions innerParsingOptions,
+      GlobalOptions globalOptions,
+      FileUtil files,
+      KExceptionManager kem,
+      Stopwatch sw) {
+    this(
+        kompileOptions,
+        outerParsingOptions,
+        innerParsingOptions,
+        globalOptions,
+        files,
+        kem,
+        sw,
+        true);
+  }
+
+  public Kompile(
+      KompileOptions kompileOptions,
+      OuterParsingOptions outerParsingOptions,
+      InnerParsingOptions innerParsingOptions,
+      GlobalOptions globalOptions,
+      FileUtil files,
+      KExceptionManager kem,
+      Stopwatch sw,
+      boolean cacheParses) {
+    this.outerParsingOptions = outerParsingOptions;
+    this.innerParsingOptions = innerParsingOptions;
+    this.kompileOptions = kompileOptions;
+    this.globalOptions = globalOptions;
+    this.files = files;
+    this.kem = kem;
+    this.errors = new HashSet<>();
+    this.parser = new ParserUtils(files, kem, kem.options, outerParsingOptions);
+    List lookupDirectories =
+        this.outerParsingOptions.includes.stream()
+            .map(files::resolveWorkingDirectory)
+            .collect(Collectors.toList());
+    // these directories should be relative to the current working directory if we refer to them
+    // later after the WD has changed.
+    this.outerParsingOptions.includes =
+        lookupDirectories.stream().map(File::getAbsolutePath).collect(Collectors.toList());
+    File cacheFile =
+        kompileOptions.cacheFile != null
+            ? files.resolveWorkingDirectory(kompileOptions.cacheFile)
+            : files.resolveKompiled(CACHE_FILE_NAME);
+    this.definitionParsing =
+        new DefinitionParsing(
+            lookupDirectories,
+            kompileOptions,
+            outerParsingOptions,
+            innerParsingOptions,
+            globalOptions,
+            kem,
+            files,
+            parser,
+            cacheParses,
+            cacheFile,
+            sw);
+    this.sw = sw;
+  }
+
+  /**
+   * Executes the Kompile tool. This tool accesses a
+   *
+   * @param definitionFile
+   * @param mainModuleName
+   * @param mainProgramsModuleName
+   * @param programStartSymbol
+   * @return
+   */
+  public CompiledDefinition run(
+      File definitionFile,
+      String mainModuleName,
+      String mainProgramsModuleName,
+      Function pipeline,
+      Set excludedModuleTags) {
+    files.resolveKompiled(".").mkdirs();
+
+    Definition parsedDef =
+        parseDefinition(definitionFile, mainModuleName, mainProgramsModuleName, excludedModuleTags);
+
+    files.saveToKompiled("parsed.txt", parsedDef.toString());
+    checkDefinition(parsedDef, excludedModuleTags);
+    sw.printIntermediate("Validate definition");
+
+    Definition kompiledDefinition = pipeline.apply(parsedDef);
+    files.saveToKompiled("compiled.txt", kompiledDefinition.toString());
+    sw.printIntermediate("Apply compile pipeline");
+
+    // final check for sort correctness
+    for (Module m : mutable(kompiledDefinition.modules())) m.checkSorts();
+    if (kompileOptions.postProcess != null) {
+      kompiledDefinition = postProcessJSON(kompiledDefinition, kompileOptions.postProcess);
+      files.saveToKompiled("post-processed.txt", kompiledDefinition.toString());
     }
 
-    public Kompile(KompileOptions kompileOptions, OuterParsingOptions outerParsingOptions, InnerParsingOptions innerParsingOptions, GlobalOptions globalOptions, FileUtil files, KExceptionManager kem) {
-        this(kompileOptions, outerParsingOptions, innerParsingOptions, globalOptions, files, kem, true);
-    }
+    files.saveToKompiled("allRules.txt", ruleSourceMap(kompiledDefinition));
 
-    @Inject
-    public Kompile(KompileOptions kompileOptions, OuterParsingOptions outerParsingOptions, InnerParsingOptions innerParsingOptions, GlobalOptions globalOptions, FileUtil files, KExceptionManager kem, Stopwatch sw) {
-        this(kompileOptions, outerParsingOptions, innerParsingOptions, globalOptions, files, kem, sw, true);
+    if (kompileOptions.emitJson) {
+      Stopwatch sw = new Stopwatch(globalOptions);
+      files.saveToKompiled(
+          "parsed.json", new String(ToJson.apply(parsedDef), StandardCharsets.UTF_8));
+      files.saveToKompiled(
+          "compiled.json", new String(ToJson.apply(kompiledDefinition), StandardCharsets.UTF_8));
+      sw.printIntermediate("  Emit parsed & compiled JSON");
     }
 
-    public Kompile(KompileOptions kompileOptions, OuterParsingOptions outerParsingOptions, InnerParsingOptions innerParsingOptions, GlobalOptions globalOptions, FileUtil files, KExceptionManager kem, Stopwatch sw, boolean cacheParses) {
-        this.outerParsingOptions = outerParsingOptions;
-        this.innerParsingOptions = innerParsingOptions;
-        this.kompileOptions = kompileOptions;
-        this.globalOptions = globalOptions;
-        this.files = files;
-        this.kem = kem;
-        this.errors = new HashSet<>();
-        this.parser = new ParserUtils(files, kem, kem.options, outerParsingOptions);
-        List lookupDirectories = this.outerParsingOptions.includes.stream().map(files::resolveWorkingDirectory).collect(Collectors.toList());
-        // these directories should be relative to the current working directory if we refer to them later after the WD has changed.
-        this.outerParsingOptions.includes = lookupDirectories.stream().map(File::getAbsolutePath).collect(Collectors.toList());
-        File cacheFile = kompileOptions.cacheFile != null
-                ? files.resolveWorkingDirectory(kompileOptions.cacheFile) : files.resolveKompiled(CACHE_FILE_NAME);
-        this.definitionParsing = new DefinitionParsing(
-                lookupDirectories, kompileOptions, outerParsingOptions, innerParsingOptions, globalOptions, kem, files,
-                parser, cacheParses, cacheFile, sw);
-        this.sw = sw;
-    }
-
-    /**
-     * Executes the Kompile tool. This tool accesses a
-     *
-     * @param definitionFile
-     * @param mainModuleName
-     * @param mainProgramsModuleName
-     * @param programStartSymbol
-     * @return
-     */
-    public CompiledDefinition run(File definitionFile, String mainModuleName, String mainProgramsModuleName, Function pipeline, Set excludedModuleTags) {
-        files.resolveKompiled(".").mkdirs();
-
-        Definition parsedDef = parseDefinition(definitionFile, mainModuleName, mainProgramsModuleName, excludedModuleTags);
-
-        files.saveToKompiled("parsed.txt", parsedDef.toString());
-        checkDefinition(parsedDef, excludedModuleTags);
-        sw.printIntermediate("Validate definition");
-
-        Definition kompiledDefinition = pipeline.apply(parsedDef);
-        files.saveToKompiled("compiled.txt", kompiledDefinition.toString());
-        sw.printIntermediate("Apply compile pipeline");
-
-        // final check for sort correctness
-        for (Module m : mutable(kompiledDefinition.modules()))
-            m.checkSorts();
-        if (kompileOptions.postProcess != null) {
-            kompiledDefinition = postProcessJSON(kompiledDefinition, kompileOptions.postProcess);
-            files.saveToKompiled("post-processed.txt", kompiledDefinition.toString());
-        }
-
-        files.saveToKompiled("allRules.txt", ruleSourceMap(kompiledDefinition));
+    ConfigurationInfoFromModule configInfo =
+        new ConfigurationInfoFromModule(kompiledDefinition.mainModule());
 
-        if (kompileOptions.emitJson) {
-            Stopwatch sw = new Stopwatch(globalOptions);
-            files.saveToKompiled("parsed.json",   new String(ToJson.apply(parsedDef), StandardCharsets.UTF_8));
-            files.saveToKompiled("compiled.json", new String(ToJson.apply(kompiledDefinition), StandardCharsets.UTF_8));
-            sw.printIntermediate("  Emit parsed & compiled JSON");
-        }
-
-        ConfigurationInfoFromModule configInfo = new ConfigurationInfoFromModule(kompiledDefinition.mainModule());
-
-        boolean isKast = excludedModuleTags.contains(Att.KORE());
-        Sort rootCell;
-        if (isKast) {
-          rootCell = configInfo.getRootCell();
-        } else {
-          rootCell = Sorts.GeneratedTopCell();
+    boolean isKast = excludedModuleTags.contains(Att.KORE());
+    Sort rootCell;
+    if (isKast) {
+      rootCell = configInfo.getRootCell();
+    } else {
+      rootCell = Sorts.GeneratedTopCell();
+    }
+    CompiledDefinition def =
+        new CompiledDefinition(
+            kompileOptions,
+            kompileOptions.outerParsing,
+            kompileOptions.innerParsing,
+            globalOptions,
+            parsedDef,
+            kompiledDefinition,
+            files,
+            kem,
+            configInfo.getDefaultCell(rootCell).klabel());
+
+    if (kompileOptions.genBisonParser || kompileOptions.genGlrBisonParser) {
+      if (def.configurationVariableDefaultSorts.containsKey("$PGM")) {
+        String filename =
+            getBisonParserFilename(def.programStartSymbol.name(), def.mainSyntaxModuleName());
+        File outputFile = files.resolveKompiled(filename);
+        File linkFile = files.resolveKompiled("parser_PGM");
+        new KRead(kem, files, InputModes.PROGRAM, globalOptions)
+            .createBisonParser(
+                def.programParsingModuleFor(def.mainSyntaxModuleName(), kem).get(),
+                def.programStartSymbol,
+                outputFile,
+                kompileOptions.genGlrBisonParser,
+                kompileOptions.bisonFile,
+                kompileOptions.bisonStackMaxDepth,
+                kompileOptions.genBisonParserLibrary);
+        try {
+          linkFile.delete();
+          Files.createSymbolicLink(
+              linkFile.toPath(),
+              files.resolveKompiled(".").toPath().relativize(outputFile.toPath()));
+        } catch (IOException e) {
+          throw KEMException.internalError("Cannot write to kompiled directory.", e);
         }
-        CompiledDefinition def = new CompiledDefinition(kompileOptions, kompileOptions.outerParsing, kompileOptions.innerParsing, globalOptions, parsedDef, kompiledDefinition, files, kem, configInfo.getDefaultCell(rootCell).klabel());
-
-        if (kompileOptions.genBisonParser || kompileOptions.genGlrBisonParser) {
-            if (def.configurationVariableDefaultSorts.containsKey("$PGM")) {
-                String filename = getBisonParserFilename(def.programStartSymbol.name(), def.mainSyntaxModuleName());
-                File outputFile = files.resolveKompiled(filename);
-                File linkFile = files.resolveKompiled("parser_PGM");
-                new KRead(kem, files, InputModes.PROGRAM, globalOptions).createBisonParser(def.programParsingModuleFor(def.mainSyntaxModuleName(), kem).get(), def.programStartSymbol, outputFile, kompileOptions.genGlrBisonParser, kompileOptions.bisonFile, kompileOptions.bisonStackMaxDepth, kompileOptions.genBisonParserLibrary);
-                try {
-                    linkFile.delete();
-                    Files.createSymbolicLink(linkFile.toPath(), files.resolveKompiled(".").toPath().relativize(outputFile.toPath()));
-                } catch (IOException e) {
-                    throw KEMException.internalError("Cannot write to kompiled directory.", e);
-                }
+      }
+      for (Production prod : iterable(kompiledDefinition.mainModule().productions())) {
+        if (prod.att().contains(Att.CELL()) && prod.att().contains(Att.PARSER())) {
+          String att = prod.att().get(Att.PARSER());
+          String[][] parts = StringUtil.splitTwoDimensionalAtt(att);
+          for (String[] part : parts) {
+            if (part.length != 2) {
+              throw KEMException.compilerError("Invalid value for parser attribute: " + att, prod);
             }
-            for (Production prod : iterable(kompiledDefinition.mainModule().productions())) {
-                if (prod.att().contains(Att.CELL()) && prod.att().contains(Att.PARSER())) {
-                    String att = prod.att().get(Att.PARSER());
-                    String[][] parts = StringUtil.splitTwoDimensionalAtt(att);
-                    for (String[] part : parts) {
-                        if (part.length != 2) {
-                            throw KEMException.compilerError("Invalid value for parser attribute: " + att, prod);
-                        }
-                        String name = part[0];
-                        String module = part[1];
-                        Option mod = def.programParsingModuleFor(module, kem);
-                        if (!mod.isDefined()) {
-                            throw KEMException.compilerError("Could not find module referenced by parser attribute: " + module, prod);
-                        }
-                        Sort sort = def.configurationVariableDefaultSorts.getOrDefault("$" + name, Sorts.K());
-                        String filename = getBisonParserFilename(sort.name(), module);
-                        File outputFile = files.resolveKompiled(filename);
-                        File linkFile = files.resolveKompiled("parser_" + name);
-                        new KRead(kem, files, InputModes.PROGRAM, globalOptions).createBisonParser(mod.get(), sort, outputFile, kompileOptions.genGlrBisonParser, null, kompileOptions.bisonStackMaxDepth, kompileOptions.genBisonParserLibrary);
-                        try {
-                            linkFile.delete();
-                            Files.createSymbolicLink(linkFile.toPath(), files.resolveKompiled(".").toPath().relativize(outputFile.toPath()));
-                        } catch (IOException e) {
-                            throw KEMException.internalError("Cannot write to kompiled directory.", e);
-                        }
-                    }
-                }
+            String name = part[0];
+            String module = part[1];
+            Option mod = def.programParsingModuleFor(module, kem);
+            if (!mod.isDefined()) {
+              throw KEMException.compilerError(
+                  "Could not find module referenced by parser attribute: " + module, prod);
             }
+            Sort sort = def.configurationVariableDefaultSorts.getOrDefault("$" + name, Sorts.K());
+            String filename = getBisonParserFilename(sort.name(), module);
+            File outputFile = files.resolveKompiled(filename);
+            File linkFile = files.resolveKompiled("parser_" + name);
+            new KRead(kem, files, InputModes.PROGRAM, globalOptions)
+                .createBisonParser(
+                    mod.get(),
+                    sort,
+                    outputFile,
+                    kompileOptions.genGlrBisonParser,
+                    null,
+                    kompileOptions.bisonStackMaxDepth,
+                    kompileOptions.genBisonParserLibrary);
+            try {
+              linkFile.delete();
+              Files.createSymbolicLink(
+                  linkFile.toPath(),
+                  files.resolveKompiled(".").toPath().relativize(outputFile.toPath()));
+            } catch (IOException e) {
+              throw KEMException.internalError("Cannot write to kompiled directory.", e);
+            }
+          }
         }
-
-        return def;
+      }
     }
 
-    private String getBisonParserFilename(String sort, String module) {
-        String baseName = "parser_" + sort + "_" + module;
+    return def;
+  }
 
-        if (kompileOptions.genBisonParserLibrary) {
-            return "lib" + baseName + OS.current().getSharedLibraryExtension();
-        } else {
-            return baseName;
-        }
-    }
+  private String getBisonParserFilename(String sort, String module) {
+    String baseName = "parser_" + sort + "_" + module;
 
-    private Definition postProcessJSON(Definition defn, String postProcess) {
-        List command = new ArrayList<>(Arrays.asList(postProcess.split(" ")));
-        Map environment = new HashMap<>();
-        File compiledJson;
-        try {
-            String inputDefinition = new String(ToJson.apply(defn), StandardCharsets.UTF_8);
-            compiledJson = files.resolveTemp("post-process-compiled.json");
-            FileUtils.writeStringToFile(compiledJson, inputDefinition);
-        } catch (UnsupportedEncodingException e) {
-            throw KEMException.criticalError("Could not encode definition to JSON!");
-        } catch (IOException e) {
-            throw KEMException.criticalError("Could not make temporary file!");
-        }
-        command.add(compiledJson.getAbsolutePath());
-        RunProcess.ProcessOutput output = RunProcess.execute(environment, files.getProcessBuilder(), command.toArray(new String[command.size()]));
-        sw.printIntermediate("Post process JSON: " + String.join(" ", command));
-        if (output.exitCode() != 0) {
-            throw KEMException.criticalError("Post-processing returned a non-zero exit code: "
-                    + output.exitCode() + "\nStdout:\n" + new String(output.stdout()) + "\nStderr:\n" + new String(output.stderr()));
-        }
-        return JsonParser.parseDefinition(new String(output.stdout()));
+    if (kompileOptions.genBisonParserLibrary) {
+      return "lib" + baseName + OS.current().getSharedLibraryExtension();
+    } else {
+      return baseName;
     }
-
-    private static String ruleSourceMap(Definition def) {
-        List ruleLocs = new ArrayList();
-        for (Sentence s: JavaConverters.setAsJavaSet(def.mainModule().sentences())) {
-            if (s instanceof RuleOrClaim) {
-                var optFile = s.att().getOptional(Source.class);
-                var optLine = s.att().getOptional(Location.class);
-                var optCol  = s.att().getOptional(Location.class);
-                var optId   = s.att().getOptional(Att.UNIQUE_ID());
-                if (optFile.isPresent() && optLine.isPresent() && optCol.isPresent() && optId.isPresent()) {
-                    String file = optFile.get().source();
-                    int line    = optLine.get().startLine();
-                    int col     = optCol.get().startColumn();
-                    String loc  = file + ":" + line + ":" + col;
-                    String id   = optId.get();
-                    ruleLocs.add(id + " " + loc);
-                }
-            }
-        }
-        return String.join("\n", ruleLocs);
+  }
+
+  private Definition postProcessJSON(Definition defn, String postProcess) {
+    List command = new ArrayList<>(Arrays.asList(postProcess.split(" ")));
+    Map environment = new HashMap<>();
+    File compiledJson;
+    try {
+      String inputDefinition = new String(ToJson.apply(defn), StandardCharsets.UTF_8);
+      compiledJson = files.resolveTemp("post-process-compiled.json");
+      FileUtils.writeStringToFile(compiledJson, inputDefinition);
+    } catch (UnsupportedEncodingException e) {
+      throw KEMException.criticalError("Could not encode definition to JSON!");
+    } catch (IOException e) {
+      throw KEMException.criticalError("Could not make temporary file!");
     }
-
-    public Definition parseDefinition(File definitionFile, String mainModuleName, String mainProgramsModule, Set excludedModuleTags) {
-        return definitionParsing.parseDefinitionAndResolveBubbles(definitionFile, mainModuleName, mainProgramsModule, excludedModuleTags);
+    command.add(compiledJson.getAbsolutePath());
+    RunProcess.ProcessOutput output =
+        RunProcess.execute(
+            environment, files.getProcessBuilder(), command.toArray(new String[command.size()]));
+    sw.printIntermediate("Post process JSON: " + String.join(" ", command));
+    if (output.exitCode() != 0) {
+      throw KEMException.criticalError(
+          "Post-processing returned a non-zero exit code: "
+              + output.exitCode()
+              + "\nStdout:\n"
+              + new String(output.stdout())
+              + "\nStderr:\n"
+              + new String(output.stderr()));
     }
-
-    private static Module filterStreamModules(Module input) {
-        if (input.name().equals("STDIN-STREAM") || input.name().equals("STDOUT-STREAM")) {
-            return Module(input.name(), Set(), Set(), input.att());
+    return JsonParser.parseDefinition(new String(output.stdout()));
+  }
+
+  private static String ruleSourceMap(Definition def) {
+    List ruleLocs = new ArrayList();
+    for (Sentence s : JavaConverters.setAsJavaSet(def.mainModule().sentences())) {
+      if (s instanceof RuleOrClaim) {
+        var optFile = s.att().getOptional(Source.class);
+        var optLine = s.att().getOptional(Location.class);
+        var optCol = s.att().getOptional(Location.class);
+        var optId = s.att().getOptional(Att.UNIQUE_ID());
+        if (optFile.isPresent() && optLine.isPresent() && optCol.isPresent() && optId.isPresent()) {
+          String file = optFile.get().source();
+          int line = optLine.get().startLine();
+          int col = optCol.get().startColumn();
+          String loc = file + ":" + line + ":" + col;
+          String id = optId.get();
+          ruleLocs.add(id + " " + loc);
         }
-        return input;
-    }
-
-    public static Definition resolveIOStreams(KExceptionManager kem,Definition d) {
-        return DefinitionTransformer.from(new ResolveIOStreams(d, kem)::resolve, "resolving io streams")
-                .andThen(DefinitionTransformer.from(Kompile::filterStreamModules, "resolving io streams"))
-                .apply(d);
+      }
     }
-
-    private static Module excludeModulesByTag(Set excludedModuleTags, Module mod) {
-        Predicate f = _import -> excludedModuleTags.stream().noneMatch(tag -> _import.module().att().contains(tag));
-        Set newImports = stream(mod.imports()).filter(f).collect(Collectors.toSet());
-        return Module(mod.name(), immutable(newImports), mod.localSentences(), mod.att());
+    return String.join("\n", ruleLocs);
+  }
+
+  public Definition parseDefinition(
+      File definitionFile,
+      String mainModuleName,
+      String mainProgramsModule,
+      Set excludedModuleTags) {
+    return definitionParsing.parseDefinitionAndResolveBubbles(
+        definitionFile, mainModuleName, mainProgramsModule, excludedModuleTags);
+  }
+
+  private static Module filterStreamModules(Module input) {
+    if (input.name().equals("STDIN-STREAM") || input.name().equals("STDOUT-STREAM")) {
+      return Module(input.name(), Set(), Set(), input.att());
     }
-
-    private static Definition excludeModulesByTag(Set excludedModuleTags, String syntaxModule, Definition d) {
-        for (Att.Key k : excludedModuleTags) {
-            if (d.mainModule().att().contains(k)) {
-                throw KEMException.compilerError("Main module " + d.mainModule().name() + " has excluded attribute [" + k + "].");
-            }
-            d.getModule(syntaxModule).map(m -> {
+    return input;
+  }
+
+  public static Definition resolveIOStreams(KExceptionManager kem, Definition d) {
+    return DefinitionTransformer.from(new ResolveIOStreams(d, kem)::resolve, "resolving io streams")
+        .andThen(DefinitionTransformer.from(Kompile::filterStreamModules, "resolving io streams"))
+        .apply(d);
+  }
+
+  private static Module excludeModulesByTag(Set excludedModuleTags, Module mod) {
+    Predicate f =
+        _import ->
+            excludedModuleTags.stream().noneMatch(tag -> _import.module().att().contains(tag));
+    Set newImports = stream(mod.imports()).filter(f).collect(Collectors.toSet());
+    return Module(mod.name(), immutable(newImports), mod.localSentences(), mod.att());
+  }
+
+  private static Definition excludeModulesByTag(
+      Set excludedModuleTags, String syntaxModule, Definition d) {
+    for (Att.Key k : excludedModuleTags) {
+      if (d.mainModule().att().contains(k)) {
+        throw KEMException.compilerError(
+            "Main module " + d.mainModule().name() + " has excluded attribute [" + k + "].");
+      }
+      d.getModule(syntaxModule)
+          .map(
+              m -> {
                 if (m.att().contains(k)) {
-                    throw KEMException.compilerError("Syntax module " + m.name() + " has excluded attribute [" + k + "].");
+                  throw KEMException.compilerError(
+                      "Syntax module " + m.name() + " has excluded attribute [" + k + "].");
                 }
                 return null;
-            });
-        }
-
-        return Definition(d.mainModule(), immutable(stream(d.entryModules()).filter(mod -> excludedModuleTags.stream().noneMatch(tag -> mod.att().contains(tag))).collect(Collectors.toSet())), d.att());
-    }
-
-    public static Function1 excludeModulesByTag(Set excludedModuleTags, String syntaxModule) {
-        Function1 excludeModules = d -> excludeModulesByTag(excludedModuleTags, syntaxModule, d);
-        DefinitionTransformer walkModules = DefinitionTransformer.from(mod -> excludeModulesByTag(excludedModuleTags, mod), "remove modules based on attributes");
-
-        return excludeModules.andThen(walkModules);
+              });
     }
 
-    public static Sentence removePolyKLabels(Sentence s) {
-      if (s instanceof Production p) {
-          if (!p.isSyntacticSubsort() && p.params().nonEmpty()) {
-            p = p.substitute(immutable(Collections.nCopies(p.params().size(), Sorts.K())));
-            return Production(p.klabel().map(KLabel::head), Seq(), p.sort(), p.items(), p.att());
-        }
+    return Definition(
+        d.mainModule(),
+        immutable(
+            stream(d.entryModules())
+                .filter(
+                    mod -> excludedModuleTags.stream().noneMatch(tag -> mod.att().contains(tag)))
+                .collect(Collectors.toSet())),
+        d.att());
+  }
+
+  public static Function1 excludeModulesByTag(
+      Set excludedModuleTags, String syntaxModule) {
+    Function1 excludeModules =
+        d -> excludeModulesByTag(excludedModuleTags, syntaxModule, d);
+    DefinitionTransformer walkModules =
+        DefinitionTransformer.from(
+            mod -> excludeModulesByTag(excludedModuleTags, mod),
+            "remove modules based on attributes");
+
+    return excludeModules.andThen(walkModules);
+  }
+
+  public static Sentence removePolyKLabels(Sentence s) {
+    if (s instanceof Production p) {
+      if (!p.isSyntacticSubsort() && p.params().nonEmpty()) {
+        p = p.substitute(immutable(Collections.nCopies(p.params().size(), Sorts.K())));
+        return Production(p.klabel().map(KLabel::head), Seq(), p.sort(), p.items(), p.att());
       }
-      return s;
     }
-
-    public static Module subsortKItem(Module module) {
-        java.util.Set prods = new HashSet<>();
-        for (Sort srt : iterable(module.allSorts())) {
-            if (!RuleGrammarGenerator.isParserSort(srt)) {
-                // KItem ::= Sort
-                Production prod = Production(Seq(), Sorts.KItem(), Seq(NonTerminal(srt)), Att());
-                if (!module.sentences().contains(prod)) {
-                    prods.add(prod);
-                }
-            }
-        }
-        if (prods.isEmpty()) {
-            return module;
-        } else {
-            return Module(module.name(), module.imports(), Stream.concat(stream(module.localSentences()), prods.stream())
-                    .collect(org.kframework.Collections.toSet()), module.att());
+    return s;
+  }
+
+  public static Module subsortKItem(Module module) {
+    java.util.Set prods = new HashSet<>();
+    for (Sort srt : iterable(module.allSorts())) {
+      if (!RuleGrammarGenerator.isParserSort(srt)) {
+        // KItem ::= Sort
+        Production prod = Production(Seq(), Sorts.KItem(), Seq(NonTerminal(srt)), Att());
+        if (!module.sentences().contains(prod)) {
+          prods.add(prod);
         }
+      }
     }
-
-    public Rule parseAndCompileRule(CompiledDefinition compiledDef, String contents, Source source, Optional parsedRule) {
-        Rule parsed = parsedRule.orElseGet(() -> parseRule(compiledDef, contents, source));
-        return compileRule(compiledDef.kompiledDefinition, parsed);
-    }
-
-    public Rule parseRule(CompiledDefinition compiledDef, String contents, Source source) {
-        return definitionParsing.parseRule(compiledDef, contents, source);
-    }
-
-    private void checkDefinition(Definition parsedDef, Set excludedModuleTags) {
-        scala.collection.Set modules = parsedDef.modules();
-        Module mainModule = parsedDef.mainModule();
-        Option kModule = parsedDef.getModule("K");
-        definitionChecks(stream(modules).collect(Collectors.toSet()));
-        structuralChecks(modules, mainModule, kModule, excludedModuleTags);
+    if (prods.isEmpty()) {
+      return module;
+    } else {
+      return Module(
+          module.name(),
+          module.imports(),
+          Stream.concat(stream(module.localSentences()), prods.stream())
+              .collect(org.kframework.Collections.toSet()),
+          module.att());
     }
-
-    // checks that are not verified in the prover
-    public void definitionChecks(Set modules) {
-        modules.forEach(m -> stream(m.localSentences()).forEach(s -> {
-            // Check that the `claim` keyword is not used in the definition.
-            if (s instanceof Claim)
-                errors.add(KEMException.compilerError("Claims are not allowed in the definition.", s));
-        }));
-    }
-
-    // Extra checks just for the prover specification.
-    public Module proverChecks(Module specModule, Module mainDefModule) {
-        // check rogue syntax in spec module
-        Set toCheck = mutable(specModule.sentences().$minus$minus(mainDefModule.sentences()));
-        for (Sentence s : toCheck)
-            if (s.isSyntax() && !s.att().contains(Att.TOKEN()))
-                kem.registerCompilerWarning(ExceptionType.FUTURE_ERROR, errors,
-                        "Found syntax declaration in proof module. This will not be visible from the main module.", s);
-
-        // TODO: remove once transition to claim rules is done
-        // transform rules into claims if
-        // - they are in the spec modules but not in the definition modules
-        // - they don't contain the `simplification` attribute
-        ModuleTransformer mt = ModuleTransformer.fromSentenceTransformer((m, s) -> {
-            if (m.name().equals(mainDefModule.name()) || mainDefModule.importedModuleNames().contains(m.name()))
-                return s;
-            if (s instanceof Rule && !s.att().contains(Att.SIMPLIFICATION())) {
-                kem.registerCompilerWarning(KException.ExceptionType.FUTURE_ERROR, errors, "Deprecated: use claim instead of rule to specify proof objectives.", s);
-                return new Claim(((Rule) s).body(), ((Rule) s).requires(), ((Rule) s).ensures(), s.att());
-            }
-            return s;
-        }, "rules to claim");
-        return mt.apply(specModule);
-    }
-
-    // Extra checks just for the prover specification.
-    public void proverChecksX(Module specModule, Module mainDefModule) {
-        // check rogue syntax in spec module
-        Set toCheck = mutable(specModule.sentences().$minus$minus(mainDefModule.sentences()));
-        for (Sentence s : toCheck)
-            if (s.isSyntax() && (!s.att().contains(Att.TOKEN()) || !mainDefModule.allSorts().contains(((Production) s).sort())))
-                errors.add(KEMException.compilerError("Found syntax declaration in proof module. Only tokens for existing sorts are allowed.", s));
-
-        ModuleTransformer mt = ModuleTransformer.fromSentenceTransformer((m, s) -> {
-            if (m.name().equals(mainDefModule.name()) || mainDefModule.importedModuleNames().contains(m.name()))
-                return s;
-            if (!(s instanceof Claim || s.isSyntax())) {
+  }
+
+  public Rule parseAndCompileRule(
+      CompiledDefinition compiledDef, String contents, Source source, Optional parsedRule) {
+    Rule parsed = parsedRule.orElseGet(() -> parseRule(compiledDef, contents, source));
+    return compileRule(compiledDef.kompiledDefinition, parsed);
+  }
+
+  public Rule parseRule(CompiledDefinition compiledDef, String contents, Source source) {
+    return definitionParsing.parseRule(compiledDef, contents, source);
+  }
+
+  private void checkDefinition(Definition parsedDef, Set excludedModuleTags) {
+    scala.collection.Set modules = parsedDef.modules();
+    Module mainModule = parsedDef.mainModule();
+    Option kModule = parsedDef.getModule("K");
+    definitionChecks(stream(modules).collect(Collectors.toSet()));
+    structuralChecks(modules, mainModule, kModule, excludedModuleTags);
+  }
+
+  // checks that are not verified in the prover
+  public void definitionChecks(Set modules) {
+    modules.forEach(
+        m ->
+            stream(m.localSentences())
+                .forEach(
+                    s -> {
+                      // Check that the `claim` keyword is not used in the definition.
+                      if (s instanceof Claim)
+                        errors.add(
+                            KEMException.compilerError(
+                                "Claims are not allowed in the definition.", s));
+                    }));
+  }
+
+  // Extra checks just for the prover specification.
+  public Module proverChecks(Module specModule, Module mainDefModule) {
+    // check rogue syntax in spec module
+    Set toCheck = mutable(specModule.sentences().$minus$minus(mainDefModule.sentences()));
+    for (Sentence s : toCheck)
+      if (s.isSyntax() && !s.att().contains(Att.TOKEN()))
+        kem.registerCompilerWarning(
+            ExceptionType.FUTURE_ERROR,
+            errors,
+            "Found syntax declaration in proof module. This will not be visible from the main"
+                + " module.",
+            s);
+
+    // TODO: remove once transition to claim rules is done
+    // transform rules into claims if
+    // - they are in the spec modules but not in the definition modules
+    // - they don't contain the `simplification` attribute
+    ModuleTransformer mt =
+        ModuleTransformer.fromSentenceTransformer(
+            (m, s) -> {
+              if (m.name().equals(mainDefModule.name())
+                  || mainDefModule.importedModuleNames().contains(m.name())) return s;
+              if (s instanceof Rule && !s.att().contains(Att.SIMPLIFICATION())) {
+                kem.registerCompilerWarning(
+                    KException.ExceptionType.FUTURE_ERROR,
+                    errors,
+                    "Deprecated: use claim instead of rule to specify proof objectives.",
+                    s);
+                return new Claim(
+                    ((Rule) s).body(), ((Rule) s).requires(), ((Rule) s).ensures(), s.att());
+              }
+              return s;
+            },
+            "rules to claim");
+    return mt.apply(specModule);
+  }
+
+  // Extra checks just for the prover specification.
+  public void proverChecksX(Module specModule, Module mainDefModule) {
+    // check rogue syntax in spec module
+    Set toCheck = mutable(specModule.sentences().$minus$minus(mainDefModule.sentences()));
+    for (Sentence s : toCheck)
+      if (s.isSyntax()
+          && (!s.att().contains(Att.TOKEN())
+              || !mainDefModule.allSorts().contains(((Production) s).sort())))
+        errors.add(
+            KEMException.compilerError(
+                "Found syntax declaration in proof module. Only tokens for existing sorts are"
+                    + " allowed.",
+                s));
+
+    ModuleTransformer mt =
+        ModuleTransformer.fromSentenceTransformer(
+            (m, s) -> {
+              if (m.name().equals(mainDefModule.name())
+                  || mainDefModule.importedModuleNames().contains(m.name())) return s;
+              if (!(s instanceof Claim || s.isSyntax())) {
                 if (s instanceof Rule && !s.att().contains(Att.SIMPLIFICATION()))
-                    errors.add(KEMException.compilerError("Only claims and simplification rules are allowed in proof modules.", s));
-            }
-            return s;
-        }, "rules in spec module");
-        mt.apply(specModule);
-    }
+                  errors.add(
+                      KEMException.compilerError(
+                          "Only claims and simplification rules are allowed in proof modules.", s));
+              }
+              return s;
+            },
+            "rules in spec module");
+    mt.apply(specModule);
+  }
+
+  public void structuralChecks(
+      scala.collection.Set modules,
+      Module mainModule,
+      Option kModule,
+      Set excludedModuleTags) {
+    checkAnywhereRules(modules);
+    boolean isSymbolic = excludedModuleTags.contains(Att.CONCRETE());
+    boolean isKast = excludedModuleTags.contains(Att.KORE());
+    CheckRHSVariables checkRHSVariables =
+        new CheckRHSVariables(errors, !isSymbolic, kompileOptions.backend);
+    stream(modules).forEach(m -> stream(m.localSentences()).forEach(checkRHSVariables::check));
+
+    stream(modules)
+        .forEach(
+            m -> {
+              CheckAtt checkAtt = new CheckAtt(errors, kem, m, isSymbolic && isKast);
+              checkAtt.checkUnrecognizedModuleAtts();
+              stream(m.localSentences()).forEach(checkAtt::check);
+            });
 
-    public void structuralChecks(scala.collection.Set modules, Module mainModule, Option kModule, Set excludedModuleTags) {
-        checkAnywhereRules(modules);
-        boolean isSymbolic = excludedModuleTags.contains(Att.CONCRETE());
-        boolean isKast = excludedModuleTags.contains(Att.KORE());
-        CheckRHSVariables checkRHSVariables = new CheckRHSVariables(errors, !isSymbolic, kompileOptions.backend);
-        stream(modules).forEach(m -> stream(m.localSentences()).forEach(checkRHSVariables::check));
+    stream(modules)
+        .forEach(
+            m ->
+                stream(m.localSentences())
+                    .forEach(new CheckConfigurationCells(errors, m, isSymbolic && isKast)::check));
 
-        stream(modules).forEach(m -> {
-            CheckAtt checkAtt = new CheckAtt(errors, kem, m, isSymbolic && isKast);
-            checkAtt.checkUnrecognizedModuleAtts();
-            stream(m.localSentences()).forEach(checkAtt::check);
-        });
+    stream(modules)
+        .forEach(
+            m -> stream(m.localSentences()).forEach(new CheckSortTopUniqueness(errors, m)::check));
 
-        stream(modules).forEach(m -> stream(m.localSentences()).forEach(new CheckConfigurationCells(errors, m, isSymbolic && isKast)::check));
+    stream(modules)
+        .forEach(m -> stream(m.localSentences()).forEach(new CheckStreams(errors, m)::check));
 
-        stream(modules).forEach(m -> stream(m.localSentences()).forEach(new CheckSortTopUniqueness(errors, m)::check));
+    stream(modules)
+        .forEach(m -> stream(m.localSentences()).forEach(new CheckRewrite(errors, m)::check));
 
-        stream(modules).forEach(m -> stream(m.localSentences()).forEach(new CheckStreams(errors, m)::check));
+    stream(modules)
+        .forEach(m -> stream(m.localSentences()).forEach(new CheckHOLE(errors, m)::check));
 
-        stream(modules).forEach(m -> stream(m.localSentences()).forEach(new CheckRewrite(errors, m)::check));
+    if (!(isSymbolic && isKast)) { // if it's not the java backend
+      stream(modules)
+          .forEach(m -> stream(m.localSentences()).forEach(new CheckTokens(errors, m)::check));
+    }
 
-        stream(modules).forEach(m -> stream(m.localSentences()).forEach(new CheckHOLE(errors, m)::check));
+    stream(modules).forEach(m -> stream(m.localSentences()).forEach(new CheckK(errors)::check));
 
-        if (!(isSymbolic && isKast)) { // if it's not the java backend
-            stream(modules).forEach(m -> stream(m.localSentences()).forEach(new CheckTokens(errors, m)::check));
-        }
+    stream(modules)
+        .forEach(m -> stream(m.localSentences()).forEach(new CheckFunctions(errors, m)::check));
 
-        stream(modules).forEach(m -> stream(m.localSentences()).forEach(new CheckK(errors)::check));
+    stream(modules)
+        .forEach(
+            m -> stream(m.localSentences()).forEach(new CheckAnonymous(errors, m, kem)::check));
 
-        stream(modules).forEach(m -> stream(m.localSentences()).forEach(
-              new CheckFunctions(errors, m)::check));
+    stream(modules)
+        .forEach(
+            m -> stream(m.localSentences()).forEach(new CheckSyntaxGroups(errors, m, kem)::check));
 
-        stream(modules).forEach(m -> stream(m.localSentences()).forEach(new CheckAnonymous(errors, m, kem)::check));
+    stream(modules)
+        .forEach(m -> stream(m.localSentences()).forEach(new CheckAssoc(errors, m)::check));
 
-        stream(modules).forEach(m -> stream(m.localSentences()).forEach(new CheckSyntaxGroups(errors, m, kem)::check));
-
-        stream(modules).forEach(m -> stream(m.localSentences()).forEach(new CheckAssoc(errors, m)::check));
+    Set moduleNames = new HashSet<>();
+    stream(modules)
+        .forEach(
+            m -> {
+              if (moduleNames.contains(m.name())) {
+                errors.add(
+                    KEMException.compilerError("Found multiple modules with name: " + m.name()));
+              }
+              moduleNames.add(m.name());
+            });
 
-        Set moduleNames = new HashSet<>();
-        stream(modules).forEach(m -> {
-            if (moduleNames.contains(m.name())) {
-                errors.add(KEMException.compilerError("Found multiple modules with name: " + m.name()));
-            }
-            moduleNames.add(m.name());
-        });
-
-        CheckKLabels checkKLabels = new CheckKLabels(errors, kem, files, kompileOptions.extraConcreteRuleLabels);
-        Set checkedModules = new HashSet<>();
-        // only check imported modules because otherwise we might have false positives
-        Consumer checkModuleKLabels = m -> {
-            if (!checkedModules.contains(m.name())) {
-                stream(m.localSentences()).forEach(s -> checkKLabels.check(s, m));
-            }
-            checkedModules.add(m.name());
+    CheckKLabels checkKLabels =
+        new CheckKLabels(errors, kem, files, kompileOptions.extraConcreteRuleLabels);
+    Set checkedModules = new HashSet<>();
+    // only check imported modules because otherwise we might have false positives
+    Consumer checkModuleKLabels =
+        m -> {
+          if (!checkedModules.contains(m.name())) {
+            stream(m.localSentences()).forEach(s -> checkKLabels.check(s, m));
+          }
+          checkedModules.add(m.name());
         };
 
-        if (kModule.nonEmpty()) {
-            stream(kModule.get().importedModules()).forEach(checkModuleKLabels);
-            checkModuleKLabels.accept(kModule.get());
-        }
-        stream(mainModule.importedModules()).forEach(checkModuleKLabels);
-        checkModuleKLabels.accept(mainModule);
-        checkKLabels.check(mainModule);
-
-        stream(modules).forEach(m -> stream(m.localSentences()).forEach(new CheckLabels(errors)::check));
-
-        checkIsSortPredicates(modules);
-
-        if (!errors.isEmpty()) {
-            kem.addAllKException(errors.stream().map(e -> e.exception).collect(Collectors.toList()));
-            throw KEMException.compilerError("Had " + errors.size() + " structural errors.");
-        }
-    }
-
-    private void checkAnywhereRules(scala.collection.Set modules) {
-        if (kompileOptions.backend.equals(Backends.HASKELL) && !kompileOptions.allowAnywhereRulesHaskell) {
-            errors.addAll(stream(modules).flatMap(m -> stream(m.localSentences()).flatMap(s -> {
-                if (s instanceof Rule && s.att().contains(Att.ANYWHERE()))
-                    return Stream.of(KEMException.compilerError(Att.ANYWHERE() + " is not supported by the " + Backends.HASKELL + " backend.", s));
-                return Stream.empty();
-            })).collect(Collectors.toSet()));
-        }
-    }
-
-    private void checkIsSortPredicates(scala.collection.Set modules) {
-        Set generatedIsSorts =
-                stream(modules)
-                        .flatMap(m -> stream(m.definedSorts()))
-                        .map(s -> "is" + s.toString())
-                        .collect(Collectors.toSet());
-
-        stream(modules)
-                .flatMap(m -> stream(m.productionsForSort().getOrElse(Sorts.Bool().head(), Set$.MODULE$::empty)))
-                .collect(Collectors.toSet())
-                .stream()
-                .forEach(prod -> {
-                    Seq items = prod.items();
-                    if (items.size() < 3) {
-                        return;
-                    }
-                    ProductionItem first = items.head();
-                    ProductionItem second = items.tail().head();
-                    ProductionItem last = items.last();
-                    // Check if the production is of the form isSort ( ... )
-                    if ((first instanceof Terminal) && (second instanceof Terminal) && (last instanceof Terminal) &&
-                            generatedIsSorts.contains(((Terminal) first).value()) &&
-                            ((Terminal) second).value().equals("(") && ((Terminal) last).value().equals(")")) {
-                        errors.add(
-                                KEMException.compilerError(
-                                        "Syntax declaration conflicts with automatically generated " +
-                                                ((Terminal) first).value() + " predicate.", prod));
-                    }
-                });
+    if (kModule.nonEmpty()) {
+      stream(kModule.get().importedModules()).forEach(checkModuleKLabels);
+      checkModuleKLabels.accept(kModule.get());
     }
+    stream(mainModule.importedModules()).forEach(checkModuleKLabels);
+    checkModuleKLabels.accept(mainModule);
+    checkKLabels.check(mainModule);
 
-    public static Definition addSemanticsModule(Definition d) {
-        java.util.Set allModules = mutable(d.modules());
+    stream(modules)
+        .forEach(m -> stream(m.localSentences()).forEach(new CheckLabels(errors)::check));
 
-        Module languageParsingModule = Constructors.Module("LANGUAGE-PARSING",
-                Set(Import(d.mainModule(), true),
-                        Import(d.getModule(d.att().get(Att.SYNTAX_MODULE())).get(), true),
-                        Import(d.getModule("K-TERM").get(), true),
-                        Import(d.getModule(RuleGrammarGenerator.ID_PROGRAM_PARSING).get(), true)), Set(), Att());
-        allModules.add(languageParsingModule);
-        return Constructors.Definition(d.mainModule(), immutable(allModules), d.att());
-    }
+    checkIsSortPredicates(modules);
 
-    public Rule compileRule(Definition compiledDef, Rule parsedRule) {
-        return (Rule) UnaryOperator.identity()
-                .andThen(new ResolveAnonVar()::resolve)
-                .andThen(new ResolveSemanticCasts(false)::resolve)
-                .andThen(s -> concretizeSentence(s, compiledDef))
-                .apply(parsedRule);
+    if (!errors.isEmpty()) {
+      kem.addAllKException(errors.stream().map(e -> e.exception).collect(Collectors.toList()));
+      throw KEMException.compilerError("Had " + errors.size() + " structural errors.");
     }
-
-    public Set parseModules(CompiledDefinition definition, String mainModule, String entryPointModule, File definitionFile, Set excludedModuleTags, boolean readOnlyCache, boolean useCachedScanner) {
-        Set modules = definitionParsing.parseModules(definition, mainModule, entryPointModule, definitionFile, excludedModuleTags, readOnlyCache, useCachedScanner);
-        int totalBubbles = definitionParsing.parsedBubbles.get() + definitionParsing.cachedBubbles.get();
-        sw.printIntermediate("Parse spec modules [" + definitionParsing.parsedBubbles.get() + "/" + totalBubbles + " rules]");
-        return modules;
+  }
+
+  private void checkAnywhereRules(scala.collection.Set modules) {
+    if (kompileOptions.backend.equals(Backends.HASKELL)
+        && !kompileOptions.allowAnywhereRulesHaskell) {
+      errors.addAll(
+          stream(modules)
+              .flatMap(
+                  m ->
+                      stream(m.localSentences())
+                          .flatMap(
+                              s -> {
+                                if (s instanceof Rule && s.att().contains(Att.ANYWHERE()))
+                                  return Stream.of(
+                                      KEMException.compilerError(
+                                          Att.ANYWHERE()
+                                              + " is not supported by the "
+                                              + Backends.HASKELL
+                                              + " backend.",
+                                          s));
+                                return Stream.empty();
+                              }))
+              .collect(Collectors.toSet()));
     }
+  }
 
-    private Sentence concretizeSentence(Sentence s, Definition input) {
-        ConfigurationInfoFromModule configInfo = new ConfigurationInfoFromModule(input.mainModule());
-        LabelInfo labelInfo = new LabelInfoFromModule(input.mainModule());
-        SortInfo sortInfo = SortInfo.fromModule(input.mainModule());
-        return new ConcretizeCells(configInfo, labelInfo, sortInfo, input.mainModule()).concretize(input.mainModule(), s);
-    }
+  private void checkIsSortPredicates(scala.collection.Set modules) {
+    Set generatedIsSorts =
+        stream(modules)
+            .flatMap(m -> stream(m.definedSorts()))
+            .map(s -> "is" + s.toString())
+            .collect(Collectors.toSet());
+
+    stream(modules)
+        .flatMap(
+            m ->
+                stream(
+                    m.productionsForSort()
+                        .getOrElse(Sorts.Bool().head(), Set$.MODULE$::empty)))
+        .collect(Collectors.toSet())
+        .stream()
+        .forEach(
+            prod -> {
+              Seq items = prod.items();
+              if (items.size() < 3) {
+                return;
+              }
+              ProductionItem first = items.head();
+              ProductionItem second = items.tail().head();
+              ProductionItem last = items.last();
+              // Check if the production is of the form isSort ( ... )
+              if ((first instanceof Terminal)
+                  && (second instanceof Terminal)
+                  && (last instanceof Terminal)
+                  && generatedIsSorts.contains(((Terminal) first).value())
+                  && ((Terminal) second).value().equals("(")
+                  && ((Terminal) last).value().equals(")")) {
+                errors.add(
+                    KEMException.compilerError(
+                        "Syntax declaration conflicts with automatically generated "
+                            + ((Terminal) first).value()
+                            + " predicate.",
+                        prod));
+              }
+            });
+  }
+
+  public static Definition addSemanticsModule(Definition d) {
+    java.util.Set allModules = mutable(d.modules());
+
+    Module languageParsingModule =
+        Constructors.Module(
+            "LANGUAGE-PARSING",
+            Set(
+                Import(d.mainModule(), true),
+                Import(d.getModule(d.att().get(Att.SYNTAX_MODULE())).get(), true),
+                Import(d.getModule("K-TERM").get(), true),
+                Import(d.getModule(RuleGrammarGenerator.ID_PROGRAM_PARSING).get(), true)),
+            Set(),
+            Att());
+    allModules.add(languageParsingModule);
+    return Constructors.Definition(d.mainModule(), immutable(allModules), d.att());
+  }
+
+  public Rule compileRule(Definition compiledDef, Rule parsedRule) {
+    return (Rule)
+        UnaryOperator.identity()
+            .andThen(new ResolveAnonVar()::resolve)
+            .andThen(new ResolveSemanticCasts(false)::resolve)
+            .andThen(s -> concretizeSentence(s, compiledDef))
+            .apply(parsedRule);
+  }
+
+  public Set parseModules(
+      CompiledDefinition definition,
+      String mainModule,
+      String entryPointModule,
+      File definitionFile,
+      Set excludedModuleTags,
+      boolean readOnlyCache,
+      boolean useCachedScanner) {
+    Set modules =
+        definitionParsing.parseModules(
+            definition,
+            mainModule,
+            entryPointModule,
+            definitionFile,
+            excludedModuleTags,
+            readOnlyCache,
+            useCachedScanner);
+    int totalBubbles =
+        definitionParsing.parsedBubbles.get() + definitionParsing.cachedBubbles.get();
+    sw.printIntermediate(
+        "Parse spec modules ["
+            + definitionParsing.parsedBubbles.get()
+            + "/"
+            + totalBubbles
+            + " rules]");
+    return modules;
+  }
+
+  private Sentence concretizeSentence(Sentence s, Definition input) {
+    ConfigurationInfoFromModule configInfo = new ConfigurationInfoFromModule(input.mainModule());
+    LabelInfo labelInfo = new LabelInfoFromModule(input.mainModule());
+    SortInfo sortInfo = SortInfo.fromModule(input.mainModule());
+    return new ConcretizeCells(configInfo, labelInfo, sortInfo, input.mainModule())
+        .concretize(input.mainModule(), s);
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kompile/KompileFrontEnd.java b/kernel/src/main/java/org/kframework/kompile/KompileFrontEnd.java
index 2bf3aaabfaa..f6da03a19f3 100644
--- a/kernel/src/main/java/org/kframework/kompile/KompileFrontEnd.java
+++ b/kernel/src/main/java/org/kframework/kompile/KompileFrontEnd.java
@@ -4,6 +4,8 @@
 import com.google.inject.Inject;
 import com.google.inject.Module;
 import com.google.inject.Provider;
+import java.util.ArrayList;
+import java.util.List;
 import org.kframework.compile.Backend;
 import org.kframework.main.FrontEnd;
 import org.kframework.main.GlobalOptions;
@@ -19,77 +21,88 @@
 import org.kframework.utils.options.InnerParsingOptions;
 import org.kframework.utils.options.OuterParsingOptions;
 
-import java.util.ArrayList;
-import java.util.List;
-
 public class KompileFrontEnd extends FrontEnd {
 
-    public static List getModules() {
-        List modules = new ArrayList<>();
-        modules.add(new KompileModule());
-        modules.add(new JCommanderModule());
-        modules.add(new CommonModule());
-        return modules;
-    }
+  public static List getModules() {
+    List modules = new ArrayList<>();
+    modules.add(new KompileModule());
+    modules.add(new JCommanderModule());
+    modules.add(new CommonModule());
+    return modules;
+  }
 
+  private final KompileOptions options;
+  private final OuterParsingOptions outerOptions;
+  private final InnerParsingOptions innerOptions;
+  private final Provider koreBackend;
+  private final Stopwatch sw;
+  private final KExceptionManager kem;
+  private final BinaryLoader loader;
+  private final Provider files;
 
-    private final KompileOptions options;
-    private final OuterParsingOptions outerOptions;
-    private final InnerParsingOptions innerOptions;
-    private final Provider koreBackend;
-    private final Stopwatch sw;
-    private final KExceptionManager kem;
-    private final BinaryLoader loader;
-    private final Provider files;
+  @Inject
+  KompileFrontEnd(
+      KompileOptions options,
+      OuterParsingOptions outerOptions,
+      InnerParsingOptions innerOptions,
+      GlobalOptions globalOptions,
+      @Usage String usage,
+      Provider koreBackend,
+      Stopwatch sw,
+      KExceptionManager kem,
+      BinaryLoader loader,
+      JarInfo jarInfo,
+      Provider files) {
+    super(kem, globalOptions, usage, jarInfo, files);
+    this.options = options;
+    this.outerOptions = outerOptions;
+    this.innerOptions = innerOptions;
+    this.koreBackend = koreBackend;
+    this.sw = sw;
+    this.kem = kem;
+    this.loader = loader;
+    this.files = files;
+  }
 
-    @Inject
-    KompileFrontEnd(
-            KompileOptions options,
-            OuterParsingOptions outerOptions,
-            InnerParsingOptions innerOptions,
-            GlobalOptions globalOptions,
-            @Usage String usage,
-            Provider koreBackend,
-            Stopwatch sw,
-            KExceptionManager kem,
-            BinaryLoader loader,
-            JarInfo jarInfo,
-            Provider files) {
-        super(kem, globalOptions, usage, jarInfo, files);
-        this.options = options;
-        this.outerOptions = outerOptions;
-        this.innerOptions = innerOptions;
-        this.koreBackend = koreBackend;
-        this.sw = sw;
-        this.kem = kem;
-        this.loader = loader;
-        this.files = files;
+  @Override
+  public int run() {
+    if (!outerOptions.mainDefinitionFile(files.get()).exists()) {
+      throw KEMException.criticalError(
+          "Definition file doesn't exist: "
+              + outerOptions.mainDefinitionFile(files.get()).getAbsolutePath());
     }
 
-    @Override
-    public int run() {
-        if (!outerOptions.mainDefinitionFile(files.get()).exists()) {
-            throw KEMException.criticalError("Definition file doesn't exist: " +
-                    outerOptions.mainDefinitionFile(files.get()).getAbsolutePath());
-        }
-
-        Kompile kompile = new Kompile(options, outerOptions, innerOptions, globalOptions, files.get(), kem, sw, innerOptions.profileRules == null);
-        Backend backend = koreBackend.get();
-        CompiledDefinition def = kompile.run(outerOptions.mainDefinitionFile(files.get()), options.mainModule(files.get()), options.syntaxModule(files.get()), backend.steps(), backend.excludedModuleTags());
-        kompile = null;
-        files.get().saveToKompiled("mainModule.txt", def.executionModule().name());
-        files.get().saveToKompiled("mainSyntaxModule.txt", def.mainSyntaxModuleName());
-        sw.printIntermediate("Kompile to kore");
-        loader.saveOrDie(files.get().resolveKompiled("compiled.bin"), def);
-        files.get().saveToKompiled("backend.txt", options.backend); // used by the krun script
-        sw.printIntermediate("Save to disk");
-        Backend.Holder h = new Backend.Holder(def);
-        def = null;
-        backend.accept(h);
-        sw.printIntermediate("Backend");
-        loader.saveOrDie(files.get().resolveKompiled("timestamp"), "");
-        sw.printTotal("Total");
-        return 0;
-    }
+    Kompile kompile =
+        new Kompile(
+            options,
+            outerOptions,
+            innerOptions,
+            globalOptions,
+            files.get(),
+            kem,
+            sw,
+            innerOptions.profileRules == null);
+    Backend backend = koreBackend.get();
+    CompiledDefinition def =
+        kompile.run(
+            outerOptions.mainDefinitionFile(files.get()),
+            options.mainModule(files.get()),
+            options.syntaxModule(files.get()),
+            backend.steps(),
+            backend.excludedModuleTags());
+    kompile = null;
+    files.get().saveToKompiled("mainModule.txt", def.executionModule().name());
+    files.get().saveToKompiled("mainSyntaxModule.txt", def.mainSyntaxModuleName());
+    sw.printIntermediate("Kompile to kore");
+    loader.saveOrDie(files.get().resolveKompiled("compiled.bin"), def);
+    files.get().saveToKompiled("backend.txt", options.backend); // used by the krun script
+    sw.printIntermediate("Save to disk");
+    Backend.Holder h = new Backend.Holder(def);
+    def = null;
+    backend.accept(h);
+    sw.printIntermediate("Backend");
+    loader.saveOrDie(files.get().resolveKompiled("timestamp"), "");
+    sw.printTotal("Total");
+    return 0;
+  }
 }
-
diff --git a/kernel/src/main/java/org/kframework/kompile/KompileModule.java b/kernel/src/main/java/org/kframework/kompile/KompileModule.java
index 3757d7c6432..9bf8acaabc7 100644
--- a/kernel/src/main/java/org/kframework/kompile/KompileModule.java
+++ b/kernel/src/main/java/org/kframework/kompile/KompileModule.java
@@ -4,7 +4,6 @@
 import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
 import com.google.inject.multibindings.Multibinder;
-import com.google.inject.name.Named;
 import org.kframework.main.FrontEnd;
 import org.kframework.main.GlobalOptions;
 import org.kframework.main.Tool;
@@ -16,42 +15,47 @@
 import org.kframework.utils.options.OutputDirectoryOptions;
 import org.kframework.utils.options.SMTOptions;
 
-import java.util.List;
-
 public class KompileModule extends AbstractModule {
 
-    public KompileModule() {
-    }
-
-    @Override
-    protected void configure() {
-        binder().requireAtInjectOnConstructors();
-        bind(FrontEnd.class).to(KompileFrontEnd.class);
-        bind(Tool.class).toInstance(Tool.KOMPILE);
-
-        install(new OuterParsingModule());
-        install(new BackendModule());
-
-        Multibinder optionsBinder = Multibinder.newSetBinder(binder(), Object.class, Options.class);
-        optionsBinder.addBinding().to(KompileOptions.class);
-    }
-
-    @Provides
-    SMTOptions smtOptions(KompileOptions options) {
-        return options.smt;
-    }
-
-    @Provides @RequestScoped
-    GlobalOptions globalOptions(KompileOptions options) {
-        return options.getGlobalOptions_UseOnlyInGuiceProvider();
-    }
-
-    @Provides
-    OuterParsingOptions outerParsingOptions(KompileOptions options) { return options.outerParsing; }
-
-    @Provides
-    InnerParsingOptions innerParsingOptions(KompileOptions options) { return options.innerParsing; }
-
-    @Provides
-    OutputDirectoryOptions outputDirectoryOptions(KompileOptions options) { return options.outputDirectory; }
+  public KompileModule() {}
+
+  @Override
+  protected void configure() {
+    binder().requireAtInjectOnConstructors();
+    bind(FrontEnd.class).to(KompileFrontEnd.class);
+    bind(Tool.class).toInstance(Tool.KOMPILE);
+
+    install(new OuterParsingModule());
+    install(new BackendModule());
+
+    Multibinder optionsBinder =
+        Multibinder.newSetBinder(binder(), Object.class, Options.class);
+    optionsBinder.addBinding().to(KompileOptions.class);
+  }
+
+  @Provides
+  SMTOptions smtOptions(KompileOptions options) {
+    return options.smt;
+  }
+
+  @Provides
+  @RequestScoped
+  GlobalOptions globalOptions(KompileOptions options) {
+    return options.getGlobalOptions_UseOnlyInGuiceProvider();
+  }
+
+  @Provides
+  OuterParsingOptions outerParsingOptions(KompileOptions options) {
+    return options.outerParsing;
+  }
+
+  @Provides
+  InnerParsingOptions innerParsingOptions(KompileOptions options) {
+    return options.innerParsing;
+  }
+
+  @Provides
+  OutputDirectoryOptions outputDirectoryOptions(KompileOptions options) {
+    return options.outputDirectory;
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/kompile/KompileOptions.java b/kernel/src/main/java/org/kframework/kompile/KompileOptions.java
index ba96c409fdd..fb5cbd39b8e 100644
--- a/kernel/src/main/java/org/kframework/kompile/KompileOptions.java
+++ b/kernel/src/main/java/org/kframework/kompile/KompileOptions.java
@@ -1,13 +1,17 @@
 // Copyright (c) K Team. All Rights Reserved.
 package org.kframework.kompile;
 
+import static org.kframework.kompile.Kompile.CACHE_FILE_NAME;
+
 import com.beust.jcommander.Parameter;
 import com.beust.jcommander.ParametersDelegate;
 import com.google.inject.Inject;
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
 import org.apache.commons.io.FilenameUtils;
 import org.kframework.backend.Backends;
 import org.kframework.main.GlobalOptions;
-import org.kframework.utils.errorsystem.KEMException;
 import org.kframework.utils.file.FileUtil;
 import org.kframework.utils.inject.RequestScoped;
 import org.kframework.utils.options.InnerParsingOptions;
@@ -16,136 +20,228 @@
 import org.kframework.utils.options.SMTOptions;
 import org.kframework.utils.options.StringListConverter;
 
-import java.io.Serializable;
-import java.util.Collections;
-import java.util.List;
-
-import static org.kframework.kompile.Kompile.CACHE_FILE_NAME;
-
 @RequestScoped
 public class KompileOptions implements Serializable {
-    @Inject
-    public KompileOptions() {}
-
-    /**
-     * WARNING: this field will be non-null in kompile tool, but null when KompileOption is deserialized,
-     * as part of CompiledDefinition, in any other tool. usability depends on context.
-     */
-    @ParametersDelegate
-    private final transient GlobalOptions global = new GlobalOptions();
-
-    /**
-     * Use only in the Guice Provider method, so it can be replaced by GlobalOptions from other tools.
-     */
-    GlobalOptions getGlobalOptions_UseOnlyInGuiceProvider() {
-        return global;
+  @Inject
+  public KompileOptions() {}
+
+  /**
+   * WARNING: this field will be non-null in kompile tool, but null when KompileOption is
+   * deserialized, as part of CompiledDefinition, in any other tool. usability depends on context.
+   */
+  @ParametersDelegate private final transient GlobalOptions global = new GlobalOptions();
+
+  /**
+   * Use only in the Guice Provider method, so it can be replaced by GlobalOptions from other tools.
+   */
+  GlobalOptions getGlobalOptions_UseOnlyInGuiceProvider() {
+    return global;
+  }
+
+  @ParametersDelegate public OuterParsingOptions outerParsing = new OuterParsingOptions();
+
+  @ParametersDelegate public transient InnerParsingOptions innerParsing = new InnerParsingOptions();
+
+  @ParametersDelegate public OutputDirectoryOptions outputDirectory = new OutputDirectoryOptions();
+
+  // Common options
+  @Parameter(
+      names = "--backend",
+      description =
+          "Choose a backend.  is one of [llvm|haskell|kore]. Each creates the kompiled K"
+              + " definition.",
+      descriptionKey = "backend")
+  public String backend = Backends.LLVM;
+
+  @Parameter(
+      names = "--main-module",
+      descriptionKey = "name",
+      description =
+          "Specify main module in which a program starts to execute. This information is used by"
+              + " 'krun'. The default is the name of the given K definition file without the"
+              + " extension (.k).")
+  private String mainModule;
+
+  public String mainModule(FileUtil files) {
+    if (mainModule == null) {
+      return FilenameUtils.getBaseName(outerParsing.mainDefinitionFile(files).getName())
+          .toUpperCase();
     }
-
-    @ParametersDelegate
-    public OuterParsingOptions outerParsing = new OuterParsingOptions();
-
-    @ParametersDelegate
-    public transient InnerParsingOptions innerParsing = new InnerParsingOptions();
-
-    @ParametersDelegate
-    public OutputDirectoryOptions outputDirectory = new OutputDirectoryOptions();
-
-    // Common options
-    @Parameter(names="--backend", description="Choose a backend.  is one of [llvm|haskell|kore]. Each creates the kompiled K definition.", descriptionKey = "backend")
-    public String backend = Backends.LLVM;
-
-    @Parameter(names="--main-module", descriptionKey = "name",
-            description="Specify main module in which a program starts to execute. This information is used by 'krun'. " +
-            "The default is the name of the given K definition file without the extension (.k).")
-    private String mainModule;
-
-    public String mainModule(FileUtil files) {
-        if (mainModule == null) {
-            return FilenameUtils.getBaseName(outerParsing.mainDefinitionFile(files).getName()).toUpperCase();
-        }
-        return mainModule;
+    return mainModule;
+  }
+
+  @Parameter(
+      names = "--syntax-module",
+      descriptionKey = "name",
+      description =
+          "Specify main module for syntax. This information is used by 'krun'. (Default:"
+              + " -SYNTAX).")
+  private String syntaxModule;
+
+  public String syntaxModule(FileUtil files) {
+    if (syntaxModule == null) {
+      return mainModule(files) + "-SYNTAX";
     }
-
-    @Parameter(names="--syntax-module", descriptionKey = "name",
-            description="Specify main module for syntax. This information is used by 'krun'. (Default: -SYNTAX).")
-    private String syntaxModule;
-
-    public String syntaxModule(FileUtil files) {
-        if (syntaxModule == null) {
-            return mainModule(files) + "-SYNTAX";
-        }
-        return syntaxModule;
-    }
-
-    @Parameter(names="--coverage", description="Generate coverage data when executing semantics.")
-    public boolean coverage;
-
-    @Parameter(names="--hook-namespaces", listConverter=StringListConverter.class, description=" is a whitespace-separated list of namespaces to include in the hooks defined in the definition", descriptionKey = "string", hidden = true)
-    public List hookNamespaces = Collections.emptyList();
-
-    @Parameter(names="-O1", description="Optimize in ways that improve performance and code size, but interfere with debugging and increase compilation time slightly.")
-    public boolean optimize1;
-
-    @Parameter(names="-O2", description="Optimize further in ways that improve performance and code size, but interfere with debugging more and increase compilation time somewhat.")
-    public boolean optimize2;
-
-    @Parameter(names="-O3", description="Optimize aggressively in ways that significantly improve performance, but interfere with debugging significantly and also increase compilation time and code size substantially.")
-    public boolean optimize3;
-
-    @Parameter(names="-E", description="Perform outer parsing and then stop and pretty print the definition to standard output. Useful for converting a K definition into a completely self-contained file when reporting a bug.")
-    public boolean preprocess;
-
-    @Parameter(names="--bison-lists", description="Make List and NeList left associative. This is useful for creating Bison parsers that use bounded stack space.", hidden = true)
-    public boolean bisonLists;
-
-    @Parameter(names="--read-only-kompiled-directory", description="Files in the generated kompiled directory should be read-only to other frontend tools.", hidden = true)
-    public boolean readOnlyKompiledDirectory = false;
-
-    @Parameter(names="--concrete-rules", description="List of rule labels to be considered concrete, in addition to rules marked with `[concrete]` attribute", descriptionKey = "labels")
-    public List extraConcreteRuleLabels = Collections.emptyList();
-
-    @ParametersDelegate
-    public SMTOptions smt = new SMTOptions();
-
-    @Parameter(names="--cache-file", description="Location of parse cache file. Default is $KOMPILED_DIR/" + CACHE_FILE_NAME + ".", hidden = true)
-    public String cacheFile;
-
-    @Parameter(names="--emit-json", description="Emit JSON serialized version of parsed and kompiled definitions.")
-    public boolean emitJson = false;
-
-    @Parameter(names="--gen-bison-parser", description="Emit bison parser for the PGM configuration variable within the syntax module of your definition into the kompiled definition.")
-    public boolean genBisonParser;
-
-    @Parameter(names="--gen-glr-bison-parser", description="Emit GLR bison parser for the PGM configuration variable within the syntax module of your definition into the kompiled definition.")
-    public boolean genGlrBisonParser;
-
-    @Parameter(names="--bison-file", description="C file containing functions to link into bison parser.", descriptionKey = "file", hidden = true)
-    public String bisonFile;
-
-    @Parameter(names="--bison-stack-max-depth", description="Maximum size of bison parsing stack.", descriptionKey = "size", hidden = true)
-    public long bisonStackMaxDepth = 10000;
-
-    @Parameter(names="--bison-parser-library", description="Generate a shared library rather than an executable for Bison parsers", hidden = true)
-    public boolean genBisonParserLibrary;
-
-    public static final String DEFAULT_TRANSITION = "transition";
-
-    @Parameter(names="--top-cell", description="Choose the top configuration cell when more than one is provided. Does nothing if only one top cell exists.")
-    public String topCell;
-
-    @Parameter(names="--debug-type-inference", description="Filename and source line of rule to debug type inference for. This is generally an option used only by maintainers.", descriptionKey = "file", hidden = true)
-    public String debugTypeInference;
-
-    @Parameter(names={"--post-process"}, description="JSON KAST => JSON KAST converter to run on definition after kompile pipeline.", descriptionKey = "command", hidden = true)
-    public String postProcess;
-
-    // TODO(dwightguth): remove this when it is no longer needed
-    @Parameter(names={"--disable-ceil-simplification-rules"}, description="Disable all rules with the simplification attribute whose left-hand side has a #Ceil at the top.", hidden = true)
-    public boolean disableCeilSimplificationRules;
-
-    @Parameter(names={"--allow-anywhere-haskell"}, description="Disable error message for anywhere rules on the Haskell backend.", hidden = true)
-    public boolean allowAnywhereRulesHaskell;
-
-    @Parameter(names="--enable-kore-antileft", description="Enable generation of legacy antileft priority predicates ", hidden = true)
-    public boolean enableKoreAntileft;
+    return syntaxModule;
+  }
+
+  @Parameter(names = "--coverage", description = "Generate coverage data when executing semantics.")
+  public boolean coverage;
+
+  @Parameter(
+      names = "--hook-namespaces",
+      listConverter = StringListConverter.class,
+      description =
+          " is a whitespace-separated list of namespaces to include in the hooks defined in"
+              + " the definition",
+      descriptionKey = "string",
+      hidden = true)
+  public List hookNamespaces = Collections.emptyList();
+
+  @Parameter(
+      names = "-O1",
+      description =
+          "Optimize in ways that improve performance and code size, but interfere with debugging"
+              + " and increase compilation time slightly.")
+  public boolean optimize1;
+
+  @Parameter(
+      names = "-O2",
+      description =
+          "Optimize further in ways that improve performance and code size, but interfere with"
+              + " debugging more and increase compilation time somewhat.")
+  public boolean optimize2;
+
+  @Parameter(
+      names = "-O3",
+      description =
+          "Optimize aggressively in ways that significantly improve performance, but interfere with"
+              + " debugging significantly and also increase compilation time and code size"
+              + " substantially.")
+  public boolean optimize3;
+
+  @Parameter(
+      names = "-E",
+      description =
+          "Perform outer parsing and then stop and pretty print the definition to standard output."
+              + " Useful for converting a K definition into a completely self-contained file when"
+              + " reporting a bug.")
+  public boolean preprocess;
+
+  @Parameter(
+      names = "--bison-lists",
+      description =
+          "Make List and NeList left associative. This is useful for creating Bison parsers that"
+              + " use bounded stack space.",
+      hidden = true)
+  public boolean bisonLists;
+
+  @Parameter(
+      names = "--read-only-kompiled-directory",
+      description =
+          "Files in the generated kompiled directory should be read-only to other frontend tools.",
+      hidden = true)
+  public boolean readOnlyKompiledDirectory = false;
+
+  @Parameter(
+      names = "--concrete-rules",
+      description =
+          "List of rule labels to be considered concrete, in addition to rules marked with"
+              + " `[concrete]` attribute",
+      descriptionKey = "labels")
+  public List extraConcreteRuleLabels = Collections.emptyList();
+
+  @ParametersDelegate public SMTOptions smt = new SMTOptions();
+
+  @Parameter(
+      names = "--cache-file",
+      description =
+          "Location of parse cache file. Default is $KOMPILED_DIR/" + CACHE_FILE_NAME + ".",
+      hidden = true)
+  public String cacheFile;
+
+  @Parameter(
+      names = "--emit-json",
+      description = "Emit JSON serialized version of parsed and kompiled definitions.")
+  public boolean emitJson = false;
+
+  @Parameter(
+      names = "--gen-bison-parser",
+      description =
+          "Emit bison parser for the PGM configuration variable within the syntax module of your"
+              + " definition into the kompiled definition.")
+  public boolean genBisonParser;
+
+  @Parameter(
+      names = "--gen-glr-bison-parser",
+      description =
+          "Emit GLR bison parser for the PGM configuration variable within the syntax module of"
+              + " your definition into the kompiled definition.")
+  public boolean genGlrBisonParser;
+
+  @Parameter(
+      names = "--bison-file",
+      description = "C file containing functions to link into bison parser.",
+      descriptionKey = "file",
+      hidden = true)
+  public String bisonFile;
+
+  @Parameter(
+      names = "--bison-stack-max-depth",
+      description = "Maximum size of bison parsing stack.",
+      descriptionKey = "size",
+      hidden = true)
+  public long bisonStackMaxDepth = 10000;
+
+  @Parameter(
+      names = "--bison-parser-library",
+      description = "Generate a shared library rather than an executable for Bison parsers",
+      hidden = true)
+  public boolean genBisonParserLibrary;
+
+  public static final String DEFAULT_TRANSITION = "transition";
+
+  @Parameter(
+      names = "--top-cell",
+      description =
+          "Choose the top configuration cell when more than one is provided. Does nothing if only"
+              + " one top cell exists.")
+  public String topCell;
+
+  @Parameter(
+      names = "--debug-type-inference",
+      description =
+          "Filename and source line of rule to debug type inference for. This is generally an"
+              + " option used only by maintainers.",
+      descriptionKey = "file",
+      hidden = true)
+  public String debugTypeInference;
+
+  @Parameter(
+      names = {"--post-process"},
+      description = "JSON KAST => JSON KAST converter to run on definition after kompile pipeline.",
+      descriptionKey = "command",
+      hidden = true)
+  public String postProcess;
+
+  // TODO(dwightguth): remove this when it is no longer needed
+  @Parameter(
+      names = {"--disable-ceil-simplification-rules"},
+      description =
+          "Disable all rules with the simplification attribute whose left-hand side has a #Ceil at"
+              + " the top.",
+      hidden = true)
+  public boolean disableCeilSimplificationRules;
+
+  @Parameter(
+      names = {"--allow-anywhere-haskell"},
+      description = "Disable error message for anywhere rules on the Haskell backend.",
+      hidden = true)
+  public boolean allowAnywhereRulesHaskell;
+
+  @Parameter(
+      names = "--enable-kore-antileft",
+      description = "Enable generation of legacy antileft priority predicates ",
+      hidden = true)
+  public boolean enableKoreAntileft;
 }
diff --git a/kernel/src/main/java/org/kframework/kompile/KompileUsageFormatter.java b/kernel/src/main/java/org/kframework/kompile/KompileUsageFormatter.java
index f77bb3f154e..d82cab88997 100644
--- a/kernel/src/main/java/org/kframework/kompile/KompileUsageFormatter.java
+++ b/kernel/src/main/java/org/kframework/kompile/KompileUsageFormatter.java
@@ -3,422 +3,424 @@
 
 import com.beust.jcommander.*;
 import com.beust.jcommander.internal.Lists;
-
 import java.util.*;
 import java.util.ResourceBundle;
 
 /**
  * This is a customized `usage` for kompile based on DefaultUsageFormatter
  * https://github.com/cbeust/jcommander/blob/master/src/main/java/com/beust/jcommander/DefaultUsageFormatter.java
- * from the JCommander library. The only changes are related to printing
- * hidden options when --help-hidden is provided.
+ * from the JCommander library. The only changes are related to printing hidden options when
+ * --help-hidden is provided.
  *
- * Original licence:
+ * 

Original licence: * - * Copyright (C) 2010 the original author or authors. - * See the notice.md file distributed with this work for additional - * information regarding copyright ownership. + *

Copyright (C) 2010 the original author or authors. See the notice.md file distributed with + * this work for additional information regarding copyright ownership. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + *

http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and + *

Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and * limitations under the License. * - * NOTICE: - * JCommander Copyright Notices - * Copyright 2010 Cedric Beust cedric@beust.com + *

NOTICE: JCommander Copyright Notices Copyright 2010 Cedric Beust cedric@beust.com */ public class KompileUsageFormatter implements IUsageFormatter { - private final JCommander commander; - private boolean helpHidden = false; - private boolean help = false; - - public KompileUsageFormatter(JCommander commander) { - this.commander = commander; - for (ParameterDescription pd : commander.getFields().values()) { - if (pd.getParameter().isHelp()) { - if (pd.getLongestName().equals("--help") && pd.isAssigned()) - help = true; - if (pd.getLongestName().equals("--help-hidden") && pd.isAssigned()) - helpHidden = true; - } - } + private final JCommander commander; + private boolean helpHidden = false; + private boolean help = false; + + public KompileUsageFormatter(JCommander commander) { + this.commander = commander; + for (ParameterDescription pd : commander.getFields().values()) { + if (pd.getParameter().isHelp()) { + if (pd.getLongestName().equals("--help") && pd.isAssigned()) help = true; + if (pd.getLongestName().equals("--help-hidden") && pd.isAssigned()) helpHidden = true; + } } - - /** - * Prints the usage to {@link JCommander#getConsole()} on the underlying commander instance. - */ - public final void usage(String commandName) { - StringBuilder sb = new StringBuilder(); - usage(commandName, sb); - commander.getConsole().println(sb.toString()); + } + + /** Prints the usage to {@link JCommander#getConsole()} on the underlying commander instance. */ + public final void usage(String commandName) { + StringBuilder sb = new StringBuilder(); + usage(commandName, sb); + commander.getConsole().println(sb.toString()); + } + + /** Store the usage for the argument command in the argument string builder. */ + public final void usage(String commandName, StringBuilder out) { + usage(commandName, out, ""); + } + + /** Store the usage in the argument string builder. */ + public final void usage(StringBuilder out) { + usage(out, ""); + } + + /** + * Store the usage for the command in the argument string builder, indenting every line with the + * value of indent. + */ + public final void usage(String commandName, StringBuilder out, String indent) { + String description = getCommandDescription(commandName); + JCommander jc = commander.findCommandByAlias(commandName); + + if (description != null) { + out.append(indent).append(description); + out.append("\n"); } - - /** - * Store the usage for the argument command in the argument string builder. - */ - public final void usage(String commandName, StringBuilder out) { - usage(commandName, out, ""); + jc.getUsageFormatter().usage(out, indent); + } + + /** + * Stores the usage in the argument string builder, with the argument indentation. This works by + * appending each portion of the help in the following order. Their outputs can be modified by + * overriding them in a subclass of this class. + * + *

    + *
  • Main line - {@link #appendMainLine(StringBuilder, boolean, boolean, int, String)} + *
  • Parameters - {@link #appendAllParametersDetails(StringBuilder, int, String, List)} + *
  • Commands - {@link #appendCommands(StringBuilder, int, int, String)} + *
+ */ + public void usage(StringBuilder out, String indent) { + if (commander.getDescriptions() == null) { + commander.createDescriptions(); } + boolean hasCommands = !commander.getCommands().isEmpty(); + boolean hasOptions = !commander.getDescriptions().isEmpty(); - /** - * Store the usage in the argument string builder. - */ - public final void usage(StringBuilder out) { - usage(out, ""); - } + // Indentation constants + final int descriptionIndent = 6; + final int indentCount = indent.length() + descriptionIndent; - /** - * Store the usage for the command in the argument string builder, indenting every line with the - * value of indent. - */ - public final void usage(String commandName, StringBuilder out, String indent) { - String description = getCommandDescription(commandName); - JCommander jc = commander.findCommandByAlias(commandName); - - if (description != null) { - out.append(indent).append(description); - out.append("\n"); - } - jc.getUsageFormatter().usage(out, indent); - } - - /** - * Stores the usage in the argument string builder, with the argument indentation. This works by appending - * each portion of the help in the following order. Their outputs can be modified by overriding them in a - * subclass of this class. - * - *
    - *
  • Main line - {@link #appendMainLine(StringBuilder, boolean, boolean, int, String)}
  • - *
  • Parameters - {@link #appendAllParametersDetails(StringBuilder, int, String, List)}
  • - *
  • Commands - {@link #appendCommands(StringBuilder, int, int, String)}
  • - *
- */ - public void usage(StringBuilder out, String indent) { - if (commander.getDescriptions() == null) { - commander.createDescriptions(); - } - boolean hasCommands = !commander.getCommands().isEmpty(); - boolean hasOptions = !commander.getDescriptions().isEmpty(); - - // Indentation constants - final int descriptionIndent = 6; - final int indentCount = indent.length() + descriptionIndent; - - // Append first line (aka main line) of the usage - appendMainLine(out, hasOptions, hasCommands, indentCount, indent); - - // Align the descriptions at the "longestName" column - int longestName = 0; - List sortedParameters = Lists.newArrayList(); - - for (ParameterDescription pd : commander.getFields().values()) { - if ((help && !pd.getParameter().hidden()) || (helpHidden && pd.getParameter().hidden())) { - sortedParameters.add(pd); - // + to have an extra space between the name and the description - int length = pd.getNames().length() + 2; - - if (length > longestName) { - longestName = length; - } - } - } + // Append first line (aka main line) of the usage + appendMainLine(out, hasOptions, hasCommands, indentCount, indent); - // Sort the options - sortedParameters.sort(commander.getParameterDescriptionComparator()); + // Align the descriptions at the "longestName" column + int longestName = 0; + List sortedParameters = Lists.newArrayList(); - // Append all the parameter names and descriptions - appendAllParametersDetails(out, indentCount, indent, sortedParameters); + for (ParameterDescription pd : commander.getFields().values()) { + if ((help && !pd.getParameter().hidden()) || (helpHidden && pd.getParameter().hidden())) { + sortedParameters.add(pd); + // + to have an extra space between the name and the description + int length = pd.getNames().length() + 2; - // Append commands if they were specified - if (hasCommands) { - appendCommands(out, indentCount, descriptionIndent, indent); + if (length > longestName) { + longestName = length; } + } } - /** - * Appends the main line segment of the usage to the argument string builder, indenting every - * line with indentCount-many indent. - * - * @param out the builder to append to - * @param hasOptions if the options section should be appended - * @param hasCommands if the comments section should be appended - * @param indentCount the amount of indentation to apply - * @param indent the indentation - */ - public void appendMainLine(StringBuilder out, boolean hasOptions, boolean hasCommands, int indentCount, - String indent) { - String programName = commander.getProgramDisplayName() != null - ? commander.getProgramDisplayName() : "
"; - StringBuilder mainLine = new StringBuilder(); - mainLine.append(indent).append("Usage: ").append(programName); - - if (hasOptions) { - mainLine.append(" [options] "); - } + // Sort the options + sortedParameters.sort(commander.getParameterDescriptionComparator()); - if (hasCommands) { - mainLine.append(indent).append(" [command] [command options]"); - } + // Append all the parameter names and descriptions + appendAllParametersDetails(out, indentCount, indent, sortedParameters); - //if (commander.getMainParameter() != null && commander.getMainParameter().getDescription() != null) { - // mainLine.append(" ").append(commander.getMainParameter().getDescription().getDescription()); - //} - wrapDescription(out, indentCount, mainLine.toString()); - out.append("\n"); + // Append commands if they were specified + if (hasCommands) { + appendCommands(out, indentCount, descriptionIndent, indent); + } + } + + /** + * Appends the main line segment of the usage to the argument string builder, indenting every line + * with indentCount-many indent. + * + * @param out the builder to append to + * @param hasOptions if the options section should be appended + * @param hasCommands if the comments section should be appended + * @param indentCount the amount of indentation to apply + * @param indent the indentation + */ + public void appendMainLine( + StringBuilder out, boolean hasOptions, boolean hasCommands, int indentCount, String indent) { + String programName = + commander.getProgramDisplayName() != null + ? commander.getProgramDisplayName() + : "
"; + StringBuilder mainLine = new StringBuilder(); + mainLine.append(indent).append("Usage: ").append(programName); + + if (hasOptions) { + mainLine.append(" [options] "); } - /** - * Appends the details of all parameters in the given order to the argument string builder, indenting every - * line with indentCount-many indent. - * - * @param out the builder to append to - * @param indentCount the amount of indentation to apply - * @param indent the indentation - * @param sortedParameters the parameters to append to the builder - */ - public void appendAllParametersDetails(StringBuilder out, int indentCount, String indent, - List sortedParameters) { - if (sortedParameters.size() > 0) { - out.append(indent).append(" Options:\n"); - } + if (hasCommands) { + mainLine.append(indent).append(" [command] [command options]"); + } - for (ParameterDescription pd : sortedParameters) { - WrappedParameter parameter = pd.getParameter(); - String description = pd.getDescription(); - boolean hasDescription = !description.isEmpty(); - String descriptionKey = pd.getParameter().getParameter().descriptionKey(); - descriptionKey = descriptionKey.isEmpty() ? "" : " <" + descriptionKey + ">"; - - // First line, command name - out.append(indent) - .append(" ") - .append(parameter.required() ? "* " : " ") - .append(pd.getLongestName()) - .append(descriptionKey) - .append(Arrays.stream(pd.getParameter().names()).count() > 1 ? ", " + pd.getParameter().names()[1] + descriptionKey : "") - .append("\n"); - - if (hasDescription) { - wrapDescription(out, indentCount, s(indentCount) + description); - } - Object def = pd.getDefault(); - - if (pd.isDynamicParameter()) { - String syntax = "Syntax: " + parameter.names()[0] + "key" + parameter.getAssignment() + "value"; - - if (hasDescription) { - out.append(newLineAndIndent(indentCount)); - } else { - out.append(s(indentCount)); - } - out.append(syntax); - } - - if (def != null && !pd.isHelp()) { - String displayedDef = Strings.isStringEmpty(def.toString()) ? "" : def.toString(); - String defaultText = "Default: " + (parameter.password() ? "********" : displayedDef); - - if (hasDescription) { - out.append(newLineAndIndent(indentCount)); - } else { - out.append(s(indentCount)); - } - out.append(defaultText); - } - Class type = pd.getParameterized().getType(); - - if (type.isEnum()) { - String valueList = EnumSet.allOf((Class) type).toString(); - String possibleValues = "Possible Values: " + valueList; - - // Prevent duplicate values list, since it is set as 'Options: [values]' if the description - // of an enum field is empty in ParameterDescription#init(..) - if (!description.contains("Options: " + valueList)) { - if (hasDescription) { - out.append(newLineAndIndent(indentCount)); - } else { - out.append(s(indentCount)); - } - out.append(possibleValues); - } - } - out.append("\n"); - } + // if (commander.getMainParameter() != null && commander.getMainParameter().getDescription() != + // null) { + // mainLine.append(" + // ").append(commander.getMainParameter().getDescription().getDescription()); + // } + wrapDescription(out, indentCount, mainLine.toString()); + out.append("\n"); + } + + /** + * Appends the details of all parameters in the given order to the argument string builder, + * indenting every line with indentCount-many indent. + * + * @param out the builder to append to + * @param indentCount the amount of indentation to apply + * @param indent the indentation + * @param sortedParameters the parameters to append to the builder + */ + public void appendAllParametersDetails( + StringBuilder out, + int indentCount, + String indent, + List sortedParameters) { + if (sortedParameters.size() > 0) { + out.append(indent).append(" Options:\n"); } - /** - * Appends the details of all commands to the argument string builder, indenting every line with - * indentCount-many indent. The commands are obtained from calling - * {@link JCommander#getRawCommands()} and the commands are resolved using - * {@link JCommander#findCommandByAlias(String)} on the underlying commander instance. - * - * @param out the builder to append to - * @param indentCount the amount of indentation to apply - * @param descriptionIndent the indentation for the description - * @param indent the indentation - */ - public void appendCommands(StringBuilder out, int indentCount, int descriptionIndent, String indent) { - boolean hasOnlyHiddenCommands = true; - for (Map.Entry commands : commander.getRawCommands().entrySet()) { - Object arg = commands.getValue().getObjects().get(0); - Parameters p = arg.getClass().getAnnotation(Parameters.class); - - if (p == null || ((help && !p.hidden()) || (helpHidden && p.hidden()))) - hasOnlyHiddenCommands = false; + for (ParameterDescription pd : sortedParameters) { + WrappedParameter parameter = pd.getParameter(); + String description = pd.getDescription(); + boolean hasDescription = !description.isEmpty(); + String descriptionKey = pd.getParameter().getParameter().descriptionKey(); + descriptionKey = descriptionKey.isEmpty() ? "" : " <" + descriptionKey + ">"; + + // First line, command name + out.append(indent) + .append(" ") + .append(parameter.required() ? "* " : " ") + .append(pd.getLongestName()) + .append(descriptionKey) + .append( + Arrays.stream(pd.getParameter().names()).count() > 1 + ? ", " + pd.getParameter().names()[1] + descriptionKey + : "") + .append("\n"); + + if (hasDescription) { + wrapDescription(out, indentCount, s(indentCount) + description); + } + Object def = pd.getDefault(); + + if (pd.isDynamicParameter()) { + String syntax = + "Syntax: " + parameter.names()[0] + "key" + parameter.getAssignment() + "value"; + + if (hasDescription) { + out.append(newLineAndIndent(indentCount)); + } else { + out.append(s(indentCount)); + } + out.append(syntax); + } + + if (def != null && !pd.isHelp()) { + String displayedDef = + Strings.isStringEmpty(def.toString()) ? "" : def.toString(); + String defaultText = "Default: " + (parameter.password() ? "********" : displayedDef); + + if (hasDescription) { + out.append(newLineAndIndent(indentCount)); + } else { + out.append(s(indentCount)); } + out.append(defaultText); + } + Class type = pd.getParameterized().getType(); + + if (type.isEnum()) { + String valueList = EnumSet.allOf((Class) type).toString(); + String possibleValues = "Possible Values: " + valueList; + + // Prevent duplicate values list, since it is set as 'Options: [values]' if the description + // of an enum field is empty in ParameterDescription#init(..) + if (!description.contains("Options: " + valueList)) { + if (hasDescription) { + out.append(newLineAndIndent(indentCount)); + } else { + out.append(s(indentCount)); + } + out.append(possibleValues); + } + } + out.append("\n"); + } + } + + /** + * Appends the details of all commands to the argument string builder, indenting every line with + * indentCount-many indent. The commands are obtained from calling {@link + * JCommander#getRawCommands()} and the commands are resolved using {@link + * JCommander#findCommandByAlias(String)} on the underlying commander instance. + * + * @param out the builder to append to + * @param indentCount the amount of indentation to apply + * @param descriptionIndent the indentation for the description + * @param indent the indentation + */ + public void appendCommands( + StringBuilder out, int indentCount, int descriptionIndent, String indent) { + boolean hasOnlyHiddenCommands = true; + for (Map.Entry commands : + commander.getRawCommands().entrySet()) { + Object arg = commands.getValue().getObjects().get(0); + Parameters p = arg.getClass().getAnnotation(Parameters.class); + + if (p == null || ((help && !p.hidden()) || (helpHidden && p.hidden()))) + hasOnlyHiddenCommands = false; + } - if (hasOnlyHiddenCommands) - return; + if (hasOnlyHiddenCommands) return; - out.append(indent + " Commands:\n"); + out.append(indent + " Commands:\n"); - // The magic value 3 is the number of spaces between the name of the option and its description - for (Map.Entry commands : commander.getRawCommands().entrySet()) { - Object arg = commands.getValue().getObjects().get(0); - Parameters p = arg.getClass().getAnnotation(Parameters.class); + // The magic value 3 is the number of spaces between the name of the option and its description + for (Map.Entry commands : + commander.getRawCommands().entrySet()) { + Object arg = commands.getValue().getObjects().get(0); + Parameters p = arg.getClass().getAnnotation(Parameters.class); - if (p == null || ((help && !p.hidden()) || (helpHidden && p.hidden()))) { - JCommander.ProgramName progName = commands.getKey(); - String dispName = progName.getDisplayName(); - String description = indent + s(4) + dispName + s(6) + getCommandDescription(progName.getName()); - wrapDescription(out, indentCount + descriptionIndent, description); - out.append("\n"); + if (p == null || ((help && !p.hidden()) || (helpHidden && p.hidden()))) { + JCommander.ProgramName progName = commands.getKey(); + String dispName = progName.getDisplayName(); + String description = + indent + s(4) + dispName + s(6) + getCommandDescription(progName.getName()); + wrapDescription(out, indentCount + descriptionIndent, description); + out.append("\n"); - // Options for this command - JCommander jc = commander.findCommandByAlias(progName.getName()); - jc.getUsageFormatter().usage(out, indent + s(6)); - out.append("\n"); - } - } + // Options for this command + JCommander jc = commander.findCommandByAlias(progName.getName()); + jc.getUsageFormatter().usage(out, indent + s(6)); + out.append("\n"); + } } - - /** - * Returns the description of the command corresponding to the argument command name. The commands are resolved - * by calling {@link JCommander#findCommandByAlias(String)}, and the default resource bundle used from - * {@link JCommander#getBundle()} on the underlying commander instance. - * - * @param commandName the name of the command to get the description for - * @return the description of the command. - */ - public String getCommandDescription(String commandName) { - JCommander jc = commander.findCommandByAlias(commandName); - - if (jc == null) { - throw new ParameterException("Asking description for unknown command: " + commandName); - } - Object arg = jc.getObjects().get(0); - Parameters p = arg.getClass().getAnnotation(Parameters.class); - java.util.ResourceBundle bundle; - String result = null; - - if (p != null) { - result = p.commandDescription(); - String bundleName = p.resourceBundle(); - - if (!bundleName.isEmpty()) { - bundle = ResourceBundle.getBundle(bundleName, Locale.getDefault()); - } else { - bundle = commander.getBundle(); - } - - if (bundle != null) { - String descriptionKey = p.commandDescriptionKey(); - - if (!descriptionKey.isEmpty()) { - result = getI18nString(bundle, descriptionKey, p.commandDescription()); - } - } - } - return result; + } + + /** + * Returns the description of the command corresponding to the argument command name. The commands + * are resolved by calling {@link JCommander#findCommandByAlias(String)}, and the default resource + * bundle used from {@link JCommander#getBundle()} on the underlying commander instance. + * + * @param commandName the name of the command to get the description for + * @return the description of the command. + */ + public String getCommandDescription(String commandName) { + JCommander jc = commander.findCommandByAlias(commandName); + + if (jc == null) { + throw new ParameterException("Asking description for unknown command: " + commandName); } - - /** - * Wrap a potentially long line to the value obtained by calling {@link JCommander#getColumnSize()} on the - * underlying commander instance. - * - * @param out the output - * @param indent the indentation in spaces for lines after the first line. - * @param currentLineIndent the length of the indentation of the initial line - * @param description the text to wrap. No extra spaces are inserted before {@code - * description}. If the first line needs to be indented prepend the - * correct number of spaces to {@code description}. - */ - public void wrapDescription(StringBuilder out, int indent, int currentLineIndent, String description) { - int max = commander.getColumnSize(); - String[] words = description.split(" "); - int current = currentLineIndent; - - for (int i = 0; i < words.length; i++) { - String word = words[i]; - - if (word.length() > max || current + 1 + word.length() <= max) { - out.append(word); - current += word.length(); - - if (i != words.length - 1) { - out.append(" "); - current++; - } - } else { - out.append("\n").append(s(indent)).append(word).append(" "); - current = indent + word.length() + 1; - } + Object arg = jc.getObjects().get(0); + Parameters p = arg.getClass().getAnnotation(Parameters.class); + java.util.ResourceBundle bundle; + String result = null; + + if (p != null) { + result = p.commandDescription(); + String bundleName = p.resourceBundle(); + + if (!bundleName.isEmpty()) { + bundle = ResourceBundle.getBundle(bundleName, Locale.getDefault()); + } else { + bundle = commander.getBundle(); + } + + if (bundle != null) { + String descriptionKey = p.commandDescriptionKey(); + + if (!descriptionKey.isEmpty()) { + result = getI18nString(bundle, descriptionKey, p.commandDescription()); } + } } - - /** - * Wrap a potentially long line to { @link #commander#getColumnSize()}. - * - * @param out the output - * @param indent the indentation in spaces for lines after the first line. - * @param description the text to wrap. No extra spaces are inserted before {@code - * description}. If the first line needs to be indented prepend the - * correct number of spaces to {@code description}. - * @see #wrapDescription(StringBuilder, int, int, String) - */ - public void wrapDescription(StringBuilder out, int indent, String description) { - wrapDescription(out, indent, 0, description); - } - - /** - * Returns the internationalized version of the string if available, otherwise it returns def. - * - * @return the internationalized version of the string if available, otherwise it returns def - */ - public static String getI18nString(ResourceBundle bundle, String key, String def) { - String s = bundle != null ? bundle.getString(key) : null; - return s != null ? s : def; - } - - /** - * Returns count-many spaces. - * - * @return count-many spaces - */ - public static String s(int count) { - StringBuilder result = new StringBuilder(); - - for (int i = 0; i < count; i++) { - result.append(" "); + return result; + } + + /** + * Wrap a potentially long line to the value obtained by calling {@link + * JCommander#getColumnSize()} on the underlying commander instance. + * + * @param out the output + * @param indent the indentation in spaces for lines after the first line. + * @param currentLineIndent the length of the indentation of the initial line + * @param description the text to wrap. No extra spaces are inserted before {@code description}. + * If the first line needs to be indented prepend the correct number of spaces to {@code + * description}. + */ + public void wrapDescription( + StringBuilder out, int indent, int currentLineIndent, String description) { + int max = commander.getColumnSize(); + String[] words = description.split(" "); + int current = currentLineIndent; + + for (int i = 0; i < words.length; i++) { + String word = words[i]; + + if (word.length() > max || current + 1 + word.length() <= max) { + out.append(word); + current += word.length(); + + if (i != words.length - 1) { + out.append(" "); + current++; } - return result.toString(); + } else { + out.append("\n").append(s(indent)).append(word).append(" "); + current = indent + word.length() + 1; + } } - - /** - * Returns new line followed by indent-many spaces. - * - * @return new line followed by indent-many spaces - */ - private static String newLineAndIndent(int indent) { - return "\n" + s(indent); + } + + /** + * Wrap a potentially long line to { @link #commander#getColumnSize()}. + * + * @param out the output + * @param indent the indentation in spaces for lines after the first line. + * @param description the text to wrap. No extra spaces are inserted before {@code description}. + * If the first line needs to be indented prepend the correct number of spaces to {@code + * description}. + * @see #wrapDescription(StringBuilder, int, int, String) + */ + public void wrapDescription(StringBuilder out, int indent, String description) { + wrapDescription(out, indent, 0, description); + } + + /** + * Returns the internationalized version of the string if available, otherwise it returns def. + * + * @return the internationalized version of the string if available, otherwise it returns def + */ + public static String getI18nString(ResourceBundle bundle, String key, String def) { + String s = bundle != null ? bundle.getString(key) : null; + return s != null ? s : def; + } + + /** + * Returns count-many spaces. + * + * @return count-many spaces + */ + public static String s(int count) { + StringBuilder result = new StringBuilder(); + + for (int i = 0; i < count; i++) { + result.append(" "); } + return result.toString(); + } + + /** + * Returns new line followed by indent-many spaces. + * + * @return new line followed by indent-many spaces + */ + private static String newLineAndIndent(int indent) { + return "\n" + s(indent); + } } diff --git a/kernel/src/main/java/org/kframework/kore/convertors/KILTransformation.java b/kernel/src/main/java/org/kframework/kore/convertors/KILTransformation.java index 2a863b991ec..a62fb914812 100644 --- a/kernel/src/main/java/org/kframework/kore/convertors/KILTransformation.java +++ b/kernel/src/main/java/org/kframework/kore/convertors/KILTransformation.java @@ -6,42 +6,53 @@ import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; import java.util.function.Function; - import org.kframework.kil.ASTNode; import org.kframework.utils.errorsystem.KEMException; public class KILTransformation implements Function { - @SuppressWarnings("serial") - static class VisitingException extends RuntimeException { - VisitingException(String message, Throwable e) { - super(message, e); - } + @SuppressWarnings("serial") + static class VisitingException extends RuntimeException { + VisitingException(String message, Throwable e) { + super(message, e); } + } - // DISABLE EXCEPTION CHECKING - public R apply(ASTNode t) { - try { - MethodHandles.Lookup lookup = MethodHandles.lookup(); - Method visitMethod = this.getClass().getDeclaredMethod("apply", t.getClass()); - MethodHandle visitMethodHandle = lookup.unreflect(visitMethod); - return (R) visitMethodHandle.invoke(this, t); - } catch (NoSuchMethodException e) { - throw new VisitingException("Visitor " + this.getClass() - + " is missing a definition for visit(" + t.getClass() + ")" - + ". Encounteed when visiting " + makeErrorMessage(t), e); - // DISABLE EXCEPTION CHECKSTYLE - } catch (VisitingException | KEMException e) { - throw e; - } catch (Throwable e) { - throw new VisitingException(makeErrorMessage(t), e); - } - // ENABLE EXCEPTION CHECKSTYLE + // DISABLE EXCEPTION CHECKING + public R apply(ASTNode t) { + try { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + Method visitMethod = this.getClass().getDeclaredMethod("apply", t.getClass()); + MethodHandle visitMethodHandle = lookup.unreflect(visitMethod); + return (R) visitMethodHandle.invoke(this, t); + } catch (NoSuchMethodException e) { + throw new VisitingException( + "Visitor " + + this.getClass() + + " is missing a definition for visit(" + + t.getClass() + + ")" + + ". Encounteed when visiting " + + makeErrorMessage(t), + e); + // DISABLE EXCEPTION CHECKSTYLE + } catch (VisitingException | KEMException e) { + throw e; + } catch (Throwable e) { + throw new VisitingException(makeErrorMessage(t), e); } - // ENABLE EXCEPTION CHECKING + // ENABLE EXCEPTION CHECKSTYLE + } - public String makeErrorMessage(ASTNode t) { - return t.toString() + " at location " + t.getLocation() + " in file " + t.getSource() - + " of class " + t.getClass().toString(); - } + // ENABLE EXCEPTION CHECKING + + public String makeErrorMessage(ASTNode t) { + return t.toString() + + " at location " + + t.getLocation() + + " in file " + + t.getSource() + + " of class " + + t.getClass().toString(); + } } diff --git a/kernel/src/main/java/org/kframework/kore/convertors/KILtoKORE.java b/kernel/src/main/java/org/kframework/kore/convertors/KILtoKORE.java index af9b5cbeecb..71f88cabb73 100644 --- a/kernel/src/main/java/org/kframework/kore/convertors/KILtoKORE.java +++ b/kernel/src/main/java/org/kframework/kore/convertors/KILtoKORE.java @@ -2,7 +2,15 @@ package org.kframework.kore.convertors; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + import com.google.common.collect.Sets; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.kframework.attributes.Att; import org.kframework.attributes.Location; import org.kframework.attributes.Source; @@ -25,318 +33,382 @@ import org.kframework.utils.errorsystem.KEMException; import scala.collection.Seq; -import java.util.*; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.kframework.Collections.*; -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; - public class KILtoKORE extends KILTransformation { - private final org.kframework.kil.loader.Context context; - private final boolean syntactic; - private String moduleName; - private final boolean bisonLists; - - public KILtoKORE(org.kframework.kil.loader.Context context, boolean syntactic, boolean bisonLists) { - this.context = context; - this.syntactic = syntactic; - this.bisonLists = bisonLists; - } - - public FlatModule toFlatModule(Module m) { - CheckListDecl.check(m); - CheckBracket.check(m); - moduleName = m.getName(); - - Set items = m.getItems().stream() - .filter(j -> !(j instanceof org.kframework.kil.Import)) - .flatMap(j -> apply(j).stream()).collect(Collectors.toSet()); - - // temporarily declare cell sorts used in the RHS of productions until we - // can parse the configuration so Module checks don't fail - Set tempCellSorts = items.stream().filter(p -> p instanceof org.kframework.definition.Production) - .map(p -> (org.kframework.definition.Production) p) - .flatMap(p -> stream(p.items()).filter(itm -> itm instanceof org.kframework.definition.NonTerminal) - .map(i -> (org.kframework.definition.NonTerminal) i) - .flatMap(nt -> nt.sort().name().endsWith("Cell") || nt.sort().name().endsWith("CellFragment") ? - Stream.of(SyntaxSort.apply(Seq(), nt.sort(), Att.empty().add(Att.TEMPORARY_CELL_SORT_DECL()))) : Stream.of()) - ).collect(Collectors.toSet()); - items.addAll(tempCellSorts); - - Set importedModuleNames = m.getItems().stream() - .filter(imp -> imp instanceof Import) - .map(imp -> apply((Import)imp)) - .collect(Collectors.toSet()); - - Att att = convertAttributes(m); - att = att.add(Att.DIGEST(), m.digest()); - - return new FlatModule(moduleName, immutable(importedModuleNames), immutable(items), att); - } - - public org.kframework.definition.FlatImport apply(Import imp) { - return org.kframework.definition.FlatImport.apply(imp.getName(), imp.isPublic(), convertAttributes(imp)); + private final org.kframework.kil.loader.Context context; + private final boolean syntactic; + private String moduleName; + private final boolean bisonLists; + + public KILtoKORE( + org.kframework.kil.loader.Context context, boolean syntactic, boolean bisonLists) { + this.context = context; + this.syntactic = syntactic; + this.bisonLists = bisonLists; + } + + public FlatModule toFlatModule(Module m) { + CheckListDecl.check(m); + CheckBracket.check(m); + moduleName = m.getName(); + + Set items = + m.getItems().stream() + .filter(j -> !(j instanceof org.kframework.kil.Import)) + .flatMap(j -> apply(j).stream()) + .collect(Collectors.toSet()); + + // temporarily declare cell sorts used in the RHS of productions until we + // can parse the configuration so Module checks don't fail + Set tempCellSorts = + items.stream() + .filter(p -> p instanceof org.kframework.definition.Production) + .map(p -> (org.kframework.definition.Production) p) + .flatMap( + p -> + stream(p.items()) + .filter(itm -> itm instanceof org.kframework.definition.NonTerminal) + .map(i -> (org.kframework.definition.NonTerminal) i) + .flatMap( + nt -> + nt.sort().name().endsWith("Cell") + || nt.sort().name().endsWith("CellFragment") + ? Stream.of( + SyntaxSort.apply( + Seq(), + nt.sort(), + Att.empty().add(Att.TEMPORARY_CELL_SORT_DECL()))) + : Stream.of())) + .collect(Collectors.toSet()); + items.addAll(tempCellSorts); + + Set importedModuleNames = + m.getItems().stream() + .filter(imp -> imp instanceof Import) + .map(imp -> apply((Import) imp)) + .collect(Collectors.toSet()); + + Att att = convertAttributes(m); + att = att.add(Att.DIGEST(), m.digest()); + + return new FlatModule(moduleName, immutable(importedModuleNames), immutable(items), att); + } + + public org.kframework.definition.FlatImport apply(Import imp) { + return org.kframework.definition.FlatImport.apply( + imp.getName(), imp.isPublic(), convertAttributes(imp)); + } + + public org.kframework.definition.Definition apply(Definition d) { + Set kilModules = + d.getItems().stream() + .filter(i -> i instanceof Module) + .map(mod -> (Module) mod) + .collect(Collectors.toSet()); + + List flatModules = + kilModules.stream() + .map(this::toFlatModule) + .sorted(Comparator.comparing(FlatModule::name)) + .collect(Collectors.toList()); + scala.collection.Set koreModules = + FlatModule.toModules(immutable(flatModules), Set()); + + return Definition( + koreModules + .find(x -> x.name().equals(d.getMainModule())) + .getOrElse( + () -> { + throw new AssertionError( + "Could not find main module name: " + + d.getMainModule() + + " when loading from front-end classes."); + }), + koreModules, + Att()); + } + + @SuppressWarnings("unchecked") + public Set apply(ModuleItem i) { + if (i instanceof Syntax || i instanceof PriorityExtended) { + return (Set) apply((ASTNode) i); + } else { + return Sets.newHashSet((org.kframework.definition.Sentence) apply((ASTNode) i)); } + } - public org.kframework.definition.Definition apply(Definition d) { - Set kilModules = d.getItems().stream().filter(i -> i instanceof Module) - .map(mod -> (Module) mod).collect(Collectors.toSet()); + public org.kframework.definition.Sentence apply(SortSynonym synonym) { + return new org.kframework.definition.SortSynonym( + synonym.newSort, synonym.oldSort, convertAttributes(synonym)); + } - List flatModules = kilModules.stream().map(this::toFlatModule).sorted(Comparator.comparing(FlatModule::name)).collect(Collectors.toList()); - scala.collection.Set koreModules = FlatModule.toModules(immutable(flatModules), Set()); + public org.kframework.definition.Sentence apply(SyntaxLexical lexical) { + return new org.kframework.definition.SyntaxLexical( + lexical.name, lexical.regex, convertAttributes(lexical)); + } - return Definition( - koreModules.find(x -> x.name().equals(d.getMainModule())) - .getOrElse(() -> { throw new AssertionError("Could not find main module name: " + d.getMainModule() + " when loading from front-end classes."); }), - koreModules, Att()); - } - - @SuppressWarnings("unchecked") - public Set apply(ModuleItem i) { - if (i instanceof Syntax || i instanceof PriorityExtended) { - return (Set) apply((ASTNode) i); - } else { - return Sets.newHashSet((org.kframework.definition.Sentence) apply((ASTNode) i)); - } - } - - public org.kframework.definition.Sentence apply(SortSynonym synonym) { - return new org.kframework.definition.SortSynonym(synonym.newSort, synonym.oldSort, convertAttributes(synonym)); - } - - public org.kframework.definition.Sentence apply(SyntaxLexical lexical) { - return new org.kframework.definition.SyntaxLexical(lexical.name, lexical.regex, convertAttributes(lexical)); - } - - - public org.kframework.definition.Bubble apply(StringSentence sentence) { - org.kframework.attributes.Att attrs = - convertAttributes(sentence) + public org.kframework.definition.Bubble apply(StringSentence sentence) { + org.kframework.attributes.Att attrs = + convertAttributes(sentence) .add(Att.CONTENT_START_LINE(), sentence.getContentStartLine()) .add(Att.CONTENT_START_COLUMN(), sentence.getContentStartColumn()); - String label = sentence.getLabel(); - if (!label.isEmpty()) { - attrs = attrs.add(Att.LABEL(), sentence.getType().equals(Att.ALIAS().key()) ? label : moduleName + "." + label); - } - - return Bubble(sentence.getType(), sentence.getContent(), attrs); - } - - public org.kframework.definition.SyntaxAssociativity apply(PriorityExtendedAssoc ii) { - scala.collection.Set tags = toTags(ii.getTags(), ii); - String assocOrig = ii.getAssoc(); - Associativity assoc = applyAssoc(assocOrig); - return SyntaxAssociativity(assoc, tags, convertAttributes(ii)); - } - - public Associativity applyAssoc(String assocOrig) { - // "left", "right", "non-assoc" - return switch (assocOrig) { - case "left" -> Associativity.Left; - case "right" -> Associativity.Right; - case "non-assoc" -> Associativity.NonAssoc; - default -> throw new AssertionError("Incorrect assoc string: " + assocOrig); - }; + String label = sentence.getLabel(); + if (!label.isEmpty()) { + attrs = + attrs.add( + Att.LABEL(), + sentence.getType().equals(Att.ALIAS().key()) ? label : moduleName + "." + label); } - public Set apply(PriorityExtended pe) { - Seq> seqOfSetOfTags = immutable(pe.getPriorityBlocks() - .stream().map(block -> toTags(block.getProductions(), pe)) + return Bubble(sentence.getType(), sentence.getContent(), attrs); + } + + public org.kframework.definition.SyntaxAssociativity apply(PriorityExtendedAssoc ii) { + scala.collection.Set tags = toTags(ii.getTags(), ii); + String assocOrig = ii.getAssoc(); + Associativity assoc = applyAssoc(assocOrig); + return SyntaxAssociativity(assoc, tags, convertAttributes(ii)); + } + + public Associativity applyAssoc(String assocOrig) { + // "left", "right", "non-assoc" + return switch (assocOrig) { + case "left" -> Associativity.Left; + case "right" -> Associativity.Right; + case "non-assoc" -> Associativity.NonAssoc; + default -> throw new AssertionError("Incorrect assoc string: " + assocOrig); + }; + } + + public Set apply(PriorityExtended pe) { + Seq> seqOfSetOfTags = + immutable( + pe.getPriorityBlocks().stream() + .map(block -> toTags(block.getProductions(), pe)) .collect(Collectors.toList())); - return Sets.newHashSet(SyntaxPriority(seqOfSetOfTags)); + return Sets.newHashSet(SyntaxPriority(seqOfSetOfTags)); + } + + public scala.collection.Set toTags(List labels, ASTNode loc) { + return immutable( + labels.stream() + .flatMap( + l -> { + java.util.Set productions = context.tags.get(l.name()); + if (productions.isEmpty()) + throw KEMException.outerParserError( + "Could not find any productions for tag: " + l.name(), + loc.getSource(), + loc.getLocation()); + return productions.stream() + .map( + p -> { + String label = p.getKLabel(true); + if (label == null && p.getAttributes().contains(Att.BRACKET())) { + label = p.getBracketLabel(true); + } + return Tag(label); + }); + }) + .collect(Collectors.toSet())); + } + + public Set apply(Syntax s) { + Set res = new HashSet<>(); + + org.kframework.kore.Sort sort = s.getDeclaredSort().getSort(); + + // just a sort declaration + if (s.getPriorityBlocks().size() == 0) { + res.add(SyntaxSort(immutable(s.getParams()), sort, convertAttributes(s))); + return res; } - public scala.collection.Set toTags(List labels, ASTNode loc) { - return immutable(labels.stream().flatMap(l -> { - java.util.Set productions = context.tags.get(l.name()); - if (productions.isEmpty()) - throw KEMException.outerParserError("Could not find any productions for tag: " + l.name(), loc.getSource(), loc.getLocation()); - return productions.stream().map(p -> { - String label = p.getKLabel(true); - if (label == null && p.getAttributes().contains(Att.BRACKET())) { - label = p.getBracketLabel(true); - } - return Tag(label); - }); - }).collect(Collectors.toSet())); + Function> applyToTags = + (PriorityBlock b) -> + immutable( + Stream.concat( + b.getProductions().stream() + .filter(p -> p.getKLabel(true) != null) + .map(p -> Tag(p.getKLabel(true))), + b.getProductions().stream() + .filter(p -> p.containsAttribute(Att.BRACKET())) + .map(p -> Tag(p.getBracketLabel(true)))) + .collect(Collectors.toSet())); + + if (s.getPriorityBlocks().size() > 1) { + res.add( + SyntaxPriority( + immutable( + s.getPriorityBlocks().stream().map(applyToTags).collect(Collectors.toList())))); } - public Set apply(Syntax s) { - Set res = new HashSet<>(); - - org.kframework.kore.Sort sort = s.getDeclaredSort().getSort(); - - // just a sort declaration - if (s.getPriorityBlocks().size() == 0) { - res.add(SyntaxSort(immutable(s.getParams()), sort, convertAttributes(s))); - return res; - } - - Function> applyToTags = (PriorityBlock b) -> immutable(Stream.concat(b - .getProductions().stream().filter(p -> p.getKLabel(true) != null).map(p -> Tag(p.getKLabel(true))), - b.getProductions().stream().filter(p -> p.containsAttribute(Att.BRACKET())).map(p -> Tag(p.getBracketLabel(true)))) - .collect(Collectors.toSet())); - - if (s.getPriorityBlocks().size() > 1) { - res.add(SyntaxPriority(immutable(s.getPriorityBlocks().stream().map(applyToTags) - .collect(Collectors.toList())))); - } - - // there are some productions - for (PriorityBlock b : s.getPriorityBlocks()) { - if (!b.getAssoc().equals("")) { - Associativity assoc = applyAssoc(b.getAssoc()); - res.add(SyntaxAssociativity(assoc, applyToTags.apply(b))); - } - - for (Production p : b.getProductions()) { - if (p.containsAttribute(Att.REJECT())) // skip productions of the old reject type - continue; - // Handle a special case first: List productions have only - // one item. - if (p.getItems().size() == 1 && p.getItems().get(0) instanceof UserList) { - applyUserList(res, sort, p, (UserList) p.getItems().get(0)); - } else { - List items = new ArrayList<>(); - for (org.kframework.kil.ProductionItem it : p.getItems()) { - if (it instanceof NonTerminal nt) { - items.add(NonTerminal(nt.getSort(), nt.getName())); - } else if (it instanceof UserList) { - throw new AssertionError("Lists should have applied before."); - } else if (it instanceof Lexical) { - String regex; - if (p.containsAttribute(Att.REGEX())) - regex = p.getAttribute(Att.REGEX()); - else - regex = ((Lexical) it).getLexicalRule(); - RegexTerminal regexTerminal = getRegexTerminal(regex); - - items.add(regexTerminal); - } else if (it instanceof Terminal) { - items.add(Terminal(((Terminal) it).getTerminal())); - } else { - throw new AssertionError("Unhandled case"); - } - } - - org.kframework.attributes.Att attrs = convertAttributes(p); - if (attrs.contains(Att.BRACKET())) { - attrs = attrs.add(Att.BRACKET_LABEL(), KLabel.class, KLabel(p.getBracketLabel(true), immutable(p.getParams()))); - } - - org.kframework.definition.Production prod; - if (p.getKLabel(true) == null) - prod = Production( - immutable(p.getParams()), - sort, - immutable(items), - attrs); - else - prod = Production( - KLabel(p.getKLabel(true), immutable(p.getParams())), - sort, - immutable(items), - attrs); - - res.add(prod); - // handle associativity for the production - if (p.containsAttribute(Att.LEFT())) - res.add(SyntaxAssociativity(applyAssoc("left"), Set(Tag(p.getKLabel(true))))); - else if (p.containsAttribute(Att.RIGHT())) - res.add(SyntaxAssociativity(applyAssoc("right"), Set(Tag(p.getKLabel(true))))); - else if (p.containsAttribute(Att.NON_ASSOC())) - res.add(SyntaxAssociativity(applyAssoc("non-assoc"), Set(Tag(p.getKLabel(true))))); - } + // there are some productions + for (PriorityBlock b : s.getPriorityBlocks()) { + if (!b.getAssoc().equals("")) { + Associativity assoc = applyAssoc(b.getAssoc()); + res.add(SyntaxAssociativity(assoc, applyToTags.apply(b))); + } + + for (Production p : b.getProductions()) { + if (p.containsAttribute(Att.REJECT())) // skip productions of the old reject type + continue; + // Handle a special case first: List productions have only + // one item. + if (p.getItems().size() == 1 && p.getItems().get(0) instanceof UserList) { + applyUserList(res, sort, p, (UserList) p.getItems().get(0)); + } else { + List items = new ArrayList<>(); + for (org.kframework.kil.ProductionItem it : p.getItems()) { + if (it instanceof NonTerminal nt) { + items.add(NonTerminal(nt.getSort(), nt.getName())); + } else if (it instanceof UserList) { + throw new AssertionError("Lists should have applied before."); + } else if (it instanceof Lexical) { + String regex; + if (p.containsAttribute(Att.REGEX())) regex = p.getAttribute(Att.REGEX()); + else regex = ((Lexical) it).getLexicalRule(); + RegexTerminal regexTerminal = getRegexTerminal(regex); + + items.add(regexTerminal); + } else if (it instanceof Terminal) { + items.add(Terminal(((Terminal) it).getTerminal())); + } else { + throw new AssertionError("Unhandled case"); } + } + + org.kframework.attributes.Att attrs = convertAttributes(p); + if (attrs.contains(Att.BRACKET())) { + attrs = + attrs.add( + Att.BRACKET_LABEL(), + KLabel.class, + KLabel(p.getBracketLabel(true), immutable(p.getParams()))); + } + + org.kframework.definition.Production prod; + if (p.getKLabel(true) == null) + prod = Production(immutable(p.getParams()), sort, immutable(items), attrs); + else + prod = + Production( + KLabel(p.getKLabel(true), immutable(p.getParams())), + sort, + immutable(items), + attrs); + + res.add(prod); + // handle associativity for the production + if (p.containsAttribute(Att.LEFT())) + res.add(SyntaxAssociativity(applyAssoc("left"), Set(Tag(p.getKLabel(true))))); + else if (p.containsAttribute(Att.RIGHT())) + res.add(SyntaxAssociativity(applyAssoc("right"), Set(Tag(p.getKLabel(true))))); + else if (p.containsAttribute(Att.NON_ASSOC())) + res.add(SyntaxAssociativity(applyAssoc("non-assoc"), Set(Tag(p.getKLabel(true))))); } - return res; + } } - - public static RegexTerminal getRegexTerminal(String regex) { - String precede = "#"; - if (regex.startsWith("(? 0 && regex.charAt(followIndex - 1) == '\\')) { - follow = regex.substring(followIndex + "(?!".length(), regex.length() - 1); - regex = regex.substring(0, followIndex); - } + if (regex.charAt(i) == '(') depth++; + if (regex.charAt(i) == ')') depth--; + if (depth == 0) { + precede = regex.substring("(? res, - org.kframework.kore.Sort sort, Production p, UserList userList) { - - // Transform list declarations of the form Es ::= List{E, ","} into something representable in kore - org.kframework.kore.Sort elementSort = userList.getSort(); - - org.kframework.attributes.Att attrs = convertAttributes(p).add(Att.USER_LIST(), userList.getListType()); - String kilProductionId = "" + System.identityHashCode(p); - org.kframework.definition.Production prod1, prod3; - - - if (bisonLists) { - // Es ::= Es "," E - prod1 = Production(KLabel(p.getKLabel(true), immutable(p.getParams())), sort, - Seq(NonTerminal(sort), Terminal(userList.getSeparator()), NonTerminal(elementSort)), attrs); - } else { - // Es ::= E "," Es - prod1 = Production(KLabel(p.getKLabel(true), immutable(p.getParams())), sort, - Seq(NonTerminal(elementSort), Terminal(userList.getSeparator()), NonTerminal(sort)), attrs); - } - - - // Es ::= ".Es" - prod3 = Production(KLabel(p.getTerminatorKLabel(true), immutable(p.getParams())), sort, Seq(Terminal("." + sort.toString())), - attrs.remove(Att.FORMAT()).remove(Att.STRICT()).add(Att.KLABEL(), p.getTerminatorKLabel(false))); - - res.add(prod1); - res.add(prod3); - } - - public static org.kframework.attributes.Att convertAttributes(ASTNode t) { - Att attributes = ProcessGroupAttributes.getProcessedAtt(t.getAttributes(), t); - - return attributes - .addAll(attributesFromLocation(t.getLocation())) - .addAll(attributesFromSource(t.getSource())); + String follow = "#"; + int followIndex = regex.lastIndexOf("(?!"); + if (followIndex != -1 && regex.endsWith(")")) { // find the follow pattern at the end: (?!X) + if (!(followIndex > 0 && regex.charAt(followIndex - 1) == '\\')) { + follow = regex.substring(followIndex + "(?!".length(), regex.length() - 1); + regex = regex.substring(0, followIndex); + } } - - private static Att attributesFromSource(Source source) { - if (source != null) { - return Att().add(Source.class, source); - } - return Att(); + return RegexTerminal(precede, regex, follow); + } + + public void applyUserList( + Set res, + org.kframework.kore.Sort sort, + Production p, + UserList userList) { + + // Transform list declarations of the form Es ::= List{E, ","} into something representable in + // kore + org.kframework.kore.Sort elementSort = userList.getSort(); + + org.kframework.attributes.Att attrs = + convertAttributes(p).add(Att.USER_LIST(), userList.getListType()); + String kilProductionId = "" + System.identityHashCode(p); + org.kframework.definition.Production prod1, prod3; + + if (bisonLists) { + // Es ::= Es "," E + prod1 = + Production( + KLabel(p.getKLabel(true), immutable(p.getParams())), + sort, + Seq(NonTerminal(sort), Terminal(userList.getSeparator()), NonTerminal(elementSort)), + attrs); + } else { + // Es ::= E "," Es + prod1 = + Production( + KLabel(p.getKLabel(true), immutable(p.getParams())), + sort, + Seq(NonTerminal(elementSort), Terminal(userList.getSeparator()), NonTerminal(sort)), + attrs); } - private static org.kframework.attributes.Att attributesFromLocation(Location location) { - if (location != null) { - return Att().add(Location.class, location); - } else - return Att(); + // Es ::= ".Es" + prod3 = + Production( + KLabel(p.getTerminatorKLabel(true), immutable(p.getParams())), + sort, + Seq(Terminal("." + sort.toString())), + attrs + .remove(Att.FORMAT()) + .remove(Att.STRICT()) + .add(Att.KLABEL(), p.getTerminatorKLabel(false))); + + res.add(prod1); + res.add(prod3); + } + + public static org.kframework.attributes.Att convertAttributes(ASTNode t) { + Att attributes = ProcessGroupAttributes.getProcessedAtt(t.getAttributes(), t); + + return attributes + .addAll(attributesFromLocation(t.getLocation())) + .addAll(attributesFromSource(t.getSource())); + } + + private static Att attributesFromSource(Source source) { + if (source != null) { + return Att().add(Source.class, source); } - + return Att(); + } + + private static org.kframework.attributes.Att attributesFromLocation(Location location) { + if (location != null) { + return Att().add(Location.class, location); + } else return Att(); + } } diff --git a/kernel/src/main/java/org/kframework/kprove/KProve.java b/kernel/src/main/java/org/kframework/kprove/KProve.java index 528b4ef23cb..20cc944a84b 100644 --- a/kernel/src/main/java/org/kframework/kprove/KProve.java +++ b/kernel/src/main/java/org/kframework/kprove/KProve.java @@ -1,7 +1,13 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.kprove; +import static org.kframework.Collections.*; + import com.google.inject.Inject; +import java.nio.charset.StandardCharsets; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; import org.kframework.RewriterResult; import org.kframework.definition.Definition; import org.kframework.definition.Module; @@ -9,7 +15,6 @@ import org.kframework.rewriter.Rewriter; import org.kframework.unparser.KPrint; import org.kframework.unparser.ToJson; -import org.kframework.utils.BinaryLoader; import org.kframework.utils.Stopwatch; import org.kframework.utils.errorsystem.KEMException; import org.kframework.utils.errorsystem.KException; @@ -17,72 +22,82 @@ import org.kframework.utils.file.FileUtil; import scala.Tuple2; -import java.io.UnsupportedEncodingException; -import java.nio.charset.StandardCharsets; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; - -import static org.kframework.Collections.*; - public record KProve( - KExceptionManager kem, - FileUtil files, - KPrint kprint, - KProveOptions kproveOptions, - CompiledDefinition compiledDefinition, - ProofDefinitionBuilder proofDefinitionBuilder, - Function rewriterGenerator, - Stopwatch sw -) { - private static final int KPROVE_SUCCESS_EXIT_CODE = 0; - private static final int KPROVE_MISMATCH_CONFIG_CODE = 1; - - @Inject - public KProve {} + KExceptionManager kem, + FileUtil files, + KPrint kprint, + KProveOptions kproveOptions, + CompiledDefinition compiledDefinition, + ProofDefinitionBuilder proofDefinitionBuilder, + Function rewriterGenerator, + Stopwatch sw) { + private static final int KPROVE_SUCCESS_EXIT_CODE = 0; + private static final int KPROVE_MISMATCH_CONFIG_CODE = 1; - public int run() { - if (!kproveOptions.specFile(files).exists()) { - throw KEMException.criticalError("Definition file doesn't exist: " + - kproveOptions.specFile(files).getAbsolutePath()); - } + @Inject + public KProve {} - Tuple2 compiled = proofDefinitionBuilder - .build(kproveOptions.specFile(files), kproveOptions.specModule, compiledDefinition.kompileOptions.readOnlyKompiledDirectory); + public int run() { + if (!kproveOptions.specFile(files).exists()) { + throw KEMException.criticalError( + "Definition file doesn't exist: " + kproveOptions.specFile(files).getAbsolutePath()); + } - Rewriter rewriter = rewriterGenerator.apply(compiled._1()); - Module specModule = compiled._2(); + Tuple2 compiled = + proofDefinitionBuilder.build( + kproveOptions.specFile(files), + kproveOptions.specModule, + compiledDefinition.kompileOptions.readOnlyKompiledDirectory); - if (kproveOptions.emitJson) { - files.saveToKompiled("prove-definition.json", new String(ToJson.apply(compiled._1()), StandardCharsets.UTF_8)); - } + Rewriter rewriter = rewriterGenerator.apply(compiled._1()); + Module specModule = compiled._2(); - if (kproveOptions.emitJsonSpec != null) { - Set names = stream(compiled._1().modules()).map(Module::name).collect(Collectors.toSet()); - Set specMods = stream(specModule.importedModules()).filter(m -> !names.contains(m.name())).collect(Collectors.toSet()); - specMods.add(specModule); - files.saveToWorkingDirectory(kproveOptions.emitJsonSpec, ToJson.apply(specMods, specModule.name())); - } + if (kproveOptions.emitJson) { + files.saveToKompiled( + "prove-definition.json", new String(ToJson.apply(compiled._1()), StandardCharsets.UTF_8)); + } - RewriterResult results = rewriter.prove(specModule, true); - sw.printIntermediate("Backend"); - kprint.prettyPrint(compiled._1(), compiled._1().getModule("LANGUAGE-PARSING").get(), kprint::outputFile, - results.k()); - sw.printTotal("Total"); + if (kproveOptions.emitJsonSpec != null) { + Set names = + stream(compiled._1().modules()).map(Module::name).collect(Collectors.toSet()); + Set specMods = + stream(specModule.importedModules()) + .filter(m -> !names.contains(m.name())) + .collect(Collectors.toSet()); + specMods.add(specModule); + files.saveToWorkingDirectory( + kproveOptions.emitJsonSpec, ToJson.apply(specMods, specModule.name())); + } - int errCode = results.exitCode().orElse(0); - switch (errCode) { - case KPROVE_SUCCESS_EXIT_CODE -> {} - case KPROVE_MISMATCH_CONFIG_CODE -> { - kem.addKException(new KException(KException.ExceptionType.ERROR, KException.KExceptionGroup.PROVER, - "backend terminated because the configuration cannot be rewritten further. See output for more details.")); - } - default -> { - kem.addKException(new KException(KException.ExceptionType.ERROR, KException.KExceptionGroup.PROVER, - "backend crashed with exit code " + errCode)); - } - } + RewriterResult results = rewriter.prove(specModule, true); + sw.printIntermediate("Backend"); + kprint.prettyPrint( + compiled._1(), + compiled._1().getModule("LANGUAGE-PARSING").get(), + kprint::outputFile, + results.k()); + sw.printTotal("Total"); - return results.exitCode().orElse(KEMException.TERMINATED_WITH_ERRORS_EXIT_CODE); + int errCode = results.exitCode().orElse(0); + switch (errCode) { + case KPROVE_SUCCESS_EXIT_CODE -> {} + case KPROVE_MISMATCH_CONFIG_CODE -> { + kem.addKException( + new KException( + KException.ExceptionType.ERROR, + KException.KExceptionGroup.PROVER, + "backend terminated because the configuration cannot be rewritten further. See" + + " output for more details.")); + } + default -> { + kem.addKException( + new KException( + KException.ExceptionType.ERROR, + KException.KExceptionGroup.PROVER, + "backend crashed with exit code " + errCode)); + } } + + return results.exitCode().orElse(KEMException.TERMINATED_WITH_ERRORS_EXIT_CODE); + } } diff --git a/kernel/src/main/java/org/kframework/kprove/KProveFrontEnd.java b/kernel/src/main/java/org/kframework/kprove/KProveFrontEnd.java index 7b9c291bfe3..4350d6f78c1 100644 --- a/kernel/src/main/java/org/kframework/kprove/KProveFrontEnd.java +++ b/kernel/src/main/java/org/kframework/kprove/KProveFrontEnd.java @@ -5,6 +5,8 @@ import com.google.inject.Inject; import com.google.inject.Module; import com.google.inject.Provider; +import java.io.File; +import java.util.List; import org.kframework.main.FrontEnd; import org.kframework.main.GlobalOptions; import org.kframework.utils.errorsystem.KExceptionManager; @@ -16,48 +18,43 @@ import org.kframework.utils.inject.DefinitionScope; import org.kframework.utils.inject.JCommanderModule; -import java.io.File; -import java.util.List; - public class KProveFrontEnd extends FrontEnd { - - public static List getModules() { - return ImmutableList.of( - new KProveModule(), - new CommonModule(), - new JCommanderModule(), - new DefinitionLoadingModule()); - } - - - private final DefinitionScope scope; - private final Provider kompiledDir; - private final Provider kprove; - - @Inject - KProveFrontEnd( - GlobalOptions options, - @JCommanderModule.Usage String usage, - JarInfo jarInfo, - DefinitionScope scope, - @KompiledDir Provider kompiledDir, - KExceptionManager kem, - Provider files, - Provider kprove) { - super(kem, options, usage, jarInfo, files); - this.scope = scope; - this.kompiledDir = kompiledDir; - this.kprove = kprove; - } - - @Override - protected int run() { - scope.enter(kompiledDir.get()); - try { - return kprove.get().run(); - } finally { - scope.exit(); - } + public static List getModules() { + return ImmutableList.of( + new KProveModule(), + new CommonModule(), + new JCommanderModule(), + new DefinitionLoadingModule()); + } + + private final DefinitionScope scope; + private final Provider kompiledDir; + private final Provider kprove; + + @Inject + KProveFrontEnd( + GlobalOptions options, + @JCommanderModule.Usage String usage, + JarInfo jarInfo, + DefinitionScope scope, + @KompiledDir Provider kompiledDir, + KExceptionManager kem, + Provider files, + Provider kprove) { + super(kem, options, usage, jarInfo, files); + this.scope = scope; + this.kompiledDir = kompiledDir; + this.kprove = kprove; + } + + @Override + protected int run() { + scope.enter(kompiledDir.get()); + try { + return kprove.get().run(); + } finally { + scope.exit(); } + } } diff --git a/kernel/src/main/java/org/kframework/kprove/KProveModule.java b/kernel/src/main/java/org/kframework/kprove/KProveModule.java index ca375b76ad7..487f9b8ccb1 100644 --- a/kernel/src/main/java/org/kframework/kprove/KProveModule.java +++ b/kernel/src/main/java/org/kframework/kprove/KProveModule.java @@ -18,47 +18,56 @@ import org.kframework.utils.options.SMTOptions; public class KProveModule extends AbstractModule { - @Override - protected void configure() { - binder().requireAtInjectOnConstructors(); - bind(FrontEnd.class).to(KProveFrontEnd.class); - bind(Tool.class).toInstance(Tool.KPROVE); + @Override + protected void configure() { + binder().requireAtInjectOnConstructors(); + bind(FrontEnd.class).to(KProveFrontEnd.class); + bind(Tool.class).toInstance(Tool.KPROVE); - install(new BackendModule()); - install(new RewriterModule()); + install(new BackendModule()); + install(new RewriterModule()); - Multibinder optionsBinder = Multibinder.newSetBinder(binder(), Object.class, Options.class); - optionsBinder.addBinding().to(KProveOptions.class); - } + Multibinder optionsBinder = + Multibinder.newSetBinder(binder(), Object.class, Options.class); + optionsBinder.addBinding().to(KProveOptions.class); + } - @Provides @RequestScoped - GlobalOptions globalOptions(KProveOptions options) { - return options.getGlobalOptions_useOnlyInGuiceProvider(); - } + @Provides + @RequestScoped + GlobalOptions globalOptions(KProveOptions options) { + return options.getGlobalOptions_useOnlyInGuiceProvider(); + } - @Provides @RequestScoped - OuterParsingOptions outerParsingOptions(KProveOptions options) { return options.outerParsing; } + @Provides + @RequestScoped + OuterParsingOptions outerParsingOptions(KProveOptions options) { + return options.outerParsing; + } - @Provides @RequestScoped - InnerParsingOptions InnerParsingOptions(KProveOptions options) { return options.innerParsing; } + @Provides + @RequestScoped + InnerParsingOptions InnerParsingOptions(KProveOptions options) { + return options.innerParsing; + } - @Provides @RequestScoped - PrintOptions printOptions(KProveOptions options) { - return options.print; - } + @Provides + @RequestScoped + PrintOptions printOptions(KProveOptions options) { + return options.print; + } - @Provides - DefinitionLoadingOptions loadingOptions(KProveOptions options) { - return options.definitionLoading; - } + @Provides + DefinitionLoadingOptions loadingOptions(KProveOptions options) { + return options.definitionLoading; + } - @Provides - BackendOptions backendOptions(KProveOptions options) { - return options.backend; - } + @Provides + BackendOptions backendOptions(KProveOptions options) { + return options.backend; + } - @Provides - SMTOptions smtOptions(KProveOptions options) { - return options.smt; - } + @Provides + SMTOptions smtOptions(KProveOptions options) { + return options.smt; + } } diff --git a/kernel/src/main/java/org/kframework/kprove/KProveOptions.java b/kernel/src/main/java/org/kframework/kprove/KProveOptions.java index d25a5b6b6c3..57921f54583 100644 --- a/kernel/src/main/java/org/kframework/kprove/KProveOptions.java +++ b/kernel/src/main/java/org/kframework/kprove/KProveOptions.java @@ -5,8 +5,10 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.ParametersDelegate; import com.google.inject.Inject; -import org.kframework.unparser.PrintOptions; +import java.io.File; +import java.util.List; import org.kframework.main.GlobalOptions; +import org.kframework.unparser.PrintOptions; import org.kframework.utils.file.FileUtil; import org.kframework.utils.inject.RequestScoped; import org.kframework.utils.options.BackendOptions; @@ -15,83 +17,96 @@ import org.kframework.utils.options.OuterParsingOptions; import org.kframework.utils.options.SMTOptions; -import java.io.File; -import java.util.Collections; -import java.util.List; - @RequestScoped public class KProveOptions { - @Inject - public KProveOptions() {} - - @ParametersDelegate - private final transient GlobalOptions global = new GlobalOptions(); - - /** - * Use only in the Guice Provider method, so it can replace the GlobalOptions from kompile. - */ - public GlobalOptions getGlobalOptions_useOnlyInGuiceProvider() { - return global; - } - - @ParametersDelegate - public DefinitionLoadingOptions definitionLoading = new DefinitionLoadingOptions(); - - @ParametersDelegate - public OuterParsingOptions outerParsing = new OuterParsingOptions(); - - @ParametersDelegate - public InnerParsingOptions innerParsing = new InnerParsingOptions(); - - private File specFile; - - public synchronized File specFile(FileUtil files) { - return outerParsing.mainDefinitionFile(files); - } - - @ParametersDelegate - public BackendOptions backend = new BackendOptions(); - - @ParametersDelegate - public SMTOptions smt = new SMTOptions(); - - @ParametersDelegate - public PrintOptions print = new PrintOptions(); - - @Parameter(names="--branching-allowed", descriptionKey = "number", arity = 1, - description="Number of branching events allowed before a forcible stop.") - public int branchingAllowed = Integer.MAX_VALUE; - - @Parameter(names={"--spec-module", "-sm"}, descriptionKey = "name", - description="Name of module containing specification to prove") - public String specModule; - - @Parameter(names="--depth", descriptionKey = "number", - description="The maximum number of computational steps to prove") - public Integer depth; - - @Parameter(names="--trusted", descriptionKey = "labels", - description="Mark this comma separated list of claims as [trusted]") - public List trusted = null; - - @Parameter(names="--exclude", descriptionKey = "labels", description="Exclude this comma separated list of claims") - public List exclude = null; - - @Parameter(names="--claims", descriptionKey = "labels", description="Only keep this comma separated list of claims") - public List claims = null; - - @Parameter(names="--debugger", - description="Launch proof in an interactive debugger. Currently only supported by the Haskell backend.") - public boolean debugger; - - @Parameter(names="--debug-script", descriptionKey = "file", - description="Run script passed in specified file when the debugger starts. Used with --debugger.") - public String debugScript; - - @Parameter(names="--emit-json", description="Emit JSON serialized main definition for proving.") - public boolean emitJson = false; - - @Parameter(names="--emit-json-spec", descriptionKey = "file", - description="If set, emit the JSON serialization of the spec module to the specified file.") - public String emitJsonSpec = null; + @Inject + public KProveOptions() {} + + @ParametersDelegate private final transient GlobalOptions global = new GlobalOptions(); + + /** Use only in the Guice Provider method, so it can replace the GlobalOptions from kompile. */ + public GlobalOptions getGlobalOptions_useOnlyInGuiceProvider() { + return global; + } + + @ParametersDelegate + public DefinitionLoadingOptions definitionLoading = new DefinitionLoadingOptions(); + + @ParametersDelegate public OuterParsingOptions outerParsing = new OuterParsingOptions(); + + @ParametersDelegate public InnerParsingOptions innerParsing = new InnerParsingOptions(); + + private File specFile; + + public synchronized File specFile(FileUtil files) { + return outerParsing.mainDefinitionFile(files); + } + + @ParametersDelegate public BackendOptions backend = new BackendOptions(); + + @ParametersDelegate public SMTOptions smt = new SMTOptions(); + + @ParametersDelegate public PrintOptions print = new PrintOptions(); + + @Parameter( + names = "--branching-allowed", + descriptionKey = "number", + arity = 1, + description = "Number of branching events allowed before a forcible stop.") + public int branchingAllowed = Integer.MAX_VALUE; + + @Parameter( + names = {"--spec-module", "-sm"}, + descriptionKey = "name", + description = "Name of module containing specification to prove") + public String specModule; + + @Parameter( + names = "--depth", + descriptionKey = "number", + description = "The maximum number of computational steps to prove") + public Integer depth; + + @Parameter( + names = "--trusted", + descriptionKey = "labels", + description = "Mark this comma separated list of claims as [trusted]") + public List trusted = null; + + @Parameter( + names = "--exclude", + descriptionKey = "labels", + description = "Exclude this comma separated list of claims") + public List exclude = null; + + @Parameter( + names = "--claims", + descriptionKey = "labels", + description = "Only keep this comma separated list of claims") + public List claims = null; + + @Parameter( + names = "--debugger", + description = + "Launch proof in an interactive debugger. Currently only supported by the Haskell" + + " backend.") + public boolean debugger; + + @Parameter( + names = "--debug-script", + descriptionKey = "file", + description = + "Run script passed in specified file when the debugger starts. Used with --debugger.") + public String debugScript; + + @Parameter( + names = "--emit-json", + description = "Emit JSON serialized main definition for proving.") + public boolean emitJson = false; + + @Parameter( + names = "--emit-json-spec", + descriptionKey = "file", + description = "If set, emit the JSON serialization of the spec module to the specified file.") + public String emitJsonSpec = null; } diff --git a/kernel/src/main/java/org/kframework/kprove/ProofDefinitionBuilder.java b/kernel/src/main/java/org/kframework/kprove/ProofDefinitionBuilder.java index 2ff777a4a25..7c6461849db 100644 --- a/kernel/src/main/java/org/kframework/kprove/ProofDefinitionBuilder.java +++ b/kernel/src/main/java/org/kframework/kprove/ProofDefinitionBuilder.java @@ -1,8 +1,16 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.kprove; +import static org.kframework.Collections.*; + import com.google.common.collect.Sets; import com.google.inject.Inject; +import java.io.File; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.io.FilenameUtils; import org.kframework.attributes.Att; import org.kframework.compile.Backend; @@ -16,95 +24,118 @@ import scala.Option; import scala.Tuple2; -import java.io.File; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; +public record ProofDefinitionBuilder( + CompiledDefinition compiledDefinition, + Backend backend, + Kompile kompile, + KProveOptions proveOptions, + FileUtil files, + Stopwatch sw) { -import static org.kframework.Collections.*; + @Inject + public ProofDefinitionBuilder {} -public record ProofDefinitionBuilder(CompiledDefinition compiledDefinition, - Backend backend, Kompile kompile, - KProveOptions proveOptions, - FileUtil files, Stopwatch sw) { + /** + * @param specFile File containing specification rules to prove. Not part of definition. + * @param specModuleName Module containing specifications to prove + */ + public Tuple2 build( + File specFile, String specModuleName, boolean readOnlyCache) { + String defModuleNameUpdated = compiledDefinition.kompiledDefinition.mainModule().name(); + String specModuleNameUpdated = + specModuleName == null + ? FilenameUtils.getBaseName(specFile.getName()).toUpperCase() + : specModuleName; + File absSpecFile = files.resolveWorkingDirectory(specFile).getAbsoluteFile(); - @Inject - public ProofDefinitionBuilder { - } - - /** - * @param specFile File containing specification rules to prove. Not part of definition. - * @param specModuleName Module containing specifications to prove - */ - public Tuple2 build(File specFile, String specModuleName, boolean readOnlyCache) { - String defModuleNameUpdated = compiledDefinition.kompiledDefinition.mainModule().name(); - String specModuleNameUpdated = - specModuleName == null ? FilenameUtils.getBaseName(specFile.getName()).toUpperCase() : specModuleName; - File absSpecFile = files.resolveWorkingDirectory(specFile).getAbsoluteFile(); - - Set modules = kompile.parseModules(compiledDefinition, defModuleNameUpdated, specModuleNameUpdated, absSpecFile, - backend.excludedModuleTags(), true, true); - Map modulesMap = modules.stream().collect(Collectors.toMap(Module::name, m -> m)); - Definition parsedDefinition = compiledDefinition.getParsedDefinition(); - Module specModule = getModule(specModuleNameUpdated, modulesMap, parsedDefinition); - specModule = filter(specModule); - kompile.proverChecksX(specModule, modulesMap.get(defModuleNameUpdated)); - kompile.structuralChecks(immutable(modules), specModule, Option.empty(), backend.excludedModuleTags()); - specModule = backend.specificationSteps(compiledDefinition.kompiledDefinition).apply(specModule); - sw.printIntermediate("Apply prover steps"); - return Tuple2.apply(compiledDefinition.kompiledDefinition, specModule); - } + Set modules = + kompile.parseModules( + compiledDefinition, + defModuleNameUpdated, + specModuleNameUpdated, + absSpecFile, + backend.excludedModuleTags(), + true, + true); + Map modulesMap = + modules.stream().collect(Collectors.toMap(Module::name, m -> m)); + Definition parsedDefinition = compiledDefinition.getParsedDefinition(); + Module specModule = getModule(specModuleNameUpdated, modulesMap, parsedDefinition); + specModule = filter(specModule); + kompile.proverChecksX(specModule, modulesMap.get(defModuleNameUpdated)); + kompile.structuralChecks( + immutable(modules), specModule, Option.empty(), backend.excludedModuleTags()); + specModule = + backend.specificationSteps(compiledDefinition.kompiledDefinition).apply(specModule); + sw.printIntermediate("Apply prover steps"); + return Tuple2.apply(compiledDefinition.kompiledDefinition, specModule); + } - private static Module getModule(String defModule, Map modules, Definition parsedDefinition) { - if (modules.containsKey(defModule)) - return modules.get(defModule); - Option mod = parsedDefinition.getModule(defModule); - if (mod.isDefined()) { - return mod.get(); - } - throw KEMException.criticalError("Module " + defModule + " does not exist."); + private static Module getModule( + String defModule, Map modules, Definition parsedDefinition) { + if (modules.containsKey(defModule)) return modules.get(defModule); + Option mod = parsedDefinition.getModule(defModule); + if (mod.isDefined()) { + return mod.get(); } + throw KEMException.criticalError("Module " + defModule + " does not exist."); + } - // filter claims according the command line options - private Module filter(Module specModule) { - if (proveOptions.trusted != null || proveOptions.exclude != null || proveOptions.claims != null) { - Set unused = new HashSet<>(); - if (proveOptions.trusted != null) unused.addAll(proveOptions.trusted); - if (proveOptions.exclude != null) unused.addAll(proveOptions.exclude); - if (proveOptions.claims != null) unused.addAll(proveOptions.claims); - if (proveOptions.exclude != null && proveOptions.claims != null) { - Sets.SetView intersection = Sets.intersection(new HashSet<>(proveOptions.exclude), new HashSet<>(proveOptions.claims)); - if (intersection.size() != 0) - throw KEMException.criticalError("Labels used for both --exclude and --claims: " + intersection); - } - specModule = new ModuleTransformer((m -> { - Set filtered = stream(m.localSentences()).flatMap(s -> { - if (s instanceof Claim c && s.att().getOptional(Att.LABEL()).isPresent()) { - String label = s.att().getOptional(Att.LABEL()).get(); - if (proveOptions.trusted != null && proveOptions.trusted.contains(label)) { - s = c.newInstance(c.body(), c.requires(), c.ensures(), c.att().add(Att.TRUSTED())); - unused.remove(label); - } - if (proveOptions.exclude != null && proveOptions.exclude.contains(label)) { - unused.remove(label); - return Stream.empty(); - } - if (proveOptions.claims != null) - if (proveOptions.claims.contains(label)) - unused.remove(label); - else - return Stream.empty(); - } - return Stream.of(s); - }).collect(Collectors.toSet()); + // filter claims according the command line options + private Module filter(Module specModule) { + if (proveOptions.trusted != null + || proveOptions.exclude != null + || proveOptions.claims != null) { + Set unused = new HashSet<>(); + if (proveOptions.trusted != null) unused.addAll(proveOptions.trusted); + if (proveOptions.exclude != null) unused.addAll(proveOptions.exclude); + if (proveOptions.claims != null) unused.addAll(proveOptions.claims); + if (proveOptions.exclude != null && proveOptions.claims != null) { + Sets.SetView intersection = + Sets.intersection( + new HashSet<>(proveOptions.exclude), new HashSet<>(proveOptions.claims)); + if (intersection.size() != 0) + throw KEMException.criticalError( + "Labels used for both --exclude and --claims: " + intersection); + } + specModule = + new ModuleTransformer( + (m -> { + Set filtered = + stream(m.localSentences()) + .flatMap( + s -> { + if (s instanceof Claim c + && s.att().getOptional(Att.LABEL()).isPresent()) { + String label = s.att().getOptional(Att.LABEL()).get(); + if (proveOptions.trusted != null + && proveOptions.trusted.contains(label)) { + s = + c.newInstance( + c.body(), + c.requires(), + c.ensures(), + c.att().add(Att.TRUSTED())); + unused.remove(label); + } + if (proveOptions.exclude != null + && proveOptions.exclude.contains(label)) { + unused.remove(label); + return Stream.empty(); + } + if (proveOptions.claims != null) + if (proveOptions.claims.contains(label)) unused.remove(label); + else return Stream.empty(); + } + return Stream.of(s); + }) + .collect(Collectors.toSet()); return Module.apply(m.name(), m.imports(), immutable(filtered), m.att()); - }), "Filter claims") { - }.apply(specModule); - if (unused.size() != 0) - throw KEMException.criticalError("Unused filtering labels: " + unused); - } - return specModule; + }), + "Filter claims") {}.apply(specModule); + if (unused.size() != 0) + throw KEMException.criticalError("Unused filtering labels: " + unused); } + return specModule; + } } diff --git a/kernel/src/main/java/org/kframework/kprove/RewriterModule.java b/kernel/src/main/java/org/kframework/kprove/RewriterModule.java index 90ea42f76b8..b962834f9d6 100644 --- a/kernel/src/main/java/org/kframework/kprove/RewriterModule.java +++ b/kernel/src/main/java/org/kframework/kprove/RewriterModule.java @@ -4,6 +4,8 @@ import com.google.inject.AbstractModule; import com.google.inject.Provider; import com.google.inject.Provides; +import java.util.Map; +import java.util.function.Function; import org.kframework.definition.Definition; import org.kframework.kompile.KompileOptions; import org.kframework.rewriter.Rewriter; @@ -11,23 +13,26 @@ import org.kframework.utils.errorsystem.KExceptionManager; import org.kframework.utils.file.FileUtil; -import java.util.Map; -import java.util.function.Function; - public class RewriterModule extends AbstractModule { - @Override - protected void configure() { - binder().requireAtInjectOnConstructors(); - bind(FileUtil.class); - } + @Override + protected void configure() { + binder().requireAtInjectOnConstructors(); + bind(FileUtil.class); + } - @Provides - Function getRewriter(KompileOptions options, Map>> map, KExceptionManager kem) { - Provider> provider = map.get(options.backend); - if (provider == null) { - throw KEMException.criticalError("Backend " + options.backend + " does not support execution. Supported backends are: " - + map.keySet()); - } - return provider.get(); + @Provides + Function getRewriter( + KompileOptions options, + Map>> map, + KExceptionManager kem) { + Provider> provider = map.get(options.backend); + if (provider == null) { + throw KEMException.criticalError( + "Backend " + + options.backend + + " does not support execution. Supported backends are: " + + map.keySet()); } + return provider.get(); + } } diff --git a/kernel/src/main/java/org/kframework/ksearchpattern/KSearchPatternFrontEnd.java b/kernel/src/main/java/org/kframework/ksearchpattern/KSearchPatternFrontEnd.java index ed495154d8c..e74bea8597c 100644 --- a/kernel/src/main/java/org/kframework/ksearchpattern/KSearchPatternFrontEnd.java +++ b/kernel/src/main/java/org/kframework/ksearchpattern/KSearchPatternFrontEnd.java @@ -1,9 +1,12 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.ksearchpattern; -import com.google.inject.Provider; import com.google.inject.Inject; import com.google.inject.Module; +import com.google.inject.Provider; +import java.io.File; +import java.util.ArrayList; +import java.util.List; import org.kframework.attributes.Source; import org.kframework.backend.kore.ModuleToKORE; import org.kframework.builtin.BooleanUtils; @@ -25,93 +28,91 @@ import org.kframework.utils.inject.JCommanderModule; import org.kframework.utils.inject.JCommanderModule.Usage; -import java.io.File; -import java.util.ArrayList; -import java.util.List; - /** * Frontend for k-compiled-search-pattern tool. - *

- * k-compile-search-patterrn is used by the new krun frontend in order to - * convert a search pattern written as a rule bubble into a KORE search pattern - * for the Haskell backend. + * + *

k-compile-search-patterrn is used by the new krun frontend in order to convert a search + * pattern written as a rule bubble into a KORE search pattern for the Haskell backend. */ public class KSearchPatternFrontEnd extends FrontEnd { - private final KSearchPatternOptions options; - private final Provider kompileOptions; - private final KExceptionManager kem; - private final Provider files; - private final GlobalOptions globalOptions; - private final DefinitionScope scope; - private final Provider kompiledDir; - private final Provider compiledDef; + private final KSearchPatternOptions options; + private final Provider kompileOptions; + private final KExceptionManager kem; + private final Provider files; + private final GlobalOptions globalOptions; + private final DefinitionScope scope; + private final Provider kompiledDir; + private final Provider compiledDef; - @Inject - public KSearchPatternFrontEnd( - KSearchPatternOptions options, - KExceptionManager kem, - Provider kompileOptions, - GlobalOptions globalOptions, - @Usage String usage, - JarInfo jarInfo, - Provider files, - @KompiledDir Provider kompiledDir, - Provider compiledDef, - DefinitionScope scope) { - super(kem, globalOptions, usage, jarInfo, files); - this.options = options; - this.kompileOptions = kompileOptions; - this.globalOptions = globalOptions; - this.kem = kem; - this.files = files; - this.scope = scope; - this.kompiledDir = kompiledDir; - this.compiledDef = compiledDef; - } + @Inject + public KSearchPatternFrontEnd( + KSearchPatternOptions options, + KExceptionManager kem, + Provider kompileOptions, + GlobalOptions globalOptions, + @Usage String usage, + JarInfo jarInfo, + Provider files, + @KompiledDir Provider kompiledDir, + Provider compiledDef, + DefinitionScope scope) { + super(kem, globalOptions, usage, jarInfo, files); + this.options = options; + this.kompileOptions = kompileOptions; + this.globalOptions = globalOptions; + this.kem = kem; + this.files = files; + this.scope = scope; + this.kompiledDir = kompiledDir; + this.compiledDef = compiledDef; + } - public static List getModules() { - List modules = new ArrayList<>(); - modules.add(new KSearchPatternModule()); - modules.add(new JCommanderModule()); - modules.add(new CommonModule()); - return modules; - } + public static List getModules() { + List modules = new ArrayList<>(); + modules.add(new KSearchPatternModule()); + modules.add(new JCommanderModule()); + modules.add(new CommonModule()); + return modules; + } - @Override - protected int run() { - scope.enter(kompiledDir.get()); - try { - FileUtil files = this.files.get(); - CompiledDefinition compiledDef = this.compiledDef.get(); - KompileOptions kompileOptions = this.kompileOptions.get(); - Rule pattern = compiledDef.compilePatternIfAbsent(files, kem, options.pattern(), Source.apply("")); - K patternTerm = RewriteToTop.toLeft(pattern.body()); - K patternCondition = pattern.requires(); - org.kframework.definition.Module mod = compiledDef.executionModule(); - AddSortInjections addSortInjections = new AddSortInjections(mod); - ModuleToKORE converter = new ModuleToKORE(mod, compiledDef.topCellInitializer, kompileOptions); - StringBuilder sb = new StringBuilder(); - ExpandMacros macroExpander = ExpandMacros.forNonSentences(mod, files, kompileOptions, false); - K withMacros = macroExpander.expand(patternTerm); - K kWithInjections = addSortInjections.addInjections(withMacros); - sb.append("\\and{SortGeneratedTopCell{}}("); - converter.convert(kWithInjections, sb); - sb.append(", "); - if (patternCondition.equals(BooleanUtils.TRUE)) { - sb.append("\\top{SortGeneratedTopCell{}}()"); - } else { - sb.append("\\equals{SortBool{},SortGeneratedTopCell{}}("); - withMacros = macroExpander.expand(patternCondition); - kWithInjections = addSortInjections.addInjections(withMacros); - converter.convert(kWithInjections, sb); - sb.append(", \\dv{SortBool{}}(\"true\"))"); - } - sb.append(")"); - System.out.println(sb); - return 0; - } finally { - scope.exit(); - } + @Override + protected int run() { + scope.enter(kompiledDir.get()); + try { + FileUtil files = this.files.get(); + CompiledDefinition compiledDef = this.compiledDef.get(); + KompileOptions kompileOptions = this.kompileOptions.get(); + Rule pattern = + compiledDef.compilePatternIfAbsent( + files, kem, options.pattern(), Source.apply("")); + K patternTerm = RewriteToTop.toLeft(pattern.body()); + K patternCondition = pattern.requires(); + org.kframework.definition.Module mod = compiledDef.executionModule(); + AddSortInjections addSortInjections = new AddSortInjections(mod); + ModuleToKORE converter = + new ModuleToKORE(mod, compiledDef.topCellInitializer, kompileOptions); + StringBuilder sb = new StringBuilder(); + ExpandMacros macroExpander = ExpandMacros.forNonSentences(mod, files, kompileOptions, false); + K withMacros = macroExpander.expand(patternTerm); + K kWithInjections = addSortInjections.addInjections(withMacros); + sb.append("\\and{SortGeneratedTopCell{}}("); + converter.convert(kWithInjections, sb); + sb.append(", "); + if (patternCondition.equals(BooleanUtils.TRUE)) { + sb.append("\\top{SortGeneratedTopCell{}}()"); + } else { + sb.append("\\equals{SortBool{},SortGeneratedTopCell{}}("); + withMacros = macroExpander.expand(patternCondition); + kWithInjections = addSortInjections.addInjections(withMacros); + converter.convert(kWithInjections, sb); + sb.append(", \\dv{SortBool{}}(\"true\"))"); + } + sb.append(")"); + System.out.println(sb); + return 0; + } finally { + scope.exit(); } + } } diff --git a/kernel/src/main/java/org/kframework/ksearchpattern/KSearchPatternModule.java b/kernel/src/main/java/org/kframework/ksearchpattern/KSearchPatternModule.java index 93328edf004..6a2437bb6b1 100644 --- a/kernel/src/main/java/org/kframework/ksearchpattern/KSearchPatternModule.java +++ b/kernel/src/main/java/org/kframework/ksearchpattern/KSearchPatternModule.java @@ -3,42 +3,43 @@ import com.google.inject.AbstractModule; import com.google.inject.Provides; -import com.google.inject.TypeLiteral; import com.google.inject.multibindings.Multibinder; import org.kframework.main.FrontEnd; import org.kframework.main.GlobalOptions; import org.kframework.main.Tool; -import org.kframework.utils.inject.Options; import org.kframework.utils.inject.DefinitionLoadingModule; +import org.kframework.utils.inject.Options; import org.kframework.utils.inject.RequestScoped; import org.kframework.utils.options.DefinitionLoadingOptions; /** * Guice module for k-compile-search-pattern tool. * - * Binds the information needed to compute the kompiled directory as well as the options - * and frontend. + *

Binds the information needed to compute the kompiled directory as well as the options and + * frontend. */ public class KSearchPatternModule extends AbstractModule { - @Override - protected void configure() { - binder().requireAtInjectOnConstructors(); - bind(FrontEnd.class).to(KSearchPatternFrontEnd.class); - bind(Tool.class).toInstance(Tool.KSEARCHPATTERN); + @Override + protected void configure() { + binder().requireAtInjectOnConstructors(); + bind(FrontEnd.class).to(KSearchPatternFrontEnd.class); + bind(Tool.class).toInstance(Tool.KSEARCHPATTERN); - install(new DefinitionLoadingModule()); + install(new DefinitionLoadingModule()); - Multibinder optionsBinder = Multibinder.newSetBinder(binder(), Object.class, Options.class); - optionsBinder.addBinding().to(KSearchPatternOptions.class); - } + Multibinder optionsBinder = + Multibinder.newSetBinder(binder(), Object.class, Options.class); + optionsBinder.addBinding().to(KSearchPatternOptions.class); + } - @Provides @RequestScoped - GlobalOptions globalOptions(KSearchPatternOptions options) { - return options.global; - } + @Provides + @RequestScoped + GlobalOptions globalOptions(KSearchPatternOptions options) { + return options.global; + } - @Provides - DefinitionLoadingOptions defLoadingOptions(KSearchPatternOptions options) { - return options.definitionLoading; - } + @Provides + DefinitionLoadingOptions defLoadingOptions(KSearchPatternOptions options) { + return options.definitionLoading; + } } diff --git a/kernel/src/main/java/org/kframework/ksearchpattern/KSearchPatternOptions.java b/kernel/src/main/java/org/kframework/ksearchpattern/KSearchPatternOptions.java index 9fcc1baec84..dd3ec39d0e2 100644 --- a/kernel/src/main/java/org/kframework/ksearchpattern/KSearchPatternOptions.java +++ b/kernel/src/main/java/org/kframework/ksearchpattern/KSearchPatternOptions.java @@ -4,32 +4,27 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.ParametersDelegate; import com.google.inject.Inject; +import java.util.List; import org.kframework.main.GlobalOptions; import org.kframework.utils.inject.RequestScoped; import org.kframework.utils.options.DefinitionLoadingOptions; -import java.util.List; - -/** - * JCommander options for k-compile-search-pattern. - */ - +/** JCommander options for k-compile-search-pattern. */ @RequestScoped public class KSearchPatternOptions { - @Inject - public KSearchPatternOptions() {} + @Inject + public KSearchPatternOptions() {} - @ParametersDelegate - public transient GlobalOptions global = new GlobalOptions(); + @ParametersDelegate public transient GlobalOptions global = new GlobalOptions(); - @ParametersDelegate - public DefinitionLoadingOptions definitionLoading = new DefinitionLoadingOptions(); + @ParametersDelegate + public DefinitionLoadingOptions definitionLoading = new DefinitionLoadingOptions(); - @Parameter(description="") - private List parameters; + @Parameter(description = "") + private List parameters; - public String pattern() { - return parameters.get(0); - } + public String pattern() { + return parameters.get(0); + } } diff --git a/kernel/src/main/java/org/kframework/kserver/KServerFrontEnd.java b/kernel/src/main/java/org/kframework/kserver/KServerFrontEnd.java index b1ea8a48939..4d69b9ad1c1 100644 --- a/kernel/src/main/java/org/kframework/kserver/KServerFrontEnd.java +++ b/kernel/src/main/java/org/kframework/kserver/KServerFrontEnd.java @@ -1,5 +1,6 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.kserver; + import com.google.common.collect.ImmutableList; import com.google.inject.Inject; import com.google.inject.Injector; @@ -9,6 +10,15 @@ import com.martiansoftware.nailgun.NGListeningAddress; import com.martiansoftware.nailgun.NGServer; import com.martiansoftware.nailgun.ThreadLocalPrintStream; +import java.io.File; +import java.io.PrintStream; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import org.kframework.main.FrontEnd; import org.kframework.main.Main; import org.kframework.utils.OS; @@ -21,171 +31,164 @@ import org.kframework.utils.inject.JCommanderModule.Usage; import org.kframework.utils.inject.SimpleScope; -import java.io.File; -import java.io.PrintStream; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - - public class KServerFrontEnd extends FrontEnd { - public static List getModules() { - List modules = new ArrayList<>(); - modules.add(new KServerModule()); - modules.add(new JCommanderModule()); - modules.add(new CommonModule()); - return modules; + public static List getModules() { + List modules = new ArrayList<>(); + modules.add(new KServerModule()); + modules.add(new JCommanderModule()); + modules.add(new CommonModule()); + return modules; + } + + @Inject + public KServerFrontEnd( + KExceptionManager kem, + KServerOptions options, + @Usage String usage, + JarInfo jarInfo, + Provider files) { + super(kem, options.global, usage, jarInfo, files); + this.options = options; + } + + private static KServerFrontEnd instance; + private static Thread threadInstance; + private static final ImmutableList tools = + ImmutableList.of( + "-kompile", "-kast", "-kdep", "-kprove", "-kserver", "-k-compile-search-pattern"); + + private final KServerOptions options; + private final Map injectors = new HashMap<>(); + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + + @Override + protected int run() { + for (String tool : tools) { + injectors.put(tool, Main.getInjector(tool)); } - - @Inject - public KServerFrontEnd( - KExceptionManager kem, - KServerOptions options, - @Usage String usage, - JarInfo jarInfo, - Provider files) { - super(kem, options.global, usage, jarInfo, files); - this.options = options; + NGServer server; + File dir = null; + if (isLocal()) { + // can use more secure unix domain sockets + // we use HOME variable to get home on linux because user.home does not match HOME variable + // when run with sudo + // unless run with sudo -H, and we want to ensure that the installer works correctly. + String dirpath = options.socket; + if (dirpath == null) { + String home = + OS.current() == OS.LINUX ? System.getenv("HOME") : System.getProperty("user.home"); + dirpath = home + File.separatorChar + ".kserver"; + } + dir = new File(dirpath); + dir.mkdirs(); + dir.setReadable(false, false); + dir.setReadable(true, true); + dir.setWritable(false, false); + dir.setWritable(true, true); + dir.setExecutable(false, false); + dir.setExecutable(true, true); + File socket = new File(dir, "socket"); + socket.deleteOnExit(); + if (socket.exists()) { + System.out.println("Warning: K server already started."); + socket.delete(); + } + server = new NGServer(new NGListeningAddress(socket.getAbsolutePath()), 10, 10000); + } else { + server = new NGServer(InetAddress.getLoopbackAddress(), options.port); } - - private static KServerFrontEnd instance; - private static Thread threadInstance; - private static final ImmutableList tools = ImmutableList.of("-kompile", "-kast", - "-kdep", "-kprove", "-kserver", "-k-compile-search-pattern"); - - private final KServerOptions options; - private final Map injectors = new HashMap<>(); - private final ReadWriteLock lock = new ReentrantReadWriteLock(); - - @Override - protected int run() { - for (String tool : tools) { - injectors.put(tool, Main.getInjector(tool)); - } - NGServer server; - File dir = null; - if (isLocal()) { - // can use more secure unix domain sockets - // we use HOME variable to get home on linux because user.home does not match HOME variable when run with sudo - // unless run with sudo -H, and we want to ensure that the installer works correctly. - String dirpath = options.socket; - if (dirpath == null) { - String home = OS.current() == OS.LINUX ? System.getenv("HOME") : System.getProperty("user.home"); - dirpath = home + File.separatorChar + ".kserver"; - } - dir = new File(dirpath); - dir.mkdirs(); - dir.setReadable(false, false); - dir.setReadable(true, true); - dir.setWritable(false, false); - dir.setWritable(true, true); - dir.setExecutable(false, false); - dir.setExecutable(true, true); - File socket = new File(dir, "socket"); - socket.deleteOnExit(); - if (socket.exists()) { - System.out.println("Warning: K server already started."); - socket.delete(); - } - server = new NGServer(new NGListeningAddress(socket.getAbsolutePath()), 10, 10000); - } else { - server = new NGServer(InetAddress.getLoopbackAddress(), options.port); - } - Thread t = new Thread(server); - instance = this; - threadInstance = t; - t.start(); - - if (isLocal()) { - System.out.println("K server started using IPC at " + dir.getAbsolutePath()); - } else { - int runningPort = server.getPort(); - while (runningPort == 0) { - try { - Thread.sleep(50L); - } catch (InterruptedException e) { - } - runningPort = server.getPort(); - } - System.out.println("K server started on 127.0.0.1:" + options.port); - } - + Thread t = new Thread(server); + instance = this; + threadInstance = t; + t.start(); + + if (isLocal()) { + System.out.println("K server started using IPC at " + dir.getAbsolutePath()); + } else { + int runningPort = server.getPort(); + while (runningPort == 0) { try { - t.join(); - return 0; + Thread.sleep(50L); } catch (InterruptedException e) { - //application is about to die - return 0; } + runningPort = server.getPort(); + } + System.out.println("K server started on 127.0.0.1:" + options.port); } - public static KServerFrontEnd instance() { - return instance; + try { + t.join(); + return 0; + } catch (InterruptedException e) { + // application is about to die + return 0; } + } - public int run(String tool, String[] args, File workingDir, Map env, long startTime) { - ThreadLocalPrintStream system_out = (ThreadLocalPrintStream) System.out; - ThreadLocalPrintStream system_err = (ThreadLocalPrintStream) System.err; + public static KServerFrontEnd instance() { + return instance; + } - Injector injector; + public int run( + String tool, String[] args, File workingDir, Map env, long startTime) { + ThreadLocalPrintStream system_out = (ThreadLocalPrintStream) System.out; + ThreadLocalPrintStream system_err = (ThreadLocalPrintStream) System.err; - lock.readLock().lock(); - try { - injector = injectors.get(tool); - } finally { - lock.readLock().unlock(); - } + Injector injector; - Main launcher = injector.getInstance(Main.class); - SimpleScope requestScope = launcher.getRequestScope(); - try { - requestScope.enter(); - Main.seedInjector(requestScope, tool, args, workingDir, env, startTime); - TTYInfo tty = injector.getInstance(TTYInfo.class); - if (!tty.stdout()) { - system_out.init(new PrintStream(system_out.getPrintStream())); - } - if (!tty.stderr()) { - system_err.init(new PrintStream(system_err.getPrintStream())); - } - - int result = launcher.runApplication(); - System.out.flush(); - System.err.flush(); - return result; - } finally { - requestScope.exit(); - } + lock.readLock().lock(); + try { + injector = injectors.get(tool); + } finally { + lock.readLock().unlock(); } - public static void nailMain(NGContext context) { - KServerFrontEnd kserver = KServerFrontEnd.instance(); - if (!kserver.isLocal()) { - context.assertLoopbackClient(); - } - if (context.getArgs()[0].equals("shutdown")) { - System.setSecurityManager(null); - context.getNGServer().shutdown(true); - } else if (context.getArgs()[0].equals("reset")) { - kserver.lock.writeLock().lock(); - try { - kserver.injectors.clear(); - for (String tool : tools) { - kserver.injectors.put(tool, Main.getInjector(tool)); - } - } finally { - kserver.lock.writeLock().unlock(); - } - System.gc(); - } + Main launcher = injector.getInstance(Main.class); + SimpleScope requestScope = launcher.getRequestScope(); + try { + requestScope.enter(); + Main.seedInjector(requestScope, tool, args, workingDir, env, startTime); + TTYInfo tty = injector.getInstance(TTYInfo.class); + if (!tty.stdout()) { + system_out.init(new PrintStream(system_out.getPrintStream())); + } + if (!tty.stderr()) { + system_err.init(new PrintStream(system_err.getPrintStream())); + } + + int result = launcher.runApplication(); + System.out.flush(); + System.err.flush(); + return result; + } finally { + requestScope.exit(); } + } - public boolean isLocal() { - return (OS.current() != OS.WINDOWS) || (options.socket != null); + public static void nailMain(NGContext context) { + KServerFrontEnd kserver = KServerFrontEnd.instance(); + if (!kserver.isLocal()) { + context.assertLoopbackClient(); } + if (context.getArgs()[0].equals("shutdown")) { + System.setSecurityManager(null); + context.getNGServer().shutdown(true); + } else if (context.getArgs()[0].equals("reset")) { + kserver.lock.writeLock().lock(); + try { + kserver.injectors.clear(); + for (String tool : tools) { + kserver.injectors.put(tool, Main.getInjector(tool)); + } + } finally { + kserver.lock.writeLock().unlock(); + } + System.gc(); + } + } + + public boolean isLocal() { + return (OS.current() != OS.WINDOWS) || (options.socket != null); + } } diff --git a/kernel/src/main/java/org/kframework/kserver/KServerModule.java b/kernel/src/main/java/org/kframework/kserver/KServerModule.java index 865cdfc76f4..36c1ed017ff 100644 --- a/kernel/src/main/java/org/kframework/kserver/KServerModule.java +++ b/kernel/src/main/java/org/kframework/kserver/KServerModule.java @@ -1,40 +1,40 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.kserver; -import java.io.File; +import com.google.inject.AbstractModule; +import com.google.inject.Provides; +import com.google.inject.TypeLiteral; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.util.Providers; +import java.io.File; import org.kframework.main.FrontEnd; import org.kframework.main.GlobalOptions; import org.kframework.main.Tool; import org.kframework.utils.file.DefinitionDir; import org.kframework.utils.file.KompiledDir; import org.kframework.utils.inject.Options; - -import com.google.inject.AbstractModule; -import com.google.inject.Provides; -import com.google.inject.TypeLiteral; -import com.google.inject.multibindings.Multibinder; -import com.google.inject.util.Providers; import org.kframework.utils.inject.RequestScoped; public class KServerModule extends AbstractModule { - @Override - protected void configure() { - binder().requireAtInjectOnConstructors(); - bind(Tool.class).toInstance(Tool.KSERVER); - bind(FrontEnd.class).to(KServerFrontEnd.class); - - Multibinder optionsBinder = Multibinder.newSetBinder(binder(), Object.class, Options.class); - optionsBinder.addBinding().to(KServerOptions.class); - Multibinder.newSetBinder(binder(), new TypeLiteral>() {}, Options.class); - - bind(File.class).annotatedWith(DefinitionDir.class).toProvider(Providers.of(null)); - bind(File.class).annotatedWith(KompiledDir.class).toProvider(Providers.of(null)); - } - - @Provides @RequestScoped - GlobalOptions globalOptions(KServerOptions options) { - return options.global; - } - + @Override + protected void configure() { + binder().requireAtInjectOnConstructors(); + bind(Tool.class).toInstance(Tool.KSERVER); + bind(FrontEnd.class).to(KServerFrontEnd.class); + + Multibinder optionsBinder = + Multibinder.newSetBinder(binder(), Object.class, Options.class); + optionsBinder.addBinding().to(KServerOptions.class); + Multibinder.newSetBinder(binder(), new TypeLiteral>() {}, Options.class); + + bind(File.class).annotatedWith(DefinitionDir.class).toProvider(Providers.of(null)); + bind(File.class).annotatedWith(KompiledDir.class).toProvider(Providers.of(null)); + } + + @Provides + @RequestScoped + GlobalOptions globalOptions(KServerOptions options) { + return options.global; + } } diff --git a/kernel/src/main/java/org/kframework/kserver/KServerOptions.java b/kernel/src/main/java/org/kframework/kserver/KServerOptions.java index b09695b0ac3..668d0a46ca3 100644 --- a/kernel/src/main/java/org/kframework/kserver/KServerOptions.java +++ b/kernel/src/main/java/org/kframework/kserver/KServerOptions.java @@ -1,26 +1,27 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.kserver; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.ParametersDelegate; import com.google.inject.Inject; import org.kframework.main.GlobalOptions; import org.kframework.utils.inject.RequestScoped; -import com.beust.jcommander.Parameter; -import com.beust.jcommander.ParametersDelegate; - @RequestScoped public class KServerOptions { - @Inject - public KServerOptions() {} - - @ParametersDelegate - public transient GlobalOptions global = new GlobalOptions(); + @Inject + public KServerOptions() {} - @Parameter(names={"--port", "-p"}, description="The port to start the server on.") - public int port = 2113; + @ParametersDelegate public transient GlobalOptions global = new GlobalOptions(); - @Parameter(names={"--socket"}, description="The directory to put the unix domain socket in.") - public String socket = null; + @Parameter( + names = {"--port", "-p"}, + description = "The port to start the server on.") + public int port = 2113; + @Parameter( + names = {"--socket"}, + description = "The directory to put the unix domain socket in.") + public String socket = null; } diff --git a/kernel/src/main/java/org/kframework/lsp/CompletionHelper.java b/kernel/src/main/java/org/kframework/lsp/CompletionHelper.java index 3d85328a9d5..0e35074a344 100644 --- a/kernel/src/main/java/org/kframework/lsp/CompletionHelper.java +++ b/kernel/src/main/java/org/kframework/lsp/CompletionHelper.java @@ -1,210 +1,249 @@ // Copyright (c) Runtime Verification, Inc. All Rights Reserved. package org.kframework.lsp; +import static org.kframework.Collections.immutable; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.eclipse.lsp4j.CompletionItem; import org.eclipse.lsp4j.CompletionItemKind; import org.eclipse.lsp4j.InsertTextFormat; import org.jetbrains.annotations.NotNull; import org.kframework.attributes.Att; -import org.kframework.kil.Module; import org.kframework.kil.*; +import org.kframework.kil.Module; import org.kframework.kore.Sort; import scala.Tuple2; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import static org.kframework.Collections.immutable; - -/** - * Helper methods for building CompletionItems - */ +/** Helper methods for building CompletionItems */ public class CompletionHelper { - static Pattern ptrn = Pattern.compile("[a-zA-Z0-9#]+"); - - // create the list of CompletionItems for every piece of syntax - // for now we filter by alphanumeric but can be improved greatly. - public static List getRuleCompletion(List dis) { - List lci = new ArrayList<>(); - // Traverse all the modules and all the syntax declarations to find the Terminals in productions - // For each Terminal that follows the above, create a CompletionItem with some documentation - // Tree structure: Definition -> Module -> Syntax -> PriorityBlock -> Production -> Terminal - dis.stream().filter(i -> i instanceof Module) - .map(m -> ((Module) m)) - .forEach(m -> m.getItems().stream() - .filter(mi -> mi instanceof Syntax) - .map(s -> ((Syntax) s)) - .forEach(s -> s.getPriorityBlocks() - .forEach((pb -> pb.getProductions() - .forEach(p -> { - if (p.getItems().get(0) instanceof Terminal && ptrn.matcher(((Terminal)p.getItems().get(0)).getTerminal()).matches()) { - CompletionItem completionItem = buildRuleCompletionItem(m, s, p); - lci.add(completionItem); - } else - p.getItems().stream() - .filter(pi -> pi instanceof Terminal) - .map(t -> (Terminal) t) - .forEach(t -> { - if (ptrn.matcher(t.getTerminal()).matches()) { - CompletionItem completionItem = buildRuleCompletionItem(m, s, p, t); - lci.add(completionItem); - } - }); - } - ))))); - - return lci; - } - - // for productions that start with a valid terminal - // build a code snippet with tabstops - @NotNull - public static CompletionItem buildRuleCompletionItem(Module m, Syntax s, Production p) { - CompletionItem completionItem = new CompletionItem(); - Terminal t = (Terminal) p.getItems().get(0); - completionItem.setLabel(t.getTerminal()); - StringBuilder snip = new StringBuilder(); - int codeSnip = 1; - String prevToken = "("; - for (int i = 0; i < p.getItems().size(); i++) { - ProductionItem pi = p.getItems().get(i); - if (pi instanceof Terminal) { - String trm = ((Terminal) pi).getTerminal(); - // don't insert whitespaces around ( ) parentheses - if (!(prevToken.equals("(") || prevToken.equals(")")) && !"(".equals(trm) && !")".equals(trm) && !",".equals(trm)) - snip.append(" "); - prevToken = trm; - snip.append(trm); - } else if (pi instanceof NonTerminal) { - if (!(prevToken.equals("(") || prevToken.equals(")"))) - snip.append(" "); - prevToken = ""; - snip.append("${"); - snip.append(codeSnip++); - snip.append(":_:"); - snip.append(((NonTerminal) pi).getSort()); - snip.append("}"); - } - } - - completionItem.setInsertText(snip.toString()); - completionItem.setDetail("module " + m.getName()); - String doc = "syntax "; - doc += !s.getParams().isEmpty() ? - "{" + s.getParams().stream().map(Sort::toString).collect(Collectors.joining(", ")) + "} " : ""; - doc += s.getDeclaredSort() + " ::= "; - doc += p.toString(); - completionItem.setDocumentation(doc); - completionItem.setInsertTextFormat(InsertTextFormat.Snippet); - completionItem.setKind(CompletionItemKind.Function); - return completionItem; - } - - @NotNull - private static CompletionItem buildRuleCompletionItem(Module m, Syntax s, Production p, Terminal t) { - CompletionItem completionItem = new CompletionItem(); - completionItem.setLabel(t.getTerminal()); - completionItem.setInsertText(t.getTerminal()); - completionItem.setDetail("module " + m.getName()); - String doc = "syntax "; - doc += !s.getParams().isEmpty() ? - "{" + s.getParams().stream().map(Sort::toString).collect(Collectors.joining(", ")) + "} " : ""; - doc += s.getDeclaredSort() + " ::= "; - doc += p.toString(); - completionItem.setDocumentation(doc); - completionItem.setInsertTextFormat(InsertTextFormat.PlainText); - completionItem.setKind(CompletionItemKind.Operator); - return completionItem; - } - - public static CompletionItem getNewRequiresCompletion() { - CompletionItem completionItem = new CompletionItem(); - completionItem.setLabel("requires"); - completionItem.setInsertText("requires \"${1:file}\""); - completionItem.setInsertTextFormat(InsertTextFormat.Snippet); - completionItem.setKind(CompletionItemKind.Keyword); - return completionItem; + static Pattern ptrn = Pattern.compile("[a-zA-Z0-9#]+"); + + // create the list of CompletionItems for every piece of syntax + // for now we filter by alphanumeric but can be improved greatly. + public static List getRuleCompletion(List dis) { + List lci = new ArrayList<>(); + // Traverse all the modules and all the syntax declarations to find the Terminals in productions + // For each Terminal that follows the above, create a CompletionItem with some + // documentation + // Tree structure: Definition -> Module -> Syntax -> PriorityBlock -> Production -> Terminal + dis.stream() + .filter(i -> i instanceof Module) + .map(m -> ((Module) m)) + .forEach( + m -> + m.getItems().stream() + .filter(mi -> mi instanceof Syntax) + .map(s -> ((Syntax) s)) + .forEach( + s -> + s.getPriorityBlocks() + .forEach( + (pb -> + pb.getProductions() + .forEach( + p -> { + if (p.getItems().get(0) instanceof Terminal + && ptrn.matcher( + ((Terminal) p.getItems().get(0)) + .getTerminal()) + .matches()) { + CompletionItem completionItem = + buildRuleCompletionItem(m, s, p); + lci.add(completionItem); + } else + p.getItems().stream() + .filter(pi -> pi instanceof Terminal) + .map(t -> (Terminal) t) + .forEach( + t -> { + if (ptrn.matcher(t.getTerminal()) + .matches()) { + CompletionItem completionItem = + buildRuleCompletionItem( + m, s, p, t); + lci.add(completionItem); + } + }); + }))))); + + return lci; + } + + // for productions that start with a valid terminal + // build a code snippet with tabstops + @NotNull + public static CompletionItem buildRuleCompletionItem(Module m, Syntax s, Production p) { + CompletionItem completionItem = new CompletionItem(); + Terminal t = (Terminal) p.getItems().get(0); + completionItem.setLabel(t.getTerminal()); + StringBuilder snip = new StringBuilder(); + int codeSnip = 1; + String prevToken = "("; + for (int i = 0; i < p.getItems().size(); i++) { + ProductionItem pi = p.getItems().get(i); + if (pi instanceof Terminal) { + String trm = ((Terminal) pi).getTerminal(); + // don't insert whitespaces around ( ) parentheses + if (!(prevToken.equals("(") || prevToken.equals(")")) + && !"(".equals(trm) + && !")".equals(trm) + && !",".equals(trm)) snip.append(" "); + prevToken = trm; + snip.append(trm); + } else if (pi instanceof NonTerminal) { + if (!(prevToken.equals("(") || prevToken.equals(")"))) snip.append(" "); + prevToken = ""; + snip.append("${"); + snip.append(codeSnip++); + snip.append(":_:"); + snip.append(((NonTerminal) pi).getSort()); + snip.append("}"); + } } - public static CompletionItem getNewModuleCompletion() { - CompletionItem completionItem = new CompletionItem(); - completionItem.setLabel("module"); - completionItem.setInsertText("module ${0:NAME}\nendmodule"); - completionItem.setInsertTextFormat(InsertTextFormat.Snippet); - completionItem.setKind(CompletionItemKind.Keyword); - return completionItem; - } - - public static List getNewSentenceCompletion() { - CompletionItem completionItem = new CompletionItem(); - completionItem.setLabel("syntax"); - completionItem.setInsertText("syntax ${1:SORT} ::= $0"); - completionItem.setInsertTextFormat(InsertTextFormat.Snippet); - completionItem.setKind(CompletionItemKind.Keyword); - - return List.of(completionItem, - new CompletionItem("rule"), - new CompletionItem("configuration"), - new CompletionItem("context"), - new CompletionItem("claim")); - } - - public static CompletionItem getNewImportCompletion() { - CompletionItem completionItem = new CompletionItem(); - completionItem.setLabel("imports"); - completionItem.setInsertText("imports ${0:NAME}"); - completionItem.setInsertTextFormat(InsertTextFormat.Snippet); - completionItem.setKind(CompletionItemKind.Keyword); - return completionItem; - } - - public static List getImportCompletion(List allDi) { - return allDi.stream() - .filter(mi2 -> mi2 instanceof Module) - .map(m2 -> ((Module) m2)) - .map(m2 -> { - CompletionItem ci = new CompletionItem(); - ci.setLabel(m2.getName()); - ci.setInsertText(m2.getName()); - ci.setDetail(Path.of(m2.getSource().source()).getFileName().toString()); - ci.setKind(CompletionItemKind.Module); - return ci; - }).collect(Collectors.toList()); - } - - // create a list of all the visible declared sorts - public static List getSyntaxCompletion(List allDi) { - Map> allSorts = allDi.stream().filter(i -> i instanceof Module) - .map(m3 -> ((Module) m3)) - .flatMap(m3 -> m3.getItems().stream() + completionItem.setInsertText(snip.toString()); + completionItem.setDetail("module " + m.getName()); + String doc = "syntax "; + doc += + !s.getParams().isEmpty() + ? "{" + + s.getParams().stream().map(Sort::toString).collect(Collectors.joining(", ")) + + "} " + : ""; + doc += s.getDeclaredSort() + " ::= "; + doc += p.toString(); + completionItem.setDocumentation(doc); + completionItem.setInsertTextFormat(InsertTextFormat.Snippet); + completionItem.setKind(CompletionItemKind.Function); + return completionItem; + } + + @NotNull + private static CompletionItem buildRuleCompletionItem( + Module m, Syntax s, Production p, Terminal t) { + CompletionItem completionItem = new CompletionItem(); + completionItem.setLabel(t.getTerminal()); + completionItem.setInsertText(t.getTerminal()); + completionItem.setDetail("module " + m.getName()); + String doc = "syntax "; + doc += + !s.getParams().isEmpty() + ? "{" + + s.getParams().stream().map(Sort::toString).collect(Collectors.joining(", ")) + + "} " + : ""; + doc += s.getDeclaredSort() + " ::= "; + doc += p.toString(); + completionItem.setDocumentation(doc); + completionItem.setInsertTextFormat(InsertTextFormat.PlainText); + completionItem.setKind(CompletionItemKind.Operator); + return completionItem; + } + + public static CompletionItem getNewRequiresCompletion() { + CompletionItem completionItem = new CompletionItem(); + completionItem.setLabel("requires"); + completionItem.setInsertText("requires \"${1:file}\""); + completionItem.setInsertTextFormat(InsertTextFormat.Snippet); + completionItem.setKind(CompletionItemKind.Keyword); + return completionItem; + } + + public static CompletionItem getNewModuleCompletion() { + CompletionItem completionItem = new CompletionItem(); + completionItem.setLabel("module"); + completionItem.setInsertText("module ${0:NAME}\nendmodule"); + completionItem.setInsertTextFormat(InsertTextFormat.Snippet); + completionItem.setKind(CompletionItemKind.Keyword); + return completionItem; + } + + public static List getNewSentenceCompletion() { + CompletionItem completionItem = new CompletionItem(); + completionItem.setLabel("syntax"); + completionItem.setInsertText("syntax ${1:SORT} ::= $0"); + completionItem.setInsertTextFormat(InsertTextFormat.Snippet); + completionItem.setKind(CompletionItemKind.Keyword); + + return List.of( + completionItem, + new CompletionItem("rule"), + new CompletionItem("configuration"), + new CompletionItem("context"), + new CompletionItem("claim")); + } + + public static CompletionItem getNewImportCompletion() { + CompletionItem completionItem = new CompletionItem(); + completionItem.setLabel("imports"); + completionItem.setInsertText("imports ${0:NAME}"); + completionItem.setInsertTextFormat(InsertTextFormat.Snippet); + completionItem.setKind(CompletionItemKind.Keyword); + return completionItem; + } + + public static List getImportCompletion(List allDi) { + return allDi.stream() + .filter(mi2 -> mi2 instanceof Module) + .map(m2 -> ((Module) m2)) + .map( + m2 -> { + CompletionItem ci = new CompletionItem(); + ci.setLabel(m2.getName()); + ci.setInsertText(m2.getName()); + ci.setDetail(Path.of(m2.getSource().source()).getFileName().toString()); + ci.setKind(CompletionItemKind.Module); + return ci; + }) + .collect(Collectors.toList()); + } + + // create a list of all the visible declared sorts + public static List getSyntaxCompletion(List allDi) { + Map> allSorts = + allDi.stream() + .filter(i -> i instanceof Module) + .map(m3 -> ((Module) m3)) + .flatMap( + m3 -> + m3.getItems().stream() .filter(mi3 -> mi3 instanceof Syntax) .map(s -> ((Syntax) s)) .filter(s -> !s.getParams().contains(s.getDeclaredSort().getRealSort())) - .map(s -> Tuple2.apply(s.getDeclaredSort().getRealSort().name(), s.getAttributes()))) - .collect(Collectors.groupingBy(Tuple2::_1, Collectors.mapping(Tuple2::_2, Collectors.toSet()))); - Map allSorts2 = allSorts.entrySet().stream() - .map(e -> Tuple2.apply(e.getKey(), Att.mergeAttributes(immutable(e.getValue())))) - .collect(Collectors.toMap(Tuple2::_1, Tuple2::_2)); - return allSorts2.entrySet().stream().map(e -> { - CompletionItem ci = new CompletionItem(); - ci.setLabel(e.getKey()); - ci.setInsertText(e.getKey()); - // TODO: to calculate properly we need to recurse through the inclusion tree - // and find the first module to declare the sort. This should be easy to get - // when we connect to the kompile pipeline. - //ci.setDetail("module " + m.getName()); - String documentation = "syntax " + e.getKey() + " "; - documentation += e.getValue().toString(); - ci.setDocumentation(documentation); - ci.setKind(CompletionItemKind.TypeParameter); - return ci; - }).collect(Collectors.toList()); - } + .map( + s -> + Tuple2.apply( + s.getDeclaredSort().getRealSort().name(), s.getAttributes()))) + .collect( + Collectors.groupingBy( + Tuple2::_1, Collectors.mapping(Tuple2::_2, Collectors.toSet()))); + Map allSorts2 = + allSorts.entrySet().stream() + .map(e -> Tuple2.apply(e.getKey(), Att.mergeAttributes(immutable(e.getValue())))) + .collect(Collectors.toMap(Tuple2::_1, Tuple2::_2)); + return allSorts2.entrySet().stream() + .map( + e -> { + CompletionItem ci = new CompletionItem(); + ci.setLabel(e.getKey()); + ci.setInsertText(e.getKey()); + // TODO: to calculate properly we need to recurse through the inclusion tree + // and find the first module to declare the sort. This should be easy to get + // when we connect to the kompile pipeline. + // ci.setDetail("module " + m.getName()); + String documentation = "syntax " + e.getKey() + " "; + documentation += e.getValue().toString(); + ci.setDocumentation(documentation); + ci.setKind(CompletionItemKind.TypeParameter); + return ci; + }) + .collect(Collectors.toList()); + } } diff --git a/kernel/src/main/java/org/kframework/lsp/KLanguageServer.java b/kernel/src/main/java/org/kframework/lsp/KLanguageServer.java index 8487a314c18..5d6d0c6614e 100644 --- a/kernel/src/main/java/org/kframework/lsp/KLanguageServer.java +++ b/kernel/src/main/java/org/kframework/lsp/KLanguageServer.java @@ -1,7 +1,14 @@ // Copyright (c) Runtime Verification, Inc. All Rights Reserved. package org.kframework.lsp; +import static org.kframework.kompile.Kompile.CACHE_FILE_NAME; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; import org.eclipse.lsp4j.*; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.services.LanguageClient; @@ -11,121 +18,126 @@ import org.eclipse.lsp4j.services.WorkspaceService; import org.kframework.main.Main; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; - -import static org.kframework.kompile.Kompile.CACHE_FILE_NAME; - -/** - * Language Server implementation for the K framework. - */ +/** Language Server implementation for the K framework. */ public class KLanguageServer implements LanguageServer, LanguageClientAware { - private final TextDocumentService textDocumentService; - private final WorkspaceService workspaceService; - private ClientCapabilities clientCapabilities; - LanguageClient languageClient; - List workspaceFolders; - private int shutdown = 1; - - public KLanguageServer() { - try { - this.textDocumentService = new KTextDocumentService(this); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - this.workspaceService = new KWorkspaceService(this); + private final TextDocumentService textDocumentService; + private final WorkspaceService workspaceService; + private ClientCapabilities clientCapabilities; + LanguageClient languageClient; + List workspaceFolders; + private int shutdown = 1; + + public KLanguageServer() { + try { + this.textDocumentService = new KTextDocumentService(this); + } catch (URISyntaxException e) { + throw new RuntimeException(e); } - - @Override - public CompletableFuture initialize(InitializeParams initializeParams) { - workspaceFolders = initializeParams.getWorkspaceFolders(); - LSClientLogger.getInstance().logMessage("initWorkspaceFolders: " + initializeParams.getWorkspaceFolders()); - - final InitializeResult response = new InitializeResult(new ServerCapabilities()); - //Set the document synchronization capabilities to full. - response.getCapabilities().setTextDocumentSync(TextDocumentSyncKind.Full); - this.clientCapabilities = initializeParams.getCapabilities(); - - /* Check if dynamic registration of completion capability is allowed by the client. If so we don't register the capability. - Else, we register the completion capability. - */ - // TODO: check if this needs to be guarded - response.getCapabilities().setReferencesProvider(true); - response.getCapabilities().setDefinitionProvider(true); - response.getCapabilities().setSelectionRangeProvider(true); - - if (!isDynamicCompletionRegistration()) - response.getCapabilities().setCompletionProvider(new CompletionOptions()); - if (!isDiagnosticRegistration()) - response.getCapabilities().setDiagnosticProvider(new DiagnosticRegistrationOptions(false, false)); - return CompletableFuture.supplyAsync(() -> response); - } - - @Override - public void initialized(InitializedParams params) { - //Check if dynamic completion support is allowed, if so register. - if (isDynamicCompletionRegistration()) { - CompletionRegistrationOptions completionRegistrationOptions = new CompletionRegistrationOptions(); - Registration completionRegistration = new Registration(UUID.randomUUID().toString(), - "textDocument/completion", completionRegistrationOptions); - languageClient.registerCapability(new RegistrationParams(List.of(completionRegistration))); - } - if (isDiagnosticRegistration()) { - DiagnosticRegistrationOptions diagnosticRegistrationOptions = new DiagnosticRegistrationOptions(false, false); - Registration diagnosticRegistration = new Registration(UUID.randomUUID().toString(), - "textDocument/diagnostic", diagnosticRegistrationOptions); - languageClient.registerCapability(new RegistrationParams(List.of(diagnosticRegistration))); - } - // Register file watchers - List watchers = new ArrayList<>(); - watchers.add(new FileSystemWatcher(Either.forLeft("/**/" + CACHE_FILE_NAME), - WatchKind.Create + WatchKind.Delete + WatchKind.Change)); - DidChangeWatchedFilesRegistrationOptions opts = new DidChangeWatchedFilesRegistrationOptions(watchers); - Registration registration = new Registration(UUID.randomUUID().toString(), - "workspace/didChangeWatchedFiles", opts); - languageClient.registerCapability(new RegistrationParams(Collections.singletonList(registration))); - } - - @Override - public CompletableFuture shutdown() { - shutdown = 0; - return CompletableFuture.supplyAsync(Object::new); - } - - @Override - public void exit() { - Main.exit(shutdown); - } - - @Override - public TextDocumentService getTextDocumentService() { - return this.textDocumentService; - } - - @Override - public WorkspaceService getWorkspaceService() { - return this.workspaceService; - } - - @Override - public void connect(LanguageClient languageClient) { - this.languageClient = languageClient; - LSClientLogger.getInstance().initialize(this.languageClient); - } - - private boolean isDynamicCompletionRegistration() { - TextDocumentClientCapabilities textDocumentCapabilities = clientCapabilities.getTextDocument(); - return textDocumentCapabilities != null && textDocumentCapabilities.getCompletion() != null - && Boolean.FALSE.equals(textDocumentCapabilities.getCompletion().getDynamicRegistration()); + this.workspaceService = new KWorkspaceService(this); + } + + @Override + public CompletableFuture initialize(InitializeParams initializeParams) { + workspaceFolders = initializeParams.getWorkspaceFolders(); + LSClientLogger.getInstance() + .logMessage("initWorkspaceFolders: " + initializeParams.getWorkspaceFolders()); + + final InitializeResult response = new InitializeResult(new ServerCapabilities()); + // Set the document synchronization capabilities to full. + response.getCapabilities().setTextDocumentSync(TextDocumentSyncKind.Full); + this.clientCapabilities = initializeParams.getCapabilities(); + + /* Check if dynamic registration of completion capability is allowed by the client. If so we don't register the capability. + Else, we register the completion capability. + */ + // TODO: check if this needs to be guarded + response.getCapabilities().setReferencesProvider(true); + response.getCapabilities().setDefinitionProvider(true); + response.getCapabilities().setSelectionRangeProvider(true); + + if (!isDynamicCompletionRegistration()) + response.getCapabilities().setCompletionProvider(new CompletionOptions()); + if (!isDiagnosticRegistration()) + response + .getCapabilities() + .setDiagnosticProvider(new DiagnosticRegistrationOptions(false, false)); + return CompletableFuture.supplyAsync(() -> response); + } + + @Override + public void initialized(InitializedParams params) { + // Check if dynamic completion support is allowed, if so register. + if (isDynamicCompletionRegistration()) { + CompletionRegistrationOptions completionRegistrationOptions = + new CompletionRegistrationOptions(); + Registration completionRegistration = + new Registration( + UUID.randomUUID().toString(), + "textDocument/completion", + completionRegistrationOptions); + languageClient.registerCapability(new RegistrationParams(List.of(completionRegistration))); } - - private boolean isDiagnosticRegistration() { - TextDocumentClientCapabilities textDocumentCapabilities = clientCapabilities.getTextDocument(); - return textDocumentCapabilities != null && textDocumentCapabilities.getDiagnostic() != null; + if (isDiagnosticRegistration()) { + DiagnosticRegistrationOptions diagnosticRegistrationOptions = + new DiagnosticRegistrationOptions(false, false); + Registration diagnosticRegistration = + new Registration( + UUID.randomUUID().toString(), + "textDocument/diagnostic", + diagnosticRegistrationOptions); + languageClient.registerCapability(new RegistrationParams(List.of(diagnosticRegistration))); } + // Register file watchers + List watchers = new ArrayList<>(); + watchers.add( + new FileSystemWatcher( + Either.forLeft("/**/" + CACHE_FILE_NAME), + WatchKind.Create + WatchKind.Delete + WatchKind.Change)); + DidChangeWatchedFilesRegistrationOptions opts = + new DidChangeWatchedFilesRegistrationOptions(watchers); + Registration registration = + new Registration(UUID.randomUUID().toString(), "workspace/didChangeWatchedFiles", opts); + languageClient.registerCapability( + new RegistrationParams(Collections.singletonList(registration))); + } + + @Override + public CompletableFuture shutdown() { + shutdown = 0; + return CompletableFuture.supplyAsync(Object::new); + } + + @Override + public void exit() { + Main.exit(shutdown); + } + + @Override + public TextDocumentService getTextDocumentService() { + return this.textDocumentService; + } + + @Override + public WorkspaceService getWorkspaceService() { + return this.workspaceService; + } + + @Override + public void connect(LanguageClient languageClient) { + this.languageClient = languageClient; + LSClientLogger.getInstance().initialize(this.languageClient); + } + + private boolean isDynamicCompletionRegistration() { + TextDocumentClientCapabilities textDocumentCapabilities = clientCapabilities.getTextDocument(); + return textDocumentCapabilities != null + && textDocumentCapabilities.getCompletion() != null + && Boolean.FALSE.equals(textDocumentCapabilities.getCompletion().getDynamicRegistration()); + } + + private boolean isDiagnosticRegistration() { + TextDocumentClientCapabilities textDocumentCapabilities = clientCapabilities.getTextDocument(); + return textDocumentCapabilities != null && textDocumentCapabilities.getDiagnostic() != null; + } } diff --git a/kernel/src/main/java/org/kframework/lsp/KLanguageServerLauncher.java b/kernel/src/main/java/org/kframework/lsp/KLanguageServerLauncher.java index 96bce81e480..775dead12ff 100644 --- a/kernel/src/main/java/org/kframework/lsp/KLanguageServerLauncher.java +++ b/kernel/src/main/java/org/kframework/lsp/KLanguageServerLauncher.java @@ -1,37 +1,36 @@ // Copyright (c) Runtime Verification, Inc. All Rights Reserved. package org.kframework.lsp; -import org.eclipse.lsp4j.jsonrpc.Launcher; -import org.eclipse.lsp4j.services.LanguageClient; - import java.io.InputStream; import java.io.OutputStream; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; +import org.eclipse.lsp4j.jsonrpc.Launcher; +import org.eclipse.lsp4j.services.LanguageClient; -/** - * Standard IO Launcher for K Language Server. - */ +/** Standard IO Launcher for K Language Server. */ public class KLanguageServerLauncher { - public static void main(String[] args) throws InterruptedException, ExecutionException { - startServer(System.in, System.out); - } + public static void main(String[] args) throws InterruptedException, ExecutionException { + startServer(System.in, System.out); + } - /** - * Starts the language server given the input and output streams to read and write messages. - * - * @param in input stream. - * @param out output stream. - * @throws InterruptedException - * @throws ExecutionException - */ - public static void startServer(InputStream in, OutputStream out) throws InterruptedException, ExecutionException { - KLanguageServer server = new KLanguageServer(); - Launcher launcher = Launcher.createLauncher(server, LanguageClient.class, in, out); - LanguageClient client = launcher.getRemoteProxy(); - server.connect(client); - Future startListening = launcher.startListening(); - startListening.get(); - } + /** + * Starts the language server given the input and output streams to read and write messages. + * + * @param in input stream. + * @param out output stream. + * @throws InterruptedException + * @throws ExecutionException + */ + public static void startServer(InputStream in, OutputStream out) + throws InterruptedException, ExecutionException { + KLanguageServer server = new KLanguageServer(); + Launcher launcher = + Launcher.createLauncher(server, LanguageClient.class, in, out); + LanguageClient client = launcher.getRemoteProxy(); + server.connect(client); + Future startListening = launcher.startListening(); + startListening.get(); + } } diff --git a/kernel/src/main/java/org/kframework/lsp/KPos.java b/kernel/src/main/java/org/kframework/lsp/KPos.java index 19561a547d9..942414f8bf6 100644 --- a/kernel/src/main/java/org/kframework/lsp/KPos.java +++ b/kernel/src/main/java/org/kframework/lsp/KPos.java @@ -1,79 +1,71 @@ // Copyright (c) Runtime Verification, Inc. All Rights Reserved. package org.kframework.lsp; -import org.eclipse.lsp4j.Position; - import java.util.Objects; +import org.eclipse.lsp4j.Position; /** - * KPos a position in a file starting from one. - * Use this class when converting positions to aviod off by one errors. + * KPos a position in a file starting from one. Use this class when converting positions to aviod + * off by one errors. */ public class KPos { - /** - * Line position in a document (one-based). - */ - private int line; - - /** - * Character offset on a line in a document (one-based). - */ - private int character; - - public KPos() { - line = 0; - character = 0; - } - - public KPos(int line, int character) { - this.line = line; - this.character = character; - } - - public KPos(Position pos) { - this.line = pos.getLine() + 1; - this.character = pos.getCharacter() + 1; - } - - public Position toPosition() { - return new Position(line - 1, character - 1); - } - - public int getLine() { - return line; - } - - public void setLine(int line) { - this.line = line; - } - - public int getCharacter() { - return character; - } - - public void setCharacter(int character) { - this.character = character; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - KPos kPos = (KPos) o; - return line == kPos.line && character == kPos.character; - } - - @Override - public int hashCode() { - return Objects.hash(line, character); - } - - @Override - public String toString() { - return "KPos{" + - "l:" + line + - ", c:" + character + - '}'; - } + /** Line position in a document (one-based). */ + private int line; + + /** Character offset on a line in a document (one-based). */ + private int character; + + public KPos() { + line = 0; + character = 0; + } + + public KPos(int line, int character) { + this.line = line; + this.character = character; + } + + public KPos(Position pos) { + this.line = pos.getLine() + 1; + this.character = pos.getCharacter() + 1; + } + + public Position toPosition() { + return new Position(line - 1, character - 1); + } + + public int getLine() { + return line; + } + + public void setLine(int line) { + this.line = line; + } + + public int getCharacter() { + return character; + } + + public void setCharacter(int character) { + this.character = character; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + KPos kPos = (KPos) o; + return line == kPos.line && character == kPos.character; + } + + @Override + public int hashCode() { + return Objects.hash(line, character); + } + + @Override + public String toString() { + return "KPos{" + "l:" + line + ", c:" + character + '}'; + } } diff --git a/kernel/src/main/java/org/kframework/lsp/KTextDocument.java b/kernel/src/main/java/org/kframework/lsp/KTextDocument.java index f287b1f80e8..d0163b91176 100644 --- a/kernel/src/main/java/org/kframework/lsp/KTextDocument.java +++ b/kernel/src/main/java/org/kframework/lsp/KTextDocument.java @@ -1,7 +1,10 @@ // Copyright (c) Runtime Verification, Inc. All Rights Reserved. package org.kframework.lsp; - +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.DiagnosticSeverity; import org.eclipse.lsp4j.Range; @@ -12,82 +15,78 @@ import org.kframework.parser.outer.Outer; import org.kframework.utils.errorsystem.KEMException; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Store information about each file. - */ +/** Store information about each file. */ public class KTextDocument { - public String content = ""; - public String uri = ""; - public int[] lines; - public int[] columns; - public boolean linesOutdated = true; - public boolean parsingOutdated = true; - public List problems = new ArrayList<>(); - // definition items provided by outer parsing - public List dis = new ArrayList<>(); - public static final ExtractFencedKCodeFromMarkdown mdExtractor = new ExtractFencedKCodeFromMarkdown(null, "k"); + public String content = ""; + public String uri = ""; + public int[] lines; + public int[] columns; + public boolean linesOutdated = true; + public boolean parsingOutdated = true; + public List problems = new ArrayList<>(); + // definition items provided by outer parsing + public List dis = new ArrayList<>(); + public static final ExtractFencedKCodeFromMarkdown mdExtractor = + new ExtractFencedKCodeFromMarkdown(null, "k"); - public void updateText(String input) { - parsingOutdated = true; - linesOutdated = true; - content = input; - problems.clear(); - } + public void updateText(String input) { + parsingOutdated = true; + linesOutdated = true; + content = input; + problems.clear(); + } - private static final Pattern p = Pattern.compile("(module|endmodule|syntax|context|configuration|rule|claim|import[s]?)"); + private static final Pattern p = + Pattern.compile("(module|endmodule|syntax|context|configuration|rule|claim|import[s]?)"); - // get the last keyword at KPos in order to provide contextual completion - public String getContextAt(KPos pos) { - if (linesOutdated) { - linesOutdated = false; - lines = new int[content.length() + 1]; - columns = new int[content.length() + 1]; - int l = 1; - int c = 1; - for (int offset = 0; offset < content.length(); offset++) { - lines[offset] = l; - columns[offset] = c; - if (content.codePointAt(offset) == '\n') { - l++; - c = 0; - } - c++; - } - lines[content.length()] = l; - columns[content.length()] = c; + // get the last keyword at KPos in order to provide contextual completion + public String getContextAt(KPos pos) { + if (linesOutdated) { + linesOutdated = false; + lines = new int[content.length() + 1]; + columns = new int[content.length() + 1]; + int l = 1; + int c = 1; + for (int offset = 0; offset < content.length(); offset++) { + lines[offset] = l; + columns[offset] = c; + if (content.codePointAt(offset) == '\n') { + l++; + c = 0; } - Matcher m = p.matcher(content); - String context = ""; - while (m.find()) { - if (lines[m.end()] > pos.getLine() || lines[m.end()] == pos.getLine() && columns[m.end()] > pos.getCharacter()) - break; - context = m.group(); - } - return context; + c++; + } + lines[content.length()] = l; + columns[content.length()] = c; + } + Matcher m = p.matcher(content); + String context = ""; + while (m.find()) { + if (lines[m.end()] > pos.getLine() + || lines[m.end()] == pos.getLine() && columns[m.end()] > pos.getCharacter()) break; + context = m.group(); } + return context; + } - public void outerParse() { - if (parsingOutdated) { - parsingOutdated = false; - problems.clear(); - try { - String contents = this.content; - if (uri.endsWith(".md")) - contents = mdExtractor.extract(contents, Source.apply(uri)); - dis = Outer.parse(Source.apply(uri), contents, null); - } catch (KEMException e) { - Location loc = e.exception.getLocation(); - if (loc == null) loc = new Location(1, 1, 1, 2); - Range range = TextDocumentSyncHandler.loc2range(loc); - Diagnostic d = new Diagnostic(range, e.exception.getMessage(), DiagnosticSeverity.Error, "Outer Parser"); - problems.add(d); - } - } + public void outerParse() { + if (parsingOutdated) { + parsingOutdated = false; + problems.clear(); + try { + String contents = this.content; + if (uri.endsWith(".md")) contents = mdExtractor.extract(contents, Source.apply(uri)); + dis = Outer.parse(Source.apply(uri), contents, null); + } catch (KEMException e) { + Location loc = e.exception.getLocation(); + if (loc == null) loc = new Location(1, 1, 1, 2); + Range range = TextDocumentSyncHandler.loc2range(loc); + Diagnostic d = + new Diagnostic( + range, e.exception.getMessage(), DiagnosticSeverity.Error, "Outer Parser"); + problems.add(d); + } } + } } diff --git a/kernel/src/main/java/org/kframework/lsp/KTextDocumentService.java b/kernel/src/main/java/org/kframework/lsp/KTextDocumentService.java index 4d005a8ec27..dca4abad70d 100644 --- a/kernel/src/main/java/org/kframework/lsp/KTextDocumentService.java +++ b/kernel/src/main/java/org/kframework/lsp/KTextDocumentService.java @@ -1,11 +1,6 @@ // Copyright (c) Runtime Verification, Inc. All Rights Reserved. package org.kframework.lsp; -import org.eclipse.lsp4j.*; -import org.eclipse.lsp4j.jsonrpc.messages.Either; -import org.eclipse.lsp4j.services.TextDocumentService; -import org.kframework.kompile.Kompile; - import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; @@ -13,92 +8,119 @@ import java.nio.file.Path; import java.util.List; import java.util.concurrent.CompletableFuture; +import org.eclipse.lsp4j.*; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.eclipse.lsp4j.services.TextDocumentService; +import org.kframework.kompile.Kompile; -/** - * TextDocumentService implementation for K. - */ +/** TextDocumentService implementation for K. */ public class KTextDocumentService implements TextDocumentService { - private final KLanguageServer languageServer; - private final LSClientLogger clientLogger; - - public final TextDocumentSyncHandler memo; - public static final URI domains; - public static final URI kast; - // time delay after which to start doing completion calculation - public static long DELAY_EXECUTION_MS = 1000; - - static { - try { - domains = Path.of(Kompile.BUILTIN_DIRECTORY.toString(), "domains.md").toRealPath(LinkOption.NOFOLLOW_LINKS).toUri(); - kast = Path.of(Kompile.BUILTIN_DIRECTORY.toString(), "kast.md").toRealPath(LinkOption.NOFOLLOW_LINKS).toUri(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - - public KTextDocumentService(KLanguageServer languageServer) throws URISyntaxException { - this.languageServer = languageServer; - this.clientLogger = LSClientLogger.getInstance(); - memo = new TextDocumentSyncHandler(clientLogger, languageServer); - memo.add(domains.toString()); - memo.add(kast.toString()); - this.clientLogger.logMessage("Operation '" + "text/workspaceFolders " + languageServer.workspaceFolders); - } - - @Override - public void didOpen(DidOpenTextDocumentParams didOpenTextDocumentParams) { - this.clientLogger.logMessage("Operation '" + "text/didOpen" + - "' {fileUri: '" + didOpenTextDocumentParams.getTextDocument().getUri() + "'} opened"); - memo.didOpen(didOpenTextDocumentParams); - } - - @Override - public void didChange(DidChangeTextDocumentParams didChangeTextDocumentParams) { - this.clientLogger.logMessage("Operation '" + "text/didChange" + - "' {fileUri: '" + didChangeTextDocumentParams.getTextDocument().getUri() + "'} Changed"); - memo.didChange(didChangeTextDocumentParams); - } - - @Override - public void didClose(DidCloseTextDocumentParams didCloseTextDocumentParams) { - this.clientLogger.logMessage("Operation '" + "text/didClose" + - "' {fileUri: '" + didCloseTextDocumentParams.getTextDocument().getUri() + "'} Closed"); - if (!(didCloseTextDocumentParams.getTextDocument().getUri().equals(domains.toString()) - || didCloseTextDocumentParams.getTextDocument().getUri().equals(kast.toString()))) - memo.didClose(didCloseTextDocumentParams); - } - - @Override - public void didSave(DidSaveTextDocumentParams didSaveTextDocumentParams) { - this.clientLogger.logMessage("Operation '" + "text/didSave" + - "' {fileUri: '" + didSaveTextDocumentParams.getTextDocument().getUri() + "'} Saved"); - memo.didSave(didSaveTextDocumentParams); - } - - @Override - public CompletableFuture, CompletionList>> completion(CompletionParams position) { - return memo.completion(position); - } - - @Override - public CompletableFuture diagnostic(DocumentDiagnosticParams params) { - return memo.diagnostic(params); - } - - @Override - public CompletableFuture, List>> definition(DefinitionParams params) { - return memo.definition(params); - } - - @Override - public CompletableFuture> references(ReferenceParams params) { - return memo.references(params); - } - - @Override - public CompletableFuture> selectionRange(SelectionRangeParams params) { - return memo.selectionRange(params); + private final KLanguageServer languageServer; + private final LSClientLogger clientLogger; + + public final TextDocumentSyncHandler memo; + public static final URI domains; + public static final URI kast; + // time delay after which to start doing completion calculation + public static long DELAY_EXECUTION_MS = 1000; + + static { + try { + domains = + Path.of(Kompile.BUILTIN_DIRECTORY.toString(), "domains.md") + .toRealPath(LinkOption.NOFOLLOW_LINKS) + .toUri(); + kast = + Path.of(Kompile.BUILTIN_DIRECTORY.toString(), "kast.md") + .toRealPath(LinkOption.NOFOLLOW_LINKS) + .toUri(); + } catch (IOException e) { + throw new RuntimeException(e); } + } + + public KTextDocumentService(KLanguageServer languageServer) throws URISyntaxException { + this.languageServer = languageServer; + this.clientLogger = LSClientLogger.getInstance(); + memo = new TextDocumentSyncHandler(clientLogger, languageServer); + memo.add(domains.toString()); + memo.add(kast.toString()); + this.clientLogger.logMessage( + "Operation '" + "text/workspaceFolders " + languageServer.workspaceFolders); + } + + @Override + public void didOpen(DidOpenTextDocumentParams didOpenTextDocumentParams) { + this.clientLogger.logMessage( + "Operation '" + + "text/didOpen" + + "' {fileUri: '" + + didOpenTextDocumentParams.getTextDocument().getUri() + + "'} opened"); + memo.didOpen(didOpenTextDocumentParams); + } + + @Override + public void didChange(DidChangeTextDocumentParams didChangeTextDocumentParams) { + this.clientLogger.logMessage( + "Operation '" + + "text/didChange" + + "' {fileUri: '" + + didChangeTextDocumentParams.getTextDocument().getUri() + + "'} Changed"); + memo.didChange(didChangeTextDocumentParams); + } + + @Override + public void didClose(DidCloseTextDocumentParams didCloseTextDocumentParams) { + this.clientLogger.logMessage( + "Operation '" + + "text/didClose" + + "' {fileUri: '" + + didCloseTextDocumentParams.getTextDocument().getUri() + + "'} Closed"); + if (!(didCloseTextDocumentParams.getTextDocument().getUri().equals(domains.toString()) + || didCloseTextDocumentParams.getTextDocument().getUri().equals(kast.toString()))) + memo.didClose(didCloseTextDocumentParams); + } + + @Override + public void didSave(DidSaveTextDocumentParams didSaveTextDocumentParams) { + this.clientLogger.logMessage( + "Operation '" + + "text/didSave" + + "' {fileUri: '" + + didSaveTextDocumentParams.getTextDocument().getUri() + + "'} Saved"); + memo.didSave(didSaveTextDocumentParams); + } + + @Override + public CompletableFuture, CompletionList>> completion( + CompletionParams position) { + return memo.completion(position); + } + + @Override + public CompletableFuture diagnostic(DocumentDiagnosticParams params) { + return memo.diagnostic(params); + } + + @Override + public CompletableFuture< + Either, List>> + definition(DefinitionParams params) { + return memo.definition(params); + } + + @Override + public CompletableFuture> references(ReferenceParams params) { + return memo.references(params); + } + + @Override + public CompletableFuture> selectionRange(SelectionRangeParams params) { + return memo.selectionRange(params); + } } diff --git a/kernel/src/main/java/org/kframework/lsp/KWorkspaceService.java b/kernel/src/main/java/org/kframework/lsp/KWorkspaceService.java index 7e0852a1362..93e05d10ca6 100644 --- a/kernel/src/main/java/org/kframework/lsp/KWorkspaceService.java +++ b/kernel/src/main/java/org/kframework/lsp/KWorkspaceService.java @@ -6,34 +6,32 @@ import org.eclipse.lsp4j.RenameFilesParams; import org.eclipse.lsp4j.services.WorkspaceService; -/** - * WorkspaceService implementation for K. - */ +/** WorkspaceService implementation for K. */ public class KWorkspaceService implements WorkspaceService { - private final KLanguageServer languageServer; - LSClientLogger clientLogger; + private final KLanguageServer languageServer; + LSClientLogger clientLogger; - public KWorkspaceService(KLanguageServer languageServer) { - this.languageServer = languageServer; - this.clientLogger = LSClientLogger.getInstance(); - } + public KWorkspaceService(KLanguageServer languageServer) { + this.languageServer = languageServer; + this.clientLogger = LSClientLogger.getInstance(); + } - @Override - public void didChangeConfiguration(DidChangeConfigurationParams didChangeConfigurationParams) { - this.clientLogger.logMessage("Operation 'workspace/didChangeConfiguration' Ack"); - } + @Override + public void didChangeConfiguration(DidChangeConfigurationParams didChangeConfigurationParams) { + this.clientLogger.logMessage("Operation 'workspace/didChangeConfiguration' Ack"); + } - @Override - public void didChangeWatchedFiles(DidChangeWatchedFilesParams didChangeWatchedFilesParams) { - this.clientLogger.logMessage("Operation 'workspace/didChangeWatchedFiles' Ack"); - KTextDocumentService ktxt = (KTextDocumentService) languageServer.getTextDocumentService(); - ktxt.memo.loadCaches(); - languageServer.languageClient.refreshDiagnostics(); - } + @Override + public void didChangeWatchedFiles(DidChangeWatchedFilesParams didChangeWatchedFilesParams) { + this.clientLogger.logMessage("Operation 'workspace/didChangeWatchedFiles' Ack"); + KTextDocumentService ktxt = (KTextDocumentService) languageServer.getTextDocumentService(); + ktxt.memo.loadCaches(); + languageServer.languageClient.refreshDiagnostics(); + } - @Override - public void didRenameFiles(RenameFilesParams params) { - this.clientLogger.logMessage("Operation 'workspace/didRenameFiles' Ack"); - } + @Override + public void didRenameFiles(RenameFilesParams params) { + this.clientLogger.logMessage("Operation 'workspace/didRenameFiles' Ack"); + } } diff --git a/kernel/src/main/java/org/kframework/lsp/LSClientLogger.java b/kernel/src/main/java/org/kframework/lsp/LSClientLogger.java index 41f3377541d..9f6f06d2847 100644 --- a/kernel/src/main/java/org/kframework/lsp/LSClientLogger.java +++ b/kernel/src/main/java/org/kframework/lsp/LSClientLogger.java @@ -5,36 +5,33 @@ import org.eclipse.lsp4j.MessageType; import org.eclipse.lsp4j.services.LanguageClient; -/** - * Use this class to send log messages to the client. - */ +/** Use this class to send log messages to the client. */ public class LSClientLogger { - private static LSClientLogger INSTANCE; - private LanguageClient client; - private boolean isInitialized; + private static LSClientLogger INSTANCE; + private LanguageClient client; + private boolean isInitialized; - private LSClientLogger() { - } + private LSClientLogger() {} - public void initialize(LanguageClient languageClient) { - if (!Boolean.TRUE.equals(isInitialized)) { - this.client = languageClient; - } - isInitialized = true; + public void initialize(LanguageClient languageClient) { + if (!Boolean.TRUE.equals(isInitialized)) { + this.client = languageClient; } + isInitialized = true; + } - public static LSClientLogger getInstance() { - if (INSTANCE == null) { - INSTANCE = new LSClientLogger(); - } - return INSTANCE; + public static LSClientLogger getInstance() { + if (INSTANCE == null) { + INSTANCE = new LSClientLogger(); } + return INSTANCE; + } - public void logMessage(String message) { - if (!isInitialized) { - return; - } - client.logMessage(new MessageParams(MessageType.Info, message)); + public void logMessage(String message) { + if (!isInitialized) { + return; } + client.logMessage(new MessageParams(MessageType.Info, message)); + } } diff --git a/kernel/src/main/java/org/kframework/lsp/TextDocumentSyncHandler.java b/kernel/src/main/java/org/kframework/lsp/TextDocumentSyncHandler.java index d204107e46a..810ba699368 100644 --- a/kernel/src/main/java/org/kframework/lsp/TextDocumentSyncHandler.java +++ b/kernel/src/main/java/org/kframework/lsp/TextDocumentSyncHandler.java @@ -1,15 +1,30 @@ // Copyright (c) Runtime Verification, Inc. All Rights Reserved. package org.kframework.lsp; +import static org.kframework.kompile.Kompile.CACHE_FILE_NAME; +import static org.kframework.lsp.CompletionHelper.*; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; import org.eclipse.lsp4j.*; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.kframework.attributes.Att; import org.kframework.attributes.Source; import org.kframework.definition.Bubble; import org.kframework.definition.KViz; -import org.kframework.kil.Module; import org.kframework.kil.*; +import org.kframework.kil.Module; import org.kframework.kompile.DefinitionParsing; import org.kframework.kore.K; import org.kframework.main.GlobalOptions; @@ -19,501 +34,771 @@ import org.kframework.utils.errorsystem.KExceptionManager; import org.kframework.utils.file.FileUtil; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.Path; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; - -import static org.kframework.kompile.Kompile.CACHE_FILE_NAME; -import static org.kframework.lsp.CompletionHelper.*; - -/** - * Handle the caches of all the files of interest. - */ +/** Handle the caches of all the files of interest. */ public class TextDocumentSyncHandler { - public Map files = new HashMap<>(); - public Map caches = new HashMap<>(); - private final LSClientLogger clientLogger; - private final KLanguageServer kls; - private WorkspaceFolder workspaceFolder; - private Optional cacheFile = Optional.empty(); - - private static final BinaryLoader loader = new BinaryLoader(new KExceptionManager(new GlobalOptions())); - - - public TextDocumentSyncHandler(LSClientLogger clientLogger, KLanguageServer kls) { - this.clientLogger = clientLogger; - this.kls = kls; - } - - public void findCacheFile() { - if (workspaceFolder != null || kls.workspaceFolders == null) - return; - try { - workspaceFolder = kls.workspaceFolders.get(0); - // TODO: find a better way to get the kompiled directory - maybe with the toml file from KBuild - cacheFile = Files.walk(Path.of(URI.create(workspaceFolder.getUri()))) - .filter(p -> p.endsWith(CACHE_FILE_NAME)).findFirst(); - loadCaches(); - } catch (IOException e) { - clientLogger.logMessage("findCachesException: " + e); - } + public Map files = new HashMap<>(); + public Map caches = new HashMap<>(); + private final LSClientLogger clientLogger; + private final KLanguageServer kls; + private WorkspaceFolder workspaceFolder; + private Optional cacheFile = Optional.empty(); + + private static final BinaryLoader loader = + new BinaryLoader(new KExceptionManager(new GlobalOptions())); + + public TextDocumentSyncHandler(LSClientLogger clientLogger, KLanguageServer kls) { + this.clientLogger = clientLogger; + this.kls = kls; + } + + public void findCacheFile() { + if (workspaceFolder != null || kls.workspaceFolders == null) return; + try { + workspaceFolder = kls.workspaceFolders.get(0); + // TODO: find a better way to get the kompiled directory - maybe with the toml file from + // KBuild + cacheFile = + Files.walk(Path.of(URI.create(workspaceFolder.getUri()))) + .filter(p -> p.endsWith(CACHE_FILE_NAME)) + .findFirst(); + loadCaches(); + } catch (IOException e) { + clientLogger.logMessage("findCachesException: " + e); } - - public void loadCaches() { - cacheFile.ifPresent(path -> caches = loader.loadCache(java.util.Map.class, path.toFile())); - if (caches == null) - caches = new HashMap<>(); - caches.forEach((key, val) -> { - String uri = Path.of(val.module().att().get(org.kframework.attributes.Source.class).source()).toUri().toString(); - // load into LSP all the files found in the caches, even if they are not open in the IDE. - // this way we can find all the updated locations when finding references - if (!files.containsKey(uri)) - add(uri); + } + + public void loadCaches() { + cacheFile.ifPresent(path -> caches = loader.loadCache(java.util.Map.class, path.toFile())); + if (caches == null) caches = new HashMap<>(); + caches.forEach( + (key, val) -> { + String uri = + Path.of(val.module().att().get(org.kframework.attributes.Source.class).source()) + .toUri() + .toString(); + // load into LSP all the files found in the caches, even if they are not open in the IDE. + // this way we can find all the updated locations when finding references + if (!files.containsKey(uri)) add(uri); }); - clientLogger.logMessage("loaded cached modules: " + caches.size()); - } - - public void add(String uri) { - try { - KTextDocument doc = new KTextDocument(); - files.put(uri, doc); - doc.uri = uri; - doc.updateText(FileUtil.load(new File(new URI(uri)))); - doc.outerParse(); - } catch (URISyntaxException e) { - clientLogger.logMessage(TextDocumentSyncHandler.class + ".addUri failed: " + uri); - } - } - - public void didOpen(DidOpenTextDocumentParams params) { - String uri = params.getTextDocument().getUri(); - KTextDocument doc; - if (files.containsKey(uri)) - doc = files.get(uri); - else { - doc = new KTextDocument(); - doc.uri = uri; - files.put(uri, doc); - } - doc.updateText(params.getTextDocument().getText()); - } - - public void didChange(DidChangeTextDocumentParams params) { - files.get(params.getTextDocument().getUri()).updateText(params.getContentChanges().get(0).getText()); - } - - public void didSave(DidSaveTextDocumentParams params) { - KTextDocument doc = files.get(params.getTextDocument().getUri()); - doc.outerParse(); + clientLogger.logMessage("loaded cached modules: " + caches.size()); + } + + public void add(String uri) { + try { + KTextDocument doc = new KTextDocument(); + files.put(uri, doc); + doc.uri = uri; + doc.updateText(FileUtil.load(new File(new URI(uri)))); + doc.outerParse(); + } catch (URISyntaxException e) { + clientLogger.logMessage(TextDocumentSyncHandler.class + ".addUri failed: " + uri); } - - public void didClose(DidCloseTextDocumentParams params) { - // do not remove files from memory - //files.remove(params.getTextDocument().getUri()); + } + + public void didOpen(DidOpenTextDocumentParams params) { + String uri = params.getTextDocument().getUri(); + KTextDocument doc; + if (files.containsKey(uri)) doc = files.get(uri); + else { + doc = new KTextDocument(); + doc.uri = uri; + files.put(uri, doc); } - - // recurse through all the required files and return the list of DefinitionItems - public List slurp(String uri) { - Set visited = new HashSet<>(); - visited.add(KTextDocumentService.domains.toString()); - visited.add(KTextDocumentService.kast.toString()); - slurp2(uri, visited); - List dis = files.entrySet().stream() - .filter(e -> visited.contains(e.getKey())) - .flatMap(e -> e.getValue().dis.stream()) - .collect(Collectors.toList()); - return dis; + doc.updateText(params.getTextDocument().getText()); + } + + public void didChange(DidChangeTextDocumentParams params) { + files + .get(params.getTextDocument().getUri()) + .updateText(params.getContentChanges().get(0).getText()); + } + + public void didSave(DidSaveTextDocumentParams params) { + KTextDocument doc = files.get(params.getTextDocument().getUri()); + doc.outerParse(); + } + + public void didClose(DidCloseTextDocumentParams params) { + // do not remove files from memory + // files.remove(params.getTextDocument().getUri()); + } + + // recurse through all the required files and return the list of DefinitionItems + public List slurp(String uri) { + Set visited = new HashSet<>(); + visited.add(KTextDocumentService.domains.toString()); + visited.add(KTextDocumentService.kast.toString()); + slurp2(uri, visited); + List dis = + files.entrySet().stream() + .filter(e -> visited.contains(e.getKey())) + .flatMap(e -> e.getValue().dis.stream()) + .collect(Collectors.toList()); + return dis; + } + + private void slurp2(String uri, Set visited) { + if (!files.containsKey(uri)) { + try { + if (new File(new URI(uri)).exists()) add(uri); + } catch (URISyntaxException e) { + clientLogger.logMessage( + TextDocumentSyncHandler.class + ".slurp failed1: " + uri + "\n" + e.getMessage()); + } } - - private void slurp2(String uri, Set visited) { - if (!files.containsKey(uri)) { - try { - if (new File(new URI(uri)).exists()) - add(uri); - } catch (URISyntaxException e) { - clientLogger.logMessage(TextDocumentSyncHandler.class + ".slurp failed1: " + uri + "\n" + e.getMessage()); - } - } - KTextDocument current = files.get(uri); - if (current.parsingOutdated) - current.outerParse(); - if (!visited.contains(uri) && files.containsKey(uri)) { - visited.add(uri); - files.get(uri).dis.forEach(r -> { + KTextDocument current = files.get(uri); + if (current.parsingOutdated) current.outerParse(); + if (!visited.contains(uri) && files.containsKey(uri)) { + visited.add(uri); + files + .get(uri) + .dis + .forEach( + r -> { if (r instanceof Require) { - try { - Require req = (Require) r; - URI targetURI = Path.of(new File(new URI(uri)).getParent(), req.getValue()).toRealPath(LinkOption.NOFOLLOW_LINKS).toUri(); - slurp2(targetURI.toString(), visited); - } catch (IOException | URISyntaxException e) { - clientLogger.logMessage(TextDocumentSyncHandler.class + ".slurp failed2: " + uri + "\n" + e); - } + try { + Require req = (Require) r; + URI targetURI = + Path.of(new File(new URI(uri)).getParent(), req.getValue()) + .toRealPath(LinkOption.NOFOLLOW_LINKS) + .toUri(); + slurp2(targetURI.toString(), visited); + } catch (IOException | URISyntaxException e) { + clientLogger.logMessage( + TextDocumentSyncHandler.class + ".slurp failed2: " + uri + "\n" + e); + } } - }); - } + }); } - - // Create a list of CompletionItems depending on the context. - // A work in progress file may not parse, but we still want to have completion working, therefore we use a regex - // to find the closest keyword, and approximate the context. - public CompletableFuture, CompletionList>> completion(CompletionParams position) { - return CompletableFuture.supplyAsync(() -> { - KPos pos = new KPos(position.getPosition()); - List lci = new ArrayList<>(); - KTextDocument doc = files.get(position.getTextDocument().getUri()); - String context = doc.getContextAt(pos); - List allDi = slurp(position.getTextDocument().getUri()); - switch (context) { + } + + // Create a list of CompletionItems depending on the context. + // A work in progress file may not parse, but we still want to have completion working, therefore + // we use a regex + // to find the closest keyword, and approximate the context. + public CompletableFuture, CompletionList>> completion( + CompletionParams position) { + return CompletableFuture.supplyAsync( + () -> { + KPos pos = new KPos(position.getPosition()); + List lci = new ArrayList<>(); + KTextDocument doc = files.get(position.getTextDocument().getUri()); + String context = doc.getContextAt(pos); + List allDi = slurp(position.getTextDocument().getUri()); + switch (context) { case "" -> { - lci.add(getNewRequiresCompletion()); - lci.add(getNewModuleCompletion()); + lci.add(getNewRequiresCompletion()); + lci.add(getNewModuleCompletion()); } case "endmodule" -> lci.add(getNewModuleCompletion()); case "module" -> { - lci.add(getNewImportCompletion()); - lci.addAll(getNewSentenceCompletion()); + lci.add(getNewImportCompletion()); + lci.addAll(getNewSentenceCompletion()); } case "import", "imports" -> { - lci.add(getNewImportCompletion()); - lci.addAll(getNewSentenceCompletion()); - lci.addAll(getImportCompletion(allDi)); + lci.add(getNewImportCompletion()); + lci.addAll(getNewSentenceCompletion()); + lci.addAll(getImportCompletion(allDi)); } case "syntax" -> { - lci.addAll(getNewSentenceCompletion()); - lci.addAll(getSyntaxCompletion(allDi)); + lci.addAll(getNewSentenceCompletion()); + lci.addAll(getSyntaxCompletion(allDi)); } case "context", "rule", "configuration", "claim" -> { - lci.addAll(getNewSentenceCompletion()); - lci.addAll(getRuleCompletion(allDi)); + lci.addAll(getNewSentenceCompletion()); + lci.addAll(getRuleCompletion(allDi)); } - } - this.clientLogger.logMessage("Operation '" + "text/completion: " + position.getTextDocument().getUri() + " #pos: " - + pos.getLine() + " " + pos.getCharacter() + " context: " + context + " #: " + lci.size()); - - // TODO: add completion for attributes - return Either.forLeft(lci); + } + this.clientLogger.logMessage( + "Operation '" + + "text/completion: " + + position.getTextDocument().getUri() + + " #pos: " + + pos.getLine() + + " " + + pos.getCharacter() + + " context: " + + context + + " #: " + + lci.size()); + + // TODO: add completion for attributes + return Either.forLeft(lci); }); - } - - // ctrl+click - goto definition for requires, imports terms inside rules - public CompletableFuture, List>> definition(DefinitionParams params) { - findCacheFile(); - KPos pos = new KPos(params.getPosition()); - return CompletableFuture.supplyAsync(() -> { - this.clientLogger.logMessage("Operation '" + "text/definition: " + params.getTextDocument().getUri() + " #pos: " + pos.getLine() + " " + pos.getCharacter()); - List lls = new ArrayList<>(); - try { - List dis = files.get(params.getTextDocument().getUri()).dis; - for (DefinitionItem di : dis) { - if (di instanceof Require) { // goto required file - org.kframework.attributes.Location loc = getSafeLoc(di); - if (isPositionOverLocation(pos, loc)) { - Require req = (Require) di; - File f = new File(new URI(params.getTextDocument().getUri())); - URI targetURI = Path.of(f.getParent(), req.getValue()).toRealPath(LinkOption.NOFOLLOW_LINKS).toUri(); - lls.add(new LocationLink(targetURI.toString(), - new Range(new Position(0, 0), new Position(10, 0)), - new Range(new Position(0, 0), new Position(0, 0)), - loc2range(loc))); - break; - } - } else if (di instanceof Module m) { - for (ModuleItem mi : m.getItems()) { - org.kframework.attributes.Location loc = getSafeLoc(mi); - if (isPositionOverLocation(pos, loc)) { - if (mi instanceof Import imp) { // goto module definition - List allDi = slurp(params.getTextDocument().getUri()); - allDi.stream().filter(ddi -> ddi instanceof Module) - .map(ddi -> ((Module) ddi)) - .filter(m2 -> m2.getName().equals(imp.getName())) - .forEach(m3 -> lls.add(new LocationLink(URI.create(m3.getSource().source()).toString(), - loc2range(getSafeLoc(m3)), - loc2range(getSafeLoc(m3)), - loc2range(getSafeLoc(imp))))); - } else if (mi instanceof StringSentence ss) { // goto syntax of term inside rule - String suffix = ss.getType().equals(DefinitionParsing.configuration) ? "-" + RuleGrammarGenerator.CONFIG_CELLS : "-" + RuleGrammarGenerator.RULE_CELLS; - Optional> ch = caches.entrySet().stream().filter(elm -> elm.getKey().startsWith(m.getName() + suffix)).findFirst(); - if (ch.isPresent()) { - Map parsedSent = ch.get().getValue().cache(); - if (parsedSent.containsKey(ss.getContent())) { - Bubble b = new Bubble(ss.getType(), ss.getContent(), ss.getAttributes() - .add(org.kframework.attributes.Location.class, ss.getLocation()).add(Source.class, ss.getSource()) - .add(Att.CONTENT_START_LINE(), ss.getContentStartLine()).add(Att.CONTENT_START_COLUMN(), ss.getContentStartColumn())); - ParseCache.ParsedSentence parse = DefinitionParsing.updateLocation(parsedSent.get(ss.getContent()), b); - AtomicReference x = new AtomicReference<>(); - KViz.from(t -> { - if (isPositionOverLocation(pos, t.location().get())) - x.set(t); // find the deepest term that contains this position - return t; - }, "Find def in rule").apply(parse.parse()); - if (x.get() != null && x.get().att().get(org.kframework.definition.Production.class).source().isPresent()) { - org.kframework.definition.Production prd = x.get().att().get(org.kframework.definition.Production.class); - if (prd.source().isPresent()) // exclude generated productions like casts - lls.add(new LocationLink(URI.create(prd.source().get().source()).toString(), - loc2range(prd.location().get()), - loc2range(prd.location().get()), - loc2range(x.get().att().get(org.kframework.attributes.Location.class)))); - } else - clientLogger.logMessage("definition failed no origin for prod: " + (x.get() != null ? x.get().att().get(org.kframework.definition.Production.class) : null)); - - } - } else { - kls.languageClient.showMessage(new MessageParams(MessageType.Error, "No caches found for this sentence. 'Go to definition' inside rules is only available in workspace mode and requires a kompiled definition.")); - } - } - break; - } + } + + // ctrl+click - goto definition for requires, imports terms inside rules + public CompletableFuture< + Either, List>> + definition(DefinitionParams params) { + findCacheFile(); + KPos pos = new KPos(params.getPosition()); + return CompletableFuture.supplyAsync( + () -> { + this.clientLogger.logMessage( + "Operation '" + + "text/definition: " + + params.getTextDocument().getUri() + + " #pos: " + + pos.getLine() + + " " + + pos.getCharacter()); + List lls = new ArrayList<>(); + try { + List dis = files.get(params.getTextDocument().getUri()).dis; + for (DefinitionItem di : dis) { + if (di instanceof Require) { // goto required file + org.kframework.attributes.Location loc = getSafeLoc(di); + if (isPositionOverLocation(pos, loc)) { + Require req = (Require) di; + File f = new File(new URI(params.getTextDocument().getUri())); + URI targetURI = + Path.of(f.getParent(), req.getValue()) + .toRealPath(LinkOption.NOFOLLOW_LINKS) + .toUri(); + lls.add( + new LocationLink( + targetURI.toString(), + new Range(new Position(0, 0), new Position(10, 0)), + new Range(new Position(0, 0), new Position(0, 0)), + loc2range(loc))); + break; + } + } else if (di instanceof Module m) { + for (ModuleItem mi : m.getItems()) { + org.kframework.attributes.Location loc = getSafeLoc(mi); + if (isPositionOverLocation(pos, loc)) { + if (mi instanceof Import imp) { // goto module definition + List allDi = slurp(params.getTextDocument().getUri()); + allDi.stream() + .filter(ddi -> ddi instanceof Module) + .map(ddi -> ((Module) ddi)) + .filter(m2 -> m2.getName().equals(imp.getName())) + .forEach( + m3 -> + lls.add( + new LocationLink( + URI.create(m3.getSource().source()).toString(), + loc2range(getSafeLoc(m3)), + loc2range(getSafeLoc(m3)), + loc2range(getSafeLoc(imp))))); + } else if (mi instanceof StringSentence ss) { // goto syntax of term inside rule + String suffix = + ss.getType().equals(DefinitionParsing.configuration) + ? "-" + RuleGrammarGenerator.CONFIG_CELLS + : "-" + RuleGrammarGenerator.RULE_CELLS; + Optional> ch = + caches.entrySet().stream() + .filter(elm -> elm.getKey().startsWith(m.getName() + suffix)) + .findFirst(); + if (ch.isPresent()) { + Map parsedSent = + ch.get().getValue().cache(); + if (parsedSent.containsKey(ss.getContent())) { + Bubble b = + new Bubble( + ss.getType(), + ss.getContent(), + ss.getAttributes() + .add( + org.kframework.attributes.Location.class, + ss.getLocation()) + .add(Source.class, ss.getSource()) + .add(Att.CONTENT_START_LINE(), ss.getContentStartLine()) + .add(Att.CONTENT_START_COLUMN(), ss.getContentStartColumn())); + ParseCache.ParsedSentence parse = + DefinitionParsing.updateLocation(parsedSent.get(ss.getContent()), b); + AtomicReference x = new AtomicReference<>(); + KViz.from( + t -> { + if (isPositionOverLocation(pos, t.location().get())) + x.set(t); // find the deepest term that contains this position + return t; + }, + "Find def in rule") + .apply(parse.parse()); + if (x.get() != null + && x.get() + .att() + .get(org.kframework.definition.Production.class) + .source() + .isPresent()) { + org.kframework.definition.Production prd = + x.get().att().get(org.kframework.definition.Production.class); + if (prd.source() + .isPresent()) // exclude generated productions like casts + lls.add( + new LocationLink( + URI.create(prd.source().get().source()).toString(), + loc2range(prd.location().get()), + loc2range(prd.location().get()), + loc2range( + x.get() + .att() + .get(org.kframework.attributes.Location.class)))); + } else + clientLogger.logMessage( + "definition failed no origin for prod: " + + (x.get() != null + ? x.get() + .att() + .get(org.kframework.definition.Production.class) + : null)); } + } else { + kls.languageClient.showMessage( + new MessageParams( + MessageType.Error, + "No caches found for this sentence. 'Go to definition' inside rules" + + " is only available in workspace mode and requires a kompiled" + + " definition.")); + } } + break; + } } - } catch (URISyntaxException | IOException e) { - clientLogger.logMessage("definition failed: " + params.getTextDocument().getUri() + e); + } } + } catch (URISyntaxException | IOException e) { + clientLogger.logMessage("definition failed: " + params.getTextDocument().getUri() + e); + } - return Either.forRight(lls); + return Either.forRight(lls); }); - } - - // in case a node doesn't have a location information, return Location(0,0,0,0) - public static org.kframework.attributes.Location getSafeLoc(ASTNode node) { - return node.location().orElse(new org.kframework.attributes.Location(0,0,0,0)); - } - - // true if Position(l,c) is over Location(startL, startC, endL, endC). Expects Position to start from 1, just as Location - public static boolean isPositionOverLocation(KPos pos, org.kframework.attributes.Location loc) { - return (loc.startLine() < pos.getLine() || (loc.startLine() == pos.getLine() && loc.startColumn() <= pos.getCharacter())) && - (pos.getLine() < loc.endLine() || (pos.getLine() == loc.endLine() && pos.getCharacter() <= loc.endColumn())); - } - - // K starts counting lines and columns from 1 and LSP starts counting from 0 - public static Range loc2range(org.kframework.attributes.Location loc) { - return new Range(new Position(loc.startLine() - 1 , loc.startColumn() - 1), new Position(loc.endLine() - 1, loc.endColumn() - 1)); - } - - // previous diagnostics task. If it's still active, cancel it and run a newer, updated one - private final Map> latestDiagnosticScheduled = new HashMap<>(); - - public CompletableFuture diagnostic(DocumentDiagnosticParams params) { - findCacheFile(); - String uri = params.getTextDocument().getUri(); - if (latestDiagnosticScheduled.containsKey(uri) && !latestDiagnosticScheduled.get(uri).isDone()) - latestDiagnosticScheduled.get(uri).completeExceptionally(new Throwable("Cancelled diagnostic publisher")); - - Executor delayedExecutor = CompletableFuture.delayedExecutor(KTextDocumentService.DELAY_EXECUTION_MS, TimeUnit.MILLISECONDS); - CompletableFuture scheduledFuture = CompletableFuture.supplyAsync(() -> { - // make sure the out syntax is up to date - files.get(params.getTextDocument().getUri()).outerParse(); - List problems = files.get(params.getTextDocument().getUri()).problems; - // for each bubble in the current file - // search for a cached parsed version and collect reported errors - files.get(params.getTextDocument().getUri()).dis.stream().filter(di -> di instanceof Module).map(di -> (Module) di) - .forEach(m -> m.getItems().stream().filter(mi -> mi instanceof StringSentence).map(mi -> (StringSentence) mi) - .forEach(ss -> { - String suffix = ss.getType().equals(DefinitionParsing.configuration) ? "-" + RuleGrammarGenerator.CONFIG_CELLS : "-" + RuleGrammarGenerator.RULE_CELLS; - Optional> ch = caches.entrySet().stream().filter(elm -> elm.getKey().startsWith(m.getName() + suffix)).findFirst(); - if (ch.isPresent()) { - ParseCache parseCache = ch.get().getValue(); - Map parsedSent = parseCache.cache(); - if (parsedSent.containsKey(ss.getContent())) { - Bubble b = new Bubble(ss.getType(), ss.getContent(), ss.getAttributes() - .add(org.kframework.attributes.Location.class, ss.getLocation()).add(Source.class, ss.getSource()) - .add(Att.CONTENT_START_LINE(), ss.getContentStartLine()).add(Att.CONTENT_START_COLUMN(), ss.getContentStartColumn())); - ParseCache.ParsedSentence parse = DefinitionParsing.updateLocation(parsedSent.get(b.contents()), b); - parse.errors().forEach(err -> { - Diagnostic d = new Diagnostic(loc2range(err.exception.getLocation()), err.exception.getMessage(), DiagnosticSeverity.Error, "Inner Parser"); - problems.add(d); - }); - parse.warnings().forEach(err -> { - Diagnostic d = new Diagnostic(loc2range(err.exception.getLocation()), err.exception.getMessage(), DiagnosticSeverity.Warning, "Inner Parser"); - problems.add(d); - }); + } + + // in case a node doesn't have a location information, return Location(0,0,0,0) + public static org.kframework.attributes.Location getSafeLoc(ASTNode node) { + return node.location().orElse(new org.kframework.attributes.Location(0, 0, 0, 0)); + } + + // true if Position(l,c) is over Location(startL, startC, endL, endC). Expects Position to start + // from 1, just as Location + public static boolean isPositionOverLocation(KPos pos, org.kframework.attributes.Location loc) { + return (loc.startLine() < pos.getLine() + || (loc.startLine() == pos.getLine() && loc.startColumn() <= pos.getCharacter())) + && (pos.getLine() < loc.endLine() + || (pos.getLine() == loc.endLine() && pos.getCharacter() <= loc.endColumn())); + } + + // K starts counting lines and columns from 1 and LSP starts counting from 0 + public static Range loc2range(org.kframework.attributes.Location loc) { + return new Range( + new Position(loc.startLine() - 1, loc.startColumn() - 1), + new Position(loc.endLine() - 1, loc.endColumn() - 1)); + } + + // previous diagnostics task. If it's still active, cancel it and run a newer, updated one + private final Map> latestDiagnosticScheduled = + new HashMap<>(); + + public CompletableFuture diagnostic(DocumentDiagnosticParams params) { + findCacheFile(); + String uri = params.getTextDocument().getUri(); + if (latestDiagnosticScheduled.containsKey(uri) && !latestDiagnosticScheduled.get(uri).isDone()) + latestDiagnosticScheduled + .get(uri) + .completeExceptionally(new Throwable("Cancelled diagnostic publisher")); + + Executor delayedExecutor = + CompletableFuture.delayedExecutor( + KTextDocumentService.DELAY_EXECUTION_MS, TimeUnit.MILLISECONDS); + CompletableFuture scheduledFuture = + CompletableFuture.supplyAsync( + () -> { + // make sure the out syntax is up to date + files.get(params.getTextDocument().getUri()).outerParse(); + List problems = files.get(params.getTextDocument().getUri()).problems; + // for each bubble in the current file + // search for a cached parsed version and collect reported errors + files.get(params.getTextDocument().getUri()).dis.stream() + .filter(di -> di instanceof Module) + .map(di -> (Module) di) + .forEach( + m -> + m.getItems().stream() + .filter(mi -> mi instanceof StringSentence) + .map(mi -> (StringSentence) mi) + .forEach( + ss -> { + String suffix = + ss.getType().equals(DefinitionParsing.configuration) + ? "-" + RuleGrammarGenerator.CONFIG_CELLS + : "-" + RuleGrammarGenerator.RULE_CELLS; + Optional> ch = + caches.entrySet().stream() + .filter( + elm -> + elm.getKey().startsWith(m.getName() + suffix)) + .findFirst(); + if (ch.isPresent()) { + ParseCache parseCache = ch.get().getValue(); + Map parsedSent = + parseCache.cache(); + if (parsedSent.containsKey(ss.getContent())) { + Bubble b = + new Bubble( + ss.getType(), + ss.getContent(), + ss.getAttributes() + .add( + org.kframework.attributes.Location.class, + ss.getLocation()) + .add(Source.class, ss.getSource()) + .add( + Att.CONTENT_START_LINE(), + ss.getContentStartLine()) + .add( + Att.CONTENT_START_COLUMN(), + ss.getContentStartColumn())); + ParseCache.ParsedSentence parse = + DefinitionParsing.updateLocation( + parsedSent.get(b.contents()), b); + parse + .errors() + .forEach( + err -> { + Diagnostic d = + new Diagnostic( + loc2range(err.exception.getLocation()), + err.exception.getMessage(), + DiagnosticSeverity.Error, + "Inner Parser"); + problems.add(d); + }); + parse + .warnings() + .forEach( + err -> { + Diagnostic d = + new Diagnostic( + loc2range(err.exception.getLocation()), + err.exception.getMessage(), + DiagnosticSeverity.Warning, + "Inner Parser"); + problems.add(d); + }); + } } - } - })); - - DocumentDiagnosticReport report = new DocumentDiagnosticReport(new RelatedFullDocumentDiagnosticReport(problems)); - this.clientLogger.logMessage("Operation '" + "text/diagnostics: " + params.getTextDocument().getUri() + " #problems: " + problems.size()); - return report; - }, delayedExecutor); - latestDiagnosticScheduled.put(uri, scheduledFuture); - return scheduledFuture; - } - - // find references of modules being used in imports and syntax used in rules - public CompletableFuture> references(ReferenceParams params) { - return CompletableFuture.supplyAsync(() -> { - KPos pos = new KPos(params.getPosition()); - List lloc = new ArrayList<>(); - - // look in the current file and find the term under KPos - List dis = files.get(params.getTextDocument().getUri()).dis; - for (DefinitionItem di : dis) { - org.kframework.attributes.Location loc = getSafeLoc(di); - if (isPositionOverLocation(pos, loc)) { - Module m = (Module) di; - // want to activate when over `module NAME` and nothing else but, - // we don't store the exact position for the module name so try to calculate it - String name = m.getName(); - org.kframework.attributes.Location nameLoc = new org.kframework.attributes.Location( - loc.startLine(), loc.startColumn(), loc.startLine(), loc.startColumn() + "module ".length() + name.length()); - - if (isPositionOverLocation(pos, nameLoc)) { - List allDi = files.values().stream() - .flatMap(doc -> doc.dis.stream()).collect(Collectors.toList()); - allDi.stream().filter(ddi -> ddi instanceof Module) - .map(ddi -> ((Module) ddi)) - .forEach(m3 -> m3.getItems().stream() + })); + + DocumentDiagnosticReport report = + new DocumentDiagnosticReport(new RelatedFullDocumentDiagnosticReport(problems)); + this.clientLogger.logMessage( + "Operation '" + + "text/diagnostics: " + + params.getTextDocument().getUri() + + " #problems: " + + problems.size()); + return report; + }, + delayedExecutor); + latestDiagnosticScheduled.put(uri, scheduledFuture); + return scheduledFuture; + } + + // find references of modules being used in imports and syntax used in rules + public CompletableFuture> references(ReferenceParams params) { + return CompletableFuture.supplyAsync( + () -> { + KPos pos = new KPos(params.getPosition()); + List lloc = new ArrayList<>(); + + // look in the current file and find the term under KPos + List dis = files.get(params.getTextDocument().getUri()).dis; + for (DefinitionItem di : dis) { + org.kframework.attributes.Location loc = getSafeLoc(di); + if (isPositionOverLocation(pos, loc)) { + Module m = (Module) di; + // want to activate when over `module NAME` and nothing else but, + // we don't store the exact position for the module name so try to calculate it + String name = m.getName(); + org.kframework.attributes.Location nameLoc = + new org.kframework.attributes.Location( + loc.startLine(), + loc.startColumn(), + loc.startLine(), + loc.startColumn() + "module ".length() + name.length()); + + if (isPositionOverLocation(pos, nameLoc)) { + List allDi = + files.values().stream() + .flatMap(doc -> doc.dis.stream()) + .collect(Collectors.toList()); + allDi.stream() + .filter(ddi -> ddi instanceof Module) + .map(ddi -> ((Module) ddi)) + .forEach( + m3 -> + m3.getItems().stream() .filter(mi -> mi instanceof Import) .map(mmi -> (Import) mmi) .filter(i -> i.getName().equals(name)) - .forEach(imp -> - lloc.add(new Location(URI.create(m3.getSource().source()).toString(), - loc2range(getSafeLoc(imp)))))); - } else { - // look for syntax references - AtomicReference xprd = new AtomicReference<>(); - m.getItems().stream().filter(a -> a instanceof Syntax) - .map(a -> ((Syntax)a)) - .forEach(b -> b.getPriorityBlocks().forEach(c -> c.getProductions().stream() - .filter(d -> isPositionOverLocation(pos, d.getLocation())).forEach(xprd::set))); - if (xprd.get() != null) { - Production prd = xprd.get(); - String psource = Path.of(URI.create(prd.source().get().source())).toString(); - org.kframework.attributes.Location ploc = prd.location().get(); - - if (caches.isEmpty()) - kls.languageClient.showMessage(new MessageParams(MessageType.Error, "No caches found. 'Find references' for productions is only available in workspace mode and requires a kompiled definition.")); - - // caches remember previous versions for quick access, so we may find more instances than there actually exist in the source file - // 1. for each cached sentence - // 2. find if it still exists in the source file and get its updated location - caches.forEach((mname, parseCache) -> { - String uri = Path.of(parseCache.module().att().get(org.kframework.attributes.Source.class).source()).toUri().toString(); - files.get(uri).dis.stream().filter(di2 -> di2 instanceof Module) - .map(di2 -> (Module) di2) - .filter(mm -> mname.startsWith((mm.getName()))) - .forEach(mm -> mm.getItems().stream().filter(mi -> mi instanceof StringSentence) - .map(mi -> (StringSentence) mi) - .forEach(ss -> { - if (parseCache.cache().containsKey(ss.getContent())) { - ParseCache.ParsedSentence ps = parseCache.cache().get(ss.getContent()); - if (ps.parse() != null) { - Bubble b = new Bubble(ss.getType(), ss.getContent(), ss.getAttributes() - .add(org.kframework.attributes.Location.class, ss.getLocation()).add(Source.class, ss.getSource()) - .add(Att.CONTENT_START_LINE(), ss.getContentStartLine()).add(Att.CONTENT_START_COLUMN(), ss.getContentStartColumn())); - ps = DefinitionParsing.updateLocation(ps, b); - KViz.from(t -> { - // the two production elements are not compatible so compare location information which should match - // if the definition wasn't modified - org.kframework.definition.Production dprd = t.att().get(org.kframework.definition.Production.class); - if (dprd.location().isPresent() && dprd.location().get().equals(ploc) && - dprd.source().isPresent() && dprd.source().get().source().equals(psource)) { - lloc.add(new Location(URI.create(t.source().get().source()).toString(), - loc2range(t.location().get()))); - } - return t; - }, "Find ref in rule").apply(ps.parse()); - } - } - }) - ); - }); - } - } - break; + .forEach( + imp -> + lloc.add( + new Location( + URI.create(m3.getSource().source()).toString(), + loc2range(getSafeLoc(imp)))))); + } else { + // look for syntax references + AtomicReference xprd = new AtomicReference<>(); + m.getItems().stream() + .filter(a -> a instanceof Syntax) + .map(a -> ((Syntax) a)) + .forEach( + b -> + b.getPriorityBlocks() + .forEach( + c -> + c.getProductions().stream() + .filter( + d -> isPositionOverLocation(pos, d.getLocation())) + .forEach(xprd::set))); + if (xprd.get() != null) { + Production prd = xprd.get(); + String psource = Path.of(URI.create(prd.source().get().source())).toString(); + org.kframework.attributes.Location ploc = prd.location().get(); + + if (caches.isEmpty()) + kls.languageClient.showMessage( + new MessageParams( + MessageType.Error, + "No caches found. 'Find references' for productions is only available" + + " in workspace mode and requires a kompiled definition.")); + + // caches remember previous versions for quick access, so we may find more + // instances than there actually exist in the source file + // 1. for each cached sentence + // 2. find if it still exists in the source file and get its updated location + caches.forEach( + (mname, parseCache) -> { + String uri = + Path.of( + parseCache + .module() + .att() + .get(org.kframework.attributes.Source.class) + .source()) + .toUri() + .toString(); + files.get(uri).dis.stream() + .filter(di2 -> di2 instanceof Module) + .map(di2 -> (Module) di2) + .filter(mm -> mname.startsWith((mm.getName()))) + .forEach( + mm -> + mm.getItems().stream() + .filter(mi -> mi instanceof StringSentence) + .map(mi -> (StringSentence) mi) + .forEach( + ss -> { + if (parseCache.cache().containsKey(ss.getContent())) { + ParseCache.ParsedSentence ps = + parseCache.cache().get(ss.getContent()); + if (ps.parse() != null) { + Bubble b = + new Bubble( + ss.getType(), + ss.getContent(), + ss.getAttributes() + .add( + org.kframework.attributes.Location + .class, + ss.getLocation()) + .add(Source.class, ss.getSource()) + .add( + Att.CONTENT_START_LINE(), + ss.getContentStartLine()) + .add( + Att.CONTENT_START_COLUMN(), + ss.getContentStartColumn())); + ps = DefinitionParsing.updateLocation(ps, b); + KViz.from( + t -> { + // the two production elements are not + // compatible so compare location + // information which should match + // if the definition wasn't modified + org.kframework.definition.Production + dprd = + t.att() + .get( + org.kframework + .definition + .Production.class); + if (dprd.location().isPresent() + && dprd.location() + .get() + .equals(ploc) + && dprd.source().isPresent() + && dprd.source() + .get() + .source() + .equals(psource)) { + lloc.add( + new Location( + URI.create( + t.source() + .get() + .source()) + .toString(), + loc2range( + t.location().get()))); + } + return t; + }, + "Find ref in rule") + .apply(ps.parse()); + } + } + })); + }); } + } + break; } - // TODO: check why sometimes this message is duplicated - clientLogger.logMessage("Operation '" + "text/references: " + params.getTextDocument().getUri() + " #pos: " - + pos.getLine() + " " + pos.getCharacter() + " found: " + lloc.size()); - - return lloc; + } + // TODO: check why sometimes this message is duplicated + clientLogger.logMessage( + "Operation '" + + "text/references: " + + params.getTextDocument().getUri() + + " #pos: " + + pos.getLine() + + " " + + pos.getCharacter() + + " found: " + + lloc.size()); + + return lloc; }); - } - - // Return the selection tree for a given position. This follows the AST as much as we have location information. - // The SelectionRange object is a recursive structure where the head is the smallest AST node next to the cursor - // and the deepest element is the entire file, the root of the document. - public CompletableFuture> selectionRange(SelectionRangeParams params) { - return CompletableFuture.supplyAsync(() -> { - List lloc = new ArrayList<>(); - - KTextDocument txtDoc = files.get(params.getTextDocument().getUri()); - List dis = txtDoc.dis; - txtDoc.getContextAt(new KPos(1, 1)); // warmup to avoid NPE - SelectionRange topsr = new SelectionRange(new Range(new Position(0,0), - new Position(txtDoc.lines[txtDoc.content.length()], txtDoc.columns[txtDoc.content.length()])), null); - for (Position ppos : params.getPositions()) { - KPos pos = new KPos(ppos); - for (DefinitionItem di : dis) { - if (isPositionOverLocation(pos, getSafeLoc(di))) { - if (di instanceof Module m) { - SelectionRange msr = new SelectionRange(loc2range(m.getLocation()), topsr); - for (ModuleItem mi : m.getItems()) { - if (isPositionOverLocation(pos, getSafeLoc(mi))) { - SelectionRange sentsr = new SelectionRange(loc2range(getSafeLoc(mi)), msr); - if (mi instanceof Syntax stx) { - for (PriorityBlock pb : stx.getPriorityBlocks()) { - SelectionRange pbsr = new SelectionRange(loc2range(getSafeLoc(pb)), sentsr); - for (Production prd : pb.getProductions()) - if (isPositionOverLocation(pos, getSafeLoc(prd))) - lloc.add(new SelectionRange(loc2range(getSafeLoc(prd)), pbsr)); - } - } else if (mi instanceof StringSentence ss) { // if we have caches, find the deepest term - String suffix = ss.getType().equals(DefinitionParsing.configuration) ? "-" + RuleGrammarGenerator.CONFIG_CELLS : "-" + RuleGrammarGenerator.RULE_CELLS; - Optional> ch = caches.entrySet().stream().filter(elm -> elm.getKey().startsWith(m.getName() + suffix)).findFirst(); - AtomicReference x = new AtomicReference<>(sentsr); - if (ch.isPresent()) { - Map parsedSent = ch.get().getValue().cache(); - if (parsedSent.containsKey(ss.getContent())) { - Bubble b = new Bubble(ss.getType(), ss.getContent(), ss.getAttributes() - .add(org.kframework.attributes.Location.class, ss.getLocation()).add(Source.class, ss.getSource()) - .add(Att.CONTENT_START_LINE(), ss.getContentStartLine()).add(Att.CONTENT_START_COLUMN(), ss.getContentStartColumn())); - ParseCache.ParsedSentence parse = DefinitionParsing.updateLocation(parsedSent.get(ss.getContent()), b); - KViz.from(t -> { - if (isPositionOverLocation(pos, t.location().get())) { - SelectionRange tsr = new SelectionRange(loc2range(t.location().get()), x.get()); - x.set(tsr); // find the deepest term that contains this position - } - return t; - }, "Find selectionRange in rule").apply(parse.parse()); - } - } - lloc.add(x.get()); - } else { // anything else that doesn't have a complex structure: Import, SyntaxPriorities... - lloc.add(new SelectionRange(loc2range(getSafeLoc(mi)), msr)); - } - } - } - } else { - lloc.add(new SelectionRange(loc2range(di.getLocation()), topsr)); + } + + // Return the selection tree for a given position. This follows the AST as much as we have + // location information. + // The SelectionRange object is a recursive structure where the head is the smallest AST node next + // to the cursor + // and the deepest element is the entire file, the root of the document. + public CompletableFuture> selectionRange(SelectionRangeParams params) { + return CompletableFuture.supplyAsync( + () -> { + List lloc = new ArrayList<>(); + + KTextDocument txtDoc = files.get(params.getTextDocument().getUri()); + List dis = txtDoc.dis; + txtDoc.getContextAt(new KPos(1, 1)); // warmup to avoid NPE + SelectionRange topsr = + new SelectionRange( + new Range( + new Position(0, 0), + new Position( + txtDoc.lines[txtDoc.content.length()], + txtDoc.columns[txtDoc.content.length()])), + null); + for (Position ppos : params.getPositions()) { + KPos pos = new KPos(ppos); + for (DefinitionItem di : dis) { + if (isPositionOverLocation(pos, getSafeLoc(di))) { + if (di instanceof Module m) { + SelectionRange msr = new SelectionRange(loc2range(m.getLocation()), topsr); + for (ModuleItem mi : m.getItems()) { + if (isPositionOverLocation(pos, getSafeLoc(mi))) { + SelectionRange sentsr = new SelectionRange(loc2range(getSafeLoc(mi)), msr); + if (mi instanceof Syntax stx) { + for (PriorityBlock pb : stx.getPriorityBlocks()) { + SelectionRange pbsr = + new SelectionRange(loc2range(getSafeLoc(pb)), sentsr); + for (Production prd : pb.getProductions()) + if (isPositionOverLocation(pos, getSafeLoc(prd))) + lloc.add(new SelectionRange(loc2range(getSafeLoc(prd)), pbsr)); + } + } else if (mi + instanceof + StringSentence + ss) { // if we have caches, find the deepest term + String suffix = + ss.getType().equals(DefinitionParsing.configuration) + ? "-" + RuleGrammarGenerator.CONFIG_CELLS + : "-" + RuleGrammarGenerator.RULE_CELLS; + Optional> ch = + caches.entrySet().stream() + .filter(elm -> elm.getKey().startsWith(m.getName() + suffix)) + .findFirst(); + AtomicReference x = new AtomicReference<>(sentsr); + if (ch.isPresent()) { + Map parsedSent = + ch.get().getValue().cache(); + if (parsedSent.containsKey(ss.getContent())) { + Bubble b = + new Bubble( + ss.getType(), + ss.getContent(), + ss.getAttributes() + .add( + org.kframework.attributes.Location.class, + ss.getLocation()) + .add(Source.class, ss.getSource()) + .add(Att.CONTENT_START_LINE(), ss.getContentStartLine()) + .add( + Att.CONTENT_START_COLUMN(), + ss.getContentStartColumn())); + ParseCache.ParsedSentence parse = + DefinitionParsing.updateLocation( + parsedSent.get(ss.getContent()), b); + KViz.from( + t -> { + if (isPositionOverLocation(pos, t.location().get())) { + SelectionRange tsr = + new SelectionRange( + loc2range(t.location().get()), x.get()); + x.set( + tsr); // find the deepest term that contains this + // position + } + return t; + }, + "Find selectionRange in rule") + .apply(parse.parse()); + } } + lloc.add(x.get()); + } else { // anything else that doesn't have a complex structure: Import, + // SyntaxPriorities... + lloc.add(new SelectionRange(loc2range(getSafeLoc(mi)), msr)); + } } + } + } else { + lloc.add(new SelectionRange(loc2range(di.getLocation()), topsr)); } + } } - - String poss = params.getPositions().stream().map(pos -> new KPos(pos).toString()).collect(Collectors.joining(" ")); - clientLogger.logMessage("Operation '" + "text/selectionRange: " + params.getTextDocument().getUri() + " #poss: " - + poss + " rezDepth: " + lloc.stream().map(TextDocumentSyncHandler::getSelectionRangeDepth).collect(Collectors.toList())); - - return lloc; + } + + String poss = + params.getPositions().stream() + .map(pos -> new KPos(pos).toString()) + .collect(Collectors.joining(" ")); + clientLogger.logMessage( + "Operation '" + + "text/selectionRange: " + + params.getTextDocument().getUri() + + " #poss: " + + poss + + " rezDepth: " + + lloc.stream() + .map(TextDocumentSyncHandler::getSelectionRangeDepth) + .collect(Collectors.toList())); + + return lloc; }); - } + } - // for debug purposes, return the depth of the selection range - private static int getSelectionRangeDepth(SelectionRange sr) { - if (sr == null) return 0; - return 1 + getSelectionRangeDepth(sr.getParent()); - } + // for debug purposes, return the depth of the selection range + private static int getSelectionRangeDepth(SelectionRange sr) { + if (sr == null) return 0; + return 1 + getSelectionRangeDepth(sr.getParent()); + } } diff --git a/kernel/src/main/java/org/kframework/main/AbstractKModule.java b/kernel/src/main/java/org/kframework/main/AbstractKModule.java index 2a8dd35361b..60721518a0d 100644 --- a/kernel/src/main/java/org/kframework/main/AbstractKModule.java +++ b/kernel/src/main/java/org/kframework/main/AbstractKModule.java @@ -5,97 +5,97 @@ import com.google.inject.AbstractModule; import com.google.inject.Binder; import com.google.inject.Module; -import com.google.inject.TypeLiteral; -import com.google.inject.multibindings.MapBinder; import com.google.inject.multibindings.Multibinder; -import org.apache.commons.lang3.tuple.Pair; -import org.kframework.backend.PosterBackend; -import org.kframework.utils.inject.Builtins; -import org.kframework.utils.inject.Options; - import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.function.Supplier; +import org.apache.commons.lang3.tuple.Pair; +import org.kframework.backend.PosterBackend; +import org.kframework.utils.inject.Options; public abstract class AbstractKModule implements KModule { - public List>> posterTypes() { - return Collections.emptyList(); - } + public List>> posterTypes() { + return Collections.emptyList(); + } - public List, Boolean>> kompileOptions() { - return Collections.emptyList(); - } + public List, Boolean>> kompileOptions() { + return Collections.emptyList(); + } - public List, Boolean>> krunOptions() { - return Collections.emptyList(); - } + public List, Boolean>> krunOptions() { + return Collections.emptyList(); + } - public List, Boolean>> kproveOptions() { - return Collections.emptyList(); - } + public List, Boolean>> kproveOptions() { + return Collections.emptyList(); + } - protected void bindOptions(Supplier, Boolean>>> action, Binder binder) { - binder.requireAtInjectOnConstructors(); - Multibinder optionsBinder = Multibinder.newSetBinder(binder, Object.class, Options.class); - for (Pair, Boolean> option : action.get()) { - optionsBinder.addBinding().to(option.getKey()); - } + protected void bindOptions(Supplier, Boolean>>> action, Binder binder) { + binder.requireAtInjectOnConstructors(); + Multibinder optionsBinder = + Multibinder.newSetBinder(binder, Object.class, Options.class); + for (Pair, Boolean> option : action.get()) { + optionsBinder.addBinding().to(option.getKey()); } + } - @Override - public List getKompileModules() { - return Lists.newArrayList(new AbstractModule() { + @Override + public List getKompileModules() { + return Lists.newArrayList( + new AbstractModule() { - @Override - protected void configure() { - bindOptions(AbstractKModule.this::kompileOptions, binder()); - } + @Override + protected void configure() { + bindOptions(AbstractKModule.this::kompileOptions, binder()); + } }); - } - - @Override - public List getKastModules() { - return Lists.newArrayList(); - } - - @Override - public List getKRunModules() { - return Lists.newArrayList(new AbstractModule() { - - @Override - protected void configure() { - bindOptions(AbstractKModule.this::krunOptions, binder()); - } + } + + @Override + public List getKastModules() { + return Lists.newArrayList(); + } + + @Override + public List getKRunModules() { + return Lists.newArrayList( + new AbstractModule() { + + @Override + protected void configure() { + bindOptions(AbstractKModule.this::krunOptions, binder()); + } }); - } - - @Override - public List getKEqModules(List definitionSpecificModules) { - return Lists.newArrayList(); - } - - @Override - public List getDefinitionSpecificKEqModules() { - return Lists.newArrayList(new AbstractModule() { - - @Override - protected void configure() { - binder().requireAtInjectOnConstructors(); - //bind backend implementations of tools to their interfaces - } + } + + @Override + public List getKEqModules(List definitionSpecificModules) { + return Lists.newArrayList(); + } + + @Override + public List getDefinitionSpecificKEqModules() { + return Lists.newArrayList( + new AbstractModule() { + + @Override + protected void configure() { + binder().requireAtInjectOnConstructors(); + // bind backend implementations of tools to their interfaces + } }); - } + } - @Override - public List getKProveModules() { - return Lists.newArrayList(new AbstractModule() { + @Override + public List getKProveModules() { + return Lists.newArrayList( + new AbstractModule() { - @Override - protected void configure() { - bindOptions(AbstractKModule.this::kproveOptions, binder()); - } + @Override + protected void configure() { + bindOptions(AbstractKModule.this::kproveOptions, binder()); + } }); - } + } } diff --git a/kernel/src/main/java/org/kframework/main/AnnotatedByDefinitionModule.java b/kernel/src/main/java/org/kframework/main/AnnotatedByDefinitionModule.java index 2e88d9e5364..48b6800496c 100644 --- a/kernel/src/main/java/org/kframework/main/AnnotatedByDefinitionModule.java +++ b/kernel/src/main/java/org/kframework/main/AnnotatedByDefinitionModule.java @@ -8,30 +8,35 @@ import com.google.inject.spi.DefaultElementVisitor; import com.google.inject.spi.Element; import com.google.inject.spi.Elements; - import java.lang.annotation.Annotation; import java.util.List; import java.util.function.Function; public abstract class AnnotatedByDefinitionModule extends PrivateModule { - public void exposeBindings(List modules, Class cls, Function func) { - for (Element element : Elements.getElements(modules)) { - element.acceptVisitor(new DefaultElementVisitor() { - @Override - public Void visit(Binding binding) { - Key key = binding.getKey(); - if (key.getAnnotation() == null && key.getAnnotationType() == null) { - bind(key.getTypeLiteral()).annotatedWith(cls).to(key.getTypeLiteral()); - expose(key.getTypeLiteral()).annotatedWith(cls); - } else if (key.getAnnotationType() != null && !key.getAnnotationType().getPackage().equals(Package.getPackage("com.google.inject.multibindings"))) { - bind(key.getTypeLiteral()).annotatedWith(func.apply(key.getAnnotationType())).to(key); - expose(key.getTypeLiteral()).annotatedWith(func.apply(key.getAnnotationType())); - } - return null; - } - }); - } - modules.forEach(AnnotatedByDefinitionModule.this::install); + public void exposeBindings(List modules, Class cls, Function func) { + for (Element element : Elements.getElements(modules)) { + element.acceptVisitor( + new DefaultElementVisitor() { + @Override + public Void visit(Binding binding) { + Key key = binding.getKey(); + if (key.getAnnotation() == null && key.getAnnotationType() == null) { + bind(key.getTypeLiteral()).annotatedWith(cls).to(key.getTypeLiteral()); + expose(key.getTypeLiteral()).annotatedWith(cls); + } else if (key.getAnnotationType() != null + && !key.getAnnotationType() + .getPackage() + .equals(Package.getPackage("com.google.inject.multibindings"))) { + bind(key.getTypeLiteral()) + .annotatedWith(func.apply(key.getAnnotationType())) + .to(key); + expose(key.getTypeLiteral()).annotatedWith(func.apply(key.getAnnotationType())); + } + return null; + } + }); } + modules.forEach(AnnotatedByDefinitionModule.this::install); + } } diff --git a/kernel/src/main/java/org/kframework/main/BinaryToText.java b/kernel/src/main/java/org/kframework/main/BinaryToText.java index 3139e8ef914..7d15899491e 100644 --- a/kernel/src/main/java/org/kframework/main/BinaryToText.java +++ b/kernel/src/main/java/org/kframework/main/BinaryToText.java @@ -2,40 +2,38 @@ package org.kframework.main; import com.martiansoftware.nailgun.NGContext; -import org.kframework.kore.K; -import org.kframework.parser.binary.BinaryParser; -import org.kframework.unparser.ToKast; -import org.kframework.utils.file.FileUtil; - import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; +import org.kframework.kore.K; +import org.kframework.parser.binary.BinaryParser; +import org.kframework.unparser.ToKast; +import org.kframework.utils.file.FileUtil; -/** - * Created by dwightguth on 3/29/16. - */ +/** Created by dwightguth on 3/29/16. */ public class BinaryToText { - // main method used to decipher binary outputs - + // main method used to decipher binary outputs - public static void nailMain(NGContext context) throws IOException { - FileUtil files = new FileUtil(null, new File(context.getWorkingDirectory()),null,null,null); - File f = files.resolveWorkingDirectory(context.getArgs()[0]); - FileChannel channel = FileChannel.open(f.toPath()); - ByteBuffer buf = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); - K result = BinaryParser.parse(buf); - ToKast.apply(result, new PrintStream(new FileOutputStream(files.resolveWorkingDirectory(context.getArgs()[1])))); - } + public static void nailMain(NGContext context) throws IOException { + FileUtil files = new FileUtil(null, new File(context.getWorkingDirectory()), null, null, null); + File f = files.resolveWorkingDirectory(context.getArgs()[0]); + FileChannel channel = FileChannel.open(f.toPath()); + ByteBuffer buf = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); + K result = BinaryParser.parse(buf); + ToKast.apply( + result, + new PrintStream(new FileOutputStream(files.resolveWorkingDirectory(context.getArgs()[1])))); + } - public static void main(String[] args) throws IOException { - File f = new File(args[0]); - FileChannel channel = FileChannel.open(f.toPath()); - ByteBuffer buf = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); - K result = BinaryParser.parse(buf); - ToKast.apply(result, new PrintStream(new FileOutputStream(new File(args[1])))); - } + public static void main(String[] args) throws IOException { + File f = new File(args[0]); + FileChannel channel = FileChannel.open(f.toPath()); + ByteBuffer buf = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); + K result = BinaryParser.parse(buf); + ToKast.apply(result, new PrintStream(new FileOutputStream(new File(args[1])))); + } } diff --git a/kernel/src/main/java/org/kframework/main/FrontEnd.java b/kernel/src/main/java/org/kframework/main/FrontEnd.java index 08e015488e0..58c7b6c9f74 100644 --- a/kernel/src/main/java/org/kframework/main/FrontEnd.java +++ b/kernel/src/main/java/org/kframework/main/FrontEnd.java @@ -15,69 +15,72 @@ public abstract class FrontEnd { - protected abstract int run(); + protected abstract int run(); - private final KExceptionManager kem; - protected final GlobalOptions globalOptions; - private final String usage; - private final JarInfo jarInfo; - private final Provider files; + private final KExceptionManager kem; + protected final GlobalOptions globalOptions; + private final String usage; + private final JarInfo jarInfo; + private final Provider files; - @Inject - public FrontEnd( - KExceptionManager kem, - GlobalOptions globalOptions, - String usage, - JarInfo jarInfo, - Provider files) { - this.kem = kem; - this.globalOptions = globalOptions; - this.usage = usage; - this.jarInfo = jarInfo; - this.files = files; - } + @Inject + public FrontEnd( + KExceptionManager kem, + GlobalOptions globalOptions, + String usage, + JarInfo jarInfo, + Provider files) { + this.kem = kem; + this.globalOptions = globalOptions; + this.usage = usage; + this.jarInfo = jarInfo; + this.files = files; + } - public int main() { - int retval; + public int main() { + int retval; + try { + if (globalOptions.help || globalOptions.helpHidden) { + System.out.print(usage); + retval = 0; + } else if (globalOptions.version) { + jarInfo.printVersionMessage(); + retval = 0; + } else { + if (globalOptions.timeout != null) { + new ExitOnTimeoutThread(globalOptions.timeout.toMillis()).start(); + } + if (globalOptions.shutdownWaitTime != null && !Main.isNailgun()) { + // Will interrupt the thread on Ctrl+C and allow the backend to terminate gracefully. + Runtime.getRuntime() + .addShutdownHook( + new Thread( + new InterrupterRunnable( + Thread.currentThread(), globalOptions.shutdownWaitTime.toMillis()))); + } try { - if (globalOptions.help || globalOptions.helpHidden) { - System.out.print(usage); - retval = 0; - } else if (globalOptions.version) { - jarInfo.printVersionMessage(); - retval = 0; - } else { - if (globalOptions.timeout != null) { - new ExitOnTimeoutThread(globalOptions.timeout.toMillis()).start(); - } - if (globalOptions.shutdownWaitTime != null && !Main.isNailgun()) { - //Will interrupt the thread on Ctrl+C and allow the backend to terminate gracefully. - Runtime.getRuntime().addShutdownHook(new Thread(new InterrupterRunnable(Thread.currentThread(), - globalOptions.shutdownWaitTime.toMillis()))); - } - try { - retval = run(); - } catch (ParameterException e) { - throw KEMException.criticalError(e.getMessage(), e); - } finally { - files.get().deleteTempDir(kem); - } - kem.print(); - } - } catch (KEMException e) { - // terminated with errors, so we need to return nonzero error code. - retval = KEMException.TERMINATED_WITH_ERRORS_EXIT_CODE; - if (globalOptions.debug()) { - e.printStackTrace(); - } else { - kem.registerThrown(e); - } - kem.print(); + retval = run(); + } catch (ParameterException e) { + throw KEMException.criticalError(e.getMessage(), e); + } finally { + files.get().deleteTempDir(kem); } - return retval; + kem.print(); + } + } catch (KEMException e) { + // terminated with errors, so we need to return nonzero error code. + retval = KEMException.TERMINATED_WITH_ERRORS_EXIT_CODE; + if (globalOptions.debug()) { + e.printStackTrace(); + } else { + kem.registerThrown(e); + } + kem.print(); } + return retval; + } - public static void printBootError(String message) { - System.err.println(StringUtil.splitLines(KException.criticalError(message).toString())); - } + public static void printBootError(String message) { + System.err.println(StringUtil.splitLines(KException.criticalError(message).toString())); + } } diff --git a/kernel/src/main/java/org/kframework/main/GlobalOptions.java b/kernel/src/main/java/org/kframework/main/GlobalOptions.java index a9558dabf7c..a234971cd55 100644 --- a/kernel/src/main/java/org/kframework/main/GlobalOptions.java +++ b/kernel/src/main/java/org/kframework/main/GlobalOptions.java @@ -2,152 +2,191 @@ package org.kframework.main; import com.beust.jcommander.Parameter; -import com.google.inject.Inject; -import org.kframework.utils.errorsystem.KEMException; -import org.kframework.utils.errorsystem.KException.ExceptionType; -import org.kframework.utils.options.BaseEnumConverter; -import org.kframework.utils.options.DurationConverter; - import java.time.Duration; import java.util.Collections; import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Set; +import org.kframework.utils.errorsystem.KEMException; +import org.kframework.utils.errorsystem.KException.ExceptionType; +import org.kframework.utils.options.BaseEnumConverter; +import org.kframework.utils.options.DurationConverter; public final class GlobalOptions { - public GlobalOptions() {} + public GlobalOptions() {} - public GlobalOptions(boolean debug, Warnings warnings, boolean verbose) { - this.debug = debug; - this.warnings = warnings; - this.verbose = verbose; - } + public GlobalOptions(boolean debug, Warnings warnings, boolean verbose) { + this.debug = debug; + this.warnings = warnings; + this.verbose = verbose; + } - public GlobalOptions(boolean debug, Warnings warnings, boolean verbose, boolean warnings2errors) { - this.debug = debug; - this.warnings = warnings; - this.verbose = verbose; - this.warnings2errors = warnings2errors; - } + public GlobalOptions(boolean debug, Warnings warnings, boolean verbose, boolean warnings2errors) { + this.debug = debug; + this.warnings = warnings; + this.verbose = verbose; + this.warnings2errors = warnings2errors; + } - public enum Warnings { - /** - * All warnings and errors - */ - ALL(EnumSet.allOf(ExceptionType.class)), + public enum Warnings { + /** All warnings and errors */ + ALL(EnumSet.allOf(ExceptionType.class)), - /** - * All warnings and errors except hidden warnings - */ - NORMAL(EnumSet.range(ExceptionType.ERROR, ExceptionType.FIRST_HIDDEN)), + /** All warnings and errors except hidden warnings */ + NORMAL(EnumSet.range(ExceptionType.ERROR, ExceptionType.FIRST_HIDDEN)), - /** - * No warnings, only errors - */ - NONE(EnumSet.of(ExceptionType.ERROR)); + /** No warnings, only errors */ + NONE(EnumSet.of(ExceptionType.ERROR)); - Warnings(Set types) { - typesIncluded = types; - } - private final Set typesIncluded; - - public Set getTypesIncluded() { return typesIncluded; } + Warnings(Set types) { + typesIncluded = types; } - public static class WarningsConverter extends BaseEnumConverter { - - public WarningsConverter(String optionName) { - super(optionName); - } + private final Set typesIncluded; - @Override - public Class enumClass() { - return Warnings.class; - } + public Set getTypesIncluded() { + return typesIncluded; } + } - public static class ExceptionTypeConverter extends BaseEnumConverter { - - public ExceptionTypeConverter(String optionName) { - super(optionName); - } + public static class WarningsConverter extends BaseEnumConverter { - @Override - public Class enumClass() { - return ExceptionType.class; - } + public WarningsConverter(String optionName) { + super(optionName); } - private Set typesIncluded; - - public boolean includesExceptionType(ExceptionType e) { - Set t = typesIncluded; - if (t == null) { - t = new HashSet<>(warnings.getTypesIncluded()); - t.addAll(enableWarnings); - t.removeAll(disableWarnings); - if (!t.contains(ExceptionType.ERROR)) { - throw KEMException.criticalError("Cannot disable errors with -Wno."); - } - typesIncluded = t; - } - return t.contains(e); + @Override + public Class enumClass() { + return Warnings.class; } + } - @Parameter(names={"--help", "-h"}, description="Print this help message", help = true) - public boolean help = false; + public static class ExceptionTypeConverter extends BaseEnumConverter { - @Parameter(names={"--help-hidden", "-hh"}, description="Print advanced options help message", help = true) - public boolean helpHidden = false; - - @Parameter(names="--version", description="Print version information") - public boolean version = false; - - @Parameter(names={"--verbose", "-v"}, description="Print verbose output messages") - public boolean verbose = false; - - @Parameter(names="--debug", description="Print debugging output messages and error stack traces") - private boolean debug = false; - - @Parameter(names="--debug-warnings", description="Print debugging output messages and error/warning stack traces") - public boolean debugWarnings = false; - - @Parameter(names={"--warnings", "-w"}, converter=WarningsConverter.class, description="Warning level. Values: [all|normal|none]", descriptionKey = "level") - public Warnings warnings = Warnings.NORMAL; - - @Parameter(names="-W", description="Enable specific warning categories. Values: [non-exhaustive-match|undeleted-temp-dir|missing-hook-java|missing-syntax-module|invalid-exit-code|deprecated-backend|invalid-config-var|future-error|unused-var|proof-lint|useless-rule|unresolved-function-symbol|malformed-markdown|invalidated-cache|unused-symbol|removed-anywhere]", descriptionKey = "warning", converter=ExceptionTypeConverter.class) - public List enableWarnings = Collections.emptyList(); - - @Parameter(names="-Wno", description="Disable specific warning categories.", descriptionKey = "warning", converter=ExceptionTypeConverter.class) - public List disableWarnings = Collections.emptyList(); - - @Parameter(names={"--warnings-to-errors", "-w2e"}, description="Convert warnings to errors.") - public boolean warnings2errors = false; - - @Parameter(names = {"--shutdown-wait-time"}, converter = DurationConverter.class, - description = "If option is set, a shutdown hook will be registered " + - "that, once invoked, interrupts the main thread and waits its termination. " + - "The wait time is the argument of this option, in format like 10ms/10s/10m/10h. " + - "Useful if K is interrupted by Ctrl+C, because it allows the backend to detect " + - "interruption and print diagnostics information. Currently interruption detection is implemented " + - "in Java Backend. If K is invoked from KServer (e.g. Nailgun), the option is ignored.", descriptionKey = "time", hidden = true) - public Duration shutdownWaitTime; - - @Parameter(names = {"--timeout"}, converter = DurationConverter.class, - description = "If option is set, timeout for this process, in format like 10ms/10s/10m/10h. " + - "Using this option is preferred compared to bash timeout command, which has known limitations " + - "when invoked from scripts.", descriptionKey = "duration", hidden = true) - public Duration timeout; - - @Parameter(names={"--no-exc-wrap"}, description="Do not wrap exception messages to 80 chars. Keep long lines.", hidden = true) - public boolean noExcWrap = false; - - public boolean debug() { - return debug || debugWarnings; + public ExceptionTypeConverter(String optionName) { + super(optionName); } - @Parameter(names={"--temp-dir"}, description="Put temp files in this location. Default is /tmp/.-xxx", descriptionKey = "path") - public String tempDir = null; + @Override + public Class enumClass() { + return ExceptionType.class; + } + } + + private Set typesIncluded; + + public boolean includesExceptionType(ExceptionType e) { + Set t = typesIncluded; + if (t == null) { + t = new HashSet<>(warnings.getTypesIncluded()); + t.addAll(enableWarnings); + t.removeAll(disableWarnings); + if (!t.contains(ExceptionType.ERROR)) { + throw KEMException.criticalError("Cannot disable errors with -Wno."); + } + typesIncluded = t; + } + return t.contains(e); + } + + @Parameter( + names = {"--help", "-h"}, + description = "Print this help message", + help = true) + public boolean help = false; + + @Parameter( + names = {"--help-hidden", "-hh"}, + description = "Print advanced options help message", + help = true) + public boolean helpHidden = false; + + @Parameter(names = "--version", description = "Print version information") + public boolean version = false; + + @Parameter( + names = {"--verbose", "-v"}, + description = "Print verbose output messages") + public boolean verbose = false; + + @Parameter( + names = "--debug", + description = "Print debugging output messages and error stack traces") + private boolean debug = false; + + @Parameter( + names = "--debug-warnings", + description = "Print debugging output messages and error/warning stack traces") + public boolean debugWarnings = false; + + @Parameter( + names = {"--warnings", "-w"}, + converter = WarningsConverter.class, + description = "Warning level. Values: [all|normal|none]", + descriptionKey = "level") + public Warnings warnings = Warnings.NORMAL; + + @Parameter( + names = "-W", + description = + "Enable specific warning categories. Values:" + + " [non-exhaustive-match|undeleted-temp-dir|missing-hook-java|missing-syntax-module|invalid-exit-code|deprecated-backend|invalid-config-var|future-error|unused-var|proof-lint|useless-rule|unresolved-function-symbol|malformed-markdown|invalidated-cache|unused-symbol|removed-anywhere]", + descriptionKey = "warning", + converter = ExceptionTypeConverter.class) + public List enableWarnings = Collections.emptyList(); + + @Parameter( + names = "-Wno", + description = "Disable specific warning categories.", + descriptionKey = "warning", + converter = ExceptionTypeConverter.class) + public List disableWarnings = Collections.emptyList(); + + @Parameter( + names = {"--warnings-to-errors", "-w2e"}, + description = "Convert warnings to errors.") + public boolean warnings2errors = false; + + @Parameter( + names = {"--shutdown-wait-time"}, + converter = DurationConverter.class, + description = + "If option is set, a shutdown hook will be registered that, once invoked, interrupts the" + + " main thread and waits its termination. The wait time is the argument of this" + + " option, in format like 10ms/10s/10m/10h. Useful if K is interrupted by Ctrl+C," + + " because it allows the backend to detect interruption and print diagnostics" + + " information. Currently interruption detection is implemented in Java Backend. If" + + " K is invoked from KServer (e.g. Nailgun), the option is ignored.", + descriptionKey = "time", + hidden = true) + public Duration shutdownWaitTime; + + @Parameter( + names = {"--timeout"}, + converter = DurationConverter.class, + description = + "If option is set, timeout for this process, in format like 10ms/10s/10m/10h. Using this" + + " option is preferred compared to bash timeout command, which has known limitations" + + " when invoked from scripts.", + descriptionKey = "duration", + hidden = true) + public Duration timeout; + + @Parameter( + names = {"--no-exc-wrap"}, + description = "Do not wrap exception messages to 80 chars. Keep long lines.", + hidden = true) + public boolean noExcWrap = false; + + public boolean debug() { + return debug || debugWarnings; + } + + @Parameter( + names = {"--temp-dir"}, + description = "Put temp files in this location. Default is /tmp/.-xxx", + descriptionKey = "path") + public String tempDir = null; } diff --git a/kernel/src/main/java/org/kframework/main/JavaVersion.java b/kernel/src/main/java/org/kframework/main/JavaVersion.java index eea02fb9943..8ef3445e881 100644 --- a/kernel/src/main/java/org/kframework/main/JavaVersion.java +++ b/kernel/src/main/java/org/kframework/main/JavaVersion.java @@ -1,13 +1,11 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.main; -/** - * Created by dwightguth on 9/23/15. - */ +/** Created by dwightguth on 9/23/15. */ public class JavaVersion { - public static void main(String[] args) { - System.err.println("java version \"" + System.getProperty("java.version") + "\""); - System.err.println(System.getProperty("sun.arch.data.model") + "-Bit"); - } + public static void main(String[] args) { + System.err.println("java version \"" + System.getProperty("java.version") + "\""); + System.err.println(System.getProperty("sun.arch.data.model") + "-Bit"); + } } diff --git a/kernel/src/main/java/org/kframework/main/KModule.java b/kernel/src/main/java/org/kframework/main/KModule.java index 324779e53de..2f279c22726 100644 --- a/kernel/src/main/java/org/kframework/main/KModule.java +++ b/kernel/src/main/java/org/kframework/main/KModule.java @@ -2,15 +2,19 @@ package org.kframework.main; import com.google.inject.Module; - import java.util.List; public interface KModule { - List getKompileModules(); - List getKastModules(); - List getKRunModules(); - List getKEqModules(List definitionSpecificModules); - List getDefinitionSpecificKEqModules(); - List getKProveModules(); + List getKompileModules(); + + List getKastModules(); + + List getKRunModules(); + + List getKEqModules(List definitionSpecificModules); + + List getDefinitionSpecificKEqModules(); + + List getKProveModules(); } diff --git a/kernel/src/main/java/org/kframework/main/Main.java b/kernel/src/main/java/org/kframework/main/Main.java index adbdb5b2f3a..4313d59412e 100644 --- a/kernel/src/main/java/org/kframework/main/Main.java +++ b/kernel/src/main/java/org/kframework/main/Main.java @@ -12,6 +12,14 @@ import com.google.inject.name.Named; import com.google.inject.spi.Message; import com.martiansoftware.nailgun.NGContext; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.concurrent.ExecutionException; import org.fusesource.jansi.AnsiConsole; import org.kframework.kast.KastFrontEnd; import org.kframework.kdep.KDepFrontEnd; @@ -26,184 +34,191 @@ import org.kframework.utils.inject.SimpleScope; import org.kframework.utils.inject.StartTime; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.ServiceLoader; -import java.util.concurrent.ExecutionException; - public record Main( - Provider kem, - Provider frontEnd, - @Named("requestScope") SimpleScope requestScope -) { - - /** - * @param args - * - the running arguments for the K3 tool. First argument must be one of the following: kompile|kast|krun. - * @throws IOException when loadDefinition fails - */ - public static void main(String[] args) { - isNailgun = false; - long startTime = System.nanoTime(); - AnsiConsole.systemInstall(); - if (args.length >= 1) { - - String[] args2 = Arrays.copyOfRange(args, 1, args.length); - Injector injector = getInjector(args[0]); - int result = injector.getInstance(Main.class).runApplication(args[0], args2, new File("."), System.getenv(), startTime); - AnsiConsole.systemUninstall(); - System.exit(result); - } - AnsiConsole.systemUninstall(); - invalidJarArguments(); + Provider kem, + Provider frontEnd, + @Named("requestScope") SimpleScope requestScope) { + + /** + * @param args - the running arguments for the K3 tool. First argument must be one of the + * following: kompile|kast|krun. + * @throws IOException when loadDefinition fails + */ + public static void main(String[] args) { + isNailgun = false; + long startTime = System.nanoTime(); + AnsiConsole.systemInstall(); + if (args.length >= 1) { + + String[] args2 = Arrays.copyOfRange(args, 1, args.length); + Injector injector = getInjector(args[0]); + int result = + injector + .getInstance(Main.class) + .runApplication(args[0], args2, new File("."), System.getenv(), startTime); + AnsiConsole.systemUninstall(); + System.exit(result); } + AnsiConsole.systemUninstall(); + invalidJarArguments(); + } - private static volatile boolean isNailgun; + private static volatile boolean isNailgun; - public static boolean isNailgun() { - return isNailgun; - } + public static boolean isNailgun() { + return isNailgun; + } - private static NGContext context = null; + private static NGContext context = null; - public static void exit(int exitCode) { - if (context != null) { - context.exit(exitCode); - } else { - System.exit(exitCode); - } + public static void exit(int exitCode) { + if (context != null) { + context.exit(exitCode); + } else { + System.exit(exitCode); } + } - public static void nailMain(NGContext context) { - long startTime = System.nanoTime(); - KServerFrontEnd kserver = KServerFrontEnd.instance(); - if (!kserver.isLocal()) { - context.assertLoopbackClient(); - } - isNailgun = true; - Main.context = context; - if (context.getArgs().length >= 1) { - String[] args2 = Arrays.copyOfRange(context.getArgs(), 1, context.getArgs().length); - int result = kserver.run(context.getArgs()[0], args2, new File(context.getWorkingDirectory()), (Map) context.getEnv(), startTime); - exit(result); - return; - } - invalidJarArguments(); + public static void nailMain(NGContext context) { + long startTime = System.nanoTime(); + KServerFrontEnd kserver = KServerFrontEnd.instance(); + if (!kserver.isLocal()) { + context.assertLoopbackClient(); } - - @Inject - public Main {} - - public SimpleScope getRequestScope() { - return requestScope; + isNailgun = true; + Main.context = context; + if (context.getArgs().length >= 1) { + String[] args2 = Arrays.copyOfRange(context.getArgs(), 1, context.getArgs().length); + int result = + kserver.run( + context.getArgs()[0], + args2, + new File(context.getWorkingDirectory()), + (Map) context.getEnv(), + startTime); + exit(result); + return; } - - public int runApplication(String tool, String[] args, File workingDir, Map env, long startTime) { - try { - requestScope.enter(); - seedInjector(requestScope, tool, args, workingDir, env, startTime); - return runApplication(); - } finally { - requestScope.exit(); - } + invalidJarArguments(); + } + + @Inject + public Main {} + + public SimpleScope getRequestScope() { + return requestScope; + } + + public int runApplication( + String tool, String[] args, File workingDir, Map env, long startTime) { + try { + requestScope.enter(); + seedInjector(requestScope, tool, args, workingDir, env, startTime); + return runApplication(); + } finally { + requestScope.exit(); } - - public int runApplication() { - KExceptionManager kem = this.kem.get(); - kem.installForUncaughtExceptions(); - try { - return frontEnd.get().main(); - } catch (ProvisionException e) { - for (Message m : e.getErrorMessages()) { - if (!(m.getCause() instanceof KEMException ex)) { - throw e; - } else { - kem.registerThrown(ex); - } - } - kem.print(); - return 1; + } + + public int runApplication() { + KExceptionManager kem = this.kem.get(); + kem.installForUncaughtExceptions(); + try { + return frontEnd.get().main(); + } catch (ProvisionException e) { + for (Message m : e.getErrorMessages()) { + if (!(m.getCause() instanceof KEMException ex)) { + throw e; + } else { + kem.registerThrown(ex); } + } + kem.print(); + return 1; } - - public static void seedInjector(SimpleScope scope, String tool, String[] args, File workingDir, - Map env, long startTime) { - scope.seed(Key.get(File.class, WorkingDir.class), workingDir); - scope.seed(Key.get(new TypeLiteral>() {}, Environment.class), env); - scope.seed(Key.get(String[].class, Options.class), args); - scope.seed(Key.get(Long.class, StartTime.class), startTime); + } + + public static void seedInjector( + SimpleScope scope, + String tool, + String[] args, + File workingDir, + Map env, + long startTime) { + scope.seed(Key.get(File.class, WorkingDir.class), workingDir); + scope.seed(Key.get(new TypeLiteral>() {}, Environment.class), env); + scope.seed(Key.get(String[].class, Options.class), args); + scope.seed(Key.get(Long.class, StartTime.class), startTime); + } + + public static Injector getInjector(String tool) { + ServiceLoader kLoader = ServiceLoader.load(KModule.class); + List kModules = new ArrayList<>(); + for (KModule m : kLoader) { + kModules.add(m); } - public static Injector getInjector(String tool) { - ServiceLoader kLoader = ServiceLoader.load(KModule.class); - List kModules = new ArrayList<>(); - for (KModule m : kLoader) { - kModules.add(m); - } + List modules = new ArrayList<>(); - List modules = new ArrayList<>(); - - switch (tool) { - case "-klsp": - try { - org.kframework.lsp.KLanguageServerLauncher.startServer(System.in, System.out); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - break; - case "-kserver": - modules.addAll(KServerFrontEnd.getModules()); - break; - case "-kompile": - modules.addAll(KompileFrontEnd.getModules()); - for (KModule kModule : kModules) { - List ms = kModule.getKompileModules(); - if (ms != null) { - modules.addAll(ms); - } - } - break; - case "-kast": - modules.addAll(KastFrontEnd.getModules()); - for (KModule kModule : kModules) { - List ms = kModule.getKastModules(); - if (ms != null) { - modules.addAll(ms); - } - } - break; - case "-kdep": - modules = KDepFrontEnd.getModules(); - break; - case "-k-compile-search-pattern": - modules = KSearchPatternFrontEnd.getModules(); - break; - case "-kprove": - modules.addAll(org.kframework.kprove.KProveFrontEnd.getModules()); - for (KModule kModule : kModules) { - List ms = kModule.getKProveModules(); - if (ms != null) { - modules.addAll(ms); - } - } - break; - default: - invalidJarArguments(); - throw new AssertionError("unreachable"); + switch (tool) { + case "-klsp": + try { + org.kframework.lsp.KLanguageServerLauncher.startServer(System.in, System.out); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); } - if (modules.size() == 0) { - //boot error, we should have printed it already - Main.exit(1); + break; + case "-kserver": + modules.addAll(KServerFrontEnd.getModules()); + break; + case "-kompile": + modules.addAll(KompileFrontEnd.getModules()); + for (KModule kModule : kModules) { + List ms = kModule.getKompileModules(); + if (ms != null) { + modules.addAll(ms); + } } - return Guice.createInjector(modules); + break; + case "-kast": + modules.addAll(KastFrontEnd.getModules()); + for (KModule kModule : kModules) { + List ms = kModule.getKastModules(); + if (ms != null) { + modules.addAll(ms); + } + } + break; + case "-kdep": + modules = KDepFrontEnd.getModules(); + break; + case "-k-compile-search-pattern": + modules = KSearchPatternFrontEnd.getModules(); + break; + case "-kprove": + modules.addAll(org.kframework.kprove.KProveFrontEnd.getModules()); + for (KModule kModule : kModules) { + List ms = kModule.getKProveModules(); + if (ms != null) { + modules.addAll(ms); + } + } + break; + default: + invalidJarArguments(); + throw new AssertionError("unreachable"); } - - private static void invalidJarArguments() { - System.err.println("The first argument of the K java compiler not recognized. Try -kompile, -kast, -kdep, -kserver, or -klsp."); - Main.exit(1); + if (modules.size() == 0) { + // boot error, we should have printed it already + Main.exit(1); } + return Guice.createInjector(modules); + } + + private static void invalidJarArguments() { + System.err.println( + "The first argument of the K java compiler not recognized. Try -kompile, -kast, -kdep," + + " -kserver, or -klsp."); + Main.exit(1); + } } diff --git a/kernel/src/main/java/org/kframework/main/Tool.java b/kernel/src/main/java/org/kframework/main/Tool.java index 1cd161e379b..b9f74af6159 100644 --- a/kernel/src/main/java/org/kframework/main/Tool.java +++ b/kernel/src/main/java/org/kframework/main/Tool.java @@ -2,5 +2,10 @@ package org.kframework.main; public enum Tool { - KOMPILE, KAST, KPROVE, KSEARCHPATTERN, KSERVER, KDEP + KOMPILE, + KAST, + KPROVE, + KSEARCHPATTERN, + KSERVER, + KDEP } diff --git a/kernel/src/main/java/org/kframework/parser/InputModes.java b/kernel/src/main/java/org/kframework/parser/InputModes.java index f545727b9c5..930ba8efd0d 100644 --- a/kernel/src/main/java/org/kframework/parser/InputModes.java +++ b/kernel/src/main/java/org/kframework/parser/InputModes.java @@ -2,5 +2,10 @@ package org.kframework.parser; public enum InputModes { - PROGRAM, BINARY, JSON, KAST, KORE, RULE + PROGRAM, + BINARY, + JSON, + KAST, + KORE, + RULE } diff --git a/kernel/src/main/java/org/kframework/parser/KRead.java b/kernel/src/main/java/org/kframework/parser/KRead.java index 63dbcab708e..1b0e9ae148c 100644 --- a/kernel/src/main/java/org/kframework/parser/KRead.java +++ b/kernel/src/main/java/org/kframework/parser/KRead.java @@ -1,6 +1,16 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import javax.json.Json; +import javax.json.JsonObject; +import javax.json.JsonReader; import org.kframework.attributes.Source; import org.kframework.definition.Module; import org.kframework.kompile.CompiledDefinition; @@ -20,139 +30,181 @@ import org.kframework.utils.errorsystem.KExceptionManager; import org.kframework.utils.file.FileUtil; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.List; -import java.util.ArrayList; -import java.util.Arrays; - -import javax.json.Json; -import javax.json.JsonObject; -import javax.json.JsonReader; - public record KRead( - KExceptionManager kem, - FileUtil files, - InputModes input, - GlobalOptions globalOptions -) { - - public String showTokens(Module mod, CompiledDefinition def, String stringToParse, Source source) { - return def.showTokens(mod, files, stringToParse, source); - } - - public K prettyRead(Module mod, Sort sort, String startSymbolLocation, CompiledDefinition def, Source source, String stringToParse, boolean partialParseDebug) { - return prettyRead(mod, sort, startSymbolLocation, def, source, stringToParse, this.input, partialParseDebug); - } - - public K prettyRead(Module mod, Sort sort, String startSymbolLocation, CompiledDefinition def, Source source, String stringToParse, InputModes inputMode, boolean partialParseDebug) { - return switch (inputMode) { - case BINARY, JSON, KAST -> deserialize(stringToParse, inputMode, source); - case KORE -> new KoreParser(mod.sortAttributesFor()).parseString(stringToParse); - case PROGRAM -> def.parseSingleTerm(mod, sort, startSymbolLocation, kem, files, stringToParse, source, partialParseDebug); - case RULE -> throw KEMException.internalError("Should have been handled directly by the kast front end: " + inputMode); - default -> throw KEMException.criticalError("Unsupported input mode: " + inputMode); - }; - } - - public void createBisonParser(Module mod, Sort sort, File outputFile, boolean glr, String bisonFile, long stackDepth, boolean library) { - Stopwatch sw = new Stopwatch(globalOptions); - try (ParseInModule parseInModule = RuleGrammarGenerator.getCombinedGrammar(mod, true, false, true, false, files)) { - try (Scanner scanner = parseInModule.getScanner(kem.options)) { - File scannerFile = files.resolveTemp("scanner.l"); - File scanHdr = files.resolveTemp("scanner.h"); - File parserFile = files.resolveTemp("parser.y"); - scanner.writeStandaloneScanner(scannerFile); - KSyntax2Bison.writeParser(parseInModule.getParsingModule(), parseInModule.getDisambiguationModule(), scanner, sort, parserFile, glr, stackDepth, kem); - int exit = files.getProcessBuilder() - .directory(files.resolveTemp(".")) - .command("flex", "--header-file=" + scanHdr.getAbsolutePath(), "-w", scannerFile.getAbsolutePath()) - .inheritIO() - .start() - .waitFor(); - if (exit != 0) { - throw KEMException.internalError("flex returned nonzero exit code: " + exit + "\n"); - } - exit = files.getProcessBuilder() - .directory(files.resolveTemp(".")) - .command("bison", "-d", "-Wno-other", "-Wno-conflicts-sr", "-Wno-conflicts-rr", parserFile.getAbsolutePath()) - .inheritIO() - .start() - .waitFor(); - if (exit != 0) { - throw KEMException.internalError("bison returned nonzero exit code: " + exit + "\n"); - } - List command = new ArrayList<>(Arrays.asList( - Scanner.COMPILER, - "-DK_BISON_PARSER_SORT=" + sort.name(), - files.resolveKInclude("cparser/main.c").getAbsolutePath(), - files.resolveTemp("lex.yy.c").getAbsolutePath(), - files.resolveTemp("parser.tab.c").getAbsolutePath(), - "-iquote", files.resolveTemp(".").getAbsolutePath(), - "-iquote", files.resolveKInclude("cparser").getAbsolutePath(), - "-o", outputFile.getAbsolutePath())); - - if (library) { - command.addAll(OS.current().getSharedLibraryCompilerFlags()); - } else { - command.add("-DK_BISON_PARSER_MAIN"); - } + KExceptionManager kem, FileUtil files, InputModes input, GlobalOptions globalOptions) { + + public String showTokens( + Module mod, CompiledDefinition def, String stringToParse, Source source) { + return def.showTokens(mod, files, stringToParse, source); + } + + public K prettyRead( + Module mod, + Sort sort, + String startSymbolLocation, + CompiledDefinition def, + Source source, + String stringToParse, + boolean partialParseDebug) { + return prettyRead( + mod, sort, startSymbolLocation, def, source, stringToParse, this.input, partialParseDebug); + } + + public K prettyRead( + Module mod, + Sort sort, + String startSymbolLocation, + CompiledDefinition def, + Source source, + String stringToParse, + InputModes inputMode, + boolean partialParseDebug) { + return switch (inputMode) { + case BINARY, JSON, KAST -> deserialize(stringToParse, inputMode, source); + case KORE -> new KoreParser(mod.sortAttributesFor()).parseString(stringToParse); + case PROGRAM -> def.parseSingleTerm( + mod, sort, startSymbolLocation, kem, files, stringToParse, source, partialParseDebug); + case RULE -> throw KEMException.internalError( + "Should have been handled directly by the kast front end: " + inputMode); + default -> throw KEMException.criticalError("Unsupported input mode: " + inputMode); + }; + } + + public void createBisonParser( + Module mod, + Sort sort, + File outputFile, + boolean glr, + String bisonFile, + long stackDepth, + boolean library) { + Stopwatch sw = new Stopwatch(globalOptions); + try (ParseInModule parseInModule = + RuleGrammarGenerator.getCombinedGrammar(mod, true, false, true, false, files)) { + try (Scanner scanner = parseInModule.getScanner(kem.options)) { + File scannerFile = files.resolveTemp("scanner.l"); + File scanHdr = files.resolveTemp("scanner.h"); + File parserFile = files.resolveTemp("parser.y"); + scanner.writeStandaloneScanner(scannerFile); + KSyntax2Bison.writeParser( + parseInModule.getParsingModule(), + parseInModule.getDisambiguationModule(), + scanner, + sort, + parserFile, + glr, + stackDepth, + kem); + int exit = + files + .getProcessBuilder() + .directory(files.resolveTemp(".")) + .command( + "flex", + "--header-file=" + scanHdr.getAbsolutePath(), + "-w", + scannerFile.getAbsolutePath()) + .inheritIO() + .start() + .waitFor(); + if (exit != 0) { + throw KEMException.internalError("flex returned nonzero exit code: " + exit + "\n"); + } + exit = + files + .getProcessBuilder() + .directory(files.resolveTemp(".")) + .command( + "bison", + "-d", + "-Wno-other", + "-Wno-conflicts-sr", + "-Wno-conflicts-rr", + parserFile.getAbsolutePath()) + .inheritIO() + .start() + .waitFor(); + if (exit != 0) { + throw KEMException.internalError("bison returned nonzero exit code: " + exit + "\n"); + } + List command = + new ArrayList<>( + Arrays.asList( + Scanner.COMPILER, + "-DK_BISON_PARSER_SORT=" + sort.name(), + files.resolveKInclude("cparser/main.c").getAbsolutePath(), + files.resolveTemp("lex.yy.c").getAbsolutePath(), + files.resolveTemp("parser.tab.c").getAbsolutePath(), + "-iquote", + files.resolveTemp(".").getAbsolutePath(), + "-iquote", + files.resolveKInclude("cparser").getAbsolutePath(), + "-o", + outputFile.getAbsolutePath())); + + if (library) { + command.addAll(OS.current().getSharedLibraryCompilerFlags()); + } else { + command.add("-DK_BISON_PARSER_MAIN"); + } - if (bisonFile != null) { - command.add(files.resolveWorkingDirectory(bisonFile).getAbsolutePath()); - } - exit = files.getProcessBuilder() - .command(command.toArray(new String[0])) - .inheritIO() - .start() - .waitFor(); - if (exit != 0) { - throw KEMException.internalError(Scanner.COMPILER + " returned nonzero exit code: " + exit + "\n"); - } - } catch(IOException | InterruptedException e) { - throw KEMException.internalError("Failed to execute process.", e); - } + if (bisonFile != null) { + command.add(files.resolveWorkingDirectory(bisonFile).getAbsolutePath()); + } + exit = + files + .getProcessBuilder() + .command(command.toArray(new String[0])) + .inheritIO() + .start() + .waitFor(); + if (exit != 0) { + throw KEMException.internalError( + Scanner.COMPILER + " returned nonzero exit code: " + exit + "\n"); } - sw.printIntermediate(" New Bison parser: " + mod.name()); + } catch (IOException | InterruptedException e) { + throw KEMException.internalError("Failed to execute process.", e); + } } - - public K deserialize(String stringToParse, Source source) { - return deserialize(stringToParse, this.input, source); + sw.printIntermediate(" New Bison parser: " + mod.name()); + } + + public K deserialize(String stringToParse, Source source) { + return deserialize(stringToParse, this.input, source); + } + + public static K deserialize(String stringToParse, InputModes inputMode, Source source) { + return switch (inputMode) { + case BINARY -> BinaryParser.parse(stringToParse.getBytes()); + case JSON -> JsonParser.parse(stringToParse); + case KAST -> KastParser.parse(stringToParse, source); + default -> throw KEMException.criticalError( + "Unsupported input mode for deserialization: " + inputMode); + }; + } + + public static K autoDeserialize(byte[] kast, Source source) { + if (BinaryParser.isBinaryKast(kast)) return BinaryParser.parse(kast); + + InputStream input = new ByteArrayInputStream(kast); + int c; + try { + while (Character.isWhitespace(c = input.read())) + ; + } catch (IOException e) { + throw KEMException.criticalError("Could not read output from parser: ", e); } - public static K deserialize(String stringToParse, InputModes inputMode, Source source) { - return switch (inputMode) { - case BINARY -> BinaryParser.parse(stringToParse.getBytes()); - case JSON -> JsonParser.parse(stringToParse); - case KAST -> KastParser.parse(stringToParse, source); - default -> throw KEMException.criticalError("Unsupported input mode for deserialization: " + inputMode); - }; + if (c == '{') { + JsonReader reader = Json.createReader(new ByteArrayInputStream(kast)); + JsonObject data = reader.readObject(); + return JsonParser.parseJson(data); } - public static K autoDeserialize(byte[] kast, Source source) { - if (BinaryParser.isBinaryKast(kast)) - return BinaryParser.parse(kast); - - InputStream input = new ByteArrayInputStream(kast); - int c; - try { - while (Character.isWhitespace(c = input.read())); - } catch (IOException e) { - throw KEMException.criticalError("Could not read output from parser: ", e); - } - - if ( c == '{' ) { - JsonReader reader = Json.createReader(new ByteArrayInputStream(kast)); - JsonObject data = reader.readObject(); - return JsonParser.parseJson(data); - } - - try { - return KastParser.parse(new String(kast), source); - } catch ( KEMException e ) { - throw KEMException.criticalError("Could not read input: " + source.source()); - } + try { + return KastParser.parse(new String(kast), source); + } catch (KEMException e) { + throw KEMException.criticalError("Could not read input: " + source.source()); } + } } diff --git a/kernel/src/main/java/org/kframework/parser/ParserUtils.java b/kernel/src/main/java/org/kframework/parser/ParserUtils.java index 711997864f4..4349231eff4 100644 --- a/kernel/src/main/java/org/kframework/parser/ParserUtils.java +++ b/kernel/src/main/java/org/kframework/parser/ParserUtils.java @@ -1,7 +1,23 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser; +import static org.kframework.Collections.*; +import static org.kframework.kore.KORE.*; + import com.google.common.collect.Streams; +import java.io.File; +import java.io.IOException; +import java.nio.file.LinkOption; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; import org.kframework.attributes.Att; import org.kframework.attributes.Location; @@ -29,321 +45,402 @@ import org.kframework.utils.options.OuterParsingOptions; import scala.Tuple4; -import java.io.File; -import java.io.IOException; -import java.nio.file.LinkOption; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import static org.kframework.Collections.*; -import static org.kframework.kore.KORE.*; - -/** - * A few functions that are a common pattern when calling the new parser. - */ +/** A few functions that are a common pattern when calling the new parser. */ public class ParserUtils { - private final KExceptionManager kem; - private final GlobalOptions options; - private final FileUtil files; - private final ExtractFencedKCodeFromMarkdown mdExtractor; - - public ParserUtils(FileUtil files, KExceptionManager kem) { - this(files, kem, new GlobalOptions(), new OuterParsingOptions()); + private final KExceptionManager kem; + private final GlobalOptions options; + private final FileUtil files; + private final ExtractFencedKCodeFromMarkdown mdExtractor; + + public ParserUtils(FileUtil files, KExceptionManager kem) { + this(files, kem, new GlobalOptions(), new OuterParsingOptions()); + } + + public ParserUtils( + FileUtil files, + KExceptionManager kem, + GlobalOptions options, + OuterParsingOptions outerParsingOptions) { + this.kem = kem; + this.options = options; + this.files = files; + this.mdExtractor = new ExtractFencedKCodeFromMarkdown(this.kem, outerParsingOptions.mdSelector); + } + + /** + * Takes a definition in e-kore textual format and a main module name, and returns the KORE + * representation of that module. Current implementation uses JavaCC and goes through KIL. + * + * @param definitionText textual representation of the modules. + * @param mainModule main module name. + * @return KORE representation of the main module. + */ + public static Module parseMainModuleOuterSyntax( + String definitionText, Source source, String mainModule) { + Definition def = new Definition(); + def.setItems(Outer.parse(source, definitionText, null)); + def.setMainModule(mainModule); + def.setMainSyntaxModule(mainModule); + + ProcessGroupAttributes.apply(def); + Context context = new Context(); + new CollectProductionsVisitor(context).visit(def); + + KILtoKORE kilToKore = new KILtoKORE(context, false, false); + return kilToKore.apply(def).getModule(mainModule).get(); + } + + public List slurp( + String definitionText, + Source source, + File currentDirectory, + List lookupDirectories, + Set requiredFiles) { + try { + source = + Source.apply(Paths.get(source.source()).toRealPath(LinkOption.NOFOLLOW_LINKS).toString()); + } catch (IOException e) { + // if it fails, just keep the original option } - - public ParserUtils(FileUtil files, KExceptionManager kem, GlobalOptions options, OuterParsingOptions outerParsingOptions) { - this.kem = kem; - this.options = options; - this.files = files; - this.mdExtractor = new ExtractFencedKCodeFromMarkdown(this.kem, outerParsingOptions.mdSelector); + if (source.source().endsWith(".md")) { + definitionText = mdExtractor.extract(definitionText, source); + if (options.debug()) { // save .k files in temp directory + String fname = new File(source.source()).getName(); + fname = fname.substring(0, fname.lastIndexOf(".md")) + ".k"; + File file = files.resolveTemp(".md2.k/" + fname); + // if multiple files exists with the same name, append an index + // and add a comment at the end of the file with the full path + // Note: the comment is not sent to the parser + int index = 2; + while (file.exists()) file = files.resolveTemp(".md2.k/" + fname + "_" + index++); + FileUtil.save(file, definitionText + "\n// " + source.source() + "\n"); + } } + List items = Outer.parse(source, definitionText, null); + items.stream() + .filter((d) -> d instanceof org.kframework.kil.Module) + .forEach((m) -> ProcessGroupAttributes.apply((org.kframework.kil.Module) m)); - /** - * Takes a definition in e-kore textual format and a main module name, and returns the KORE - * representation of that module. Current implementation uses JavaCC and goes through KIL. - * - * @param definitionText textual representation of the modules. - * @param mainModule main module name. - * @return KORE representation of the main module. - */ - public static Module parseMainModuleOuterSyntax(String definitionText, Source source, String mainModule) { - Definition def = new Definition(); - def.setItems(Outer.parse(source, definitionText, null)); - def.setMainModule(mainModule); - def.setMainSyntaxModule(mainModule); - - ProcessGroupAttributes.apply(def); - Context context = new Context(); - new CollectProductionsVisitor(context).visit(def); - - KILtoKORE kilToKore = new KILtoKORE(context, false, false); - return kilToKore.apply(def).getModule(mainModule).get(); + if (options.verbose) { + System.out.println("Importing: " + source); } - - public List slurp( - String definitionText, - Source source, - File currentDirectory, - List lookupDirectories, - Set requiredFiles) { - try { - source = Source.apply(Paths.get(source.source()).toRealPath(LinkOption.NOFOLLOW_LINKS).toString()); - } catch (IOException e) { - // if it fails, just keep the original option + List results = new ArrayList<>(); + + for (DefinitionItem di : items) { + if (di instanceof org.kframework.kil.Module) results.add((org.kframework.kil.Module) di); + else if (di instanceof Require) { + // resolve location of the new file + + String definitionFileName = ((Require) di).getValue(); + + if (definitionFileName.equals("ffi.k") + || definitionFileName.equals("json.k") + || definitionFileName.equals("rat.k") + || definitionFileName.equals("substitution.k") + || definitionFileName.equals("domains.k") + || definitionFileName.equals("kast.k")) { + kem.registerCompilerWarning( + ExceptionType.FUTURE_ERROR, + "Requiring a K file in the K builtin directory via " + + "a deprecated filename. Please replace \"" + + definitionFileName + + "\" with \"" + + definitionFileName.substring(0, definitionFileName.length() - 2) + + ".md\".", + di); + definitionFileName = + definitionFileName.substring(0, definitionFileName.length() - 2) + ".md"; } - if (source.source().endsWith(".md")) { - definitionText = mdExtractor.extract(definitionText, source); - if (options.debug()) { // save .k files in temp directory - String fname = new File(source.source()).getName(); - fname = fname.substring(0, fname.lastIndexOf(".md")) + ".k"; - File file = files.resolveTemp(".md2.k/" + fname); - // if multiple files exists with the same name, append an index - // and add a comment at the end of the file with the full path - // Note: the comment is not sent to the parser - int index = 2; - while (file.exists()) - file = files.resolveTemp(".md2.k/" + fname + "_" + index++); - FileUtil.save(file, definitionText + "\n// " + source.source() + "\n"); - } - } - List items = Outer.parse(source, definitionText, null); - items.stream().filter((d) -> d instanceof org.kframework.kil.Module) - .forEach((m) -> ProcessGroupAttributes.apply((org.kframework.kil.Module) m)); - if (options.verbose) { - System.out.println("Importing: " + source); - } - List results = new ArrayList<>(); - - for (DefinitionItem di : items) { - if (di instanceof org.kframework.kil.Module) - results.add((org.kframework.kil.Module) di); - else if (di instanceof Require) { - // resolve location of the new file - - String definitionFileName = ((Require) di).getValue(); - - if (definitionFileName.equals("ffi.k") || definitionFileName.equals("json.k") || - definitionFileName.equals("rat.k") || definitionFileName.equals("substitution.k") || - definitionFileName.equals("domains.k") || definitionFileName.equals("kast.k")) { - kem.registerCompilerWarning(ExceptionType.FUTURE_ERROR, - "Requiring a K file in the K builtin directory via " + - "a deprecated filename. Please replace \"" + definitionFileName + - "\" with \"" + definitionFileName.substring(0, definitionFileName.length() - 2) + ".md\".", di); - definitionFileName = definitionFileName.substring(0, definitionFileName.length() - 2) + ".md"; - } - - String finalDefinitionFile = definitionFileName; - - ArrayList allLookupDirectories = new ArrayList<>(lookupDirectories); - allLookupDirectories.add(1, currentDirectory); //after builtin directory but before anything else - - Optional definitionFile = allLookupDirectories.stream() - .map(lookupDirectory -> { - if (new File(finalDefinitionFile).isAbsolute()) { - return new File(finalDefinitionFile); - } else { - return new File(lookupDirectory, finalDefinitionFile); - } - }) - .filter(file -> file.exists()).findFirst(); - - if (definitionFile.isPresent()) { - File canonical = definitionFile.get().getAbsoluteFile(); - try { - canonical = definitionFile.get().toPath().toRealPath(LinkOption.NOFOLLOW_LINKS).toFile(); - } catch (IOException e) { - // if it fails, just keep the original option - } - if (!requiredFiles.contains(canonical)) { - requiredFiles.add(canonical); - results.addAll(slurp(loadDefinitionText(canonical), - Source.apply(canonical.getAbsolutePath()), - canonical.getParentFile(), - lookupDirectories, requiredFiles)); - } - } else { - throw KEMException.criticalError("Could not find file: " + - finalDefinitionFile + "\nLookup directories:" + allLookupDirectories, di); - } - } + String finalDefinitionFile = definitionFileName; + + ArrayList allLookupDirectories = new ArrayList<>(lookupDirectories); + allLookupDirectories.add( + 1, currentDirectory); // after builtin directory but before anything else + + Optional definitionFile = + allLookupDirectories.stream() + .map( + lookupDirectory -> { + if (new File(finalDefinitionFile).isAbsolute()) { + return new File(finalDefinitionFile); + } else { + return new File(lookupDirectory, finalDefinitionFile); + } + }) + .filter(file -> file.exists()) + .findFirst(); + + if (definitionFile.isPresent()) { + File canonical = definitionFile.get().getAbsoluteFile(); + try { + canonical = + definitionFile.get().toPath().toRealPath(LinkOption.NOFOLLOW_LINKS).toFile(); + } catch (IOException e) { + // if it fails, just keep the original option + } + if (!requiredFiles.contains(canonical)) { + requiredFiles.add(canonical); + results.addAll( + slurp( + loadDefinitionText(canonical), + Source.apply(canonical.getAbsolutePath()), + canonical.getParentFile(), + lookupDirectories, + requiredFiles)); + } + } else { + throw KEMException.criticalError( + "Could not find file: " + + finalDefinitionFile + + "\nLookup directories:" + + allLookupDirectories, + di); } - return results; + } } - - private String loadDefinitionText(File definitionFile) { - try { - return FileUtils.readFileToString(files.resolveWorkingDirectory(definitionFile)); - } catch (IOException e) { - throw KEMException.criticalError(e.getMessage(), e); - } + return results; + } + + private String loadDefinitionText(File definitionFile) { + try { + return FileUtils.readFileToString(files.resolveWorkingDirectory(definitionFile)); + } catch (IOException e) { + throw KEMException.criticalError(e.getMessage(), e); } - - public Set loadModules( - Set previousModules, - Context context, - String definitionText, - Source source, - File currentDirectory, - List lookupDirectories, - Set requiredFiles, - boolean preprocess, - boolean leftAssoc) { - - List kilModules = - slurp(definitionText, source, currentDirectory, lookupDirectories, requiredFiles); - - Definition def = new Definition(); - def.setItems((List) (Object) kilModules); - - ProcessGroupAttributes.apply(def); - new CollectProductionsVisitor(context).visit(def); - - // Tuple4 of moduleName, Source, Location, digest - Map>> groupedModules = - Streams.concat( - previousModules.stream().map(m -> Tuple4.apply(m.name(), m.att().get(Att.SOURCE(), Source.class), - m.att().get(Att.LOCATION(), Location.class), m.att().get(Att.DIGEST()))), - kilModules.stream().map(m -> Tuple4.apply(m.getName(), m.getSource(), m.getLocation(), m.digest()))) - // make sure we have unique modules (double requires), and preserve order - .collect(Collectors.toCollection(LinkedHashSet::new)).stream() - .collect(Collectors.groupingBy(Tuple4::_1)); - - List duplicateModules = groupedModules - .entrySet().stream() - .filter(e -> e.getValue().size() > 1) - .map(Map.Entry::getKey) - .collect(Collectors.toList()); - - int errors = 0; - for (String moduleName : duplicateModules) { - Tuple4 firstMod = groupedModules.get(moduleName).get(0); - Tuple4 secondMod = groupedModules.get(moduleName).get(1); - // give an error message only if we have - // the same module name found in different filenames (path doesn't matter) - // the location is different or - // the hashes of the pretty printed contents are different. - if (!Paths.get(firstMod._2().source()).getFileName().equals(Paths.get(secondMod._2().source()).getFileName()) - || !firstMod._3().equals(secondMod._3()) - || !firstMod._4().equals(secondMod._4())) { - String extraMDWarning = ""; - if (Paths.get(secondMod._2().source()).getFileName().toString().endsWith(".md")) - extraMDWarning = ". This can happen if --md-selector differs for kompile and kprove"; - KEMException ex = KEMException.outerParserError("Module " + moduleName + " differs from previous declaration at " - + firstMod._2() + " and " + firstMod._3() + extraMDWarning, secondMod._2(), secondMod._3()); - errors++; - kem.addKException(ex.getKException()); - } - } - - if (errors > 0) { - throw KEMException.outerParserError("Had " + errors + " outer parsing errors."); - } - - if (preprocess) { - System.out.println(def); - } - - KILtoKORE kilToKore = new KILtoKORE(context, false, leftAssoc); - // Order modules by name to stabilize the error message for circular imports - java.util.List flatModules = kilModules.stream().map(kilToKore::toFlatModule).sorted(Comparator.comparing(FlatModule::name)).collect(Collectors.toList()); - Set finalModules = mutable(FlatModule.toModules(immutable(flatModules), immutable(previousModules))); - - Set result = new HashSet<>(); - ModuleTransformer applySynonyms = ModuleTransformer.fromSentenceTransformer(new ApplySynonyms()::apply, "Apply sort synonyms"); - for (Module mod : finalModules) { - result.add(applySynonyms.apply(mod)); - } - return result; + } + + public Set loadModules( + Set previousModules, + Context context, + String definitionText, + Source source, + File currentDirectory, + List lookupDirectories, + Set requiredFiles, + boolean preprocess, + boolean leftAssoc) { + + List kilModules = + slurp(definitionText, source, currentDirectory, lookupDirectories, requiredFiles); + + Definition def = new Definition(); + def.setItems((List) (Object) kilModules); + + ProcessGroupAttributes.apply(def); + new CollectProductionsVisitor(context).visit(def); + + // Tuple4 of moduleName, Source, Location, digest + Map>> groupedModules = + Streams.concat( + previousModules.stream() + .map( + m -> + Tuple4.apply( + m.name(), + m.att().get(Att.SOURCE(), Source.class), + m.att().get(Att.LOCATION(), Location.class), + m.att().get(Att.DIGEST()))), + kilModules.stream() + .map( + m -> Tuple4.apply(m.getName(), m.getSource(), m.getLocation(), m.digest()))) + // make sure we have unique modules (double requires), and preserve order + .collect(Collectors.toCollection(LinkedHashSet::new)) + .stream() + .collect(Collectors.groupingBy(Tuple4::_1)); + + List duplicateModules = + groupedModules.entrySet().stream() + .filter(e -> e.getValue().size() > 1) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + + int errors = 0; + for (String moduleName : duplicateModules) { + Tuple4 firstMod = groupedModules.get(moduleName).get(0); + Tuple4 secondMod = groupedModules.get(moduleName).get(1); + // give an error message only if we have + // the same module name found in different filenames (path doesn't matter) + // the location is different or + // the hashes of the pretty printed contents are different. + if (!Paths.get(firstMod._2().source()) + .getFileName() + .equals(Paths.get(secondMod._2().source()).getFileName()) + || !firstMod._3().equals(secondMod._3()) + || !firstMod._4().equals(secondMod._4())) { + String extraMDWarning = ""; + if (Paths.get(secondMod._2().source()).getFileName().toString().endsWith(".md")) + extraMDWarning = ". This can happen if --md-selector differs for kompile and kprove"; + KEMException ex = + KEMException.outerParserError( + "Module " + + moduleName + + " differs from previous declaration at " + + firstMod._2() + + " and " + + firstMod._3() + + extraMDWarning, + secondMod._2(), + secondMod._3()); + errors++; + kem.addKException(ex.getKException()); + } } - public org.kframework.definition.Definition loadDefinition( - String mainModuleName, - Set previousModules, - String definitionText, - Source source, - File currentDirectory, - List lookupDirectories, - boolean preprocess, - boolean leftAssoc) { - Set modules = loadModules(previousModules, new Context(), definitionText, source, currentDirectory, lookupDirectories, new HashSet<>(), preprocess, leftAssoc); - Set allModules = new HashSet<>(modules); - allModules.addAll(previousModules); - Module mainModule = getMainModule(mainModuleName, allModules); - return org.kframework.definition.Definition.apply(mainModule, immutable(allModules), Att.empty()); + if (errors > 0) { + throw KEMException.outerParserError("Had " + errors + " outer parsing errors."); } - public org.kframework.definition.Definition loadDefinition( - String mainModuleName, - String syntaxModuleName, - String definitionText, - File source, - File currentDirectory, - List lookupDirectories, - boolean autoImportDomains, - boolean preprocess, - boolean leftAssoc) { - String strSource = source.getAbsolutePath(); - try { - strSource = source.toPath().toRealPath(LinkOption.NOFOLLOW_LINKS).toString(); - } catch (IOException e) { - // if it fails, just keep the original option - } - return loadDefinition(mainModuleName, syntaxModuleName, definitionText, - Source.apply(strSource), - currentDirectory, lookupDirectories, autoImportDomains, preprocess, leftAssoc); + if (preprocess) { + System.out.println(def); } - public org.kframework.definition.Definition loadDefinition( - String mainModuleName, - String syntaxModuleName, - String definitionText, - Source source, - File currentDirectory, - List lookupDirectories, - boolean autoImportDomains, - boolean preprocess, - boolean leftAssoc) { - Set previousModules = new HashSet<>(); - Set requiredFiles = new HashSet<>(); - Context context = new Context(); - if (autoImportDomains) - previousModules.addAll(loadModules(new HashSet<>(), context, Kompile.REQUIRE_PRELUDE_K, Source.apply("Auto imported prelude"), currentDirectory, lookupDirectories, requiredFiles, preprocess, leftAssoc)); - Set modules = loadModules(previousModules, context, definitionText, source, currentDirectory, lookupDirectories, requiredFiles, preprocess, leftAssoc); - if (preprocess) { - Main.exit(0); - } - modules.addAll(previousModules); // add the previous modules, since load modules is not additive - Module mainModule = getMainModule(mainModuleName, modules); - Optional opt; - opt = modules.stream().filter(m -> m.name().equals(syntaxModuleName)).findFirst(); - Module syntaxModule; - if (!opt.isPresent()) { - kem.registerCompilerWarning(ExceptionType.MISSING_SYNTAX_MODULE, "Could not find main syntax module with name " + syntaxModuleName - + " in definition. Use --syntax-module to specify one. Using " + mainModuleName + " as default."); - syntaxModule = mainModule; - } else { - syntaxModule = opt.get(); - } - - return org.kframework.definition.Definition.apply(mainModule, immutable(modules), Att().add(Att.SYNTAX_MODULE(), syntaxModule.name())); + KILtoKORE kilToKore = new KILtoKORE(context, false, leftAssoc); + // Order modules by name to stabilize the error message for circular imports + java.util.List flatModules = + kilModules.stream() + .map(kilToKore::toFlatModule) + .sorted(Comparator.comparing(FlatModule::name)) + .collect(Collectors.toList()); + Set finalModules = + mutable(FlatModule.toModules(immutable(flatModules), immutable(previousModules))); + + Set result = new HashSet<>(); + ModuleTransformer applySynonyms = + ModuleTransformer.fromSentenceTransformer( + new ApplySynonyms()::apply, "Apply sort synonyms"); + for (Module mod : finalModules) { + result.add(applySynonyms.apply(mod)); + } + return result; + } + + public org.kframework.definition.Definition loadDefinition( + String mainModuleName, + Set previousModules, + String definitionText, + Source source, + File currentDirectory, + List lookupDirectories, + boolean preprocess, + boolean leftAssoc) { + Set modules = + loadModules( + previousModules, + new Context(), + definitionText, + source, + currentDirectory, + lookupDirectories, + new HashSet<>(), + preprocess, + leftAssoc); + Set allModules = new HashSet<>(modules); + allModules.addAll(previousModules); + Module mainModule = getMainModule(mainModuleName, allModules); + return org.kframework.definition.Definition.apply( + mainModule, immutable(allModules), Att.empty()); + } + + public org.kframework.definition.Definition loadDefinition( + String mainModuleName, + String syntaxModuleName, + String definitionText, + File source, + File currentDirectory, + List lookupDirectories, + boolean autoImportDomains, + boolean preprocess, + boolean leftAssoc) { + String strSource = source.getAbsolutePath(); + try { + strSource = source.toPath().toRealPath(LinkOption.NOFOLLOW_LINKS).toString(); + } catch (IOException e) { + // if it fails, just keep the original option + } + return loadDefinition( + mainModuleName, + syntaxModuleName, + definitionText, + Source.apply(strSource), + currentDirectory, + lookupDirectories, + autoImportDomains, + preprocess, + leftAssoc); + } + + public org.kframework.definition.Definition loadDefinition( + String mainModuleName, + String syntaxModuleName, + String definitionText, + Source source, + File currentDirectory, + List lookupDirectories, + boolean autoImportDomains, + boolean preprocess, + boolean leftAssoc) { + Set previousModules = new HashSet<>(); + Set requiredFiles = new HashSet<>(); + Context context = new Context(); + if (autoImportDomains) + previousModules.addAll( + loadModules( + new HashSet<>(), + context, + Kompile.REQUIRE_PRELUDE_K, + Source.apply("Auto imported prelude"), + currentDirectory, + lookupDirectories, + requiredFiles, + preprocess, + leftAssoc)); + Set modules = + loadModules( + previousModules, + context, + definitionText, + source, + currentDirectory, + lookupDirectories, + requiredFiles, + preprocess, + leftAssoc); + if (preprocess) { + Main.exit(0); + } + modules.addAll(previousModules); // add the previous modules, since load modules is not additive + Module mainModule = getMainModule(mainModuleName, modules); + Optional opt; + opt = modules.stream().filter(m -> m.name().equals(syntaxModuleName)).findFirst(); + Module syntaxModule; + if (!opt.isPresent()) { + kem.registerCompilerWarning( + ExceptionType.MISSING_SYNTAX_MODULE, + "Could not find main syntax module with name " + + syntaxModuleName + + " in definition. Use --syntax-module to specify one. Using " + + mainModuleName + + " as default."); + syntaxModule = mainModule; + } else { + syntaxModule = opt.get(); } - private Module getMainModule(String mainModuleName, Set modules) { - Optional opt = modules.stream().filter(m -> m.name().equals(mainModuleName)).findFirst(); - if (!opt.isPresent()) { - throw KEMException.compilerError("Could not find main module with name " + mainModuleName - + " in definition. Use --main-module to specify one."); - } - return opt.get(); + return org.kframework.definition.Definition.apply( + mainModule, immutable(modules), Att().add(Att.SYNTAX_MODULE(), syntaxModule.name())); + } + + private Module getMainModule(String mainModuleName, Set modules) { + Optional opt = + modules.stream().filter(m -> m.name().equals(mainModuleName)).findFirst(); + if (!opt.isPresent()) { + throw KEMException.compilerError( + "Could not find main module with name " + + mainModuleName + + " in definition. Use --main-module to specify one."); } + return opt.get(); + } } diff --git a/kernel/src/main/java/org/kframework/parser/binary/BinaryParser.java b/kernel/src/main/java/org/kframework/parser/binary/BinaryParser.java index dead9cabc35..05d5d27a546 100644 --- a/kernel/src/main/java/org/kframework/parser/binary/BinaryParser.java +++ b/kernel/src/main/java/org/kframework/parser/binary/BinaryParser.java @@ -1,11 +1,7 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.binary; -import org.kframework.kore.K; -import org.kframework.kore.KLabel; -import org.kframework.kore.KToken; -import org.kframework.parser.outer.Outer; -import org.kframework.utils.errorsystem.KEMException; +import static org.kframework.kore.KORE.*; import java.io.IOException; import java.nio.ByteBuffer; @@ -16,188 +12,196 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.kframework.kore.K; +import org.kframework.kore.KLabel; +import org.kframework.kore.KToken; +import org.kframework.parser.outer.Outer; +import org.kframework.utils.errorsystem.KEMException; -import static org.kframework.kore.KORE.*; /** * Parses a KAST binary term into the KORE data structures. * - * Format of the KAST binary term is as follows: + *

Format of the KAST binary term is as follows: * - * First five bytes are the magic header "\x7fKAST". - * Next 3 bytes are the major, minor, and release version of the format. Currently - * they are set to "\x04\x00\x00". + *

First five bytes are the magic header "\x7fKAST". Next 3 bytes are the major, minor, and + * release version of the format. Currently they are set to "\x04\x00\x00". * - * Subsequently, the format contains a post-order traversal of the term according to the following rules: + *

Subsequently, the format contains a post-order traversal of the term according to the + * following rules: * - * * KToken: the byte "\x01" followed by a representation of the string of the token, and then the sort of the - * token. - * * KApply: Representation of each child of the KApply followed by the byte "\x02" followed by a representation - * of the klabel, followed by a 4-byte arity of the KApply. - * * KSequence: Representation of each child of the KSequence followed by the byte "\x03" followed by a 4-byte - * length of the KSequence. - * * KVariable: The byte "\x04" followed by a representation of the name of the variable. - * * KRewrite: Representation of the LHS of the rewrite, followed by the RHS, followed by the byte "\x05". - * * InjectedKLabel: The byte "\x06" followed by the representation of the klabel. - * * KLabel: The representation of the string of the klabel, followed by the byte "\x01" if the klabel is a - * variable, and "\x00" if it's a concrete klabel. - * * String: A 4-byte offset in the string intern table. The intern table is commputed as the term is traversed. - * An offset of 0 means that the string has not appeared before in this term, and is followed by a - * 4-byte length of the string followed by the String in UTF-16. An offset of 1 means the string - * refers to the most recent previous string in the intern table. An offset of 2 means the - * next-most-recent, and so forth. + *

* KToken: the byte "\x01" followed by a representation of the string of the token, and then + * the sort of the token. * KApply: Representation of each child of the KApply followed by the byte + * "\x02" followed by a representation of the klabel, followed by a 4-byte arity of the KApply. * + * KSequence: Representation of each child of the KSequence followed by the byte "\x03" followed by + * a 4-byte length of the KSequence. * KVariable: The byte "\x04" followed by a representation of + * the name of the variable. * KRewrite: Representation of the LHS of the rewrite, followed by the + * RHS, followed by the byte "\x05". * InjectedKLabel: The byte "\x06" followed by the + * representation of the klabel. * KLabel: The representation of the string of the klabel, followed + * by the byte "\x01" if the klabel is a variable, and "\x00" if it's a concrete klabel. * String: A + * 4-byte offset in the string intern table. The intern table is commputed as the term is traversed. + * An offset of 0 means that the string has not appeared before in this term, and is followed by a + * 4-byte length of the string followed by the String in UTF-16. An offset of 1 means the string + * refers to the most recent previous string in the intern table. An offset of 2 means the + * next-most-recent, and so forth. * - * Note one exception to this rule in binary format 4.0.1: if a term is encountered that has already been serialized, - * instead of serializing it again we serialize the byte '\x08' followed by a 4-byte offset in the term intern table. - * The term intern table behaves the same as the string intern table except that it contains every term that has been - * traversed to date. + *

Note one exception to this rule in binary format 4.0.1: if a term is encountered that has + * already been serialized, instead of serializing it again we serialize the byte '\x08' followed by + * a 4-byte offset in the term intern table. The term intern table behaves the same as the string + * intern table except that it contains every term that has been traversed to date. * - * After the term is traversed, it terminates with the byte "\x07". Note that KAST terms are constructed to be - * self-contained and composable. A client can take the output of two KAST terms and combine them into a single term - * simply by concatenating the terms together after stripping their MAGIC prefix and suffix. This will not be as - * space-compact as if the term was outputted all at once, but can be done in constant time without requiring the terms - * to be modified internally, and will still deserialze correctly. + *

After the term is traversed, it terminates with the byte "\x07". Note that KAST terms are + * constructed to be self-contained and composable. A client can take the output of two KAST terms + * and combine them into a single term simply by concatenating the terms together after stripping + * their MAGIC prefix and suffix. This will not be as space-compact as if the term was outputted all + * at once, but can be done in constant time without requiring the terms to be modified internally, + * and will still deserialze correctly. */ public class BinaryParser { - public static final byte[] MAGIC = {0x7f, 'K', 'A', 'S', 'T'}; - - public static final int BEGIN = 0, KTOKEN = 1, KAPPLY = 2, KSEQUENCE = 3, KVARIABLE = 4, KREWRITE = 5, - INJECTEDKLABEL = 6, END = 7, BACK_REFERENCE = 8; - - private final ByteBuffer data; - private final List interns = new ArrayList<>(); - private final List kInterns = new ArrayList<>(); - - private static final K[] EMPTY_KLIST = new K[0]; - - private BinaryParser(ByteBuffer data) { - this.data = data; + public static final byte[] MAGIC = {0x7f, 'K', 'A', 'S', 'T'}; + + public static final int BEGIN = 0, + KTOKEN = 1, + KAPPLY = 2, + KSEQUENCE = 3, + KVARIABLE = 4, + KREWRITE = 5, + INJECTEDKLABEL = 6, + END = 7, + BACK_REFERENCE = 8; + + private final ByteBuffer data; + private final List interns = new ArrayList<>(); + private final List kInterns = new ArrayList<>(); + + private static final K[] EMPTY_KLIST = new K[0]; + + private BinaryParser(ByteBuffer data) { + this.data = data; + } + + private K read400(boolean _401) throws IOException { + + Deque stack = new ArrayDeque<>(); + int type = 0; + while (type != END) { + type = data.get(); + K[] items; + int arity; + switch (type) { + case KTOKEN: + String s = readString(); + String sort = readString(); + Map sortCache = + ktokenCache.computeIfAbsent(sort, sort2 -> new HashMap<>()); + KToken token = sortCache.computeIfAbsent(s, s2 -> KToken(s, Outer.parseSort(sort))); + stack.push(token); + break; + case KAPPLY: + KLabel lbl = readKLabel(); + arity = data.getInt(); + if (arity == 0) items = EMPTY_KLIST; + else items = new K[arity]; + for (int i = arity - 1; i >= 0; i--) { + items[i] = stack.pop(); + } + stack.push(KApply(lbl, items)); + break; + case KSEQUENCE: + arity = data.getInt(); + if (arity == 0) items = EMPTY_KLIST; + else items = new K[arity]; + for (int i = arity - 1; i >= 0; i--) { + items[i] = stack.pop(); + } + stack.push(KSequence(items)); + break; + case KVARIABLE: + stack.push(KVariable(readString())); + break; + case KREWRITE: + K right = stack.pop(); + K left = stack.pop(); + stack.push(KRewrite(left, right)); + break; + case INJECTEDKLABEL: + stack.push(InjectedKLabel(readKLabel())); + break; + case END: + break; + case BACK_REFERENCE: + if (!_401) + throw KEMException.criticalError("Unexpected code found in KAST binary term: " + type); + int idx = data.getInt(); + stack.push(kInterns.get(kInterns.size() - idx)); + break; + default: + throw KEMException.criticalError("Unexpected code found in KAST binary term: " + type); + } + kInterns.add(stack.peek()); } - - private K read400(boolean _401) throws IOException { - - Deque stack = new ArrayDeque<>(); - int type = 0; - while(type != END) { - type = data.get(); - K[] items; - int arity; - switch (type) { - case KTOKEN: - String s = readString(); - String sort = readString(); - Map sortCache = ktokenCache.computeIfAbsent(sort, sort2 -> new HashMap<>()); - KToken token = sortCache.computeIfAbsent(s, s2 -> KToken(s, Outer.parseSort(sort))); - stack.push(token); - break; - case KAPPLY: - KLabel lbl = readKLabel(); - arity = data.getInt(); - if (arity == 0) - items = EMPTY_KLIST; - else - items = new K[arity]; - for (int i = arity - 1; i >= 0; i--) { - items[i] = stack.pop(); - } - stack.push(KApply(lbl, items)); - break; - case KSEQUENCE: - arity = data.getInt(); - if (arity == 0) - items = EMPTY_KLIST; - else - items = new K[arity]; - for (int i = arity - 1; i >= 0; i--) { - items[i] = stack.pop(); - } - stack.push(KSequence(items)); - break; - case KVARIABLE: - stack.push(KVariable(readString())); - break; - case KREWRITE: - K right = stack.pop(); - K left = stack.pop(); - stack.push(KRewrite(left, right)); - break; - case INJECTEDKLABEL: - stack.push(InjectedKLabel(readKLabel())); - break; - case END: - break; - case BACK_REFERENCE: - if (!_401) - throw KEMException.criticalError("Unexpected code found in KAST binary term: " + type); - int idx = data.getInt(); - stack.push(kInterns.get(kInterns.size() - idx)); - break; - default: - throw KEMException.criticalError("Unexpected code found in KAST binary term: " + type); - } - kInterns.add(stack.peek()); - } - // gc hints - interns.clear(); - klabelCache.clear(); - ktokenCache.clear(); - kInterns.clear(); - return stack.peek(); + // gc hints + interns.clear(); + klabelCache.clear(); + ktokenCache.clear(); + kInterns.clear(); + return stack.peek(); + } + + private final Map klabelCache = new HashMap<>(); + private final Map> ktokenCache = new HashMap<>(); + + private KLabel readKLabel() throws IOException { + String lbl = readString(); + if (data.get() != 0) return KVariable(lbl); + return klabelCache.computeIfAbsent(lbl, org.kframework.kore.KORE::KLabel); + } + + private String readString() throws IOException { + int idx = data.getInt(); + if (idx == 0) { + int len = data.getInt(); + char[] buf = new char[len]; + for (int i = 0; i < len; i++) { + buf[i] = data.getChar(); + } + String s = new String(buf); + interns.add(s); + return s; + } else { + return interns.get(interns.size() - idx); } - - private final Map klabelCache = new HashMap<>(); - private final Map> ktokenCache = new HashMap<>(); - - private KLabel readKLabel() throws IOException { - String lbl = readString(); - if (data.get() != 0) - return KVariable(lbl); - return klabelCache.computeIfAbsent(lbl, org.kframework.kore.KORE::KLabel); - } - - private String readString() throws IOException { - int idx = data.getInt(); - if (idx == 0) { - int len = data.getInt(); - char[] buf = new char[len]; - for (int i = 0; i < len; i++) { - buf[i] = data.getChar(); - } - String s = new String(buf); - interns.add(s); - return s; - } else { - return interns.get(interns.size() - idx); - } - } - - public static boolean isBinaryKast(byte[] bytes) { - return Arrays.equals(Arrays.copyOfRange(bytes, 0, 5), MAGIC); - } - - public static K parse(byte[] in) { - return parse(ByteBuffer.wrap(in)); - } - - public static K parse(ByteBuffer data) { - try { - byte[] magic = new byte[5]; - data.get(magic); - if (!Arrays.equals(magic, MAGIC)) { - throw KEMException.compilerError("Reading binary data from input source which is not a KAST term."); - } - int major = data.get(); - int minor = data.get(); - int build = data.get(); - if (major == 4 && minor == 0 && build == 0) { - return new BinaryParser(data).read400(false); - } else if (major == 4 && minor == 0 && build == 1) { - return new BinaryParser(data).read400(true); - } else { - throw KEMException.compilerError("Unsupported version of KAST binary file: " + major + "." + minor + "." + build); - } - } catch (IOException e) { - throw KEMException.criticalError("Could not read K term from binary", e); - } + } + + public static boolean isBinaryKast(byte[] bytes) { + return Arrays.equals(Arrays.copyOfRange(bytes, 0, 5), MAGIC); + } + + public static K parse(byte[] in) { + return parse(ByteBuffer.wrap(in)); + } + + public static K parse(ByteBuffer data) { + try { + byte[] magic = new byte[5]; + data.get(magic); + if (!Arrays.equals(magic, MAGIC)) { + throw KEMException.compilerError( + "Reading binary data from input source which is not a KAST term."); + } + int major = data.get(); + int minor = data.get(); + int build = data.get(); + if (major == 4 && minor == 0 && build == 0) { + return new BinaryParser(data).read400(false); + } else if (major == 4 && minor == 0 && build == 1) { + return new BinaryParser(data).read400(true); + } else { + throw KEMException.compilerError( + "Unsupported version of KAST binary file: " + major + "." + minor + "." + build); + } + } catch (IOException e) { + throw KEMException.criticalError("Could not read K term from binary", e); } + } } diff --git a/kernel/src/main/java/org/kframework/parser/inner/ApplySynonyms.java b/kernel/src/main/java/org/kframework/parser/inner/ApplySynonyms.java index 18211cff8fa..2e197d685e2 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/ApplySynonyms.java +++ b/kernel/src/main/java/org/kframework/parser/inner/ApplySynonyms.java @@ -1,6 +1,11 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; + +import java.util.ArrayList; +import java.util.List; import org.kframework.definition.Module; import org.kframework.definition.NonTerminal; import org.kframework.definition.Production; @@ -8,32 +13,26 @@ import org.kframework.definition.Sentence; import org.kframework.kore.Sort; -import java.util.ArrayList; -import java.util.List; - -import static org.kframework.definition.Constructors.*; -import static org.kframework.Collections.*; - public class ApplySynonyms { - public Production apply(Module m, Production p) { - Sort returnSort = m.sortSynonymMap().applyOrElse(p.sort(), s -> p.sort()); - List pis = new ArrayList<>(); - for (ProductionItem pi : iterable(p.items())) { - if (pi instanceof NonTerminal nt) { - pis.add(NonTerminal(m.sortSynonymMap().applyOrElse(nt.sort(), s -> nt.sort()), nt.name())); - } else { - pis.add(pi); - } + public Production apply(Module m, Production p) { + Sort returnSort = m.sortSynonymMap().applyOrElse(p.sort(), s -> p.sort()); + List pis = new ArrayList<>(); + for (ProductionItem pi : iterable(p.items())) { + if (pi instanceof NonTerminal nt) { + pis.add(NonTerminal(m.sortSynonymMap().applyOrElse(nt.sort(), s -> nt.sort()), nt.name())); + } else { + pis.add(pi); } - return Production(p.klabel(), p.params(), returnSort, immutable(pis), p.att()); } + return Production(p.klabel(), p.params(), returnSort, immutable(pis), p.att()); + } - public Sentence apply(Module m, Sentence s) { - if (s instanceof Production) { - return apply(m, (Production)s); - } else { - return s; - } + public Sentence apply(Module m, Sentence s) { + if (s instanceof Production) { + return apply(m, (Production) s); + } else { + return s; } + } } diff --git a/kernel/src/main/java/org/kframework/parser/inner/CollectProductionsVisitor.java b/kernel/src/main/java/org/kframework/parser/inner/CollectProductionsVisitor.java index 9ace6039109..a4c3e57722c 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/CollectProductionsVisitor.java +++ b/kernel/src/main/java/org/kframework/parser/inner/CollectProductionsVisitor.java @@ -1,45 +1,52 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner; +import java.util.List; import org.kframework.kil.Definition; import org.kframework.kil.Module; import org.kframework.kil.Production; -import org.kframework.kore.Sort; import org.kframework.kil.Syntax; import org.kframework.kil.loader.Context; - -import java.util.List; +import org.kframework.kore.Sort; public class CollectProductionsVisitor { - private final Context context; - - public CollectProductionsVisitor(Context context) { - this.context = context; - } - - private String moduleName; - private Sort sort; - private List params; - - public void visit(Module mod) { - this.moduleName = mod.getName(); - mod.getItems().forEach(mi -> { if (mi instanceof Syntax) visit((Syntax)mi); }); - } - - public void visit(Syntax syntax) { - this.sort = syntax.getDeclaredSort().getRealSort(); - this.params = syntax.getParams(); - syntax.getPriorityBlocks().forEach(pb -> pb.getProductions().forEach(this::visit)); - } - - public void visit(Production node) { - node.setSort(sort); - node.setOwnerModuleName(moduleName); - node.setParams(params); - context.addProduction(node); - } - - public void visit(Definition def) { - def.getItems().forEach(di -> { if (di instanceof Module) visit((Module)di); }); - } + private final Context context; + + public CollectProductionsVisitor(Context context) { + this.context = context; + } + + private String moduleName; + private Sort sort; + private List params; + + public void visit(Module mod) { + this.moduleName = mod.getName(); + mod.getItems() + .forEach( + mi -> { + if (mi instanceof Syntax) visit((Syntax) mi); + }); + } + + public void visit(Syntax syntax) { + this.sort = syntax.getDeclaredSort().getRealSort(); + this.params = syntax.getParams(); + syntax.getPriorityBlocks().forEach(pb -> pb.getProductions().forEach(this::visit)); + } + + public void visit(Production node) { + node.setSort(sort); + node.setOwnerModuleName(moduleName); + node.setParams(params); + context.addProduction(node); + } + + public void visit(Definition def) { + def.getItems() + .forEach( + di -> { + if (di instanceof Module) visit((Module) di); + }); + } } diff --git a/kernel/src/main/java/org/kframework/parser/inner/ParseCache.java b/kernel/src/main/java/org/kframework/parser/inner/ParseCache.java index 0c4fee0651f..57acd30cdb2 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/ParseCache.java +++ b/kernel/src/main/java/org/kframework/parser/inner/ParseCache.java @@ -1,23 +1,23 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner; +import java.io.Serializable; +import java.util.Map; +import java.util.Set; import org.kframework.attributes.Source; import org.kframework.definition.Module; import org.kframework.kore.K; import org.kframework.utils.errorsystem.KEMException; -import java.io.Serializable; -import java.util.Map; -import java.util.Set; - -/** - * Created by dwightguth on 4/20/15. - */ -public record ParseCache(Module module, boolean strict, - Map cache) implements Serializable { - public record ParsedSentence(K parse, - Set warnings, - Set errors, int startLine, - int startColumn, Source source) implements Serializable { - } +/** Created by dwightguth on 4/20/15. */ +public record ParseCache(Module module, boolean strict, Map cache) + implements Serializable { + public record ParsedSentence( + K parse, + Set warnings, + Set errors, + int startLine, + int startColumn, + Source source) + implements Serializable {} } diff --git a/kernel/src/main/java/org/kframework/parser/inner/ParseInModule.java b/kernel/src/main/java/org/kframework/parser/inner/ParseInModule.java index 059dfda65fe..9a71cc3d75f 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/ParseInModule.java +++ b/kernel/src/main/java/org/kframework/parser/inner/ParseInModule.java @@ -2,6 +2,13 @@ package org.kframework.parser.inner; import com.google.common.collect.Sets; +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; import org.kframework.attributes.Location; import org.kframework.attributes.Source; @@ -27,362 +34,477 @@ import scala.util.Left; import scala.util.Right; -import java.io.File; -import java.io.IOException; -import java.io.Serializable; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.stream.Collectors; - /** - * A wrapper that takes a module and one can call the parser - * for that module in thread safe way. + * A wrapper that takes a module and one can call the parser for that module in thread safe way. * Declarative disambiguation filters are also applied. */ public class ParseInModule implements Serializable, AutoCloseable { - private final Module seedModule; - private Module extensionModule; - /** - * The module in which parsing will be done. - * Note that this module will be used for disambiguation, and the parsing module can be different. - * This allows for grammar rewriting and more flexibility in the implementation. - */ - private Module disambModule; - /** - * The exact module used for parsing. This can contain productions and sorts that are not - * necessarily representable in KORE (sorts like Ne#Ids, to avoid name collisions). - * In this case the modified production will be annotated with the information from the - * original production, so disambiguation can be done safely. - */ - private volatile Module parsingModule; - private volatile EarleyParser parser = null; - private final boolean strict; - private final boolean profileRules; - private final boolean isBison; - private final boolean forGlobalScanner; - private final FileUtil files; - private final String typeInferenceDebug; - private final boolean partialParseDebug; - - ParseInModule(Module seedModule, boolean strict, boolean profileRules, boolean isBison, boolean forGlobalScanner, FileUtil files, String typeInferenceDebug, boolean partialParseDebug) { - this(seedModule, null, null, null, null, strict, profileRules, isBison, forGlobalScanner, files, typeInferenceDebug, partialParseDebug); - } + private final Module seedModule; + private Module extensionModule; - ParseInModule(Module seedModule, Scanner scanner, boolean strict, boolean profileRules, boolean isBison, boolean forGlobalScanner, FileUtil files, String typeInferenceDebug, boolean partialParseDebug) { - this(seedModule, null, null, null, scanner, strict, profileRules, isBison, forGlobalScanner, files, typeInferenceDebug, partialParseDebug); - } + /** + * The module in which parsing will be done. Note that this module will be used for + * disambiguation, and the parsing module can be different. This allows for grammar rewriting and + * more flexibility in the implementation. + */ + private Module disambModule; - private ParseInModule(Module seedModule, Module extensionModule, Module disambModule, Module parsingModule, Scanner scanner, boolean strict, boolean profileRules, boolean isBison, boolean forGlobalScanner, FileUtil files, String typeInferenceDebug, boolean partialParseDebug) { - this.seedModule = seedModule; - this.extensionModule = extensionModule; - this.disambModule = disambModule; - this.parsingModule = parsingModule; - this.scanner = scanner; - this.strict = strict; - this.profileRules = profileRules; - this.isBison = isBison; - this.forGlobalScanner = forGlobalScanner; - this.files = files; - this.typeInferenceDebug = typeInferenceDebug; - this.partialParseDebug = partialParseDebug; - } + /** + * The exact module used for parsing. This can contain productions and sorts that are not + * necessarily representable in KORE (sorts like Ne#Ids, to avoid name collisions). In this case + * the modified production will be annotated with the information from the original production, so + * disambiguation can be done safely. + */ + private volatile Module parsingModule; - /** - * The original module, which includes all the marker/flags imports. - * This can be used to invalidate caches. - * @return Module given by the user. - */ - public Module seedModule() { - return seedModule; - } + private volatile EarleyParser parser = null; + private final boolean strict; + private final boolean profileRules; + private final boolean isBison; + private final boolean forGlobalScanner; + private final FileUtil files; + private final String typeInferenceDebug; + private final boolean partialParseDebug; - /** - * An extension module of the seedModule which includes all productions, unmodified, and in addition, - * contains extra productions auto-defined, like casts. - * @return Module with extra productions defined during parser generator. - */ - public Module getExtensionModule() { - Module extM = extensionModule; - if (extM == null) { - Tuple3 mods = RuleGrammarGenerator.getCombinedGrammarImpl(seedModule, isBison, forGlobalScanner); - extM = mods._1(); - disambModule = mods._2(); - parsingModule = mods._3(); - extensionModule = extM; - } - return extM; + ParseInModule( + Module seedModule, + boolean strict, + boolean profileRules, + boolean isBison, + boolean forGlobalScanner, + FileUtil files, + String typeInferenceDebug, + boolean partialParseDebug) { + this( + seedModule, + null, + null, + null, + null, + strict, + profileRules, + isBison, + forGlobalScanner, + files, + typeInferenceDebug, + partialParseDebug); + } + + ParseInModule( + Module seedModule, + Scanner scanner, + boolean strict, + boolean profileRules, + boolean isBison, + boolean forGlobalScanner, + FileUtil files, + String typeInferenceDebug, + boolean partialParseDebug) { + this( + seedModule, + null, + null, + null, + scanner, + strict, + profileRules, + isBison, + forGlobalScanner, + files, + typeInferenceDebug, + partialParseDebug); + } + + private ParseInModule( + Module seedModule, + Module extensionModule, + Module disambModule, + Module parsingModule, + Scanner scanner, + boolean strict, + boolean profileRules, + boolean isBison, + boolean forGlobalScanner, + FileUtil files, + String typeInferenceDebug, + boolean partialParseDebug) { + this.seedModule = seedModule; + this.extensionModule = extensionModule; + this.disambModule = disambModule; + this.parsingModule = parsingModule; + this.scanner = scanner; + this.strict = strict; + this.profileRules = profileRules; + this.isBison = isBison; + this.forGlobalScanner = forGlobalScanner; + this.files = files; + this.typeInferenceDebug = typeInferenceDebug; + this.partialParseDebug = partialParseDebug; + } + + /** + * The original module, which includes all the marker/flags imports. This can be used to + * invalidate caches. + * + * @return Module given by the user. + */ + public Module seedModule() { + return seedModule; + } + + /** + * An extension module of the seedModule which includes all productions, unmodified, and in + * addition, contains extra productions auto-defined, like casts. + * + * @return Module with extra productions defined during parser generator. + */ + public Module getExtensionModule() { + Module extM = extensionModule; + if (extM == null) { + Tuple3 mods = + RuleGrammarGenerator.getCombinedGrammarImpl(seedModule, isBison, forGlobalScanner); + extM = mods._1(); + disambModule = mods._2(); + parsingModule = mods._3(); + extensionModule = extM; } + return extM; + } - public Module getParsingModule() { - Module parseM = parsingModule; - if (parseM == null) { - Tuple3 mods = RuleGrammarGenerator.getCombinedGrammarImpl(seedModule, isBison, forGlobalScanner); - extensionModule = mods._1(); - disambModule = mods._2(); - parseM = mods._3(); - parsingModule = parseM; - } - return parseM; + public Module getParsingModule() { + Module parseM = parsingModule; + if (parseM == null) { + Tuple3 mods = + RuleGrammarGenerator.getCombinedGrammarImpl(seedModule, isBison, forGlobalScanner); + extensionModule = mods._1(); + disambModule = mods._2(); + parseM = mods._3(); + parsingModule = parseM; } + return parseM; + } - public Module getDisambiguationModule() { - Module disambM = disambModule; - if (disambM == null) { - Tuple3 mods = RuleGrammarGenerator.getCombinedGrammarImpl(seedModule, isBison, forGlobalScanner); - extensionModule = mods._1(); - disambM = mods._2(); - parsingModule = mods._3(); - disambModule = disambM; - } - return disambM; + public Module getDisambiguationModule() { + Module disambM = disambModule; + if (disambM == null) { + Tuple3 mods = + RuleGrammarGenerator.getCombinedGrammarImpl(seedModule, isBison, forGlobalScanner); + extensionModule = mods._1(); + disambM = mods._2(); + parsingModule = mods._3(); + disambModule = disambM; } + return disambM; + } + public void initialize() { + Module m = getDisambiguationModule(); + m.definedSorts(); + m.subsorts(); + m.priorities(); + m.leftAssoc(); + m.rightAssoc(); + m.productionsFor(); + m.overloads(); + } - public void initialize() { - Module m = getDisambiguationModule(); - m.definedSorts(); - m.subsorts(); - m.priorities(); - m.leftAssoc(); - m.rightAssoc(); - m.productionsFor(); - m.overloads(); + /** + * Parse as input the given string and start symbol using the module stored in the object. + * + * @param input the string to parse. + * @param startSymbol the start symbol from which to parse. + * @return the Term representation of the parsed input. + */ + public Tuple2, K>, Set> parseString( + String input, Sort startSymbol, Source source) { + try (Scanner scanner = getScanner()) { + return parseString(input, startSymbol, "unit test", scanner, source, 1, 1, true, false); } + } - /** - * Parse as input the given string and start symbol using the module stored in the object. - * @param input the string to parse. - * @param startSymbol the start symbol from which to parse. - * @return the Term representation of the parsed input. - */ - public Tuple2, K>, Set> - parseString(String input, Sort startSymbol, Source source) { - try (Scanner scanner = getScanner()) { - return parseString(input, startSymbol, "unit test", scanner, source, 1, 1, true, false); - } + /** + * Print the list of tokens matched by the scanner, the location and the Regex Terminal The output + * is a valid Markdown table. + */ + public String tokenizeString(String input, Source source) { + StringBuilder sb = new StringBuilder(); + try (Scanner scanner = getScanner()) { + EarleyParser.ParserMetadata mdata = + new EarleyParser.ParserMetadata(input, scanner, source, 1, 1); + Map kind2Token = + scanner.getTokens().entrySet().stream() + .map(a -> new Tuple2<>(a.getValue()._1, a.getKey())) + .collect(Collectors.toMap(Tuple2::_1, Tuple2::_2)); + List lines = mdata.getLines(); + List columns = mdata.getColumns(); + int maxTokenLen = 7, maxLocLen = 10, maxTerminalLen = 10; + List locs = new ArrayList<>(); + List tokens = new ArrayList<>(); + List terminals = new ArrayList<>(); + List words = mdata.getWords(); + for (Scanner.Token word : mdata.getWords()) { + String loc = + String.format( + "(%d,%d,%d,%d)", + lines.get(word.startLoc), + columns.get(word.startLoc), + lines.get(word.endLoc), + columns.get(word.endLoc)); + locs.add(loc); + maxLocLen = Math.max(maxLocLen, loc.length()); + String tok = StringUtil.enquoteKString(word.value); + tokens.add(tok); + maxTokenLen = Math.max(maxTokenLen, tok.length()); + String terminal = kind2Token.getOrDefault(word.kind, Terminal.apply("")).toString(); + terminals.add(terminal); + maxTerminalLen = Math.max(maxTerminalLen, terminal.length()); + } + // if the token is absurdly large limit the column to 80 chars to maintain alignment + maxTokenLen = Math.min(maxTokenLen, 80); + maxTerminalLen = Math.min(maxTerminalLen, 20); + sb.append( + String.format( + "|%-" + maxTokenLen + "s | %-" + maxLocLen + "s | %-" + maxTerminalLen + "s|\n", + "\"Match\"", + "(location)", + "Terminal")); + sb.append( + String.format( + "|-%s|--%s|-%s|\n", + "-".repeat(maxTokenLen), "-".repeat(maxLocLen), "-".repeat(maxTerminalLen))); + for (int i = 0; i < words.size(); i++) { + Scanner.Token word = words.get(i); + sb.append( + String.format( + "|%-" + maxTokenLen + "s | %-" + maxLocLen + "s | %-" + maxTerminalLen + "s|\n", + tokens.get(i), + locs.get(i), + terminals.get(i))); + } } + return sb.toString(); + } - /** - * Print the list of tokens matched by the scanner, the location and the Regex Terminal - * The output is a valid Markdown table. - */ - public String tokenizeString(String input, Source source) { - StringBuilder sb = new StringBuilder(); - try (Scanner scanner = getScanner()) { - EarleyParser.ParserMetadata mdata = new EarleyParser.ParserMetadata(input, scanner, source, 1, 1); - Map kind2Token = - scanner.getTokens().entrySet().stream().map(a -> new Tuple2<>(a.getValue()._1, a.getKey())) - .collect(Collectors.toMap(Tuple2::_1, Tuple2::_2)); - List lines = mdata.getLines(); - List columns = mdata.getColumns(); - int maxTokenLen = 7, maxLocLen = 10, maxTerminalLen = 10; - List locs = new ArrayList<>(); - List tokens = new ArrayList<>(); - List terminals = new ArrayList<>(); - List words = mdata.getWords(); - for (Scanner.Token word : mdata.getWords()) { - String loc = String.format("(%d,%d,%d,%d)", - lines.get(word.startLoc), columns.get(word.startLoc), - lines.get(word.endLoc), columns.get(word.endLoc)); - locs.add(loc); - maxLocLen = Math.max(maxLocLen, loc.length()); - String tok = StringUtil.enquoteKString(word.value); - tokens.add(tok); - maxTokenLen = Math.max(maxTokenLen, tok.length()); - String terminal = kind2Token.getOrDefault(word.kind, Terminal.apply("")).toString(); - terminals.add(terminal); - maxTerminalLen = Math.max(maxTerminalLen, terminal.length()); - } - // if the token is absurdly large limit the column to 80 chars to maintain alignment - maxTokenLen = Math.min(maxTokenLen, 80); - maxTerminalLen = Math.min(maxTerminalLen, 20); - sb.append(String.format("|%-" + maxTokenLen + "s | %-" + maxLocLen + "s | %-" + maxTerminalLen + "s|\n", - "\"Match\"", "(location)", "Terminal")); - sb.append(String.format("|-%s|--%s|-%s|\n", "-".repeat(maxTokenLen), "-".repeat(maxLocLen), "-".repeat(maxTerminalLen))); - for (int i = 0; i < words.size(); i++) { - Scanner.Token word = words.get(i); - sb.append(String.format("|%-" + maxTokenLen + "s | %-" + maxLocLen + "s | %-" + maxTerminalLen + "s|\n", - tokens.get(i), locs.get(i), terminals.get(i))); - } - } - return sb.toString(); + public Tuple2, K>, Set> parseString( + String input, Sort startSymbol, String startSymbolLocation, Source source) { + try (Scanner scanner = getScanner()) { + return parseString( + input, startSymbol, startSymbolLocation, scanner, source, 1, 1, true, false); } + } - public Tuple2, K>, Set> - parseString(String input, Sort startSymbol, String startSymbolLocation, Source source) { - try (Scanner scanner = getScanner()) { - return parseString(input, startSymbol, startSymbolLocation, scanner, source, 1, 1, true, false); - } - } - private void getParser(Scanner scanner, Sort startSymbol) { - EarleyParser p = parser; - if (p == null) { - Module m = getParsingModule(); - p = new EarleyParser(m, scanner, startSymbol, partialParseDebug); - parser = p; - } + private void getParser(Scanner scanner, Sort startSymbol) { + EarleyParser p = parser; + if (p == null) { + Module m = getParsingModule(); + p = new EarleyParser(m, scanner, startSymbol, partialParseDebug); + parser = p; } + } - private Scanner scanner; - private final ThreadLocal inferencer = new ThreadLocal<>(); - private final Queue inferencers = new ConcurrentLinkedQueue<>(); + private Scanner scanner; + private final ThreadLocal inferencer = new ThreadLocal<>(); + private final Queue inferencers = new ConcurrentLinkedQueue<>(); - public Scanner getScanner(GlobalOptions go) { - if (scanner == null) { - scanner = new Scanner(this, go); - } - return scanner; + public Scanner getScanner(GlobalOptions go) { + if (scanner == null) { + scanner = new Scanner(this, go); } - public Scanner getScanner() { - if (scanner == null) { - scanner = new Scanner(this); - } - return scanner; + return scanner; + } + + public Scanner getScanner() { + if (scanner == null) { + scanner = new Scanner(this); } - public void setScanner(Scanner s) { - scanner = s; + return scanner; + } + + public void setScanner(Scanner s) { + scanner = s; + } + + public Tuple2, K>, Set> parseString( + String input, + Sort startSymbol, + String startSymbolLocation, + Scanner scanner, + Source source, + int startLine, + int startColumn, + boolean inferSortChecks, + boolean isAnywhere) { + final Tuple2, Term>, Set> result = + parseStringTerm( + input, + startSymbol, + startSymbolLocation, + scanner, + source, + startLine, + startColumn, + inferSortChecks, + isAnywhere); + Either, K> parseInfo; + if (result._1().isLeft()) { + parseInfo = Left.apply(result._1().left().get()); + } else { + parseInfo = + Right.apply( + new TreeNodesToKORE(Outer::parseSort, inferSortChecks && strict) + .apply(result._1().right().get())); } + return new Tuple2<>(parseInfo, result._2()); + } + + /** + * Parse the given input. This function is private because the final disambiguation in {@link + * AmbFilter} eliminates ambiguities that will be equivalent only after calling {@link + * TreeNodesToKORE#apply(Term)}, but returns a result that is somewhat arbitrary as an actual + * parser {@link Term}. Fortunately all callers want the result as a K, and can use the public + * version of this method. + * + * @param input + * @param startSymbol + * @param source + * @param startLine + * @param startColumn + * @return + */ + private Tuple2, Term>, Set> parseStringTerm( + String input, + Sort startSymbol, + String startSymbolLocation, + Scanner scanner, + Source source, + int startLine, + int startColumn, + boolean inferSortChecks, + boolean isAnywhere) { + if (!parsingModule.definedSorts().contains(startSymbol.head())) + throw KEMException.innerParserError( + "Could not find start symbol: " + startSymbol + " provided to " + startSymbolLocation, + source, + Location.apply(startLine, startColumn, startLine, startColumn + 1)); + getParser(scanner, startSymbol); - public Tuple2, K>, Set> - parseString(String input, Sort startSymbol, String startSymbolLocation, Scanner scanner, Source source, int startLine, int startColumn, boolean inferSortChecks, boolean isAnywhere) { - final Tuple2, Term>, Set> result - = parseStringTerm(input, startSymbol, startSymbolLocation, scanner, source, startLine, startColumn, inferSortChecks, isAnywhere); - Either, K> parseInfo; - if (result._1().isLeft()) { - parseInfo = Left.apply(result._1().left().get()); - } else { - parseInfo = Right.apply(new TreeNodesToKORE(Outer::parseSort, inferSortChecks && strict).apply(result._1().right().get())); + long start, endParse = 0, startTypeInf = 0, endTypeInf = 0; + start = profileRules ? System.currentTimeMillis() : 0; + + try { + Set warn = Sets.newHashSet(); + Term parsed; + try { + parsed = parser.parse(input, source, startLine, startColumn); + } catch (KEMException e) { + return Tuple2.apply(Left.apply(Collections.singleton(e)), Collections.emptySet()); + } + endParse = profileRules ? System.currentTimeMillis() : 0; + + Term rez3 = new TreeCleanerVisitor().apply(parsed); + Either, Term> rez = new CollapseRecordProdsVisitor(rez3).apply(rez3); + if (rez.isLeft()) return new Tuple2<>(rez, warn); + rez = + new PriorityVisitor( + disambModule.priorities(), disambModule.leftAssoc(), disambModule.rightAssoc()) + .apply(rez.right().get()); + if (rez.isLeft()) return new Tuple2<>(rez, warn); + rez = new KAppToTermConsVisitor(disambModule).apply(rez.right().get()); + if (rez.isLeft()) return new Tuple2<>(rez, warn); + rez3 = new PushAmbiguitiesDownAndPreferAvoid().apply(rez.right().get()); + rez3 = new PushTopAmbiguityUp().apply(rez3); + startTypeInf = profileRules ? System.currentTimeMillis() : 0; + + TypeInferencer currentInferencer; + if (isDebug(source, startLine)) { + currentInferencer = new TypeInferencer(disambModule, true); + inferencers.add(currentInferencer); + } else { + currentInferencer = inferencer.get(); + if (currentInferencer == null) { + currentInferencer = new TypeInferencer(disambModule, isDebug(source, startLine)); + inferencer.set(currentInferencer); + inferencers.add(currentInferencer); } - return new Tuple2<>(parseInfo, result._2()); - } + } + + rez = + new TypeInferenceVisitor( + currentInferencer, startSymbol, strict && inferSortChecks, true, isAnywhere) + .apply(rez3); + if (rez.isLeft()) return new Tuple2<>(rez, warn); + endTypeInf = profileRules ? System.currentTimeMillis() : 0; - /** - * Parse the given input. This function is private because the final disambiguation - * in {@link AmbFilter} eliminates ambiguities that will be equivalent only after - * calling {@link TreeNodesToKORE#apply(Term)}, but returns a result that is - * somewhat arbitrary as an actual parser {@link Term}. - * Fortunately all callers want the result as a K, and can use the public - * version of this method. - * @param input - * @param startSymbol - * @param source - * @param startLine - * @param startColumn - * @return - */ - private Tuple2, Term>, Set> - parseStringTerm(String input, Sort startSymbol, String startSymbolLocation, Scanner scanner, Source source, int startLine, int startColumn, boolean inferSortChecks, boolean isAnywhere) { - if (!parsingModule.definedSorts().contains(startSymbol.head())) - throw KEMException.innerParserError("Could not find start symbol: " + startSymbol + " provided to " + startSymbolLocation, source, Location.apply(startLine, startColumn, startLine, startColumn + 1)); - getParser(scanner, startSymbol); - - long start, endParse = 0, startTypeInf = 0, endTypeInf = 0; - start = profileRules ? System.currentTimeMillis() : 0; + rez = new ResolveOverloadedTerminators(disambModule.overloads()).apply(rez.right().get()); + if (rez.isLeft()) return new Tuple2<>(rez, warn); + rez3 = + new PushAmbiguitiesDownAndPreferAvoid(disambModule.overloads()).apply(rez.right().get()); + rez = new AmbFilterError(strict && inferSortChecks).apply(rez3); + if (rez.isLeft()) return new Tuple2<>(rez, warn); + Tuple2, Term>, Set> rez2 = + new AddEmptyLists(disambModule, startSymbol).apply(rez.right().get()); + warn = Sets.union(rez2._2(), warn); + if (rez2._1().isLeft()) return rez2; + rez3 = new RemoveBracketVisitor().apply(rez2._1().right().get()); + return new Tuple2<>(Right.apply(rez3), warn); + } finally { + if (profileRules) { try { - Set warn = Sets.newHashSet(); - Term parsed; - try { - parsed = parser.parse(input, source, startLine, startColumn); - } catch (KEMException e) { - return Tuple2.apply(Left.apply(Collections.singleton(e)), Collections.emptySet()); - } - endParse = profileRules ? System.currentTimeMillis() : 0; - - Term rez3 = new TreeCleanerVisitor().apply(parsed); - Either, Term> rez = new CollapseRecordProdsVisitor(rez3).apply(rez3); - if (rez.isLeft()) - return new Tuple2<>(rez, warn); - rez = new PriorityVisitor(disambModule.priorities(), disambModule.leftAssoc(), disambModule.rightAssoc()).apply(rez.right().get()); - if (rez.isLeft()) - return new Tuple2<>(rez, warn); - rez = new KAppToTermConsVisitor(disambModule).apply(rez.right().get()); - if (rez.isLeft()) - return new Tuple2<>(rez, warn); - rez3 = new PushAmbiguitiesDownAndPreferAvoid().apply(rez.right().get()); - rez3 = new PushTopAmbiguityUp().apply(rez3); - startTypeInf = profileRules ? System.currentTimeMillis() : 0; - - TypeInferencer currentInferencer; - if (isDebug(source, startLine)) { - currentInferencer = new TypeInferencer(disambModule, true); - inferencers.add(currentInferencer); - } else { - currentInferencer = inferencer.get(); - if (currentInferencer == null) { - currentInferencer = new TypeInferencer(disambModule, isDebug(source, startLine)); - inferencer.set(currentInferencer); - inferencers.add(currentInferencer); - } - } - - rez = new TypeInferenceVisitor(currentInferencer, startSymbol, strict && inferSortChecks, true, isAnywhere).apply(rez3); - if (rez.isLeft()) - return new Tuple2<>(rez, warn); - endTypeInf = profileRules ? System.currentTimeMillis() : 0; - - rez = new ResolveOverloadedTerminators(disambModule.overloads()).apply(rez.right().get()); - if (rez.isLeft()) - return new Tuple2<>(rez, warn); - rez3 = new PushAmbiguitiesDownAndPreferAvoid(disambModule.overloads()).apply(rez.right().get()); - rez = new AmbFilterError(strict && inferSortChecks).apply(rez3); - if (rez.isLeft()) - return new Tuple2<>(rez, warn); - Tuple2, Term>, Set> rez2 = new AddEmptyLists(disambModule, startSymbol).apply(rez.right().get()); - warn = Sets.union(rez2._2(), warn); - if (rez2._1().isLeft()) - return rez2; - rez3 = new RemoveBracketVisitor().apply(rez2._1().right().get()); - - return new Tuple2<>(Right.apply(rez3), warn); - } finally { - if (profileRules) { - try { - long stop = System.currentTimeMillis(); - long totalTime = stop - start; - long parseTime = endParse - start; - long tiTime = endTypeInf - startTypeInf; - File f = File.createTempFile("timing", ".log", files.resolveTemp("")); - FileUtils.writeStringToFile(f, String.format("%s:%d\n%5d %s:%d parse:%4d typeInf:%4d", - source.source(), startLine, totalTime, source.source(), startLine, parseTime, tiTime), StandardCharsets.UTF_8); - } catch (IOException e) { - throw KEMException.internalError("Could not write to timing.log", e); - } - } + long stop = System.currentTimeMillis(); + long totalTime = stop - start; + long parseTime = endParse - start; + long tiTime = endTypeInf - startTypeInf; + File f = File.createTempFile("timing", ".log", files.resolveTemp("")); + FileUtils.writeStringToFile( + f, + String.format( + "%s:%d\n%5d %s:%d parse:%4d typeInf:%4d", + source.source(), + startLine, + totalTime, + source.source(), + startLine, + parseTime, + tiTime), + StandardCharsets.UTF_8); + } catch (IOException e) { + throw KEMException.internalError("Could not write to timing.log", e); } + } } + } - private boolean isDebug(Source source, int startLine) { - if (typeInferenceDebug == null) { - return false; - } - return (source.source() + ":" + startLine).endsWith(typeInferenceDebug); + private boolean isDebug(Source source, int startLine) { + if (typeInferenceDebug == null) { + return false; } + return (source.source() + ":" + startLine).endsWith(typeInferenceDebug); + } - public void close() { - if (scanner != null) { - scanner.close(); - } - for (TypeInferencer inferencer : inferencers) { - inferencer.close(); - } - inferencers.clear(); + public void close() { + if (scanner != null) { + scanner.close(); + } + for (TypeInferencer inferencer : inferencers) { + inferencer.close(); } + inferencers.clear(); + } - public static Term disambiguateForUnparse(Module mod, Term ambiguity) { - Term rez3 = new PushTopAmbiguityUp().apply(ambiguity); - Either, Term> rez; - Tuple2, Term>, Set> rez2; - try (TypeInferencer inferencer = new TypeInferencer(mod, false)) { - rez = new TypeInferenceVisitor(inferencer, Sorts.K(), false, false, false).apply(rez3); - } - if (rez.isLeft()) { - rez2 = new AmbFilter(false).apply(rez3); - return rez2._1().right().get(); - } - rez3 = new PushAmbiguitiesDownAndPreferAvoid(mod.overloads()).apply(rez.right().get()); - rez2 = new AmbFilter(false).apply(rez3); - return rez2._1().right().get(); + public static Term disambiguateForUnparse(Module mod, Term ambiguity) { + Term rez3 = new PushTopAmbiguityUp().apply(ambiguity); + Either, Term> rez; + Tuple2, Term>, Set> rez2; + try (TypeInferencer inferencer = new TypeInferencer(mod, false)) { + rez = new TypeInferenceVisitor(inferencer, Sorts.K(), false, false, false).apply(rez3); + } + if (rez.isLeft()) { + rez2 = new AmbFilter(false).apply(rez3); + return rez2._1().right().get(); } + rez3 = new PushAmbiguitiesDownAndPreferAvoid(mod.overloads()).apply(rez.right().get()); + rez2 = new AmbFilter(false).apply(rez3); + return rez2._1().right().get(); + } } diff --git a/kernel/src/main/java/org/kframework/parser/inner/RuleGrammarGenerator.java b/kernel/src/main/java/org/kframework/parser/inner/RuleGrammarGenerator.java index 9181ef13773..126c862849f 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/RuleGrammarGenerator.java +++ b/kernel/src/main/java/org/kframework/parser/inner/RuleGrammarGenerator.java @@ -1,6 +1,19 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.definition.Constructors.Module; +import static org.kframework.kore.KORE.*; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.UnaryOperator; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.collections4.trie.PatriciaTrie; import org.kframework.attributes.Att; import org.kframework.builtin.Sorts; @@ -29,545 +42,733 @@ import scala.Tuple3; import scala.collection.Seq; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.UnaryOperator; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.kframework.Collections.*; -import static org.kframework.definition.Constructors.Module; -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; - /** - * Generator for rule and ground parsers. - * Takes as input a reference to a definition containing all the base syntax of K - * and uses it to generate a grammar by connecting all users sorts in a lattice with - * the top sort KItem#Top and the bottom sort KItem#Bottom. - *

- * The instances of the non-terminal KItem is renamed in KItem#Top if found in the right - * hand side of a production, and into KItem#Bottom if found in the left hand side. + * Generator for rule and ground parsers. Takes as input a reference to a definition containing all + * the base syntax of K and uses it to generate a grammar by connecting all users sorts in a lattice + * with the top sort KItem#Top and the bottom sort KItem#Bottom. + * + *

The instances of the non-terminal KItem is renamed in KItem#Top if found in the right hand + * side of a production, and into KItem#Bottom if found in the left hand side. */ public record RuleGrammarGenerator(Definition baseK) { - private static final Set kSorts = new HashSet<>(); - - static { - kSorts.add(Sorts.KBott()); - kSorts.add(Sorts.K()); - kSorts.add(Sorts.KLabel()); - kSorts.add(Sorts.KList()); - kSorts.add(Sorts.KItem()); - kSorts.add(Sorts.KConfigVar()); - kSorts.add(Sorts.KString()); + private static final Set kSorts = new HashSet<>(); + + static { + kSorts.add(Sorts.KBott()); + kSorts.add(Sorts.K()); + kSorts.add(Sorts.KLabel()); + kSorts.add(Sorts.KList()); + kSorts.add(Sorts.KItem()); + kSorts.add(Sorts.KConfigVar()); + kSorts.add(Sorts.KString()); + } + + private static Set kSorts() { + return java.util.Collections.unmodifiableSet(kSorts); + } + + /// modules that have a meaning: + public static final String DEFAULT_LAYOUT = "DEFAULT-LAYOUT"; + public static final String RULE_CELLS = "RULE-CELLS"; + public static final String CONFIG_CELLS = "CONFIG-CELLS"; + public static final String K = "K"; + public static final String AUTO_CASTS = "AUTO-CASTS"; + public static final String KSEQ_SYMBOLIC = "KSEQ-SYMBOLIC"; + public static final String K_TOP_SORT = "K-TOP-SORT"; + public static final String K_BOTTOM_SORT = "K-BOTTOM-SORT"; + public static final String AUTO_FOLLOW = "AUTO-FOLLOW"; + public static final String PROGRAM_LISTS = "PROGRAM-LISTS"; + public static final String RULE_LISTS = "RULE-LISTS"; + public static final String RECORD_PRODS = "RECORD-PRODUCTIONS"; + public static final String SORT_PREDICATES = "SORT-PREDICATES"; + + public static final String POSTFIX = "-PROGRAM-PARSING"; + + public static final String NOT_INJECTION = "notInjection"; + public static final String ID = "ID"; + private static final String ID_SYNTAX = "ID-SYNTAX"; + public static final String ID_PROGRAM_PARSING = ID_SYNTAX + POSTFIX; + + /** + * Initialize a grammar generator. + * + * @param baseK A Definition containing a K module giving the syntax of K itself. The default K + * syntax is defined in include/kast.k. + */ + public RuleGrammarGenerator {} + + private Set renameKItem2Bottom(Set def) { + // TODO: do renaming of KItem and K in the LHS to KBott? + return def; + } + + /** + * Creates the seed module that can be used to parse rules. Imports module markers RULE-CELLS and + * K found in /include/kast.k. + * + * @param mod The user defined module from which to start. + * @return a new module which imports the original user module and a set of marker modules. + */ + public Module getRuleGrammar(Module mod) { + return getGrammar(mod, RULE_CELLS); + } + + private Module getGrammar(Module mod, String name) { + // import RULE-CELLS in order to parse cells specific to rules + Module newM = + new Module( + mod.name() + "-" + name, + (scala.collection.Set) + mod.imports() + .$bar( + Set( + Import(baseK.getModule(K).get(), true), + Import(baseK.getModule(name).get(), true), + Import(baseK.getModule(DEFAULT_LAYOUT).get(), true))), + mod.localSentences(), + mod.att()); + return newM; + } + + /** + * Creates the seed module that can be used to parse configurations. Imports module markers + * CONFIG-CELLS and K found in /include/kast.k. + * + * @param mod The user defined module from which to start. + * @return a new module which imports the original user module and a set of marker modules. + */ + public Module getConfigGrammar(Module mod) { + return getGrammar(mod, CONFIG_CELLS); + } + + /** + * Creates the seed module that can be used to parse programs. Imports module markers + * PROGRAM-LISTS found in /include/kast.k. + * + * @param mod The user defined module from which to start. + * @return a new module which imports the original user module and a set of marker modules. + */ + public Module getProgramsGrammar(Module mod) { + + if (mod.name().endsWith(POSTFIX)) { + return mod; + } else { + Module newMod = + ModuleTransformer.from( + oldMod -> { + UnaryOperator f = + _import -> { + Option programParsing = + baseK.getModule(_import.module().name() + "-PROGRAM-PARSING"); + if (programParsing.isDefined()) { + return Import(programParsing.get(), _import.isPublic()); + } + return _import; + }; + Set imports = + stream(oldMod.imports()).map(f).collect(Collectors.toSet()); + return Module.apply( + oldMod.name(), immutable(imports), oldMod.localSentences(), oldMod.att()); + }, + "apply program parsing modules") + .apply(mod); + + Set modules = new HashSet(); + for (Import m : iterable(newMod.imports())) { + modules.add(m); + } + + // import PROGRAM-LISTS so user lists are modified to parse programs + modules.add(Import(baseK.getModule(PROGRAM_LISTS).get(), true)); + + // check if `#Layout` has been declared, import `DEFAULT-LAYOUT` if not + if (!mod.allSorts().contains(Sorts.Layout())) { + modules.add(Import(baseK.getModule(DEFAULT_LAYOUT).get(), true)); + } + + return Module.apply( + mod.name() + "-PROGRAM-GRAMMAR", + immutable(modules), + newMod.localSentences(), + newMod.att()); } - - private static Set kSorts() { - return java.util.Collections.unmodifiableSet(kSorts); - } - - /// modules that have a meaning: - public static final String DEFAULT_LAYOUT = "DEFAULT-LAYOUT"; - public static final String RULE_CELLS = "RULE-CELLS"; - public static final String CONFIG_CELLS = "CONFIG-CELLS"; - public static final String K = "K"; - public static final String AUTO_CASTS = "AUTO-CASTS"; - public static final String KSEQ_SYMBOLIC = "KSEQ-SYMBOLIC"; - public static final String K_TOP_SORT = "K-TOP-SORT"; - public static final String K_BOTTOM_SORT = "K-BOTTOM-SORT"; - public static final String AUTO_FOLLOW = "AUTO-FOLLOW"; - public static final String PROGRAM_LISTS = "PROGRAM-LISTS"; - public static final String RULE_LISTS = "RULE-LISTS"; - public static final String RECORD_PRODS = "RECORD-PRODUCTIONS"; - public static final String SORT_PREDICATES = "SORT-PREDICATES"; - - public static final String POSTFIX = "-PROGRAM-PARSING"; - - public static final String NOT_INJECTION = "notInjection"; - public static final String ID = "ID"; - private static final String ID_SYNTAX = "ID-SYNTAX"; - public static final String ID_PROGRAM_PARSING = ID_SYNTAX + POSTFIX; - - /** - * Initialize a grammar generator. - * - * @param baseK A Definition containing a K module giving the syntax of K itself. - * The default K syntax is defined in include/kast.k. - */ - public RuleGrammarGenerator { - } - - private Set renameKItem2Bottom(Set def) { - // TODO: do renaming of KItem and K in the LHS to KBott? - return def; + } + + public static boolean isParserSort(Sort s) { + return kSorts.contains(s) || s.name().startsWith("#") || s.isNat(); + } + + /* use this overload if you don't need to profile rule parse times. */ + public static ParseInModule getCombinedGrammar(Module mod, boolean strict, FileUtil files) { + return getCombinedGrammar(mod, strict, false, false, false, files, null, false); + } + + public static ParseInModule getCombinedGrammar( + Module mod, boolean strict, FileUtil files, boolean partialParseDebug) { + return getCombinedGrammar(mod, strict, false, false, false, files, null, partialParseDebug); + } + + public static ParseInModule getCombinedGrammar( + Module mod, boolean strict, boolean timing, FileUtil files) { + return getCombinedGrammar(mod, strict, timing, false, false, files, null, false); + } + + public static ParseInModule getCombinedGrammar( + Module mod, boolean strict, boolean timing, FileUtil files, String debugTypeInference) { + return getCombinedGrammar(mod, strict, timing, false, false, files, debugTypeInference, false); + } + + public static ParseInModule getCombinedGrammar( + Module mod, boolean strict, boolean timing, boolean isBison, FileUtil files) { + return getCombinedGrammar(mod, strict, timing, isBison, false, files, null, false); + } + + public static ParseInModule getCombinedGrammar( + Module mod, + boolean strict, + boolean timing, + boolean isBison, + boolean forGlobalScanner, + FileUtil files) { + return getCombinedGrammar(mod, strict, timing, isBison, forGlobalScanner, files, null, false); + } + + public static ParseInModule getCombinedGrammar( + Module mod, + Scanner scanner, + boolean strict, + boolean timing, + boolean isBison, + FileUtil files) { + return getCombinedGrammar(mod, scanner, strict, timing, isBison, files, null, false); + } + + // the forGlobalScanner flag tells the ParseInModule class not to exclude + // private syntax from the grammar generated for the module. It should + // not be used when actually performing parsing as this will lead to + // incorrect grammars. However, it is used in one place in the code: + // during rule parsing, we generate a single scanner to scan all the + // modules. This must include the private syntax of those modules, + // otherwise we would not be able to use it to scan the modules in which + // that private syntax is visible. + + /** + * Create the rule parser for the given module. It creates a module which includes the given + * module and the base K module given to the constructor. The new module contains syntax + * declaration for Casts and the diamond which connects the user concrete syntax with K syntax. + * + * @param mod module for which to create the parser. + * @param partialParseDebug + * @return parser which applies disambiguation filters by default. + */ + public static ParseInModule getCombinedGrammar( + Module mod, + boolean strict, + boolean timing, + boolean isBison, + boolean forGlobalScanner, + FileUtil files, + String debugTypeInference, + boolean partialParseDebug) { + return new ParseInModule( + mod, + strict, + timing, + isBison, + forGlobalScanner, + files, + debugTypeInference, + partialParseDebug); + } + + public static ParseInModule getCombinedGrammar( + Module mod, + Scanner scanner, + boolean strict, + boolean timing, + boolean isBison, + FileUtil files, + String debugTypeInference, + boolean partialParseDebug) { + return new ParseInModule( + mod, scanner, strict, timing, isBison, false, files, debugTypeInference, partialParseDebug); + } + + public static Tuple3 getCombinedGrammarImpl( + Module mod, boolean isBison, boolean forGlobalScanner) { + Set prods = new HashSet<>(); + Set extensionProds = new HashSet<>(); + Set disambProds; + + Module origMod = mod; + + if (!forGlobalScanner) { + mod = mod.signature(); } - /** - * Creates the seed module that can be used to parse rules. - * Imports module markers RULE-CELLS and K found in /include/kast.k. - * - * @param mod The user defined module from which to start. - * @return a new module which imports the original user module and a set of marker modules. - */ - public Module getRuleGrammar(Module mod) { - return getGrammar(mod, RULE_CELLS); - } - - private Module getGrammar(Module mod, String name) { - // import RULE-CELLS in order to parse cells specific to rules - Module newM = new Module(mod.name() + "-" + name - , (scala.collection.Set) mod.imports().$bar(Set(Import(baseK.getModule(K).get(), true), Import(baseK.getModule(name).get(), true), Import(baseK.getModule(DEFAULT_LAYOUT).get(), true))) - , mod.localSentences() - , mod.att() - ); - return newM; - } - - /** - * Creates the seed module that can be used to parse configurations. - * Imports module markers CONFIG-CELLS and K found in /include/kast.k. - * - * @param mod The user defined module from which to start. - * @return a new module which imports the original user module and a set of marker modules. - */ - public Module getConfigGrammar(Module mod) { - return getGrammar(mod, CONFIG_CELLS); - } - - /** - * Creates the seed module that can be used to parse programs. - * Imports module markers PROGRAM-LISTS found in /include/kast.k. - * - * @param mod The user defined module from which to start. - * @return a new module which imports the original user module and a set of marker modules. - */ - public Module getProgramsGrammar(Module mod) { - - if (mod.name().endsWith(POSTFIX)) { - return mod; - } else { - Module newMod = ModuleTransformer.from(oldMod -> { - UnaryOperator f = _import -> { - Option programParsing = baseK.getModule(_import.module().name() + "-PROGRAM-PARSING"); - if (programParsing.isDefined()) { - return Import(programParsing.get(), _import.isPublic()); + if (isBison) { + mod = + ModuleTransformer.from( + m -> { + if (m.att().contains(Att.NOT_LR1())) { + return Module(m.name(), m.imports(), Set(), m.att()); } - return _import; - }; - Set imports = stream(oldMod.imports()).map(f).collect(Collectors.toSet()); - return Module.apply(oldMod.name(), immutable(imports), oldMod.localSentences(), oldMod.att()); - }, "apply program parsing modules").apply(mod); - - Set modules = new HashSet(); - for (Import m : iterable(newMod.imports())) { - modules.add(m); - } - - // import PROGRAM-LISTS so user lists are modified to parse programs - modules.add(Import(baseK.getModule(PROGRAM_LISTS).get(), true)); - - // check if `#Layout` has been declared, import `DEFAULT-LAYOUT` if not - if (!mod.allSorts().contains(Sorts.Layout())) { - modules.add(Import(baseK.getModule(DEFAULT_LAYOUT).get(), true)); - } - - return Module.apply(mod.name() + "-PROGRAM-GRAMMAR", immutable(modules), newMod.localSentences(), newMod.att()); - } - } - - public static boolean isParserSort(Sort s) { - return kSorts.contains(s) || s.name().startsWith("#") || s.isNat(); + return m; + }, + "strip not-lr1 modules from bison grammar") + .apply(mod); } - /* use this overload if you don't need to profile rule parse times. */ - public static ParseInModule getCombinedGrammar(Module mod, boolean strict, FileUtil files) { - return getCombinedGrammar(mod, strict, false, false, false, files, null, false); - } - - public static ParseInModule getCombinedGrammar(Module mod, boolean strict, FileUtil files, boolean partialParseDebug) { - return getCombinedGrammar(mod, strict, false, false, false, files, null, partialParseDebug); - } - - public static ParseInModule getCombinedGrammar(Module mod, boolean strict, boolean timing, FileUtil files) { - return getCombinedGrammar(mod, strict, timing, false, false, files, null, false); - } - - public static ParseInModule getCombinedGrammar(Module mod, boolean strict, boolean timing, FileUtil files, String debugTypeInference) { - return getCombinedGrammar(mod, strict, timing, false, false, files, debugTypeInference, false); + if (mod.importedModuleNames().contains(AUTO_CASTS)) { // create the diamond + Set temp; + for (Sort srt : iterable(mod.allSorts())) { + if (!isParserSort(srt) || mod.subsorts().directlyLessThan(Sorts.KVariable(), srt)) { + // K ::= K "::Sort" | K ":Sort" | K "<:Sort" | K ":>Sort" + prods.addAll(makeCasts(Sorts.KBott(), Sorts.K(), srt, srt)); + } + } + prods.addAll(makeCasts(Sorts.KLabel(), Sorts.KLabel(), Sorts.KLabel(), Sorts.KLabel())); + prods.addAll(makeCasts(Sorts.KList(), Sorts.KList(), Sorts.KList(), Sorts.KList())); + prods.addAll(makeCasts(Sorts.KBott(), Sorts.K(), Sorts.KItem(), Sorts.KItem())); + prods.addAll(makeCasts(Sorts.KBott(), Sorts.K(), Sorts.K(), Sorts.K())); + for (SortSynonym syn : iterable(mod.sortSynonyms())) { + prods.addAll(makeCasts(Sorts.KBott(), Sorts.K(), syn.newSort(), syn.oldSort())); + } } - public static ParseInModule getCombinedGrammar(Module mod, boolean strict, boolean timing, boolean isBison, FileUtil files) { - return getCombinedGrammar(mod, strict, timing, isBison, false, files, null, false); + if (mod.importedModuleNames().contains(SORT_PREDICATES)) { + for (Sort s : iterable(mod.allSorts())) { + prods.addAll(new GenerateSortPredicateSyntax().gen(mod, s)); + prods.addAll(new GenerateSortProjections(mod).gen(s).collect(Collectors.toSet())); + } } - public static ParseInModule getCombinedGrammar(Module mod, boolean strict, boolean timing, boolean isBison, boolean forGlobalScanner, FileUtil files) { - return getCombinedGrammar(mod, strict, timing, isBison, forGlobalScanner, files, null, false); + for (Production p : iterable(mod.productions())) + prods.addAll(new GenerateSortProjections(mod).gen(p).collect(Collectors.toSet())); + + // Because parametric productions introduce Non-Terminal names that do not exist in the module, + // we can't + // create a valid parsing grammar. + // Here we go through every parametric production and, we replace it with concrete sorts. + // We have 4 distinct cases depending on whether the return type of the production is parametric + // or not: + // 1 - prod sort is a simple parameter + // 2 - prod sort is a parametric sort + // 3 - special case for `syntax {Sort} KItem ::= Sort` // the subsort production found in + // kast.md + // 4 - prod sort is a concrete sort. + // Because, at parse time, the prod sort is decided by the parent production we can instantiate + // it with concrete + // sorts. Parameters appearing only on the RHS of a production do not have any sort information + // at parsing time, + // so we use the top sort `K` as a placeholder. Meaning we can expect anything there. The + // connection is then handled + // at case 3 where we add subsorts for every parsable sort in the module. + // TODO: for now we only have `MInt{Widht}` as parametric sorts, so we can treat it as a corner + // case. + // LHS gets instantiated the same way, but RHS gets instantiated with `MInt{K}` and we need to + // add an extra subsort + // production `syntax MInt{K} ::= MInt{6}` to make the connection between the new sort and the + // concrete sorts. + // This production doesn't have a klabel so it's not going to appear in the final AST. + List allSorts = + stream(mod.allSorts()) + .filter(s -> (!isParserSort(s) || s.equals(Sorts.KItem()) || s.equals(Sorts.K()))) + .collect(Collectors.toList()); + for (SortHead sh : mutable(mod.definedInstantiations()).keySet()) { + for (Sort s : mutable(mod.definedInstantiations().apply(sh))) { + // syntax MInt{K} ::= MInt{6} + Production p1 = + Production( + Option.empty(), Seq(), Sort(s.name(), Sorts.K()), Seq(NonTerminal(s)), Att()); + prods.add(p1); + } } - - public static ParseInModule getCombinedGrammar(Module mod, Scanner scanner, boolean strict, boolean timing, boolean isBison, FileUtil files) { - return getCombinedGrammar(mod, scanner, strict, timing, isBison, files, null, false); + for (Production p : iterable(mod.productions())) { + if (p.params().nonEmpty()) { + if (p.params().contains(p.sort())) { // case 1 + // syntax {P, R} P ::= P "+" R "-" Int + // syntax S ::= S "+" K "-" Int + for (Sort s : allSorts) { + List instantiationMask = new ArrayList<>(); + for (Sort param : mutable(p.params())) + if (param.equals(p.sort())) instantiationMask.add(s); + else instantiationMask.add(Sorts.K()); + Production subst = p.substitute(immutable(instantiationMask)); + Production p1 = + Production( + subst.klabel().map(lbl -> KLabel(lbl.name())), + Seq(), + subst.sort(), + subst.items(), + subst.att().add(Att.ORIGINAL_PRD(), Production.class, p)); + prods.add(p1); + } + } else if (!p.sort().params().isEmpty()) { // case 2 + // TODO: assuming sorts have only one parameter for now + // syntax {W, X, Y} MInt{W} ::= MInt{W} "+" MInt{X} "-" Y "/" Int + // syntax MInt{6} ::= MInt{6} "+" MInt{K} "-" K "/" Int + Set instantations = mutable(mod.definedInstantiations().apply(p.sort().head())); + for (Sort s : instantations) { + List instantiationMask = new ArrayList<>(); + for (Sort param : mutable(p.params())) + if (param.equals(p.sort().params().apply(0))) + instantiationMask.add(s.params().apply(0)); + else instantiationMask.add(Sorts.K()); + Production subst = p.substitute(immutable(instantiationMask)); + Production p1 = + Production( + subst.klabel().map(lbl -> KLabel(lbl.name())), + Seq(), + subst.sort(), + subst.items(), + subst.att().add(Att.ORIGINAL_PRD(), Production.class, p)); + prods.add(p1); + } + } else if (p.isSyntacticSubsort()) { // case 3 + // a single production found in kast.md that handles the subsorting to the top sort + // syntax {Sort} KItem ::= Sort + // syntax KItem ::= Int + for (Sort s : allSorts) { + if (!p.params().contains(p.sort()) && (s.equals(Sorts.KItem()) || s.equals(Sorts.K()))) + continue; + List instantiationMask = new ArrayList<>(); + instantiationMask.add(s); + Production subst = p.substitute(immutable(instantiationMask)); + Production p1 = + Production( + subst.klabel().map(lbl -> KLabel(lbl.name())), + Seq(), + subst.sort(), + subst.items(), + subst.att().add(Att.ORIGINAL_PRD(), Production.class, p)); + prods.add(p1); + } + } else { // case 4 + // the rest of the productions that return a concrete sort can accept any sort inside + // syntax {P} Int ::= P "+" Int + // syntax Int ::= K "+" Int + List instantiationMask = new ArrayList<>(); + for (Sort param : mutable(p.params())) instantiationMask.add(Sorts.K()); + Production subst = p.substitute(immutable(instantiationMask)); + Production p1 = + Production( + subst.klabel().map(lbl -> KLabel(lbl.name())), + Seq(), + subst.sort(), + subst.items(), + subst.att().add(Att.ORIGINAL_PRD(), Production.class, p)); + prods.add(p1); + } + } } - - // the forGlobalScanner flag tells the ParseInModule class not to exclude - // private syntax from the grammar generated for the module. It should - // not be used when actually performing parsing as this will lead to - // incorrect grammars. However, it is used in one place in the code: - // during rule parsing, we generate a single scanner to scan all the - // modules. This must include the private syntax of those modules, - // otherwise we would not be able to use it to scan the modules in which - // that private syntax is visible. - - /** - * Create the rule parser for the given module. - * It creates a module which includes the given module and the base K module given to the - * constructor. The new module contains syntax declaration for Casts and the diamond - * which connects the user concrete syntax with K syntax. - * - * @param mod module for which to create the parser. - * @param partialParseDebug - * @return parser which applies disambiguation filters by default. - */ - public static ParseInModule getCombinedGrammar(Module mod, boolean strict, boolean timing, boolean isBison, boolean forGlobalScanner, FileUtil files, String debugTypeInference, boolean partialParseDebug) { - return new ParseInModule(mod, strict, timing, isBison, forGlobalScanner, files, debugTypeInference, partialParseDebug); + extensionProds.addAll(prods); + + Set recordProds = new HashSet<>(); + if (mod.importedModuleNames().contains(RECORD_PRODS)) { + // these should be visible only in the parsing module + // but are required by config cell names + UidProvider uid = new UidProvider(mod.name()); + for (Production p : iterable(mod.productions())) { + if (p.isPrefixProduction()) { + recordProds.addAll(mutable(p.recordProductions(uid))); + } + } } - public static ParseInModule getCombinedGrammar(Module mod, Scanner scanner, boolean strict, boolean timing, boolean isBison, FileUtil files, String debugTypeInference, boolean partialParseDebug) { - return new ParseInModule(mod, scanner, strict, timing, isBison, false, files, debugTypeInference, partialParseDebug); + boolean addRuleCells; + if (mod.importedModuleNames() + .contains(RULE_CELLS)) { // prepare cell productions for rule parsing + // make sure a configuration actually exists, otherwise ConfigurationInfoFromModule explodes. + addRuleCells = + mod.sentences().exists(p -> p instanceof Production && p.att().contains(Att.CELL())); + } else { + addRuleCells = false; } - - public static Tuple3 getCombinedGrammarImpl(Module mod, boolean isBison, boolean forGlobalScanner) { - Set prods = new HashSet<>(); - Set extensionProds = new HashSet<>(); - Set disambProds; - - Module origMod = mod; - - if (!forGlobalScanner) { - mod = mod.signature(); - } - - if (isBison) { - mod = ModuleTransformer.from(m -> { - if (m.att().contains(Att.NOT_LR1())) { - return Module(m.name(), m.imports(), Set(), m.att()); - } - return m; - }, "strip not-lr1 modules from bison grammar").apply(mod); - } - - if (mod.importedModuleNames().contains(AUTO_CASTS)) { // create the diamond - Set temp; - for (Sort srt : iterable(mod.allSorts())) { - if (!isParserSort(srt) || mod.subsorts().directlyLessThan(Sorts.KVariable(), srt)) { - // K ::= K "::Sort" | K ":Sort" | K "<:Sort" | K ":>Sort" - prods.addAll(makeCasts(Sorts.KBott(), Sorts.K(), srt, srt)); - } - } - prods.addAll(makeCasts(Sorts.KLabel(), Sorts.KLabel(), Sorts.KLabel(), Sorts.KLabel())); - prods.addAll(makeCasts(Sorts.KList(), Sorts.KList(), Sorts.KList(), Sorts.KList())); - prods.addAll(makeCasts(Sorts.KBott(), Sorts.K(), Sorts.KItem(), Sorts.KItem())); - prods.addAll(makeCasts(Sorts.KBott(), Sorts.K(), Sorts.K(), Sorts.K())); - for (SortSynonym syn : iterable(mod.sortSynonyms())) { - prods.addAll(makeCasts(Sorts.KBott(), Sorts.K(), syn.newSort(), syn.oldSort())); - } - } - - if (mod.importedModuleNames().contains(SORT_PREDICATES)) { - for (Sort s : iterable(mod.allSorts())) { - prods.addAll(new GenerateSortPredicateSyntax().gen(mod, s)); - prods.addAll(new GenerateSortProjections(mod).gen(s).collect(Collectors.toSet())); - } - } - - for (Production p : iterable(mod.productions())) - prods.addAll(new GenerateSortProjections(mod).gen(p).collect(Collectors.toSet())); - - // Because parametric productions introduce Non-Terminal names that do not exist in the module, we can't - // create a valid parsing grammar. - // Here we go through every parametric production and, we replace it with concrete sorts. - // We have 4 distinct cases depending on whether the return type of the production is parametric or not: - // 1 - prod sort is a simple parameter - // 2 - prod sort is a parametric sort - // 3 - special case for `syntax {Sort} KItem ::= Sort` // the subsort production found in kast.md - // 4 - prod sort is a concrete sort. - // Because, at parse time, the prod sort is decided by the parent production we can instantiate it with concrete - // sorts. Parameters appearing only on the RHS of a production do not have any sort information at parsing time, - // so we use the top sort `K` as a placeholder. Meaning we can expect anything there. The connection is then handled - // at case 3 where we add subsorts for every parsable sort in the module. - // TODO: for now we only have `MInt{Widht}` as parametric sorts, so we can treat it as a corner case. - // LHS gets instantiated the same way, but RHS gets instantiated with `MInt{K}` and we need to add an extra subsort - // production `syntax MInt{K} ::= MInt{6}` to make the connection between the new sort and the concrete sorts. - // This production doesn't have a klabel so it's not going to appear in the final AST. - List allSorts = stream(mod.allSorts()).filter( - s -> (!isParserSort(s) || s.equals(Sorts.KItem()) || s.equals(Sorts.K()))).collect(Collectors.toList()); - for (SortHead sh : mutable(mod.definedInstantiations()).keySet()) { - for (Sort s : mutable(mod.definedInstantiations().apply(sh))) { - // syntax MInt{K} ::= MInt{6} - Production p1 = Production(Option.empty(), Seq(), Sort(s.name(), Sorts.K()), Seq(NonTerminal(s)), Att()); - prods.add(p1); - } - } - for (Production p : iterable(mod.productions())) { - if (p.params().nonEmpty()) { - if (p.params().contains(p.sort())) { // case 1 - // syntax {P, R} P ::= P "+" R "-" Int - // syntax S ::= S "+" K "-" Int - for (Sort s : allSorts) { - List instantiationMask = new ArrayList<>(); - for (Sort param : mutable(p.params())) - if (param.equals(p.sort())) - instantiationMask.add(s); - else - instantiationMask.add(Sorts.K()); - Production subst = p.substitute(immutable(instantiationMask)); - Production p1 = Production(subst.klabel().map(lbl -> KLabel(lbl.name())), Seq(), subst.sort(), subst.items(), subst.att().add(Att.ORIGINAL_PRD(), Production.class, p)); - prods.add(p1); - } - } else if (!p.sort().params().isEmpty()) { // case 2 - // TODO: assuming sorts have only one parameter for now - // syntax {W, X, Y} MInt{W} ::= MInt{W} "+" MInt{X} "-" Y "/" Int - // syntax MInt{6} ::= MInt{6} "+" MInt{K} "-" K "/" Int - Set instantations = mutable(mod.definedInstantiations().apply(p.sort().head())); - for (Sort s : instantations) { - List instantiationMask = new ArrayList<>(); - for (Sort param : mutable(p.params())) - if (param.equals(p.sort().params().apply(0))) - instantiationMask.add(s.params().apply(0)); - else - instantiationMask.add(Sorts.K()); - Production subst = p.substitute(immutable(instantiationMask)); - Production p1 = Production(subst.klabel().map(lbl -> KLabel(lbl.name())), Seq(), subst.sort(), subst.items(), subst.att().add(Att.ORIGINAL_PRD(), Production.class, p)); - prods.add(p1); + boolean addConfigCells = mod.importedModuleNames().contains(CONFIG_CELLS); + Set parseProds; + if (addRuleCells) { + ConfigurationInfo cfgInfo = new ConfigurationInfoFromModule(mod); + parseProds = + Stream.concat(prods.stream(), stream(mod.sentences())) + .flatMap( + s -> { + if (s instanceof Production && s.att().contains(Att.CELL_COLLECTION())) { + return Stream.empty(); } - } else if (p.isSyntacticSubsort()) { // case 3 - // a single production found in kast.md that handles the subsorting to the top sort - // syntax {Sort} KItem ::= Sort - // syntax KItem ::= Int - for (Sort s : allSorts) { - if (!p.params().contains(p.sort()) && (s.equals(Sorts.KItem()) || s.equals(Sorts.K()))) - continue; - List instantiationMask = new ArrayList<>(); - instantiationMask.add(s); - Production subst = p.substitute(immutable(instantiationMask)); - Production p1 = Production(subst.klabel().map(lbl -> KLabel(lbl.name())), Seq(), subst.sort(), subst.items(), subst.att().add(Att.ORIGINAL_PRD(), Production.class, p)); - prods.add(p1); - } - } else { // case 4 - // the rest of the productions that return a concrete sort can accept any sort inside - // syntax {P} Int ::= P "+" Int - // syntax Int ::= K "+" Int - List instantiationMask = new ArrayList<>(); - for (Sort param : mutable(p.params())) - instantiationMask.add(Sorts.K()); - Production subst = p.substitute(immutable(instantiationMask)); - Production p1 = Production(subst.klabel().map(lbl -> KLabel(lbl.name())), Seq(), subst.sort(), subst.items(), subst.att().add(Att.ORIGINAL_PRD(), Production.class, p)); - prods.add(p1); - } - } - } - extensionProds.addAll(prods); - - Set recordProds = new HashSet<>(); - if (mod.importedModuleNames().contains(RECORD_PRODS)) { - // these should be visible only in the parsing module - // but are required by config cell names - UidProvider uid = new UidProvider(mod.name()); - for (Production p : iterable(mod.productions())) { - if (p.isPrefixProduction()) { - recordProds.addAll(mutable(p.recordProductions(uid))); - } - } - } - - boolean addRuleCells; - if (mod.importedModuleNames().contains(RULE_CELLS)) { // prepare cell productions for rule parsing - // make sure a configuration actually exists, otherwise ConfigurationInfoFromModule explodes. - addRuleCells = mod.sentences().exists(p -> p instanceof Production && p.att().contains(Att.CELL())); - } else { - addRuleCells = false; - } - boolean addConfigCells = mod.importedModuleNames().contains(CONFIG_CELLS); - Set parseProds; - if (addRuleCells) { - ConfigurationInfo cfgInfo = new ConfigurationInfoFromModule(mod); - parseProds = Stream.concat(prods.stream(), stream(mod.sentences())).flatMap(s -> { - if (s instanceof Production && s.att().contains(Att.CELL_COLLECTION())) { - return Stream.empty(); - } - if (s instanceof Production p && (s.att().contains(Att.CELL()))) { - // assuming that productions tagged with 'cell' start and end with terminals, and only have non-terminals in the middle - assert p.items().head() instanceof Terminal || p.items().head() instanceof RegexTerminal; - assert p.items().last() instanceof Terminal || p.items().last() instanceof RegexTerminal; - final ProductionItem body; - if (cfgInfo.isLeafCell(p.sort())) { + if (s instanceof Production p && (s.att().contains(Att.CELL()))) { + // assuming that productions tagged with 'cell' start and end with terminals, + // and only have non-terminals in the middle + assert p.items().head() instanceof Terminal + || p.items().head() instanceof RegexTerminal; + assert p.items().last() instanceof Terminal + || p.items().last() instanceof RegexTerminal; + final ProductionItem body; + if (cfgInfo.isLeafCell(p.sort())) { body = p.items().tail().head(); - } else { + } else { body = NonTerminal(Sorts.Bag()); + } + final ProductionItem optDots = NonTerminal(Sort("#OptionalDots")); + Seq pi = + Seq(p.items().head(), optDots, body, optDots, p.items().last()); + Production p1 = Production(p.klabel().get(), p.sort(), pi, p.att()); + Production p2 = Production(Seq(), Sorts.Cell(), Seq(NonTerminal(p.sort()))); + return Stream.of(p1, p2); } - final ProductionItem optDots = NonTerminal(Sort("#OptionalDots")); - Seq pi = Seq(p.items().head(), optDots, body, optDots, p.items().last()); - Production p1 = Production(p.klabel().get(), p.sort(), pi, p.att()); - Production p2 = Production(Seq(), Sorts.Cell(), Seq(NonTerminal(p.sort()))); - return Stream.of(p1, p2); - } - if (s instanceof Production p && (s.att().contains(Att.CELL_FRAGMENT(), Sort.class))) { - Production p1 = Production(Seq(), Sorts.Cell(), Seq(NonTerminal(p.sort()))); - return Stream.of(p, p1); - } - return Stream.of(s); - }).collect(Collectors.toSet()); - } else if (addConfigCells) { - // remove cells from parsing config cells so they don't conflict with the production in kast.k - // also add all matching terminals to the #CellName sort - for (Sentence prod : extensionProds) { - addCellNameProd(prods, prod); - } - for (Sentence prod : recordProds) { - addCellNameProd(prods, prod); - } - for (Sentence prod : iterable(mod.productions())) { - addCellNameProd(prods, prod); - } - parseProds = Stream.concat(prods.stream(), stream(mod.sentences()).filter(s -> !s.att().contains(Att.CELL()))).collect(Collectors.toSet()); - } else - parseProds = Stream.concat(prods.stream(), stream(mod.sentences())).collect(Collectors.toSet()); - - if (mod.importedModuleNames().contains(AUTO_FOLLOW)) { - Object PRESENT = new Object(); - PatriciaTrie terminals = new PatriciaTrie<>(); // collect all terminals so we can do automatic follow restriction for prefix terminals - parseProds.stream().filter(sent -> sent instanceof Production).forEach(p -> stream(((Production) p).items()).forEach(i -> { - if (i instanceof Terminal) terminals.put(((Terminal) i).value(), PRESENT); - })); - parseProds = parseProds.stream().map(s -> { - if (s instanceof Production p) { - if (p.sort().name().startsWith("#")) - return p; // don't do anything for such productions since they are advanced features - // rewrite productions to contain follow restrictions for prefix terminals - // example _==_ and _==K_ can produce ambiguities. Rewrite the first into _(==(?![K])_ - // this also takes care of casting and productions that have ":" - if (p.klabel().isDefined()) + if (s instanceof Production p + && (s.att().contains(Att.CELL_FRAGMENT(), Sort.class))) { + Production p1 = Production(Seq(), Sorts.Cell(), Seq(NonTerminal(p.sort()))); + return Stream.of(p, p1); + } + return Stream.of(s); + }) + .collect(Collectors.toSet()); + } else if (addConfigCells) { + // remove cells from parsing config cells so they don't conflict with the production in kast.k + // also add all matching terminals to the #CellName sort + for (Sentence prod : extensionProds) { + addCellNameProd(prods, prod); + } + for (Sentence prod : recordProds) { + addCellNameProd(prods, prod); + } + for (Sentence prod : iterable(mod.productions())) { + addCellNameProd(prods, prod); + } + parseProds = + Stream.concat( + prods.stream(), + stream(mod.sentences()).filter(s -> !s.att().contains(Att.CELL()))) + .collect(Collectors.toSet()); + } else + parseProds = + Stream.concat(prods.stream(), stream(mod.sentences())).collect(Collectors.toSet()); + + if (mod.importedModuleNames().contains(AUTO_FOLLOW)) { + Object PRESENT = new Object(); + PatriciaTrie terminals = + new PatriciaTrie<>(); // collect all terminals so we can do automatic follow restriction + // for prefix terminals + parseProds.stream() + .filter(sent -> sent instanceof Production) + .forEach( + p -> + stream(((Production) p).items()) + .forEach( + i -> { + if (i instanceof Terminal) + terminals.put(((Terminal) i).value(), PRESENT); + })); + parseProds = + parseProds.stream() + .map( + s -> { + if (s instanceof Production p) { + if (p.sort().name().startsWith("#")) + return p; // don't do anything for such productions since they are advanced + // features + // rewrite productions to contain follow restrictions for prefix terminals + // example _==_ and _==K_ can produce ambiguities. Rewrite the first into + // _(==(?![K])_ + // this also takes care of casting and productions that have ":" + if (p.klabel().isDefined()) p = Production(p.klabel().get(), p.sort(), p.items(), p.att()); - else - p = Production(p.params(), p.sort(), p.items(), p.att()); - return p; - } - return s; - }).collect(Collectors.toSet()); - } - - disambProds = parseProds.stream().collect(Collectors.toSet()); - if (mod.importedModuleNames().contains(PROGRAM_LISTS)) { - Set prods3 = new HashSet<>(); - // if no start symbol has been defined in the configuration, then use K - for (Sort srt : iterable(mod.allSorts())) { - if (!isParserSort(srt) && !mod.listSorts().contains(srt)) { - // K ::= Sort - prods3.add(Production(Seq(), Sorts.KItem(), Seq(NonTerminal(srt)), Att())); - } - } - // for each triple, generate a new pattern which works better for parsing lists in programs. - prods3.addAll(parseProds.stream().collect(Collectors.toSet())); - Set res = new HashSet<>(); - for (UserList ul : UserList.getLists(prods3)) { - Production prod1, prod2, prod3 = null, prod4 = null, prod5 = null; - - Att newAtts = ul.attrs.remove(Att.USER_LIST()); - if (ul.leftAssoc && ul.nonEmpty) { - prod1 = Production(ul.klabel, ul.sort, - Seq(NonTerminal(ul.sort), Terminal(ul.separator), NonTerminal(ul.childSort)), - newAtts.add(Att.ORIGINAL_PRD(), Production.class, ul.pList)); - prod2 = Production(Seq(), ul.sort, - Seq(NonTerminal(ul.childSort)), - newAtts.add(Att.ORIGINAL_PRD(), Production.class, ul.pList).add(Att.USER_LIST(), ul.klabel.name()).add(Att.USER_LIST_TERMINATOR(), ul.terminatorKLabel.name())); - prod3 = Production(ul.terminatorKLabel, Sort(ul.sort.name() + "#Terminator", ul.sort.params()), Seq(Terminal("")), - newAtts.remove(Att.FORMAT()).add(Att.ORIGINAL_PRD(), Production.class, ul.pTerminator)); - } else if (ul.leftAssoc) { - throw KEMException.compilerError("Cannot use List with --bison-lists", ul.pList); - } else { - // Es#Terminator ::= "" [klabel('.Es)] - prod1 = Production(ul.terminatorKLabel, Sort(ul.sort.name() + "#Terminator", ul.sort.params()), Seq(Terminal("")), - newAtts.remove(Att.FORMAT()).add(Att.ORIGINAL_PRD(), Production.class, ul.pTerminator)); - // Ne#Es ::= E "," Ne#Es [klabel('_,_)] - prod2 = Production(ul.klabel, Sort("Ne#" + ul.sort.name(), ul.sort.params()), - Seq(NonTerminal(ul.childSort), Terminal(ul.separator), NonTerminal(Sort("Ne#" + ul.sort.name(), ul.sort.params()))), - newAtts.add(Att.ORIGINAL_PRD(), Production.class, ul.pList)); - // Ne#Es ::= E "" Es#Terminator [klabel('_,_)] - prod3 = Production(ul.klabel, Sort("Ne#" + ul.sort.name(), ul.sort.params()), - Seq(NonTerminal(ul.childSort), Terminal(""), NonTerminal(Sort(ul.sort.name() + "#Terminator", ul.sort.params()))), - newAtts.add(Att.ORIGINAL_PRD(), Production.class, ul.pList)); - // Es ::= Ne#Es - prod4 = Production(Seq(), ul.sort, Seq(NonTerminal(Sort("Ne#" + ul.sort.name(), ul.sort.params()))), Att().add(Att.NOT_INJECTION())); - // Es ::= Es#Terminator // if the list is * - prod5 = Production(Seq(), ul.sort, Seq(NonTerminal(Sort(ul.sort.name() + "#Terminator", ul.sort.params()))), Att().add(Att.NOT_INJECTION())); - } - - res.add(prod1); - res.add(prod2); - res.add(prod3); - if (!ul.leftAssoc) { - res.add(prod4); - res.add(SyntaxSort(Seq(), Sort(ul.sort.name() + "#Terminator", ul.sort.params()))); - res.add(SyntaxSort(Seq(), Sort("Ne#" + ul.sort.name(), ul.sort.params()))); - if (!ul.nonEmpty) { - res.add(prod5); + else p = Production(p.params(), p.sort(), p.items(), p.att()); + return p; } - } - } - res.addAll(prods3.stream().filter(p -> !(p instanceof Production && (p.att().contains(Att.GENERATED_BY_LIST_SUBSORTING()) || p.att().contains(Att.USER_LIST())))).collect(Collectors.toSet())); - parseProds = res; - } + return s; + }) + .collect(Collectors.toSet()); + } - if (mod.importedModuleNames().contains(RULE_LISTS)) { - Set res = new HashSet<>(); - for (UserList ul : UserList.getLists(parseProds)) { - Production prod1; - // Es ::= E - prod1 = Production(Seq(), ul.sort, Seq(NonTerminal(ul.childSort))); - res.add(prod1); - } - - parseProds.addAll(res); - disambProds.addAll(res); + disambProds = parseProds.stream().collect(Collectors.toSet()); + if (mod.importedModuleNames().contains(PROGRAM_LISTS)) { + Set prods3 = new HashSet<>(); + // if no start symbol has been defined in the configuration, then use K + for (Sort srt : iterable(mod.allSorts())) { + if (!isParserSort(srt) && !mod.listSorts().contains(srt)) { + // K ::= Sort + prods3.add(Production(Seq(), Sorts.KItem(), Seq(NonTerminal(srt)), Att())); + } + } + // for each triple, generate a new pattern which works better for parsing lists in programs. + prods3.addAll(parseProds.stream().collect(Collectors.toSet())); + Set res = new HashSet<>(); + for (UserList ul : UserList.getLists(prods3)) { + Production prod1, prod2, prod3 = null, prod4 = null, prod5 = null; + + Att newAtts = ul.attrs.remove(Att.USER_LIST()); + if (ul.leftAssoc && ul.nonEmpty) { + prod1 = + Production( + ul.klabel, + ul.sort, + Seq(NonTerminal(ul.sort), Terminal(ul.separator), NonTerminal(ul.childSort)), + newAtts.add(Att.ORIGINAL_PRD(), Production.class, ul.pList)); + prod2 = + Production( + Seq(), + ul.sort, + Seq(NonTerminal(ul.childSort)), + newAtts + .add(Att.ORIGINAL_PRD(), Production.class, ul.pList) + .add(Att.USER_LIST(), ul.klabel.name()) + .add(Att.USER_LIST_TERMINATOR(), ul.terminatorKLabel.name())); + prod3 = + Production( + ul.terminatorKLabel, + Sort(ul.sort.name() + "#Terminator", ul.sort.params()), + Seq(Terminal("")), + newAtts + .remove(Att.FORMAT()) + .add(Att.ORIGINAL_PRD(), Production.class, ul.pTerminator)); + } else if (ul.leftAssoc) { + throw KEMException.compilerError("Cannot use List with --bison-lists", ul.pList); + } else { + // Es#Terminator ::= "" [klabel('.Es)] + prod1 = + Production( + ul.terminatorKLabel, + Sort(ul.sort.name() + "#Terminator", ul.sort.params()), + Seq(Terminal("")), + newAtts + .remove(Att.FORMAT()) + .add(Att.ORIGINAL_PRD(), Production.class, ul.pTerminator)); + // Ne#Es ::= E "," Ne#Es [klabel('_,_)] + prod2 = + Production( + ul.klabel, + Sort("Ne#" + ul.sort.name(), ul.sort.params()), + Seq( + NonTerminal(ul.childSort), + Terminal(ul.separator), + NonTerminal(Sort("Ne#" + ul.sort.name(), ul.sort.params()))), + newAtts.add(Att.ORIGINAL_PRD(), Production.class, ul.pList)); + // Ne#Es ::= E "" Es#Terminator [klabel('_,_)] + prod3 = + Production( + ul.klabel, + Sort("Ne#" + ul.sort.name(), ul.sort.params()), + Seq( + NonTerminal(ul.childSort), + Terminal(""), + NonTerminal(Sort(ul.sort.name() + "#Terminator", ul.sort.params()))), + newAtts.add(Att.ORIGINAL_PRD(), Production.class, ul.pList)); + // Es ::= Ne#Es + prod4 = + Production( + Seq(), + ul.sort, + Seq(NonTerminal(Sort("Ne#" + ul.sort.name(), ul.sort.params()))), + Att().add(Att.NOT_INJECTION())); + // Es ::= Es#Terminator // if the list is * + prod5 = + Production( + Seq(), + ul.sort, + Seq(NonTerminal(Sort(ul.sort.name() + "#Terminator", ul.sort.params()))), + Att().add(Att.NOT_INJECTION())); } - parseProds.addAll(recordProds); - Att att = mod.att(); - List notLrModules = stream(mod.importedModules()).filter(m -> m.att().contains(Att.NOT_LR1())).map(Module::name).collect(Collectors.toList()); - if (!notLrModules.isEmpty()) { - att = att.add(Att.NOT_LR1_MODULES(), notLrModules.toString()); + res.add(prod1); + res.add(prod2); + res.add(prod3); + if (!ul.leftAssoc) { + res.add(prod4); + res.add(SyntaxSort(Seq(), Sort(ul.sort.name() + "#Terminator", ul.sort.params()))); + res.add(SyntaxSort(Seq(), Sort("Ne#" + ul.sort.name(), ul.sort.params()))); + if (!ul.nonEmpty) { + res.add(prod5); + } } - Module extensionM = new Module(mod.name() + "-EXTENSION", Set(Import(origMod, true)), immutable(extensionProds), att); - Module disambM = new Module(mod.name() + "-DISAMB", Set(), immutable(disambProds), att); - Module parseM = new Module(mod.name() + "-PARSER", Set(), immutable(parseProds), att); - parseM.subsorts(); - return Tuple3.apply(extensionM, disambM, parseM); + } + res.addAll( + prods3.stream() + .filter( + p -> + !(p instanceof Production + && (p.att().contains(Att.GENERATED_BY_LIST_SUBSORTING()) + || p.att().contains(Att.USER_LIST())))) + .collect(Collectors.toSet())); + parseProds = res; } - private static final Pattern alphaNum = Pattern.compile("[A-Za-z][A-Za-z0-9\\-]*"); - - private static void addCellNameProd(Set prods, Sentence prod) { - if (prod instanceof Production) { - for (ProductionItem pi : iterable(((Production) prod).items())) { - if (pi instanceof Terminal t) { - if (alphaNum.matcher(t.value()).matches()) { - prods.add(Production(Seq(), Sorts.CellName(), Seq(t), Att().add(Att.TOKEN()))); - } - } - } - } + if (mod.importedModuleNames().contains(RULE_LISTS)) { + Set res = new HashSet<>(); + for (UserList ul : UserList.getLists(parseProds)) { + Production prod1; + // Es ::= E + prod1 = Production(Seq(), ul.sort, Seq(NonTerminal(ul.childSort))); + res.add(prod1); + } + + parseProds.addAll(res); + disambProds.addAll(res); } - private static Set makeCasts(Sort outerSort, Sort innerSort, Sort castSort, Sort labelSort) { - Set prods = new HashSet<>(); - Att attrs1 = Att().add(Sort.class, castSort); - prods.add(Production(KLabel("#SyntacticCast"), castSort, Seq(NonTerminal(labelSort), Terminal("::" + castSort.toString())), attrs1.add(Att.FORMAT(), "%1%2"))); - prods.add(Production(KLabel("#SemanticCastTo" + labelSort.toString()), labelSort, Seq(NonTerminal(labelSort), Terminal(":" + castSort)), attrs1.add(Att.FORMAT(), "%1%2"))); - prods.add(Production(KLabel("#InnerCast"), castSort, Seq(Terminal("{"), NonTerminal(labelSort), Terminal("}"), Terminal("<:" + castSort)), attrs1.add(Att.FORMAT(), "%1 %2 %3%4"))); - prods.add(Production(KLabel("#OuterCast"), labelSort, Seq(Terminal("{"), NonTerminal(innerSort), Terminal("}"), Terminal(":>" + castSort)), attrs1.add(Att.FORMAT(), "%1 %2 %3%4"))); - return prods; + parseProds.addAll(recordProds); + Att att = mod.att(); + List notLrModules = + stream(mod.importedModules()) + .filter(m -> m.att().contains(Att.NOT_LR1())) + .map(Module::name) + .collect(Collectors.toList()); + if (!notLrModules.isEmpty()) { + att = att.add(Att.NOT_LR1_MODULES(), notLrModules.toString()); + } + Module extensionM = + new Module( + mod.name() + "-EXTENSION", Set(Import(origMod, true)), immutable(extensionProds), att); + Module disambM = new Module(mod.name() + "-DISAMB", Set(), immutable(disambProds), att); + Module parseM = new Module(mod.name() + "-PARSER", Set(), immutable(parseProds), att); + parseM.subsorts(); + return Tuple3.apply(extensionM, disambM, parseM); + } + + private static final Pattern alphaNum = Pattern.compile("[A-Za-z][A-Za-z0-9\\-]*"); + + private static void addCellNameProd(Set prods, Sentence prod) { + if (prod instanceof Production) { + for (ProductionItem pi : iterable(((Production) prod).items())) { + if (pi instanceof Terminal t) { + if (alphaNum.matcher(t.value()).matches()) { + prods.add(Production(Seq(), Sorts.CellName(), Seq(t), Att().add(Att.TOKEN()))); + } + } + } } + } + + private static Set makeCasts( + Sort outerSort, Sort innerSort, Sort castSort, Sort labelSort) { + Set prods = new HashSet<>(); + Att attrs1 = Att().add(Sort.class, castSort); + prods.add( + Production( + KLabel("#SyntacticCast"), + castSort, + Seq(NonTerminal(labelSort), Terminal("::" + castSort.toString())), + attrs1.add(Att.FORMAT(), "%1%2"))); + prods.add( + Production( + KLabel("#SemanticCastTo" + labelSort.toString()), + labelSort, + Seq(NonTerminal(labelSort), Terminal(":" + castSort)), + attrs1.add(Att.FORMAT(), "%1%2"))); + prods.add( + Production( + KLabel("#InnerCast"), + castSort, + Seq(Terminal("{"), NonTerminal(labelSort), Terminal("}"), Terminal("<:" + castSort)), + attrs1.add(Att.FORMAT(), "%1 %2 %3%4"))); + prods.add( + Production( + KLabel("#OuterCast"), + labelSort, + Seq(Terminal("{"), NonTerminal(innerSort), Terminal("}"), Terminal(":>" + castSort)), + attrs1.add(Att.FORMAT(), "%1 %2 %3%4"))); + return prods; + } } diff --git a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/AddEmptyLists.java b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/AddEmptyLists.java index f03c0439400..003d42404d7 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/AddEmptyLists.java +++ b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/AddEmptyLists.java @@ -1,8 +1,21 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner.disambiguation; +import static org.kframework.Collections.*; +import static org.kframework.kore.KORE.*; import com.google.common.collect.Sets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; import org.kframework.POSet; import org.kframework.attributes.Att; import org.kframework.builtin.Sorts; @@ -11,6 +24,7 @@ import org.kframework.definition.NonTerminal; import org.kframework.definition.Production; import org.kframework.definition.ProductionItem; +import org.kframework.definition.UserList; import org.kframework.kore.KLabel; import org.kframework.kore.Sort; import org.kframework.parser.Constant; @@ -18,7 +32,6 @@ import org.kframework.parser.SetsGeneralTransformer; import org.kframework.parser.Term; import org.kframework.parser.TermCons; -import org.kframework.definition.UserList; import org.kframework.utils.errorsystem.KEMException; import org.pcollections.ConsPStack; import scala.Tuple2; @@ -26,235 +39,268 @@ import scala.util.Left; import scala.util.Right; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import static org.kframework.kore.KORE.*; -import static org.kframework.Collections.*; - -/** - * Transformer class adding the implicit terminator (.List{""}) to user defined lists. - */ +/** Transformer class adding the implicit terminator (.List{""}) to user defined lists. */ public class AddEmptyLists extends SetsGeneralTransformer { - private final Module m; - private final POSet subsorts; - private final scala.collection.Set listSorts; - private final Map> lists; - private final AddSortInjections inj; - private Sort expectedSort; + private final Module m; + private final POSet subsorts; + private final scala.collection.Set listSorts; + private final Map> lists; + private final AddSortInjections inj; + private Sort expectedSort; - public AddEmptyLists(Module m, Sort expectedSort) { - this.m = m; - subsorts = m.subsorts(); - listSorts = m.listSorts(); - lists = UserList.getLists(mutable(m.sentences())).stream().collect(Collectors.groupingBy(p -> p.sort)); - inj = new AddSortInjections(m); - this.expectedSort = expectedSort; - } + public AddEmptyLists(Module m, Sort expectedSort) { + this.m = m; + subsorts = m.subsorts(); + listSorts = m.listSorts(); + lists = + UserList.getLists(mutable(m.sentences())).stream() + .collect(Collectors.groupingBy(p -> p.sort)); + inj = new AddSortInjections(m); + this.expectedSort = expectedSort; + } - // instead of calling super.apply in the apply function below, we should - // call this function, because super.apply does not correctly set the - // expected sort before recursing, which means that we end up with - // the wrong sort computations otherwise - private Tuple2, Term>, Set> superApply(TermCons tc) { - Set warns = new HashSet<>(); - TermCons orig = tc; - Production p = inj.substituteProd(orig.production(), expectedSort, (i, fresh) -> getSort((ProductionReference)orig.get(i), fresh.nonterminals().apply(i).sort()), orig); - for (int i = 0; i < tc.items().size(); i++) { - Sort oldExpectedSort = expectedSort; - expectedSort = p.nonterminals().apply(i).sort(); - Tuple2, Term>, Set> rez = apply(tc.get(i)); - warns.addAll(rez._2()); - if (rez._1().isLeft()) { - return Tuple2.apply(rez._1(), warns); - } - tc = tc.with(i, rez._1().right().get()); - expectedSort = oldExpectedSort; - } - return Tuple2.apply(Right.apply(tc), warns); + // instead of calling super.apply in the apply function below, we should + // call this function, because super.apply does not correctly set the + // expected sort before recursing, which means that we end up with + // the wrong sort computations otherwise + private Tuple2, Term>, Set> superApply(TermCons tc) { + Set warns = new HashSet<>(); + TermCons orig = tc; + Production p = + inj.substituteProd( + orig.production(), + expectedSort, + (i, fresh) -> + getSort((ProductionReference) orig.get(i), fresh.nonterminals().apply(i).sort()), + orig); + for (int i = 0; i < tc.items().size(); i++) { + Sort oldExpectedSort = expectedSort; + expectedSort = p.nonterminals().apply(i).sort(); + Tuple2, Term>, Set> rez = apply(tc.get(i)); + warns.addAll(rez._2()); + if (rez._1().isLeft()) { + return Tuple2.apply(rez._1(), warns); + } + tc = tc.with(i, rez._1().right().get()); + expectedSort = oldExpectedSort; } + return Tuple2.apply(Right.apply(tc), warns); + } - @Override - public Tuple2, Term>, Set> apply(TermCons tc) { - TermCons orig = tc; - Production p = inj.substituteProd(orig.production(), expectedSort, (i, fresh) -> getSort((ProductionReference)orig.get(i), fresh.nonterminals().apply(i).sort()), orig); - java.util.Set warnings = new HashSet<>(); - - List reversed = tc.items().stream().collect(Collectors.toList()); - Collections.reverse(reversed); // TermCons with PStack requires the elements to be in the reverse order - Iterator items = reversed.iterator(); - List newItems = new LinkedList<>(); - boolean changed = false; + @Override + public Tuple2, Term>, Set> apply(TermCons tc) { + TermCons orig = tc; + Production p = + inj.substituteProd( + orig.production(), + expectedSort, + (i, fresh) -> + getSort((ProductionReference) orig.get(i), fresh.nonterminals().apply(i).sort()), + orig); + java.util.Set warnings = new HashSet<>(); - if (tc.production().klabel().isDefined()) { - final String tcLabelName = tc.production().klabel().get().name(); - // Never add a list wrapper between a sort annotation and the annotated term - if (tcLabelName.equals("#SyntacticCast") - || tcLabelName.startsWith("#SemanticCastTo") - || tcLabelName.equals("#InnerCast")) { - return superApply(tc); - } - } + List reversed = tc.items().stream().collect(Collectors.toList()); + Collections.reverse( + reversed); // TermCons with PStack requires the elements to be in the reverse order + Iterator items = reversed.iterator(); + List newItems = new LinkedList<>(); + boolean changed = false; - for (ProductionItem pi : mutable(p.items())) { - if (!(pi instanceof NonTerminal)) - continue; - Sort oldExpectedSort = expectedSort; - expectedSort = ((NonTerminal)pi).sort(); - ProductionReference child = (ProductionReference) items.next(); - Sort childSort = getSort(child, expectedSort); - if (listSorts.contains(expectedSort) && - !(subsorts.lessThanEq(childSort, expectedSort) && listSorts.contains(childSort))) { - final boolean isBracket = child.production().att().contains(Att.BRACKET()); - if (isBracket) { - newItems.add(child); - } else if (childSort.equals(Sorts.K()) || !subsorts.lessThan(childSort, expectedSort)) { - newItems.add(child); - } else { - Set least = subsorts.minimal(stream(listSorts).filter(s -> subsorts.greaterThanEq(lists.get(s).get(0).childSort, childSort) && subsorts.lessThanEq(s, expectedSort)).collect(Collectors.toList())); - if (least.size() != 1) { - String msg = "Overloaded term does not have a least sort. Possible sorts: " + least; - return new Tuple2<>(Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, tc))), warnings); - } - UserList ul = lists.get(least.iterator().next()).get(0); - Set leastTerm = subsorts.minimal(stream(listSorts).filter(s -> subsorts.lessThanEq(s, expectedSort)).collect(Collectors.toList())); - if (leastTerm.size() != 1) { - String msg = "List terminator for overloaded term does not have a least sort. Possible sorts: " + leastTerm; - return new Tuple2<>(Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, tc))), warnings); - } - UserList ulTerm = lists.get(leastTerm.iterator().next()).get(0); - TermCons terminator = TermCons.apply(ConsPStack.empty(), ulTerm.pTerminator, child.location(), child.source()); - // TermCons with PStack requires the elements to be in the reverse order - TermCons newTc; - if (ul.leftAssoc) { - newTc = TermCons.apply(ConsPStack.from(Arrays.asList(child, terminator)), ul.pList, child.location(), child.source()); - } else { - newTc = TermCons.apply(ConsPStack.from(Arrays.asList(terminator, child)), ul.pList, child.location(), child.source()); - } - newItems.add(newTc); - changed = true; - } - } else { - newItems.add(child); - } - expectedSort = oldExpectedSort; - } + if (tc.production().klabel().isDefined()) { + final String tcLabelName = tc.production().klabel().get().name(); + // Never add a list wrapper between a sort annotation and the annotated term + if (tcLabelName.equals("#SyntacticCast") + || tcLabelName.startsWith("#SemanticCastTo") + || tcLabelName.equals("#InnerCast")) { + return superApply(tc); + } + } - if (changed) { - Collections.reverse(newItems); // TermCons with PStack requires the elements to be in the reverse order - tc = TermCons.apply(ConsPStack.from(newItems), tc.production(), tc.location(), tc.source()); - } - if (!warnings.isEmpty()) { - Tuple2, Term>, Set> rez = superApply(tc); - return new Tuple2<>(Right.apply(rez._1().right().get()), Sets.union(warnings, rez._2())); + for (ProductionItem pi : mutable(p.items())) { + if (!(pi instanceof NonTerminal)) continue; + Sort oldExpectedSort = expectedSort; + expectedSort = ((NonTerminal) pi).sort(); + ProductionReference child = (ProductionReference) items.next(); + Sort childSort = getSort(child, expectedSort); + if (listSorts.contains(expectedSort) + && !(subsorts.lessThanEq(childSort, expectedSort) && listSorts.contains(childSort))) { + final boolean isBracket = child.production().att().contains(Att.BRACKET()); + if (isBracket) { + newItems.add(child); + } else if (childSort.equals(Sorts.K()) || !subsorts.lessThan(childSort, expectedSort)) { + newItems.add(child); } else { - return superApply(tc); + Set least = + subsorts.minimal( + stream(listSorts) + .filter( + s -> + subsorts.greaterThanEq(lists.get(s).get(0).childSort, childSort) + && subsorts.lessThanEq(s, expectedSort)) + .collect(Collectors.toList())); + if (least.size() != 1) { + String msg = "Overloaded term does not have a least sort. Possible sorts: " + least; + return new Tuple2<>( + Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, tc))), warnings); + } + UserList ul = lists.get(least.iterator().next()).get(0); + Set leastTerm = + subsorts.minimal( + stream(listSorts) + .filter(s -> subsorts.lessThanEq(s, expectedSort)) + .collect(Collectors.toList())); + if (leastTerm.size() != 1) { + String msg = + "List terminator for overloaded term does not have a least sort. Possible sorts: " + + leastTerm; + return new Tuple2<>( + Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, tc))), warnings); + } + UserList ulTerm = lists.get(leastTerm.iterator().next()).get(0); + TermCons terminator = + TermCons.apply( + ConsPStack.empty(), ulTerm.pTerminator, child.location(), child.source()); + // TermCons with PStack requires the elements to be in the reverse order + TermCons newTc; + if (ul.leftAssoc) { + newTc = + TermCons.apply( + ConsPStack.from(Arrays.asList(child, terminator)), + ul.pList, + child.location(), + child.source()); + } else { + newTc = + TermCons.apply( + ConsPStack.from(Arrays.asList(terminator, child)), + ul.pList, + child.location(), + child.source()); + } + newItems.add(newTc); + changed = true; } + } else { + newItems.add(child); + } + expectedSort = oldExpectedSort; } - private Sort getSort(ProductionReference child, Sort expectedSort) { - if (m.syntacticSubsorts().greaterThan(expectedSort, Sorts.K())) { - expectedSort = Sorts.K(); - } - return inj.substituteProd(child.production(), expectedSort, (i, fresh) -> getSort((ProductionReference)((TermCons)child).get(i), fresh.nonterminals().apply(i).sort()), child).sort(); + if (changed) { + Collections.reverse( + newItems); // TermCons with PStack requires the elements to be in the reverse order + tc = TermCons.apply(ConsPStack.from(newItems), tc.production(), tc.location(), tc.source()); } + if (!warnings.isEmpty()) { + Tuple2, Term>, Set> rez = superApply(tc); + return new Tuple2<>(Right.apply(rez._1().right().get()), Sets.union(warnings, rez._2())); + } else { + return superApply(tc); + } + } - /** - * Returns an element of sorts which is related to and no - * greater than every other element of sorts, if any exists. - */ - private Optional least(Iterable sorts) { - Iterator iter = sorts.iterator(); - if (!iter.hasNext()) { - return Optional.empty(); - } - Sort min = iter.next(); - while (iter.hasNext()) { - Sort next = iter.next(); - if (!subsorts.lessThanEq(min, next)) { - // if min is unrelated to next, it can't be the least sort so - // we also might as well switch - min = next; - } - } - for (Sort s : sorts) { - if (!subsorts.lessThanEq(min, s)) { - return Optional.empty(); - } - } - return Optional.of(min); + private Sort getSort(ProductionReference child, Sort expectedSort) { + if (m.syntacticSubsorts().greaterThan(expectedSort, Sorts.K())) { + expectedSort = Sorts.K(); } + return inj.substituteProd( + child.production(), + expectedSort, + (i, fresh) -> + getSort( + (ProductionReference) ((TermCons) child).get(i), + fresh.nonterminals().apply(i).sort()), + child) + .sort(); + } - /** - * Returns an element of sorts which is related to and no - * less than every other element of sorts, if any exists. - */ - private Optional greatest(Iterable sorts) { - Iterator iter = sorts.iterator(); - if (!iter.hasNext()) { - return Optional.empty(); - } - Sort max = iter.next(); - while (iter.hasNext()) { - Sort next = iter.next(); - if (!subsorts.greaterThanEq(max, next)) { - // if min is unrelated to next, it can't be the least sort so - // we also might as well switch - max = next; - } - } - for (Sort s : sorts) { - if (!subsorts.greaterThanEq(max, s)) { - return Optional.empty(); - } - } - return Optional.of(max); + /** + * Returns an element of sorts which is related to and no greater than every other element of + * sorts, if any exists. + */ + private Optional least(Iterable sorts) { + Iterator iter = sorts.iterator(); + if (!iter.hasNext()) { + return Optional.empty(); + } + Sort min = iter.next(); + while (iter.hasNext()) { + Sort next = iter.next(); + if (!subsorts.lessThanEq(min, next)) { + // if min is unrelated to next, it can't be the least sort so + // we also might as well switch + min = next; + } + } + for (Sort s : sorts) { + if (!subsorts.lessThanEq(min, s)) { + return Optional.empty(); + } } + return Optional.of(min); + } - private Optional klabelFromTerm(Term labelTerm) { - if (labelTerm instanceof Constant labelCon) { - if (labelCon.production().sort().equals(Sorts.KLabel())) { - String labelVal = labelCon.value(); - if (labelVal.charAt(0) == '`') { - return Optional.of(KLabel(labelVal.substring(1, labelVal.length() - 1))); - } else { - return Optional.of(KLabel(labelVal)); - } - } - } + /** + * Returns an element of sorts which is related to and no less than every other element of sorts, + * if any exists. + */ + private Optional greatest(Iterable sorts) { + Iterator iter = sorts.iterator(); + if (!iter.hasNext()) { + return Optional.empty(); + } + Sort max = iter.next(); + while (iter.hasNext()) { + Sort next = iter.next(); + if (!subsorts.greaterThanEq(max, next)) { + // if min is unrelated to next, it can't be the least sort so + // we also might as well switch + max = next; + } + } + for (Sort s : sorts) { + if (!subsorts.greaterThanEq(max, s)) { return Optional.empty(); + } } + return Optional.of(max); + } - private List lowerKList(Term listTerm) { - List items = new ArrayList<>(); - lowerKListAcc(listTerm, items); - return items; + private Optional klabelFromTerm(Term labelTerm) { + if (labelTerm instanceof Constant labelCon) { + if (labelCon.production().sort().equals(Sorts.KLabel())) { + String labelVal = labelCon.value(); + if (labelVal.charAt(0) == '`') { + return Optional.of(KLabel(labelVal.substring(1, labelVal.length() - 1))); + } else { + return Optional.of(KLabel(labelVal)); + } + } } + return Optional.empty(); + } + + private List lowerKList(Term listTerm) { + List items = new ArrayList<>(); + lowerKListAcc(listTerm, items); + return items; + } - private void lowerKListAcc(Term term, List items) { - if (term instanceof TermCons cons) { - if (cons.production().klabel().isDefined()) { - String labelName = cons.production().klabel().get().name(); - if (labelName.equals("#KList")) { - assert cons.items().size() == 2; - lowerKListAcc(cons.get(0), items); - lowerKListAcc(cons.get(1), items); - return; - } else if (labelName.equals("#EmptyKList")) { - return; - } - } + private void lowerKListAcc(Term term, List items) { + if (term instanceof TermCons cons) { + if (cons.production().klabel().isDefined()) { + String labelName = cons.production().klabel().get().name(); + if (labelName.equals("#KList")) { + assert cons.items().size() == 2; + lowerKListAcc(cons.get(0), items); + lowerKListAcc(cons.get(1), items); + return; + } else if (labelName.equals("#EmptyKList")) { + return; } - items.add(term); + } } + items.add(term); + } } diff --git a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/AmbFilter.java b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/AmbFilter.java index a934b9bca59..9ed5c7e2b33 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/AmbFilter.java +++ b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/AmbFilter.java @@ -1,6 +1,7 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner.disambiguation; +import java.util.Set; import org.kframework.kore.K; import org.kframework.parser.Ambiguity; import org.kframework.parser.ProductionReference; @@ -13,55 +14,54 @@ import scala.util.Either; import scala.util.Right; -import java.util.Set; - -/** - * Eliminate remaining ambiguities by choosing one of them. - */ +/** Eliminate remaining ambiguities by choosing one of them. */ public class AmbFilter extends SetsGeneralTransformer { - private final boolean strict; + private final boolean strict; - public AmbFilter(boolean strict) { - this.strict = strict; - } + public AmbFilter(boolean strict) { + this.strict = strict; + } - @Override - public Tuple2, Term>, Set> apply(Ambiguity amb) { - K last = null; - boolean equal = true; - Tuple2, Term>, Set> candidate = null; - for (Term t : amb.items()) { - candidate = this.apply(t); - K next = new TreeNodesToKORE(Outer::parseSort, strict).apply(new RemoveBracketVisitor().apply(candidate._1().right().get())); - if (last != null) { - if (!last.equals(next)) { - equal = false; - break; - } - } - last = next; - } - if(equal) { - // all ambiguities have the same abstract syntax, so just pick one - return candidate; + @Override + public Tuple2, Term>, Set> apply(Ambiguity amb) { + K last = null; + boolean equal = true; + Tuple2, Term>, Set> candidate = null; + for (Term t : amb.items()) { + candidate = this.apply(t); + K next = + new TreeNodesToKORE(Outer::parseSort, strict) + .apply(new RemoveBracketVisitor().apply(candidate._1().right().get())); + if (last != null) { + if (!last.equals(next)) { + equal = false; + break; } + } + last = next; + } + if (equal) { + // all ambiguities have the same abstract syntax, so just pick one + return candidate; + } - String msg = "Parsing ambiguity. Arbitrarily choosing the first."; + String msg = "Parsing ambiguity. Arbitrarily choosing the first."; - for (int i = 0; i < amb.items().size(); i++) { - msg += "\n" + (i + 1) + ": "; - Term elem = (Term) amb.items().toArray()[i]; - if (elem instanceof ProductionReference tc) { - msg += tc.production().toString(); - } - // TODO: use the unparser - //Unparser unparser = new Unparser(context); - //msg += "\n " + unparser.print(elem).replace("\n", "\n "); - msg += "\n " + new RemoveBracketVisitor().apply(elem); - } - // TODO: add location information - Tuple2, Term>, Set> rez = this.apply(amb.items().iterator().next()); - return new Tuple2<>(Right.apply(rez._1().right().get()), rez._2()); + for (int i = 0; i < amb.items().size(); i++) { + msg += "\n" + (i + 1) + ": "; + Term elem = (Term) amb.items().toArray()[i]; + if (elem instanceof ProductionReference tc) { + msg += tc.production().toString(); + } + // TODO: use the unparser + // Unparser unparser = new Unparser(context); + // msg += "\n " + unparser.print(elem).replace("\n", "\n "); + msg += "\n " + new RemoveBracketVisitor().apply(elem); } + // TODO: add location information + Tuple2, Term>, Set> rez = + this.apply(amb.items().iterator().next()); + return new Tuple2<>(Right.apply(rez._1().right().get()), rez._2()); + } } diff --git a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/AmbFilterError.java b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/AmbFilterError.java index 2d09a1612c3..033f8373595 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/AmbFilterError.java +++ b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/AmbFilterError.java @@ -2,6 +2,7 @@ package org.kframework.parser.inner.disambiguation; import com.google.common.collect.Sets; +import java.util.Set; import org.kframework.kore.K; import org.kframework.parser.Ambiguity; import org.kframework.parser.ProductionReference; @@ -13,59 +14,57 @@ import scala.util.Either; import scala.util.Left; -import java.util.Set; - -/** - * Report remaining ambiguities as errors. - */ +/** Report remaining ambiguities as errors. */ public class AmbFilterError extends SetsTransformerWithErrors { - private final boolean strict; + private final boolean strict; - public AmbFilterError(boolean strict) { - this.strict = strict; - } + public AmbFilterError(boolean strict) { + this.strict = strict; + } - @Override - public Either, Term> apply(Ambiguity amb) { - K last = null; - boolean equal = true; - Either, Term> candidate = null; - for (Term t : amb.items()) { - candidate = this.apply(t); - if (candidate.isLeft()) { - return candidate; - } - K next = new TreeNodesToKORE(Outer::parseSort, strict).apply(new RemoveBracketVisitor().apply(candidate.right().get())); - if (last != null) { - if (!last.equals(next)) { - equal = false; - break; - } - } - last = next; - } - if(equal) { - // all ambiguities have the same abstract syntax, so just pick one - return candidate; + @Override + public Either, Term> apply(Ambiguity amb) { + K last = null; + boolean equal = true; + Either, Term> candidate = null; + for (Term t : amb.items()) { + candidate = this.apply(t); + if (candidate.isLeft()) { + return candidate; + } + K next = + new TreeNodesToKORE(Outer::parseSort, strict) + .apply(new RemoveBracketVisitor().apply(candidate.right().get())); + if (last != null) { + if (!last.equals(next)) { + equal = false; + break; } + } + last = next; + } + if (equal) { + // all ambiguities have the same abstract syntax, so just pick one + return candidate; + } - String msg = "Parsing ambiguity."; + String msg = "Parsing ambiguity."; - for (int i = 0; i < amb.items().size(); i++) { - msg += "\n" + (i + 1) + ": "; - Term elem = (Term) amb.items().toArray()[i]; - if (elem instanceof ProductionReference tc) { - msg += tc.production().toString(); - } - // TODO: use the unparser - //Unparser unparser = new Unparser(context); - //msg += "\n " + unparser.print(elem).replace("\n", "\n "); - msg += "\n " + new RemoveBracketVisitor().apply(elem); - } + for (int i = 0; i < amb.items().size(); i++) { + msg += "\n" + (i + 1) + ": "; + Term elem = (Term) amb.items().toArray()[i]; + if (elem instanceof ProductionReference tc) { + msg += tc.production().toString(); + } + // TODO: use the unparser + // Unparser unparser = new Unparser(context); + // msg += "\n " + unparser.print(elem).replace("\n", "\n "); + msg += "\n " + new RemoveBracketVisitor().apply(elem); + } - KEMException e = KEMException.innerParserError(msg, amb.items().iterator().next()); + KEMException e = KEMException.innerParserError(msg, amb.items().iterator().next()); - return Left.apply(Sets.newHashSet(e)); - } + return Left.apply(Sets.newHashSet(e)); + } } diff --git a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/CollapseRecordProdsVisitor.java b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/CollapseRecordProdsVisitor.java index 67c26931f81..62603af5695 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/CollapseRecordProdsVisitor.java +++ b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/CollapseRecordProdsVisitor.java @@ -1,7 +1,13 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner.disambiguation; +import static org.kframework.Collections.*; + import com.google.common.collect.Sets; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import org.kframework.attributes.Att; import org.kframework.builtin.Sorts; import org.kframework.definition.NonTerminal; @@ -18,86 +24,91 @@ import scala.util.Either; import scala.util.Left; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import static org.kframework.Collections.*; - /** - * Collapse the record productions from a cons list to a single term with all parameters. - * Expecting the exact pattern of productions described in Production.recordProductions() + * Collapse the record productions from a cons list to a single term with all parameters. Expecting + * the exact pattern of productions described in Production.recordProductions() */ public class CollapseRecordProdsVisitor extends SetsTransformerWithErrors { - CollectVariableNames varNamesVis; - // require the term before starting the visitor to make sure we generate unique variable names - public CollapseRecordProdsVisitor(Term t) { - varNamesVis = new CollectVariableNames(); - varNamesVis.apply(t); - } + CollectVariableNames varNamesVis; - @Override - public Either, Term> apply(TermCons tc) { - if (tc.production().att().contains(Att.RECORD_PRD(), Production.class)) { - Production origPrd = tc.production().att().get(Att.RECORD_PRD(), Production.class); - Map children = new HashMap<>(); - TermCons iterator = tc; + // require the term before starting the visitor to make sure we generate unique variable names + public CollapseRecordProdsVisitor(Term t) { + varNamesVis = new CollectVariableNames(); + varNamesVis.apply(t); + } - // find all named items - while (iterator != null && iterator.production().att().contains(Att.RECORD_PRD(), Production.class)) { - if (iterator.production().att().contains(Att.RECORD_PRD_MAIN())) - iterator = (TermCons) iterator.get(0); - else if (iterator.production().att().contains(Att.RECORD_PRD_ONE())) { - String key = iterator.production().att().get(Att.RECORD_PRD_ONE()); - children.put(key, iterator.get(0)); - iterator = null; - } else if (iterator.production().att().contains(Att.RECORD_PRD_ITEM())) { - String key = iterator.production().att().get(Att.RECORD_PRD_ITEM()); - if (children.containsKey(key)) - return Left.apply(Sets.newHashSet(KEMException.innerParserError("Duplicate record production key: " + key, tc))); - else - children.put(key, iterator.get(0)); - iterator = null; - } else if (iterator.production().att().contains(Att.RECORD_PRD_REPEAT())) { - TermCons item = (TermCons) iterator.get(1); - String key = item.production().att().get(Att.RECORD_PRD_ITEM()); - if (children.containsKey(key)) - return Left.apply(Sets.newHashSet(KEMException.innerParserError("Duplicate record production key: " + key, tc))); - else - children.put(key, item.get(0)); - iterator = (TermCons) iterator.get(0); - } else - iterator = null; - } - // construct expanded term with provided items and anonymous variables for missing ones - PStack collapsedItems = ConsPStack.empty(); - for (NonTerminal nt : mutable(origPrd.nonterminals())) { - if (nt.name().isDefined() && children.containsKey(nt.name().get())) - collapsedItems = collapsedItems.plus(children.get(nt.name().get())); - else { - Production anonVarPrd = Production.apply(Seq(), Sorts.KVariable(), Seq(Terminal.apply("_[A-Z][A-Za-z0-9'_]*")), Att.empty()); - // The name is required so disambiguation doesn't collapse the variables into the same term. - String varName; - do { varName = "_" + nt.name().getOrElse(() -> "Gen") + uid++; } while (varNamesVis.varNames.contains(varName)); - collapsedItems = collapsedItems.plus(Constant.apply(varName, anonVarPrd, tc.location(), tc.source())); - } - } - TermCons collapsed = TermCons.apply(collapsedItems, origPrd, tc.location(), tc.source()); - return super.apply(collapsed); + @Override + public Either, Term> apply(TermCons tc) { + if (tc.production().att().contains(Att.RECORD_PRD(), Production.class)) { + Production origPrd = tc.production().att().get(Att.RECORD_PRD(), Production.class); + Map children = new HashMap<>(); + TermCons iterator = tc; + + // find all named items + while (iterator != null + && iterator.production().att().contains(Att.RECORD_PRD(), Production.class)) { + if (iterator.production().att().contains(Att.RECORD_PRD_MAIN())) + iterator = (TermCons) iterator.get(0); + else if (iterator.production().att().contains(Att.RECORD_PRD_ONE())) { + String key = iterator.production().att().get(Att.RECORD_PRD_ONE()); + children.put(key, iterator.get(0)); + iterator = null; + } else if (iterator.production().att().contains(Att.RECORD_PRD_ITEM())) { + String key = iterator.production().att().get(Att.RECORD_PRD_ITEM()); + if (children.containsKey(key)) + return Left.apply( + Sets.newHashSet( + KEMException.innerParserError("Duplicate record production key: " + key, tc))); + else children.put(key, iterator.get(0)); + iterator = null; + } else if (iterator.production().att().contains(Att.RECORD_PRD_REPEAT())) { + TermCons item = (TermCons) iterator.get(1); + String key = item.production().att().get(Att.RECORD_PRD_ITEM()); + if (children.containsKey(key)) + return Left.apply( + Sets.newHashSet( + KEMException.innerParserError("Duplicate record production key: " + key, tc))); + else children.put(key, item.get(0)); + iterator = (TermCons) iterator.get(0); + } else iterator = null; + } + // construct expanded term with provided items and anonymous variables for missing ones + PStack collapsedItems = ConsPStack.empty(); + for (NonTerminal nt : mutable(origPrd.nonterminals())) { + if (nt.name().isDefined() && children.containsKey(nt.name().get())) + collapsedItems = collapsedItems.plus(children.get(nt.name().get())); + else { + Production anonVarPrd = + Production.apply( + Seq(), + Sorts.KVariable(), + Seq(Terminal.apply("_[A-Z][A-Za-z0-9'_]*")), + Att.empty()); + // The name is required so disambiguation doesn't collapse the variables into the same + // term. + String varName; + do { + varName = "_" + nt.name().getOrElse(() -> "Gen") + uid++; + } while (varNamesVis.varNames.contains(varName)); + collapsedItems = + collapsedItems.plus(Constant.apply(varName, anonVarPrd, tc.location(), tc.source())); } - return super.apply(tc); + } + TermCons collapsed = TermCons.apply(collapsedItems, origPrd, tc.location(), tc.source()); + return super.apply(collapsed); } + return super.apply(tc); + } - private int uid = 0; + private int uid = 0; - private static class CollectVariableNames extends SafeTransformer { - public Set varNames = new HashSet<>(); - @Override - public Term apply(Constant c) { - if (c.production().sort().equals(Sorts.KVariable())) - varNames.add(c.value()); - return super.apply(c); - } + private static class CollectVariableNames extends SafeTransformer { + public Set varNames = new HashSet<>(); + + @Override + public Term apply(Constant c) { + if (c.production().sort().equals(Sorts.KVariable())) varNames.add(c.value()); + return super.apply(c); } + } } diff --git a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/KAppToTermConsVisitor.java b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/KAppToTermConsVisitor.java index fc09bb686f9..373abecf3e5 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/KAppToTermConsVisitor.java +++ b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/KAppToTermConsVisitor.java @@ -1,8 +1,17 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner.disambiguation; +import static org.kframework.Collections.*; +import static org.kframework.kore.KORE.*; + import com.google.common.collect.Lists; import com.google.common.collect.Sets; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.kframework.builtin.KLabels; import org.kframework.builtin.Sorts; import org.kframework.definition.Module; @@ -21,94 +30,110 @@ import scala.util.Either; import scala.util.Left; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.kframework.Collections.*; -import static org.kframework.kore.KORE.*; - - /** - * Transform the KApps found in a term into the corresponding TermCons so type checking and - * variable type inference takes place correctly. Must be applied between type inference and - * priority filter. + * Transform the KApps found in a term into the corresponding TermCons so type checking and variable + * type inference takes place correctly. Must be applied between type inference and priority filter. */ public class KAppToTermConsVisitor extends SetsTransformerWithErrors { - private final Module mod; + private final Module mod; - public KAppToTermConsVisitor(Module mod) { - super(); - this.mod = mod; - } + public KAppToTermConsVisitor(Module mod) { + super(); + this.mod = mod; + } - @Override - public Either, Term> apply(TermCons tc) { - assert tc.production() != null : this.getClass() + ":" + " production not found." + tc; - if (tc.production().klabel().isDefined() && tc.production().klabel().get().equals(KLabels.KAPP)) { - if (!(tc.get(0) instanceof Constant kl) || !((Constant) tc.get(0)).production().sort().equals(Sorts.KLabel())) - // TODO: remove check once the java backend is no longer supported. - return super.apply(tc); // don't do anything if the label is not a token KLabel (in case of variable or casted variable) - String klvalue = kl.value(); - try { klvalue = StringUtil.unescapeKoreKLabel(kl.value()); } catch (IllegalArgumentException e) { /* ignore */ } // if possible, unescape - Set prods = mutable(mod.productionsFor().get(KLabel(klvalue)) - .getOrElse(Set$.MODULE$::emptyInstance).toSet()); - Set sol = new HashSet<>(); - Term t = new PushTopAmbiguityUp2().apply(tc.get(1)); - Stream uppedAmb = t instanceof Ambiguity ? ((Ambiguity) t).items().stream() : Lists.newArrayList(t).stream(); - Map>> flattKLists = uppedAmb - .map(KAppToTermConsVisitor::flattenKList) - .collect(Collectors.groupingBy(PStack::size)); - for (Production prd : prods) - for (PStack terms : flattKLists.getOrDefault(prd.arity(), Lists.newArrayList())) - sol.add(TermCons.apply(terms, prd, tc.location(), tc.source())); + @Override + public Either, Term> apply(TermCons tc) { + assert tc.production() != null : this.getClass() + ":" + " production not found." + tc; + if (tc.production().klabel().isDefined() + && tc.production().klabel().get().equals(KLabels.KAPP)) { + if (!(tc.get(0) instanceof Constant kl) + || !((Constant) tc.get(0)).production().sort().equals(Sorts.KLabel())) + // TODO: remove check once the java backend is no longer supported. + return super.apply( + tc); // don't do anything if the label is not a token KLabel (in case of variable or + // casted variable) + String klvalue = kl.value(); + try { + klvalue = StringUtil.unescapeKoreKLabel(kl.value()); + } catch (IllegalArgumentException e) { + /* ignore */ + } // if possible, unescape + Set prods = + mutable( + mod.productionsFor() + .get(KLabel(klvalue)) + .getOrElse(Set$.MODULE$::emptyInstance) + .toSet()); + Set sol = new HashSet<>(); + Term t = new PushTopAmbiguityUp2().apply(tc.get(1)); + Stream uppedAmb = + t instanceof Ambiguity + ? ((Ambiguity) t).items().stream() + : Lists.newArrayList(t).stream(); + Map>> flattKLists = + uppedAmb + .map(KAppToTermConsVisitor::flattenKList) + .collect(Collectors.groupingBy(PStack::size)); + for (Production prd : prods) + for (PStack terms : flattKLists.getOrDefault(prd.arity(), Lists.newArrayList())) + sol.add(TermCons.apply(terms, prd, tc.location(), tc.source())); - if (sol.size() == 0) { - String msg = "Could not find any suitable production for label " + kl.value(); - return Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, kl))); - } else if (sol.size() == 1) { - return super.apply(sol.iterator().next()); - } else - return super.apply(Ambiguity.apply(sol, tc.location(), tc.source())); - } - return super.apply(tc); + if (sol.size() == 0) { + String msg = "Could not find any suitable production for label " + kl.value(); + return Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, kl))); + } else if (sol.size() == 1) { + return super.apply(sol.iterator().next()); + } else return super.apply(Ambiguity.apply(sol, tc.location(), tc.source())); } + return super.apply(tc); + } - /** Recurse under #KList and flatten all the terms */ - private static PStack flattenKList(Term t) { - if (t instanceof Ambiguity) { - assert false : KAppToTermConsVisitor.class + " expected all ambiguities to already be pushed to the top:\n" + - " Source: " + ((Ambiguity) t).items().iterator().next().source().orElse(null) + "\n" + - " Location: " + ((Ambiguity) t).items().iterator().next().location().orElse(null); - } else if (t instanceof TermCons tc) { - if (tc.production().klabel().isDefined() && tc.production().klabel().get().equals(KLabels.KLIST)) - return flattenKList(tc.get(0)).plusAll(Lists.reverse(Lists.newArrayList(flattenKList(tc.get(1))))); - else if (tc.production().klabel().isDefined() && tc.production().klabel().get().equals(KLabels.EMPTYKLIST)) - return ConsPStack.empty(); - } - return ConsPStack.singleton(t); + /** Recurse under #KList and flatten all the terms */ + private static PStack flattenKList(Term t) { + if (t instanceof Ambiguity) { + assert false + : KAppToTermConsVisitor.class + + " expected all ambiguities to already be pushed to the top:\n" + + " Source: " + + ((Ambiguity) t).items().iterator().next().source().orElse(null) + + "\n" + + " Location: " + + ((Ambiguity) t).items().iterator().next().location().orElse(null); + } else if (t instanceof TermCons tc) { + if (tc.production().klabel().isDefined() + && tc.production().klabel().get().equals(KLabels.KLIST)) + return flattenKList(tc.get(0)) + .plusAll(Lists.reverse(Lists.newArrayList(flattenKList(tc.get(1))))); + else if (tc.production().klabel().isDefined() + && tc.production().klabel().get().equals(KLabels.EMPTYKLIST)) return ConsPStack.empty(); } + return ConsPStack.singleton(t); + } - // push ambiguities top so we can get easy access to KList - private static class PushTopAmbiguityUp2 extends SafeTransformer { - @Override - public Term apply(TermCons tc) { - if (tc.production().klabel().isDefined() && tc.production().klabel().get().head().equals(KLabels.KLIST)) { - Term v0 = super.apply(tc.get(0)); - Term v1 = super.apply(tc.get(1)); - Set t0 = v0 instanceof Ambiguity ? ((Ambiguity) v0).items() : Sets.newHashSet(v0); - Set t1 = v1 instanceof Ambiguity ? ((Ambiguity) v1).items() : Sets.newHashSet(v1); - Set rez = Sets.newHashSet(); - for (Term t00 : t0) - for (Term t11 : t1) - rez.add(TermCons.apply(ConsPStack.singleton(t00).plus(t11), tc.production(), tc.location(), tc.source())); - return Ambiguity.apply(rez, tc.location(), tc.source()); - } - return tc; - } + // push ambiguities top so we can get easy access to KList + private static class PushTopAmbiguityUp2 extends SafeTransformer { + @Override + public Term apply(TermCons tc) { + if (tc.production().klabel().isDefined() + && tc.production().klabel().get().head().equals(KLabels.KLIST)) { + Term v0 = super.apply(tc.get(0)); + Term v1 = super.apply(tc.get(1)); + Set t0 = v0 instanceof Ambiguity ? ((Ambiguity) v0).items() : Sets.newHashSet(v0); + Set t1 = v1 instanceof Ambiguity ? ((Ambiguity) v1).items() : Sets.newHashSet(v1); + Set rez = Sets.newHashSet(); + for (Term t00 : t0) + for (Term t11 : t1) + rez.add( + TermCons.apply( + ConsPStack.singleton(t00).plus(t11), + tc.production(), + tc.location(), + tc.source())); + return Ambiguity.apply(rez, tc.location(), tc.source()); + } + return tc; } + } } diff --git a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/PriorityVisitor.java b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/PriorityVisitor.java index e4a58d21b6d..152420684b3 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/PriorityVisitor.java +++ b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/PriorityVisitor.java @@ -1,14 +1,17 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner.disambiguation; +import static org.kframework.Collections.*; + import com.google.common.collect.Sets; +import java.util.HashSet; import org.kframework.Collections; import org.kframework.POSet; import org.kframework.attributes.Att; import org.kframework.definition.NonTerminal; import org.kframework.definition.Tag; -import org.kframework.parser.SetsTransformerWithErrors; import org.kframework.parser.Ambiguity; +import org.kframework.parser.SetsTransformerWithErrors; import org.kframework.parser.Term; import org.kframework.parser.TermCons; import org.kframework.utils.StringUtil; @@ -19,207 +22,258 @@ import scala.util.Left; import scala.util.Right; -import java.util.HashSet; +/** Apply the priority and associativity filters. */ +public class PriorityVisitor extends SetsTransformerWithErrors { -import static org.kframework.Collections.*; + private final POSet priorities; + private final Set> leftAssoc; + private final Set> rightAssoc; -/** - * Apply the priority and associativity filters. - */ -public class PriorityVisitor extends SetsTransformerWithErrors { + public PriorityVisitor( + POSet priorities, Set> leftAssoc, Set> rightAssoc) { + super(); + this.priorities = priorities; + this.leftAssoc = leftAssoc; + this.rightAssoc = rightAssoc; + } - private final POSet priorities; - private final Set> leftAssoc; - private final Set> rightAssoc; - public PriorityVisitor(POSet priorities, Set> leftAssoc, Set> rightAssoc) { - super(); - this.priorities = priorities; - this.leftAssoc = leftAssoc; - this.rightAssoc = rightAssoc; - } + @Override + public Either, Term> apply(Ambiguity amb) { + // if the ambiguity has rewrites at the top, prefer them, and eliminate the rest + scala.collection.Set rewrites = + amb.items().stream() + .filter( + o -> + o instanceof TermCons + && ((TermCons) o).production().klabel().isDefined() + && ((TermCons) o).production().klabel().get().name().equals("#KRewrite")) + .collect(Collections.toSet()); + if (rewrites.size() == 1) return apply(rewrites.head()); + if (rewrites.size() != 0 && rewrites.size() != amb.items().size()) + amb = Ambiguity.apply(mutable(rewrites)); - @Override - public Either, Term> apply(Ambiguity amb) { - // if the ambiguity has rewrites at the top, prefer them, and eliminate the rest - scala.collection.Set rewrites = amb.items().stream().filter(o -> - o instanceof TermCons && - ((TermCons) o).production().klabel().isDefined() && - ((TermCons) o).production().klabel().get().name().equals("#KRewrite")).collect(Collections.toSet()); - if (rewrites.size() == 1) - return apply(rewrites.head()); - if (rewrites.size() != 0 && rewrites.size() != amb.items().size()) - amb = Ambiguity.apply(mutable(rewrites)); - - // if the ambiguity has KSeq at the top, prefer them, and eliminate the rest - rewrites = amb.items().stream().filter(o -> - o instanceof TermCons && - ((TermCons) o).production().klabel().isDefined() && - ((TermCons) o).production().klabel().get().name().equals("#KSequence")).collect(Collections.toSet()); - if (rewrites.size() == 1) - return apply(rewrites.head()); - if (rewrites.size() != 0 && rewrites.size() != amb.items().size()) - amb = Ambiguity.apply(mutable(rewrites)); - - // if the ambiguity has let at the top, prefer them, and eliminate the rest - rewrites = amb.items().stream().filter(o -> - o instanceof TermCons && - ((TermCons) o).production().klabel().isDefined() && - ((TermCons) o).production().klabel().get().name().equals("#let")).collect(Collections.toSet()); - if (rewrites.size() == 1) - return apply(rewrites.head()); - if (rewrites.size() != 0 && rewrites.size() != amb.items().size()) - amb = Ambiguity.apply(mutable(rewrites)); - - return super.apply(amb); - } + // if the ambiguity has KSeq at the top, prefer them, and eliminate the rest + rewrites = + amb.items().stream() + .filter( + o -> + o instanceof TermCons + && ((TermCons) o).production().klabel().isDefined() + && ((TermCons) o).production().klabel().get().name().equals("#KSequence")) + .collect(Collections.toSet()); + if (rewrites.size() == 1) return apply(rewrites.head()); + if (rewrites.size() != 0 && rewrites.size() != amb.items().size()) + amb = Ambiguity.apply(mutable(rewrites)); - @Override - public Either, Term> apply(TermCons tc) { - assert tc.production() != null : this.getClass() + ":" + " production not found." + tc; - if (!tc.production().isSyntacticSubsort()) { - // match only on the outermost elements - if (tc.production().att().contains(Att.APPLY_PRIORITY())) { - String[] pieces = StringUtil.splitOneDimensionalAtt(tc.production().att().get(Att.APPLY_PRIORITY())); - java.util.Set applyAt = new HashSet<>(); - for (String piece : pieces) { - try { - int i = Integer.parseInt(piece.trim()); - applyAt.add(i); - } catch (NumberFormatException e) { - throw KEMException.innerParserError("Invalid applyPriority attribute value: " + piece, e, tc.production().source().orElse(null), tc.production().location().orElse(null)); - } - } - for (int i = 0, j = 0; i < tc.production().items().size(); i++) { - if (tc.production().items().apply(i) instanceof NonTerminal) { - j++; - if (applyAt.contains(j)) { - PriorityVisitor2.Side side; - if (i == 0) { - side = PriorityVisitor2.Side.LEFT; - } else if (i == tc.production().items().size() - 1) { - side = PriorityVisitor2.Side.RIGHT; - } else { - side = PriorityVisitor2.Side.MIDDLE; - } - Either, Term> rez = - new PriorityVisitor2(tc, side, priorities, leftAssoc, rightAssoc).apply(tc.get(j-1)); - if (rez.isLeft()) - return rez; - tc = tc.with(j-1, rez.right().get()); - } - } + // if the ambiguity has let at the top, prefer them, and eliminate the rest + rewrites = + amb.items().stream() + .filter( + o -> + o instanceof TermCons + && ((TermCons) o).production().klabel().isDefined() + && ((TermCons) o).production().klabel().get().name().equals("#let")) + .collect(Collections.toSet()); + if (rewrites.size() == 1) return apply(rewrites.head()); + if (rewrites.size() != 0 && rewrites.size() != amb.items().size()) + amb = Ambiguity.apply(mutable(rewrites)); + + return super.apply(amb); + } + + @Override + public Either, Term> apply(TermCons tc) { + assert tc.production() != null : this.getClass() + ":" + " production not found." + tc; + if (!tc.production().isSyntacticSubsort()) { + // match only on the outermost elements + if (tc.production().att().contains(Att.APPLY_PRIORITY())) { + String[] pieces = + StringUtil.splitOneDimensionalAtt(tc.production().att().get(Att.APPLY_PRIORITY())); + java.util.Set applyAt = new HashSet<>(); + for (String piece : pieces) { + try { + int i = Integer.parseInt(piece.trim()); + applyAt.add(i); + } catch (NumberFormatException e) { + throw KEMException.innerParserError( + "Invalid applyPriority attribute value: " + piece, + e, + tc.production().source().orElse(null), + tc.production().location().orElse(null)); + } + } + for (int i = 0, j = 0; i < tc.production().items().size(); i++) { + if (tc.production().items().apply(i) instanceof NonTerminal) { + j++; + if (applyAt.contains(j)) { + PriorityVisitor2.Side side; + if (i == 0) { + side = PriorityVisitor2.Side.LEFT; + } else if (i == tc.production().items().size() - 1) { + side = PriorityVisitor2.Side.RIGHT; + } else { + side = PriorityVisitor2.Side.MIDDLE; } - } else { - if (tc.production().items().apply(0) instanceof NonTerminal) { - Either, Term> rez = - new PriorityVisitor2(tc, PriorityVisitor2.Side.LEFT, priorities, leftAssoc, rightAssoc).apply(tc.get(0)); - if (rez.isLeft()) - return rez; - tc = tc.with(0, rez.right().get()); - } - if (tc.production().items().apply(tc.production().items().size() - 1) instanceof NonTerminal) { - int last = tc.items().size() - 1; - Either, Term> rez = - new PriorityVisitor2(tc, PriorityVisitor2.Side.RIGHT, priorities, leftAssoc, rightAssoc).apply(tc.get(last)); - if (rez.isLeft()) - return rez; - tc = tc.with(last, rez.right().get()); - } + Either, Term> rez = + new PriorityVisitor2(tc, side, priorities, leftAssoc, rightAssoc) + .apply(tc.get(j - 1)); + if (rez.isLeft()) return rez; + tc = tc.with(j - 1, rez.right().get()); } + } + } + } else { + if (tc.production().items().apply(0) instanceof NonTerminal) { + Either, Term> rez = + new PriorityVisitor2( + tc, PriorityVisitor2.Side.LEFT, priorities, leftAssoc, rightAssoc) + .apply(tc.get(0)); + if (rez.isLeft()) return rez; + tc = tc.with(0, rez.right().get()); } - return super.apply(tc); + if (tc.production().items().apply(tc.production().items().size() - 1) + instanceof NonTerminal) { + int last = tc.items().size() - 1; + Either, Term> rez = + new PriorityVisitor2( + tc, PriorityVisitor2.Side.RIGHT, priorities, leftAssoc, rightAssoc) + .apply(tc.get(last)); + if (rez.isLeft()) return rez; + tc = tc.with(last, rez.right().get()); + } + } } + return super.apply(tc); + } - private static class PriorityVisitor2 extends SetsTransformerWithErrors { - /** - * Specifies whether the current node is the left most or the right most child of the parent. - * This is useful because associativity can be checked at the same time with priorities. - */ - public enum Side {LEFT, RIGHT, MIDDLE} - private final TermCons parent; - private final Side side; - private final POSet priorities; - private final Set> leftAssoc; - private final Set> rigthAssoc; - - public PriorityVisitor2(TermCons parent, Side side, POSet priorities, Set> leftAssoc, Set> rightAssoc) { - this.parent = parent; - this.side = side; - this.priorities = priorities; - this.leftAssoc = leftAssoc; - this.rigthAssoc = rightAssoc; - } + private static class PriorityVisitor2 extends SetsTransformerWithErrors { + /** + * Specifies whether the current node is the left most or the right most child of the parent. + * This is useful because associativity can be checked at the same time with priorities. + */ + public enum Side { + LEFT, + RIGHT, + MIDDLE + } - private final static java.util.Set rewriteExceptions, kseqExceptions, letExceptions; - - static { - rewriteExceptions = new HashSet<>(); - rewriteExceptions.add("#ruleRequires"); - rewriteExceptions.add("#ruleEnsures"); - rewriteExceptions.add("#ruleRequiresEnsures"); - rewriteExceptions.add("#KRewrite"); - rewriteExceptions.add("#withConfig"); - rewriteExceptions.add("#KList"); - kseqExceptions = new HashSet<>(); - kseqExceptions.add("#ruleRequires"); - kseqExceptions.add("#ruleEnsures"); - kseqExceptions.add("#ruleRequiresEnsures"); - kseqExceptions.add("#KRewrite"); - kseqExceptions.add("#KSequence"); - kseqExceptions.add("#KList"); - letExceptions = new HashSet<>(); - letExceptions.add("#ruleRequires"); - letExceptions.add("#ruleEnsures"); - letExceptions.add("#ruleRequiresEnsures"); - letExceptions.add("#KRewrite"); - letExceptions.add("#KSequence"); - letExceptions.add("#let"); - letExceptions.add("#KList"); - } + private final TermCons parent; + private final Side side; + private final POSet priorities; + private final Set> leftAssoc; + private final Set> rigthAssoc; - public Either, Term> apply(TermCons tc) { - Tag parentLabel = new Tag(parent.production().parseLabel().name()); - Tag localLabel = new Tag(tc.production().parseLabel().name()); - if (!parent.production().isSyntacticSubsort() && parent.production().klabel().isDefined()) { - if (!rewriteExceptions.contains(parentLabel.name()) && localLabel.name().equals("#KRewrite")) { - String msg = "Rewrite is not allowed to be an immediate child of " + parent.production().parseLabel() + - " Use parentheses: (x)=>(y) to set the proper scope of the operations."; - return Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, tc))); - } - if (!kseqExceptions.contains(parentLabel.name()) && localLabel.name().equals("#KSequence")) { - String msg = "~> is not allowed to be an immediate child of " + parent.production().parseLabel() + - " Use parentheses: (x)~>(y) to set the proper scope of the operations."; - return Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, tc))); - } - if (!letExceptions.contains(parentLabel.name()) && localLabel.name().equals("#let")) { - String msg = "#let is not allowed to be an immediate child of " + parent.production().parseLabel() + - " Use parentheses: #let x = y #in (z) to set the proper scope of the operations."; - return Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, tc))); - } - if ((parentLabel.name().equals("#SyntacticCast") || parentLabel.name().startsWith("#SemanticCastTo")) && - tc.production().items().apply(tc.production().items().size() - 1) instanceof NonTerminal) { - String msg = parent.production().klabel().get() + " is not allowed to be an immediate child of cast." + - " Use parentheses: (x):Sort to set the proper scope of the operations."; - return Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, tc))); - } - } - if (priorities.lessThan(parentLabel, localLabel)) { - String msg = "Priority filter exception. Cannot use " + localLabel + " as an immediate child of " + - parentLabel + ". Consider using parentheses around " + localLabel; - return Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, tc))); - } - if (leftAssoc.contains(new Tuple2<>(parentLabel, localLabel)) && Side.RIGHT == side) { - String msg = "Associativity filter exception. Cannot use " + localLabel + " as an immediate right child of " + - parentLabel + ". Consider using parentheses around " + localLabel; - return Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, tc))); - } - if (rigthAssoc.contains(new Tuple2<>(parentLabel, localLabel)) && Side.LEFT == side) { - String msg = "Associativity filter exception. Cannot use " + localLabel + " as an immediate left child of " + - parentLabel + ". Consider using parentheses around " + localLabel; - return Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, tc))); - } + public PriorityVisitor2( + TermCons parent, + Side side, + POSet priorities, + Set> leftAssoc, + Set> rightAssoc) { + this.parent = parent; + this.side = side; + this.priorities = priorities; + this.leftAssoc = leftAssoc; + this.rigthAssoc = rightAssoc; + } + + private static final java.util.Set rewriteExceptions, kseqExceptions, letExceptions; - return Right.apply(tc); + static { + rewriteExceptions = new HashSet<>(); + rewriteExceptions.add("#ruleRequires"); + rewriteExceptions.add("#ruleEnsures"); + rewriteExceptions.add("#ruleRequiresEnsures"); + rewriteExceptions.add("#KRewrite"); + rewriteExceptions.add("#withConfig"); + rewriteExceptions.add("#KList"); + kseqExceptions = new HashSet<>(); + kseqExceptions.add("#ruleRequires"); + kseqExceptions.add("#ruleEnsures"); + kseqExceptions.add("#ruleRequiresEnsures"); + kseqExceptions.add("#KRewrite"); + kseqExceptions.add("#KSequence"); + kseqExceptions.add("#KList"); + letExceptions = new HashSet<>(); + letExceptions.add("#ruleRequires"); + letExceptions.add("#ruleEnsures"); + letExceptions.add("#ruleRequiresEnsures"); + letExceptions.add("#KRewrite"); + letExceptions.add("#KSequence"); + letExceptions.add("#let"); + letExceptions.add("#KList"); + } + + public Either, Term> apply(TermCons tc) { + Tag parentLabel = new Tag(parent.production().parseLabel().name()); + Tag localLabel = new Tag(tc.production().parseLabel().name()); + if (!parent.production().isSyntacticSubsort() && parent.production().klabel().isDefined()) { + if (!rewriteExceptions.contains(parentLabel.name()) + && localLabel.name().equals("#KRewrite")) { + String msg = + "Rewrite is not allowed to be an immediate child of " + + parent.production().parseLabel() + + " Use parentheses: (x)=>(y) to set the proper scope of the operations."; + return Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, tc))); } + if (!kseqExceptions.contains(parentLabel.name()) + && localLabel.name().equals("#KSequence")) { + String msg = + "~> is not allowed to be an immediate child of " + + parent.production().parseLabel() + + " Use parentheses: (x)~>(y) to set the proper scope of the operations."; + return Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, tc))); + } + if (!letExceptions.contains(parentLabel.name()) && localLabel.name().equals("#let")) { + String msg = + "#let is not allowed to be an immediate child of " + + parent.production().parseLabel() + + " Use parentheses: #let x = y #in (z) to set the proper scope of the" + + " operations."; + return Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, tc))); + } + if ((parentLabel.name().equals("#SyntacticCast") + || parentLabel.name().startsWith("#SemanticCastTo")) + && tc.production().items().apply(tc.production().items().size() - 1) + instanceof NonTerminal) { + String msg = + parent.production().klabel().get() + + " is not allowed to be an immediate child of cast." + + " Use parentheses: (x):Sort to set the proper scope of the operations."; + return Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, tc))); + } + } + if (priorities.lessThan(parentLabel, localLabel)) { + String msg = + "Priority filter exception. Cannot use " + + localLabel + + " as an immediate child of " + + parentLabel + + ". Consider using parentheses around " + + localLabel; + return Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, tc))); + } + if (leftAssoc.contains(new Tuple2<>(parentLabel, localLabel)) && Side.RIGHT == side) { + String msg = + "Associativity filter exception. Cannot use " + + localLabel + + " as an immediate right child of " + + parentLabel + + ". Consider using parentheses around " + + localLabel; + return Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, tc))); + } + if (rigthAssoc.contains(new Tuple2<>(parentLabel, localLabel)) && Side.LEFT == side) { + String msg = + "Associativity filter exception. Cannot use " + + localLabel + + " as an immediate left child of " + + parentLabel + + ". Consider using parentheses around " + + localLabel; + return Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, tc))); + } + + return Right.apply(tc); } + } } diff --git a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/PushAmbiguitiesDownAndPreferAvoid.java b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/PushAmbiguitiesDownAndPreferAvoid.java index 9782997580e..c90eb84d5a2 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/PushAmbiguitiesDownAndPreferAvoid.java +++ b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/PushAmbiguitiesDownAndPreferAvoid.java @@ -1,6 +1,9 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner.disambiguation; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; import org.kframework.POSet; import org.kframework.attributes.Att; import org.kframework.definition.Production; @@ -10,124 +13,121 @@ import org.kframework.parser.Term; import org.kframework.parser.TermCons; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Created by dwightguth on 5/3/17. - */ +/** Created by dwightguth on 5/3/17. */ public class PushAmbiguitiesDownAndPreferAvoid extends SafeTransformer { - private final POSet overloads; - private final boolean justPush; + private final POSet overloads; + private final boolean justPush; - public PushAmbiguitiesDownAndPreferAvoid() { - this.overloads = null; - this.justPush = true; - } - - public PushAmbiguitiesDownAndPreferAvoid(POSet overloads) { - this.overloads = overloads; - this.justPush = false; - } + public PushAmbiguitiesDownAndPreferAvoid() { + this.overloads = null; + this.justPush = true; + } - public Term preferAvoid(Ambiguity amb) { - List prefer = new ArrayList<>(); - List avoid = new ArrayList<>(); - for (Term t : amb.items()) { - if (t instanceof ProductionReference) { - if (((ProductionReference) t).production().att().contains(Att.PREFER())) { - prefer.add(t); - } else if (((ProductionReference) t).production().att().contains(Att.AVOID())) { - avoid.add(t); - } - } - } - Term result = amb; + public PushAmbiguitiesDownAndPreferAvoid(POSet overloads) { + this.overloads = overloads; + this.justPush = false; + } - if (!prefer.isEmpty()) { - if (prefer.size() == 1) { - result = prefer.get(0); - } else { - result = amb.replaceChildren(prefer); - } - } else if (!avoid.isEmpty()) { - if (avoid.size() < amb.items().size()) { - amb.items().removeAll(avoid); - if (amb.items().size() == 1) - result = amb.items().iterator().next(); - } + public Term preferAvoid(Ambiguity amb) { + List prefer = new ArrayList<>(); + List avoid = new ArrayList<>(); + for (Term t : amb.items()) { + if (t instanceof ProductionReference) { + if (((ProductionReference) t).production().att().contains(Att.PREFER())) { + prefer.add(t); + } else if (((ProductionReference) t).production().att().contains(Att.AVOID())) { + avoid.add(t); } + } + } + Term result = amb; - return result; + if (!prefer.isEmpty()) { + if (prefer.size() == 1) { + result = prefer.get(0); + } else { + result = amb.replaceChildren(prefer); + } + } else if (!avoid.isEmpty()) { + if (avoid.size() < amb.items().size()) { + amb.items().removeAll(avoid); + if (amb.items().size() == 1) result = amb.items().iterator().next(); + } } - @Override - public Term apply(Ambiguity a) { - if (a.items().size() == 1) - return apply(a.items().iterator().next()); - Production prod = null; - int arity = 0; + return result; + } - if (!justPush) { - Term withoutOverloads = new RemoveOverloads(overloads).apply(a); - if (!(withoutOverloads instanceof Ambiguity)) { - return super.apply(withoutOverloads); - } - Term preferred = preferAvoid((Ambiguity)withoutOverloads); - if (!(preferred instanceof Ambiguity)) { - return super.apply(preferred); - } - a = (Ambiguity)preferred; - } + @Override + public Term apply(Ambiguity a) { + if (a.items().size() == 1) return apply(a.items().iterator().next()); + Production prod = null; + int arity = 0; + + if (!justPush) { + Term withoutOverloads = new RemoveOverloads(overloads).apply(a); + if (!(withoutOverloads instanceof Ambiguity)) { + return super.apply(withoutOverloads); + } + Term preferred = preferAvoid((Ambiguity) withoutOverloads); + if (!(preferred instanceof Ambiguity)) { + return super.apply(preferred); + } + a = (Ambiguity) preferred; + } - a = (Ambiguity)super.apply(a); - for (Term t : a.items()) { - if (!(t instanceof ProductionReference ref)) { - return a; - } - if (prod == null) { - prod = ref.production(); - if (ref instanceof TermCons) { - arity = ((TermCons) ref).items().size(); - } - } else if (!prod.equals(ref.production())) { - return a; - } + a = (Ambiguity) super.apply(a); + for (Term t : a.items()) { + if (!(t instanceof ProductionReference ref)) { + return a; + } + if (prod == null) { + prod = ref.production(); + if (ref instanceof TermCons) { + arity = ((TermCons) ref).items().size(); } - if (arity == 0) - return a; - boolean[] same = new boolean[arity]; - for (int i = 0; i < arity; i++) { - boolean sameAtIdx = true; - Term sameTerm = null; - for (Term t : a.items()) { - TermCons tc = (TermCons)t; - if (sameTerm == null) { - sameTerm = tc.get(i); - } else if (!sameTerm.equals(tc.get(i))) { - sameAtIdx = false; - } - } - same[i] = sameAtIdx; + } else if (!prod.equals(ref.production())) { + return a; + } + } + if (arity == 0) return a; + boolean[] same = new boolean[arity]; + for (int i = 0; i < arity; i++) { + boolean sameAtIdx = true; + Term sameTerm = null; + for (Term t : a.items()) { + TermCons tc = (TermCons) t; + if (sameTerm == null) { + sameTerm = tc.get(i); + } else if (!sameTerm.equals(tc.get(i))) { + sameAtIdx = false; } - TermCons first = (TermCons)a.items().iterator().next(); - for (int i = 0; i < arity; i++) { - final int idx = i; - boolean candidate = true; - for (int j = 0; j < arity; j++) { - if (i == j) - continue; - if (!same[j]) { - candidate = false; - break; - } - } - if (candidate) { - return apply(first.with(i, new Ambiguity(a.items().stream().map(t -> (TermCons)t).map(t -> t.get(idx)).collect(Collectors.toSet())))); - } + } + same[i] = sameAtIdx; + } + TermCons first = (TermCons) a.items().iterator().next(); + for (int i = 0; i < arity; i++) { + final int idx = i; + boolean candidate = true; + for (int j = 0; j < arity; j++) { + if (i == j) continue; + if (!same[j]) { + candidate = false; + break; } - return a; + } + if (candidate) { + return apply( + first.with( + i, + new Ambiguity( + a.items().stream() + .map(t -> (TermCons) t) + .map(t -> t.get(idx)) + .collect(Collectors.toSet())))); + } } + return a; + } } diff --git a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/PushTopAmbiguityUp.java b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/PushTopAmbiguityUp.java index c75479b4690..8f59be9393b 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/PushTopAmbiguityUp.java +++ b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/PushTopAmbiguityUp.java @@ -1,6 +1,8 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner.disambiguation; +import java.util.HashSet; +import java.util.Set; import org.kframework.builtin.KLabels; import org.kframework.builtin.Sorts; import org.kframework.parser.Ambiguity; @@ -8,41 +10,39 @@ import org.kframework.parser.Term; import org.kframework.parser.TermCons; -import java.util.HashSet; -import java.util.Set; - public class PushTopAmbiguityUp extends SafeTransformer { - @Override - public Term apply(TermCons tc) { - if (tc.production().sort().equals(Sorts.RuleContent())) { - Term t = new PushTopAmbiguityUp2().apply(tc.get(0)); - if (t instanceof Ambiguity old) { - Set newTerms = new HashSet<>(); - for (Term child : old.items()) { - Term newTerm = tc.with(0, child); - newTerms.add(newTerm); - } - return Ambiguity.apply(newTerms); - } + @Override + public Term apply(TermCons tc) { + if (tc.production().sort().equals(Sorts.RuleContent())) { + Term t = new PushTopAmbiguityUp2().apply(tc.get(0)); + if (t instanceof Ambiguity old) { + Set newTerms = new HashSet<>(); + for (Term child : old.items()) { + Term newTerm = tc.with(0, child); + newTerms.add(newTerm); } - return super.apply(tc); + return Ambiguity.apply(newTerms); + } } + return super.apply(tc); + } - private class PushTopAmbiguityUp2 extends SafeTransformer { - @Override - public Term apply(TermCons tc) { - if (tc.production().klabel().isDefined() && tc.production().klabel().get().head().equals(KLabels.KREWRITE)) { - Term t = tc.get(0); - if (t instanceof Ambiguity old) { - Set newTerms = new HashSet<>(); - for (Term child : old.items()) { - Term newTerm = tc.with(0, child); - newTerms.add(newTerm); - } - return Ambiguity.apply(newTerms); - } - } - return super.apply(tc); + private class PushTopAmbiguityUp2 extends SafeTransformer { + @Override + public Term apply(TermCons tc) { + if (tc.production().klabel().isDefined() + && tc.production().klabel().get().head().equals(KLabels.KREWRITE)) { + Term t = tc.get(0); + if (t instanceof Ambiguity old) { + Set newTerms = new HashSet<>(); + for (Term child : old.items()) { + Term newTerm = tc.with(0, child); + newTerms.add(newTerm); + } + return Ambiguity.apply(newTerms); } + } + return super.apply(tc); } + } } diff --git a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/RemoveBracketVisitor.java b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/RemoveBracketVisitor.java index 0ef450bd3f6..1d914cb360c 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/RemoveBracketVisitor.java +++ b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/RemoveBracketVisitor.java @@ -7,14 +7,13 @@ import org.kframework.parser.TermCons; public class RemoveBracketVisitor extends SafeTransformer { - @Override - public Term apply(TermCons tc) { - if (tc.production().att().contains(Att.BRACKET()) || - tc.production().klabel().get().name().equals("#SyntacticCast") || - tc.production().klabel().get().name().equals("#InnerCast")) - { - return apply(tc.get(0)); - } - return super.apply(tc); + @Override + public Term apply(TermCons tc) { + if (tc.production().att().contains(Att.BRACKET()) + || tc.production().klabel().get().name().equals("#SyntacticCast") + || tc.production().klabel().get().name().equals("#InnerCast")) { + return apply(tc.get(0)); } + return super.apply(tc); + } } diff --git a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/RemoveOverloads.java b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/RemoveOverloads.java index 7f0868b4faf..7cd8328d09c 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/RemoveOverloads.java +++ b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/RemoveOverloads.java @@ -1,6 +1,9 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner.disambiguation; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; import org.kframework.POSet; import org.kframework.definition.Production; import org.kframework.parser.Ambiguity; @@ -8,26 +11,26 @@ import org.kframework.parser.Term; import org.kframework.parser.TermCons; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; - public record RemoveOverloads(POSet overloads) { - public Term apply(Ambiguity a) { - Set productions = new HashSet<>(); - for (Term t : a.items()) { - if (t instanceof TermCons tc) { - productions.add(tc.production()); - } else { - return a; - } - } - Set candidates = overloads.minimal(productions); - Ambiguity result = Ambiguity.apply(a.items().stream().filter(t -> candidates.contains(((ProductionReference) t).production())).collect(Collectors.toSet())); - if (result.items().size() == 1) { - return result.items().iterator().next(); - } - return result; + public Term apply(Ambiguity a) { + Set productions = new HashSet<>(); + for (Term t : a.items()) { + if (t instanceof TermCons tc) { + productions.add(tc.production()); + } else { + return a; + } + } + Set candidates = overloads.minimal(productions); + Ambiguity result = + Ambiguity.apply( + a.items().stream() + .filter(t -> candidates.contains(((ProductionReference) t).production())) + .collect(Collectors.toSet())); + if (result.items().size() == 1) { + return result.items().iterator().next(); } + return result; + } } diff --git a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/ResolveOverloadedTerminators.java b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/ResolveOverloadedTerminators.java index 37d1da30d83..f1d68d055fb 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/ResolveOverloadedTerminators.java +++ b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/ResolveOverloadedTerminators.java @@ -1,7 +1,11 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner.disambiguation; +import static org.kframework.Collections.*; + import com.google.common.collect.Sets; +import java.util.Set; +import java.util.stream.Collectors; import org.kframework.POSet; import org.kframework.attributes.Att; import org.kframework.definition.Production; @@ -9,37 +13,37 @@ import org.kframework.parser.Term; import org.kframework.parser.TermCons; import org.kframework.utils.errorsystem.KEMException; - -import java.util.Set; -import java.util.stream.Collectors; - import scala.util.Either; import scala.util.Left; -import static org.kframework.Collections.*; - public class ResolveOverloadedTerminators extends SetsTransformerWithErrors { - private final POSet overloads; - - public ResolveOverloadedTerminators(POSet overloads) { - this.overloads = overloads; - } - - @Override - public Either, Term> apply(TermCons tc) { - if (overloads.elements().contains(tc.production()) && tc.items().isEmpty()) { - Set candidates = stream(overloads.elements()).filter(p -> p.klabel().isDefined() && p.klabelAtt().equals(tc.production().klabelAtt()) && overloads.lessThanEq(p, tc.production())).collect(Collectors.toSet()); - candidates = overloads.minimal(candidates); - if (candidates.size() != 1) { - String msg = "Overloaded term does not have a least sort. Possible sorts: " + candidates; - return Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, tc))); - } - Production prod = candidates.iterator().next(); - prod = prod.withAtt(prod.att() - .add(Att.ORIGINAL_PRD(), Production.class, tc.production())); - return super.apply(TermCons.apply(tc.items(), prod, tc.location(), tc.source())); - } - return super.apply(tc); + private final POSet overloads; + + public ResolveOverloadedTerminators(POSet overloads) { + this.overloads = overloads; + } + + @Override + public Either, Term> apply(TermCons tc) { + if (overloads.elements().contains(tc.production()) && tc.items().isEmpty()) { + Set candidates = + stream(overloads.elements()) + .filter( + p -> + p.klabel().isDefined() + && p.klabelAtt().equals(tc.production().klabelAtt()) + && overloads.lessThanEq(p, tc.production())) + .collect(Collectors.toSet()); + candidates = overloads.minimal(candidates); + if (candidates.size() != 1) { + String msg = "Overloaded term does not have a least sort. Possible sorts: " + candidates; + return Left.apply(Sets.newHashSet(KEMException.innerParserError(msg, tc))); + } + Production prod = candidates.iterator().next(); + prod = prod.withAtt(prod.att().add(Att.ORIGINAL_PRD(), Production.class, tc.production())); + return super.apply(TermCons.apply(tc.items(), prod, tc.location(), tc.source())); } + return super.apply(tc); + } } diff --git a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/TreeCleanerVisitor.java b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/TreeCleanerVisitor.java index 040260b987c..344d3059a68 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/TreeCleanerVisitor.java +++ b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/TreeCleanerVisitor.java @@ -1,23 +1,20 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner.disambiguation; -import org.kframework.parser.SafeTransformer; import org.kframework.parser.Ambiguity; +import org.kframework.parser.SafeTransformer; import org.kframework.parser.Term; -import org.kframework.utils.errorsystem.KEMException; -/** - * Remove parsing artifacts such as single element ambiguities. - */ +/** Remove parsing artifacts such as single element ambiguities. */ public class TreeCleanerVisitor extends SafeTransformer { - @Override - public Term apply(Ambiguity amb) { - Term newTerm = super.apply(amb); - if (newTerm instanceof Ambiguity newAmb) { - if (newAmb.items().size() == 1) { - return newAmb.items().iterator().next(); - } - } - return newTerm; + @Override + public Term apply(Ambiguity amb) { + Term newTerm = super.apply(amb); + if (newTerm instanceof Ambiguity newAmb) { + if (newAmb.items().size() == 1) { + return newAmb.items().iterator().next(); + } } + return newTerm; + } } diff --git a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/TypeInferenceVisitor.java b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/TypeInferenceVisitor.java index 6af78b0bef0..94247730d49 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/TypeInferenceVisitor.java +++ b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/TypeInferenceVisitor.java @@ -1,61 +1,52 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner.disambiguation; +import static org.kframework.Collections.*; +import static org.kframework.kore.KORE.*; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import org.kframework.builtin.Sorts; -import org.kframework.definition.Production; import org.kframework.definition.NonTerminal; +import org.kframework.definition.Production; import org.kframework.kore.Sort; import org.kframework.parser.Ambiguity; import org.kframework.parser.Constant; import org.kframework.parser.ProductionReference; +import org.kframework.parser.SetsTransformerWithErrors; import org.kframework.parser.Term; import org.kframework.parser.TermCons; -import org.kframework.parser.SetsTransformerWithErrors; import org.kframework.utils.errorsystem.KEMException; - import org.pcollections.ConsPStack; - import scala.util.Either; import scala.util.Left; import scala.util.Right; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static org.kframework.Collections.*; -import static org.kframework.kore.KORE.*; - /** * Disambiguation transformer which performs type checking and variable type inference. * - * This class is responsible for most of the interaction at the K level, the overall driving of the disambiguation - * algorithm, and the pruning of ill-typed branches from the parse forest. All of the z3 code is managed by - * {@link TypeInferencer}. + *

This class is responsible for most of the interaction at the K level, the overall driving of + * the disambiguation algorithm, and the pruning of ill-typed branches from the parse forest. All of + * the z3 code is managed by {@link TypeInferencer}. * - * At a high level, the algorithm does the following: - * - * 1. Define all the sorts in z3 and the subsort relationship between sorts. - * 2. Declare all the variables and sort parameters in the term as z3 constants. - * 3. Assert that the sort parameters are not sort KLabel. - * 4. While preserving sharing, assert the constraints that determine whether the term is well-typed. - * 5. Add soft constraints indicating that we prefer larger solutions to smaller solutions. These serve as a heuristic - * only and do not exhaustively describe maximality. Their purpose is simply to cut the search space. - * 6. Check satisfiability. If the constraints cannot be satisfied, replay the constraints one at a time to determine - * the constraint at which the solution becomes unsatisfiable, and use the model of the last constraint to be - * satisfied to generate a type error. Otherwise: - * 7. Assert that the variables are not less than or equal to the model of the first solution, and check for another - * solution. Repeat this in a loop until the constraints become unsatisfied. - * 8. Filter out all models which are strictly less than some other model we have obtained. - * 9. For each remaining model, substitute the sorts of the variables into the term and trim out the branches of the - * parse forest for which that solution is not well typed. - * 10. Disjunct the substituted solutions together and return them. + *

At a high level, the algorithm does the following: * + *

1. Define all the sorts in z3 and the subsort relationship between sorts. 2. Declare all the + * variables and sort parameters in the term as z3 constants. 3. Assert that the sort parameters are + * not sort KLabel. 4. While preserving sharing, assert the constraints that determine whether the + * term is well-typed. 5. Add soft constraints indicating that we prefer larger solutions to smaller + * solutions. These serve as a heuristic only and do not exhaustively describe maximality. Their + * purpose is simply to cut the search space. 6. Check satisfiability. If the constraints cannot be + * satisfied, replay the constraints one at a time to determine the constraint at which the solution + * becomes unsatisfiable, and use the model of the last constraint to be satisfied to generate a + * type error. Otherwise: 7. Assert that the variables are not less than or equal to the model of + * the first solution, and check for another solution. Repeat this in a loop until the constraints + * become unsatisfied. 8. Filter out all models which are strictly less than some other model we + * have obtained. 9. For each remaining model, substitute the sorts of the variables into the term + * and trim out the branches of the parse forest for which that solution is not well typed. 10. + * Disjunct the substituted solutions together and return them. */ public class TypeInferenceVisitor extends SetsTransformerWithErrors { private final TypeInferencer inferencer; @@ -65,14 +56,18 @@ public class TypeInferenceVisitor extends SetsTransformerWithErrors, Term> apply(Term t) { Term loc = t; if (loc instanceof Ambiguity) { - loc = ((Ambiguity)loc).items().iterator().next(); + loc = ((Ambiguity) loc).items().iterator().next(); } // add constraints to inferencer inferencer.push(t, topSort, isAnywhere); @@ -95,28 +90,30 @@ public Either, Term> apply(Term t) { return Right.apply(t); } switch (inferencer.status()) { - case SATISFIABLE -> { - // there is at least one solution, so compute it and pop the soft constraints - inferencer.computeModel(); - inferencer.pop(); - } - case UNKNOWN -> { - // constraints could not be solved, so error - inferencer.pop(); - throw KEMException.internalError("Could not solve sort constraints.", t); - } - case UNSATISFIABLE -> { - // no solutions exist. This is a type error, so ask the inferencer for an error message and return - inferencer.pop(); - Set kex = inferencer.error(); - return Left.apply(kex); - } + case SATISFIABLE -> { + // there is at least one solution, so compute it and pop the soft constraints + inferencer.computeModel(); + inferencer.pop(); + } + case UNKNOWN -> { + // constraints could not be solved, so error + inferencer.pop(); + throw KEMException.internalError("Could not solve sort constraints.", t); + } + case UNSATISFIABLE -> { + // no solutions exist. This is a type error, so ask the inferencer for an error message + // and return + inferencer.pop(); + Set kex = inferencer.error(); + return Left.apply(kex); + } } boolean hasAnotherSolution = true; Set> models = new HashSet<>(); boolean once = true; do { - // compute the last solution except the first time through the loop, when it was already done + // compute the last solution except the first time through the loop, when it was already + // done if (!once) { inferencer.computeModel(); } @@ -125,35 +122,36 @@ public Either, Term> apply(Term t) { do { inferencer.pushGreater(); switch (inferencer.status()) { - case SATISFIABLE -> { - // is not maximal, keep going - isMaximal = false; - inferencer.computeModel(); - inferencer.pop(); - } - case UNKNOWN -> - // constraints coiuld not be solved, so error - throw KEMException.internalError("Could not solve sortconstraints.", t); - case UNSATISFIABLE -> { - isMaximal = true; - inferencer.pop(); - } + case SATISFIABLE -> { + // is not maximal, keep going + isMaximal = false; + inferencer.computeModel(); + inferencer.pop(); + } + case UNKNOWN -> + // constraints coiuld not be solved, so error + throw KEMException.internalError("Could not solve sortconstraints.", t); + case UNSATISFIABLE -> { + isMaximal = true; + inferencer.pop(); + } } } while (!isMaximal); models.add(inferencer.getModel()); // assert that we don't want any solutions less than this one inferencer.pushNotModel(); - hasAnotherSolution = switch (inferencer.status()) { - case SATISFIABLE -> - // found another solution, keep going - true; - case UNKNOWN -> - // constraints could not be solved, so error - throw KEMException.internalError("Could not solve sort constraints.", t); - case UNSATISFIABLE -> - // no more solutions, terminate loop - false; - }; + hasAnotherSolution = + switch (inferencer.status()) { + case SATISFIABLE -> + // found another solution, keep going + true; + case UNKNOWN -> + // constraints could not be solved, so error + throw KEMException.internalError("Could not solve sort constraints.", t); + case UNSATISFIABLE -> + // no more solutions, terminate loop + false; + }; } while (hasAnotherSolution); // remove all models that are not maximal Set candidates = new HashSet<>(); @@ -184,24 +182,41 @@ public Either, Term> apply(Term t) { /** * A transformer which takes a particular type inference solution and applies it to a given term. * - * Essentially, for each term in the parse forest, we compute the actual sort of the term from the model, and compare - * it to the expected sort. If it is not well typed, we remove that branch of the parse forest entirely. We also, - * depending on the flags passed to the parent class, might add casts to the term around variables. + *

Essentially, for each term in the parse forest, we compute the actual sort of the term from + * the model, and compare it to the expected sort. If it is not well typed, we remove that branch + * of the parse forest entirely. We also, depending on the flags passed to the parent class, might + * add casts to the term around variables. */ public class TypeCheckVisitor extends SetsTransformerWithErrors { private Sort expectedSort; private boolean hasCastAlready = false, hasCheckAlready = false; + public TypeCheckVisitor(Sort topSort) { this.expectedSort = topSort; } - private Either, Term> typeError(ProductionReference pr, Sort expectedSort, Sort actualSort) { + private Either, Term> typeError( + ProductionReference pr, Sort expectedSort, Sort actualSort) { String msg; if (pr instanceof Constant) { - msg = "Unexpected sort " + actualSort + " for term " + ((Constant)pr).value() + ". Expected " + expectedSort + "."; + msg = + "Unexpected sort " + + actualSort + + " for term " + + ((Constant) pr).value() + + ". Expected " + + expectedSort + + "."; } else { - msg = "Unexpected sort " + actualSort + " for term parsed as production " + pr.production() + ". Expected " + expectedSort + "."; + msg = + "Unexpected sort " + + actualSort + + " for term parsed as production " + + pr.production() + + ". Expected " + + expectedSort + + "."; } return Left.apply(Collections.singleton(KEMException.innerParserError(msg, pr))); } @@ -211,23 +226,29 @@ public Either, Term> apply(Term term) { if (term instanceof Ambiguity amb) { return super.apply(amb); } - ProductionReference pr = (ProductionReference)term; - if (pr instanceof Constant && (pr.production().sort().equals(Sorts.KVariable()) || pr.production().sort().equals(Sorts.KConfigVar()))) { - // this is a variable, so check that its inferred sort is less than or equal to the expected sort + ProductionReference pr = (ProductionReference) term; + if (pr instanceof Constant + && (pr.production().sort().equals(Sorts.KVariable()) + || pr.production().sort().equals(Sorts.KConfigVar()))) { + // this is a variable, so check that its inferred sort is less than or equal to the expected + // sort Sort inferred = inferencer.getArgs(pr).apply(0); - if (!inferencer.module().subsorts().lessThanEq(inferred, expectedSort) && !expectedSort.equals(Sorts.KVariable())) { + if (!inferencer.module().subsorts().lessThanEq(inferred, expectedSort) + && !expectedSort.equals(Sorts.KVariable())) { // this variable is not well typed at this location, so prune this branch return typeError(pr, expectedSort, inferred); } // well typed, so add a cast and return - return wrapTermWithCast((Constant)pr, inferred); + return wrapTermWithCast((Constant) pr, inferred); } // compute the instantiated production with its sort parameters Production substituted = pr.production().substitute(inferencer.getArgs(pr)); Sort actualSort = substituted.sort(); boolean isExactSort = hasCastAlready && !hasCheckAlready; // check type: inner casts and syntactic casts indicate type equality, everything else is <= - if ((isExactSort && !actualSort.equals(expectedSort)) || (!isExactSort && !inferencer.module().subsorts().lessThanEq(actualSort, expectedSort))) { + if ((isExactSort && !actualSort.equals(expectedSort)) + || (!isExactSort + && !inferencer.module().subsorts().lessThanEq(actualSort, expectedSort))) { // not well typed, so prune this branch return typeError(pr, expectedSort, actualSort); } @@ -239,12 +260,13 @@ public Either, Term> apply(Term term) { boolean wasCast = hasCastAlready; boolean wasCheck = hasCheckAlready; // compute whether this is a cast already - if (substituted.klabel().isDefined() && substituted.klabel().get().name().startsWith("#SemanticCastTo")) { + if (substituted.klabel().isDefined() + && substituted.klabel().get().name().startsWith("#SemanticCastTo")) { hasCheckAlready = true; hasCastAlready = true; - } else if (substituted.klabel().isDefined() && - (substituted.klabel().get().name().equals("#SyntacticCast") || - substituted.klabel().get().name().equals("#InnerCast"))) { + } else if (substituted.klabel().isDefined() + && (substituted.klabel().get().name().equals("#SyntacticCast") + || substituted.klabel().get().name().equals("#InnerCast"))) { hasCastAlready = true; hasCheckAlready = false; } else { @@ -261,8 +283,7 @@ public Either, Term> apply(Term term) { expectedSort = oldExpected; hasCastAlready = wasCast; hasCheckAlready = wasCheck; - if (rez.isLeft()) - return rez; + if (rez.isLeft()) return rez; // apply result of visiting child to the term. tc = tc.with(j, rez.right().get()); j++; @@ -277,10 +298,21 @@ private Either, Term> wrapTermWithCast(Constant c, Sort declar Production cast; if (inferSortChecks && !hasCheckAlready) { // strictly typing variables and one does not already exist, so add :Sort - cast = inferencer.module().productionsFor().apply(KLabel("#SemanticCastTo" + declared.toString())).head(); - } else if (inferCasts && !hasCastAlready && inferencer.module().productionsFor().contains(KLabel("#SyntacticCast"))) { + cast = + inferencer + .module() + .productionsFor() + .apply(KLabel("#SemanticCastTo" + declared.toString())) + .head(); + } else if (inferCasts + && !hasCastAlready + && inferencer.module().productionsFor().contains(KLabel("#SyntacticCast"))) { // casting variables and one doeds not already exist, so add ::Sort - cast = stream(inferencer.module().productionsFor().apply(KLabel("#SyntacticCast"))).filter(p -> p.sort().equals(declared)).findAny().get(); + cast = + stream(inferencer.module().productionsFor().apply(KLabel("#SyntacticCast"))) + .filter(p -> p.sort().equals(declared)) + .findAny() + .get(); } else { // unparsing or cast already exists, so do nothing cast = null; diff --git a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/TypeInferencer.java b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/TypeInferencer.java index e373279f727..21d22822985 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/disambiguation/TypeInferencer.java +++ b/kernel/src/main/java/org/kframework/parser/inner/disambiguation/TypeInferencer.java @@ -1,10 +1,27 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner.disambiguation; +import static org.kframework.Collections.*; +import static org.kframework.kore.KORE.*; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import org.kframework.Collections; import org.kframework.POSet; import org.kframework.attributes.Att; -import org.kframework.Collections; -import org.kframework.TopologicalSort; import org.kframework.attributes.Location; import org.kframework.builtin.KLabels; import org.kframework.builtin.Sorts; @@ -22,35 +39,16 @@ import org.kframework.parser.inner.RuleGrammarGenerator; import org.kframework.utils.OS; import org.kframework.utils.errorsystem.KEMException; - +import scala.Tuple2; import scala.collection.Seq; import scala.collection.Set; -import scala.Tuple2; - -import java.io.BufferedReader; -import java.io.File; -import java.io.InputStreamReader; -import java.io.IOException; -import java.io.PrintStream; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; - -import static org.kframework.kore.KORE.*; -import static org.kframework.Collections.*; /** * Class to manage communication with z3 for the purposes of type inference. This class is driven by - * {@link TypeInferenceVisitor} and handles all the communication to/from z3 as well as construction of constraints. + * {@link TypeInferenceVisitor} and handles all the communication to/from z3 as well as construction + * of constraints. * - * For a description of the algorithm, see the companion class's javadoc. + *

For a description of the algorithm, see the companion class's javadoc. */ public class TypeInferencer implements AutoCloseable { @@ -69,10 +67,10 @@ public enum Status { private final Module mod; private final java.util.Set sorts; - // logic QF_DT is best if it exists as it will be faster than ALL. However, some z3 versions do not have this logic. + // logic QF_DT is best if it exists as it will be faster than ALL. However, some z3 versions do + // not have this logic. // Fortunately, z3 ignores unknown logics. - private static final String PRELUDE1 = - "(set-logic QF_DT)\n"; + private static final String PRELUDE1 = "(set-logic QF_DT)\n"; private final boolean destroyOnReset; @@ -89,22 +87,24 @@ private void initProcess() { /** * Create a new z3 process and write the sorts and subsort relation to it. + * * @param mod the module to create an inferencer for. */ public TypeInferencer(Module mod, boolean debug) { initProcess(); println("(get-info :version)"); try { - String version = output.readLine(); - version = version.substring("(:version \"".length()); - version = version.substring(0, version.indexOf('"')); - String[] parts = version.split("\\."); - // example of version string: - // "4.8.8 - build hashcode ad55a1f1c617" - int major = Integer.valueOf(parts[0]); - int minor = Integer.valueOf(parts[1]); - int patch = Integer.valueOf(parts[2].split(" ")[0]); - destroyOnReset = major < 4 || (major == 4 && minor < 6) || (major == 4 && minor == 8 && patch == 9); + String version = output.readLine(); + version = version.substring("(:version \"".length()); + version = version.substring(0, version.indexOf('"')); + String[] parts = version.split("\\."); + // example of version string: + // "4.8.8 - build hashcode ad55a1f1c617" + int major = Integer.valueOf(parts[0]); + int minor = Integer.valueOf(parts[1]); + int patch = Integer.valueOf(parts[2].split(" ")[0]); + destroyOnReset = + major < 4 || (major == 4 && minor < 6) || (major == 4 && minor == 8 && patch == 9); } catch (IOException e) { throw KEMException.internalError("Could not read from z3 process", e); } @@ -115,13 +115,19 @@ public TypeInferencer(Module mod, boolean debug) { push(mod); } - // returns whether a particular sort should be written to z3 and thus be a possible sort for variables. + // returns whether a particular sort should be written to z3 and thus be a possible sort for + // variables. private boolean isRealSort(SortHead head) { if (head.params() > 0) { return true; } Sort s = Sort(head); - return !RuleGrammarGenerator.isParserSort(s) || s.equals(Sorts.K()) || s.equals(Sorts.KItem()) || s.equals(Sorts.KLabel()) || s.equals(Sorts.RuleTag()) || s.isNat(); + return !RuleGrammarGenerator.isParserSort(s) + || s.equals(Sorts.K()) + || s.equals(Sorts.KItem()) + || s.equals(Sorts.KLabel()) + || s.equals(Sorts.RuleTag()) + || s.isNat(); } public Module module() { @@ -130,6 +136,7 @@ public Module module() { /** * Writes the prelude for a particular module. + * * @param mod */ private void push(Module mod) { @@ -152,7 +159,8 @@ private void push(Module mod) { } private void makeSubsorts(Module mod, String name, POSet relations) { - // map from each sort to an integer representing the topological sorting of the sorts. higher numbers mean greater + // map from each sort to an integer representing the topological sorting of the sorts. higher + // numbers mean greater // sorts Map ordinals = new HashMap<>(); int i = 0; @@ -165,7 +173,10 @@ private void makeSubsorts(Module mod, String name, POSet relations) { } // provide fixed interpretation of subsort relation println("(define-fun " + name + " ((s1 Sort) (s2 Sort)) Bool (or"); - for (Tuple2> relation : stream(relations.relations()).sorted(Comparator.comparing(t -> -ordinals.getOrDefault(t._1().head(), 0))).collect(Collectors.toList())) { + for (Tuple2> relation : + stream(relations.relations()) + .sorted(Comparator.comparing(t -> -ordinals.getOrDefault(t._1().head(), 0))) + .collect(Collectors.toList())) { if (!isRealSort(relation._1().head())) { continue; } @@ -200,7 +211,8 @@ private void makeSubsorts(Module mod, String name, POSet relations) { private final List variables = new ArrayList<>(); // list of names for sort parameters only in z3 private final List parameters = new ArrayList<>(); - // array mapping from integer id of terms to the list of names of their variable or sort parameters in z3 + // array mapping from integer id of terms to the list of names of their variable or sort + // parameters in z3 private final List> variablesById = new ArrayList<>(); // array mapping from integer id of terms to the expected sorts they have appeared under so far private final List> cacheById = new ArrayList<>(); @@ -211,6 +223,7 @@ private void makeSubsorts(Module mod, String name, POSet relations) { private static class LocalizedError extends RuntimeException { private final Term loc; + LocalizedError(String message, Term loc) { super(message); this.loc = loc; @@ -222,8 +235,9 @@ public Term getLocation() { } /** - * Replays a list of {@link Constraint} one at a time and throws an exception with the error message explaining - * why the constraints are not satisfied. + * Replays a list of {@link Constraint} one at a time and throws an exception with the error + * message explaining why the constraints are not satisfied. + * * @param constraints */ private void replayConstraints(List constraints) { @@ -235,36 +249,48 @@ private void replayConstraints(List constraints) { status = null; Status status = status(); switch (status) { - case SATISFIABLE -> { - println("(pop)"); - print("(assert (and "); - print(constraint.smt); - println("))"); - } - case UNKNOWN -> throw KEMException.internalError("Could not solve sort constraints.", currentTerm); - case UNSATISFIABLE -> { - println("(pop)"); - computeStatus(); - if (constraint.name != null) { - Sort actualSort = computeValue(constraint.name); - Sort expectedSort = eval(constraint.expectedSort, constraint.expectedParams); - throw new LocalizedError("Unexpected sort " + actualSort + " for variable " + constraint.loc.value() + ". Expected: " + expectedSort, constraint.loc); - } else { - Sort actualSort = eval(constraint.actualSort, constraint.actualParams); - Sort expectedSort = eval(constraint.expectedSort, constraint.expectedParams); - throw new LocalizedError("Unexpected sort " + actualSort + " for term parsed as production " + constraint.actualParams.get().production() + ". Expected: " + expectedSort, constraint.actualParams.get()); + case SATISFIABLE -> { + println("(pop)"); + print("(assert (and "); + print(constraint.smt); + println("))"); + } + case UNKNOWN -> throw KEMException.internalError( + "Could not solve sort constraints.", currentTerm); + case UNSATISFIABLE -> { + println("(pop)"); + computeStatus(); + if (constraint.name != null) { + Sort actualSort = computeValue(constraint.name); + Sort expectedSort = eval(constraint.expectedSort, constraint.expectedParams); + throw new LocalizedError( + "Unexpected sort " + + actualSort + + " for variable " + + constraint.loc.value() + + ". Expected: " + + expectedSort, + constraint.loc); + } else { + Sort actualSort = eval(constraint.actualSort, constraint.actualParams); + Sort expectedSort = eval(constraint.expectedSort, constraint.expectedParams); + throw new LocalizedError( + "Unexpected sort " + + actualSort + + " for term parsed as production " + + constraint.actualParams.get().production() + + ". Expected: " + + expectedSort, + constraint.actualParams.get()); + } } - } } } } - /** - * Asserts that none of the sort parameters are of the KLabel sort. - */ + /** Asserts that none of the sort parameters are of the KLabel sort. */ private void assertNotKLabel() { - if (!sorts.contains(Sorts.KLabel().head())) - return; + if (!sorts.contains(Sorts.KLabel().head())) return; for (String param : parameters) { print("(distinct |" + param + "| "); printSort(Sorts.KLabel()); @@ -274,6 +300,7 @@ private void assertNotKLabel() { /** * Uses z3 to compute the error message associated with a badly typed term. + * * @return */ private KEMException push() { @@ -286,7 +313,7 @@ private KEMException push() { for (String var : variables) { println("(declare-const |" + var + "| Sort)"); } - //assert sort parameters are not sort KLabel. + // assert sort parameters are not sort KLabel. print("(assert (and true "); assertNotKLabel(); println("))"); @@ -303,6 +330,7 @@ private KEMException push() { /** * write constraints to z3 for a term. + * * @param t The term to compute constraints for. * @param topSort The expected sort at the top of the term. * @param isAnywhere Whether the term is an anywhere rule. @@ -311,14 +339,14 @@ public void push(Term t, Sort topSort, boolean isAnywhere) { currentTerm = t; currentTopSort = topSort; this.isAnywhere = isAnywhere; - level+=2; + level += 2; println("(push)"); // compute constraints in non-incremental mode ExpectedSortsVisitor viz = new ExpectedSortsVisitor(topSort, isAnywhere, false); String id = viz.apply(t); if (variables.isEmpty()) { - // there are no variables. so return as there is nothing to infer. - return; + // there are no variables. so return as there is nothing to infer. + return; } // declare variables and sort parameters for (String var : variables) { @@ -346,6 +374,7 @@ public void push(Term t, Sort topSort, boolean isAnywhere) { /** * Returns the top term on the lhs of a rule, if one exists. + * * @param t * @return */ @@ -354,40 +383,47 @@ private static Optional getFunction(Term t) { return Optional.empty(); } while (child.production().att().contains(Att.BRACKET())) { - if (((TermCons)child).get(0) instanceof Ambiguity) { + if (((TermCons) child).get(0) instanceof Ambiguity) { return Optional.empty(); } - child = (ProductionReference)((TermCons)child).get(0); + child = (ProductionReference) ((TermCons) child).get(0); } - if (child.production().klabel().isDefined() && child.production().klabel().get().head().equals(KLabels.KREWRITE)) { - if (((TermCons)child).get(0) instanceof Ambiguity) { + if (child.production().klabel().isDefined() + && child.production().klabel().get().head().equals(KLabels.KREWRITE)) { + if (((TermCons) child).get(0) instanceof Ambiguity) { return Optional.empty(); } - child = (ProductionReference)((TermCons)child).get(0); + child = (ProductionReference) ((TermCons) child).get(0); } while (child.production().att().contains(Att.BRACKET())) { - if (((TermCons)child).get(0) instanceof Ambiguity) { + if (((TermCons) child).get(0) instanceof Ambiguity) { return Optional.empty(); } - child = (ProductionReference)((TermCons)child).get(0); + child = (ProductionReference) ((TermCons) child).get(0); } return Optional.of(child); } /** * Returns true if a rule is a function or anywhere rule. + * * @param t * @param isAnywhere * @return */ private static boolean isFunction(Term t, boolean isAnywhere) { - return isAnywhere || - getFunction(t).filter(pr -> pr.production().att().contains(Att.FUNCTION()) - || ExpandMacros.isMacro(pr.production())).isPresent(); + return isAnywhere + || getFunction(t) + .filter( + pr -> + pr.production().att().contains(Att.FUNCTION()) + || ExpandMacros.isMacro(pr.production())) + .isPresent(); } /** * Returns return sort of a function or anywhere rule. + * * @param t * @return */ @@ -397,27 +433,26 @@ private static Sort getFunctionSort(Term t) { /** * Returns the declared of a cast. + * * @param tc * @return */ private static Sort getSortOfCast(TermCons tc) { switch (tc.production().klabel().get().name()) { - case "#SyntacticCast": - case "#OuterCast": - return tc.production().sort(); - case "#InnerCast": - return ((NonTerminal)tc.production().items().apply(1)).sort(); - default: - if (tc.production().klabel().get().name().startsWith("#SemanticCastTo")) { + case "#SyntacticCast": + case "#OuterCast": return tc.production().sort(); - } - throw new AssertionError("Unexpected cast type"); + case "#InnerCast": + return ((NonTerminal) tc.production().items().apply(1)).sort(); + default: + if (tc.production().klabel().get().name().startsWith("#SemanticCastTo")) { + return tc.production().sort(); + } + throw new AssertionError("Unexpected cast type"); } } - /** - * A z3 constraint to be replayed later. - */ + /** A z3 constraint to be replayed later. */ public static class Constraint { public final String smt; public final String name; @@ -429,13 +464,19 @@ public static class Constraint { /** * Creates an upper bound constraint on a variable. + * * @param smt The smtlib of the constraint * @param name The name of the variable * @param loc The variable * @param expectedSort The upper bound on the variable. * @param expectedParams The term that provided that upper bound. */ - public Constraint(String smt, String name, Constant loc, Sort expectedSort, Optional expectedParams) { + public Constraint( + String smt, + String name, + Constant loc, + Sort expectedSort, + Optional expectedParams) { this.smt = smt; this.name = name; this.loc = loc; @@ -447,13 +488,19 @@ public Constraint(String smt, String name, Constant loc, Sort expectedSort, Opti /** * Creates a constraint that an actual sort is less than or equal to an expected sort. + * * @param smt The smtlib of the constraint * @param actualSort The actual sort of the term. * @param actualParams The term * @param expectedSort The expected sort of the term. * @param expectedParams The term that provided that expected sort. */ - public Constraint(String smt, Sort actualSort, Optional actualParams, Sort expectedSort, Optional expectedParams) { + public Constraint( + String smt, + Sort actualSort, + Optional actualParams, + Sort expectedSort, + Optional expectedParams) { this.smt = smt; this.name = null; this.loc = null; @@ -465,6 +512,7 @@ public Constraint(String smt, Sort actualSort, Optional act /** * Returns true if a constraint is an upper bound on a variable. + * * @return */ public boolean isVar() { @@ -473,8 +521,8 @@ public boolean isVar() { } /** - * Computes the smtlib constraints for a term by traversing it and creating a set of declarations that capture the - * sort constraints. + * Computes the smtlib constraints for a term by traversing it and creating a set of declarations + * that capture the sort constraints. */ public class ExpectedSortsVisitor { private Sort expectedSort; @@ -490,10 +538,10 @@ public class ExpectedSortsVisitor { private final Map> ambCache = new IdentityHashMap<>(); /** - * * @param topSort Expected sort at top of term. * @param isAnywhere true if the term is an anywhere rule. - * @param isIncremental true if we should compute the constraints as a list of {@link Constraint} + * @param isIncremental true if we should compute the constraints as a list of {@link + * Constraint} */ ExpectedSortsVisitor(Sort topSort, boolean isAnywhere, boolean isIncremental) { this.expectedSort = topSort; @@ -502,16 +550,18 @@ public class ExpectedSortsVisitor { } /** - * Generate constraints for a term and accumulate them either in sb if in non-incremental mode, or in constraints - * if in incremental model. + * Generate constraints for a term and accumulate them either in sb if in non-incremental mode, + * or in constraints if in incremental model. + * * @param t The term to generate constraints for. - * @return the name of the function generated by this term in non-incremental mode that captures the constraints of - * this term. + * @return the name of the function generated by this term in non-incremental mode that captures + * the constraints of this term. */ public String apply(Term t) { if (t instanceof Ambiguity amb) { if (isIncremental) { - // we are error checking an ill typed term, so we pick just one branch of the ambiguity and explain why it is + // we are error checking an ill typed term, so we pick just one branch of the ambiguity + // and explain why it is // ill typed. return apply(amb.items().iterator().next()); } @@ -521,7 +571,8 @@ public String apply(Term t) { int id = contexts.computeIfAbsent(expected, expected2 -> ambId); boolean cached = id != ambId; if (!cached) { - // if this is the first time reaching this ambiguity node with this expected sort, define a new function + // if this is the first time reaching this ambiguity node with this expected sort, define + // a new function // with a disjunction of each of the children of the ambiguity. ambId++; List ids = new ArrayList<>(); @@ -537,13 +588,15 @@ public String apply(Term t) { // return name of created or cached function return "amb" + id; } - ProductionReference pr = (ProductionReference)t; + ProductionReference pr = (ProductionReference) t; List ids = new ArrayList<>(); - boolean isTopSort = expectedSort.equals(Sorts.RuleContent()) || expectedSort.name().equals("#RuleBody"); + boolean isTopSort = + expectedSort.equals(Sorts.RuleContent()) || expectedSort.name().equals("#RuleBody"); int id = nextId; boolean shared = pr.id().isPresent() && variablesById.size() > pr.id().get(); if (!shared) { - // if this is the first time reaching this term, initialize data structures with the variables associated with + // if this is the first time reaching this term, initialize data structures with the + // variables associated with // this term. nextId++; variablesById.add(new ArrayList<>()); @@ -572,14 +625,16 @@ public String apply(Term t) { isStrictEquality = false; // compute expected sort and whether this is a cast if (tc.production().klabel().isDefined() - && (tc.production().klabel().get().name().equals("#SyntacticCast") - || tc.production().klabel().get().name().startsWith("#SemanticCastTo") - || tc.production().klabel().get().name().equals("#InnerCast"))) { + && (tc.production().klabel().get().name().equals("#SyntacticCast") + || tc.production().klabel().get().name().startsWith("#SemanticCastTo") + || tc.production().klabel().get().name().equals("#InnerCast"))) { expectedSort = getSortOfCast(tc); - isStrictEquality = tc.production().klabel().get().name().equals("#SyntacticCast") - || tc.production().klabel().get().name().equals("#InnerCast"); + isStrictEquality = + tc.production().klabel().get().name().equals("#SyntacticCast") + || tc.production().klabel().get().name().equals("#InnerCast"); if (tc.get(0) instanceof Constant child) { - if (child.production().sort().equals(Sorts.KVariable()) || child.production().sort().equals(Sorts.KConfigVar())) { + if (child.production().sort().equals(Sorts.KVariable()) + || child.production().sort().equals(Sorts.KConfigVar())) { isStrictEquality = true; } } @@ -589,7 +644,8 @@ public String apply(Term t) { } else { expectedSort = nt.sort(); } - // recurse and add name of function generated by child to the current children of this constraint. + // recurse and add name of function generated by child to the current children of this + // constraint. ids.add(apply(tc.get(j))); j++; } @@ -602,15 +658,24 @@ public String apply(Term t) { String expected = printSort(expectedSort, expectedParams, false).replace("|", ""); boolean cached = !cacheById.get(id).add(expected); if (!isIncremental && (!shared || !cached)) { - // if we are in non-incremental mode and this is the first time reaching this term under this expected sort, - // define a new function with a conjunction of each of the children of the term and the constraints of the + // if we are in non-incremental mode and this is the first time reaching this term under + // this expected sort, + // define a new function with a conjunction of each of the children of the term and the + // constraints of the // current term. - sb.append("(define-fun |constraint").append(id).append("_").append(expected).append("| () Bool (and true "); + sb.append("(define-fun |constraint") + .append(id) + .append("_") + .append(expected) + .append("| () Bool (and true "); } if (isIncremental || !shared || !cached) { - // if we are in incremental mode or this is the first time reaching this term under this expected sort, + // if we are in incremental mode or this is the first time reaching this term under this + // expected sort, // compute the local constraints of this term and add them to the current constraint. - if (pr instanceof Constant c && (pr.production().sort().equals(Sorts.KVariable()) || pr.production().sort().equals(Sorts.KConfigVar()))) { + if (pr instanceof Constant c + && (pr.production().sort().equals(Sorts.KVariable()) + || pr.production().sort().equals(Sorts.KConfigVar()))) { String name; boolean oldStrictEquality = isStrictEquality; if (!shared) { @@ -637,7 +702,7 @@ public String apply(Term t) { pushConstraint(pr.production().sort(), Optional.of(pr)); } } - if (!isIncremental && (!shared || !cached)) { + if (!isIncremental && (!shared || !cached)) { for (String i : ids) { sb.append(i).append(" "); } @@ -653,11 +718,13 @@ boolean isAnonVar(Constant var) { /** * Add a constraint that an actual sort is less than an expected sort. + * * @param actualSort * @param actualParams */ private void pushConstraint(Sort actualSort, Optional actualParams) { - if (mod.subsorts().lessThanEq(actualSort, Sorts.KBott()) || mod.subsorts().lessThan(Sorts.K(), actualSort)) { + if (mod.subsorts().lessThanEq(actualSort, Sorts.KBott()) + || mod.subsorts().lessThan(Sorts.K(), actualSort)) { return; } if (isBadNatSort(actualSort)) { @@ -683,6 +750,7 @@ private void pushConstraint(Sort actualSort, Optional actua /** * Add a constraint that a variable is less than an expected sort. + * * @param name * @param loc */ @@ -694,7 +762,7 @@ private void pushConstraint(String name, Constant loc) { } sb.append("|").append(name).append("| "); if (mod.subsorts().lessThan(Sorts.K(), expectedSort)) { - expectedSort = Sorts.K(); + expectedSort = Sorts.K(); } sb.append(printSort(expectedSort, expectedParams, isIncremental)); sb.append(") "); @@ -711,7 +779,8 @@ private void saveConstraint(String name, Constant loc) { } private void saveConstraint(Sort actualSort, Optional actualParams) { - constraints.add(new Constraint(sb.toString(), actualSort, actualParams, expectedSort, expectedParams)); + constraints.add( + new Constraint(sb.toString(), actualSort, actualParams, expectedSort, expectedParams)); sb = new StringBuilder(); } @@ -725,6 +794,7 @@ private boolean isBadNatSort(Sort actualSort) { if (actualSort.isNat() && !mod.definedSorts().contains(actualSort.head())) return true; return stream(actualSort.params()).anyMatch(this::isBadNatSort); } + private String printSort(Sort s, Optional t, boolean isIncremental) { Map params = new HashMap<>(); if (t.isPresent()) { @@ -732,7 +802,7 @@ private String printSort(Sort s, Optional t, boolean isIncr int id = t.get().id().get(); List names = variablesById.get(id); Seq formalParams = t.get().production().params(); - assert(names.size() == formalParams.size()); + assert (names.size() == formalParams.size()); for (int i = 0; i < names.size(); i++) { params.put(formalParams.apply(i), names.get(i)); } @@ -775,6 +845,7 @@ private void printSort(Sort s) { /** * Check satisfiability of current assertions and return status. + * * @return */ private Status computeStatus() { @@ -784,14 +855,20 @@ private Status computeStatus() { do { result = output.readLine(); if (result == null) { - throw KEMException.internalError("Unexpected EOF reached while waiting for response from z3.", currentTerm); + throw KEMException.internalError( + "Unexpected EOF reached while waiting for response from z3.", currentTerm); } - } while (!result.equals("sat") && !result.equals("unsat") && !result.equals("unknown") && !result.equals("timeout") && !result.startsWith("(error")); + } while (!result.equals("sat") + && !result.equals("unsat") + && !result.equals("unknown") + && !result.equals("timeout") + && !result.startsWith("(error")); return switch (result) { case "sat" -> Status.SATISFIABLE; case "unsat" -> Status.UNSATISFIABLE; case "unknown", "timeout" -> Status.UNKNOWN; - default -> throw KEMException.internalError("Unexpected result from z3: " + result, currentTerm); + default -> throw KEMException.internalError( + "Unexpected result from z3: " + result, currentTerm); }; } catch (IOException e) { throw KEMException.internalError("Could not read from z3 process", e, currentTerm); @@ -800,11 +877,12 @@ private Status computeStatus() { /** * Check satisfiability of current assertions and return status, cached if called multiple times. + * * @return */ public Status status() { if (status == null) { - status = computeStatus(); + status = computeStatus(); } return status; } @@ -904,8 +982,7 @@ private Sort readSort(boolean trim) { result = result.substring(startIdx, endIdx); } Sort r = new SmtSortParser(new StringReader(result)).Sort(); - if (debug) - sb.append("; ").append(r).append("\n"); + if (debug) sb.append("; ").append(r).append("\n"); return r; } catch (IOException e) { throw KEMException.internalError("Could not read from z3 process", e, currentTerm); @@ -925,8 +1002,7 @@ public void close() { } private void reset() { - if (level > 0) - return; + if (level > 0) return; if (debug) { System.err.print(sb.toString()); sb = new StringBuilder(); @@ -950,11 +1026,19 @@ private void reset() { private static String locStr(ProductionReference pr) { String suffix = ""; if (pr.production().klabel().isDefined()) { - suffix = "_" + pr.production().klabel().get().name().replace("|",""); + suffix = "_" + pr.production().klabel().get().name().replace("|", ""); } if (pr.location().isPresent()) { Location l = pr.location().get(); - return "_" + l.startLine() + "_" + l.startColumn() + "_" + l.endLine() + "_" + l.endColumn() + suffix; + return "_" + + l.startLine() + + "_" + + l.startColumn() + + "_" + + l.endLine() + + "_" + + l.endColumn() + + suffix; } return pr.id().get() + suffix; } diff --git a/kernel/src/main/java/org/kframework/parser/inner/kernel/EarleyParser.java b/kernel/src/main/java/org/kframework/parser/inner/kernel/EarleyParser.java index dd8995f655f..bea1beea752 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/kernel/EarleyParser.java +++ b/kernel/src/main/java/org/kframework/parser/inner/kernel/EarleyParser.java @@ -1,7 +1,12 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner.kernel; +import static org.kframework.Collections.*; +import static org.kframework.kore.KORE.*; + import com.google.common.primitives.Ints; +import java.util.*; +import java.util.stream.Collectors; import org.apache.commons.codec.binary.StringUtils; import org.jetbrains.annotations.NotNull; import org.kframework.attributes.Att; @@ -25,32 +30,27 @@ import org.pcollections.ConsPStack; import org.pcollections.PStack; -import java.util.*; -import java.util.stream.Collectors; - -import static org.kframework.Collections.*; -import static org.kframework.kore.KORE.*; - /** - * A parser for general context-free grammars based on the Earley recognizer algorithm found in - * Jay Earley's paper - * "An efficient context-free parsing algorithm" - * with some adaptations from Elizabeth Scott's paper - * "SPPF-Style Parsing from Earley Recognizers". - * and a minor fix relating to nullability taken from Aycock and Horspool's - * "Practical Earley Parsing" + * A parser for general context-free grammars based on the Earley recognizer algorithm found in Jay + * Earley's paper "An efficient context-free + * parsing algorithm" with some adaptations from Elizabeth Scott's paper "SPPF-Style Parsing from Earley + * Recognizers". and a minor fix relating to nullability taken from Aycock and Horspool's "Practical Earley Parsing" * - * The algorithm is not _quite_ the one described in section 5 of Scott's paper. For one thing, we use a single-token - * lookahead during prediction to cut down on the number of states we need to process. For another, we don't use quite - * the same approach for constructing parse forests. Finally, there was an issue with Scott's implementation of nullable - * non-terminals, and so we implement the prediction step from Aycock and Horspool's implementation when predicting a - * nullable non-terminal. Due to the difference in the parse forests, this algorithm is unbounded-polynomial-order in - * space usage. It would be theoretically possible to modify the way we generate parse forests to more closely follow - * Scott's paper, which would give us a parser which uses cubic space, but this would require us to modify the Term type - * rather substantially in order to binarize the TermCons nodes so that each has at most two children. This would be a - * change that requires a much more substantial rewriting of the algorithm we use to process parse forests. It is - * definitely worth considering however as it would dramatically improve the worst-case space usage of the algorithm on - * ambiguous sentences. + *

The algorithm is not _quite_ the one described in section 5 of Scott's paper. For one thing, + * we use a single-token lookahead during prediction to cut down on the number of states we need to + * process. For another, we don't use quite the same approach for constructing parse forests. + * Finally, there was an issue with Scott's implementation of nullable non-terminals, and so we + * implement the prediction step from Aycock and Horspool's implementation when predicting a + * nullable non-terminal. Due to the difference in the parse forests, this algorithm is + * unbounded-polynomial-order in space usage. It would be theoretically possible to modify the way + * we generate parse forests to more closely follow Scott's paper, which would give us a parser + * which uses cubic space, but this would require us to modify the Term type rather substantially in + * order to binarize the TermCons nodes so that each has at most two children. This would be a + * change that requires a much more substantial rewriting of the algorithm we use to process parse + * forests. It is definitely worth considering however as it would dramatically improve the + * worst-case space usage of the algorithm on ambiguous sentences. */ public class EarleyParser { @@ -66,9 +66,10 @@ private interface EarleyProductionItem { /** * @param nullable a {@link BitSet} where each index corresponds to a sort. - * @return true if this production item can be parsed as the empty string according to the BitSet, false otherwise. - * @apiNote Do not use this to check whether a sort or production is nullable. It exists solely to facilitate the - * code which computes nullability. + * @return true if this production item can be parsed as the empty string according to the + * BitSet, false otherwise. + * @apiNote Do not use this to check whether a sort or production is nullable. It exists solely + * to facilitate the code which computes nullability. */ boolean isNullable(BitSet nullable); } @@ -76,12 +77,16 @@ private interface EarleyProductionItem { /** * A single non-terminal in an {@link EarleyProduction}. * - * The sort is represented as an index within the `sorts` field. + *

The sort is represented as an index within the `sorts` field. */ - private record EarleyNonTerminal(int sort, List sorts) - implements EarleyProductionItem { - public boolean isNonTerminal() { return true; } - public boolean isNullable(BitSet nullable) { return nullable.get(sort); } + private record EarleyNonTerminal(int sort, List sorts) implements EarleyProductionItem { + public boolean isNonTerminal() { + return true; + } + + public boolean isNullable(BitSet nullable) { + return nullable.get(sort); + } public String toString() { return sorts.get(sort).toString(); @@ -91,13 +96,18 @@ public String toString() { /** * A single terminal in an {@link EarleyProduction}. * - * The terminal is represented by a particular token kind as informed by the provided {@link Scanner}. - * Token 0 is always the EOF token, which should appear only in the production used by the start state of the parser. + *

The terminal is represented by a particular token kind as informed by the provided {@link + * Scanner}. Token 0 is always the EOF token, which should appear only in the production used by + * the start state of the parser. */ - private record EarleyTerminal(Scanner scanner, int kind) - implements EarleyProductionItem { - public boolean isNonTerminal() { return false; } - public boolean isNullable(BitSet ignored) { return false; } + private record EarleyTerminal(Scanner scanner, int kind) implements EarleyProductionItem { + public boolean isNonTerminal() { + return false; + } + + public boolean isNullable(BitSet ignored) { + return false; + } public String toString() { if (kind == 0) { @@ -107,21 +117,21 @@ public String toString() { } } - /** - * A single production as represented by the parser. - */ + /** A single production as represented by the parser. */ private static final class EarleyProduction { /** - * @param index the index of the production within the `productions` field. Index 0 is reserved for the production - * `syntax ENTRY ::= STARTSYMBOL "EOF", where ENTRY is sort 0, STARTSYMBOL is the start symbol of the - * parser, and "EOF" is the EOF token, token 0. - * @param prod The {@link Production} to use to construct a {@link org.kframework.parser.ProductionReference} from - * a completed parse state. Use null in the case of production 0. + * @param index the index of the production within the `productions` field. Index 0 is reserved + * for the production `syntax ENTRY ::= STARTSYMBOL "EOF", where ENTRY is sort 0, + * STARTSYMBOL is the start symbol of the parser, and "EOF" is the EOF token, token 0. + * @param prod The {@link Production} to use to construct a {@link + * org.kframework.parser.ProductionReference} from a completed parse state. Use null in the + * case of production 0. * @param sort The sort of the production, as an index in the `sorts` field. * @param items The production's production items. * @param sorts The list of all sorts used by the parser in canonical order. */ - public EarleyProduction(int index, Production prod, int sort, List items, List sorts) { + public EarleyProduction( + int index, Production prod, int sort, List items, List sorts) { this.index = index; this.prod = prod; this.sort = sort; @@ -142,8 +152,13 @@ public EarleyProduction(int index, Production prod, int sort, ListThis class is only used to facilitate nullability computations. However, conceptually, the + * `prod` and `item` fields of an {@link EarleyState} also represent an LR(0) parsing item. */ private static final class LRItem { public LRItem(EarleyProduction prod, int item) { @@ -218,15 +233,16 @@ public String toString() { } /** - * An Earley parser parse state. Consists of an inlined LR(0) item (see {@link LRItem}) and an index within the - * sentence being parsed where the parse state began. + * An Earley parser parse state. Consists of an inlined LR(0) item (see {@link LRItem}) and an + * index within the sentence being parsed where the parse state began. * - * Each parse state also has a parse tree associated with it, in the form of a Set> object. - * Each element in the set represents a single possible parse at this state, with one element in the PStack for each - * non-terminal left of the "dot" on the right-hand-side of the production. + *

Each parse state also has a parse tree associated with it, in the form of a + * Set> object. Each element in the set represents a single possible parse at this + * state, with one element in the PStack for each non-terminal left of the "dot" on the + * right-hand-side of the production. * - * Additionally, we compute the value `ntItem` which corresponds to the number of non-terminals left of the "dot" in - * the LR(0) item this state corresponds to. + *

Additionally, we compute the value `ntItem` which corresponds to the number of non-terminals + * left of the "dot" in the LR(0) item this state corresponds to. */ private static final class EarleyState { public EarleyState(EarleyProduction prod, int item, int start) { @@ -274,9 +290,9 @@ public Set> parseTree() { } /** - * A wrapper around a {@link Set} representing the parse trees that can be parsed for a particular non-terminal - * over a particular range of the input. This information is deduplicated across multiple parse states in order to - * preserve sharing in the resulting parse forest. + * A wrapper around a {@link Set} representing the parse trees that can be parsed for a + * particular non-terminal over a particular range of the input. This information is deduplicated + * across multiple parse states in order to preserve sharing in the resulting parse forest. */ private static final class CompleteParseTreeNode { Set derivations; @@ -287,8 +303,8 @@ public CompleteParseTreeNode() { } /** - * Compute the CompleteParseTreeNode for a non-terminal and an input range from a parse tree that is - * part of an {@link EarleyState} which has parsed an entire production. + * Compute the CompleteParseTreeNode for a non-terminal and an input range from a parse tree that + * is part of an {@link EarleyState} which has parsed an entire production. * * @param parses The set of parses to add new derivations to. * @param parseTree The parse tree of the {@link EarleyState} being processed. @@ -297,7 +313,13 @@ public CompleteParseTreeNode() { * @param end The end-index of the input range that was parsed. * @param data The {@link ParserMetadata} about the current sentence being parsed. */ - private static void wrapState(Set parses, Set> parseTree, EarleyProduction eprod, int start, int end, ParserMetadata data) { + private static void wrapState( + Set parses, + Set> parseTree, + EarleyProduction eprod, + int start, + int end, + ParserMetadata data) { byte[] utf8Input = StringUtils.getBytesUtf8(data.input); for (PStack children : parseTree) { @@ -308,7 +330,7 @@ private static void wrapState(Set parses, Set> parseTree, Ear int startLoc = data.words[start].startLoc; int endLoc; if (start != end) { - endLoc = data.words[end-1].endLoc; + endLoc = data.words[end - 1].endLoc; } else { endLoc = data.words[end].endLoc; } @@ -323,7 +345,8 @@ private static void wrapState(Set parses, Set> parseTree, Ear // Note that startLoc and endLoc refer to indices into the UTF-8 encoded byte array String value = StringUtils.newStringUtf8(Arrays.copyOfRange(utf8Input, startLoc, endLoc)); if (eprod.isMInt()) { - // it's an MInt token, so make sure to add the correct bit-length to the production before creating the + // it's an MInt token, so make sure to add the correct bit-length to the production before + // creating the // Constant int index = value.indexOf('p'); if (index == -1) { @@ -348,23 +371,26 @@ private static void wrapState(Set parses, Set> parseTree, Ear /** * A set of {@link EarleyState EarleyStates}. * - * Each set corresponds to a particular end-index within the sentence being parsed, and contains all states that end - * at that index. Being a set, duplicate states are combined. For the purposes of deduplication, we only consider the - * production, start index, and "dot" of a state. We use a list of mappings from states to themselves in order to - * deduplicate states. When a state is re-added, the parse tree is merged with the one already in the set. + *

Each set corresponds to a particular end-index within the sentence being parsed, and + * contains all states that end at that index. Being a set, duplicate states are combined. For the + * purposes of deduplication, we only consider the production, start index, and "dot" of a state. + * We use a list of mappings from states to themselves in order to deduplicate states. When a + * state is re-added, the parse tree is merged with the one already in the set. * - * Each set also stores a 2D array of {@link CompleteParseTreeNode CompleteParseTreeNodes} which are used to - * deduplicate the parse trees from parse states that have finished processing a production. This implements the - * invariant that a tuple (S, i, j) for a particular sort, start-index, and end-index must exist only once in the - * parse forest. + *

Each set also stores a 2D array of {@link CompleteParseTreeNode CompleteParseTreeNodes} + * which are used to deduplicate the parse trees from parse states that have finished processing a + * production. This implements the invariant that a tuple (S, i, j) for a particular sort, + * start-index, and end-index must exist only once in the parse forest. * - * Finally, each set also stores a mapping from sorts to lists of states that is used by the complete function of - * the parser in order to identify which states need to be advanced past a particular non-terminal when a production - * is completed (i.e., the "dot" reaches the end of the production). + *

Finally, each set also stores a mapping from sorts to lists of states that is used by the + * complete function of the parser in order to identify which states need to be advanced past a + * particular non-terminal when a production is completed (i.e., the "dot" reaches the end of the + * production). * - * EarleySet implements a concurrent iterator for states. i.e., as long as states are only ever added to the end of - * the list, it is possible to iterate over the entire set one element at a time even if elements are added during - * the iteration process. This invariant is preserved by the code. + *

EarleySet implements a concurrent iterator for states. i.e., as long as states are only ever + * added to the end of the list, it is possible to iterate over the entire set one element at a + * time even if elements are added during the iteration process. This invariant is preserved by + * the code. */ private static final class EarleySet implements Iterable { private final List states = new ArrayList<>(); @@ -381,7 +407,7 @@ private static final class EarleySet implements Iterable { public EarleySet(int index, int nsorts) { this.index = index; this.nsorts = nsorts; - membership = new ArrayList<>(index+1); + membership = new ArrayList<>(index + 1); for (int i = 0; i <= index; i++) { membership.add(new HashMap<>()); } @@ -397,6 +423,7 @@ public Iterator iterator() { private class EarleySetIterator implements Iterator { int idx = 0; + public boolean hasNext() { return idx < states.size(); } @@ -408,6 +435,7 @@ public EarleyState next() { /** * Obtain the parse tree associated with a tuple (S, i, j) of sort, start-index, and end-index. + * * @param sort The sort that was just parsed. * @param start The start-index that the production was parsed from. * @return The unique parse tree node associated with that sort, start-index, and end-index. @@ -415,7 +443,7 @@ public EarleyState next() { private Set completedParses(int sort, int start) { CompleteParseTreeNode[][] nodes = completedParseTrees; if (nodes == null) { - nodes = new CompleteParseTreeNode[index+1][nsorts]; + nodes = new CompleteParseTreeNode[index + 1][nsorts]; completedParseTrees = nodes; } CompleteParseTreeNode node = nodes[start][sort]; @@ -464,8 +492,9 @@ public void add(EarleyState state, ParserMetadata data) { states.add(state); // compute metadata about new state membership.get(state.start).put(state, state); - if (state.item != state.prod.items.size() && state.prod.items.get(state.item).isNonTerminal()) { - EarleyNonTerminal nt = (EarleyNonTerminal)state.prod.items.get(state.item); + if (state.item != state.prod.items.size() + && state.prod.items.get(state.item).isNonTerminal()) { + EarleyNonTerminal nt = (EarleyNonTerminal) state.prod.items.get(state.item); completor.get(nt.sort).add(state); } else if (state.item == state.prod.items.size()) { // if the state is complete, add the proper derivations to the CompleteParseTreeNode @@ -489,9 +518,7 @@ public List completor(int sort) { return completor.get(sort); } - /** - * Finalize the set by cleaning up, after all states have been added to it. - */ + /** Finalize the set by cleaning up, after all states have been added to it. */ public void finish() { membership = null; completedParseTrees = null; @@ -499,8 +526,8 @@ public void finish() { } /** - * Metadata about the state of the sentence being parsed. We collect this all in a single place in order to simplify - * the type signatures of many methods. + * Metadata about the state of the sentence being parsed. We collect this all in a single place in + * order to simplify the type signatures of many methods. */ public static class ParserMetadata { /** @@ -510,16 +537,18 @@ public static class ParserMetadata { * @param startLine The line the sentence started on. * @param startColumn The column the sentence started on. */ - public ParserMetadata(String input, Scanner scanner, Source source, int startLine, int startColumn) { + public ParserMetadata( + String input, Scanner scanner, Source source, int startLine, int startColumn) { // compute location info byte[] utf8 = StringUtils.getBytesUtf8(input); - int[] lines = new int[utf8.length+1]; - int[] columns = new int[utf8.length+1]; + int[] lines = new int[utf8.length + 1]; + int[] columns = new int[utf8.length + 1]; int l = startLine; int c = startColumn; int length = input.codePointCount(0, input.length()); - for (int offset = 0, utf8offset = 0, codepoint, numBytes; offset < length; offset += Character.charCount(codepoint), - utf8offset += numBytes) { + for (int offset = 0, utf8offset = 0, codepoint, numBytes; + offset < length; + offset += Character.charCount(codepoint), utf8offset += numBytes) { codepoint = input.codePointAt(offset); numBytes = StringUtils.getBytesUtf8(new String(Character.toChars(codepoint))).length; for (int i = 0; i < numBytes; i++) { @@ -528,30 +557,32 @@ public ParserMetadata(String input, Scanner scanner, Source source, int startLin } switch (input.codePointAt(offset)) { - case '\r' : - if (offset+1 < input.length()) { - if (input.charAt(offset+1) == '\n') { - lines[offset+1] = l; - columns[offset+1] = c + 1; + case '\r': + if (offset + 1 < input.length()) { + if (input.charAt(offset + 1) == '\n') { + lines[offset + 1] = l; + columns[offset + 1] = c + 1; offset++; utf8offset++; } } - case '\n' : - case '\u000B' : - case '\u000C' : - case '\u0085' : - case '\u2028' : - case '\u2029' : - l++; c = 1; break; - default : + case '\n': + case '\u000B': + case '\u000C': + case '\u0085': + case '\u2028': + case '\u2029': + l++; + c = 1; + break; + default: c++; } } lines[utf8.length] = l; columns[utf8.length] = c; - //initialize + // initialize this.words = scanner.tokenize(input, source, lines, columns); this.lines = lines; this.columns = columns; @@ -597,7 +628,7 @@ public EarleyParser(Module m, Scanner scanner, Sort startSymbol, boolean partial productions = getProductions(m, scanner, startSymbol); predictor = getPredictor(); - //compute nullability + // compute nullability Set nullable = new HashSet<>(); List> callers = getCallers(); this.nullable = new BitSet(sorts.size()); @@ -605,7 +636,7 @@ public EarleyParser(Module m, Scanner scanner, Sort startSymbol, boolean partial markNullable(new LRItem(prod, 0), nullable, callers); } - //compute first set + // compute first set first = computeFirstSet(); } @@ -618,7 +649,8 @@ private Map getSorts(Module m) { sorts.put(nullSort, 1); sortList.add(nullSort); int i = 2; - // some weirdness that turns out to be necessary due to how we implement MInt in RuleGrammarGenerator + // some weirdness that turns out to be necessary due to how we implement MInt in + // RuleGrammarGenerator for (SortHead sortHead : iterable(m.definedInstantiations().keySet())) { Sort nonsenseSort = Sort(sortHead.name(), Seq(Sorts.K())); if (!m.allSorts().contains(nonsenseSort)) { @@ -636,7 +668,15 @@ private Map getSorts(Module m) { private List getProductions(Module m, Scanner scanner, Sort startSymbol) { final List productions; productions = new ArrayList<>(m.productions().size()); - productions.add(new EarleyProduction(0, null, 0, Arrays.asList(new EarleyNonTerminal(sorts.get(startSymbol), sortList), new EarleyTerminal(scanner, 0)), sortList)); + productions.add( + new EarleyProduction( + 0, + null, + 0, + Arrays.asList( + new EarleyNonTerminal(sorts.get(startSymbol), sortList), + new EarleyTerminal(scanner, 0)), + sortList)); int index = 1; for (Production prod : iterable(m.productions())) { if (prod.params().size() != 0) { @@ -656,22 +696,22 @@ private List getProductions(Module m, Scanner scanner, Sort st if (original.isPresent()) { prodToUse = original.get(); } - productions.add(new EarleyProduction(index++, prodToUse, sorts.get(prod.sort()), items, sortList)); - + productions.add( + new EarleyProduction(index++, prodToUse, sorts.get(prod.sort()), items, sortList)); } return productions; } private EarleyProductionItem toEarley(ProductionItem item, Scanner scanner) { if (item instanceof NonTerminal) { - Integer sort = sorts.get(((NonTerminal)item).sort()); + Integer sort = sorts.get(((NonTerminal) item).sort()); // sort may be null because private module imports may cause sort declarations to be missing if (sort == null) { sort = 1; // , a sort with no productions } return new EarleyNonTerminal(sort, sortList); } else { - return new EarleyTerminal(scanner, scanner.resolve((TerminalLike)item)); + return new EarleyTerminal(scanner, scanner.resolve((TerminalLike) item)); } } @@ -709,13 +749,13 @@ private void markNullable(LRItem state, Set nullable, List> nullable.add(state); if (state.item < state.prod.items.size()) { if (state.prod.items.get(state.item).isNullable(this.nullable)) { - markNullable(new LRItem(state.prod, state.item+1), nullable, callers); + markNullable(new LRItem(state.prod, state.item + 1), nullable, callers); } } else { this.nullable.set(state.prod.sort); for (LRItem s : callers.get(state.prod.sort)) { if (nullable.contains(s)) { - markNullable(new LRItem(s.prod, s.item+1), nullable, callers); + markNullable(new LRItem(s.prod, s.item + 1), nullable, callers); } } } @@ -733,13 +773,13 @@ private BitSet[] computeFirstSet() { do { dirty = false; for (int sort = 0; sort < sorts.size(); sort++) { -production: + production: for (EarleyProduction prod : predictor.get(sort)) { if (!prod.items.isEmpty()) { for (int i = 0; i < prod.items.size(); i++) { EarleyProductionItem item = prod.items.get(i); if (item.isNonTerminal()) { - EarleyNonTerminal nt = (EarleyNonTerminal)item; + EarleyNonTerminal nt = (EarleyNonTerminal) item; BitSet old = new BitSet(arraySize); BitSet curr = first[sort]; old.or(curr); @@ -749,7 +789,7 @@ private BitSet[] computeFirstSet() { continue production; } } else if (once) { - EarleyTerminal t = (EarleyTerminal)item; + EarleyTerminal t = (EarleyTerminal) item; dirty = dirty || !first[sort].get(t.kind); first[sort].set(t.kind); continue production; @@ -782,6 +822,7 @@ private BitSet[] computeFirstSet() { /** * Parse a sentence according to the grammar represented by this parser. + * * @param input The sentence to parse. * @param source The {@link Source} representing the file the string comes from. * @param startLine The line the sentence starts on. @@ -809,10 +850,11 @@ public Term parse(String input, Source source, int startLine, int startColumn) { for (int k = 0; k < data.words.length; k++) { // for each position in the tokenized sentence, compute S[k] and Q EarleySet Q = Qprime; - Qprime = new EarleySet(k+1, sorts.size()); + Qprime = new EarleySet(k + 1, sorts.size()); // loop through S[k] and process each state, predicting and completing it for (EarleyState state : S.get(k)) { - if (state.item != state.prod.items.size() && state.prod.items.get(state.item).isNonTerminal()) { + if (state.item != state.prod.items.size() + && state.prod.items.get(state.item).isNonTerminal()) { // state is ready to process a non-terminal, therefore predict predict(S, Q, state, k, data); } else { @@ -828,42 +870,51 @@ public Term parse(String input, Source source, int startLine, int startColumn) { scan(S, Qprime, state, k, data); } - // this loop condition is a bit tricky because we want to stop at the exact loop iteration that parsing failed - // in order to generate the correct error message. Nominally speaking, parsing succeeds if after all loop - // iterations, S[data.words.length] contains the state (productions.get(0), 2, 0). However, if S[k+1] is empty and - // Q is empty at this point, then Qprime must also be empty. Thus, if k+1==data.words.length, S[data.words.length] - // is empty, which means it's a parse error. And if k+1 list.get(0)).collect(Collectors.toSet())); + return Ambiguity.apply( + S.get(data.words.length).states.get(0).parseTree().stream() + .map(list -> list.get(0)) + .collect(Collectors.toSet())); } // We are only interested in displaying states that span the entire input // when a parse error occurs; such states have a start-index of 0. private Set spanningStates(EarleySet parses) { - return parses.states.stream() - .filter(state -> state.start == 0) - .collect(Collectors.toSet()); + return parses.states.stream().filter(state -> state.start == 0).collect(Collectors.toSet()); } // We heuristically identify the best state-set for producing diagnostics as the // most recent such set that includes a _spanning state_; i.e. one with a start // index of zero. private Set bestDiagnosticStates(List S, int k) { - for(int i = k; i >= 0; --i) { + for (int i = k; i >= 0; --i) { Set candidate = spanningStates(S.get(i)); - if(!candidate.isEmpty()) { + if (!candidate.isEmpty()) { return candidate; } } @@ -872,26 +923,26 @@ private Set bestDiagnosticStates(List S, int k) { } private String partialParseTreesDiagnostic(Set spanningStates) { - if(spanningStates.isEmpty()) { + if (spanningStates.isEmpty()) { return "No top-level production could apply to this input."; } StringBuilder msg = new StringBuilder(); - for(EarleyState state : spanningStates) { + for (EarleyState state : spanningStates) { msg.append(" Attempting to apply production:\n ").append(state.prod).append("\n"); - for(PStack possibleTree : state.parseTree()) { - var cleanedChildren = possibleTree - .stream() + for (PStack possibleTree : state.parseTree()) { + var cleanedChildren = + possibleTree.stream() .map(term -> new TreeCleanerVisitor().apply(term)) .collect(Collectors.toList()); - if(state.prod.prod.klabel().isDefined()) { + if (state.prod.prod.klabel().isDefined()) { var term = TermCons.apply(ConsPStack.from(cleanedChildren), state.prod.prod); msg.append(" produced partial term:\n ").append(term).append("\n"); } else { msg.append(" produced partial term with no KLabel, and children:\n"); - for(var child : cleanedChildren) { + for (var child : cleanedChildren) { msg.append(" ").append(child).append("\n"); } } @@ -904,7 +955,8 @@ private String partialParseTreesDiagnostic(Set spanningStates) { /** * @param data The {@link ParserMetadata} about the sentence being parsed * @param S The set of {@link EarleyState}s for each end-index in the input - * @param k The end-index at which a parse error occurred. In other words, the index just prior to the first token that + * @param k The end-index at which a parse error occurred. In other words, the index just prior to + * the first token that */ private void parseError(ParserMetadata data, List S, int k) { int startLine, startColumn, endLine, endColumn; @@ -920,18 +972,18 @@ private void parseError(ParserMetadata data, List S, int k) { endLine = data.lines[t.endLoc]; endColumn = data.columns[t.endLoc]; } - String msg = data.words.length-1 == k ? - "Parse error: unexpected end of file" : - "Parse error: unexpected token '" + data.words[k].value + "'"; + String msg = + data.words.length - 1 == k + ? "Parse error: unexpected end of file" + : "Parse error: unexpected token '" + data.words[k].value + "'"; if (k != 0) { msg = msg + " following token '" + data.words[k - 1].value + "'."; } else { msg = msg + "."; } - Location loc = new Location(startLine, startColumn, - endLine, endColumn); + Location loc = new Location(startLine, startColumn, endLine, endColumn); - if(partialParseDebug) { + if (partialParseDebug) { msg += " Additional parsing diagnostic information:\n"; msg += partialParseTreesDiagnostic(bestDiagnosticStates(S, k)); } @@ -940,6 +992,7 @@ private void parseError(ParserMetadata data, List S, int k) { } private static final Set> EMPTY_PARSE_TREE = new HashSet<>(); + static { EMPTY_PARSE_TREE.add(ConsPStack.empty()); } @@ -953,8 +1006,9 @@ private void parseError(ParserMetadata data, List S, int k) { * @param k the current end-index being parsed * @param data The {@link ParserMetadata} about the sentence being parsed */ - private void predict(List S, EarleySet Q, EarleyState state, int k, ParserMetadata data) { - EarleyNonTerminal nt = (EarleyNonTerminal)state.prod.items.get(state.item); + private void predict( + List S, EarleySet Q, EarleyState state, int k, ParserMetadata data) { + EarleyNonTerminal nt = (EarleyNonTerminal) state.prod.items.get(state.item); // first, use lookahead to check if we need to predict this sort at all if (nullable.get(nt.sort) || first[nt.sort].get(data.words[k].kind)) { List prods = predictor.get(nt.sort); @@ -962,7 +1016,7 @@ private void predict(List S, EarleySet Q, EarleyState state, int k, P // for each production for the sort being predicted, add it to the appropriate set if (next.items.size() != 0 && !next.items.get(0).isNonTerminal()) { // if it's a terminal, add it to be scanned only if the next token matches - EarleyTerminal t = (EarleyTerminal)next.items.get(0); + EarleyTerminal t = (EarleyTerminal) next.items.get(0); if (t.kind == data.words[k].kind) { // if it matches, add (next, 0, k) to Q EarleyState nextState = new EarleyState(next, 0, k); @@ -978,7 +1032,7 @@ private void predict(List S, EarleySet Q, EarleyState state, int k, P if (nullable.get(nt.sort)) { // non-terminal is nullable, so complete the state by advancing past the nullable sort. // see Aycock and Horspool - EarleyState nextState = new EarleyState(state.prod, state.item+1, state.start); + EarleyState nextState = new EarleyState(state.prod, state.item + 1, state.start); nextState.parseTree = new HashSet<>(); wrapAndAppend(nt.sort, k, nextState, state, S.get(k)); addStateToSet(S, Q, state, nextState, k, data); @@ -994,16 +1048,18 @@ private void predict(List S, EarleySet Q, EarleyState state, int k, P * @param k the current end-index being parsed * @param data The {@link ParserMetadata} about the sentence being parsed */ - private void scan(List S, EarleySet Qprime, EarleyState state, int k, ParserMetadata data) { + private void scan( + List S, EarleySet Qprime, EarleyState state, int k, ParserMetadata data) { // construct next state - EarleyState nextState = new EarleyState(state.prod, state.item+1, state.start); + EarleyState nextState = new EarleyState(state.prod, state.item + 1, state.start); nextState.parseTree = state.parseTree; - addStateToSet(S, Qprime, state, nextState, k+1, data); + addStateToSet(S, Qprime, state, nextState, k + 1, data); } /** - * Add a state to either S or Q depending on whether the next production item to parse is a terminal or a - * non-terminal. May be added to neither if the next state expects a terminal not found next in the input. + * Add a state to either S or Q depending on whether the next production item to parse is a + * terminal or a non-terminal. May be added to neither if the next state expects a terminal not + * found next in the input. * * @param S The {@link EarleySet EarleySets} for completion and prediction. * @param Q The {@link EarleySet} for scanning @@ -1012,10 +1068,17 @@ private void scan(List S, EarleySet Qprime, EarleyState state, int k, * @param k the current end-index being parsed. * @param data The {@link ParserMetadata} about the sentence being parsed. */ - private void addStateToSet(List S, EarleySet Q, EarleyState state, EarleyState nextState, int k, ParserMetadata data) { + private void addStateToSet( + List S, + EarleySet Q, + EarleyState state, + EarleyState nextState, + int k, + ParserMetadata data) { // if the next item in the state is a terminal, scan it and possibly add it to Q' - if (state.item+1 != state.prod.items.size() && !state.prod.items.get(state.item+1).isNonTerminal()) { - EarleyTerminal t = (EarleyTerminal) state.prod.items.get(state.item+1); + if (state.item + 1 != state.prod.items.size() + && !state.prod.items.get(state.item + 1).isNonTerminal()) { + EarleyTerminal t = (EarleyTerminal) state.prod.items.get(state.item + 1); if (t.kind == data.words[k].kind) { Q.add(nextState, data); } @@ -1034,10 +1097,13 @@ private void addStateToSet(List S, EarleySet Q, EarleyState state, Ea * @param k the current end-index being parsed * @param data The {@link ParserMetadata} about the sentence being parsed */ - private void complete(List S, EarleySet Q, EarleyState state, int k, ParserMetadata data) { - // this state is nullable, therefore we need to compute its parse tree since it was not given one yet + private void complete( + List S, EarleySet Q, EarleyState state, int k, ParserMetadata data) { + // this state is nullable, therefore we need to compute its parse tree since it was not given + // one yet List completedStates = S.get(state.start).completor(state.prod.sort); - // for each state in S[state.start] that is waiting on the non-terminal corresponding to state.prod.sort + // for each state in S[state.start] that is waiting on the non-terminal corresponding to + // state.prod.sort for (EarleyState completed : completedStates) { // construct next state EarleyState nextState = new EarleyState(completed.prod, completed.item + 1, completed.start); @@ -1049,16 +1115,18 @@ private void complete(List S, EarleySet Q, EarleyState state, int k, } /** - * Construct the new parse tree for a state during the "complete" step, or after a nullable non-terminal in the - * "predict" step. + * Construct the new parse tree for a state during the "complete" step, or after a nullable + * non-terminal in the "predict" step. * * @param sort The sort that was just parsed. * @param start The start-index of the state being processed. * @param nextState The {@link EarleyState} to add the parse tree derivations to. - * @param completed The {@link EarleyState} containing the parse tree prior to processing this non-terminal. + * @param completed The {@link EarleyState} containing the parse tree prior to processing this + * non-terminal. * @param end The {@link EarleySet} representing the end-index being processed by the parser. */ - private void wrapAndAppend(int sort, int start, EarleyState nextState, EarleyState completed, EarleySet end) { + private void wrapAndAppend( + int sort, int start, EarleyState nextState, EarleyState completed, EarleySet end) { Term newTerm = Ambiguity.apply(end.completedParses(sort, start)); for (PStack terms : completed.parseTree()) { nextState.parseTree.add(terms.plus(newTerm)); diff --git a/kernel/src/main/java/org/kframework/parser/inner/kernel/KSyntax2Bison.java b/kernel/src/main/java/org/kframework/parser/inner/kernel/KSyntax2Bison.java index 83ce516a63e..ca34c3e4284 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/kernel/KSyntax2Bison.java +++ b/kernel/src/main/java/org/kframework/parser/inner/kernel/KSyntax2Bison.java @@ -1,10 +1,26 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner.kernel; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.mutable.MutableInt; import org.kframework.Collections; -import org.kframework.TopologicalSort; import org.kframework.attributes.Att; import org.kframework.backend.kore.ModuleToKORE; import org.kframework.definition.Module; @@ -18,39 +34,29 @@ import org.kframework.definition.TerminalLike; import org.kframework.kore.KLabel; import org.kframework.kore.Sort; -import org.kframework.parser.inner.RuleGrammarGenerator; import org.kframework.utils.StringUtil; import org.kframework.utils.errorsystem.KEMException; import org.kframework.utils.errorsystem.KException.ExceptionType; import org.kframework.utils.errorsystem.KExceptionManager; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Deque; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - import scala.Tuple2; -import static org.kframework.Collections.*; -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; - public class KSyntax2Bison { - private static void computeSide(int idx, Production prod, List items, Module module, scala.collection.Set> assoc, Map, Integer> ordinals, Set>> nts, MutableInt nextOrdinal) { + private static void computeSide( + int idx, + Production prod, + List items, + Module module, + scala.collection.Set> assoc, + Map, Integer> ordinals, + Set>> nts, + MutableInt nextOrdinal) { NonTerminal nt = (NonTerminal) items.get(idx); Tag parent = new Tag(prod.klabel().get().name()); Set prods = new HashSet<>(); - for (Tag child : iterable(module.priorities().relations().get(parent).getOrElse(() -> Collections.Set()))) { + for (Tag child : + iterable( + module.priorities().relations().get(parent).getOrElse(() -> Collections.Set()))) { prods.add(child); } for (Tuple2 entry : iterable(assoc)) { @@ -69,7 +75,8 @@ private static void computeSide(int idx, Production prod, List i ordinals.put(prods, nextOrdinal.intValue()); nextOrdinal.increment(); } - items.set(idx, NonTerminal(Sort(nt.sort().name() + "#" + ordinal, nt.sort().params()), nt.name())); + items.set( + idx, NonTerminal(Sort(nt.sort().name() + "#" + ordinal, nt.sort().params()), nt.name())); nts.add(Tuple2.apply(nt.sort(), prods)); } @@ -86,9 +93,23 @@ public static Module transformByPriorityAndAssociativity(Module module) { computeSide(0, prod, items, module, module.rightAssoc(), ordinals, nts, nextOrdinal); } if (items.size() > 1 && items.get(items.size() - 1) instanceof NonTerminal) { - computeSide(items.size()-1, prod, items, module, module.leftAssoc(), ordinals, nts, nextOrdinal); + computeSide( + items.size() - 1, + prod, + items, + module, + module.leftAssoc(), + ordinals, + nts, + nextOrdinal); } - sentences.add(Production(prod.klabel(), prod.params(), prod.sort(), immutable(items), prod.att().add(Att.ORIGINAL_PRD(), Production.class, prod))); + sentences.add( + Production( + prod.klabel(), + prod.params(), + prod.sort(), + immutable(items), + prod.att().add(Att.ORIGINAL_PRD(), Production.class, prod))); } else { sentences.add(prod.withAtt(prod.att().add(Att.ORIGINAL_PRD(), Production.class, prod))); } @@ -101,13 +122,25 @@ public static Module transformByPriorityAndAssociativity(Module module) { worklist.addAll(nts); while (!worklist.isEmpty()) { Tuple2> item = worklist.poll(); - for (Production prod : iterable(module.productionsForSort().get(item._1().head()).getOrElse(() -> Collections.Seq()))) { + for (Production prod : + iterable( + module + .productionsForSort() + .get(item._1().head()) + .getOrElse(() -> Collections.Seq()))) { int ordinal = ordinals.get(item._2()); Sort newNT = Sort(item._1().name() + "#" + ordinal, item._1().params()); if (prod.isSubsort()) { worklist.offer(Tuple2.apply(prod.getSubsortSort(), item._2())); - sentences.add(Production(prod.klabel(), prod.params(), newNT, Seq(NonTerminal(prod.getSubsortSort(), prod.nonterminals().apply(0).name())), prod.att())); - } else if (prod.klabel().isEmpty() || !item._2().contains(new Tag(prod.klabel().get().name()))) { + sentences.add( + Production( + prod.klabel(), + prod.params(), + newNT, + Seq(NonTerminal(prod.getSubsortSort(), prod.nonterminals().apply(0).name())), + prod.att())); + } else if (prod.klabel().isEmpty() + || !item._2().contains(new Tag(prod.klabel().get().name()))) { sentences.add(Production(prod.klabel(), prod.params(), newNT, prod.items(), prod.att())); } } @@ -115,55 +148,73 @@ public static Module transformByPriorityAndAssociativity(Module module) { return Module(module.name(), module.imports(), immutable(sentences), module.att()); } - public static void writeParser(Module module, Module disambModule, Scanner scanner, Sort start, File path, boolean glr, long stackDepth, KExceptionManager kem) { + public static void writeParser( + Module module, + Module disambModule, + Scanner scanner, + Sort start, + File path, + boolean glr, + long stackDepth, + KExceptionManager kem) { if (module.att().contains(Att.NOT_LR1_MODULES())) { - kem.registerInnerParserWarning(ExceptionType.NON_LR_GRAMMAR, "Skipping modules " + module.att().get(Att.NOT_LR1_MODULES()) + " tagged as " + Att.NOT_LR1() + " which are not supported by Bison."); + kem.registerInnerParserWarning( + ExceptionType.NON_LR_GRAMMAR, + "Skipping modules " + + module.att().get(Att.NOT_LR1_MODULES()) + + " tagged as " + + Att.NOT_LR1() + + " which are not supported by Bison."); } module = transformByPriorityAndAssociativity(module); StringBuilder bison = new StringBuilder(); - bison.append("%{\n" + - "#include \n" + - "#include \n" + - "#include \"node.h\"\n" + - "#include \"parser.tab.h\"\n" + - "int yylex(YYSTYPE *, YYLTYPE *, void *);\n" + - "void yyerror(YYLTYPE *, void *, const char *);\n" + - "char *enquote(char *);\n" + - "char *injSymbol(char *, char *);\n" + - "YYSTYPE mergeAmb(YYSTYPE x0, YYSTYPE x1);\n" + - "node *result;\n" + - "extern char *filename;\n" + - "# define YYMAXDEPTH " + stackDepth + "\n" + - "# define YYLLOC_DEFAULT(Cur, Rhs, N) \\\n" + - "do \\\n" + - " if (N) \\\n" + - " { \\\n" + - " (Cur).filename = YYRHSLOC(Rhs, 1).filename; \\\n" + - " (Cur).first_line = YYRHSLOC(Rhs, 1).first_line; \\\n" + - " (Cur).first_column = YYRHSLOC(Rhs, 1).first_column; \\\n" + - " (Cur).last_line = YYRHSLOC(Rhs, N).last_line; \\\n" + - " (Cur).last_column = YYRHSLOC(Rhs, N).last_column; \\\n" + - " } \\\n" + - " else \\\n" + - " { \\\n" + - " (Cur).filename = YYRHSLOC(Rhs, 0).filename; \\\n" + - " (Cur).first_line = (Cur).last_line = \\\n" + - " YYRHSLOC(Rhs, 0).last_line; \\\n" + - " (Cur).first_column = (Cur).last_column = \\\n" + - " YYRHSLOC(Rhs, 0).last_column; \\\n" + - " } \\\n" + - "while (0)\n" + - "%}\n\n"); + bison.append( + "%{\n" + + "#include \n" + + "#include \n" + + "#include \"node.h\"\n" + + "#include \"parser.tab.h\"\n" + + "int yylex(YYSTYPE *, YYLTYPE *, void *);\n" + + "void yyerror(YYLTYPE *, void *, const char *);\n" + + "char *enquote(char *);\n" + + "char *injSymbol(char *, char *);\n" + + "YYSTYPE mergeAmb(YYSTYPE x0, YYSTYPE x1);\n" + + "node *result;\n" + + "extern char *filename;\n" + + "# define YYMAXDEPTH " + + stackDepth + + "\n" + + "# define YYLLOC_DEFAULT(Cur, Rhs, N) \\\n" + + "do \\\n" + + " if (N) \\\n" + + " { \\\n" + + " (Cur).filename = YYRHSLOC(Rhs, 1).filename; \\\n" + + " (Cur).first_line = YYRHSLOC(Rhs, 1).first_line; \\\n" + + " (Cur).first_column = YYRHSLOC(Rhs, 1).first_column; \\\n" + + " (Cur).last_line = YYRHSLOC(Rhs, N).last_line; \\\n" + + " (Cur).last_column = YYRHSLOC(Rhs, N).last_column; \\\n" + + " } \\\n" + + " else \\\n" + + " { \\\n" + + " (Cur).filename = YYRHSLOC(Rhs, 0).filename; \\\n" + + " (Cur).first_line = (Cur).last_line = \\\n" + + " YYRHSLOC(Rhs, 0).last_line; \\\n" + + " (Cur).first_column = (Cur).last_column = \\\n" + + " YYRHSLOC(Rhs, 0).last_column; \\\n" + + " } \\\n" + + "while (0)\n" + + "%}\n\n"); bison.append("%define api.value.type {union value_type}\n"); bison.append("%define api.pure\n"); bison.append("%define lr.type ielr\n"); bison.append("%lex-param {void *scanner} \n"); bison.append("%parse-param {void *scanner} \n"); bison.append("%locations\n"); - bison.append("%initial-action {\n" + - " @$.filename = filename;\n" + - " @$.first_line = @$.first_column = @$.last_line = @$.last_column = 1;\n" + - "}\n"); + bison.append( + "%initial-action {\n" + + " @$.filename = filename;\n" + + " @$.first_line = @$.first_column = @$.last_line = @$.last_column = 1;\n" + + "}\n"); if (glr) { bison.append("%glr-parser\n"); } @@ -172,15 +223,17 @@ public static void writeParser(Module module, Module disambModule, Scanner scann TerminalLike tok = scanner.getTokenByKind(kind); String val; if (tok instanceof Terminal) { - val = ((Terminal)tok).value(); + val = ((Terminal) tok).value(); } else { - val = ((RegexTerminal)tok).regex(); + val = ((RegexTerminal) tok).regex(); } - bison.append("%token TOK_" + kind + " " + (kind+1) + " " + StringUtil.enquoteCString(val) + "\n"); + bison.append( + "%token TOK_" + kind + " " + (kind + 1) + " " + StringUtil.enquoteCString(val) + "\n"); } - //compute sorts reachable from start symbol - Map> prods = stream(module.productions()).collect(Collectors.groupingBy(p -> p.sort())); + // compute sorts reachable from start symbol + Map> prods = + stream(module.productions()).collect(Collectors.groupingBy(p -> p.sort())); Set reachableSorts = new HashSet<>(); Deque workList = new ArrayDeque<>(); workList.offer(start); @@ -208,7 +261,8 @@ public static void writeParser(Module module, Module disambModule, Scanner scann encode(start, bison); bison.append(" { result = $1.nterm; } ;\n"); for (Sort sort : reachableSorts) { - List prodsForSort = Optional.ofNullable(prods.get(sort)).orElse(java.util.Collections.emptyList()); + List prodsForSort = + Optional.ofNullable(prods.get(sort)).orElse(java.util.Collections.emptyList()); if (!prodsForSort.isEmpty()) { encode(sort, bison); bison.append(":\n"); @@ -222,10 +276,13 @@ public static void writeParser(Module module, Module disambModule, Scanner scann } } bison.append("\n%%\n"); - bison.append("\n" + - "void yyerror (YYLTYPE *loc, void *scanner, const char *s) {\n" + - " fprintf (stderr, \"%s:%d:%d:%d:%d:%s\\n\", loc->filename, loc->first_line, loc->first_column, loc->last_line, loc->last_column, s);\n" + - "}\n"); + bison.append( + "\n" + + "void yyerror (YYLTYPE *loc, void *scanner, const char *s) {\n" + + " fprintf (stderr, \"%s:%d:%d:%d:%d:%s\\n" + + "\", loc->filename, loc->first_line, loc->first_column, loc->last_line," + + " loc->last_column, s);\n" + + "}\n"); try { FileUtils.write(path, bison); } catch (IOException e) { @@ -237,7 +294,8 @@ public static void writeParser(Module module, Module disambModule, Scanner scann private static void encode(Sort sort, StringBuilder sb) { sb.append("Sort"); - StringUtil.encodeStringToAlphanumeric(sb, sort.name(), StringUtil.asciiReadableEncodingDefault, identChar, "_"); + StringUtil.encodeStringToAlphanumeric( + sb, sort.name(), StringUtil.asciiReadableEncodingDefault, identChar, "_"); sb.append("_"); String conn = ""; for (Sort param : iterable(sort.params())) { @@ -248,12 +306,21 @@ private static void encode(Sort sort, StringBuilder sb) { sb.append("_"); } - private static void appendOverloadCondition(StringBuilder bison, Module module, Production greater, Production lesser, List nts) { + private static void appendOverloadCondition( + StringBuilder bison, + Module module, + Production greater, + Production lesser, + List nts) { bison.append("true"); for (int i = 0; i < nts.size(); i++) { - boolean hasSameSort = lesser.nonterminals().apply(i).sort().equals(greater.nonterminals().apply(i).sort()); + boolean hasSameSort = + lesser.nonterminals().apply(i).sort().equals(greater.nonterminals().apply(i).sort()); if (!hasSameSort) { - bison.append(" && strncmp($").append(nts.get(i)).append(".nterm->symbol, \"inj{\", 4) == 0 && (false"); + bison + .append(" && strncmp($") + .append(nts.get(i)) + .append(".nterm->symbol, \"inj{\", 4) == 0 && (false"); Sort greaterSort = lesser.nonterminals().apply(i).sort(); for (Sort lesserSort : iterable(module.subsorts().elements())) { if (module.subsorts().lessThanEq(lesserSort, greaterSort)) { @@ -267,67 +334,95 @@ private static void appendOverloadCondition(StringBuilder bison, Module module, } } - private static void appendOverloadChecks(StringBuilder bison, Module module, Module disambModule, Production greater, List nts, boolean hasLocation) { + private static void appendOverloadChecks( + StringBuilder bison, + Module module, + Module disambModule, + Production greater, + List nts, + boolean hasLocation) { for (Production lesser : iterable(disambModule.overloads().sortedElements())) { if (disambModule.overloads().lessThan(lesser, greater)) { bison.append(" if ("); appendOverloadCondition(bison, module, greater, lesser, nts); - bison.append(") {\n" + - " n->symbol =\""); + bison.append(") {\n" + " n->symbol =\""); encodeKore(lesser.klabel().get(), bison); - bison.append("\";\n" + - " n->sort = \""); + bison.append("\";\n" + " n->sort = \""); encodeKore(lesser.sort(), bison); - boolean hasLesserLocation = module.sortAttributesFor().get(lesser.sort().head()).getOrElse(() -> Att.empty()).contains(Att.LOCATIONS()); - bison.append("\";\n" + - " n->hasLocation = " + (hasLesserLocation ? "1" : "0") + ";\n"); + boolean hasLesserLocation = + module + .sortAttributesFor() + .get(lesser.sort().head()) + .getOrElse(() -> Att.empty()) + .contains(Att.LOCATIONS()); + bison.append("\";\n" + " n->hasLocation = " + (hasLesserLocation ? "1" : "0") + ";\n"); for (int i = 0; i < nts.size(); i++) { - boolean hasSameSort = lesser.nonterminals().apply(i).sort().equals(greater.nonterminals().apply(i).sort()); + boolean hasSameSort = + lesser.nonterminals().apply(i).sort().equals(greater.nonterminals().apply(i).sort()); if (hasSameSort) { - bison.append( - " n->children[").append(i).append("] = $").append(nts.get(i)).append(".nterm;\n"); + bison + .append(" n->children[") + .append(i) + .append("] = $") + .append(nts.get(i)) + .append(".nterm;\n"); } else { - bison.append( - " {\n" + - " node *origChild = $").append(nts.get(i)).append(".nterm;\n" + - " char *lesserSort = \""); + bison + .append(" {\n" + " node *origChild = $") + .append(nts.get(i)) + .append(".nterm;\n" + " char *lesserSort = \""); encodeKore(lesser.nonterminals().apply(i).sort(), bison); - bison.append("\";\n" + - " if (strcmp(origChild->children[0]->sort, lesserSort) == 0) {\n" + - " n->children[").append(i).append("] = origChild->children[0];\n" + - " } else {\n" + - " node *inj = malloc(sizeof(node) + sizeof(node *));\n" + - " inj->symbol = injSymbol(origChild->children[0]->sort, lesserSort);\n" + - " inj->str = false;\n" + - " inj->location = origChild->location;\n" + - " inj->nchildren = 1;\n" + - " inj->sort = lesserSort;\n" + - " inj->hasLocation = origChild->hasLocation;\n" + - " inj->children[0] = origChild->children[0];\n" + - " n->children[").append(i).append("] = inj;\n" + - " }\n" + - " }\n"); + bison + .append( + "\";\n" + + " if (strcmp(origChild->children[0]->sort, lesserSort) == 0) {\n" + + " n->children[") + .append(i) + .append( + "] = origChild->children[0];\n" + + " } else {\n" + + " node *inj = malloc(sizeof(node) + sizeof(node *));\n" + + " inj->symbol = injSymbol(origChild->children[0]->sort," + + " lesserSort);\n" + + " inj->str = false;\n" + + " inj->location = origChild->location;\n" + + " inj->nchildren = 1;\n" + + " inj->sort = lesserSort;\n" + + " inj->hasLocation = origChild->hasLocation;\n" + + " inj->children[0] = origChild->children[0];\n" + + " n->children[") + .append(i) + .append("] = inj;\n" + " }\n" + " }\n"); } } bison.append( - " node *n2 = malloc(sizeof(node) + sizeof(node *));\n" + - " n2->str = false;\n" + - " n2->location = @$;\n" + - " n2->nchildren = 1;\n" + - " n2->sort = \""); + " node *n2 = malloc(sizeof(node) + sizeof(node *));\n" + + " n2->str = false;\n" + + " n2->location = @$;\n" + + " n2->nchildren = 1;\n" + + " n2->sort = \""); encodeKore(greater.sort(), bison); - bison.append("\";\n" + - " n2->hasLocation = " + (hasLocation ? "1" : "0") + ";\n" + - " n2->symbol = injSymbol(n->sort, n2->sort);\n" + - " n2->children[0] = n;\n" + - " value_type result = {.nterm = n2};\n" + - " $$ = result;\n" + - " } else"); + bison.append( + "\";\n" + + " n2->hasLocation = " + + (hasLocation ? "1" : "0") + + ";\n" + + " n2->symbol = injSymbol(n->sort, n2->sort);\n" + + " n2->children[0] = n;\n" + + " value_type result = {.nterm = n2};\n" + + " $$ = result;\n" + + " } else"); } } } - private static void processProduction(Production prod, Module module, Module disambModule, Scanner scanner, StringBuilder bison, boolean glr) { + private static void processProduction( + Production prod, + Module module, + Module disambModule, + Scanner scanner, + StringBuilder bison, + boolean glr) { int i = 1; List nts = new ArrayList<>(); for (ProductionItem item : iterable(prod.items())) { @@ -336,8 +431,8 @@ private static void processProduction(Production prod, Module module, Module dis bison.append(" "); nts.add(i); } else { - TerminalLike t = (TerminalLike)item; - if (!(t instanceof Terminal && ((Terminal)t).value().equals(""))) { + TerminalLike t = (TerminalLike) item; + if (!(t instanceof Terminal && ((Terminal) t).value().equals(""))) { bison.append("TOK_" + scanner.resolve(t) + " "); } else { i--; @@ -352,13 +447,30 @@ private static void processProduction(Production prod, Module module, Module dis // list prod = prod.att().getOptional(Att.ORIGINAL_PRD(), Production.class).orElse(prod); } - boolean hasLocation = module.sortAttributesFor().get(prod.sort().head()).getOrElse(() -> Att.empty()).contains(Att.LOCATIONS()); + boolean hasLocation = + module + .sortAttributesFor() + .get(prod.sort().head()) + .getOrElse(() -> Att.empty()) + .contains(Att.LOCATIONS()); if (prod.att().contains(Att.TOKEN()) && !prod.isSubsort()) { - bison.append("{\n" + - " node *n = malloc(sizeof(node));\n" + - " n->symbol = "); - boolean isString = module.sortAttributesFor().get(prod.sort().head()).getOrElse(() -> Att.empty()).getOptional(Att.HOOK()).orElse("").equals("STRING.String"); - boolean isBytes = module.sortAttributesFor().get(prod.sort().head()).getOrElse(() -> Att.empty()).getOptional(Att.HOOK()).orElse("").equals("BYTES.Bytes"); + bison.append("{\n" + " node *n = malloc(sizeof(node));\n" + " n->symbol = "); + boolean isString = + module + .sortAttributesFor() + .get(prod.sort().head()) + .getOrElse(() -> Att.empty()) + .getOptional(Att.HOOK()) + .orElse("") + .equals("STRING.String"); + boolean isBytes = + module + .sortAttributesFor() + .get(prod.sort().head()) + .getOrElse(() -> Att.empty()) + .getOptional(Att.HOOK()) + .orElse("") + .equals("BYTES.Bytes"); if (!isString && !isBytes) { bison.append("enquote("); } @@ -369,130 +481,136 @@ private static void processProduction(Production prod, Module module, Module dis if (!isString && !isBytes) { bison.append(")"); } - bison.append(";\n" + - " n->str = true;\n" + - " n->location = @$;\n" + - " n->hasLocation = 0;\n" + - " n->nchildren = 0;\n" + - " node *n2 = malloc(sizeof(node) + sizeof(node *));\n" + - " n2->symbol = \"\\\\dv{"); + bison.append( + ";\n" + + " n->str = true;\n" + + " n->location = @$;\n" + + " n->hasLocation = 0;\n" + + " n->nchildren = 0;\n" + + " node *n2 = malloc(sizeof(node) + sizeof(node *));\n" + + " n2->symbol = \"\\\\dv{"); encodeKore(prod.sort(), bison); - bison.append("}\";\n" + - " n2->sort = \""); + bison.append("}\";\n" + " n2->sort = \""); encodeKore(prod.sort(), bison); - bison.append("\";\n" + - " n2->str = false;\n" + - " n2->location = @$;\n" + - " n2->hasLocation = " + (hasLocation ? "1" : "0") + ";\n" + - " n2->nchildren = 1;\n" + - " n2->children[0] = n;\n" + - " value_type result = {.nterm = n2};\n" + - " $$ = result;\n" + - "}\n"); - } else if (!prod.att().contains(Att.TOKEN()) && prod.isSubsort() && !prod.att().contains(Att.NOT_INJECTION())) { - bison.append("{\n" + - " node *n = malloc(sizeof(node) + sizeof(node *));\n" + - " n->str = false;\n" + - " n->location = @$;\n" + - " n->hasLocation = " + (hasLocation ? "1" : "0") + ";\n" + - " n->nchildren = 1;\n" + - " n->sort = \""); + bison.append( + "\";\n" + + " n2->str = false;\n" + + " n2->location = @$;\n" + + " n2->hasLocation = " + + (hasLocation ? "1" : "0") + + ";\n" + + " n2->nchildren = 1;\n" + + " n2->children[0] = n;\n" + + " value_type result = {.nterm = n2};\n" + + " $$ = result;\n" + + "}\n"); + } else if (!prod.att().contains(Att.TOKEN()) + && prod.isSubsort() + && !prod.att().contains(Att.NOT_INJECTION())) { + bison.append( + "{\n" + + " node *n = malloc(sizeof(node) + sizeof(node *));\n" + + " n->str = false;\n" + + " n->location = @$;\n" + + " n->hasLocation = " + + (hasLocation ? "1" : "0") + + ";\n" + + " n->nchildren = 1;\n" + + " n->sort = \""); encodeKore(prod.sort(), bison); - bison.append("\";\n" + - " if (!$1.nterm->str && strncmp($1.nterm->symbol, \"inj{\", 4) == 0) {\n" + - " char *childSort = $1.nterm->children[0]->sort;\n" + - " n->symbol = injSymbol(childSort, n->sort);\n" + - " n->children[0] = $1.nterm->children[0];\n" + - " } else {\n" + - " n->symbol = \"inj{"); + bison.append( + "\";\n" + + " if (!$1.nterm->str && strncmp($1.nterm->symbol, \"inj{\", 4) == 0) {\n" + + " char *childSort = $1.nterm->children[0]->sort;\n" + + " n->symbol = injSymbol(childSort, n->sort);\n" + + " n->children[0] = $1.nterm->children[0];\n" + + " } else {\n" + + " n->symbol = \"inj{"); encodeKore(prod.getSubsortSort(), bison); bison.append(", "); encodeKore(prod.sort(), bison); - bison.append("}\";\n" + - " n->children[0] = $1.nterm;\n" + - " }\n"); + bison.append("}\";\n" + " n->children[0] = $1.nterm;\n" + " }\n"); if (prod.att().contains(Att.USER_LIST_TERMINATOR())) { KLabel nil = KLabel(prod.att().get(Att.USER_LIST_TERMINATOR())); KLabel cons = KLabel(prod.att().get(Att.USER_LIST())); - bison.append( - " node *n2 = malloc(sizeof(node));\n" + - " n2->symbol = \""); + bison.append(" node *n2 = malloc(sizeof(node));\n" + " n2->symbol = \""); encodeKore(nil, bison); - bison.append("\";\n" + - " n2->str = false;\n" + - " n2->location = @$;\n" + - " n2->hasLocation = 0;\n" + - " n2->nchildren = 0;\n" + - " n2->sort = \""); + bison.append( + "\";\n" + + " n2->str = false;\n" + + " n2->location = @$;\n" + + " n2->hasLocation = 0;\n" + + " n2->nchildren = 0;\n" + + " n2->sort = \""); encodeKore(prod.sort(), bison); - bison.append("\";\n" + - " node *n3 = malloc(sizeof(node) + 2*sizeof(node *));\n" + - " n3->symbol = \""); + bison.append( + "\";\n" + + " node *n3 = malloc(sizeof(node) + 2*sizeof(node *));\n" + + " n3->symbol = \""); encodeKore(cons, bison); - bison.append("\";\n" + - " n3->str = false;\n" + - " n3->location = @$;\n" + - " n3->hasLocation = " + (hasLocation ? "1" : "0") + ";\n" + - " n3->nchildren = 2;\n" + - " n3->children[0] = n2;\n" + - " n3->children[1] = $1.nterm;\n" + - " n3->sort = \""); + bison.append( + "\";\n" + + " n3->str = false;\n" + + " n3->location = @$;\n" + + " n3->hasLocation = " + + (hasLocation ? "1" : "0") + + ";\n" + + " n3->nchildren = 2;\n" + + " n3->children[0] = n2;\n" + + " n3->children[1] = $1.nterm;\n" + + " n3->sort = \""); encodeKore(prod.sort(), bison); - bison.append("\";\n" + - " value_type result = {.nterm = n3};\n" + - " $$ = result;\n" + - "}\n"); + bison.append( + "\";\n" + " value_type result = {.nterm = n3};\n" + " $$ = result;\n" + "}\n"); } else { - bison.append(" value_type result = {.nterm = n};\n" + - " $$ = result;\n" + - "}\n"); + bison.append(" value_type result = {.nterm = n};\n" + " $$ = result;\n" + "}\n"); } } else if (prod.att().contains(Att.TOKEN()) && prod.isSubsort()) { - bison.append("{\n" + - " node *n = malloc(sizeof(node) + sizeof(node *));\n" + - " n->symbol = \"\\\\dv{"); + bison.append( + "{\n" + + " node *n = malloc(sizeof(node) + sizeof(node *));\n" + + " n->symbol = \"\\\\dv{"); encodeKore(prod.sort(), bison); - bison.append("}\";\n" + - " n->sort = \""); + bison.append("}\";\n" + " n->sort = \""); encodeKore(prod.sort(), bison); - bison.append("\";\n" + - " n->str = false;\n" + - " n->location = @$;\n" + - " n->hasLocation = " + (hasLocation ? "1" : "0") + ";\n" + - " n->nchildren = 1;\n" + - " n->children[0] = $1.nterm->children[0];\n" + - " value_type result = {.nterm = n};\n" + - " $$ = result;\n" + - "}\n"); + bison.append( + "\";\n" + + " n->str = false;\n" + + " n->location = @$;\n" + + " n->hasLocation = " + + (hasLocation ? "1" : "0") + + ";\n" + + " n->nchildren = 1;\n" + + " n->children[0] = $1.nterm->children[0];\n" + + " value_type result = {.nterm = n};\n" + + " $$ = result;\n" + + "}\n"); } else if (prod.klabel().isDefined()) { - bison.append("{\n" + - " node *n = malloc(sizeof(node) + sizeof(node *)*").append(nts.size()).append(");\n" + - " n->str = false;\n" + - " n->location = @$;\n" + - " n->nchildren = ").append(nts.size()).append(";\n"); + bison + .append("{\n" + " node *n = malloc(sizeof(node) + sizeof(node *)*") + .append(nts.size()) + .append(");\n" + " n->str = false;\n" + " n->location = @$;\n" + " n->nchildren = ") + .append(nts.size()) + .append(";\n"); appendOverloadChecks(bison, module, disambModule, prod, nts, hasLocation); - bison.append("{\n" + - " n->symbol = \""); + bison.append("{\n" + " n->symbol = \""); encodeKore(prod.klabel().get(), bison); - bison.append("\";\n" + - " n->sort = \""); + bison.append("\";\n" + " n->sort = \""); encodeKore(prod.sort(), bison); - bison.append("\";\n" + - " n->hasLocation = " + (hasLocation ? "1" : "0") + ";\n"); + bison.append("\";\n" + " n->hasLocation = " + (hasLocation ? "1" : "0") + ";\n"); for (i = 0; i < nts.size(); i++) { - bison.append( - " n->children[").append(i).append("] = $").append(nts.get(i)).append(".nterm;\n"); + bison + .append(" n->children[") + .append(i) + .append("] = $") + .append(nts.get(i)) + .append(".nterm;\n"); } bison.append( - " value_type result = {.nterm = n};\n" + - " $$ = result;\n" + - " }\n" + - "}\n"); + " value_type result = {.nterm = n};\n" + " $$ = result;\n" + " }\n" + "}\n"); } else if (prod.att().contains(Att.BRACKET())) { - bison.append("{\n" + - " $$ = $").append(nts.get(0)).append(";\n" + - "}\n"); + bison.append("{\n" + " $$ = $").append(nts.get(0)).append(";\n" + "}\n"); } if (glr) { bison.append("%merge "); diff --git a/kernel/src/main/java/org/kframework/parser/inner/kernel/Scanner.java b/kernel/src/main/java/org/kframework/parser/inner/kernel/Scanner.java index 9a9185ef256..c9ad59fb189 100644 --- a/kernel/src/main/java/org/kframework/parser/inner/kernel/Scanner.java +++ b/kernel/src/main/java/org/kframework/parser/inner/kernel/Scanner.java @@ -1,8 +1,22 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner.kernel; +import static org.kframework.Collections.*; +import static org.kframework.kore.KORE.*; + import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.*; +import java.util.concurrent.Semaphore; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.kframework.attributes.Att; @@ -24,457 +38,486 @@ import org.kframework.utils.errorsystem.KEMException; import scala.Tuple2; -import java.io.File; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.charset.StandardCharsets; -import java.nio.file.StandardCopyOption; -import java.nio.file.Files; -import java.util.*; -import java.util.concurrent.Semaphore; -import java.util.function.BiConsumer; -import java.util.stream.Collectors; - -import static org.kframework.kore.KORE.*; -import static org.kframework.Collections.*; - -/** - * Created by dwightguth on 7/21/16. - */ +/** Created by dwightguth on 7/21/16. */ public class Scanner implements AutoCloseable { - private final Map> tokens; - private final File scanner; - private final Module module; - private GlobalOptions go = new GlobalOptions(); - - public static final String COMPILER = OS.current().equals(OS.OSX) ? "clang" : "gcc"; - - public Map> getTokens() { - return Collections.unmodifiableMap(tokens); - } - - public static Map> getTokens(Module module) { - Map tokens = new TreeMap<>(); - Set terminals = new HashSet<>(); - for (Production p : iterable(module.productions())) { - for (ProductionItem pi : iterable(p.items())) { - if (pi instanceof TerminalLike lx) { - if (tokens.containsKey(lx)) { - int prec; - if (p.att().contains(Att.PREC())) { - prec = Integer.valueOf(p.att().getOptional(Att.PREC()).get()); - } else if (lx instanceof Terminal) { - prec = Integer.MAX_VALUE; - } else { - prec = 0; - } - if (prec != tokens.get(lx)) { - throw KEMException.compilerError("Inconsistent token precedence detected.", p); - } - } else if (lx instanceof Terminal && terminals.contains(((Terminal) lx).value())) { - tokens.put(lx, Integer.MAX_VALUE); - } else { - if (lx instanceof Terminal) { - terminals.add(((Terminal) lx).value()); - tokens.put(lx, Integer.MAX_VALUE); - } else { - int prec; - if (p.att().contains(Att.PREC())) { - prec = Integer.valueOf(p.att().getOptional(Att.PREC()).get()); - } else { - prec = 0; - } - tokens.put(lx, prec); - } - } - } + private final Map> tokens; + private final File scanner; + private final Module module; + private GlobalOptions go = new GlobalOptions(); + + public static final String COMPILER = OS.current().equals(OS.OSX) ? "clang" : "gcc"; + + public Map> getTokens() { + return Collections.unmodifiableMap(tokens); + } + + public static Map> getTokens(Module module) { + Map tokens = new TreeMap<>(); + Set terminals = new HashSet<>(); + for (Production p : iterable(module.productions())) { + for (ProductionItem pi : iterable(p.items())) { + if (pi instanceof TerminalLike lx) { + if (tokens.containsKey(lx)) { + int prec; + if (p.att().contains(Att.PREC())) { + prec = Integer.valueOf(p.att().getOptional(Att.PREC()).get()); + } else if (lx instanceof Terminal) { + prec = Integer.MAX_VALUE; + } else { + prec = 0; } + if (prec != tokens.get(lx)) { + throw KEMException.compilerError("Inconsistent token precedence detected.", p); + } + } else if (lx instanceof Terminal && terminals.contains(((Terminal) lx).value())) { + tokens.put(lx, Integer.MAX_VALUE); + } else { + if (lx instanceof Terminal) { + terminals.add(((Terminal) lx).value()); + tokens.put(lx, Integer.MAX_VALUE); + } else { + int prec; + if (p.att().contains(Att.PREC())) { + prec = Integer.valueOf(p.att().getOptional(Att.PREC()).get()); + } else { + prec = 0; + } + tokens.put(lx, prec); + } + } } - - Map> finalTokens = new HashMap<>(); - // token 0 is EOF, so start at index 1 - int idx = 1; - for (TerminalLike t : tokens.keySet()) { - finalTokens.put(t, Tuple2.apply(idx++, tokens.get(t))); - } - - return finalTokens; - } - - - public Scanner(ParseInModule module, GlobalOptions go) { - this.go = go; - this.tokens = getTokens(module.getParsingModule()); - this.module = module.seedModule(); - this.scanner = getScanner(); + } } - public Scanner(ParseInModule module) { - this.tokens = getTokens(module.getParsingModule()); - this.module = module.seedModule(); - this.scanner = getScanner(); + Map> finalTokens = new HashMap<>(); + // token 0 is EOF, so start at index 1 + int idx = 1; + for (TerminalLike t : tokens.keySet()) { + finalTokens.put(t, Tuple2.apply(idx++, tokens.get(t))); } - public Scanner(ParseInModule module, GlobalOptions go, File scanner) { - this.go = go; - this.tokens = getTokens(module.getParsingModule()); - this.module = module.seedModule(); - this.scanner = scanner; + return finalTokens; + } + + public Scanner(ParseInModule module, GlobalOptions go) { + this.go = go; + this.tokens = getTokens(module.getParsingModule()); + this.module = module.seedModule(); + this.scanner = getScanner(); + } + + public Scanner(ParseInModule module) { + this.tokens = getTokens(module.getParsingModule()); + this.module = module.seedModule(); + this.scanner = getScanner(); + } + + public Scanner(ParseInModule module, GlobalOptions go, File scanner) { + this.go = go; + this.tokens = getTokens(module.getParsingModule()); + this.module = module.seedModule(); + this.scanner = scanner; + } + + public void serialize(File output) { + try { + Files.copy(scanner.toPath(), output.toPath(), StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw KEMException.criticalError("Could not write to " + output, e); } - - public void serialize(File output) { - try { - Files.copy(scanner.toPath(), output.toPath(), StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) { - throw KEMException.criticalError("Could not write to " + output, e); - } - } - - public Module getModule() { - return module; + } + + public Module getModule() { + return module; + } + + public Set kinds() { + return tokens.values().stream().map(v -> v._1()).collect(Collectors.toSet()); + } + + // debugging method + public TerminalLike getTokenByKind(int kind) { + return tokens.entrySet().stream() + .filter(e -> e.getValue()._1() == kind) + .findAny() + .get() + .getKey(); + } + + public void appendScanner( + StringBuilder flex, BiConsumer writeAction) { + if (this.module.allSorts().contains(Sorts.Layout())) { + flex.append(this.module.layout() + " ;\n"); } - - public Set kinds() { - return tokens.values().stream().map(v -> v._1()).collect(Collectors.toSet()); + List ordered = + tokens.keySet().stream() + .sorted((t1, t2) -> tokens.get(t2)._2() - tokens.get(t1)._2()) + .collect(Collectors.toList()); + for (TerminalLike key : ordered) { + if (key instanceof Terminal t) { + flex.append(StringUtil.enquoteCString(t.value())); + } else { + RegexTerminal t = (RegexTerminal) key; + flex.append(t.regex()); + } + writeAction.accept(flex, key); } - - // debugging method - public TerminalLike getTokenByKind(int kind) { - return tokens.entrySet().stream().filter(e -> e.getValue()._1() == kind).findAny().get().getKey(); + } + + public void writeStandaloneScanner(File path) { + StringBuilder flex = new StringBuilder(); + flex.append( + "%{\n" + + "#include \"node.h\"\n" + + "#include \"parser.tab.h\"\n" + + "char *filename;\n" + + "#define YY_USER_ACTION yylloc->first_line = yylloc->last_line = yylineno; \\\n" + + " yylloc->first_column = yycolumn; yylloc->last_column = yycolumn + yyleng - 1;" + + " \\\n" + + " yycolumn += yyleng; \\\n" + + " yylloc->filename = filename;\n" + + "#define ECHO do {\\\n" + + " fprintf (stderr, \"%d:%d:%d:%d:syntax error: unexpected %s\\n" + + "\", yylloc->first_line, yylloc->first_column, yylloc->last_line," + + " yylloc->last_column, yytext);\\\n" + + " exit(1);\\\n" + + "} while (0)\n" + + "void line_marker(char *, void *);\n" + + "%}\n\n" + + "%option reentrant bison-bridge\n" + + "%option bison-locations\n" + + "%option noyywrap\n" + + "%option yylineno\n"); + for (SyntaxLexical ident : iterable(module.lexicalIdentifiers())) { + flex.append(ident.name()); + flex.append(" "); + flex.append(ident.regex()); + flex.append("\n"); } - - public void appendScanner(StringBuilder flex, BiConsumer writeAction) { - if (this.module.allSorts().contains(Sorts.Layout())) { - flex.append(this.module.layout() + " ;\n"); - } - List ordered = tokens.keySet().stream().sorted((t1, t2) -> tokens.get(t2)._2() - tokens.get(t1)._2()).collect(Collectors.toList()); - for (TerminalLike key : ordered) { - if (key instanceof Terminal t) { - flex.append(StringUtil.enquoteCString(t.value())); - } else { - RegexTerminal t = (RegexTerminal) key; - flex.append(t.regex()); - } - writeAction.accept(flex, key); - } + flex.append("%%\n\n"); + if (module.productionsForSort().contains(Sorts.LineMarker().head())) { + stream(module.productionsForSort().apply(Sorts.LineMarker().head())) + .forEach( + prod -> { + if (prod.items().size() != 1 + || !(prod.items().apply(0) instanceof RegexTerminal terminal)) { + throw KEMException.compilerError( + "Productions of sort `#LineMarker` must be exactly one `RegexTerminal`.", + prod); + } + String regex = terminal.regex(); + flex.append(regex).append(" line_marker(yytext, yyscanner);\n"); + }); } - - public void writeStandaloneScanner(File path) { - StringBuilder flex = new StringBuilder(); - flex.append("%{\n" + - "#include \"node.h\"\n" + - "#include \"parser.tab.h\"\n" + - "char *filename;\n" + - "#define YY_USER_ACTION yylloc->first_line = yylloc->last_line = yylineno; \\\n" + - " yylloc->first_column = yycolumn; yylloc->last_column = yycolumn + yyleng - 1; \\\n" + - " yycolumn += yyleng; \\\n" + - " yylloc->filename = filename;\n" + - "#define ECHO do {\\\n" + - " fprintf (stderr, \"%d:%d:%d:%d:syntax error: unexpected %s\\n\", yylloc->first_line, yylloc->first_column, yylloc->last_line, yylloc->last_column, yytext);\\\n" + - " exit(1);\\\n" + - "} while (0)\n" + - "void line_marker(char *, void *);\n" + - "%}\n\n" + - "%option reentrant bison-bridge\n" + - "%option bison-locations\n" + - "%option noyywrap\n" + - "%option yylineno\n"); - for (SyntaxLexical ident : iterable(module.lexicalIdentifiers())) { - flex.append(ident.name()); - flex.append(" "); - flex.append(ident.regex()); - flex.append("\n"); - } - flex.append("%%\n\n"); - if (module.productionsForSort().contains(Sorts.LineMarker().head())) { - stream(module.productionsForSort().apply(Sorts.LineMarker().head())).forEach(prod -> { - if (prod.items().size() != 1 || !(prod.items().apply(0) instanceof RegexTerminal terminal)) { - throw KEMException.compilerError("Productions of sort `#LineMarker` must be exactly one `RegexTerminal`.", prod); - } - String regex = terminal.regex(); - flex.append(regex).append(" line_marker(yytext, yyscanner);\n"); - }); - } - appendScanner(flex, this::writeStandaloneAction); - try { - FileUtils.write(path, flex); - } catch (IOException e) { - throw KEMException.internalError("Failed to write file for scanner", e); - } + appendScanner(flex, this::writeStandaloneAction); + try { + FileUtils.write(path, flex); + } catch (IOException e) { + throw KEMException.internalError("Failed to write file for scanner", e); } - - public File getScanner() { - Stopwatch sw = new Stopwatch(go); - File scanner; - // tokenization - try { - File scannerSource = File.createTempFile("tmp-kompile-", ".l"); - scannerSource.deleteOnExit(); - StringBuilder flex = new StringBuilder(); - flex.append("%{\n" + - "#include\n" + - "#include\n" + - "#include \n" + - "#define ECHO do " + - " {" + - " long long start_pos = yytext - buffer;" + - " long long end_pos = start_pos + yyleng;" + - " fwrite(&start_pos, sizeof(start_pos), 1, stdout);" + - " fwrite(&end_pos, sizeof(end_pos), 1, stdout);" + - " int kind = -1;" + - " fwrite(&kind, sizeof(kind), 1, stdout);" + - " int len = strlen(yytext);" + - " fwrite(&len, sizeof(len), 1, stdout);" + - " fwrite(yytext, 1, len, stdout);" + - " } while (0) \n" + - "char *buffer;\n" + - "%}\n\n"); - for (SyntaxLexical ident : iterable(module.lexicalIdentifiers())) { - flex.append(ident.name()); - flex.append(" "); - flex.append(ident.regex()); - flex.append("\n"); - } - flex.append("%%\n\n"); - appendScanner(flex, this::writeAction); - //WIN32 fix for line terminator issue: https://sourceforge.net/p/mingw/mailman/message/11374534/ - flex.append("\n\n%%\n\n" + - "int main(int argc, char **argv) {\n" + - " freopen(NULL, \"rb\", stdin);\n" + - " freopen(NULL, \"wb\", stdout);\n" + - "# ifdef WIN32\n" + - " if ( -1 == _setmode( _fileno( stdout ), _O_BINARY ) ) {\n" + - " perror ( \"generated scanner: Cannot set BINARY mode for stdout\" );\n" + - " exit(1);\n" + - " }\n" + - " if ( -1 == _setmode( _fileno( stdin ), _O_BINARY ) ) {\n" + - " perror ( \"generated scanner: Cannot set BINARY mode for stdin\" );\n" + - " exit(1);\n" + - " }\n" + - "# endif /* WIN32 */\n" + - "\n" + - " while(1) {\n" + - " int length;\n" + - " size_t nread = fread(&length, sizeof(length), 1, stdin);\n" + - " if (nread < 1) exit(0);\n" + - " buffer = malloc(length + 2);\n" + - " buffer[length] = 0;\n" + - " buffer[length+1] = 0;\n" + - " fread(buffer, length, 1, stdin);\n" + - " YY_BUFFER_STATE bs = yy_scan_buffer(buffer, length + 2);\n" + - " yy_switch_to_buffer(bs);\n" + - " yylex();\n" + - " long long exit = -1;\n" + - " fwrite(&exit, sizeof(exit), 1, stdout);\n" + - " fwrite(&exit, sizeof(exit), 1, stdout);\n" + - " fwrite(&exit, sizeof(exit), 1, stdout);\n" + - " fflush(stdout);\n" + - " }\n" + - "}"); - FileUtils.write(scannerSource, flex); - File scannerCSource = File.createTempFile("tmp-kompile-", ".c"); - scannerCSource.deleteOnExit(); - ProcessBuilder pb = new ProcessBuilder("flex", "--nowarn", "--noyywrap", "-Ca", "-o", - scannerCSource.getAbsolutePath(), scannerSource.getAbsolutePath()); - pb.inheritIO(); - int exit = pb.start().waitFor(); - if (exit != 0) { - System.err.println(pb.command()); - throw KEMException.internalError( - "Flex returned nonzero exit code. See output for details. flex command: " + pb.command()); - } - scanner = File.createTempFile("tmp-kompile-", ""); - scanner.deleteOnExit(); - //Option -lfl unnecessary. Same effect achieved by --noyywrap above. - pb = new ProcessBuilder(COMPILER, scannerCSource.getAbsolutePath(), "-o", scanner.getAbsolutePath(), "-Wno-unused-result"); - pb.inheritIO(); - exit = pb.start().waitFor(); - scanner.setExecutable(true); - if (exit != 0) { - throw KEMException.internalError( - COMPILER + " returned nonzero exit code. See output for details. " + COMPILER + " command: " + pb.command()); - } - } catch (IOException | InterruptedException e) { - throw KEMException.internalError("Failed to write file for scanner", e); - } - sw.printIntermediate(" New scanner: " + module.name()); - return scanner; + } + + public File getScanner() { + Stopwatch sw = new Stopwatch(go); + File scanner; + // tokenization + try { + File scannerSource = File.createTempFile("tmp-kompile-", ".l"); + scannerSource.deleteOnExit(); + StringBuilder flex = new StringBuilder(); + flex.append( + "%{\n" + + "#include\n" + + "#include\n" + + "#include \n" + + "#define ECHO do " + + " {" + + " long long start_pos = yytext - buffer;" + + " long long end_pos = start_pos + yyleng;" + + " fwrite(&start_pos, sizeof(start_pos), 1, stdout);" + + " fwrite(&end_pos, sizeof(end_pos), 1, stdout);" + + " int kind = -1;" + + " fwrite(&kind, sizeof(kind), 1, stdout);" + + " int len = strlen(yytext);" + + " fwrite(&len, sizeof(len), 1, stdout);" + + " fwrite(yytext, 1, len, stdout);" + + " } while (0) \n" + + "char *buffer;\n" + + "%}\n\n"); + for (SyntaxLexical ident : iterable(module.lexicalIdentifiers())) { + flex.append(ident.name()); + flex.append(" "); + flex.append(ident.regex()); + flex.append("\n"); + } + flex.append("%%\n\n"); + appendScanner(flex, this::writeAction); + // WIN32 fix for line terminator issue: + // https://sourceforge.net/p/mingw/mailman/message/11374534/ + flex.append( + "\n\n%%\n\n" + + "int main(int argc, char **argv) {\n" + + " freopen(NULL, \"rb\", stdin);\n" + + " freopen(NULL, \"wb\", stdout);\n" + + "# ifdef WIN32\n" + + " if ( -1 == _setmode( _fileno( stdout ), _O_BINARY ) ) {\n" + + " perror ( \"generated scanner: Cannot set BINARY mode for stdout\" );\n" + + " exit(1);\n" + + " }\n" + + " if ( -1 == _setmode( _fileno( stdin ), _O_BINARY ) ) {\n" + + " perror ( \"generated scanner: Cannot set BINARY mode for stdin\" );\n" + + " exit(1);\n" + + " }\n" + + "# endif /* WIN32 */\n" + + "\n" + + " while(1) {\n" + + " int length;\n" + + " size_t nread = fread(&length, sizeof(length), 1, stdin);\n" + + " if (nread < 1) exit(0);\n" + + " buffer = malloc(length + 2);\n" + + " buffer[length] = 0;\n" + + " buffer[length+1] = 0;\n" + + " fread(buffer, length, 1, stdin);\n" + + " YY_BUFFER_STATE bs = yy_scan_buffer(buffer, length + 2);\n" + + " yy_switch_to_buffer(bs);\n" + + " yylex();\n" + + " long long exit = -1;\n" + + " fwrite(&exit, sizeof(exit), 1, stdout);\n" + + " fwrite(&exit, sizeof(exit), 1, stdout);\n" + + " fwrite(&exit, sizeof(exit), 1, stdout);\n" + + " fflush(stdout);\n" + + " }\n" + + "}"); + FileUtils.write(scannerSource, flex); + File scannerCSource = File.createTempFile("tmp-kompile-", ".c"); + scannerCSource.deleteOnExit(); + ProcessBuilder pb = + new ProcessBuilder( + "flex", + "--nowarn", + "--noyywrap", + "-Ca", + "-o", + scannerCSource.getAbsolutePath(), + scannerSource.getAbsolutePath()); + pb.inheritIO(); + int exit = pb.start().waitFor(); + if (exit != 0) { + System.err.println(pb.command()); + throw KEMException.internalError( + "Flex returned nonzero exit code. See output for details. flex command: " + + pb.command()); + } + scanner = File.createTempFile("tmp-kompile-", ""); + scanner.deleteOnExit(); + // Option -lfl unnecessary. Same effect achieved by --noyywrap above. + pb = + new ProcessBuilder( + COMPILER, + scannerCSource.getAbsolutePath(), + "-o", + scanner.getAbsolutePath(), + "-Wno-unused-result"); + pb.inheritIO(); + exit = pb.start().waitFor(); + scanner.setExecutable(true); + if (exit != 0) { + throw KEMException.internalError( + COMPILER + + " returned nonzero exit code. See output for details. " + + COMPILER + + " command: " + + pb.command()); + } + } catch (IOException | InterruptedException e) { + throw KEMException.internalError("Failed to write file for scanner", e); } - - private void writeAction(StringBuilder flex, TerminalLike key) { - flex.append(" {\n" + - " long long start_pos = yytext - buffer;\n" + - " long long end_pos = start_pos + yyleng;\n" + - " fwrite(&start_pos, sizeof(start_pos), 1, stdout);\n" + - " fwrite(&end_pos, sizeof(end_pos), 1, stdout);\n" + - " int kind = ").append(tokens.get(key)._1()).append(";\n" + - " fwrite(&kind, sizeof(kind), 1, stdout);\n" + - " int len = strlen(yytext);\n" + - " fwrite(&len, sizeof(len), 1, stdout);\n" + - " fwrite(yytext, 1, len, stdout);\n" + - " }\n"); + sw.printIntermediate(" New scanner: " + module.name()); + return scanner; + } + + private void writeAction(StringBuilder flex, TerminalLike key) { + flex.append( + " {\n" + + " long long start_pos = yytext - buffer;\n" + + " long long end_pos = start_pos + yyleng;\n" + + " fwrite(&start_pos, sizeof(start_pos), 1, stdout);\n" + + " fwrite(&end_pos, sizeof(end_pos), 1, stdout);\n" + + " int kind = ") + .append(tokens.get(key)._1()) + .append( + ";\n" + + " fwrite(&kind, sizeof(kind), 1, stdout);\n" + + " int len = strlen(yytext);\n" + + " fwrite(&len, sizeof(len), 1, stdout);\n" + + " fwrite(yytext, 1, len, stdout);\n" + + " }\n"); + } + + private void writeStandaloneAction(StringBuilder flex, TerminalLike key) { + flex.append(" {\n" + " int kind = ") + .append(tokens.get(key)._1() + 1) + .append( + ";\n" + + " *((char **)yylval) = malloc(strlen(yytext) + 1);\n" + + " strcpy(*((char **)yylval), yytext);\n" + + " return kind;\n" + + " }\n"); + } + + private int maxToken = -1; + + public int getMaxToken() { + int max = maxToken; + if (max == -1) { + for (Tuple2 val : tokens.values()) { + max = Integer.max(max, val._1()); + } + maxToken = max; } - - private void writeStandaloneAction(StringBuilder flex, TerminalLike key) { - flex.append(" {\n" + - " int kind = ").append(tokens.get(key)._1()+1).append(";\n" + - " *((char **)yylval) = malloc(strlen(yytext) + 1);\n" + - " strcpy(*((char **)yylval), yytext);\n" + - " return kind;\n" + - " }\n"); + return max; + } + + public int resolve(TerminalLike terminal) { + return tokens.get(terminal)._1(); + } + + public static class Token { + public final int kind; + public final String value; + public final int startLoc; + public final int endLoc; + + public Token(int kind, String value, long startLoc, long endLoc) { + this.kind = kind; + this.value = value; + assert startLoc < Integer.MAX_VALUE; + assert endLoc < Integer.MAX_VALUE; + this.startLoc = (int) startLoc; + this.endLoc = (int) endLoc; } - private int maxToken = -1; - - public int getMaxToken() { - int max = maxToken; - if (max == -1) { - for (Tuple2 val : tokens.values()) { - max = Integer.max(max, val._1()); - } - maxToken = max; - } - return max; + @Override + public String toString() { + return kind + ":" + value; } - - public int resolve(TerminalLike terminal) { - return tokens.get(terminal)._1(); + } + + @Override + public void close() { + synchronized (idleProcesses) { + for (Process p : idleProcesses.get(this)) { + p.destroy(); + cache.remove(p); + activeProcceses--; + } + idleProcesses.removeAll(this); } - - public static class Token { - public final int kind; - public final String value; - public final int startLoc; - public final int endLoc; - - public Token(int kind, String value, long startLoc, long endLoc) { - this.kind = kind; - this.value = value; - assert startLoc < Integer.MAX_VALUE; - assert endLoc < Integer.MAX_VALUE; - this.startLoc = (int)startLoc; - this.endLoc = (int)endLoc; - } - + } + + private static final int N_CPUS = Runtime.getRuntime().availableProcessors(); + private static final int N_PROCS = 512; + private static int activeProcceses = 0; + private static final Semaphore runningScanners = new Semaphore(N_PROCS); + private static final ListMultimap idleProcesses = ArrayListMultimap.create(); + private static final Map cache = + new LinkedHashMap() { @Override - public String toString() { - return kind + ":" + value; + protected boolean removeEldestEntry(Map.Entry entry) { + if (activeProcceses > N_PROCS) { + entry.getKey().destroy(); + idleProcesses.get(entry.getValue()).remove(entry.getKey()); + activeProcceses--; + return true; + } + return false; } - } - - @Override - public void close() { - synchronized(idleProcesses) { - for (Process p : idleProcesses.get(this)) { - p.destroy(); - cache.remove(p); - activeProcceses--; - } - idleProcesses.removeAll(this); + }; + + public Token[] tokenize(String input, Source source, int[] lines, int[] columns) { + try { + runningScanners.acquire(); + + Process process; + synchronized (idleProcesses) { + if (idleProcesses.get(this).size() > 0) { + List idleForThisScanner = idleProcesses.get(this); + process = idleForThisScanner.remove(idleForThisScanner.size() - 1); + cache.remove(process); + } else { + process = new ProcessBuilder(scanner.getAbsolutePath()).start(); + activeProcceses++; + // temporarily add it so that LinkedHashMap evicts the old entry + cache.put(process, this); + cache.remove(process); } + } + + byte[] buf = input.getBytes(StandardCharsets.UTF_8); + ByteBuffer size = ByteBuffer.allocate(4); + size.order(ByteOrder.nativeOrder()); + size.putInt(buf.length); + process.getOutputStream().write(size.array()); + process.getOutputStream().write(buf); + process.getOutputStream().flush(); + return readTokenizedOutput(process, source, lines, columns, input.length()); + } catch (IOException | InterruptedException e) { + throw KEMException.internalError("Failed to invoke scanner", e); + } finally { + runningScanners.release(); } - - private static final int N_CPUS = Runtime.getRuntime().availableProcessors(); - private static final int N_PROCS = 512; - private static int activeProcceses = 0; - private static final Semaphore runningScanners = new Semaphore(N_PROCS); - private static final ListMultimap idleProcesses = ArrayListMultimap.create(); - private static final Map cache = new LinkedHashMap() { - @Override - protected boolean removeEldestEntry(Map.Entry entry) { - if (activeProcceses > N_PROCS) { - entry.getKey().destroy(); - idleProcesses.get(entry.getValue()).remove(entry.getKey()); - activeProcceses--; - return true; - } - return false; + } + + private Token[] readTokenizedOutput( + Process process, Source source, int[] lines, int[] columns, int length) throws IOException { + List result = new ArrayList<>(); + boolean success = false; + try { + while (true) { + byte[] buf = new byte[24]; + IOUtils.readFully(process.getInputStream(), buf); + ByteBuffer byteBuf = ByteBuffer.wrap(buf); + byteBuf.order(ByteOrder.nativeOrder()); + long startLoc = byteBuf.getLong(); + if (startLoc < 0) { + break; } - }; - - public Token[] tokenize(String input, Source source, int[] lines, int[] columns) { - try { - runningScanners.acquire(); - - Process process; - synchronized (idleProcesses) { - if (idleProcesses.get(this).size() > 0) { - List idleForThisScanner = idleProcesses.get(this); - process = idleForThisScanner.remove(idleForThisScanner.size() - 1); - cache.remove(process); - } else { - process = new ProcessBuilder(scanner.getAbsolutePath()).start(); - activeProcceses++; - // temporarily add it so that LinkedHashMap evicts the old entry - cache.put(process, this); - cache.remove(process); - } - } - - byte[] buf = input.getBytes(StandardCharsets.UTF_8); - ByteBuffer size = ByteBuffer.allocate(4); - size.order(ByteOrder.nativeOrder()); - size.putInt(buf.length); - process.getOutputStream().write(size.array()); - process.getOutputStream().write(buf); - process.getOutputStream().flush(); - return readTokenizedOutput(process, source, lines, columns, input.length()); - } catch (IOException | InterruptedException e) { - throw KEMException.internalError("Failed to invoke scanner", e); - } finally { - runningScanners.release(); + long endLoc = byteBuf.getLong(); + int kind = byteBuf.getInt(); + int len = byteBuf.getInt(); + byte[] bytes = new byte[len]; + IOUtils.readFully(process.getInputStream(), bytes); + String value = new String(bytes, StandardCharsets.UTF_8); + Token t = new Token(kind, value, startLoc, endLoc); + if (kind == -1) { + String msg = "Scanner error: unexpected character sequence '" + value + "'."; + Location loc = + new Location( + lines[t.startLoc], columns[t.startLoc], lines[t.endLoc], columns[t.endLoc]); + throw KEMException.innerParserError(msg, source, loc); } - } - - private Token[] readTokenizedOutput(Process process, Source source, int[] lines, int[] columns, int length) throws IOException { - List result = new ArrayList<>(); - boolean success = false; - try { - while (true) { - byte[] buf = new byte[24]; - IOUtils.readFully(process.getInputStream(), buf); - ByteBuffer byteBuf = ByteBuffer.wrap(buf); - byteBuf.order(ByteOrder.nativeOrder()); - long startLoc = byteBuf.getLong(); - if (startLoc < 0) { - break; - } - long endLoc = byteBuf.getLong(); - int kind = byteBuf.getInt(); - int len = byteBuf.getInt(); - byte[] bytes = new byte[len]; - IOUtils.readFully(process.getInputStream(), bytes); - String value = new String(bytes, StandardCharsets.UTF_8); - Token t = new Token(kind, value, startLoc, endLoc); - if (kind == -1) { - String msg = "Scanner error: unexpected character sequence '" + value + "'."; - Location loc = new Location(lines[t.startLoc], columns[t.startLoc], - lines[t.endLoc], columns[t.endLoc]); - throw KEMException.innerParserError(msg, source, loc); - } - result.add(t); - } - success = true; - // add EOF token at end of token sequence - result.add(new Token(0, "", length, length)); - return result.toArray(new Token[result.size()]); - } finally { - if (success) { - synchronized (idleProcesses) { - cache.put(process, this); - idleProcesses.put(this, process); - } - } else { - // we aren't returning this process to the pool since something went wrong with it, - // so we have to clean up here and then make sure that the pool knows it can allocate a new process. - synchronized (idleProcesses) { - process.destroy(); - activeProcceses--; - } - } + result.add(t); + } + success = true; + // add EOF token at end of token sequence + result.add(new Token(0, "", length, length)); + return result.toArray(new Token[result.size()]); + } finally { + if (success) { + synchronized (idleProcesses) { + cache.put(process, this); + idleProcesses.put(this, process); + } + } else { + // we aren't returning this process to the pool since something went wrong with it, + // so we have to clean up here and then make sure that the pool knows it can allocate a new + // process. + synchronized (idleProcesses) { + process.destroy(); + activeProcceses--; } + } } - + } } diff --git a/kernel/src/main/java/org/kframework/parser/json/JsonParser.java b/kernel/src/main/java/org/kframework/parser/json/JsonParser.java index 83252ae20a1..1bf3e7c263f 100644 --- a/kernel/src/main/java/org/kframework/parser/json/JsonParser.java +++ b/kernel/src/main/java/org/kframework/parser/json/JsonParser.java @@ -1,11 +1,20 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.json; -import org.checkerframework.checker.nullness.Opt; +import static org.kframework.Collections.*; +import static org.kframework.kore.KORE.*; + +import java.io.IOException; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.json.*; import org.kframework.attributes.Att; import org.kframework.attributes.Location; import org.kframework.attributes.Source; -import org.kframework.compile.ProcessGroupAttributes; import org.kframework.definition.Associativity; import org.kframework.definition.Bubble; import org.kframework.definition.Claim; @@ -13,8 +22,8 @@ import org.kframework.definition.Constructors; import org.kframework.definition.Context; import org.kframework.definition.Definition; -import org.kframework.definition.FlatModule; import org.kframework.definition.FlatImport; +import org.kframework.definition.FlatModule; import org.kframework.definition.Module; import org.kframework.definition.NonTerminal; import org.kframework.definition.Production; @@ -40,391 +49,424 @@ import scala.collection.JavaConverters; import scala.util.Either; -import javax.json.*; -import java.io.IOException; -import java.io.StringReader; -import java.io.UnsupportedEncodingException; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -import static org.kframework.Collections.*; -import static org.kframework.kore.KORE.*; - -/** - * Parses a Json term into the KORE data structures. - */ +/** Parses a Json term into the KORE data structures. */ public class JsonParser { - public static final String INJECTEDKLABEL = "InjectedKLabel" - , KAPPLY = "KApply" - , KAS = "KAs" - , KATT = "KAtt" - , KBUBBLE = "KBubble" - , KCONFIGURATION = "KConfiguration" - , KCONTEXT = "KContext" - , KDEFINITION = "KDefinition" - , KNONTERMINAL = "KNonTerminal" - , KMODULE = "KModule" - , KFLATMODULE = "KFlatModule" - , KFLATMODULELIST = "KFlatModuleList" - , KIMPORT = "KImport" - , KPRODUCTION = "KProduction" - , KREGEXTERMINAL = "KRegexTerminal" - , KREWRITE = "KRewrite" - , KRULE = "KRule" - , KCLAIM = "KClaim" - , KSEQUENCE = "KSequence" - , KSORT = "KSort" - , KSORTSYNONYM = "KSortSynonym" - , KSYNTAXLEXICAL = "KSyntaxLexical" - , KSYNTAXASSOCIATIVITY = "KSyntaxAssociativity" - , KSYNTAXPRIORITY = "KSyntaxPriority" - , KSYNTAXSORT = "KSyntaxSort" - , KTERMINAL = "KTerminal" - , KTOKEN = "KToken" - , KVARIABLE = "KVariable" - ; - -///////////////////////////// -// Parsing Definition Json // -///////////////////////////// - - public static Definition parseDefinition(byte[] data) { - return parseDefinition(new String(data, StandardCharsets.UTF_8)); + public static final String INJECTEDKLABEL = "InjectedKLabel", + KAPPLY = "KApply", + KAS = "KAs", + KATT = "KAtt", + KBUBBLE = "KBubble", + KCONFIGURATION = "KConfiguration", + KCONTEXT = "KContext", + KDEFINITION = "KDefinition", + KNONTERMINAL = "KNonTerminal", + KMODULE = "KModule", + KFLATMODULE = "KFlatModule", + KFLATMODULELIST = "KFlatModuleList", + KIMPORT = "KImport", + KPRODUCTION = "KProduction", + KREGEXTERMINAL = "KRegexTerminal", + KREWRITE = "KRewrite", + KRULE = "KRule", + KCLAIM = "KClaim", + KSEQUENCE = "KSequence", + KSORT = "KSort", + KSORTSYNONYM = "KSortSynonym", + KSYNTAXLEXICAL = "KSyntaxLexical", + KSYNTAXASSOCIATIVITY = "KSyntaxAssociativity", + KSYNTAXPRIORITY = "KSyntaxPriority", + KSYNTAXSORT = "KSyntaxSort", + KTERMINAL = "KTerminal", + KTOKEN = "KToken", + KVARIABLE = "KVariable"; + + ///////////////////////////// + // Parsing Definition Json // + ///////////////////////////// + + public static Definition parseDefinition(byte[] data) { + return parseDefinition(new String(data, StandardCharsets.UTF_8)); + } + + public static Definition parseDefinition(String data) { + JsonReader reader = Json.createReader(new StringReader(data)); + return parseJsonDef(reader.readObject()); + } + + public static Definition parseJsonDef(JsonObject data) { + try { + if (!(data.containsKey("format") + && data.containsKey("version") + && data.containsKey("term"))) { + throw KEMException.criticalError( + "Must have `format`, `version`, and `term` fields in serialized Json!"); + } + if (!data.getString("format").equals("KAST")) { + throw KEMException.criticalError( + "Only can deserialize 'KAST' format Json! Found: " + data.getString("format")); + } + if (data.getInt("version") != ToJson.version) { + throw KEMException.criticalError( + "Only can deserialize KAST version '" + + ToJson.version + + "'! Found: " + + data.getInt("version")); + } + return toDefinition(data.getJsonObject("term")); + } catch (IOException e) { + throw KEMException.criticalError("Could not read Definition term from json", e); } - - public static Definition parseDefinition(String data) { - JsonReader reader = Json.createReader(new StringReader(data)); - return parseJsonDef(reader.readObject()); + } + + public static Definition toDefinition(JsonObject data) throws IOException { + if (!data.getString("node").equals(KDEFINITION)) + throw KEMException.criticalError( + "Unexpected node found in KAST Json term: " + data.getString("node")); + + String mainModuleName = data.getString("mainModule"); + JsonArray mods = data.getJsonArray("modules"); + List flatModules = new ArrayList<>(); + for (JsonObject m : mods.getValuesAs(JsonObject.class)) { + flatModules.add(toFlatModule(m)); } - public static Definition parseJsonDef(JsonObject data) { - try { - if (! (data.containsKey("format") && data.containsKey("version") && data.containsKey("term"))) { - throw KEMException.criticalError("Must have `format`, `version`, and `term` fields in serialized Json!"); - } - if (! data.getString("format").equals("KAST")) { - throw KEMException.criticalError("Only can deserialize 'KAST' format Json! Found: " + data.getString("format")); - } - if (data.getInt("version") != ToJson.version) { - throw KEMException.criticalError("Only can deserialize KAST version '" + ToJson.version + "'! Found: " + data.getInt("version")); - } - return toDefinition(data.getJsonObject("term")); - } catch (IOException e) { - throw KEMException.criticalError("Could not read Definition term from json", e); - } + scala.collection.Set koreModules = FlatModule.toModules(immutable(flatModules), Set()); + return Constructors.Definition( + koreModules + .find(x -> x.name().equals(mainModuleName)) + .getOrElse( + () -> { + throw new AssertionError( + "Could not find main module name " + + mainModuleName + + " when loading from JSON."); + }), + koreModules, + toAtt(data.getJsonObject("att"))); + } + + ///////////////////////// + // Parsing Module Json // + ///////////////////////// + + public static FlatModule toFlatModule(JsonObject data) throws IOException { + if (!data.getString("node").equals(KFLATMODULE)) + throw KEMException.criticalError( + "Unexpected node found in KAST Json term: " + data.getString("node")); + + String name = data.getString("name"); + + JsonArray jsonimports = data.getJsonArray("imports"); + Set imports = new HashSet<>(); + jsonimports + .getValuesAs(JsonObject.class) + .forEach( + i -> + imports.add( + FlatImport.apply(i.getString("name"), i.getBoolean("isPublic"), Att.empty()))); + + JsonArray sentences = data.getJsonArray("localSentences"); + Set localSentences = new HashSet<>(); + for (JsonObject j : sentences.getValuesAs(JsonObject.class)) { + localSentences.add(toSentence(j)); } - public static Definition toDefinition(JsonObject data) throws IOException { - if (! data.getString("node").equals(KDEFINITION)) - throw KEMException.criticalError("Unexpected node found in KAST Json term: " + data.getString("node")); - - String mainModuleName = data.getString("mainModule"); - JsonArray mods = data.getJsonArray("modules"); - List flatModules = new ArrayList<>(); - for (JsonObject m: mods.getValuesAs(JsonObject.class)) { - flatModules.add(toFlatModule(m)); + return new FlatModule( + name, immutable(imports), immutable(localSentences), toAtt(data.getJsonObject("att"))); + } + + /////////////////////////// + // Parsing Sentence Json // + /////////////////////////// + + public static Sentence toSentence(JsonObject data) { + switch (data.getString("node")) { + case KCONTEXT -> { + K body = toK(data.getJsonObject("body")); + K requires = toK(data.getJsonObject("requires")); + Att att = toAtt(data.getJsonObject("att")); + return new Context(body, requires, att); + } + case KRULE -> { + K body = toK(data.getJsonObject("body")); + K requires = toK(data.getJsonObject("requires")); + K ensures = toK(data.getJsonObject("ensures")); + Att att = toAtt(data.getJsonObject("att")); + return new Rule(body, requires, ensures, att); + } + case KCLAIM -> { + K body = toK(data.getJsonObject("body")); + K requires = toK(data.getJsonObject("requires")); + K ensures = toK(data.getJsonObject("ensures")); + Att att = toAtt(data.getJsonObject("att")); + return new Claim(body, requires, ensures, att); + } + case KSYNTAXPRIORITY -> { + JsonArray priorities = data.getJsonArray("priorities"); + Att att = toAtt(data.getJsonObject("att")); + List> syntaxPriorities = new ArrayList<>(); + priorities.getValuesAs(JsonArray.class).forEach(tags -> syntaxPriorities.add(toTags(tags))); + return new SyntaxPriority(immutable(syntaxPriorities), att); + } + case KSYNTAXASSOCIATIVITY -> { + String assocString = data.getString("assoc"); + Associativity assoc = + "Left".equals(assocString) + ? Associativity.Left + : "Right".equals(assocString) + ? Associativity.Right + : "NonAssoc".equals(assocString) + ? Associativity.NonAssoc + : Associativity.Unspecified; + scala.collection.Set tags = toTags(data.getJsonArray("tags")); + Att att = toAtt(data.getJsonObject("att")); + return new SyntaxAssociativity(assoc, tags, att); + } + case KCONFIGURATION -> { + K body = toK(data.getJsonObject("body")); + K ensures = toK(data.getJsonObject("ensures")); + Att att = toAtt(data.getJsonObject("att")); + return new Configuration(body, ensures, att); + } + case KSYNTAXSORT -> { + Sort sort = toSort(data.getJsonObject("sort")); + Att att = toAtt(data.getJsonObject("att")); + List params = new ArrayList<>(); + for (JsonObject s : data.getJsonArray("params").getValuesAs(JsonObject.class)) { + params.add(toSort(s)); } - - scala.collection.Set koreModules = FlatModule.toModules(immutable(flatModules), Set()); - return Constructors.Definition( - koreModules.find(x -> x.name().equals(mainModuleName)) - .getOrElse(() -> { throw new AssertionError("Could not find main module name " + mainModuleName + " when loading from JSON."); }), - koreModules, toAtt(data.getJsonObject("att"))); - } - -///////////////////////// -// Parsing Module Json // -///////////////////////// - - public static FlatModule toFlatModule(JsonObject data) throws IOException { - if (! data.getString("node").equals(KFLATMODULE)) - throw KEMException.criticalError("Unexpected node found in KAST Json term: " + data.getString("node")); - + return new SyntaxSort(immutable(params), sort, att); + } + case KSORTSYNONYM -> { + Sort newSort = toSort(data.getJsonObject("newSort")); + Sort oldSort = toSort(data.getJsonObject("oldSort")); + Att att = toAtt(data.getJsonObject("att")); + return new SortSynonym(newSort, oldSort, att); + } + case KSYNTAXLEXICAL -> { String name = data.getString("name"); - - JsonArray jsonimports = data.getJsonArray("imports"); - Set imports = new HashSet<>(); - jsonimports.getValuesAs(JsonObject.class).forEach(i -> imports.add(FlatImport.apply(i.getString("name"), i.getBoolean("isPublic"), Att.empty()))); - - JsonArray sentences = data.getJsonArray("localSentences"); - Set localSentences = new HashSet<>(); - for (JsonObject j: sentences.getValuesAs(JsonObject.class)) { - localSentences.add(toSentence(j)); - } - - return new FlatModule(name, immutable(imports), immutable(localSentences), toAtt(data.getJsonObject("att"))); - } - -/////////////////////////// -// Parsing Sentence Json // -/////////////////////////// - - public static Sentence toSentence(JsonObject data) { - switch (data.getString("node")) { - case KCONTEXT -> { - K body = toK(data.getJsonObject("body")); - K requires = toK(data.getJsonObject("requires")); - Att att = toAtt(data.getJsonObject("att")); - return new Context(body, requires, att); + String regex = data.getString("regex"); + Att att = toAtt(data.getJsonObject("att")); + return new SyntaxLexical(name, regex, att); + } + case KBUBBLE -> { + String sentenceType = data.getString("sentenceType"); + String contents = data.getString("contents"); + Att att = toAtt(data.getJsonObject("att")); + return new Bubble(sentenceType, contents, att); + } + case KPRODUCTION -> { + Option klabel = + Option.apply( + data.containsKey("klabel") ? toKLabel(data.getJsonObject("klabel")) : null); + Sort sort = toSort(data.getJsonObject("sort")); + Att att = toAtt(data.getJsonObject("att")); + + List pItems = new ArrayList<>(); + for (JsonObject pi : data.getJsonArray("productionItems").getValuesAs(JsonObject.class)) { + pItems.add(toProductionItem(pi)); } - case KRULE -> { - K body = toK(data.getJsonObject("body")); - K requires = toK(data.getJsonObject("requires")); - K ensures = toK(data.getJsonObject("ensures")); - Att att = toAtt(data.getJsonObject("att")); - return new Rule(body, requires, ensures, att); - } - case KCLAIM -> { - K body = toK(data.getJsonObject("body")); - K requires = toK(data.getJsonObject("requires")); - K ensures = toK(data.getJsonObject("ensures")); - Att att = toAtt(data.getJsonObject("att")); - return new Claim(body, requires, ensures, att); - } - case KSYNTAXPRIORITY -> { - JsonArray priorities = data.getJsonArray("priorities"); - Att att = toAtt(data.getJsonObject("att")); - List> syntaxPriorities = new ArrayList<>(); - priorities.getValuesAs(JsonArray.class).forEach(tags -> syntaxPriorities.add(toTags(tags))); - return new SyntaxPriority(immutable(syntaxPriorities), att); - } - case KSYNTAXASSOCIATIVITY -> { - String assocString = data.getString("assoc"); - Associativity assoc = "Left".equals(assocString) ? Associativity.Left - : "Right".equals(assocString) ? Associativity.Right - : "NonAssoc".equals(assocString) ? Associativity.NonAssoc - : Associativity.Unspecified; - scala.collection.Set tags = toTags(data.getJsonArray("tags")); - Att att = toAtt(data.getJsonObject("att")); - return new SyntaxAssociativity(assoc, tags, att); - } - case KCONFIGURATION -> { - K body = toK(data.getJsonObject("body")); - K ensures = toK(data.getJsonObject("ensures")); - Att att = toAtt(data.getJsonObject("att")); - return new Configuration(body, ensures, att); - } - case KSYNTAXSORT -> { - Sort sort = toSort(data.getJsonObject("sort")); - Att att = toAtt(data.getJsonObject("att")); - List params = new ArrayList<>(); - for (JsonObject s : data.getJsonArray("params").getValuesAs(JsonObject.class)) { - params.add(toSort(s)); - } - return new SyntaxSort(immutable(params), sort, att); - } - case KSORTSYNONYM -> { - Sort newSort = toSort(data.getJsonObject("newSort")); - Sort oldSort = toSort(data.getJsonObject("oldSort")); - Att att = toAtt(data.getJsonObject("att")); - return new SortSynonym(newSort, oldSort, att); - } - case KSYNTAXLEXICAL -> { - String name = data.getString("name"); - String regex = data.getString("regex"); - Att att = toAtt(data.getJsonObject("att")); - return new SyntaxLexical(name, regex, att); - } - case KBUBBLE -> { - String sentenceType = data.getString("sentenceType"); - String contents = data.getString("contents"); - Att att = toAtt(data.getJsonObject("att")); - return new Bubble(sentenceType, contents, att); - } - case KPRODUCTION -> { - Option klabel = Option.apply(data.containsKey("klabel") ? toKLabel(data.getJsonObject("klabel")) : null); - Sort sort = toSort(data.getJsonObject("sort")); - Att att = toAtt(data.getJsonObject("att")); - - List pItems = new ArrayList<>(); - for (JsonObject pi : data.getJsonArray("productionItems").getValuesAs(JsonObject.class)) { - pItems.add(toProductionItem(pi)); - } - List params = new ArrayList<>(); - for (JsonObject s : data.getJsonArray("params").getValuesAs(JsonObject.class)) { - params.add(toSort(s)); - } - return new Production(klabel, immutable(params), sort, immutable(pItems), att); - } - default -> throw KEMException.criticalError("Unexpected node found in KAST Json term: " + data.getString("node")); + List params = new ArrayList<>(); + for (JsonObject s : data.getJsonArray("params").getValuesAs(JsonObject.class)) { + params.add(toSort(s)); } + return new Production(klabel, immutable(params), sort, immutable(pItems), att); + } + default -> throw KEMException.criticalError( + "Unexpected node found in KAST Json term: " + data.getString("node")); } - - private static scala.collection.Set toTags(JsonArray data) { - Set tags = new HashSet<>(); - data.getValuesAs(JsonString.class).forEach(s -> tags.add(new Tag(s.getString()))); - return JavaConverters.asScalaSet(tags); + } + + private static scala.collection.Set toTags(JsonArray data) { + Set tags = new HashSet<>(); + data.getValuesAs(JsonString.class).forEach(s -> tags.add(new Tag(s.getString()))); + return JavaConverters.asScalaSet(tags); + } + + private static Sort toSort(JsonObject data) { + if (!data.getString("node").equals(KSORT)) + throw KEMException.criticalError( + "Unexpected node found in KAST Json term: " + data.getString("node")); + return Outer.parseSort(data.getString("name")); + } + + private static ProductionItem toProductionItem(JsonObject data) { + switch (data.getString("node")) { + case KNONTERMINAL -> { + Sort sort = toSort(data.getJsonObject("sort")); + Option name = + Option.apply(data.containsKey("name") ? data.getString("name") : null); + return new NonTerminal(sort, name); + } + case KREGEXTERMINAL -> { + String precedeRegex = data.getString("precedeRegex"); + String regex = data.getString("regex"); + String followRegex = data.getString("followRegex"); + return new RegexTerminal(precedeRegex, regex, followRegex); + } + case KTERMINAL -> { + String value = data.getString("value"); + return new Terminal(value); + } + default -> throw KEMException.criticalError( + "Unexpected node found in ProductionItem Json term: " + data.getString("node")); } - - private static Sort toSort(JsonObject data) { - if (! data.getString("node").equals(KSORT)) - throw KEMException.criticalError("Unexpected node found in KAST Json term: " + data.getString("node")); - return Outer.parseSort(data.getString("name")); + } + + ////////////////////// + // Parsing Att Json // + ////////////////////// + + public static Att toAtt(JsonObject data) { + if (!(data.getString("node").equals(KATT) && data.containsKey("att"))) + throw KEMException.criticalError( + "Unexpected node found in KAST Json term when unparsing KATT: " + data.getString("node")); + JsonObject attMap = data.getJsonObject("att"); + Att newAtt = Att.empty(); + for (String key : attMap.keySet()) { + if (key.equals(Location.class.getName())) { + JsonArray locarr = attMap.getJsonArray(Location.class.getName()); + newAtt = + newAtt.add( + Location.class, + Location(locarr.getInt(0), locarr.getInt(1), locarr.getInt(2), locarr.getInt(3))); + } else if (key.equals(Source.class.getName())) { + newAtt = newAtt.add(Source.class, Source.apply(attMap.getString(key))); + } else if (key.equals(Production.class.getName())) { + newAtt = newAtt.add(Production.class, (Production) toSentence(attMap.getJsonObject(key))); + } else if (key.equals(Sort.class.getName())) { + newAtt = newAtt.add(Sort.class, toSort(attMap.getJsonObject(key))); + } else if (key.equals(Att.BRACKET_LABEL().key())) { + newAtt = newAtt.add(Att.BRACKET_LABEL(), KLabel.class, toKLabel(attMap.getJsonObject(key))); + } else if (key.equals(Att.PREDICATE().key())) { + newAtt = newAtt.add(Att.PREDICATE(), Sort.class, toSort(attMap.getJsonObject(key))); + } else if (key.equals(Att.CELL_OPT_ABSENT().key())) { + newAtt = newAtt.add(Att.CELL_OPT_ABSENT(), Sort.class, toSort(attMap.getJsonObject(key))); + } else if (key.equals(Att.CELL_FRAGMENT().key())) { + newAtt = newAtt.add(Att.CELL_FRAGMENT(), Sort.class, toSort(attMap.getJsonObject(key))); + } else if (key.equals(Att.SORT_PARAMS().key())) { + newAtt = newAtt.add(Att.SORT_PARAMS(), Sort.class, toSort(attMap.getJsonObject(key))); + } else { + Att.Key attKey = + Att.getBuiltinKeyOptional(key) + // The JSON is emitted after we may have added internal attributes + .or(() -> Att.getInternalKeyOptional(key)) + .orElseThrow( + () -> + KEMException.criticalError( + "Unrecognized attribute " + + key + + " found in KAST Json term when unparsing KATT: " + + attMap + + "\n" + + "Hint: User-defined groups can be added with the group(_)" + + " attribute.")); + newAtt = newAtt.add(attKey, attMap.getString(key)); + } } - - private static ProductionItem toProductionItem(JsonObject data) { - switch (data.getString("node")) { - case KNONTERMINAL -> { - Sort sort = toSort(data.getJsonObject("sort")); - Option name = Option.apply(data.containsKey("name") ? data.getString("name") : null); - return new NonTerminal(sort, name); - } - case KREGEXTERMINAL -> { - String precedeRegex = data.getString("precedeRegex"); - String regex = data.getString("regex"); - String followRegex = data.getString("followRegex"); - return new RegexTerminal(precedeRegex, regex, followRegex); - } - case KTERMINAL -> { - String value = data.getString("value"); - return new Terminal(value); - } - default -> throw KEMException.criticalError("Unexpected node found in ProductionItem Json term: " + data.getString("node")); - } + Either newAttOrError = newAtt.withGroupAttAsUserGroups(); + if (newAttOrError.isLeft()) { + throw KEMException.criticalError( + newAttOrError.left().get() + + "\nOccurred in KAST Json term when unparsing KATT: " + + attMap); } - -////////////////////// -// Parsing Att Json // -////////////////////// - - public static Att toAtt(JsonObject data) { - if (! (data.getString("node").equals(KATT) && data.containsKey("att"))) - throw KEMException.criticalError("Unexpected node found in KAST Json term when unparsing KATT: " + data.getString("node")); - JsonObject attMap = data.getJsonObject("att"); - Att newAtt = Att.empty(); - for (String key: attMap.keySet()) { - if (key.equals(Location.class.getName())) { - JsonArray locarr = attMap.getJsonArray(Location.class.getName()); - newAtt = newAtt.add(Location.class, Location(locarr.getInt(0), locarr.getInt(1), locarr.getInt(2), locarr.getInt(3))); - } else if (key.equals(Source.class.getName())) { - newAtt = newAtt.add(Source.class, Source.apply(attMap.getString(key))); - } else if (key.equals(Production.class.getName())) { - newAtt = newAtt.add(Production.class, (Production) toSentence(attMap.getJsonObject(key))); - } else if (key.equals(Sort.class.getName())) { - newAtt = newAtt.add(Sort.class, toSort(attMap.getJsonObject(key))); - } else if (key.equals(Att.BRACKET_LABEL().key())) { - newAtt = newAtt.add(Att.BRACKET_LABEL(), KLabel.class, toKLabel(attMap.getJsonObject(key))); - } else if (key.equals(Att.PREDICATE().key())) { - newAtt = newAtt.add(Att.PREDICATE(), Sort.class, toSort(attMap.getJsonObject(key))); - } else if (key.equals(Att.CELL_OPT_ABSENT().key())) { - newAtt = newAtt.add(Att.CELL_OPT_ABSENT(), Sort.class, toSort(attMap.getJsonObject(key))); - } else if (key.equals(Att.CELL_FRAGMENT().key())) { - newAtt = newAtt.add(Att.CELL_FRAGMENT(), Sort.class, toSort(attMap.getJsonObject(key))); - } else if (key.equals(Att.SORT_PARAMS().key())) { - newAtt = newAtt.add(Att.SORT_PARAMS(), Sort.class, toSort(attMap.getJsonObject(key))); - } else { - Att.Key attKey = - Att.getBuiltinKeyOptional(key) - // The JSON is emitted after we may have added internal attributes - .or(() -> Att.getInternalKeyOptional(key)) - .orElseThrow(() -> - KEMException.criticalError("Unrecognized attribute " + key + - " found in KAST Json term when unparsing KATT: " + - attMap + - "\nHint: User-defined groups can be added with the group(_) attribute.") - ); - newAtt = newAtt.add(attKey, attMap.getString(key)); - } - } - Either newAttOrError = newAtt.withGroupAttAsUserGroups(); - if (newAttOrError.isLeft()) { - throw KEMException.criticalError(newAttOrError.left().get() + - "\nOccurred in KAST Json term when unparsing KATT: " + attMap); - } - return newAttOrError.right().get(); + return newAttOrError.right().get(); + } + + //////////////////// + // Parsing K Json // + //////////////////// + + public static K parse(byte[] data) { + return parse(new String(data, StandardCharsets.UTF_8)); + } + + public static K parse(String data) { + JsonReader reader = Json.createReader(new StringReader(data)); + return parseJson(reader.readObject()); + } + + public static K parseJson(JsonObject data) { + if (!(data.containsKey("format") && data.containsKey("version") && data.containsKey("term"))) { + throw KEMException.criticalError( + "Must have `format`, `version`, and `term` fields in serialized Json!"); } - -//////////////////// -// Parsing K Json // -//////////////////// - - public static K parse(byte[] data) { - return parse(new String(data, StandardCharsets.UTF_8)); + if (!data.getString("format").equals("KAST")) { + throw KEMException.criticalError( + "Only can deserialize 'KAST' format Json! Found: " + data.getString("format")); } - - public static K parse(String data) { - JsonReader reader = Json.createReader(new StringReader(data)); - return parseJson(reader.readObject()); + if (data.getInt("version") != ToJson.version) { + throw KEMException.criticalError( + "Only can deserialize KAST version '" + + ToJson.version + + "'! Found: " + + data.getInt("version")); } - - public static K parseJson(JsonObject data) { - if (! (data.containsKey("format") && data.containsKey("version") && data.containsKey("term"))) { - throw KEMException.criticalError("Must have `format`, `version`, and `term` fields in serialized Json!"); + return toK(data.getJsonObject("term")); + } + + private static K toK(JsonObject data) { + KLabel klabel; + + switch (data.getString("node")) { + case KTOKEN: + return KToken(data.getString("token"), toSort(data.getJsonObject("sort"))); + + case KAPPLY: + int arity = data.getInt("arity"); + K[] args = toKs(arity, data.getJsonArray("args")); + klabel = toKLabel(data.getJsonObject("label")); + return KApply(klabel, args); + + case KSEQUENCE: + int seqLen = data.getInt("arity"); + K[] items = toKs(seqLen, data.getJsonArray("items")); + return KSequence(items); + + case KVARIABLE: + Att varAtt = Att.empty(); + if (data.containsKey("sort")) { + varAtt = varAtt.add(Sort.class, toSort(data.getJsonObject("sort"))); } - if (! data.getString("format").equals("KAST")) { - throw KEMException.criticalError("Only can deserialize 'KAST' format Json! Found: " + data.getString("format")); - } - if (data.getInt("version") != ToJson.version) { - throw KEMException.criticalError("Only can deserialize KAST version '" + ToJson.version + "'! Found: " + data.getInt("version")); - } - return toK(data.getJsonObject("term")); - } - - private static K toK(JsonObject data) { - KLabel klabel; - - switch (data.getString("node")) { + return KVariable(data.getString("name"), varAtt); - case KTOKEN: - return KToken(data.getString("token"), toSort(data.getJsonObject("sort"))); + case KREWRITE: + K lhs = toK(data.getJsonObject("lhs")); + K rhs = toK(data.getJsonObject("rhs")); + return KRewrite(lhs, rhs, Att.empty()); - case KAPPLY: - int arity = data.getInt("arity"); - K[] args = toKs(arity, data.getJsonArray("args")); - klabel = toKLabel(data.getJsonObject("label")); - return KApply(klabel, args); + case KAS: + K pattern = toK(data.getJsonObject("pattern")); + K alias = toK(data.getJsonObject("alias")); + return KORE.KAs(pattern, alias, Att.empty()); - case KSEQUENCE: - int seqLen = data.getInt("arity"); - K[] items = toKs(seqLen, data.getJsonArray("items")); - return KSequence(items); + case INJECTEDKLABEL: + klabel = toKLabel(data.getJsonObject("label")); + return InjectedKLabel(klabel); - case KVARIABLE: - Att varAtt = Att.empty(); - if (data.containsKey("sort")) { - varAtt = varAtt.add(Sort.class, toSort(data.getJsonObject("sort"))); - } - return KVariable(data.getString("name"), varAtt); - - case KREWRITE: - K lhs = toK(data.getJsonObject("lhs")); - K rhs = toK(data.getJsonObject("rhs")); - return KRewrite(lhs, rhs, Att.empty()); - - case KAS: - K pattern = toK(data.getJsonObject("pattern")); - K alias = toK(data.getJsonObject("alias")); - return KORE.KAs(pattern, alias, Att.empty()); - - case INJECTEDKLABEL: - klabel = toKLabel(data.getJsonObject("label")); - return InjectedKLabel(klabel); - - default: - throw KEMException.criticalError("Unexpected node found in KAST Json term: " + data.getString("node")); - } + default: + throw KEMException.criticalError( + "Unexpected node found in KAST Json term: " + data.getString("node")); } + } - private static KLabel toKLabel(JsonObject data) { - JsonArray jparams = data.getJsonArray("params"); - List params = new ArrayList<>(); - for (JsonValue p : jparams) { - params.add(toSort((JsonObject)p)); - } - Sort[] sarray = params.toArray(new Sort[0]); - return KLabel(data.getString("name"), sarray); + private static KLabel toKLabel(JsonObject data) { + JsonArray jparams = data.getJsonArray("params"); + List params = new ArrayList<>(); + for (JsonValue p : jparams) { + params.add(toSort((JsonObject) p)); } - - private static K[] toKs(int arity, JsonArray data) { - K[] items = new K[arity]; - for (int i = 0; i < arity; i++) { - items[i] = toK(data.getValuesAs(JsonObject.class).get(i)); - } - return items; + Sort[] sarray = params.toArray(new Sort[0]); + return KLabel(data.getString("name"), sarray); + } + + private static K[] toKs(int arity, JsonArray data) { + K[] items = new K[arity]; + for (int i = 0; i < arity; i++) { + items[i] = toK(data.getValuesAs(JsonObject.class).get(i)); } + return items; + } } diff --git a/kernel/src/main/java/org/kframework/parser/outer/ExtractFencedKCodeFromMarkdown.java b/kernel/src/main/java/org/kframework/parser/outer/ExtractFencedKCodeFromMarkdown.java index f9b011dabda..adec5076a3f 100644 --- a/kernel/src/main/java/org/kframework/parser/outer/ExtractFencedKCodeFromMarkdown.java +++ b/kernel/src/main/java/org/kframework/parser/outer/ExtractFencedKCodeFromMarkdown.java @@ -7,114 +7,111 @@ import com.vladsch.flexmark.util.ast.NodeVisitor; import com.vladsch.flexmark.util.ast.VisitHandler; import com.vladsch.flexmark.util.data.MutableDataSet; +import java.util.HashSet; +import java.util.Set; import org.jetbrains.annotations.NotNull; import org.kframework.attributes.Source; import org.kframework.parser.markdown.ASTExpressionStart; import org.kframework.parser.markdown.TagSelector; -import org.kframework.utils.errorsystem.KEMException; import org.kframework.utils.errorsystem.KExceptionManager; -import java.util.HashSet; -import java.util.Set; - /** * Takes a Markdown file, and extract annotated code-blocks according to the mdSelector expression. * The inner code is kept exactly in place so error reporting can be precise. + * * @author Radu Mereuta */ public class ExtractFencedKCodeFromMarkdown { - private final KExceptionManager kem; + private final KExceptionManager kem; + private final ASTExpressionStart mdSelector; + + public ExtractFencedKCodeFromMarkdown(KExceptionManager kem, String mdSelector) { + this.kem = kem; + this.mdSelector = TagSelector.parseSelectorExp(mdSelector); + } + + public String extract(String mdText, Source source) { + KCodeExtractor extractor = new KCodeExtractor(mdText, mdSelector, source, kem); + return extractor.getKCode(); + } + + private static class KCodeExtractor { private final ASTExpressionStart mdSelector; + private final String mdText; + private final Source source; + private final KExceptionManager kem; + int lastOffset; + // gather the k code in a single string without markdown + StringBuilder kCodeSb; + // build a copy with only whitespaces so location information for code-block parsing matches + StringBuilder blankSb; - public ExtractFencedKCodeFromMarkdown(KExceptionManager kem, String mdSelector) { - this.kem = kem; - this.mdSelector = TagSelector.parseSelectorExp(mdSelector); + public KCodeExtractor( + String mdText, ASTExpressionStart mdSelector, Source source, KExceptionManager kem) { + this.mdText = mdText; + this.mdSelector = mdSelector; + this.source = source; + this.kem = kem; } - public String extract(String mdText, Source source) { - KCodeExtractor extractor = new KCodeExtractor(mdText, mdSelector, source, kem); - return extractor.getKCode(); - } + NodeVisitor visitor = new NodeVisitor(new VisitHandler<>(FencedCodeBlock.class, this::visit)); - private static class KCodeExtractor { - private final ASTExpressionStart mdSelector; - private final String mdText; - private final Source source; - private final KExceptionManager kem; - int lastOffset; - // gather the k code in a single string without markdown - StringBuilder kCodeSb; - // build a copy with only whitespaces so location information for code-block parsing matches - StringBuilder blankSb; - public KCodeExtractor(String mdText, ASTExpressionStart mdSelector, Source source, KExceptionManager kem) { - this.mdText = mdText; - this.mdSelector = mdSelector; - this.source = source; - this.kem = kem; + public void visit(FencedCodeBlock block) { + // copy up to where the code-block info starts + while (lastOffset < block.getInfo().getStartOffset()) { + if (Character.isWhitespace(mdText.charAt(lastOffset))) { + kCodeSb.append(mdText.charAt(lastOffset)); + blankSb.append(mdText.charAt(lastOffset)); + } else blankSb.append(" "); + lastOffset++; + } + String cbStr = block.getInfo().toString(); + Set tags = new HashSet<>(); + tags = TagSelector.parseTags(blankSb + cbStr, source, kem); + // interested only in code blocks marked as valid by the mdSelector expression + if (TagSelector.eval(mdSelector, tags)) { + // navigate from previous offset to the current one and + // make everything whitespace to preserve location info + long offset = block.getContentChars().getStartOffset(); + while (lastOffset < offset) { + if (Character.isWhitespace(mdText.charAt(lastOffset))) { + kCodeSb.append(mdText.charAt(lastOffset)); + blankSb.append(mdText.charAt(lastOffset)); + } else blankSb.append(" "); + lastOffset++; } - - NodeVisitor visitor = new NodeVisitor( - new VisitHandler<>(FencedCodeBlock.class, this::visit) - ); - - public void visit(FencedCodeBlock block) { - // copy up to where the code-block info starts - while (lastOffset < block.getInfo().getStartOffset()) { - if (Character.isWhitespace(mdText.charAt(lastOffset))) { - kCodeSb.append(mdText.charAt(lastOffset)); - blankSb.append(mdText.charAt(lastOffset)); - } else - blankSb.append(" "); - lastOffset++; - } - String cbStr = block.getInfo().toString(); - Set tags = new HashSet<>(); - tags = TagSelector.parseTags(blankSb + cbStr, source, kem); - // interested only in code blocks marked as valid by the mdSelector expression - if (TagSelector.eval(mdSelector, tags)) { - // navigate from previous offset to the current one and - // make everything whitespace to preserve location info - long offset = block.getContentChars().getStartOffset(); - while (lastOffset < offset) { - if (Character.isWhitespace(mdText.charAt(lastOffset))) { - kCodeSb.append(mdText.charAt(lastOffset)); - blankSb.append(mdText.charAt(lastOffset)); - } else - blankSb.append(" "); - lastOffset++; - } - // copy each character because block.getContentChars() removes indentation and can offset location info - offset = block.getContentChars().getEndOffset(); - while (lastOffset < offset) { - kCodeSb.append(mdText.charAt(lastOffset)); - if (Character.isWhitespace(mdText.charAt(lastOffset))) - blankSb.append(mdText.charAt(lastOffset)); - else - blankSb.append(" "); - lastOffset++; - } - } - // Descending into children - visitor.visitChildren(block); + // copy each character because block.getContentChars() removes indentation and can offset + // location info + offset = block.getContentChars().getEndOffset(); + while (lastOffset < offset) { + kCodeSb.append(mdText.charAt(lastOffset)); + if (Character.isWhitespace(mdText.charAt(lastOffset))) + blankSb.append(mdText.charAt(lastOffset)); + else blankSb.append(" "); + lastOffset++; } + } + // Descending into children + visitor.visitChildren(block); + } - String getKCode() { - MutableDataSet options = new MutableDataSet(); - Parser parser = Parser.builder(options).build(); - @NotNull Document doc = parser.parse(mdText); - lastOffset = 0; - kCodeSb = new StringBuilder(); - blankSb = new StringBuilder(); - visitor.visit(doc); - // make everything whitespace to preserve location info for end of file errors - while (lastOffset < mdText.length()) { - if (Character.isWhitespace(mdText.charAt(lastOffset))) - kCodeSb.append(mdText.charAt(lastOffset)); - lastOffset++; - } + String getKCode() { + MutableDataSet options = new MutableDataSet(); + Parser parser = Parser.builder(options).build(); + @NotNull Document doc = parser.parse(mdText); + lastOffset = 0; + kCodeSb = new StringBuilder(); + blankSb = new StringBuilder(); + visitor.visit(doc); + // make everything whitespace to preserve location info for end of file errors + while (lastOffset < mdText.length()) { + if (Character.isWhitespace(mdText.charAt(lastOffset))) + kCodeSb.append(mdText.charAt(lastOffset)); + lastOffset++; + } - return kCodeSb.toString(); - } + return kCodeSb.toString(); } + } } diff --git a/kernel/src/main/java/org/kframework/unparser/AddBrackets.java b/kernel/src/main/java/org/kframework/unparser/AddBrackets.java index 4dcfa92dada..a8eee37bbb8 100644 --- a/kernel/src/main/java/org/kframework/unparser/AddBrackets.java +++ b/kernel/src/main/java/org/kframework/unparser/AddBrackets.java @@ -1,6 +1,12 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.unparser; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; import org.kframework.attributes.Att; import org.kframework.builtin.Sorts; import org.kframework.definition.Module; @@ -16,196 +22,209 @@ import org.pcollections.ConsPStack; import scala.Tuple2; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; - -import static org.kframework.Collections.*; -import static org.kframework.definition.Constructors.*; - /** - * Implements the naive algorithm to add brackets in order to disambiguate an unparsed AST. This algorithm executes - * in linear time in the size of the term, but does not correctly solve grammars in which multiple productions share - * the same terminals in such a way as to cause ambiguities that cannot be resolved using priorities and associativities. - * As such, we use this algorithm in krun in output --pretty, but it is insufficient for --output sound. + * Implements the naive algorithm to add brackets in order to disambiguate an unparsed AST. This + * algorithm executes in linear time in the size of the term, but does not correctly solve grammars + * in which multiple productions share the same terminals in such a way as to cause ambiguities that + * cannot be resolved using priorities and associativities. As such, we use this algorithm in krun + * in output --pretty, but it is insufficient for --output sound. */ public record AddBrackets(Module m) { - public ProductionReference addBrackets(ProductionReference t) { - return addBrackets(t, null, null); - } + public ProductionReference addBrackets(ProductionReference t) { + return addBrackets(t, null, null); + } - public ProductionReference addBrackets(ProductionReference t, ProductionReference previousLeftCapture, ProductionReference previousRightCapture) { - if (t instanceof Constant) { - return t; - } - TermCons outer = (TermCons)t; - List newItems = new ArrayList<>(); - for (Term t2 : outer.items()) { - ProductionReference inner = (ProductionReference) t2; - ProductionReference leftCapture = getLeftCapture(previousLeftCapture, outer, inner); - ProductionReference rightCapture = getRightCapture(previousRightCapture, outer, inner); - ProductionReference newInner = addBrackets(inner, outer, leftCapture, rightCapture); - newInner = addBrackets(newInner, leftCapture, rightCapture); - newItems.add(newInner); - } - return TermCons.apply(ConsPStack.from(newItems), outer.production()); - } - - public ProductionReference addBrackets(ProductionReference inner, TermCons outer, ProductionReference leftCapture, ProductionReference rightCapture) { - if (requiresBracketWithSimpleAlgorithm(outer, leftCapture, rightCapture, inner)) { - int position = getPosition(inner, outer); - Sort outerSort = ((NonTerminal)outer.production().items().apply(position)).sort(); - Sort innerSort = inner.production().sort(); - for (Tuple2> sort : iterable(m.bracketProductionsFor())) { - boolean isCorrectOuterSort = m.subsorts().lessThanEq(sort._1(), outerSort); - if (isCorrectOuterSort) { - for (Production p : mutable(sort._2())) { - boolean isCorrectInnerSort = stream(p.items()) - .filter(i -> i instanceof NonTerminal) - .map(i -> (NonTerminal) i) - .map(NonTerminal::sort) - .anyMatch(s -> m.subsorts().lessThanEq(innerSort, s)); - if (isCorrectInnerSort) { - return TermCons.apply(ConsPStack.singleton(inner), p); - } - } - } - } - return TermCons.apply(ConsPStack.singleton(inner), Production(Seq(), Sorts.KBott(), Seq(NonTerminal(Sorts.K())))); - } - return inner; - } - - boolean requiresBracketWithSimpleAlgorithm(ProductionReference outer, ProductionReference leftCapture, ProductionReference rightCapture, ProductionReference inner) { - boolean priority = isPriorityWrong(outer, inner, getPosition(inner, outer)); - boolean inversePriority; - EnumSet fixity = getFixity(inner, outer); - EnumSet innerFixity = getFixity(inner); - if (inner.production().klabel().equals(outer.production().klabel()) && - inner.production().klabel().isDefined() && - m.attributesFor().apply(inner.production().klabel().get().head()).contains(Att.ASSOC())) - return false; - if (inner instanceof Constant) - return false; - if (fixity.size() == 0) - return false; - if (priority) - return true; - if (inner.production().isSyntacticSubsort()) - return false; - - if (innerFixity.contains(Fixity.BARE_RIGHT) && rightCapture != null) { - inversePriority = isPriorityWrong(inner, rightCapture, inner.production().items().size() - 1); - EnumSet rightCaptureFixity = getFixity(rightCapture); - if (!inversePriority && rightCaptureFixity.contains(Fixity.BARE_LEFT)) { - return true; + public ProductionReference addBrackets( + ProductionReference t, + ProductionReference previousLeftCapture, + ProductionReference previousRightCapture) { + if (t instanceof Constant) { + return t; + } + TermCons outer = (TermCons) t; + List newItems = new ArrayList<>(); + for (Term t2 : outer.items()) { + ProductionReference inner = (ProductionReference) t2; + ProductionReference leftCapture = getLeftCapture(previousLeftCapture, outer, inner); + ProductionReference rightCapture = getRightCapture(previousRightCapture, outer, inner); + ProductionReference newInner = addBrackets(inner, outer, leftCapture, rightCapture); + newInner = addBrackets(newInner, leftCapture, rightCapture); + newItems.add(newInner); + } + return TermCons.apply(ConsPStack.from(newItems), outer.production()); + } + + public ProductionReference addBrackets( + ProductionReference inner, + TermCons outer, + ProductionReference leftCapture, + ProductionReference rightCapture) { + if (requiresBracketWithSimpleAlgorithm(outer, leftCapture, rightCapture, inner)) { + int position = getPosition(inner, outer); + Sort outerSort = ((NonTerminal) outer.production().items().apply(position)).sort(); + Sort innerSort = inner.production().sort(); + for (Tuple2> sort : + iterable(m.bracketProductionsFor())) { + boolean isCorrectOuterSort = m.subsorts().lessThanEq(sort._1(), outerSort); + if (isCorrectOuterSort) { + for (Production p : mutable(sort._2())) { + boolean isCorrectInnerSort = + stream(p.items()) + .filter(i -> i instanceof NonTerminal) + .map(i -> (NonTerminal) i) + .map(NonTerminal::sort) + .anyMatch(s -> m.subsorts().lessThanEq(innerSort, s)); + if (isCorrectInnerSort) { + return TermCons.apply(ConsPStack.singleton(inner), p); } + } } - if (innerFixity.contains(Fixity.BARE_LEFT) && leftCapture != null) { - inversePriority = isPriorityWrong(inner, leftCapture, 0); - EnumSet leftCaptureFixity = getFixity(leftCapture); - return !inversePriority && leftCaptureFixity.contains(Fixity.BARE_RIGHT); - } - return false; + } + return TermCons.apply( + ConsPStack.singleton(inner), + Production(Seq(), Sorts.KBott(), Seq(NonTerminal(Sorts.K())))); } - - private boolean isPriorityWrong(ProductionReference outer, ProductionReference inner, int position) { - if (outer.production().klabel().isEmpty() || inner.production().klabel().isEmpty()) { - return false; - } - Tag parentLabel = new Tag(outer.production().klabel().get().name()); - Tag localLabel = new Tag(inner.production().klabel().get().name()); - if (!m.subsorts().lessThanEq(inner.production().sort(), ((NonTerminal)outer.production().items().apply(position)).sort())) { - return true; - } - if (m.priorities().lessThan(parentLabel, localLabel)) { - return true; - } - if (m.leftAssoc().contains(new Tuple2<>(parentLabel, localLabel)) && position == outer.production().items().size() - 1) { - return true; - } - return m.rightAssoc().contains(new Tuple2<>(parentLabel, localLabel)) && position == 0; + return inner; + } + + boolean requiresBracketWithSimpleAlgorithm( + ProductionReference outer, + ProductionReference leftCapture, + ProductionReference rightCapture, + ProductionReference inner) { + boolean priority = isPriorityWrong(outer, inner, getPosition(inner, outer)); + boolean inversePriority; + EnumSet fixity = getFixity(inner, outer); + EnumSet innerFixity = getFixity(inner); + if (inner.production().klabel().equals(outer.production().klabel()) + && inner.production().klabel().isDefined() + && m.attributesFor().apply(inner.production().klabel().get().head()).contains(Att.ASSOC())) + return false; + if (inner instanceof Constant) return false; + if (fixity.size() == 0) return false; + if (priority) return true; + if (inner.production().isSyntacticSubsort()) return false; + + if (innerFixity.contains(Fixity.BARE_RIGHT) && rightCapture != null) { + inversePriority = isPriorityWrong(inner, rightCapture, inner.production().items().size() - 1); + EnumSet rightCaptureFixity = getFixity(rightCapture); + if (!inversePriority && rightCaptureFixity.contains(Fixity.BARE_LEFT)) { + return true; + } } - - private enum Fixity { - BARE_LEFT, - BARE_RIGHT + if (innerFixity.contains(Fixity.BARE_LEFT) && leftCapture != null) { + inversePriority = isPriorityWrong(inner, leftCapture, 0); + EnumSet leftCaptureFixity = getFixity(leftCapture); + return !inversePriority && leftCaptureFixity.contains(Fixity.BARE_RIGHT); } + return false; + } - ProductionReference getLeftCapture(ProductionReference previousLeftCapture, ProductionReference outer, ProductionReference inner) { - EnumSet fixity = getFixity(outer); - int position = getPosition(inner, outer); - if (position == 0 && fixity.contains(Fixity.BARE_LEFT)) { - return previousLeftCapture; - } else { - return outer; - } + private boolean isPriorityWrong( + ProductionReference outer, ProductionReference inner, int position) { + if (outer.production().klabel().isEmpty() || inner.production().klabel().isEmpty()) { + return false; } - - ProductionReference getRightCapture(ProductionReference previousRightCapture, ProductionReference outer, ProductionReference inner) { - EnumSet fixity = getFixity(outer); - int position = getPosition(inner, outer); - if (position == outer.production().items().size() - 1 && fixity.contains(Fixity.BARE_RIGHT)) { - return previousRightCapture; - } else { - return outer; - } + Tag parentLabel = new Tag(outer.production().klabel().get().name()); + Tag localLabel = new Tag(inner.production().klabel().get().name()); + if (!m.subsorts() + .lessThanEq( + inner.production().sort(), + ((NonTerminal) outer.production().items().apply(position)).sort())) { + return true; + } + if (m.priorities().lessThan(parentLabel, localLabel)) { + return true; + } + if (m.leftAssoc().contains(new Tuple2<>(parentLabel, localLabel)) + && position == outer.production().items().size() - 1) { + return true; + } + return m.rightAssoc().contains(new Tuple2<>(parentLabel, localLabel)) && position == 0; + } + + private enum Fixity { + BARE_LEFT, + BARE_RIGHT + } + + ProductionReference getLeftCapture( + ProductionReference previousLeftCapture, + ProductionReference outer, + ProductionReference inner) { + EnumSet fixity = getFixity(outer); + int position = getPosition(inner, outer); + if (position == 0 && fixity.contains(Fixity.BARE_LEFT)) { + return previousLeftCapture; + } else { + return outer; + } + } + + ProductionReference getRightCapture( + ProductionReference previousRightCapture, + ProductionReference outer, + ProductionReference inner) { + EnumSet fixity = getFixity(outer); + int position = getPosition(inner, outer); + if (position == outer.production().items().size() - 1 && fixity.contains(Fixity.BARE_RIGHT)) { + return previousRightCapture; + } else { + return outer; } + } - private EnumSet getFixity(ProductionReference t) { - Production p = t.production(); - EnumSet set = EnumSet.noneOf(Fixity.class); - if (t instanceof Constant) { - return set; - } - if (p.items().apply(0) instanceof NonTerminal) - set.add(Fixity.BARE_LEFT); - if (p.items().apply(p.items().size() - 1) instanceof NonTerminal) - set.add(Fixity.BARE_RIGHT); - return set; - } - - private EnumSet getFixity(ProductionReference inner, ProductionReference outer) { - assert outer instanceof TermCons; - TermCons tc = (TermCons)outer; - int i; - for (i = 0; i < tc.items().size(); i++) { - if (tc.get(i) == inner) - break; - } - Production p = tc.production(); - EnumSet set = EnumSet.noneOf(Fixity.class); - int position = getPosition(inner, outer); - if (!hasTerminalAtIdx(p, position+1)) { - set.add(Fixity.BARE_RIGHT); - } - if (!hasTerminalAtIdx(p, position-1)) { - set.add(Fixity.BARE_LEFT); - } - return set; + private EnumSet getFixity(ProductionReference t) { + Production p = t.production(); + EnumSet set = EnumSet.noneOf(Fixity.class); + if (t instanceof Constant) { + return set; + } + if (p.items().apply(0) instanceof NonTerminal) set.add(Fixity.BARE_LEFT); + if (p.items().apply(p.items().size() - 1) instanceof NonTerminal) set.add(Fixity.BARE_RIGHT); + return set; + } + + private EnumSet getFixity(ProductionReference inner, ProductionReference outer) { + assert outer instanceof TermCons; + TermCons tc = (TermCons) outer; + int i; + for (i = 0; i < tc.items().size(); i++) { + if (tc.get(i) == inner) break; + } + Production p = tc.production(); + EnumSet set = EnumSet.noneOf(Fixity.class); + int position = getPosition(inner, outer); + if (!hasTerminalAtIdx(p, position + 1)) { + set.add(Fixity.BARE_RIGHT); } + if (!hasTerminalAtIdx(p, position - 1)) { + set.add(Fixity.BARE_LEFT); + } + return set; + } - boolean hasTerminalAtIdx(Production p, int position) { - if (position < 0 || position >= p.items().size()) { - return false; - } - return p.items().apply(position) instanceof TerminalLike; - } - - private int getPosition(ProductionReference inner, ProductionReference outer) { - EnumSet set = EnumSet.noneOf(Fixity.class); - assert outer instanceof TermCons; - TermCons tc = (TermCons)outer; - Production p = tc.production(); - for (int i = 0, j = 0; i < p.items().size(); i++) { - if (p.items().apply(i) instanceof NonTerminal) { - if (tc.get(j) == inner) { - return i; - } - j++; - } - } - throw new AssertionError(); + boolean hasTerminalAtIdx(Production p, int position) { + if (position < 0 || position >= p.items().size()) { + return false; + } + return p.items().apply(position) instanceof TerminalLike; + } + + private int getPosition(ProductionReference inner, ProductionReference outer) { + EnumSet set = EnumSet.noneOf(Fixity.class); + assert outer instanceof TermCons; + TermCons tc = (TermCons) outer; + Production p = tc.production(); + for (int i = 0, j = 0; i < p.items().size(); i++) { + if (p.items().apply(i) instanceof NonTerminal) { + if (tc.get(j) == inner) { + return i; + } + j++; + } } + throw new AssertionError(); + } } diff --git a/kernel/src/main/java/org/kframework/unparser/ColorSetting.java b/kernel/src/main/java/org/kframework/unparser/ColorSetting.java index 11aa53687c0..c3c752a89ff 100644 --- a/kernel/src/main/java/org/kframework/unparser/ColorSetting.java +++ b/kernel/src/main/java/org/kframework/unparser/ColorSetting.java @@ -2,9 +2,10 @@ package org.kframework.unparser; /** - * @author Denis Bogdanas - * Date: 10/9/13 + * @author Denis Bogdanas Date: 10/9/13 */ public enum ColorSetting { - OFF, ON, EXTENDED + OFF, + ON, + EXTENDED } diff --git a/kernel/src/main/java/org/kframework/unparser/Formatter.java b/kernel/src/main/java/org/kframework/unparser/Formatter.java index 8b65a020851..d56babf66ef 100644 --- a/kernel/src/main/java/org/kframework/unparser/Formatter.java +++ b/kernel/src/main/java/org/kframework/unparser/Formatter.java @@ -2,6 +2,9 @@ package org.kframework.unparser; +import static org.fusesource.jansi.Ansi.*; +import static org.kframework.Collections.*; + import org.kframework.attributes.Att; import org.kframework.definition.NonTerminal; import org.kframework.definition.Production; @@ -10,135 +13,158 @@ import org.kframework.parser.Constant; import org.kframework.parser.Term; import org.kframework.parser.TermCons; -import org.kframework.utils.errorsystem.KEMException; import org.kframework.utils.ColorUtil; - -import static org.kframework.Collections.*; -import static org.fusesource.jansi.Ansi.*; +import org.kframework.utils.errorsystem.KEMException; public class Formatter { - public static String format(Term term) { - Indenter indenter = new Indenter(2); - format(term, indenter, ColorSetting.OFF); - return indenter.toString(); - } + public static String format(Term term) { + Indenter indenter = new Indenter(2); + format(term, indenter, ColorSetting.OFF); + return indenter.toString(); + } - public static String format(Term term, ColorSetting colorize) { - Indenter indenter = new Indenter(2); - format(term, indenter, colorize); - return indenter.toString(); - } + public static String format(Term term, ColorSetting colorize) { + Indenter indenter = new Indenter(2); + format(term, indenter, colorize); + return indenter.toString(); + } - public static void format(Term term, Indenter indenter, ColorSetting colorize) { - int indent = 0; - int localColor = 0; - if (term instanceof Constant c) { - color(indenter, c.production(), 0, colorize); - indenter.append(c.value()); - resetColor(indenter, c.production(), colorize); - } else if (term instanceof TermCons tc) { - String format = tc.production().att().getOptional(Att.FORMAT()).orElse(defaultFormat(tc.production().items().size())); - for (int i = 0; i < format.length(); i++) { - char c = format.charAt(i); - if (c == '%') { - char c2 = format.charAt(i + 1); - i++; - switch (c2) { - case 'n' -> indenter.newline(); - case 'i' -> { - indenter.indent(); - indent++; - } - case 'd' -> { - indenter.dedent(); - indent--; - } - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> { - StringBuilder sb = new StringBuilder(); - for (; i < format.length() && format.charAt(i) >= '0' && format.charAt(i) <= '9'; i++) { - sb.append(format.charAt(i)); - } - i--; - int idx = Integer.parseInt(sb.toString()); - if (idx == 0 || idx > tc.production().items().size()) { - throw KEMException.compilerError("Invalid format attribute; index must be in range 1-N for a production with N items.", tc.production()); - } - ProductionItem item = tc.production().items().apply(idx - 1); - if (item instanceof Terminal) { - color(indenter, tc.production(), localColor++, colorize); - indenter.append(((Terminal) item).value()); - resetColor(indenter, tc.production(), colorize); - } else if (item instanceof NonTerminal) { - int nt = 0; - for (ProductionItem pi : iterable(tc.production().items())) { - if (pi instanceof NonTerminal) { - if (pi == item) { - break; - } - nt++; - } - } - assert tc.production().nonterminal(nt) == item; - Term inner = tc.get(nt); - boolean assoc = false; - if (inner instanceof TermCons innerTc) { - Production origProd = tc.production().att().getOptional(Att.ORIGINAL_PRD(), Production.class).orElse(tc.production()); - Production innerOrigProd = innerTc.production().att().getOptional(Att.ORIGINAL_PRD(), Production.class).orElse(innerTc.production()); - if (innerOrigProd.equals(origProd) && origProd.att().contains(Att.ASSOC())) { - assoc = true; - } - } - if (assoc) { - for (int j = 0; j < indent; j++) { - indenter.dedent(); - } - } - format(tc.get(nt), indenter, colorize); - if (assoc) { - for (int j = 0; j < indent; j++) { - indenter.indent(); - } - } - } else { - throw KEMException.internalError("Cannot currently format productions with regex terminals which are not tokens.", tc.production()); - } - } - default -> indenter.append(c2); + public static void format(Term term, Indenter indenter, ColorSetting colorize) { + int indent = 0; + int localColor = 0; + if (term instanceof Constant c) { + color(indenter, c.production(), 0, colorize); + indenter.append(c.value()); + resetColor(indenter, c.production(), colorize); + } else if (term instanceof TermCons tc) { + String format = + tc.production() + .att() + .getOptional(Att.FORMAT()) + .orElse(defaultFormat(tc.production().items().size())); + for (int i = 0; i < format.length(); i++) { + char c = format.charAt(i); + if (c == '%') { + char c2 = format.charAt(i + 1); + i++; + switch (c2) { + case 'n' -> indenter.newline(); + case 'i' -> { + indenter.indent(); + indent++; + } + case 'd' -> { + indenter.dedent(); + indent--; + } + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> { + StringBuilder sb = new StringBuilder(); + for (; + i < format.length() && format.charAt(i) >= '0' && format.charAt(i) <= '9'; + i++) { + sb.append(format.charAt(i)); + } + i--; + int idx = Integer.parseInt(sb.toString()); + if (idx == 0 || idx > tc.production().items().size()) { + throw KEMException.compilerError( + "Invalid format attribute; index must be in range 1-N for a production with N" + + " items.", + tc.production()); + } + ProductionItem item = tc.production().items().apply(idx - 1); + if (item instanceof Terminal) { + color(indenter, tc.production(), localColor++, colorize); + indenter.append(((Terminal) item).value()); + resetColor(indenter, tc.production(), colorize); + } else if (item instanceof NonTerminal) { + int nt = 0; + for (ProductionItem pi : iterable(tc.production().items())) { + if (pi instanceof NonTerminal) { + if (pi == item) { + break; } - } else { - indenter.append(c); + nt++; + } + } + assert tc.production().nonterminal(nt) == item; + Term inner = tc.get(nt); + boolean assoc = false; + if (inner instanceof TermCons innerTc) { + Production origProd = + tc.production() + .att() + .getOptional(Att.ORIGINAL_PRD(), Production.class) + .orElse(tc.production()); + Production innerOrigProd = + innerTc + .production() + .att() + .getOptional(Att.ORIGINAL_PRD(), Production.class) + .orElse(innerTc.production()); + if (innerOrigProd.equals(origProd) && origProd.att().contains(Att.ASSOC())) { + assoc = true; + } + } + if (assoc) { + for (int j = 0; j < indent; j++) { + indenter.dedent(); + } + } + format(tc.get(nt), indenter, colorize); + if (assoc) { + for (int j = 0; j < indent; j++) { + indenter.indent(); + } } + } else { + throw KEMException.internalError( + "Cannot currently format productions with regex terminals which are not" + + " tokens.", + tc.production()); + } } + default -> indenter.append(c2); + } + } else { + indenter.append(c); } + } } + } - public static String defaultFormat(int size) { - StringBuilder sb = new StringBuilder(); - for (int i = 1; i <= size; i++) { - sb.append("%").append(i).append(" "); - } - sb.deleteCharAt(sb.length() - 1); - return sb.toString(); + public static String defaultFormat(int size) { + StringBuilder sb = new StringBuilder(); + for (int i = 1; i <= size; i++) { + sb.append("%").append(i).append(" "); } + sb.deleteCharAt(sb.length() - 1); + return sb.toString(); + } - private static void color(Indenter indenter, Production p, int offset, ColorSetting colorize) { - if (p.att().contains(Att.COLOR())) { - indenter.append(ColorUtil.RgbToAnsi(p.att().get(Att.COLOR()), colorize)); - } - if (p.att().contains(Att.COLORS())) { - try { - String color = p.att().get(Att.COLORS()).split(",")[offset].trim(); - indenter.append(ColorUtil.RgbToAnsi(color, colorize)); - } catch (ArrayIndexOutOfBoundsException e) { - throw KEMException.compilerError("Invalid colors attribute. Must be a comma separated list with exactly one element per terminal.", e, p); - } - } + private static void color(Indenter indenter, Production p, int offset, ColorSetting colorize) { + if (p.att().contains(Att.COLOR())) { + indenter.append(ColorUtil.RgbToAnsi(p.att().get(Att.COLOR()), colorize)); + } + if (p.att().contains(Att.COLORS())) { + try { + String color = p.att().get(Att.COLORS()).split(",")[offset].trim(); + indenter.append(ColorUtil.RgbToAnsi(color, colorize)); + } catch (ArrayIndexOutOfBoundsException e) { + throw KEMException.compilerError( + "Invalid colors attribute. Must be a comma separated list with exactly one element per" + + " terminal.", + e, + p); + } } + } - private static void resetColor(Indenter indenter, Production p, ColorSetting colorize) { - if ((p.att().contains(Att.COLOR()) || p.att().contains(Att.COLORS())) && colorize != ColorSetting.OFF) { - indenter.append(ColorUtil.ANSI_NORMAL); - } + private static void resetColor(Indenter indenter, Production p, ColorSetting colorize) { + if ((p.att().contains(Att.COLOR()) || p.att().contains(Att.COLORS())) + && colorize != ColorSetting.OFF) { + indenter.append(ColorUtil.ANSI_NORMAL); } + } } diff --git a/kernel/src/main/java/org/kframework/unparser/Indenter.java b/kernel/src/main/java/org/kframework/unparser/Indenter.java index 2e5967c8114..7168993ed93 100644 --- a/kernel/src/main/java/org/kframework/unparser/Indenter.java +++ b/kernel/src/main/java/org/kframework/unparser/Indenter.java @@ -3,60 +3,60 @@ package org.kframework.unparser; public class Indenter implements Appendable { - private final int indentSize; - private int indentationLevel = 0; - private boolean atNewLine = true; - private final StringBuilder sb = new StringBuilder(); - - public Indenter(int indentSize) { - this.indentSize = indentSize; - } - - public Indenter append(CharSequence str) { - printIndent(); - sb.append(str); - return this; - } - - public Indenter append(CharSequence str, int start, int end) { - printIndent(); - sb.append(str, start, end); - return this; - } - - private void printIndent() { - if (atNewLine) { - for (int i = 0; i < indentSize * indentationLevel; i++) { - sb.append(" "); - } - atNewLine = false; - } - } - - public Indenter indent() { - indentationLevel++; - return this; - } - - public Indenter dedent() { - indentationLevel--; - return this; - } - - public Indenter newline() { - sb.append(System.getProperty("line.separator")); - atNewLine = true; - return this; - } - - @Override - public String toString() { - return sb.toString(); - } - - public Indenter append(char c) { - printIndent(); - sb.append(c); - return this; - } + private final int indentSize; + private int indentationLevel = 0; + private boolean atNewLine = true; + private final StringBuilder sb = new StringBuilder(); + + public Indenter(int indentSize) { + this.indentSize = indentSize; + } + + public Indenter append(CharSequence str) { + printIndent(); + sb.append(str); + return this; + } + + public Indenter append(CharSequence str, int start, int end) { + printIndent(); + sb.append(str, start, end); + return this; + } + + private void printIndent() { + if (atNewLine) { + for (int i = 0; i < indentSize * indentationLevel; i++) { + sb.append(" "); + } + atNewLine = false; + } + } + + public Indenter indent() { + indentationLevel++; + return this; + } + + public Indenter dedent() { + indentationLevel--; + return this; + } + + public Indenter newline() { + sb.append(System.getProperty("line.separator")); + atNewLine = true; + return this; + } + + @Override + public String toString() { + return sb.toString(); + } + + public Indenter append(char c) { + printIndent(); + sb.append(c); + return this; + } } diff --git a/kernel/src/main/java/org/kframework/unparser/KPrint.java b/kernel/src/main/java/org/kframework/unparser/KPrint.java index 983ca441291..7c256b17201 100644 --- a/kernel/src/main/java/org/kframework/unparser/KPrint.java +++ b/kernel/src/main/java/org/kframework/unparser/KPrint.java @@ -1,10 +1,24 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.unparser; +import static org.kframework.kore.KORE.*; + import com.davekoelle.AlphanumComparator; -import com.google.inject.Inject; -import com.google.common.collect.Multiset; import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; +import com.google.inject.Inject; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; import javax.annotation.Nullable; import org.kframework.attributes.Att; import org.kframework.backend.kore.ModuleToKORE; @@ -27,7 +41,6 @@ import org.kframework.main.GlobalOptions; import org.kframework.parser.ProductionReference; import org.kframework.parser.Term; -import org.kframework.parser.inner.ParseInModule; import org.kframework.parser.inner.RuleGrammarGenerator; import org.kframework.utils.errorsystem.KEMException; import org.kframework.utils.errorsystem.KExceptionManager; @@ -37,375 +50,406 @@ import scala.Option; import scala.Tuple2; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -import static org.kframework.kore.KORE.*; - -/** - * Class for printing information and outputting to files using various serializers. - */ +/** Class for printing information and outputting to files using various serializers. */ @RequestScoped public class KPrint { - private final KExceptionManager kem; - private final FileUtil files; - private final TTYInfo tty; - private final KompileOptions kompileOptions; - - public final PrintOptions options; - - @Nullable - private final CompiledDefinition compiledDefinition; - private final Module executionModule; - private final AddSortInjections addSortInjections; - - public KPrint() { - this(new KExceptionManager(new GlobalOptions()), FileUtil.testFileUtil(), new TTYInfo(false, false, false), - new PrintOptions(), null, new KompileOptions()); - } - - @Inject - public KPrint(KExceptionManager kem, FileUtil files, TTYInfo tty, PrintOptions options, - CompiledDefinition compiledDefinition, KompileOptions kompileOptions) { - this.kem = kem; - this.files = files; - this.tty = tty; - this.options = options; - this.compiledDefinition = compiledDefinition; - if (compiledDefinition != null) { - this.executionModule = compiledDefinition.executionModule(); - this.addSortInjections = new AddSortInjections(executionModule); - } else { - this.executionModule = null; - this.addSortInjections = null; - } - - this.kompileOptions = kompileOptions; + private final KExceptionManager kem; + private final FileUtil files; + private final TTYInfo tty; + private final KompileOptions kompileOptions; + + public final PrintOptions options; + + @Nullable private final CompiledDefinition compiledDefinition; + private final Module executionModule; + private final AddSortInjections addSortInjections; + + public KPrint() { + this( + new KExceptionManager(new GlobalOptions()), + FileUtil.testFileUtil(), + new TTYInfo(false, false, false), + new PrintOptions(), + null, + new KompileOptions()); + } + + @Inject + public KPrint( + KExceptionManager kem, + FileUtil files, + TTYInfo tty, + PrintOptions options, + CompiledDefinition compiledDefinition, + KompileOptions kompileOptions) { + this.kem = kem; + this.files = files; + this.tty = tty; + this.options = options; + this.compiledDefinition = compiledDefinition; + if (compiledDefinition != null) { + this.executionModule = compiledDefinition.executionModule(); + this.addSortInjections = new AddSortInjections(executionModule); + } else { + this.executionModule = null; + this.addSortInjections = null; } - //TODO(dwightguth): use Writer - public void outputFile(String output) { - outputFile(output.getBytes()); - } - - public void outputFile(byte[] output) { - outputFile(output, System.out); + this.kompileOptions = kompileOptions; + } + + // TODO(dwightguth): use Writer + public void outputFile(String output) { + outputFile(output.getBytes()); + } + + public void outputFile(byte[] output) { + outputFile(output, System.out); + } + + public void outputFile(byte[] output, OutputStream out) { + if (options.outputFile == null) { + try { + out.write(output); + out.flush(); + } catch (IOException e) { + throw KEMException.internalError(e.getMessage(), e); + } + } else { + files.saveToWorkingDirectory(options.outputFile, output); } - - public void outputFile(byte[] output, OutputStream out) { - if (options.outputFile == null) { - try { - out.write(output); - out.flush(); - } catch (IOException e) { - throw KEMException.internalError(e.getMessage(), e); - } - } else { - files.saveToWorkingDirectory(options.outputFile, output); + } + + public void prettyPrint(Definition def, Module module, Consumer print, K result) { + prettyPrint(def, module, print, result, Sorts.GeneratedTopCell()); + } + + public void prettyPrint(Definition def, Module module, Consumer print, K result, Sort s) { + print.accept( + prettyPrint( + def, module, result, s, options.color(tty.stdout(), files.getEnv()), options.output)); + } + + public byte[] prettyPrint(Definition def, Module module, K result) { + return prettyPrint( + def, + module, + result, + Sorts.GeneratedTopCell(), + options.color(tty.stdout(), files.getEnv()), + options.output); + } + + public byte[] prettyPrint( + Definition def, + Module module, + K orig, + Sort s, + ColorSetting colorize, + OutputModes outputMode) { + K result = abstractTerm(module, orig); + switch (outputMode) { + case KAST: + case NONE: + case BINARY: + case JSON: + case LATEX: + return serialize(result, outputMode); + case PRETTY: + Module prettyUnparsingModule = + RuleGrammarGenerator.getCombinedGrammar(module, false, files).getExtensionModule(); + return (unparseTerm(result, prettyUnparsingModule, colorize) + "\n").getBytes(); + case PROGRAM: + { + RuleGrammarGenerator gen = new RuleGrammarGenerator(def); + Module programUnparsingModule = + RuleGrammarGenerator.getCombinedGrammar(gen.getProgramsGrammar(module), false, files) + .getParsingModule(); + return (unparseTerm(result, programUnparsingModule, colorize) + "\n").getBytes(); } - } - - public void prettyPrint(Definition def, Module module, Consumer print, K result) { - prettyPrint(def, module, print, result, Sorts.GeneratedTopCell()); - } - - public void prettyPrint(Definition def, Module module, Consumer print, K result, Sort s) { - print.accept(prettyPrint(def, module, result, s, options.color(tty.stdout(), files.getEnv()), options.output)); - } - - public byte[] prettyPrint(Definition def, Module module, K result) { - return prettyPrint(def, module, result, Sorts.GeneratedTopCell(), options.color(tty.stdout(), files.getEnv()), options.output); - } - - public byte[] prettyPrint(Definition def, Module module, K orig, Sort s, ColorSetting colorize, OutputModes outputMode) { - K result = abstractTerm(module, orig); - switch (outputMode) { - case KAST: - case NONE: - case BINARY: - case JSON: - case LATEX: - return serialize(result, outputMode); - case PRETTY: - Module prettyUnparsingModule = RuleGrammarGenerator.getCombinedGrammar(module, false, files).getExtensionModule(); - return (unparseTerm(result, prettyUnparsingModule, colorize) + "\n").getBytes(); - case PROGRAM: { - RuleGrammarGenerator gen = new RuleGrammarGenerator(def); - Module programUnparsingModule = RuleGrammarGenerator.getCombinedGrammar(gen.getProgramsGrammar(module), false, files).getParsingModule(); - return (unparseTerm(result, programUnparsingModule, colorize) + "\n").getBytes(); - } - case KORE: - if (compiledDefinition == null) { - throw KEMException.criticalError("KORE output requires a compiled definition."); - } - ModuleToKORE converter = new ModuleToKORE(module, compiledDefinition.topCellInitializer, kompileOptions); - result = ExpandMacros.forNonSentences(executionModule, files, kompileOptions, false).expand(result); - result = addSortInjections.addSortInjections(result, s); - StringBuilder sb = new StringBuilder(); - converter.convert(result, sb); - return sb.toString().getBytes(); - default: - throw KEMException.criticalError("Unsupported output mode without a CompiledDefinition: " + outputMode); + case KORE: + if (compiledDefinition == null) { + throw KEMException.criticalError("KORE output requires a compiled definition."); } + ModuleToKORE converter = + new ModuleToKORE(module, compiledDefinition.topCellInitializer, kompileOptions); + result = + ExpandMacros.forNonSentences(executionModule, files, kompileOptions, false) + .expand(result); + result = addSortInjections.addSortInjections(result, s); + StringBuilder sb = new StringBuilder(); + converter.convert(result, sb); + return sb.toString().getBytes(); + default: + throw KEMException.criticalError( + "Unsupported output mode without a CompiledDefinition: " + outputMode); } + } + + public byte[] serialize(K term) { + return KPrint.serialize(term, options.output); + } + + public static byte[] serialize(K term, OutputModes outputMode) { + return switch (outputMode) { + case KAST -> (ToKast.apply(term) + "\n").getBytes(); + case NONE -> "".getBytes(); + case BINARY -> ToBinary.apply(term); + case JSON -> ToJson.apply(term); + case LATEX -> ToLatex.apply(term); + default -> throw KEMException.criticalError("Unsupported serialization mode: " + outputMode); + }; + } + + public String unparseTerm(K input, Module test) { + return unparseTerm(input, test, options.color(tty.stdout(), files.getEnv())); + } + + public String unparseTerm(K input, Module test, ColorSetting colorize) { + return unparseInternal(test, input, colorize); + } + + private Term disambiguateForUnparse(Module mod, Term t) { + return t; + } + + private String unparseInternal(Module mod, K input, ColorSetting colorize) { + ExpandMacros expandMacros = ExpandMacros.forNonSentences(mod, files, kompileOptions, true); + return Formatter.format( + new AddBrackets(mod) + .addBrackets( + (ProductionReference) + disambiguateForUnparse( + mod, + KOREToTreeNodes.apply( + KOREToTreeNodes.up(mod, expandMacros.expand(input)), mod))), + colorize); + } + + public K abstractTerm(Module mod, K term) { + K filteredSubst = options.noFilterSubstitution ? term : filterSubst(term, mod); + K origNames = + options.restoreOriginalNames ? restoreOriginalNameIfPresent(filteredSubst) : filteredSubst; + K collectionsSorted = options.noSortCollections ? origNames : sortCollections(mod, origNames); + // non-determinism is still possible if associative/commutative collection terms start with + // anonymous vars + K alphaRenamed = options.noAlphaRenaming ? collectionsSorted : alphaRename(collectionsSorted); + K squashedTerm = squashTerms(mod, alphaRenamed); + K flattenedTerm = flattenTerms(mod, squashedTerm); + + return flattenedTerm; + } + + private Set vars(K term) { + return new HashSet<>(multivars(term)); + } + + private Multiset multivars(K term) { + Multiset vars = HashMultiset.create(); + new VisitK() { + @Override + public void apply(KVariable var) { + vars.add(var); + } + }.apply(term); + return vars; + } - public byte[] serialize(K term) { - return KPrint.serialize(term, options.output); - } - - public static byte[] serialize(K term, OutputModes outputMode) { - return switch (outputMode) { - case KAST -> (ToKast.apply(term) + "\n").getBytes(); - case NONE -> "".getBytes(); - case BINARY -> ToBinary.apply(term); - case JSON -> ToJson.apply(term); - case LATEX -> ToLatex.apply(term); - default -> throw KEMException.criticalError("Unsupported serialization mode: " + outputMode); - }; - } - - public String unparseTerm(K input, Module test) { - return unparseTerm(input, test, options.color(tty.stdout(), files.getEnv())); - } - - public String unparseTerm(K input, Module test, ColorSetting colorize) { - return unparseInternal(test, input, colorize); - } - - private Term disambiguateForUnparse(Module mod, Term t) { - return t; - } - - private String unparseInternal(Module mod, K input, ColorSetting colorize) { - ExpandMacros expandMacros = ExpandMacros.forNonSentences(mod, files, kompileOptions, true); - return Formatter.format( - new AddBrackets(mod).addBrackets((ProductionReference) disambiguateForUnparse(mod, KOREToTreeNodes.apply(KOREToTreeNodes.up(mod, expandMacros.expand(input)), mod))), colorize); + private K filterSubst(K term, Module mod) { + if (!(term instanceof KApply kapp)) { + return term; } - - public K abstractTerm(Module mod, K term) { - K filteredSubst = options.noFilterSubstitution ? term : filterSubst(term, mod); - K origNames = options.restoreOriginalNames ? restoreOriginalNameIfPresent(filteredSubst) : filteredSubst; - K collectionsSorted = options.noSortCollections ? origNames : sortCollections(mod, origNames); - //non-determinism is still possible if associative/commutative collection terms start with anonymous vars - K alphaRenamed = options.noAlphaRenaming ? collectionsSorted : alphaRename(collectionsSorted); - K squashedTerm = squashTerms(mod, alphaRenamed); - K flattenedTerm = flattenTerms(mod, squashedTerm); - - return flattenedTerm; + if (kapp.klabel().head().equals(KLabels.ML_AND)) { + return filterConjunction(kapp, mod); + } else if (kapp.klabel().head().equals(KLabels.ML_OR)) { + KLabel unit = KLabel(KLabels.ML_FALSE.name(), kapp.klabel().params().apply(0)); + List disjuncts = Assoc.flatten(kapp.klabel(), kapp.items(), unit); + return disjuncts.stream() + .map(d -> filterConjunction(d, mod)) + .distinct() + .reduce((k1, k2) -> KApply(kapp.klabel(), k1, k2)) + .orElse(KApply(unit)); + } else if (kapp.klabel().head().equals(KLabels.ML_EQUALS)) { + KLabel unit = KLabel(KLabels.ML_TRUE.name(), kapp.klabel().params().apply(0)); + return filterEquality(kapp, multivars(kapp), mod).orElse(KApply(unit)); + } else { + return term; } + } - private Set vars(K term) { - return new HashSet<>(multivars(term)); + private K filterConjunction(K term, Module mod) { + if (!(term instanceof KApply kapp)) { + return term; } - - private Multiset multivars(K term) { - Multiset vars = HashMultiset.create(); - new VisitK() { - @Override - public void apply(KVariable var) { - vars.add(var); - } - }.apply(term); - return vars; + if (kapp.klabel().head().equals(KLabels.ML_AND)) { + KLabel unit = KLabel(KLabels.ML_TRUE.name(), kapp.klabel().params().apply(0)); + List conjuncts = Assoc.flatten(kapp.klabel(), kapp.items(), unit); + return conjuncts.stream() + .map(d -> filterEquality(d, multivars(kapp), mod)) + .filter(Optional::isPresent) + .map(Optional::get) + .reduce((k1, k2) -> KApply(kapp.klabel(), k1, k2)) + .orElse(KApply(unit)); + } else if (kapp.klabel().head().equals(KLabels.ML_EQUALS)) { + KLabel unit = KLabel(KLabels.ML_TRUE.name(), kapp.klabel().params().apply(0)); + return filterEquality(kapp, multivars(kapp), mod).orElse(KApply(unit)); + } else { + return term; } + } - private K filterSubst(K term, Module mod) { - if (!(term instanceof KApply kapp)) { - return term; - } - if (kapp.klabel().head().equals(KLabels.ML_AND)) { - return filterConjunction(kapp, mod); - } else if (kapp.klabel().head().equals(KLabels.ML_OR)) { - KLabel unit = KLabel(KLabels.ML_FALSE.name(), kapp.klabel().params().apply(0)); - List disjuncts = Assoc.flatten(kapp.klabel(), kapp.items(), unit); - return disjuncts.stream() - .map(d -> filterConjunction(d, mod)) - .distinct() - .reduce((k1, k2) -> KApply(kapp.klabel(), k1, k2)) - .orElse(KApply(unit)); - } else if (kapp.klabel().head().equals(KLabels.ML_EQUALS)) { - KLabel unit = KLabel(KLabels.ML_TRUE.name(), kapp.klabel().params().apply(0)); - return filterEquality(kapp, multivars(kapp), mod).orElse(KApply(unit)); - } else { - return term; - } + public Optional filterEquality(K term, Multiset vars, Module mod) { + if (!(term instanceof KApply kapp)) { + return Optional.of(term); } - - private K filterConjunction(K term, Module mod) { - if (!(term instanceof KApply kapp)) { - return term; - } - if (kapp.klabel().head().equals(KLabels.ML_AND)) { - KLabel unit = KLabel(KLabels.ML_TRUE.name(), kapp.klabel().params().apply(0)); - List conjuncts = Assoc.flatten(kapp.klabel(), kapp.items(), unit); - return conjuncts.stream() - .map(d -> filterEquality(d, multivars(kapp), mod)) - .filter(Optional::isPresent) - .map(Optional::get) - .reduce((k1, k2) -> KApply(kapp.klabel(), k1, k2)) - .orElse(KApply(unit)); - } else if (kapp.klabel().head().equals(KLabels.ML_EQUALS)) { - KLabel unit = KLabel(KLabels.ML_TRUE.name(), kapp.klabel().params().apply(0)); - return filterEquality(kapp, multivars(kapp), mod).orElse(KApply(unit)); - } else { - return term; + if (kapp.klabel().head().equals(KLabels.ML_EQUALS)) { + if (!(kapp.items().get(0) instanceof KVariable) + && (!(kapp.items().get(0) instanceof KApply) + || !mod.attributesFor() + .apply(((KApply) kapp.items().get(0)).klabel()) + .contains(Att.FUNCTION()))) { + return Optional.of(term); } - } - - public Optional filterEquality(K term, Multiset vars, Module mod) { - if (!(term instanceof KApply kapp)) { + Set leftVars = vars(kapp.items().get(0)); + if (leftVars.stream().filter(v -> !v.att().contains(Att.ANONYMOUS())).findAny().isPresent()) { return Optional.of(term); } - if (kapp.klabel().head().equals(KLabels.ML_EQUALS)) { - if (!(kapp.items().get(0) instanceof KVariable) && - (!(kapp.items().get(0) instanceof KApply) || - !mod.attributesFor().apply(((KApply)kapp.items().get(0)).klabel()).contains(Att.FUNCTION()))) { + for (KVariable var : leftVars) { + if (vars.count(var) != 1) { return Optional.of(term); } - Set leftVars = vars(kapp.items().get(0)); - if (leftVars.stream().filter(v -> !v.att().contains(Att.ANONYMOUS())).findAny().isPresent()) { - return Optional.of(term); + } + return Optional.empty(); + } else { + return Optional.of(term); + } + } + + private K sortCollections(Module mod, K input) { + Module unparsingModule = + RuleGrammarGenerator.getCombinedGrammar(mod, false, files).getExtensionModule(); + return new TransformK() { + @Override + public K apply(KApply k) { + if (k.klabel() instanceof KVariable) { + return super.apply(k); } - for (KVariable var : leftVars) { - if (vars.count(var) != 1) { - return Optional.of(term); + Att att = unparsingModule.attributesFor().apply(KLabel(k.klabel().name())); + if (att.contains(Att.COMM()) && att.contains(Att.ASSOC()) && att.contains(Att.UNIT())) { + List items = + new ArrayList<>( + Assoc.flatten(k.klabel(), k.klist().items(), KLabel(att.get(Att.UNIT())))); + List> printed = new ArrayList<>(); + for (K item : items) { + String s = unparseInternal(unparsingModule, apply(item), ColorSetting.OFF); + printed.add(Tuple2.apply(s, item)); } + printed.sort(Comparator.comparing(Tuple2::_1, new AlphanumComparator())); + items = printed.stream().map(Tuple2::_2).map(this::apply).collect(Collectors.toList()); + return items.stream() + .reduce((k1, k2) -> KApply(k.klabel(), k1, k2)) + .orElse(KApply(KLabel(att.get(Att.UNIT())))); } - return Optional.empty(); - } else { - return Optional.of(term); + return super.apply(k); } - } - - private K sortCollections(Module mod, K input) { - Module unparsingModule = RuleGrammarGenerator.getCombinedGrammar(mod, false, files).getExtensionModule(); - return new TransformK() { - @Override - public K apply(KApply k) { - if (k.klabel() instanceof KVariable) { - return super.apply(k); - } - Att att = unparsingModule.attributesFor().apply(KLabel(k.klabel().name())); - if (att.contains(Att.COMM()) && att.contains(Att.ASSOC()) && att.contains(Att.UNIT())) { - List items = new ArrayList<>(Assoc.flatten(k.klabel(), k.klist().items(), KLabel(att.get(Att.UNIT())))); - List> printed = new ArrayList<>(); - for (K item : items) { - String s = unparseInternal(unparsingModule, apply(item), ColorSetting.OFF); - printed.add(Tuple2.apply(s, item)); - } - printed.sort(Comparator.comparing(Tuple2::_1, new AlphanumComparator())); - items = printed.stream().map(Tuple2::_2).map(this::apply).collect(Collectors.toList()); - return items.stream().reduce((k1, k2) -> KApply(k.klabel(), k1, k2)).orElse(KApply(KLabel(att.get(Att.UNIT())))); - } - return super.apply(k); - } - }.apply(input); - } - - private K alphaRename(K input) { - return new TransformK() { - final Map renames = new HashMap<>(); - int newCount = 0; - - @Override - public K apply(KVariable k) { - if (k.att().contains(Att.ANONYMOUS())) { - return renames.computeIfAbsent(k, k2 -> KVariable("V" + newCount++, k.att())); - } - return k; - } - }.apply(input); - } - - private K restoreOriginalNameIfPresent(K input) { - return new TransformK() { - @Override - public K apply(KVariable k) { - if (k.att().contains(Att.ORIGINAL_NAME())) { - return KVariable(k.att().get(Att.ORIGINAL_NAME()), k.att()); - } - return k; - } - }.apply(input); - } - - private K squashTerms(Module mod, K input) { - return new TransformK() { - @Override - public K apply(KApply orig) { - String name = orig.klabel().name(); - return options.omittedKLabels.contains(name) ? omitTerm(mod, orig) - : options.tokenizedKLabels.contains(name) ? tokenizeTerm(mod, orig) - : options.tokastKLabels.contains(name) ? toKASTTerm(mod, orig) - : super.apply(orig) ; - } - }.apply(input); - } - - private static K omitTerm(Module mod, KApply kapp) { - Sort finalSort = Sorts.K(); - Option termSort = mod.sortFor().get(kapp.klabel()); - if (! termSort.isEmpty()) { - finalSort = termSort.get(); + }.apply(input); + } + + private K alphaRename(K input) { + return new TransformK() { + final Map renames = new HashMap<>(); + int newCount = 0; + + @Override + public K apply(KVariable k) { + if (k.att().contains(Att.ANONYMOUS())) { + return renames.computeIfAbsent(k, k2 -> KVariable("V" + newCount++, k.att())); } - return KApply(kapp.klabel(), KToken(kapp.klabel().name(), finalSort)); - } - - private K tokenizeTerm(Module mod, KApply kapp) { - Module unparsingModule = RuleGrammarGenerator.getCombinedGrammar(mod, false, files).getExtensionModule(); - String tokenizedTerm = unparseTerm(kapp, unparsingModule, ColorSetting.OFF); - Sort finalSort = Sorts.K(); - Option termSort = mod.sortFor().get(kapp.klabel()); - if (! termSort.isEmpty()) { - finalSort = termSort.get(); + return k; + } + }.apply(input); + } + + private K restoreOriginalNameIfPresent(K input) { + return new TransformK() { + @Override + public K apply(KVariable k) { + if (k.att().contains(Att.ORIGINAL_NAME())) { + return KVariable(k.att().get(Att.ORIGINAL_NAME()), k.att()); } - return KApply(kapp.klabel(), KToken(tokenizedTerm, finalSort)); + return k; + } + }.apply(input); + } + + private K squashTerms(Module mod, K input) { + return new TransformK() { + @Override + public K apply(KApply orig) { + String name = orig.klabel().name(); + return options.omittedKLabels.contains(name) + ? omitTerm(mod, orig) + : options.tokenizedKLabels.contains(name) + ? tokenizeTerm(mod, orig) + : options.tokastKLabels.contains(name) ? toKASTTerm(mod, orig) : super.apply(orig); + } + }.apply(input); + } + + private static K omitTerm(Module mod, KApply kapp) { + Sort finalSort = Sorts.K(); + Option termSort = mod.sortFor().get(kapp.klabel()); + if (!termSort.isEmpty()) { + finalSort = termSort.get(); } - - private static K toKASTTerm(Module mod, KApply kapp) { - String kastTerm = ToKast.apply(kapp); - Sort finalSort = Sorts.K(); - Option termSort = mod.sortFor().get(kapp.klabel()); - if (! termSort.isEmpty()) { - finalSort = termSort.get(); - } - return KToken(kastTerm, finalSort); + return KApply(kapp.klabel(), KToken(kapp.klabel().name(), finalSort)); + } + + private K tokenizeTerm(Module mod, KApply kapp) { + Module unparsingModule = + RuleGrammarGenerator.getCombinedGrammar(mod, false, files).getExtensionModule(); + String tokenizedTerm = unparseTerm(kapp, unparsingModule, ColorSetting.OFF); + Sort finalSort = Sorts.K(); + Option termSort = mod.sortFor().get(kapp.klabel()); + if (!termSort.isEmpty()) { + finalSort = termSort.get(); } - - private K flattenTerms(Module mod, K input) { - return new TransformK() { - @Override - public K apply(KApply orig) { - K newK = super.apply(orig); - if (! (newK instanceof KApply kapp)) { - return newK; - } - String name = orig.klabel().name(); - return options.flattenedKLabels.contains(name) ? flattenTerm(mod, kapp) - : kapp ; - } - }.apply(input); + return KApply(kapp.klabel(), KToken(tokenizedTerm, finalSort)); + } + + private static K toKASTTerm(Module mod, KApply kapp) { + String kastTerm = ToKast.apply(kapp); + Sort finalSort = Sorts.K(); + Option termSort = mod.sortFor().get(kapp.klabel()); + if (!termSort.isEmpty()) { + finalSort = termSort.get(); } - - private static K flattenTerm(Module mod, KApply kapp) { - List items = new ArrayList<>(); - Att att = mod.attributesFor().apply(KLabel(kapp.klabel().name())); - if (att.contains(Att.ASSOC()) && att.contains(Att.UNIT())) { - items = Assoc.flatten(kapp.klabel(), kapp.klist().items(), KLabel(att.get(Att.UNIT()))); - } else { - items = kapp.klist().items(); + return KToken(kastTerm, finalSort); + } + + private K flattenTerms(Module mod, K input) { + return new TransformK() { + @Override + public K apply(KApply orig) { + K newK = super.apply(orig); + if (!(newK instanceof KApply kapp)) { + return newK; } - return KApply(kapp.klabel(), KList(items), kapp.att()); + String name = orig.klabel().name(); + return options.flattenedKLabels.contains(name) ? flattenTerm(mod, kapp) : kapp; + } + }.apply(input); + } + + private static K flattenTerm(Module mod, KApply kapp) { + List items = new ArrayList<>(); + Att att = mod.attributesFor().apply(KLabel(kapp.klabel().name())); + if (att.contains(Att.ASSOC()) && att.contains(Att.UNIT())) { + items = Assoc.flatten(kapp.klabel(), kapp.klist().items(), KLabel(att.get(Att.UNIT()))); + } else { + items = kapp.klist().items(); } + return KApply(kapp.klabel(), KList(items), kapp.att()); + } } diff --git a/kernel/src/main/java/org/kframework/unparser/OutputModes.java b/kernel/src/main/java/org/kframework/unparser/OutputModes.java index 9e3c7a931ae..e6088da0aa5 100644 --- a/kernel/src/main/java/org/kframework/unparser/OutputModes.java +++ b/kernel/src/main/java/org/kframework/unparser/OutputModes.java @@ -3,25 +3,33 @@ /** * Contains functionality generic to all output modes. - * @author dwightguth * + * @author dwightguth */ public enum OutputModes { - PRETTY, PROGRAM, KAST, BINARY, JSON, LATEX, KORE, NONE; + PRETTY, + PROGRAM, + KAST, + BINARY, + JSON, + LATEX, + KORE, + NONE; + + private String extension; - private String extension; - static { - PRETTY.extension = "kpretty"; - PROGRAM.extension = "pgm"; - KAST.extension = "kast"; - BINARY.extension = "kbin"; - JSON.extension = "json"; - LATEX.extension = "tex"; - KORE.extension = "kore"; - NONE.extension = ""; - } + static { + PRETTY.extension = "kpretty"; + PROGRAM.extension = "pgm"; + KAST.extension = "kast"; + BINARY.extension = "kbin"; + JSON.extension = "json"; + LATEX.extension = "tex"; + KORE.extension = "kore"; + NONE.extension = ""; + } - public String ext() { - return extension; - } + public String ext() { + return extension; + } } diff --git a/kernel/src/main/java/org/kframework/unparser/PrintOptions.java b/kernel/src/main/java/org/kframework/unparser/PrintOptions.java index 74c860c80da..ac23ff1289e 100644 --- a/kernel/src/main/java/org/kframework/unparser/PrintOptions.java +++ b/kernel/src/main/java/org/kframework/unparser/PrintOptions.java @@ -2,107 +2,138 @@ package org.kframework.unparser; import com.beust.jcommander.Parameter; -import com.google.inject.Inject; -import org.kframework.utils.options.BaseEnumConverter; -import org.kframework.utils.options.StringListConverter; - import java.util.ArrayList; import java.util.List; import java.util.Map; +import org.kframework.utils.options.BaseEnumConverter; +import org.kframework.utils.options.StringListConverter; public class PrintOptions { - public PrintOptions() { + public PrintOptions() {} + + public PrintOptions(ColorSetting color) { + this.color = color; + } + + public PrintOptions(OutputModes output) { + this.output = output; + } + + @Parameter( + names = "--color", + description = "Use colors in output. Default is on.", + descriptionKey = "mode", + converter = ColorModeConverter.class) + private ColorSetting color; + + public ColorSetting color(boolean ttyStdout, Map env) { + boolean colorize = outputFile == null && ttyStdout; + ColorSetting colors = color; + if (colors == null) { + try { + if (!colorize) { + colors = ColorSetting.OFF; + } else if (Integer.parseInt(env.get("K_COLOR_SUPPORT")) >= 256) { + colors = ColorSetting.EXTENDED; + } else { + colors = ColorSetting.ON; + } + } catch (NumberFormatException e) { + colors = ColorSetting.ON; + } } + return colors; + } - public PrintOptions(ColorSetting color) { - this.color = color; - } + public static class ColorModeConverter extends BaseEnumConverter { - public PrintOptions(OutputModes output) { - this.output = output; + public ColorModeConverter(String optionName) { + super(optionName); } - @Parameter(names = "--color", description = "Use colors in output. Default is on.", descriptionKey = "mode", - converter=ColorModeConverter.class) - private ColorSetting color; - - public ColorSetting color(boolean ttyStdout, Map env) { - boolean colorize = outputFile == null && ttyStdout; - ColorSetting colors = color; - if (colors == null) { - try { - if (!colorize) { - colors = ColorSetting.OFF; - } else if (Integer.parseInt(env.get("K_COLOR_SUPPORT")) >= 256) { - colors = ColorSetting.EXTENDED; - } else { - colors = ColorSetting.ON; - } - } catch (NumberFormatException e) { - colors = ColorSetting.ON; - } - } - return colors; + @Override + public Class enumClass() { + return ColorSetting.class; } - - public static class ColorModeConverter extends BaseEnumConverter { - - public ColorModeConverter(String optionName) { - super(optionName); - } - - @Override - public Class enumClass() { - return ColorSetting.class; - } + } + + @Parameter( + names = "--output-file", + description = "Store output in the file instead of displaying it.", + descriptionKey = "file") + public String outputFile; + + @Parameter( + names = {"--output", "-o"}, + descriptionKey = "mode", + converter = OutputModeConverter.class, + description = + "How to display krun results. is either" + + " [pretty|program|kast|binary|json|latex|kore|none].") + public OutputModes output = OutputModes.PRETTY; + + public static class OutputModeConverter extends BaseEnumConverter { + + public OutputModeConverter(String optionName) { + super(optionName); } - @Parameter(names="--output-file", description="Store output in the file instead of displaying it.", - descriptionKey = "file") - public String outputFile; - - @Parameter(names={"--output", "-o"}, descriptionKey = "mode", converter=OutputModeConverter.class, - description="How to display krun results. is either [pretty|program|kast|binary|json|latex|kore|none].") - public OutputModes output = OutputModes.PRETTY; - - public static class OutputModeConverter extends BaseEnumConverter { - - public OutputModeConverter(String optionName) { - super(optionName); - } - - @Override - public Class enumClass() { - return OutputModes.class; - } + @Override + public Class enumClass() { + return OutputModes.class; } - - @Parameter(names={"--output-omit"}, descriptionKey = "KLabels", listConverter=StringListConverter.class, - description="KLabels to omit from the output.") - public List omittedKLabels = new ArrayList(); - - @Parameter(names={"--output-tokenize"}, descriptionKey = "KLabels", listConverter=StringListConverter.class, - description="KLabels to tokenize underneath (reducing output size).") - public List tokenizedKLabels = new ArrayList(); - - @Parameter(names={"--output-flatten"}, descriptionKey = "KLabels", listConverter=StringListConverter.class, - description="(Assoc) KLabels to flatten into one list.") - public List flattenedKLabels = new ArrayList(); - - @Parameter(names={"--output-tokast"}, descriptionKey = "KLabels", listConverter=StringListConverter.class, - description="KLabels to output as KAST tokens.") - public List tokastKLabels = new ArrayList(); - - @Parameter(names={"--no-alpha-renaming"}, listConverter=StringListConverter.class, description="Do not alpha rename anonymous variables in output.") - public boolean noAlphaRenaming = false; - - @Parameter(names={"--no-substitution-filtering"}, listConverter=StringListConverter.class, description="Do not remove conjuncts of anonymous variables from substitution in output.") - public boolean noFilterSubstitution = false; - - @Parameter(names={"--restore-original-names"}, listConverter=StringListConverter.class, description="Restore original variable names when provided by attributes.") - public boolean restoreOriginalNames = false; - - @Parameter(names={"--no-sort-collections"}, listConverter=StringListConverter.class, description="Do not sort collections before printing (for speed).") - public boolean noSortCollections = false; + } + + @Parameter( + names = {"--output-omit"}, + descriptionKey = "KLabels", + listConverter = StringListConverter.class, + description = "KLabels to omit from the output.") + public List omittedKLabels = new ArrayList(); + + @Parameter( + names = {"--output-tokenize"}, + descriptionKey = "KLabels", + listConverter = StringListConverter.class, + description = "KLabels to tokenize underneath (reducing output size).") + public List tokenizedKLabels = new ArrayList(); + + @Parameter( + names = {"--output-flatten"}, + descriptionKey = "KLabels", + listConverter = StringListConverter.class, + description = "(Assoc) KLabels to flatten into one list.") + public List flattenedKLabels = new ArrayList(); + + @Parameter( + names = {"--output-tokast"}, + descriptionKey = "KLabels", + listConverter = StringListConverter.class, + description = "KLabels to output as KAST tokens.") + public List tokastKLabels = new ArrayList(); + + @Parameter( + names = {"--no-alpha-renaming"}, + listConverter = StringListConverter.class, + description = "Do not alpha rename anonymous variables in output.") + public boolean noAlphaRenaming = false; + + @Parameter( + names = {"--no-substitution-filtering"}, + listConverter = StringListConverter.class, + description = "Do not remove conjuncts of anonymous variables from substitution in output.") + public boolean noFilterSubstitution = false; + + @Parameter( + names = {"--restore-original-names"}, + listConverter = StringListConverter.class, + description = "Restore original variable names when provided by attributes.") + public boolean restoreOriginalNames = false; + + @Parameter( + names = {"--no-sort-collections"}, + listConverter = StringListConverter.class, + description = "Do not sort collections before printing (for speed).") + public boolean noSortCollections = false; } diff --git a/kernel/src/main/java/org/kframework/unparser/ToBinary.java b/kernel/src/main/java/org/kframework/unparser/ToBinary.java index 217a67ae643..7df0dac8e1a 100644 --- a/kernel/src/main/java/org/kframework/unparser/ToBinary.java +++ b/kernel/src/main/java/org/kframework/unparser/ToBinary.java @@ -1,6 +1,13 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.unparser; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Map; import org.kframework.kore.InjectedKLabel; import org.kframework.kore.K; import org.kframework.kore.KApply; @@ -11,123 +18,115 @@ import org.kframework.parser.binary.BinaryParser; import org.kframework.utils.errorsystem.KEMException; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.HashMap; -import java.util.IdentityHashMap; -import java.util.Map; - /** - * Writes a KAST term to the KAST binary format. For details of that format, see {@link BinaryParser}. + * Writes a KAST term to the KAST binary format. For details of that format, see {@link + * BinaryParser}. */ public class ToBinary { - public static void apply(OutputStream out, K k) { - try { - DataOutputStream data = new DataOutputStream(out); - //magic - data.writeByte(0x7f); - data.writeBytes("KAST"); - //version - data.writeByte(4); - data.writeByte(0); - data.writeByte(1); - new ToBinary(data).traverse(k); - data.writeByte(BinaryParser.END); - } catch (IOException e) { - throw KEMException.criticalError("Could not write K term to binary", e, k); - } - + public static void apply(OutputStream out, K k) { + try { + DataOutputStream data = new DataOutputStream(out); + // magic + data.writeByte(0x7f); + data.writeBytes("KAST"); + // version + data.writeByte(4); + data.writeByte(0); + data.writeByte(1); + new ToBinary(data).traverse(k); + data.writeByte(BinaryParser.END); + } catch (IOException e) { + throw KEMException.criticalError("Could not write K term to binary", e, k); } - - public static byte[] apply(K k) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - apply(out, k); - return out.toByteArray(); + } + + public static byte[] apply(K k) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + apply(out, k); + return out.toByteArray(); + } + + private final DataOutputStream data; + private final Map interns = new HashMap<>(); + private final Map kInterns = new IdentityHashMap<>(); + private int numTermsWritten; + + private ToBinary(DataOutputStream data) { + this.data = data; + } + + private void traverse(K k) throws IOException { + if (kInterns.containsKey(k)) { + data.writeByte(BinaryParser.BACK_REFERENCE); + data.writeInt(numTermsWritten - kInterns.get(k)); + add_intern(k); + return; } + if (k instanceof KToken tok) { - private final DataOutputStream data; - private final Map interns = new HashMap<>(); - private final Map kInterns = new IdentityHashMap<>(); - private int numTermsWritten; + data.writeByte(BinaryParser.KTOKEN); + add_intern(k); + writeString(tok.s()); + writeString(tok.sort().toString()); - private ToBinary(DataOutputStream data) { - this.data = data; - } + } else if (k instanceof KApply app) { - private void traverse(K k) throws IOException { - if (kInterns.containsKey(k)) { - data.writeByte(BinaryParser.BACK_REFERENCE); - data.writeInt(numTermsWritten - kInterns.get(k)); - add_intern(k); - return; - } - if (k instanceof KToken tok) { - - data.writeByte(BinaryParser.KTOKEN); - add_intern(k); - writeString(tok.s()); - writeString(tok.sort().toString()); - - } else if (k instanceof KApply app) { - - for (K item : app.asIterable()) { - traverse(item); - } - data.writeByte(BinaryParser.KAPPLY); - add_intern(k); - writeString(app.klabel().name()); - data.writeBoolean(app.klabel() instanceof KVariable); - data.writeInt(app.size()); - - } else if (k instanceof KSequence seq) { - - for (K item : seq.asIterable()) { - traverse(item); - } - data.writeByte(BinaryParser.KSEQUENCE); - add_intern(k); - data.writeInt(seq.size()); - - } else if (k instanceof KVariable var) { - - data.writeByte(BinaryParser.KVARIABLE); - add_intern(k); - writeString(var.name()); - - } else if (k instanceof KRewrite rew) { - - traverse(rew.left()); - traverse(rew.right()); - data.writeByte(BinaryParser.KREWRITE); - add_intern(k); - - } else if (k instanceof InjectedKLabel inj) { - - data.writeByte(BinaryParser.INJECTEDKLABEL); - add_intern(k); - writeString(inj.klabel().name()); - data.writeBoolean(inj.klabel() instanceof KVariable); - - } else { - throw KEMException.criticalError("Unimplemented for Binary serialization: ", k); - } - } + for (K item : app.asIterable()) { + traverse(item); + } + data.writeByte(BinaryParser.KAPPLY); + add_intern(k); + writeString(app.klabel().name()); + data.writeBoolean(app.klabel() instanceof KVariable); + data.writeInt(app.size()); - private void add_intern(K k) { - kInterns.put(k, numTermsWritten); - numTermsWritten++; - } + } else if (k instanceof KSequence seq) { + + for (K item : seq.asIterable()) { + traverse(item); + } + data.writeByte(BinaryParser.KSEQUENCE); + add_intern(k); + data.writeInt(seq.size()); - private void writeString(String s) throws IOException { - int idx = interns.getOrDefault(s, interns.size()); - data.writeInt(interns.size() - idx); - if (idx == interns.size()) { - data.writeInt(s.length()); - data.writeChars(s); - interns.put(s, interns.size()); - } + } else if (k instanceof KVariable var) { + + data.writeByte(BinaryParser.KVARIABLE); + add_intern(k); + writeString(var.name()); + + } else if (k instanceof KRewrite rew) { + + traverse(rew.left()); + traverse(rew.right()); + data.writeByte(BinaryParser.KREWRITE); + add_intern(k); + + } else if (k instanceof InjectedKLabel inj) { + + data.writeByte(BinaryParser.INJECTEDKLABEL); + add_intern(k); + writeString(inj.klabel().name()); + data.writeBoolean(inj.klabel() instanceof KVariable); + + } else { + throw KEMException.criticalError("Unimplemented for Binary serialization: ", k); + } + } + + private void add_intern(K k) { + kInterns.put(k, numTermsWritten); + numTermsWritten++; + } + + private void writeString(String s) throws IOException { + int idx = interns.getOrDefault(s, interns.size()); + data.writeInt(interns.size() - idx); + if (idx == interns.size()) { + data.writeInt(s.length()); + data.writeChars(s); + interns.put(s, interns.size()); } + } } diff --git a/kernel/src/main/java/org/kframework/unparser/ToJson.java b/kernel/src/main/java/org/kframework/unparser/ToJson.java index 86fa96bd331..2b8f12222e7 100644 --- a/kernel/src/main/java/org/kframework/unparser/ToJson.java +++ b/kernel/src/main/java/org/kframework/unparser/ToJson.java @@ -1,17 +1,24 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.unparser; +import static org.kframework.Collections.*; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Collections; +import javax.json.*; import org.kframework.attributes.Att; -import org.kframework.attributes.Att.Key; import org.kframework.attributes.Location; import org.kframework.attributes.Source; import org.kframework.definition.Bubble; -import org.kframework.definition.Context; import org.kframework.definition.Configuration; +import org.kframework.definition.Context; import org.kframework.definition.Definition; -import org.kframework.definition.NonTerminal; -import org.kframework.definition.Module; import org.kframework.definition.FlatModule; +import org.kframework.definition.Module; +import org.kframework.definition.NonTerminal; import org.kframework.definition.Production; import org.kframework.definition.ProductionItem; import org.kframework.definition.RegexTerminal; @@ -37,467 +44,455 @@ import org.kframework.kore.Sort; import org.kframework.parser.json.JsonParser; import org.kframework.utils.errorsystem.KEMException; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.DataOutputStream; -import java.io.ByteArrayOutputStream; -import java.util.Collections; - -import javax.json.*; - import scala.Option; import scala.Tuple2; import scala.collection.JavaConverters; import scala.collection.Set; -import static org.kframework.Collections.*; - -/** - * Writes a KAST term to the KAST Json format. - */ +/** Writes a KAST term to the KAST Json format. */ public class ToJson { - public static final int version = 3; + public static final int version = 3; -/////////////////////////////// -// ToJson Definition Objects // -/////////////////////////////// + /////////////////////////////// + // ToJson Definition Objects // + /////////////////////////////// - private static final JsonBuilderFactory factory = Json.createBuilderFactory(Collections.emptyMap()); + private static final JsonBuilderFactory factory = + Json.createBuilderFactory(Collections.emptyMap()); - public static byte[] apply(Definition def) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try { - DataOutputStream data = new DataOutputStream(out); - JsonWriter jsonWriter = Json.createWriter(data); + public static byte[] apply(Definition def) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + DataOutputStream data = new DataOutputStream(out); + JsonWriter jsonWriter = Json.createWriter(data); - JsonObjectBuilder term = factory.createObjectBuilder(); - term.add("format", "KAST"); - term.add("version", version); - term.add("term", toJson(def)); + JsonObjectBuilder term = factory.createObjectBuilder(); + term.add("format", "KAST"); + term.add("version", version); + term.add("term", toJson(def)); - jsonWriter.write(term.build()); - jsonWriter.close(); - data.close(); - } catch (IOException e) { - throw KEMException.criticalError("Could not write Definition to Json", e); - } - return out.toByteArray(); + jsonWriter.write(term.build()); + jsonWriter.close(); + data.close(); + } catch (IOException e) { + throw KEMException.criticalError("Could not write Definition to Json", e); } - - public static byte[] apply(java.util.Set mods, String mainSpecModule) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try { - DataOutputStream data = new DataOutputStream(out); - JsonWriter jsonWriter = Json.createWriter(data); - - JsonObjectBuilder term = factory.createObjectBuilder(); - term.add("format", "KAST"); - term.add("version", version); - JsonObjectBuilder jmodlist = factory.createObjectBuilder(); - - jmodlist.add("node", JsonParser.KFLATMODULELIST); - jmodlist.add("mainModule", mainSpecModule); - - JsonArrayBuilder jmods = factory.createArrayBuilder(); - for (Module m : mods) { - jmods.add(toJson(m)); - } - jmodlist.add("term", jmods); - term.add("term", jmodlist); - - jsonWriter.write(term.build()); - jsonWriter.close(); - data.close(); - } catch (IOException e) { - throw KEMException.criticalError("Could not write Module to Json", e); - } - return out.toByteArray(); + return out.toByteArray(); + } + + public static byte[] apply(java.util.Set mods, String mainSpecModule) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + DataOutputStream data = new DataOutputStream(out); + JsonWriter jsonWriter = Json.createWriter(data); + + JsonObjectBuilder term = factory.createObjectBuilder(); + term.add("format", "KAST"); + term.add("version", version); + JsonObjectBuilder jmodlist = factory.createObjectBuilder(); + + jmodlist.add("node", JsonParser.KFLATMODULELIST); + jmodlist.add("mainModule", mainSpecModule); + + JsonArrayBuilder jmods = factory.createArrayBuilder(); + for (Module m : mods) { + jmods.add(toJson(m)); + } + jmodlist.add("term", jmods); + term.add("term", jmodlist); + + jsonWriter.write(term.build()); + jsonWriter.close(); + data.close(); + } catch (IOException e) { + throw KEMException.criticalError("Could not write Module to Json", e); } + return out.toByteArray(); + } - public static JsonStructure toJson(Definition def) { - JsonObjectBuilder jdef = factory.createObjectBuilder(); - - JsonArrayBuilder mods = factory.createArrayBuilder(); - for (Module m : JavaConverters.setAsJavaSet(def.modules())) { - mods.add(toJson(m)); - } - - jdef.add("node", JsonParser.KDEFINITION); - jdef.add("mainModule", def.mainModule().name()); - jdef.add("modules", mods); - jdef.add("att", toJson(def.att())); + public static JsonStructure toJson(Definition def) { + JsonObjectBuilder jdef = factory.createObjectBuilder(); - return jdef.build(); + JsonArrayBuilder mods = factory.createArrayBuilder(); + for (Module m : JavaConverters.setAsJavaSet(def.modules())) { + mods.add(toJson(m)); } - public static JsonStructure toJson(Att att) { - // Emit user groups as group(_) to prevent conflicts between user groups and internals - att = att.withUserGroupsAsGroupAtt(); - - JsonObjectBuilder jatt = factory.createObjectBuilder(); - jatt.add("node", JsonParser.KATT); - - JsonObjectBuilder jattKeys = factory.createObjectBuilder(); - for (Tuple2 attKeyPair : JavaConverters.seqAsJavaList(att.att().keys().toSeq())) { - if (attKeyPair._1().key().equals(Location.class.getName())) { - JsonArrayBuilder locarr = factory.createArrayBuilder(); - Location loc = att.get(Location.class); - locarr.add(loc.startLine()); - locarr.add(loc.startColumn()); - locarr.add(loc.endLine()); - locarr.add(loc.endColumn()); - jattKeys.add(attKeyPair._1().key(), locarr.build()); - } else if (attKeyPair._1().key().equals(Source.class.getName())){ - jattKeys.add(attKeyPair._1().key(), att.get(Source.class).source()); - } else if (attKeyPair._1().key().equals(Production.class.getName())){ - jattKeys.add(attKeyPair._1().key(), toJson(att.get(Production.class))); - } else if (attKeyPair._1().key().equals(Sort.class.getName())){ - jattKeys.add(attKeyPair._1().key(), toJson(att.get(Sort.class))); - } else if (attKeyPair._1().equals(Att.BRACKET_LABEL())) { - jattKeys.add(attKeyPair._1().key(), toJson(att.get(Att.BRACKET_LABEL(), KLabel.class))); - } else if (attKeyPair._1().equals(Att.PREDICATE())) { - jattKeys.add(attKeyPair._1().key(), toJson(att.get(Att.PREDICATE(), Sort.class))); - } else if (attKeyPair._1().equals(Att.CELL_OPT_ABSENT())) { - jattKeys.add(attKeyPair._1().key(), toJson(att.get(Att.CELL_OPT_ABSENT(), Sort.class))); - } else if (attKeyPair._1().equals(Att.CELL_FRAGMENT())) { - jattKeys.add(attKeyPair._1().key(), toJson(att.get(Att.CELL_FRAGMENT(), Sort.class))); - } else if (attKeyPair._1().equals(Att.SORT_PARAMS())) { - jattKeys.add(attKeyPair._1().key(), toJson(att.get(Att.SORT_PARAMS(), Sort.class))); - } else - jattKeys.add(attKeyPair._1().key(), att.att().get(attKeyPair).get().toString()); - } - jatt.add("att", jattKeys.build()); - - return jatt.build(); + jdef.add("node", JsonParser.KDEFINITION); + jdef.add("mainModule", def.mainModule().name()); + jdef.add("modules", mods); + jdef.add("att", toJson(def.att())); + + return jdef.build(); + } + + public static JsonStructure toJson(Att att) { + // Emit user groups as group(_) to prevent conflicts between user groups and internals + att = att.withUserGroupsAsGroupAtt(); + + JsonObjectBuilder jatt = factory.createObjectBuilder(); + jatt.add("node", JsonParser.KATT); + + JsonObjectBuilder jattKeys = factory.createObjectBuilder(); + for (Tuple2 attKeyPair : + JavaConverters.seqAsJavaList(att.att().keys().toSeq())) { + if (attKeyPair._1().key().equals(Location.class.getName())) { + JsonArrayBuilder locarr = factory.createArrayBuilder(); + Location loc = att.get(Location.class); + locarr.add(loc.startLine()); + locarr.add(loc.startColumn()); + locarr.add(loc.endLine()); + locarr.add(loc.endColumn()); + jattKeys.add(attKeyPair._1().key(), locarr.build()); + } else if (attKeyPair._1().key().equals(Source.class.getName())) { + jattKeys.add(attKeyPair._1().key(), att.get(Source.class).source()); + } else if (attKeyPair._1().key().equals(Production.class.getName())) { + jattKeys.add(attKeyPair._1().key(), toJson(att.get(Production.class))); + } else if (attKeyPair._1().key().equals(Sort.class.getName())) { + jattKeys.add(attKeyPair._1().key(), toJson(att.get(Sort.class))); + } else if (attKeyPair._1().equals(Att.BRACKET_LABEL())) { + jattKeys.add(attKeyPair._1().key(), toJson(att.get(Att.BRACKET_LABEL(), KLabel.class))); + } else if (attKeyPair._1().equals(Att.PREDICATE())) { + jattKeys.add(attKeyPair._1().key(), toJson(att.get(Att.PREDICATE(), Sort.class))); + } else if (attKeyPair._1().equals(Att.CELL_OPT_ABSENT())) { + jattKeys.add(attKeyPair._1().key(), toJson(att.get(Att.CELL_OPT_ABSENT(), Sort.class))); + } else if (attKeyPair._1().equals(Att.CELL_FRAGMENT())) { + jattKeys.add(attKeyPair._1().key(), toJson(att.get(Att.CELL_FRAGMENT(), Sort.class))); + } else if (attKeyPair._1().equals(Att.SORT_PARAMS())) { + jattKeys.add(attKeyPair._1().key(), toJson(att.get(Att.SORT_PARAMS(), Sort.class))); + } else jattKeys.add(attKeyPair._1().key(), att.att().get(attKeyPair).get().toString()); } - -/////////////////////////// -// ToJson Module Objects // -/////////////////////////// - - public static JsonStructure toJson(Module mod) { - return toJson(mod.flattened()); + jatt.add("att", jattKeys.build()); + + return jatt.build(); + } + + /////////////////////////// + // ToJson Module Objects // + /////////////////////////// + + public static JsonStructure toJson(Module mod) { + return toJson(mod.flattened()); + } + + public static JsonStructure toJson(FlatModule mod) { + JsonObjectBuilder jmod = factory.createObjectBuilder(); + + jmod.add("node", JsonParser.KFLATMODULE); + + JsonArrayBuilder imports = factory.createArrayBuilder(); + stream(mod.imports()) + .forEach( + i -> { + JsonObjectBuilder jimp = factory.createObjectBuilder(); + jimp.add("node", JsonParser.KIMPORT); + jimp.add("name", i.name()); + jimp.add("isPublic", i.isPublic()); + imports.add(jimp.build()); + }); + + JsonArrayBuilder sentences = factory.createArrayBuilder(); + mod.localSentences().foreach(s -> sentences.add(toJson(s))); + + jmod.add("name", mod.name()); + jmod.add("imports", imports); + jmod.add("localSentences", sentences); + jmod.add("att", toJson(mod.att())); + + return jmod.build(); + } + + ///////////////////////////// + // ToJSon Sentence Objects // + ///////////////////////////// + + public static JsonStructure toJson(Sentence sen) { + if (sen instanceof Context) return toJson((Context) sen); + if (sen instanceof RuleOrClaim) return toJson((RuleOrClaim) sen); + if (sen instanceof SyntaxPriority) return toJson((SyntaxPriority) sen); + if (sen instanceof SyntaxAssociativity) return toJson((SyntaxAssociativity) sen); + if (sen instanceof Configuration) return toJson((Configuration) sen); + if (sen instanceof Bubble) return toJson((Bubble) sen); + if (sen instanceof SyntaxSort) return toJson((SyntaxSort) sen); + if (sen instanceof SortSynonym) return toJson((SortSynonym) sen); + if (sen instanceof SyntaxLexical) return toJson((SyntaxLexical) sen); + if (sen instanceof Production) return toJson((Production) sen); + + JsonObjectBuilder jsen = factory.createObjectBuilder(); + jsen.add("node", "badsentence"); + return jsen.build(); + } + + public static JsonStructure toJson(Context con) { + JsonObjectBuilder jcon = factory.createObjectBuilder(); + + jcon.add("node", JsonParser.KCONTEXT); + jcon.add("body", toJson(con.body())); + jcon.add("requires", toJson(con.requires())); + jcon.add("att", toJson(con.att())); + + return jcon.build(); + } + + public static JsonStructure toJson(RuleOrClaim rule) { + JsonObjectBuilder jrule = factory.createObjectBuilder(); + + jrule.add("node", rule instanceof Rule ? JsonParser.KRULE : JsonParser.KCLAIM); + jrule.add("body", toJson(rule.body())); + jrule.add("requires", toJson(rule.requires())); + jrule.add("ensures", toJson(rule.ensures())); + jrule.add("att", toJson(rule.att())); + + return jrule.build(); + } + + public static JsonStructure toJson(SyntaxPriority syn) { + JsonObjectBuilder jsyn = factory.createObjectBuilder(); + + jsyn.add("node", JsonParser.KSYNTAXPRIORITY); + + JsonArrayBuilder priArray = factory.createArrayBuilder(); + for (Set pri : JavaConverters.seqAsJavaList(syn.priorities())) { + JsonArrayBuilder tagArray = factory.createArrayBuilder(); + pri.foreach(t -> tagArray.add(t.name())); + priArray.add(tagArray); } + jsyn.add("priorities", priArray); - public static JsonStructure toJson(FlatModule mod) { - JsonObjectBuilder jmod = factory.createObjectBuilder(); + jsyn.add("att", toJson(syn.att())); - jmod.add("node", JsonParser.KFLATMODULE); + return jsyn.build(); + } - JsonArrayBuilder imports = factory.createArrayBuilder(); - stream(mod.imports()).forEach(i -> { - JsonObjectBuilder jimp = factory.createObjectBuilder(); - jimp.add("node", JsonParser.KIMPORT); - jimp.add("name", i.name()); - jimp.add("isPublic", i.isPublic()); - imports.add(jimp.build()); - }); + public static JsonStructure toJson(SyntaxAssociativity syn) { + JsonObjectBuilder jsyn = factory.createObjectBuilder(); - JsonArrayBuilder sentences = factory.createArrayBuilder(); - mod.localSentences().foreach(s -> sentences.add(toJson(s))); + jsyn.add("node", JsonParser.KSYNTAXASSOCIATIVITY); + jsyn.add("assoc", syn.assoc().toString()); - jmod.add("name", mod.name()); - jmod.add("imports", imports); - jmod.add("localSentences", sentences); - jmod.add("att", toJson(mod.att())); + JsonArrayBuilder tagArray = factory.createArrayBuilder(); + syn.tags().foreach(t -> tagArray.add(t.name())); + jsyn.add("tags", tagArray); - return jmod.build(); - } + jsyn.add("att", toJson(syn.att())); -///////////////////////////// -// ToJSon Sentence Objects // -///////////////////////////// - - public static JsonStructure toJson(Sentence sen) { - if (sen instanceof Context) return toJson((Context) sen); - if (sen instanceof RuleOrClaim) return toJson((RuleOrClaim) sen); - if (sen instanceof SyntaxPriority) return toJson((SyntaxPriority) sen); - if (sen instanceof SyntaxAssociativity) return toJson((SyntaxAssociativity) sen); - if (sen instanceof Configuration) return toJson((Configuration) sen); - if (sen instanceof Bubble) return toJson((Bubble) sen); - if (sen instanceof SyntaxSort) return toJson((SyntaxSort) sen); - if (sen instanceof SortSynonym) return toJson((SortSynonym) sen); - if (sen instanceof SyntaxLexical) return toJson((SyntaxLexical) sen); - if (sen instanceof Production) return toJson((Production) sen); - - JsonObjectBuilder jsen = factory.createObjectBuilder(); - jsen.add("node", "badsentence"); - return jsen.build(); - } - - public static JsonStructure toJson(Context con) { - JsonObjectBuilder jcon = factory.createObjectBuilder(); - - jcon.add("node", JsonParser.KCONTEXT); - jcon.add("body", toJson(con.body())); - jcon.add("requires", toJson(con.requires())); - jcon.add("att", toJson(con.att())); - - return jcon.build(); - } - - public static JsonStructure toJson(RuleOrClaim rule) { - JsonObjectBuilder jrule = factory.createObjectBuilder(); + return jsyn.build(); + } - jrule.add("node", rule instanceof Rule ? JsonParser.KRULE : JsonParser.KCLAIM); - jrule.add("body", toJson(rule.body())); - jrule.add("requires", toJson(rule.requires())); - jrule.add("ensures", toJson(rule.ensures())); - jrule.add("att", toJson(rule.att())); - - return jrule.build(); - } + public static JsonStructure toJson(Configuration con) { + JsonObjectBuilder jcon = factory.createObjectBuilder(); - public static JsonStructure toJson(SyntaxPriority syn) { - JsonObjectBuilder jsyn = factory.createObjectBuilder(); + jcon.add("node", JsonParser.KCONFIGURATION); + jcon.add("body", toJson(con.body())); + jcon.add("ensures", toJson(con.ensures())); + jcon.add("att", toJson(con.att())); - jsyn.add("node", JsonParser.KSYNTAXPRIORITY); + return jcon.build(); + } - JsonArrayBuilder priArray = factory.createArrayBuilder(); - for (Set pri : JavaConverters.seqAsJavaList(syn.priorities())) { - JsonArrayBuilder tagArray = factory.createArrayBuilder(); - pri.foreach(t -> tagArray.add(t.name())); - priArray.add(tagArray); - } - jsyn.add("priorities", priArray); + public static JsonStructure toJson(Bubble bub) { + JsonObjectBuilder jbub = factory.createObjectBuilder(); - jsyn.add("att", toJson(syn.att())); + jbub.add("node", JsonParser.KBUBBLE); + jbub.add("sentenceType", bub.sentenceType()); + jbub.add("contents", bub.contents()); + jbub.add("att", toJson(bub.att())); - return jsyn.build(); - } - - public static JsonStructure toJson(SyntaxAssociativity syn) { - JsonObjectBuilder jsyn = factory.createObjectBuilder(); - - jsyn.add("node", JsonParser.KSYNTAXASSOCIATIVITY); - jsyn.add("assoc", syn.assoc().toString()); - - JsonArrayBuilder tagArray = factory.createArrayBuilder(); - syn.tags().foreach(t -> tagArray.add(t.name())); - jsyn.add("tags", tagArray); - - jsyn.add("att", toJson(syn.att())); - - return jsyn.build(); - } + return jbub.build(); + } - public static JsonStructure toJson(Configuration con) { - JsonObjectBuilder jcon = factory.createObjectBuilder(); + public static JsonStructure toJson(SyntaxSort syn) { + JsonObjectBuilder jsyn = factory.createObjectBuilder(); - jcon.add("node", JsonParser.KCONFIGURATION); - jcon.add("body", toJson(con.body())); - jcon.add("ensures", toJson(con.ensures())); - jcon.add("att", toJson(con.att())); + jsyn.add("node", JsonParser.KSYNTAXSORT); + jsyn.add("sort", toJson(syn.sort())); - return jcon.build(); - } + JsonArrayBuilder params = factory.createArrayBuilder(); + JavaConverters.seqAsJavaList(syn.params()).forEach(p -> params.add(toJson(p))); + jsyn.add("params", params.build()); - public static JsonStructure toJson(Bubble bub) { - JsonObjectBuilder jbub = factory.createObjectBuilder(); + jsyn.add("att", toJson(syn.att())); - jbub.add("node", JsonParser.KBUBBLE); - jbub.add("sentenceType", bub.sentenceType()); - jbub.add("contents", bub.contents()); - jbub.add("att", toJson(bub.att())); + return jsyn.build(); + } - return jbub.build(); - } + public static JsonStructure toJson(SortSynonym syn) { + JsonObjectBuilder jsyn = factory.createObjectBuilder(); - public static JsonStructure toJson(SyntaxSort syn) { - JsonObjectBuilder jsyn = factory.createObjectBuilder(); + jsyn.add("node", JsonParser.KSORTSYNONYM); + jsyn.add("newSort", toJson(syn.newSort())); + jsyn.add("oldSort", toJson(syn.oldSort())); + jsyn.add("att", toJson(syn.att())); - jsyn.add("node", JsonParser.KSYNTAXSORT); - jsyn.add("sort", toJson(syn.sort())); + return jsyn.build(); + } - JsonArrayBuilder params = factory.createArrayBuilder(); - JavaConverters.seqAsJavaList(syn.params()).forEach(p -> params.add(toJson(p))); - jsyn.add("params", params.build()); + public static JsonStructure toJson(SyntaxLexical syn) { + JsonObjectBuilder jsyn = factory.createObjectBuilder(); - jsyn.add("att", toJson(syn.att())); + jsyn.add("node", JsonParser.KSYNTAXLEXICAL); + jsyn.add("name", syn.name()); + jsyn.add("regex", syn.regex()); + jsyn.add("att", toJson(syn.att())); - return jsyn.build(); - } + return jsyn.build(); + } - public static JsonStructure toJson(SortSynonym syn) { - JsonObjectBuilder jsyn = factory.createObjectBuilder(); + public static JsonStructure toJson(Production pro) { + JsonObjectBuilder jpro = factory.createObjectBuilder(); - jsyn.add("node", JsonParser.KSORTSYNONYM); - jsyn.add("newSort", toJson(syn.newSort())); - jsyn.add("oldSort", toJson(syn.oldSort())); - jsyn.add("att", toJson(syn.att())); + jpro.add("node", JsonParser.KPRODUCTION); - return jsyn.build(); + Option klabel = pro.klabel(); + if (!klabel.isEmpty()) { + jpro.add("klabel", toJson(klabel.get())); } - public static JsonStructure toJson(SyntaxLexical syn) { - JsonObjectBuilder jsyn = factory.createObjectBuilder(); - - jsyn.add("node", JsonParser.KSYNTAXLEXICAL); - jsyn.add("name", syn.name()); - jsyn.add("regex", syn.regex()); - jsyn.add("att", toJson(syn.att())); - - return jsyn.build(); + JsonArrayBuilder productionItems = factory.createArrayBuilder(); + JavaConverters.seqAsJavaList(pro.items()).forEach(p -> productionItems.add(toJson(p))); + jpro.add("productionItems", productionItems.build()); + + JsonArrayBuilder params = factory.createArrayBuilder(); + JavaConverters.seqAsJavaList(pro.params()).forEach(p -> params.add(toJson(p))); + jpro.add("params", params.build()); + + jpro.add("sort", toJson(pro.sort())); + jpro.add("att", toJson(pro.att())); + + return jpro.build(); + } + + public static JsonObject toJson(ProductionItem prod) { + JsonObjectBuilder jsonProduction = factory.createObjectBuilder(); + + if (prod instanceof NonTerminal t) { + jsonProduction.add("node", JsonParser.KNONTERMINAL); + jsonProduction.add("sort", toJson(t.sort())); + Option name = t.name(); + if (!name.isEmpty()) jsonProduction.add("name", name.get()); + } else if (prod instanceof RegexTerminal t) { + jsonProduction.add("node", JsonParser.KREGEXTERMINAL); + jsonProduction.add("precedeRegex", t.precedeRegex()); + jsonProduction.add("regex", t.regex()); + jsonProduction.add("followRegex", t.followRegex()); + } else if (prod instanceof Terminal t) { + jsonProduction.add("node", JsonParser.KTERMINAL); + jsonProduction.add("value", t.value()); } - public static JsonStructure toJson(Production pro) { - JsonObjectBuilder jpro = factory.createObjectBuilder(); + return jsonProduction.build(); + } - jpro.add("node", JsonParser.KPRODUCTION); + public static JsonStructure toJson(Sort sort) { + JsonObjectBuilder jsort = factory.createObjectBuilder(); - Option klabel = pro.klabel(); - if (! klabel.isEmpty()) { - jpro.add("klabel", toJson(klabel.get())); - } + jsort.add("node", JsonParser.KSORT); + // store sort and its parameters as a flat string + jsort.add("name", sort.toString()); - JsonArrayBuilder productionItems = factory.createArrayBuilder(); - JavaConverters.seqAsJavaList(pro.items()).forEach(p -> productionItems.add(toJson(p))); - jpro.add("productionItems", productionItems.build()); + return jsort.build(); + } - JsonArrayBuilder params = factory.createArrayBuilder(); - JavaConverters.seqAsJavaList(pro.params()).forEach(p -> params.add(toJson(p))); - jpro.add("params", params.build()); + ////////////////////// + // ToJson K Objects // + ////////////////////// - jpro.add("sort", toJson(pro.sort())); - jpro.add("att", toJson(pro.att())); + public static void apply(OutputStream out, K k) { + try { + DataOutputStream data = new DataOutputStream(out); + JsonWriter jsonWriter = Json.createWriter(data); - return jpro.build(); - } + JsonObjectBuilder kterm = factory.createObjectBuilder(); + kterm.add("format", "KAST"); + kterm.add("version", version); + kterm.add("term", toJson(k)); - public static JsonObject toJson(ProductionItem prod) { - JsonObjectBuilder jsonProduction = factory.createObjectBuilder(); - - if (prod instanceof NonTerminal t) { - jsonProduction.add("node", JsonParser.KNONTERMINAL); - jsonProduction.add("sort", toJson(t.sort())); - Option name = t.name(); - if (! name.isEmpty()) - jsonProduction.add("name", name.get()); - } else if (prod instanceof RegexTerminal t) { - jsonProduction.add("node", JsonParser.KREGEXTERMINAL); - jsonProduction.add("precedeRegex", t.precedeRegex()); - jsonProduction.add("regex", t.regex()); - jsonProduction.add("followRegex", t.followRegex()); - } else if (prod instanceof Terminal t) { - jsonProduction.add("node", JsonParser.KTERMINAL); - jsonProduction.add("value", t.value()); - } - - return jsonProduction.build(); + jsonWriter.write(kterm.build()); + jsonWriter.close(); + data.close(); + } catch (IOException e) { + throw KEMException.criticalError("Could not write K term to Json", e, k); } + } - public static JsonStructure toJson(Sort sort) { - JsonObjectBuilder jsort = factory.createObjectBuilder(); + public static byte[] apply(K k) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + apply(out, k); + return out.toByteArray(); + } - jsort.add("node", JsonParser.KSORT); - // store sort and its parameters as a flat string - jsort.add("name", sort.toString()); + public static JsonStructure toJson(K k) { + JsonObjectBuilder knode = factory.createObjectBuilder(); + if (k instanceof KToken tok) { - return jsort.build(); - } + knode.add("node", JsonParser.KTOKEN); + knode.add("sort", toJson(tok.sort())); + knode.add("token", tok.s()); -////////////////////// -// ToJson K Objects // -////////////////////// - - public static void apply(OutputStream out, K k) { - try { - DataOutputStream data = new DataOutputStream(out); - JsonWriter jsonWriter = Json.createWriter(data); - - JsonObjectBuilder kterm = factory.createObjectBuilder(); - kterm.add("format", "KAST"); - kterm.add("version", version); - kterm.add("term", toJson(k)); - - jsonWriter.write(kterm.build()); - jsonWriter.close(); - data.close(); - } catch (IOException e) { - throw KEMException.criticalError("Could not write K term to Json", e, k); - } - } + } else if (k instanceof KApply app) { - public static byte[] apply(K k) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - apply(out, k); - return out.toByteArray(); - } - - public static JsonStructure toJson(K k) { - JsonObjectBuilder knode = factory.createObjectBuilder(); - if (k instanceof KToken tok) { - - knode.add("node", JsonParser.KTOKEN); - knode.add("sort", toJson(tok.sort())); - knode.add("token", tok.s()); - - } else if (k instanceof KApply app) { + knode.add("node", JsonParser.KAPPLY); + knode.add("label", toJson(((KApply) k).klabel())); - knode.add("node", JsonParser.KAPPLY); - knode.add("label", toJson(((KApply) k).klabel())); + JsonArrayBuilder args = factory.createArrayBuilder(); + for (K item : app.klist().asIterable()) { + args.add(toJson(item)); + } - JsonArrayBuilder args = factory.createArrayBuilder(); - for (K item : app.klist().asIterable()) { - args.add(toJson(item)); - } + knode.add("arity", app.klist().size()); + knode.add("args", args.build()); - knode.add("arity", app.klist().size()); - knode.add("args", args.build()); + } else if (k instanceof KSequence seq) { - } else if (k instanceof KSequence seq) { + knode.add("node", JsonParser.KSEQUENCE); - knode.add("node", JsonParser.KSEQUENCE); + JsonArrayBuilder items = factory.createArrayBuilder(); + for (K item : seq.asIterable()) { + items.add(toJson(item)); + } - JsonArrayBuilder items = factory.createArrayBuilder(); - for (K item : seq.asIterable()) { - items.add(toJson(item)); - } + knode.add("arity", seq.size()); + knode.add("items", items.build()); - knode.add("arity", seq.size()); - knode.add("items", items.build()); + } else if (k instanceof KVariable var) { - } else if (k instanceof KVariable var) { + knode.add("node", JsonParser.KVARIABLE); + knode.add("name", var.name()); + if (k.att().contains(Sort.class)) { + knode.add("sort", toJson(k.att().get(Sort.class))); + } - knode.add("node", JsonParser.KVARIABLE); - knode.add("name", var.name()); - if (k.att().contains(Sort.class)) { - knode.add("sort", toJson(k.att().get(Sort.class))); - } + } else if (k instanceof KRewrite rew) { - } else if (k instanceof KRewrite rew) { + knode.add("node", JsonParser.KREWRITE); + knode.add("lhs", toJson(rew.left())); + knode.add("rhs", toJson(rew.right())); - knode.add("node", JsonParser.KREWRITE); - knode.add("lhs", toJson(rew.left())); - knode.add("rhs", toJson(rew.right())); + } else if (k instanceof KAs alias) { - } else if (k instanceof KAs alias) { + knode.add("node", JsonParser.KAS); + knode.add("pattern", toJson(alias.pattern())); + knode.add("alias", toJson(alias.alias())); - knode.add("node", JsonParser.KAS); - knode.add("pattern", toJson(alias.pattern())); - knode.add("alias", toJson(alias.alias())); + } else if (k instanceof InjectedKLabel inj) { - } else if (k instanceof InjectedKLabel inj) { - - knode.add("node", JsonParser.INJECTEDKLABEL); - knode.add("label", toJson(inj.klabel())); - - } else { - throw KEMException.criticalError("Unimplemented for JSON serialization: ", k); - } - return knode.build(); - } + knode.add("node", JsonParser.INJECTEDKLABEL); + knode.add("label", toJson(inj.klabel())); - public static JsonStructure toJson(KLabel kl) { - JsonObjectBuilder jkl = factory.createObjectBuilder(); - jkl.add("node", "KLabel"); - jkl.add("name", kl.name()); - JsonArrayBuilder params = factory.createArrayBuilder(); - for (Sort s : mutable(kl.params())) - params.add(toJson(s)); - jkl.add("params", params.build()); - return jkl.build(); + } else { + throw KEMException.criticalError("Unimplemented for JSON serialization: ", k); } + return knode.build(); + } + + public static JsonStructure toJson(KLabel kl) { + JsonObjectBuilder jkl = factory.createObjectBuilder(); + jkl.add("node", "KLabel"); + jkl.add("name", kl.name()); + JsonArrayBuilder params = factory.createArrayBuilder(); + for (Sort s : mutable(kl.params())) params.add(toJson(s)); + jkl.add("params", params.build()); + return jkl.build(); + } } diff --git a/kernel/src/main/java/org/kframework/unparser/ToLatex.java b/kernel/src/main/java/org/kframework/unparser/ToLatex.java index 2b99a1ca0e0..161c72e7d35 100644 --- a/kernel/src/main/java/org/kframework/unparser/ToLatex.java +++ b/kernel/src/main/java/org/kframework/unparser/ToLatex.java @@ -1,6 +1,13 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.unparser; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Optional; +import java.util.regex.Pattern; import org.kframework.attributes.Att; import org.kframework.kore.InjectedKLabel; import org.kframework.kore.K; @@ -10,129 +17,121 @@ import org.kframework.kore.KSequence; import org.kframework.kore.KToken; import org.kframework.kore.KVariable; -import org.kframework.utils.errorsystem.KEMException; import org.kframework.utils.StringUtil; +import org.kframework.utils.errorsystem.KEMException; -import java.io.IOException; -import java.io.DataOutputStream; -import java.io.ByteArrayOutputStream; -import java.nio.charset.StandardCharsets; - -import java.util.Arrays; -import java.util.Optional; -import java.util.regex.Pattern; - -/** - * Writes a KAST term to the LaTeX format. - */ +/** Writes a KAST term to the LaTeX format. */ public class ToLatex { - public static byte[] apply(K k) { - try { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - apply(new DataOutputStream(out), k); - return out.toByteArray(); - } catch (IOException e) { - throw KEMException.criticalError("Could not write K term to LaTeX", e, k); - } - } - - private static String[] asciiReadableEncodingLatexCalc() { - String[] latexEncoder = Arrays.copyOf(StringUtil.asciiReadableEncodingDefault, StringUtil.asciiReadableEncodingDefault.length); - latexEncoder[0x30] = "Zero"; - latexEncoder[0x31] = "I"; - latexEncoder[0x32] = "II"; - latexEncoder[0x33] = "III"; - latexEncoder[0x34] = "IV"; - latexEncoder[0x35] = "V"; - latexEncoder[0x36] = "VI"; - latexEncoder[0x37] = "VII"; - latexEncoder[0x38] = "VIII"; - latexEncoder[0x39] = "IX"; - latexEncoder[0x7a] = "ActZ"; - return latexEncoder; - } - - public static final Pattern identChar = Pattern.compile("[A-Za-y]"); - public static String[] asciiReadableEncodingLatex = asciiReadableEncodingLatexCalc(); - - public static String latexedKLabel(String orig) { - StringBuilder buffer = new StringBuilder(); - StringUtil.encodeStringToAlphanumeric(buffer, orig, asciiReadableEncodingLatex, identChar, "z"); - return "klabel" + buffer; + public static byte[] apply(K k) { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + apply(new DataOutputStream(out), k); + return out.toByteArray(); + } catch (IOException e) { + throw KEMException.criticalError("Could not write K term to LaTeX", e, k); } - - private static void writeString(DataOutputStream out, String str) throws IOException { - out.write(str.getBytes(StandardCharsets.UTF_8)); - } - - public static void apply(DataOutputStream out, Att att) throws IOException { - writeString(out, ("\\outerAtt{" + att.toString() + "}")); - } - - public static void apply(DataOutputStream out, K k) throws IOException { - if (k instanceof KToken tok) { - - writeString(out, ("\\texttt{ " + tok.s() + " }")); - - } else if (k instanceof KApply app) { - - writeString(out, ("\\" + latexedKLabel(app.klabel().name()))); - - for (K item : app.klist().asIterable()) { - writeString(out, "{"); - apply(out, item); - writeString(out, "}"); - } - - } else if (k instanceof KSequence kseq) { - - writeString(out, "\\kseq{"); - - for (K item : kseq.asIterable()) { - apply(out, item); - writeString(out, "}{\\kseq{"); - } - - writeString(out, "}{\\dotk{}}"); - - } else if (k instanceof KVariable var) { - - Optional origName = var.att().getOptional(Att.ORIGINAL_NAME()); - if (origName.isPresent()) { - writeString(out, origName.get()); - } else { - writeString(out, var.name()); - } - - } else if (k instanceof KRewrite rew) { - - writeString(out, "\\krewrites{"); - apply(out, rew.left()); - writeString(out, "}{"); - apply(out, rew.right()); - writeString(out, "}{"); - apply(out, rew.att()); - writeString(out, "}"); - - } else if (k instanceof KAs alias) { - - writeString(out, "\\kas{"); - apply(out, alias.pattern()); - writeString(out, "}{"); - apply(out, alias.alias()); - writeString(out, "}{"); - apply(out, alias.att()); - writeString(out, "}"); - - } else if (k instanceof InjectedKLabel inj) { - - writeString(out, "\\injectedklabel{"); - writeString(out, inj.klabel().name()); - writeString(out, "}"); - - } else { - throw KEMException.criticalError("Unimplemented for LaTeX serialization: ", k); - } + } + + private static String[] asciiReadableEncodingLatexCalc() { + String[] latexEncoder = + Arrays.copyOf( + StringUtil.asciiReadableEncodingDefault, + StringUtil.asciiReadableEncodingDefault.length); + latexEncoder[0x30] = "Zero"; + latexEncoder[0x31] = "I"; + latexEncoder[0x32] = "II"; + latexEncoder[0x33] = "III"; + latexEncoder[0x34] = "IV"; + latexEncoder[0x35] = "V"; + latexEncoder[0x36] = "VI"; + latexEncoder[0x37] = "VII"; + latexEncoder[0x38] = "VIII"; + latexEncoder[0x39] = "IX"; + latexEncoder[0x7a] = "ActZ"; + return latexEncoder; + } + + public static final Pattern identChar = Pattern.compile("[A-Za-y]"); + public static String[] asciiReadableEncodingLatex = asciiReadableEncodingLatexCalc(); + + public static String latexedKLabel(String orig) { + StringBuilder buffer = new StringBuilder(); + StringUtil.encodeStringToAlphanumeric(buffer, orig, asciiReadableEncodingLatex, identChar, "z"); + return "klabel" + buffer; + } + + private static void writeString(DataOutputStream out, String str) throws IOException { + out.write(str.getBytes(StandardCharsets.UTF_8)); + } + + public static void apply(DataOutputStream out, Att att) throws IOException { + writeString(out, ("\\outerAtt{" + att.toString() + "}")); + } + + public static void apply(DataOutputStream out, K k) throws IOException { + if (k instanceof KToken tok) { + + writeString(out, ("\\texttt{ " + tok.s() + " }")); + + } else if (k instanceof KApply app) { + + writeString(out, ("\\" + latexedKLabel(app.klabel().name()))); + + for (K item : app.klist().asIterable()) { + writeString(out, "{"); + apply(out, item); + writeString(out, "}"); + } + + } else if (k instanceof KSequence kseq) { + + writeString(out, "\\kseq{"); + + for (K item : kseq.asIterable()) { + apply(out, item); + writeString(out, "}{\\kseq{"); + } + + writeString(out, "}{\\dotk{}}"); + + } else if (k instanceof KVariable var) { + + Optional origName = var.att().getOptional(Att.ORIGINAL_NAME()); + if (origName.isPresent()) { + writeString(out, origName.get()); + } else { + writeString(out, var.name()); + } + + } else if (k instanceof KRewrite rew) { + + writeString(out, "\\krewrites{"); + apply(out, rew.left()); + writeString(out, "}{"); + apply(out, rew.right()); + writeString(out, "}{"); + apply(out, rew.att()); + writeString(out, "}"); + + } else if (k instanceof KAs alias) { + + writeString(out, "\\kas{"); + apply(out, alias.pattern()); + writeString(out, "}{"); + apply(out, alias.alias()); + writeString(out, "}{"); + apply(out, alias.att()); + writeString(out, "}"); + + } else if (k instanceof InjectedKLabel inj) { + + writeString(out, "\\injectedklabel{"); + writeString(out, inj.klabel().name()); + writeString(out, "}"); + + } else { + throw KEMException.criticalError("Unimplemented for LaTeX serialization: ", k); } + } } diff --git a/kernel/src/main/java/org/kframework/utils/BinaryLoader.java b/kernel/src/main/java/org/kframework/utils/BinaryLoader.java index cecc17435b0..108a462f732 100644 --- a/kernel/src/main/java/org/kframework/utils/BinaryLoader.java +++ b/kernel/src/main/java/org/kframework/utils/BinaryLoader.java @@ -2,12 +2,6 @@ package org.kframework.utils; import com.google.inject.Inject; -import javax.annotation.Nullable; -import org.kframework.utils.errorsystem.KEMException; -import org.kframework.utils.errorsystem.KException.ExceptionType; -import org.kframework.utils.errorsystem.KExceptionManager; -import org.kframework.utils.inject.RequestScoped; - import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; @@ -18,81 +12,87 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamException; -import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; -import java.nio.channels.OverlappingFileLockException; -import java.util.Collections; -import java.util.HashMap; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; +import javax.annotation.Nullable; +import org.kframework.utils.errorsystem.KEMException; +import org.kframework.utils.errorsystem.KException.ExceptionType; +import org.kframework.utils.errorsystem.KExceptionManager; +import org.kframework.utils.inject.RequestScoped; @RequestScoped public class BinaryLoader { - private final KExceptionManager kem; + private final KExceptionManager kem; - @Inject - public BinaryLoader(KExceptionManager kem) { - this.kem = kem; - } + @Inject + public BinaryLoader(KExceptionManager kem) { + this.kem = kem; + } - public void saveOrDie(File file, Object o) { - File dir = file.getAbsoluteFile().getParentFile(); - if (!dir.exists() && !dir.mkdirs()) { - throw KEMException.criticalError("Could not create directory " + dir); - } - try { - saveImpl(file, o); - } catch (IOException e) { - throw KEMException.criticalError("Could not write to " + file.getAbsolutePath(), e); - } + public void saveOrDie(File file, Object o) { + File dir = file.getAbsoluteFile().getParentFile(); + if (!dir.exists() && !dir.mkdirs()) { + throw KEMException.criticalError("Could not create directory " + dir); + } + try { + saveImpl(file, o); + } catch (IOException e) { + throw KEMException.criticalError("Could not write to " + file.getAbsolutePath(), e); } + } - public T loadOrDie(Class cls, File file) { - try { - return loadImpl(file, cls); - } catch (ClassNotFoundException e) { - throw new AssertionError("Something wrong with deserialization", e); - } catch (ObjectStreamException e) { - throw KEMException.criticalError("Kompiled definition is out of date with " - + "the latest version of the K tool. Please re-run kompile and try again.", e); - } catch (IOException e) { - throw KEMException.criticalError("Could not read from " + file.getAbsolutePath(), e); - } + public T loadOrDie(Class cls, File file) { + try { + return loadImpl(file, cls); + } catch (ClassNotFoundException e) { + throw new AssertionError("Something wrong with deserialization", e); + } catch (ObjectStreamException e) { + throw KEMException.criticalError( + "Kompiled definition is out of date with " + + "the latest version of the K tool. Please re-run kompile and try again.", + e); + } catch (IOException e) { + throw KEMException.criticalError("Could not read from " + file.getAbsolutePath(), e); } + } - @Nullable - public T loadCache(Class cls, File file) { - try { - return loadImpl(file, cls); - } catch (FileNotFoundException e) { - //ignored - } catch (IOException | ClassNotFoundException e) { - kem.registerInternalWarning(ExceptionType.INVALIDATED_CACHE, "Invalidating serialized cache due to corruption.", e); - } - return null; + @Nullable + public T loadCache(Class cls, File file) { + try { + return loadImpl(file, cls); + } catch (FileNotFoundException e) { + // ignored + } catch (IOException | ClassNotFoundException e) { + kem.registerInternalWarning( + ExceptionType.INVALIDATED_CACHE, "Invalidating serialized cache due to corruption.", e); } + return null; + } - /** - * Locks the file before writing, so that it cannot be read by another instance of K. If the file is currently in - * use, this method will block until lock can be acquired. - */ - private void saveImpl(File file, Object o) throws IOException { - // we want to atomically update the file in case two kprove threads are writing to the same cache at the same time. - Path tempFile = Files.createTempFile(file.getCanonicalFile().getParentFile().toPath(), "tmp", ".bin"); - try (ObjectOutputStream serializer = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(tempFile.toFile())))) { - serializer.writeObject(o); - } - Files.move(tempFile, file.toPath(), StandardCopyOption.ATOMIC_MOVE); + /** + * Locks the file before writing, so that it cannot be read by another instance of K. If the file + * is currently in use, this method will block until lock can be acquired. + */ + private void saveImpl(File file, Object o) throws IOException { + // we want to atomically update the file in case two kprove threads are writing to the same + // cache at the same time. + Path tempFile = + Files.createTempFile(file.getCanonicalFile().getParentFile().toPath(), "tmp", ".bin"); + try (ObjectOutputStream serializer = + new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(tempFile.toFile())))) { + serializer.writeObject(o); } + Files.move(tempFile, file.toPath(), StandardCopyOption.ATOMIC_MOVE); + } - private T loadImpl(File file, Class cls) throws IOException, ClassNotFoundException { - try (ObjectInputStream deserializer = new ObjectInputStream(new BufferedInputStream(new FileInputStream(file)))) { //already buffered - Object obj = deserializer.readObject(); - return cls.cast(obj); - } + private T loadImpl(File file, Class cls) throws IOException, ClassNotFoundException { + try (ObjectInputStream deserializer = + new ObjectInputStream( + new BufferedInputStream(new FileInputStream(file)))) { // already buffered + Object obj = deserializer.readObject(); + return cls.cast(obj); } + } } diff --git a/kernel/src/main/java/org/kframework/utils/ColorUtil.java b/kernel/src/main/java/org/kframework/utils/ColorUtil.java index f202543f20b..f72c70bf4fa 100644 --- a/kernel/src/main/java/org/kframework/utils/ColorUtil.java +++ b/kernel/src/main/java/org/kframework/utils/ColorUtil.java @@ -1,522 +1,520 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils; -import org.kframework.unparser.ColorSetting; - import java.awt.Color; import java.awt.color.ColorSpace; import java.lang.reflect.Field; import java.util.Collections; -import java.util.Map; import java.util.HashMap; +import java.util.Map; +import org.kframework.unparser.ColorSetting; public final class ColorUtil { - private ColorUtil() {} - - private static Map colors; - - /** - * Terminal code corresponding to closest color for this one, from the list of basic 8 - * terminal codes only. - */ - private static final Map ansiColorsToTerminalCodes; - - /** - * Terminal code corresponding to closest color for this one, from the list of 216 colors supported by - * linux terminals. - */ - private static final Map eightBitColorsToTerminalCodes; - - /** - * A cache to avoid computing the closest terminal color for a given color each time it is needed. - */ - private static final Map, Map> colorToCodeConvertCache; - - public static Map colors() { - colors = doInitColors(); - return Collections.unmodifiableMap(colors); - } - - static { - colors = doInitColors(); - ansiColorsToTerminalCodes = initAnsiColors(); - eightBitColorsToTerminalCodes = initEightBitColors(); - colorToCodeConvertCache = initColorToCodeConvertCache(); + private ColorUtil() {} + + private static Map colors; + + /** + * Terminal code corresponding to closest color for this one, from the list of basic 8 terminal + * codes only. + */ + private static final Map ansiColorsToTerminalCodes; + + /** + * Terminal code corresponding to closest color for this one, from the list of 216 colors + * supported by linux terminals. + */ + private static final Map eightBitColorsToTerminalCodes; + + /** + * A cache to avoid computing the closest terminal color for a given color each time it is needed. + */ + private static final Map, Map> colorToCodeConvertCache; + + public static Map colors() { + colors = doInitColors(); + return Collections.unmodifiableMap(colors); + } + + static { + colors = doInitColors(); + ansiColorsToTerminalCodes = initAnsiColors(); + eightBitColorsToTerminalCodes = initEightBitColors(); + colorToCodeConvertCache = initColorToCodeConvertCache(); + } + + private static HashMap, Map> initColorToCodeConvertCache() { + HashMap, Map> map = new HashMap<>(); + map.put(ansiColorsToTerminalCodes, new HashMap()); + map.put(eightBitColorsToTerminalCodes, new HashMap()); + return map; + } + + private static Map doInitColors() { + Map colors = new HashMap(); + colors.put("black", Color.black); + colors.put("blue", Color.blue); + colors.put("brown", getColorByRgb("#C08040")); + colors.put("cyan", Color.cyan); + colors.put("darkgray", Color.darkGray); + colors.put("gray", Color.gray); + colors.put("green", Color.green); + colors.put("lightgray", Color.lightGray); + colors.put("lime", getColorByRgb("#C0FF00")); + colors.put("magenta", Color.magenta); + colors.put("olive", getColorByRgb("#808000")); + colors.put("orange", Color.orange); + colors.put("pink", Color.pink); + colors.put("purple", getColorByRgb("#C00040")); + colors.put("red", Color.red); + colors.put("teal", getColorByRgb("#008080")); + colors.put("violet", getColorByRgb("#800080")); + colors.put("white", Color.white); + colors.put("yellow", Color.yellow); + colors.put("Apricot", getColorByRgb("#FBB982")); + colors.put("Aquamarine", getColorByRgb("#00B5BE")); + colors.put("Bittersweet", getColorByRgb("#C04F17")); + colors.put("Black", getColorByRgb("#221E1F")); + colors.put("Blue", getColorByRgb("#2D2F92")); + colors.put("BlueGreen", getColorByRgb("#00B3B8")); + colors.put("BlueViolet", getColorByRgb("#473992")); + colors.put("BrickRed", getColorByRgb("#B6321C")); + colors.put("Brown", getColorByRgb("#792500")); + colors.put("BurntOrange", getColorByRgb("#F7921D")); + colors.put("CadetBlue", getColorByRgb("#74729A")); + colors.put("CarnationPink", getColorByRgb("#F282B4")); + colors.put("Cerulean", getColorByRgb("#00A2E3")); + colors.put("CornflowerBlue", getColorByRgb("#41B0E4")); + colors.put("Cyan", getColorByRgb("#00AEEF")); + colors.put("Dandelion", getColorByRgb("#FDBC42")); + colors.put("DarkOrchid", getColorByRgb("#A4538A")); + colors.put("Emerald", getColorByRgb("#00A99D")); + colors.put("ForestGreen", getColorByRgb("#009B55")); + colors.put("Fuchsia", getColorByRgb("#8C368C")); + colors.put("Goldenrod", getColorByRgb("#FFDF42")); + colors.put("Gray", getColorByRgb("#949698")); + colors.put("Green", getColorByRgb("#00A64F")); + colors.put("GreenYellow", getColorByRgb("#DFE674")); + colors.put("JungleGreen", getColorByRgb("#00A99A")); + colors.put("Lavender", getColorByRgb("#F49EC4")); + colors.put("LimeGreen", getColorByRgb("#8DC73E")); + colors.put("Magenta", getColorByRgb("#EC008C")); + colors.put("Mahogany", getColorByRgb("#A9341F")); + colors.put("Maroon", getColorByRgb("#AF3235")); + colors.put("Melon", getColorByRgb("#F89E7B")); + colors.put("MidnightBlue", getColorByRgb("#006795")); + colors.put("Mulberry", getColorByRgb("#A93C93")); + colors.put("NavyBlue", getColorByRgb("#006EB8")); + colors.put("OliveGreen", getColorByRgb("#3C8031")); + colors.put("Orange", getColorByRgb("#F58137")); + colors.put("OrangeRed", getColorByRgb("#ED135A")); + colors.put("Orchid", getColorByRgb("#AF72B0")); + colors.put("Peach", getColorByRgb("#F7965A")); + colors.put("Periwinkle", getColorByRgb("#7977B8")); + colors.put("PineGreen", getColorByRgb("#008B72")); + colors.put("Plum", getColorByRgb("#92268F")); + colors.put("ProcessBlue", getColorByRgb("#00B0F0")); + colors.put("Purple", getColorByRgb("#99479B")); + colors.put("RawSienna", getColorByRgb("#974006")); + colors.put("Red", getColorByRgb("#ED1B23")); + colors.put("RedOrange", getColorByRgb("#F26035")); + colors.put("RedViolet", getColorByRgb("#A1246B")); + colors.put("Rhodamine", getColorByRgb("#EF559F")); + colors.put("RoyalBlue", getColorByRgb("#0071BC")); + colors.put("RoyalPurple", getColorByRgb("#613F99")); + colors.put("RubineRed", getColorByRgb("#ED017D")); + colors.put("Salmon", getColorByRgb("#F69289")); + colors.put("SeaGreen", getColorByRgb("#3FBC9D")); + colors.put("Sepia", getColorByRgb("#671800")); + colors.put("SkyBlue", getColorByRgb("#46C5DD")); + colors.put("SpringGreen", getColorByRgb("#C6DC67")); + colors.put("Tan", getColorByRgb("#DA9D76")); + colors.put("TealBlue", getColorByRgb("#00AEB3")); + colors.put("Thistle", getColorByRgb("#D883B7")); + colors.put("Turquoise", getColorByRgb("#00B4CE")); + colors.put("Violet", getColorByRgb("#58429B")); + colors.put("VioletRed", getColorByRgb("#EF58A0")); + colors.put("White", getColorByRgb("#FFFFFF")); + colors.put("WildStrawberry", getColorByRgb("#EE2967")); + colors.put("Yellow", getColorByRgb("#FFF200")); + colors.put("YellowGreen", getColorByRgb("#98CC70")); + colors.put("YellowOrange", getColorByRgb("#FAA21A")); + + addSvgnamesColors(colors); + + return Collections.unmodifiableMap(colors); + } + + private static void addSvgnamesColors(Map colors) { + Object[][] svgColors = { + {"AliceBlue", .94, .972, 1}, + {"AntiqueWhite", .98, .92, .844}, + {"Aqua", 0, 1, 1}, + {"Aquamarine", .498, 1, .83}, + {"Azure", .94, 1, 1}, + {"Beige", .96, .96, .864}, + {"Bisque", 1, .894, .77}, + {"Black", 0, 0, 0}, + {"BlanchedAlmond", 1, .92, .804}, + {"Blue", 0, 0, 1}, + {"BlueViolet", .54, .17, .888}, + {"Brown", .648, .165, .165}, + {"BurlyWood", .87, .72, .53}, + {"CadetBlue", .372, .62, .628}, + {"Chartreuse", .498, 1, 0}, + {"Chocolate", .824, .41, .116}, + {"Coral", 1, .498, .312}, + {"CornflowerBlue", .392, .585, .93}, + {"Cornsilk", 1, .972, .864}, + {"Crimson", .864, .08, .235}, + {"Cyan", 0, 1, 1}, + {"DarkBlue", 0, 0, .545}, + {"DarkCyan", 0, .545, .545}, + {"DarkGoldenrod", .72, .525, .044}, + {"DarkGray", .664, .664, .664}, + {"DarkGreen", 0, .392, 0}, + {"DarkGrey", .664, .664, .664}, + {"DarkKhaki", .74, .716, .42}, + {"DarkMagenta", .545, 0, .545}, + {"DarkOliveGreen", .332, .42, .185}, + {"DarkOrange", 1, .55, 0}, + {"DarkOrchid", .6, .196, .8}, + {"DarkRed", .545, 0, 0}, + {"DarkSalmon", .912, .59, .48}, + {"DarkSeaGreen", .56, .736, .56}, + {"DarkSlateBlue", .284, .24, .545}, + {"DarkSlateGray", .185, .31, .31}, + {"DarkSlateGrey", .185, .31, .31}, + {"DarkTurquoise", 0, .808, .82}, + {"DarkViolet", .58, 0, .828}, + {"DeepPink", 1, .08, .576}, + {"DeepSkyBlue", 0, .75, 1}, + {"DimGray", .41, .41, .41}, + {"DimGrey", .41, .41, .41}, + {"DodgerBlue", .116, .565, 1}, + {"FireBrick", .698, .132, .132}, + {"FloralWhite", 1, .98, .94}, + {"ForestGreen", .132, .545, .132}, + {"Fuchsia", 1, 0, 1}, + {"Gainsboro", .864, .864, .864}, + {"GhostWhite", .972, .972, 1}, + {"Gold", 1, .844, 0}, + {"Goldenrod", .855, .648, .125}, + {"Gray", .5, .5, .5}, + {"Green", 0, .5, 0}, + {"GreenYellow", .68, 1, .185}, + {"Grey", .5, .5, .5}, + {"Honeydew", .94, 1, .94}, + {"HotPink", 1, .41, .705}, + {"IndianRed", .804, .36, .36}, + {"Indigo", .294, 0, .51}, + {"Ivory", 1, 1, .94}, + {"Khaki", .94, .9, .55}, + {"Lavender", .9, .9, .98}, + {"LavenderBlush", 1, .94, .96}, + {"LawnGreen", .488, .99, 0}, + {"LemonChiffon", 1, .98, .804}, + {"LightBlue", .68, .848, .9}, + {"LightCoral", .94, .5, .5}, + {"LightCyan", .88, 1, 1}, + {"LightGoldenrod", .933, .867, .51}, + {"LightGoldenrodYellow", .98, .98, .824}, + {"LightGray", .828, .828, .828}, + {"LightGreen", .565, .932, .565}, + {"LightGrey", .828, .828, .828}, + {"LightPink", 1, .712, .756}, + {"LightSalmon", 1, .628, .48}, + {"LightSeaGreen", .125, .698, .668}, + {"LightSkyBlue", .53, .808, .98}, + {"LightSlateBlue", .518, .44, 1}, + {"LightSlateGray", .468, .532, .6}, + {"LightSlateGrey", .468, .532, .6}, + {"LightSteelBlue", .69, .77, .87}, + {"LightYellow", 1, 1, .88}, + {"Lime", 0, 1, 0}, + {"LimeGreen", .196, .804, .196}, + {"Linen", .98, .94, .9}, + {"Magenta", 1, 0, 1}, + {"Maroon", .5, 0, 0}, + {"MediumAquamarine", .4, .804, .668}, + {"MediumBlue", 0, 0, .804}, + {"MediumOrchid", .73, .332, .828}, + {"MediumPurple", .576, .44, .86}, + {"MediumSeaGreen", .235, .7, .444}, + {"MediumSlateBlue", .484, .408, .932}, + {"MediumSpringGreen", 0, .98, .604}, + {"MediumTurquoise", .284, .82, .8}, + {"MediumVioletRed", .78, .084, .52}, + {"MidnightBlue", .098, .098, .44}, + {"MintCream", .96, 1, .98}, + {"MistyRose", 1, .894, .884}, + {"Moccasin", 1, .894, .71}, + {"NavajoWhite", 1, .87, .68}, + {"Navy", 0, 0, .5}, + {"NavyBlue", 0, 0, .5}, + {"OldLace", .992, .96, .9}, + {"Olive", .5, .5, 0}, + {"OliveDrab", .42, .556, .136}, + {"Orange", 1, .648, 0}, + {"OrangeRed", 1, .27, 0}, + {"Orchid", .855, .44, .84}, + {"PaleGoldenrod", .932, .91, .668}, + {"PaleGreen", .596, .985, .596}, + {"PaleTurquoise", .688, .932, .932}, + {"PaleVioletRed", .86, .44, .576}, + {"PapayaWhip", 1, .936, .835}, + {"PeachPuff", 1, .855, .725}, + {"Peru", .804, .52, .248}, + {"Pink", 1, .752, .796}, + {"Plum", .868, .628, .868}, + {"PowderBlue", .69, .88, .9}, + {"Purple", .5, 0, .5}, + {"Red", 1, 0, 0}, + {"RosyBrown", .736, .56, .56}, + {"RoyalBlue", .255, .41, .884}, + {"SaddleBrown", .545, .27, .075}, + {"Salmon", .98, .5, .448}, + {"SandyBrown", .956, .644, .376}, + {"SeaGreen", .18, .545, .34}, + {"Seashell", 1, .96, .932}, + {"Sienna", .628, .32, .176}, + {"Silver", .752, .752, .752}, + {"SkyBlue", .53, .808, .92}, + {"SlateBlue", .415, .352, .804}, + {"SlateGray", .44, .5, .565}, + {"SlateGrey", .44, .5, .565}, + {"Snow", 1, .98, .98}, + {"SpringGreen", 0, 1, .498}, + {"SteelBlue", .275, .51, .705}, + {"Tan", .824, .705, .55}, + {"Teal", 0, .5, .5}, + {"Thistle", .848, .75, .848}, + {"Tomato", 1, .39, .28}, + {"Turquoise", .25, .88, .815}, + {"Violet", .932, .51, .932}, + {"VioletRed", .816, .125, .565}, + {"Wheat", .96, .87, .7}, + {"White", 1, 1, 1}, + {"WhiteSmoke", .96, .96, .96}, + {"Yellow", 1, 1, 0}, + {"YellowGreen", .604, .804, .196}, + }; + for (Object[] rawColor : svgColors) { + colors.put( + (String) rawColor[0], + new Color(toFloat(rawColor[1]), toFloat(rawColor[2]), toFloat(rawColor[3]))); } - - private static HashMap, Map> initColorToCodeConvertCache() { - HashMap, Map> map = new HashMap<>(); - map.put(ansiColorsToTerminalCodes, new HashMap()); - map.put(eightBitColorsToTerminalCodes, new HashMap()); - return map; - } - - private static Map doInitColors() { - Map colors = new HashMap(); - colors.put("black", Color.black); - colors.put("blue", Color.blue); - colors.put("brown", getColorByRgb("#C08040")); - colors.put("cyan", Color.cyan); - colors.put("darkgray", Color.darkGray); - colors.put("gray", Color.gray); - colors.put("green", Color.green); - colors.put("lightgray", Color.lightGray); - colors.put("lime", getColorByRgb("#C0FF00")); - colors.put("magenta", Color.magenta); - colors.put("olive", getColorByRgb("#808000")); - colors.put("orange", Color.orange); - colors.put("pink", Color.pink); - colors.put("purple", getColorByRgb("#C00040")); - colors.put("red", Color.red); - colors.put("teal", getColorByRgb("#008080")); - colors.put("violet", getColorByRgb("#800080")); - colors.put("white", Color.white); - colors.put("yellow", Color.yellow); - colors.put("Apricot", getColorByRgb("#FBB982")); - colors.put("Aquamarine", getColorByRgb("#00B5BE")); - colors.put("Bittersweet", getColorByRgb("#C04F17")); - colors.put("Black", getColorByRgb("#221E1F")); - colors.put("Blue", getColorByRgb("#2D2F92")); - colors.put("BlueGreen", getColorByRgb("#00B3B8")); - colors.put("BlueViolet", getColorByRgb("#473992")); - colors.put("BrickRed", getColorByRgb("#B6321C")); - colors.put("Brown", getColorByRgb("#792500")); - colors.put("BurntOrange", getColorByRgb("#F7921D")); - colors.put("CadetBlue", getColorByRgb("#74729A")); - colors.put("CarnationPink", getColorByRgb("#F282B4")); - colors.put("Cerulean", getColorByRgb("#00A2E3")); - colors.put("CornflowerBlue", getColorByRgb("#41B0E4")); - colors.put("Cyan", getColorByRgb("#00AEEF")); - colors.put("Dandelion", getColorByRgb("#FDBC42")); - colors.put("DarkOrchid", getColorByRgb("#A4538A")); - colors.put("Emerald", getColorByRgb("#00A99D")); - colors.put("ForestGreen", getColorByRgb("#009B55")); - colors.put("Fuchsia", getColorByRgb("#8C368C")); - colors.put("Goldenrod", getColorByRgb("#FFDF42")); - colors.put("Gray", getColorByRgb("#949698")); - colors.put("Green", getColorByRgb("#00A64F")); - colors.put("GreenYellow", getColorByRgb("#DFE674")); - colors.put("JungleGreen", getColorByRgb("#00A99A")); - colors.put("Lavender", getColorByRgb("#F49EC4")); - colors.put("LimeGreen", getColorByRgb("#8DC73E")); - colors.put("Magenta", getColorByRgb("#EC008C")); - colors.put("Mahogany", getColorByRgb("#A9341F")); - colors.put("Maroon", getColorByRgb("#AF3235")); - colors.put("Melon", getColorByRgb("#F89E7B")); - colors.put("MidnightBlue", getColorByRgb("#006795")); - colors.put("Mulberry", getColorByRgb("#A93C93")); - colors.put("NavyBlue", getColorByRgb("#006EB8")); - colors.put("OliveGreen", getColorByRgb("#3C8031")); - colors.put("Orange", getColorByRgb("#F58137")); - colors.put("OrangeRed", getColorByRgb("#ED135A")); - colors.put("Orchid", getColorByRgb("#AF72B0")); - colors.put("Peach", getColorByRgb("#F7965A")); - colors.put("Periwinkle", getColorByRgb("#7977B8")); - colors.put("PineGreen", getColorByRgb("#008B72")); - colors.put("Plum", getColorByRgb("#92268F")); - colors.put("ProcessBlue", getColorByRgb("#00B0F0")); - colors.put("Purple", getColorByRgb("#99479B")); - colors.put("RawSienna", getColorByRgb("#974006")); - colors.put("Red", getColorByRgb("#ED1B23")); - colors.put("RedOrange", getColorByRgb("#F26035")); - colors.put("RedViolet", getColorByRgb("#A1246B")); - colors.put("Rhodamine", getColorByRgb("#EF559F")); - colors.put("RoyalBlue", getColorByRgb("#0071BC")); - colors.put("RoyalPurple", getColorByRgb("#613F99")); - colors.put("RubineRed", getColorByRgb("#ED017D")); - colors.put("Salmon", getColorByRgb("#F69289")); - colors.put("SeaGreen", getColorByRgb("#3FBC9D")); - colors.put("Sepia", getColorByRgb("#671800")); - colors.put("SkyBlue", getColorByRgb("#46C5DD")); - colors.put("SpringGreen", getColorByRgb("#C6DC67")); - colors.put("Tan", getColorByRgb("#DA9D76")); - colors.put("TealBlue", getColorByRgb("#00AEB3")); - colors.put("Thistle", getColorByRgb("#D883B7")); - colors.put("Turquoise", getColorByRgb("#00B4CE")); - colors.put("Violet", getColorByRgb("#58429B")); - colors.put("VioletRed", getColorByRgb("#EF58A0")); - colors.put("White", getColorByRgb("#FFFFFF")); - colors.put("WildStrawberry", getColorByRgb("#EE2967")); - colors.put("Yellow", getColorByRgb("#FFF200")); - colors.put("YellowGreen", getColorByRgb("#98CC70")); - colors.put("YellowOrange", getColorByRgb("#FAA21A")); - - addSvgnamesColors(colors); - - return Collections.unmodifiableMap(colors); - } - - private static void addSvgnamesColors(Map colors) { - Object[][] svgColors = { - {"AliceBlue", .94, .972, 1}, - {"AntiqueWhite", .98, .92, .844}, - {"Aqua", 0, 1, 1}, - {"Aquamarine", .498, 1, .83}, - {"Azure", .94, 1, 1}, - {"Beige", .96, .96, .864}, - {"Bisque", 1, .894, .77}, - {"Black", 0, 0, 0}, - {"BlanchedAlmond", 1, .92, .804}, - {"Blue", 0, 0, 1}, - {"BlueViolet", .54, .17, .888}, - {"Brown", .648, .165, .165}, - {"BurlyWood", .87, .72, .53}, - {"CadetBlue", .372, .62, .628}, - {"Chartreuse", .498, 1, 0}, - {"Chocolate", .824, .41, .116}, - {"Coral", 1, .498, .312}, - {"CornflowerBlue", .392, .585, .93}, - {"Cornsilk", 1, .972, .864}, - {"Crimson", .864, .08, .235}, - {"Cyan", 0, 1, 1}, - {"DarkBlue", 0, 0, .545}, - {"DarkCyan", 0, .545, .545}, - {"DarkGoldenrod", .72, .525, .044}, - {"DarkGray", .664, .664, .664}, - {"DarkGreen", 0, .392, 0}, - {"DarkGrey", .664, .664, .664}, - {"DarkKhaki", .74, .716, .42}, - {"DarkMagenta", .545, 0, .545}, - {"DarkOliveGreen", .332, .42, .185}, - {"DarkOrange", 1, .55, 0}, - {"DarkOrchid", .6, .196, .8}, - {"DarkRed", .545, 0, 0}, - {"DarkSalmon", .912, .59, .48}, - {"DarkSeaGreen", .56, .736, .56}, - {"DarkSlateBlue", .284, .24, .545}, - {"DarkSlateGray", .185, .31, .31}, - {"DarkSlateGrey", .185, .31, .31}, - {"DarkTurquoise", 0, .808, .82}, - {"DarkViolet", .58, 0, .828}, - {"DeepPink", 1, .08, .576}, - {"DeepSkyBlue", 0, .75, 1}, - {"DimGray", .41, .41, .41}, - {"DimGrey", .41, .41, .41}, - {"DodgerBlue", .116, .565, 1}, - {"FireBrick", .698, .132, .132}, - {"FloralWhite", 1, .98, .94}, - {"ForestGreen", .132, .545, .132}, - {"Fuchsia", 1, 0, 1}, - {"Gainsboro", .864, .864, .864}, - {"GhostWhite", .972, .972, 1}, - {"Gold", 1, .844, 0}, - {"Goldenrod", .855, .648, .125}, - {"Gray", .5, .5, .5}, - {"Green", 0, .5, 0}, - {"GreenYellow", .68, 1, .185}, - {"Grey", .5, .5, .5}, - {"Honeydew", .94, 1, .94}, - {"HotPink", 1, .41, .705}, - {"IndianRed", .804, .36, .36}, - {"Indigo", .294, 0, .51}, - {"Ivory", 1, 1, .94}, - {"Khaki", .94, .9, .55}, - {"Lavender", .9, .9, .98}, - {"LavenderBlush", 1, .94, .96}, - {"LawnGreen", .488, .99, 0}, - {"LemonChiffon", 1, .98, .804}, - {"LightBlue", .68, .848, .9}, - {"LightCoral", .94, .5, .5}, - {"LightCyan", .88, 1, 1}, - {"LightGoldenrod", .933, .867, .51}, - {"LightGoldenrodYellow", .98, .98, .824}, - {"LightGray", .828, .828, .828}, - {"LightGreen", .565, .932, .565}, - {"LightGrey", .828, .828, .828}, - {"LightPink", 1, .712, .756}, - {"LightSalmon", 1, .628, .48}, - {"LightSeaGreen", .125, .698, .668}, - {"LightSkyBlue", .53, .808, .98}, - {"LightSlateBlue", .518, .44, 1}, - {"LightSlateGray", .468, .532, .6}, - {"LightSlateGrey", .468, .532, .6}, - {"LightSteelBlue", .69, .77, .87}, - {"LightYellow", 1, 1, .88}, - {"Lime", 0, 1, 0}, - {"LimeGreen", .196, .804, .196}, - {"Linen", .98, .94, .9}, - {"Magenta", 1, 0, 1}, - {"Maroon", .5, 0, 0}, - {"MediumAquamarine", .4, .804, .668}, - {"MediumBlue", 0, 0, .804}, - {"MediumOrchid", .73, .332, .828}, - {"MediumPurple", .576, .44, .86}, - {"MediumSeaGreen", .235, .7, .444}, - {"MediumSlateBlue", .484, .408, .932}, - {"MediumSpringGreen", 0, .98, .604}, - {"MediumTurquoise", .284, .82, .8}, - {"MediumVioletRed", .78, .084, .52}, - {"MidnightBlue", .098, .098, .44}, - {"MintCream", .96, 1, .98}, - {"MistyRose", 1, .894, .884}, - {"Moccasin", 1, .894, .71}, - {"NavajoWhite", 1, .87, .68}, - {"Navy", 0, 0, .5}, - {"NavyBlue", 0, 0, .5}, - {"OldLace", .992, .96, .9}, - {"Olive", .5, .5, 0}, - {"OliveDrab", .42, .556, .136}, - {"Orange", 1, .648, 0}, - {"OrangeRed", 1, .27, 0}, - {"Orchid", .855, .44, .84}, - {"PaleGoldenrod", .932, .91, .668}, - {"PaleGreen", .596, .985, .596}, - {"PaleTurquoise", .688, .932, .932}, - {"PaleVioletRed", .86, .44, .576}, - {"PapayaWhip", 1, .936, .835}, - {"PeachPuff", 1, .855, .725}, - {"Peru", .804, .52, .248}, - {"Pink", 1, .752, .796}, - {"Plum", .868, .628, .868}, - {"PowderBlue", .69, .88, .9}, - {"Purple", .5, 0, .5}, - {"Red", 1, 0, 0}, - {"RosyBrown", .736, .56, .56}, - {"RoyalBlue", .255, .41, .884}, - {"SaddleBrown", .545, .27, .075}, - {"Salmon", .98, .5, .448}, - {"SandyBrown", .956, .644, .376}, - {"SeaGreen", .18, .545, .34}, - {"Seashell", 1, .96, .932}, - {"Sienna", .628, .32, .176}, - {"Silver", .752, .752, .752}, - {"SkyBlue", .53, .808, .92}, - {"SlateBlue", .415, .352, .804}, - {"SlateGray", .44, .5, .565}, - {"SlateGrey", .44, .5, .565}, - {"Snow", 1, .98, .98}, - {"SpringGreen", 0, 1, .498}, - {"SteelBlue", .275, .51, .705}, - {"Tan", .824, .705, .55}, - {"Teal", 0, .5, .5}, - {"Thistle", .848, .75, .848}, - {"Tomato", 1, .39, .28}, - {"Turquoise", .25, .88, .815}, - {"Violet", .932, .51, .932}, - {"VioletRed", .816, .125, .565}, - {"Wheat", .96, .87, .7}, - {"White", 1, 1, 1}, - {"WhiteSmoke", .96, .96, .96}, - {"Yellow", 1, 1, 0}, - {"YellowGreen", .604, .804, .196}, - }; - for (Object[] rawColor : svgColors) { - colors.put((String) rawColor[0], new Color(toFloat(rawColor[1]), toFloat(rawColor[2]), toFloat(rawColor[3]))); + } + + private static float toFloat(Object rawColor) { + return rawColor instanceof Integer ? (float) (Integer) rawColor : (float) (double) rawColor; + } + + private static Color getColorByRgb(String rgb) { + int r = Integer.valueOf(rgb.substring(1, 3), 16); + int g = Integer.valueOf(rgb.substring(3, 5), 16); + int b = Integer.valueOf(rgb.substring(5, 7), 16); + return new Color(r, g, b); + } + + private static Map initAnsiColors() { + Map map = new HashMap(8); + map.put(Color.black, getBasicTerminalCode(30)); + map.put(Color.red, getBasicTerminalCode(31)); + map.put(Color.green, getBasicTerminalCode(32)); + map.put(Color.yellow, getBasicTerminalCode(33)); + map.put(Color.blue, getBasicTerminalCode(34)); + map.put(Color.magenta, getBasicTerminalCode(35)); + map.put(Color.cyan, getBasicTerminalCode(36)); + map.put(Color.white, getBasicTerminalCode(37)); + + return Collections.unmodifiableMap(map); + } + + /** + * Basic colors codes have the form \e[<code>m . You can test them by running in your + * terminal: echo -en "\e[35mTEST" + */ + private static String getBasicTerminalCode(int code) { + return "\u001b[" + code + "m"; + } + + private static Map initEightBitColors() { + Map coordMap = new HashMap(); + coordMap.put(0, 0); + coordMap.put(1, 95); + coordMap.put(2, 135); + coordMap.put(3, 175); + coordMap.put(4, 215); + coordMap.put(5, 255); + + Map map = new HashMap(); + for (int i = 0; i < 6; i++) { + for (int j = 0; j < 6; j++) { + for (int k = 0; k < 6; k++) { + int code = i * 36 + j * 6 + k + 16; + Color color = new Color(coordMap.get(i), coordMap.get(j), coordMap.get(k)); + map.put(color, getEightBitTerminalCode(code)); } + } } - private static float toFloat(Object rawColor) { - return rawColor instanceof Integer ? (float) (Integer) rawColor : (float) (double) rawColor; - } - - private static Color getColorByRgb(String rgb) { - int r = Integer.valueOf(rgb.substring(1, 3), 16); - int g = Integer.valueOf(rgb.substring(3, 5), 16); - int b = Integer.valueOf(rgb.substring(5, 7), 16); - return new Color(r, g, b); + return Collections.unmodifiableMap(map); + } + + /** 8-bit format example: echo -en "\e[38;5;180mTEST" */ + private static String getEightBitTerminalCode(int code) { + return "\u001b[38;5;" + code + "m"; + } + + public static synchronized String RgbToAnsi(String rgb, ColorSetting colorSetting) { + return switch (colorSetting) { + case OFF -> ""; + case ON -> getClosestTerminalCode(colors.get(rgb), ansiColorsToTerminalCodes); + case EXTENDED -> getClosestTerminalCode(colors.get(rgb), eightBitColorsToTerminalCodes); + default -> throw new UnsupportedOperationException("colorSettung: " + colorSetting); + }; + } + + public static void main(String[] args) { + for (String name : colors.keySet()) { + System.out.println(name); + System.out.println(RgbToAnsi(name, ColorSetting.EXTENDED)); } + } - private static Map initAnsiColors() { - Map map = new HashMap(8); - map.put(Color.black, getBasicTerminalCode(30)); - map.put(Color.red, getBasicTerminalCode(31)); - map.put(Color.green, getBasicTerminalCode(32)); - map.put(Color.yellow, getBasicTerminalCode(33)); - map.put(Color.blue, getBasicTerminalCode(34)); - map.put(Color.magenta, getBasicTerminalCode(35)); - map.put(Color.cyan, getBasicTerminalCode(36)); - map.put(Color.white, getBasicTerminalCode(37)); - - return Collections.unmodifiableMap(map); + private static String getClosestTerminalCode(Color rgb, Map codesMap) { + if (rgb == null) return ""; + if (colorToCodeConvertCache.get(codesMap).get(rgb) == null) { + colorToCodeConvertCache.get(codesMap).put(rgb, getClosestTerminalCodeImpl(rgb, codesMap)); } - - /** - * Basic colors codes have the form \e[<code>m . You can test them by running in your terminal: - * echo -en "\e[35mTEST" - */ - private static String getBasicTerminalCode(int code) { - return "\u001b[" + code + "m"; + return colorToCodeConvertCache.get(codesMap).get(rgb); + } + + private static String getClosestTerminalCodeImpl(Color rgb, Map codesMap) { + double minColorError = Double.MAX_VALUE; + Color minColor = null; + for (Color ansi : codesMap.keySet()) { + double colorError = getColorError(rgb, ansi); + if (colorError < minColorError) { + minColorError = colorError; + minColor = ansi; + } } - - private static Map initEightBitColors() { - Map coordMap = new HashMap(); - coordMap.put(0,0); - coordMap.put(1,95); - coordMap.put(2,135); - coordMap.put(3,175); - coordMap.put(4,215); - coordMap.put(5,255); - - Map map = new HashMap(); - for (int i = 0; i < 6; i++) { - for (int j = 0; j < 6; j++) { - for (int k = 0; k < 6; k++) { - int code = i *36 + j * 6 + k + 16; - Color color = new Color(coordMap.get(i),coordMap.get(j),coordMap.get(k)); - map.put(color, getEightBitTerminalCode(code)); - } - } - } - - return Collections.unmodifiableMap(map); + return codesMap.get(minColor); + } + + public static final String ANSI_NORMAL = "\u001b[0m"; + + private static final double sl = 1.0; + private static final double kc = 1.0; + private static final double kh = 1.0; + // graphic args + private static final double kl = 1.0; + private static final double k1 = 0.045; + private static final double k2 = 0.015; + + public static final class CIELab extends ColorSpace { + private static final ColorSpace CIEXYZ = ColorSpace.getInstance(ColorSpace.CS_CIEXYZ); + + private final double xn, yn, zn; + + CIELab() { + super(ColorSpace.TYPE_Lab, 3); + float[] ref = Color.white.getColorComponents(CIEXYZ, null); + xn = ref[0]; + yn = ref[1]; + zn = ref[2]; } - /** - * 8-bit format example: echo -en "\e[38;5;180mTEST" - */ - private static String getEightBitTerminalCode(int code) { - return "\u001b[38;5;" + code + "m"; + @Override + public float[] toRGB(float[] floats) { + return CIEXYZ.toRGB(toCIEXYZ(floats)); } - public synchronized static String RgbToAnsi(String rgb, ColorSetting colorSetting) { - return switch (colorSetting) { - case OFF -> ""; - case ON -> getClosestTerminalCode(colors.get(rgb), ansiColorsToTerminalCodes); - case EXTENDED -> getClosestTerminalCode(colors.get(rgb), eightBitColorsToTerminalCodes); - default -> throw new UnsupportedOperationException("colorSettung: " + colorSetting); - }; + @Override + public float[] fromRGB(float[] floats) { + return fromCIEXYZ(CIEXYZ.fromRGB(floats)); } - public static void main(String[] args) { - for (String name : colors.keySet()) { - System.out.println(name); - System.out.println(RgbToAnsi(name, ColorSetting.EXTENDED)); - } + @Override + public float[] toCIEXYZ(float[] floats) { + double l = floats[0]; + double a = floats[1]; + double b = floats[2]; + double x = xn * finv((l + 16.0) / 116.0 + a / 500.0); + double y = yn * finv((l + 16.0) / 116.0); + double z = zn * finv((l + 16.0) / 116.0 - b / 200.0); + return new float[] {(float) x, (float) y, (float) z}; } - private static String getClosestTerminalCode(Color rgb, Map codesMap) { - if (rgb == null) - return ""; - if (colorToCodeConvertCache.get(codesMap).get(rgb) == null) { - colorToCodeConvertCache.get(codesMap).put(rgb, getClosestTerminalCodeImpl(rgb, codesMap)); - } - return colorToCodeConvertCache.get(codesMap).get(rgb); + @Override + public float[] fromCIEXYZ(float[] floats) { + double x = floats[0]; + double y = floats[1]; + double z = floats[2]; + double l = 116.0 * f(y / yn) - 16.0; + double a = 500.0 * (f(x / xn) - f(y / yn)); + double b = 200.0 * (f(y / yn) - f(z / zn)); + return new float[] {(float) l, (float) a, (float) b}; } - private static String getClosestTerminalCodeImpl(Color rgb, Map codesMap) { - double minColorError = Double.MAX_VALUE; - Color minColor = null; - for (Color ansi : codesMap.keySet()) { - double colorError = getColorError(rgb, ansi); - if (colorError < minColorError) { - minColorError = colorError; - minColor = ansi; - } - } - return codesMap.get(minColor); - } + private static final double delta = 6.0 / 29.0; + private static final double f0 = 4.0 / 29.0; - public static final String ANSI_NORMAL = "\u001b[0m"; - - private static final double sl = 1.0; - private static final double kc = 1.0; - private static final double kh = 1.0; - // graphic args - private static final double kl = 1.0; - private static final double k1 = 0.045; - private static final double k2 = 0.015; - - public static final class CIELab extends ColorSpace { - private static final ColorSpace CIEXYZ = ColorSpace.getInstance(ColorSpace.CS_CIEXYZ); - - private final double xn, yn, zn; - - CIELab() { - super(ColorSpace.TYPE_Lab, 3); - float[] ref = Color.white.getColorComponents(CIEXYZ, null); - xn = ref[0]; - yn = ref[1]; - zn = ref[2]; - } - - @Override - public float[] toRGB(float[] floats) { - return CIEXYZ.toRGB(toCIEXYZ(floats)); - } - - @Override - public float[] fromRGB(float[] floats) { - return fromCIEXYZ(CIEXYZ.fromRGB(floats)); - } - - @Override - public float[] toCIEXYZ(float[] floats) { - double l = floats[0]; - double a = floats[1]; - double b = floats[2]; - double x = xn * finv((l + 16.0)/116.0 + a/500.0); - double y = yn * finv((l + 16.0)/116.0); - double z = zn * finv((l + 16.0)/116.0 - b/200.0); - return new float[]{ (float)x, (float)y, (float)z }; - } - - @Override - public float[] fromCIEXYZ(float[] floats) { - double x = floats[0]; - double y = floats[1]; - double z = floats[2]; - double l = 116.0 * f(y/yn) - 16.0; - double a = 500.0 * (f(x/xn) - f(y/yn)); - double b = 200.0 * (f(y/yn) - f(z/zn)); - return new float[]{ (float)l, (float)a, (float)b }; - } - - private static final double delta = 6.0/29.0; - private static final double f0 = 4.0/29.0; - - private double f(double t) { - if (t > delta*delta*delta) { - return Math.cbrt(t); - } - return t/(3*delta*delta) + f0; - } - - private double finv(double t) { - if (t > delta) { - return t*t*t; - } - return 3*delta*delta*(t - f0); - } + private double f(double t) { + if (t > delta * delta * delta) { + return Math.cbrt(t); + } + return t / (3 * delta * delta) + f0; } - // Computes the CIE94 color difference of two colors - private static double getColorError(Color color1, Color color2) { - float[] rgb1 = color1.getRGBColorComponents(null); - float[] rgb2 = color2.getRGBColorComponents(null); - - ColorSpace labSpace = new CIELab(); - float[] lab1 = labSpace.fromRGB(rgb1); - float[] lab2 = labSpace.fromRGB(rgb2); - - double deltaL = lab1[0] - lab2[0]; - double deltaA = lab1[1] - lab2[1]; - double deltaB = lab1[2] - lab2[2]; - - double c1 = Math.sqrt(lab1[1]*lab1[1] + lab1[2]*lab1[2]); - double c2 = Math.sqrt(lab2[1]*lab2[1] + lab2[2]*lab2[2]); - double deltaC = c1 - c2; - - double deltaH = deltaA*deltaA + deltaB*deltaB - deltaC*deltaC; - deltaH = deltaH < 0 ? 0 : Math.sqrt(deltaH); - - double sc = 1.0 + k1*c1; - double sh = 1.0 + k2*c1; - - double l = deltaL/(kl*sl); - double c = deltaC/(kc*sc); - double h = deltaH/(kh*sh); - double i = l*l + c*c + h*h; - return i < 0 ? 0 : Math.sqrt(i); + private double finv(double t) { + if (t > delta) { + return t * t * t; + } + return 3 * delta * delta * (t - f0); } - - public static Color getColorByName(String colorName) { - try { - // Find the field and value of colorName - Field field = Class.forName("java.awt.Color").getField(colorName); - return (Color)field.get(null); - } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) { - return null; - } + } + + // Computes the CIE94 color difference of two colors + private static double getColorError(Color color1, Color color2) { + float[] rgb1 = color1.getRGBColorComponents(null); + float[] rgb2 = color2.getRGBColorComponents(null); + + ColorSpace labSpace = new CIELab(); + float[] lab1 = labSpace.fromRGB(rgb1); + float[] lab2 = labSpace.fromRGB(rgb2); + + double deltaL = lab1[0] - lab2[0]; + double deltaA = lab1[1] - lab2[1]; + double deltaB = lab1[2] - lab2[2]; + + double c1 = Math.sqrt(lab1[1] * lab1[1] + lab1[2] * lab1[2]); + double c2 = Math.sqrt(lab2[1] * lab2[1] + lab2[2] * lab2[2]); + double deltaC = c1 - c2; + + double deltaH = deltaA * deltaA + deltaB * deltaB - deltaC * deltaC; + deltaH = deltaH < 0 ? 0 : Math.sqrt(deltaH); + + double sc = 1.0 + k1 * c1; + double sh = 1.0 + k2 * c1; + + double l = deltaL / (kl * sl); + double c = deltaC / (kc * sc); + double h = deltaH / (kh * sh); + double i = l * l + c * c + h * h; + return i < 0 ? 0 : Math.sqrt(i); + } + + public static Color getColorByName(String colorName) { + try { + // Find the field and value of colorName + Field field = Class.forName("java.awt.Color").getField(colorName); + return (Color) field.get(null); + } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) { + return null; } + } } diff --git a/kernel/src/main/java/org/kframework/utils/ExitOnTimeoutThread.java b/kernel/src/main/java/org/kframework/utils/ExitOnTimeoutThread.java index 7c2c7da24fe..e2536d72e45 100644 --- a/kernel/src/main/java/org/kframework/utils/ExitOnTimeoutThread.java +++ b/kernel/src/main/java/org/kframework/utils/ExitOnTimeoutThread.java @@ -6,25 +6,24 @@ /** * A daemon thread that awaits given timeout, then exits Java process. * - * @author Denis Bogdanas - * Created on 30-Oct-19. + * @author Denis Bogdanas Created on 30-Oct-19. */ public class ExitOnTimeoutThread extends Thread { - private final long timeoutMillis; + private final long timeoutMillis; - public ExitOnTimeoutThread(long timeoutMillis) { - this.timeoutMillis = timeoutMillis; - setDaemon(true); - } + public ExitOnTimeoutThread(long timeoutMillis) { + this.timeoutMillis = timeoutMillis; + setDaemon(true); + } - @Override - public void run() { - try { - Thread.sleep(timeoutMillis); - System.err.println("K process timeout..."); - Main.exit(124); //bash timeout exit code is 124 - } catch (InterruptedException e) { - //normal termination, ignoring - } + @Override + public void run() { + try { + Thread.sleep(timeoutMillis); + System.err.println("K process timeout..."); + Main.exit(124); // bash timeout exit code is 124 + } catch (InterruptedException e) { + // normal termination, ignoring } + } } diff --git a/kernel/src/main/java/org/kframework/utils/IndentingFormatter.java b/kernel/src/main/java/org/kframework/utils/IndentingFormatter.java index ea4113d0f82..23b5c726b2f 100644 --- a/kernel/src/main/java/org/kframework/utils/IndentingFormatter.java +++ b/kernel/src/main/java/org/kframework/utils/IndentingFormatter.java @@ -1,47 +1,47 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils; -import javax.annotation.Nonnull; import java.util.Formatter; +import javax.annotation.Nonnull; /** * An extension of {@code java.util.Formatter} that indents every new line with specified prefix. * - * @author Denis Bogdanas - * Created on 14-Apr-19. + * @author Denis Bogdanas Created on 14-Apr-19. */ public class IndentingFormatter { - public static final String ENDL = "\n"; - private final Formatter formatter; - private final String endlReplacement; + public static final String ENDL = "\n"; + private final Formatter formatter; + private final String endlReplacement; - public IndentingFormatter(@Nonnull Formatter formatter, @Nonnull String indent) { - this.formatter = formatter; - this.endlReplacement = indent.isEmpty() ? ENDL : ENDL + indent; - } + public IndentingFormatter(@Nonnull Formatter formatter, @Nonnull String indent) { + this.formatter = formatter; + this.endlReplacement = indent.isEmpty() ? ENDL : ENDL + indent; + } - public Formatter format(String format, Object... args) { - if (endlReplacement.equals(ENDL)) { - return formatter.format(format, args); - } else { - String newFormat = buildNewFormat(format); - Object[] newArgs = buildNewArgs(args); - return formatter.format(newFormat, newArgs); - } + public Formatter format(String format, Object... args) { + if (endlReplacement.equals(ENDL)) { + return formatter.format(format, args); + } else { + String newFormat = buildNewFormat(format); + Object[] newArgs = buildNewArgs(args); + return formatter.format(newFormat, newArgs); } + } - public String buildNewFormat(String format) { - return format.replaceAll("\\R", endlReplacement); - } + public String buildNewFormat(String format) { + return format.replaceAll("\\R", endlReplacement); + } - public Object[] buildNewArgs(Object[] args) { - Object[] newArgs = new Object[args.length]; - for (int i = 0; i < args.length; i++) { - newArgs[i] = args[i] instanceof String - ? ((String) args[i]).replaceAll("\\R", endlReplacement) - : args[i]; - } - return newArgs; + public Object[] buildNewArgs(Object[] args) { + Object[] newArgs = new Object[args.length]; + for (int i = 0; i < args.length; i++) { + newArgs[i] = + args[i] instanceof String + ? ((String) args[i]).replaceAll("\\R", endlReplacement) + : args[i]; } + return newArgs; + } } diff --git a/kernel/src/main/java/org/kframework/utils/InterrupterRunnable.java b/kernel/src/main/java/org/kframework/utils/InterrupterRunnable.java index 9e31a10780b..7b758a2c9c4 100644 --- a/kernel/src/main/java/org/kframework/utils/InterrupterRunnable.java +++ b/kernel/src/main/java/org/kframework/utils/InterrupterRunnable.java @@ -2,20 +2,20 @@ package org.kframework.utils; /** - * A runnable that interrupts the given thread when invoked, then awaits termination for the given wait time, in ms. + * A runnable that interrupts the given thread when invoked, then awaits termination for the given + * wait time, in ms. * - * @author Denis Bogdanas - * Created on 24-Dec-18. + * @author Denis Bogdanas Created on 24-Dec-18. */ public record InterrupterRunnable(Thread thread, long waitTimeMillis) implements Runnable { - @Override - public void run() { - thread.interrupt(); - try { - thread.join(waitTimeMillis); - } catch (InterruptedException e) { - e.printStackTrace(); - } + @Override + public void run() { + thread.interrupt(); + try { + thread.join(waitTimeMillis); + } catch (InterruptedException e) { + e.printStackTrace(); } + } } diff --git a/kernel/src/main/java/org/kframework/utils/RunProcess.java b/kernel/src/main/java/org/kframework/utils/RunProcess.java index 204d70fb350..9cd82eb58bb 100644 --- a/kernel/src/main/java/org/kframework/utils/RunProcess.java +++ b/kernel/src/main/java/org/kframework/utils/RunProcess.java @@ -1,80 +1,80 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils; -import org.kframework.utils.errorsystem.KEMException; - -import org.apache.commons.io.IOUtils; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.util.Map; import java.util.function.Supplier; +import org.apache.commons.io.IOUtils; +import org.kframework.utils.errorsystem.KEMException; // instantiate processes public class RunProcess { - /** - * Returns a thread that pipes all incoming data from {@param in} to {@param out}. - * @param in A function that returns the input stream to be piped to {@param out} - * @param out The output stream to pipe data to. - * @return A {@link Thread} that will pipe all data from {@param in} to {@param out} until EOF is reached. - */ - public static Thread getOutputStreamThread(Supplier in, PrintStream out) { - return new Thread(() -> { - try { - IOUtils.copy(in.get(), out); - } catch (IOException ignored) {} - }); - } - - public record ProcessOutput(byte[] stdout, byte[] stderr, int exitCode) { - } - - private RunProcess() {} - - public static ProcessOutput execute(Map environment, ProcessBuilder pb, String... commands) { - try { - if (commands.length <= 0) { - throw KEMException.criticalError("Need command options to run"); - } - - // create process - pb = pb.command(commands); - Map realEnvironment = pb.environment(); - realEnvironment.putAll(environment); - - // start process - Process process = pb.start(); - - ByteArrayOutputStream out, err; - PrintStream outWriter, errWriter; - out = new ByteArrayOutputStream(); - err = new ByteArrayOutputStream(); - outWriter = new PrintStream(out); - errWriter = new PrintStream(err); - - Thread outThread = getOutputStreamThread(process::getInputStream, outWriter); - Thread errThread = getOutputStreamThread(process::getErrorStream, errWriter); - - outThread.start(); - errThread.start(); - - // wait for process to finish - process.waitFor(); - - outThread.join(); - errThread.join(); - outWriter.flush(); - errWriter.flush(); - - return new ProcessOutput(out.toByteArray(), err.toByteArray(), process.exitValue()); - - } catch (IOException | InterruptedException e) { - throw KEMException.criticalError("Error while running process:" + e.getMessage(), e); - } - + /** + * Returns a thread that pipes all incoming data from {@param in} to {@param out}. + * + * @param in A function that returns the input stream to be piped to {@param out} + * @param out The output stream to pipe data to. + * @return A {@link Thread} that will pipe all data from {@param in} to {@param out} until EOF is + * reached. + */ + public static Thread getOutputStreamThread(Supplier in, PrintStream out) { + return new Thread( + () -> { + try { + IOUtils.copy(in.get(), out); + } catch (IOException ignored) { + } + }); + } + + public record ProcessOutput(byte[] stdout, byte[] stderr, int exitCode) {} + + private RunProcess() {} + + public static ProcessOutput execute( + Map environment, ProcessBuilder pb, String... commands) { + try { + if (commands.length <= 0) { + throw KEMException.criticalError("Need command options to run"); + } + + // create process + pb = pb.command(commands); + Map realEnvironment = pb.environment(); + realEnvironment.putAll(environment); + + // start process + Process process = pb.start(); + + ByteArrayOutputStream out, err; + PrintStream outWriter, errWriter; + out = new ByteArrayOutputStream(); + err = new ByteArrayOutputStream(); + outWriter = new PrintStream(out); + errWriter = new PrintStream(err); + + Thread outThread = getOutputStreamThread(process::getInputStream, outWriter); + Thread errThread = getOutputStreamThread(process::getErrorStream, errWriter); + + outThread.start(); + errThread.start(); + + // wait for process to finish + process.waitFor(); + + outThread.join(); + errThread.join(); + outWriter.flush(); + errWriter.flush(); + + return new ProcessOutput(out.toByteArray(), err.toByteArray(), process.exitValue()); + + } catch (IOException | InterruptedException e) { + throw KEMException.criticalError("Error while running process:" + e.getMessage(), e); } - + } } diff --git a/kernel/src/main/java/org/kframework/utils/Stopwatch.java b/kernel/src/main/java/org/kframework/utils/Stopwatch.java index f0d295ab8b7..ecebd58df8c 100644 --- a/kernel/src/main/java/org/kframework/utils/Stopwatch.java +++ b/kernel/src/main/java/org/kframework/utils/Stopwatch.java @@ -1,61 +1,56 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils; -import org.kframework.main.GlobalOptions; -import org.kframework.utils.inject.RequestScoped; - import com.google.inject.Inject; import java.util.Formatter; +import org.kframework.main.GlobalOptions; +import org.kframework.utils.inject.RequestScoped; -/** - * To use, access {@link #instance()} after calling {@link #init(GlobalOptions) init()}. - */ +/** To use, access {@link #instance()} after calling {@link #init(GlobalOptions) init()}. */ @RequestScoped public class Stopwatch { - private final long start; - private long lastIntermediate; - Formatter f = new Formatter(System.out); - private final GlobalOptions options; - - @Inject - public Stopwatch(GlobalOptions options) { - this.options = options != null ? options : new GlobalOptions(); - start = System.currentTimeMillis(); - lastIntermediate = start; - } - - public void start() { - printIntermediate("Init"); - } - - public void printIntermediate(String message) { - long current = System.currentTimeMillis(); - if (options.verbose) - f.format("%-60s = %s%n", message, milisecondsToTime(current - lastIntermediate)); - lastIntermediate = current; - } - - public void printTotal(String message) { - printIntermediate("Cleanup"); - if (options.verbose) - f.format("%-60s = %s%n", message, milisecondsToTime(lastIntermediate - start)); - } - - private static String milisecondsToTime(long miliseconds) { - long h = miliseconds / 3600000; - long m = miliseconds % 3600000 / 60000; - double s = miliseconds % 60000 / 1000.; - if (h > 0) - return String.format("%dh %02dm %02ds", h, m, (long) s); - if (m > 0) - return String.format("%02dm %02ds", m, (long) s); - return String.format("%6.3fs", s); - } - - public long getIntermediateMilliseconds() { - long endd = System.currentTimeMillis(); - long rez = lastIntermediate - endd; - lastIntermediate = endd; - return rez; - } + private final long start; + private long lastIntermediate; + Formatter f = new Formatter(System.out); + private final GlobalOptions options; + + @Inject + public Stopwatch(GlobalOptions options) { + this.options = options != null ? options : new GlobalOptions(); + start = System.currentTimeMillis(); + lastIntermediate = start; + } + + public void start() { + printIntermediate("Init"); + } + + public void printIntermediate(String message) { + long current = System.currentTimeMillis(); + if (options.verbose) + f.format("%-60s = %s%n", message, milisecondsToTime(current - lastIntermediate)); + lastIntermediate = current; + } + + public void printTotal(String message) { + printIntermediate("Cleanup"); + if (options.verbose) + f.format("%-60s = %s%n", message, milisecondsToTime(lastIntermediate - start)); + } + + private static String milisecondsToTime(long miliseconds) { + long h = miliseconds / 3600000; + long m = miliseconds % 3600000 / 60000; + double s = miliseconds % 60000 / 1000.; + if (h > 0) return String.format("%dh %02dm %02ds", h, m, (long) s); + if (m > 0) return String.format("%02dm %02ds", m, (long) s); + return String.format("%6.3fs", s); + } + + public long getIntermediateMilliseconds() { + long endd = System.currentTimeMillis(); + long rez = lastIntermediate - endd; + lastIntermediate = endd; + return rez; + } } diff --git a/kernel/src/main/java/org/kframework/utils/errorsystem/KExceptionManager.java b/kernel/src/main/java/org/kframework/utils/errorsystem/KExceptionManager.java index 96afe4b6f2a..ac3643e5506 100644 --- a/kernel/src/main/java/org/kframework/utils/errorsystem/KExceptionManager.java +++ b/kernel/src/main/java/org/kframework/utils/errorsystem/KExceptionManager.java @@ -2,6 +2,12 @@ package org.kframework.utils.errorsystem; import com.google.inject.Inject; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Set; import org.kframework.attributes.HasLocation; import org.kframework.attributes.Location; import org.kframework.attributes.Source; @@ -11,159 +17,204 @@ import org.kframework.utils.errorsystem.KException.KExceptionGroup; import org.kframework.utils.inject.RequestScoped; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Set; - @RequestScoped public class KExceptionManager { - private final List exceptions = Collections.synchronizedList(new ArrayList<>()); - - public final GlobalOptions options; - - @Inject - public KExceptionManager(GlobalOptions options) { - this.options = options; - } - - public void installForUncaughtExceptions() { - Thread.setDefaultUncaughtExceptionHandler((t, e) -> { - String message = "Uncaught exception thrown of type " + e.getClass().getSimpleName(); - if (!options.debug()) { - message += ".\nPlease rerun your program with the --debug flag to generate a stack trace, " - + "and file a bug report at https://github.com/runtimeverification/k/issues"; - } - exceptions.add(new KException(ExceptionType.ERROR, KExceptionGroup.INTERNAL, message, e)); - print(); + private final List exceptions = Collections.synchronizedList(new ArrayList<>()); + + public final GlobalOptions options; + + @Inject + public KExceptionManager(GlobalOptions options) { + this.options = options; + } + + public void installForUncaughtExceptions() { + Thread.setDefaultUncaughtExceptionHandler( + (t, e) -> { + String message = "Uncaught exception thrown of type " + e.getClass().getSimpleName(); + if (!options.debug()) { + message += + ".\nPlease rerun your program with the --debug flag to generate a stack trace, " + + "and file a bug report at https://github.com/runtimeverification/k/issues"; + } + exceptions.add(new KException(ExceptionType.ERROR, KExceptionGroup.INTERNAL, message, e)); + print(); }); - } - - private void printStackTrace(KException e) { - if (e.getException() != null && - (options.debugWarnings || (options.debug() && e.getType() == ExceptionType.ERROR))) { - e.getException().printStackTrace(); - } - } - - public void addKException(KException e) { - registerInternal(e, false); - } - - public void addAllKException(Collection kex) { - for (KException e : kex) - registerInternal(e, false); - } - - public void registerCompilerWarning(ExceptionType type, String message) { - register(type, KExceptionGroup.COMPILER, message, null, null, null); - } - - public void registerCompilerWarning(ExceptionType type, Set errors, String message, HasLocation node) { - register(errors, type, KExceptionGroup.COMPILER, message, null, node.location().orElse(null), node.source().orElse(null)); - } - - public void registerCompilerWarning(ExceptionType type, String message, HasLocation node) { - register(type, KExceptionGroup.COMPILER, message, null, node.location().orElse(null), node.source().orElse(null)); - } - - public void registerCriticalWarning(ExceptionType type, String message) { - register(type, KExceptionGroup.CRITICAL, message, null, null, null); - } - - public void registerCriticalWarning(ExceptionType type, String message, Throwable e) { - register(type, KExceptionGroup.CRITICAL, message, e, null, null); - } - - public void registerCriticalWarning(ExceptionType type, String message, HasLocation node) { - register(type, KExceptionGroup.CRITICAL, message, null, node.location().orElse(null), node.source().orElse(null)); - } - - public void registerInternalWarning(ExceptionType type, String message) { - register(type, KExceptionGroup.INTERNAL, message, null, null, null); - } - - public void registerInternalWarning(ExceptionType type, String message, HasLocation node) { - register(type, KExceptionGroup.INTERNAL, message, null, node.location().orElse(null), node.source().orElse(null)); - } - - public void registerInternalWarning(ExceptionType type, String message, Throwable e) { - register(type, KExceptionGroup.INTERNAL, message, e, null, null); - } - - public void registerOuterParserWarning(ExceptionType type, String message, Throwable e, Source source, Location location) { - register(type, KExceptionGroup.OUTER_PARSER, message, e, location, source); - } - - public void registerInnerParserWarning(ExceptionType type, String message) { - register(type, KExceptionGroup.INNER_PARSER, message, null, null, null); - } - - private void register(ExceptionType type, KExceptionGroup group, String message, - Throwable e, Location location, Source source) { - registerInternal(new KException(type, group, message, source, location, e), true); - } - - private void register(Set errors, ExceptionType type, KExceptionGroup group, String message, - Throwable e, Location location, Source source) { - if (!options.includesExceptionType(type)) - return; - KException exception = new KException(type, group, message, source, location, e); - if (exception.type == ExceptionType.ERROR || options.warnings2errors) { - errors.add(new KEMException(exception, ExceptionType.ERROR)); - } else { - registerInternal(exception, false); - } - } - - - private void registerInternal(KException exception, boolean _throw) { - if (!options.includesExceptionType(exception.type)) - return; - if (_throw && (exception.type == ExceptionType.ERROR || options.warnings2errors)) { - throw new KEMException(exception, ExceptionType.ERROR); - } else if (options.warnings2errors) { - exceptions.add(new KException(ExceptionType.ERROR, exception.exceptionGroup, exception.getMessage(), exception.getSource(), exception.getLocation(), exception.getException())); - } else { - exceptions.add(exception); - } - } - - public void print() { - Collections.sort(exceptions, - Comparator.comparing(KException::getSource, Comparator.nullsLast(Comparator.naturalOrder())) + } + + private void printStackTrace(KException e) { + if (e.getException() != null + && (options.debugWarnings || (options.debug() && e.getType() == ExceptionType.ERROR))) { + e.getException().printStackTrace(); + } + } + + public void addKException(KException e) { + registerInternal(e, false); + } + + public void addAllKException(Collection kex) { + for (KException e : kex) registerInternal(e, false); + } + + public void registerCompilerWarning(ExceptionType type, String message) { + register(type, KExceptionGroup.COMPILER, message, null, null, null); + } + + public void registerCompilerWarning( + ExceptionType type, Set errors, String message, HasLocation node) { + register( + errors, + type, + KExceptionGroup.COMPILER, + message, + null, + node.location().orElse(null), + node.source().orElse(null)); + } + + public void registerCompilerWarning(ExceptionType type, String message, HasLocation node) { + register( + type, + KExceptionGroup.COMPILER, + message, + null, + node.location().orElse(null), + node.source().orElse(null)); + } + + public void registerCriticalWarning(ExceptionType type, String message) { + register(type, KExceptionGroup.CRITICAL, message, null, null, null); + } + + public void registerCriticalWarning(ExceptionType type, String message, Throwable e) { + register(type, KExceptionGroup.CRITICAL, message, e, null, null); + } + + public void registerCriticalWarning(ExceptionType type, String message, HasLocation node) { + register( + type, + KExceptionGroup.CRITICAL, + message, + null, + node.location().orElse(null), + node.source().orElse(null)); + } + + public void registerInternalWarning(ExceptionType type, String message) { + register(type, KExceptionGroup.INTERNAL, message, null, null, null); + } + + public void registerInternalWarning(ExceptionType type, String message, HasLocation node) { + register( + type, + KExceptionGroup.INTERNAL, + message, + null, + node.location().orElse(null), + node.source().orElse(null)); + } + + public void registerInternalWarning(ExceptionType type, String message, Throwable e) { + register(type, KExceptionGroup.INTERNAL, message, e, null, null); + } + + public void registerOuterParserWarning( + ExceptionType type, String message, Throwable e, Source source, Location location) { + register(type, KExceptionGroup.OUTER_PARSER, message, e, location, source); + } + + public void registerInnerParserWarning(ExceptionType type, String message) { + register(type, KExceptionGroup.INNER_PARSER, message, null, null, null); + } + + private void register( + ExceptionType type, + KExceptionGroup group, + String message, + Throwable e, + Location location, + Source source) { + registerInternal(new KException(type, group, message, source, location, e), true); + } + + private void register( + Set errors, + ExceptionType type, + KExceptionGroup group, + String message, + Throwable e, + Location location, + Source source) { + if (!options.includesExceptionType(type)) return; + KException exception = new KException(type, group, message, source, location, e); + if (exception.type == ExceptionType.ERROR || options.warnings2errors) { + errors.add(new KEMException(exception, ExceptionType.ERROR)); + } else { + registerInternal(exception, false); + } + } + + private void registerInternal(KException exception, boolean _throw) { + if (!options.includesExceptionType(exception.type)) return; + if (_throw && (exception.type == ExceptionType.ERROR || options.warnings2errors)) { + throw new KEMException(exception, ExceptionType.ERROR); + } else if (options.warnings2errors) { + exceptions.add( + new KException( + ExceptionType.ERROR, + exception.exceptionGroup, + exception.getMessage(), + exception.getSource(), + exception.getLocation(), + exception.getException())); + } else { + exceptions.add(exception); + } + } + + public void print() { + Collections.sort( + exceptions, + Comparator.comparing(KException::getSource, Comparator.nullsLast(Comparator.naturalOrder())) .thenComparing(KException::getLocation, Comparator.nullsLast(Comparator.naturalOrder())) .thenComparing(e -> e.toString(options.verbose))); - KException last = null; - synchronized (exceptions) { - for (KException e : exceptions) { - if (last != null && last.toString(options.verbose).equals(e.toString(options.verbose))) { - continue; - } - printStackTrace(e); - String msg = options.noExcWrap ? e.toString(options.verbose) - : StringUtil.splitLines(e.toString(options.verbose)); - System.err.println(msg); - last = e; - } - exceptions.clear(); - } - } - - public void registerThrown(KEMException e) { - KException exception = e.exception; - if (!options.includesExceptionType(exception.type)) - return; - if (options.warnings2errors) { - exceptions.add(new KException(ExceptionType.ERROR, exception.exceptionGroup, exception.getMessage(), exception.getSource(), exception.getLocation(), exception.getException())); - } else { - exceptions.add(exception); + KException last = null; + synchronized (exceptions) { + for (KException e : exceptions) { + if (last != null && last.toString(options.verbose).equals(e.toString(options.verbose))) { + continue; } - } - - public List getExceptions() { - return exceptions; - } + printStackTrace(e); + String msg = + options.noExcWrap + ? e.toString(options.verbose) + : StringUtil.splitLines(e.toString(options.verbose)); + System.err.println(msg); + last = e; + } + exceptions.clear(); + } + } + + public void registerThrown(KEMException e) { + KException exception = e.exception; + if (!options.includesExceptionType(exception.type)) return; + if (options.warnings2errors) { + exceptions.add( + new KException( + ExceptionType.ERROR, + exception.exceptionGroup, + exception.getMessage(), + exception.getSource(), + exception.getLocation(), + exception.getException())); + } else { + exceptions.add(exception); + } + } + + public List getExceptions() { + return exceptions; + } } diff --git a/kernel/src/main/java/org/kframework/utils/file/DefinitionDir.java b/kernel/src/main/java/org/kframework/utils/file/DefinitionDir.java index 0da1a7c2a9c..ea0140f6c2f 100644 --- a/kernel/src/main/java/org/kframework/utils/file/DefinitionDir.java +++ b/kernel/src/main/java/org/kframework/utils/file/DefinitionDir.java @@ -1,15 +1,16 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils.file; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import com.google.inject.BindingAnnotation; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import com.google.inject.BindingAnnotation; - -@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME) +@BindingAnnotation +@Target({FIELD, PARAMETER, METHOD}) +@Retention(RUNTIME) public @interface DefinitionDir {} diff --git a/kernel/src/main/java/org/kframework/utils/file/Environment.java b/kernel/src/main/java/org/kframework/utils/file/Environment.java index 055503f2240..4c27ba82865 100644 --- a/kernel/src/main/java/org/kframework/utils/file/Environment.java +++ b/kernel/src/main/java/org/kframework/utils/file/Environment.java @@ -1,15 +1,16 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils.file; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import com.google.inject.BindingAnnotation; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import com.google.inject.BindingAnnotation; - -@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME) +@BindingAnnotation +@Target({FIELD, PARAMETER, METHOD}) +@Retention(RUNTIME) public @interface Environment {} diff --git a/kernel/src/main/java/org/kframework/utils/file/FileUtil.java b/kernel/src/main/java/org/kframework/utils/file/FileUtil.java index 49c5514c3cd..57ee63a0b0b 100644 --- a/kernel/src/main/java/org/kframework/utils/file/FileUtil.java +++ b/kernel/src/main/java/org/kframework/utils/file/FileUtil.java @@ -2,19 +2,6 @@ package org.kframework.utils.file; import com.google.inject.Inject; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.io.IOUtils; -import org.apache.commons.io.input.BoundedInputStream; -import org.apache.commons.lang3.tuple.Pair; -import org.kframework.attributes.Location; -import org.kframework.main.GlobalOptions; -import org.kframework.utils.errorsystem.KEMException; -import org.kframework.utils.errorsystem.KException.ExceptionType; -import org.kframework.utils.errorsystem.KExceptionManager; -import org.kframework.utils.inject.RequestScoped; - -import javax.annotation.Nullable; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -35,286 +22,298 @@ import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.annotation.Nullable; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.input.BoundedInputStream; +import org.apache.commons.lang3.tuple.Pair; +import org.kframework.attributes.Location; +import org.kframework.main.GlobalOptions; +import org.kframework.utils.errorsystem.KEMException; +import org.kframework.utils.errorsystem.KException.ExceptionType; +import org.kframework.utils.errorsystem.KExceptionManager; +import org.kframework.utils.inject.RequestScoped; @RequestScoped public class FileUtil { - private final File tempDir; - private final File kompiledDir; - private final File workingDir; - private final GlobalOptions options; - private final Map env; - - @Inject - public FileUtil( - @TempDir File tempDir, - @WorkingDir File workingDir, - @KompiledDir @Nullable File kompiledDir, - GlobalOptions options, - @Environment Map env) { - this.tempDir = tempDir; - this.workingDir = workingDir; - this.kompiledDir = kompiledDir; - this.options = options; - this.env = env; - } - - public static FileUtil testFileUtil() { - File workingDir = new File("."); - return new FileUtil(workingDir, workingDir, workingDir, new GlobalOptions(), System.getenv()); - } - - public ProcessBuilder getProcessBuilder() { - ProcessBuilder pb = new ProcessBuilder().directory(workingDir); - pb.environment().clear(); - pb.environment().putAll(env); - return pb; - } - - public Map getEnv() { - return env; - } - - public void deleteTempDir(KExceptionManager kem) { - if (!options.debug()) { - try { - FileUtils.deleteDirectory(tempDir); - } catch (IOException e) { - kem.registerCriticalWarning(ExceptionType.UNDELETED_TEMP_DIR, "Failed to delete temporary directory", e); - } - } - } - - /** - * Get language name in uppercase (main module name) given the filename of definition. - */ - public static String getMainModule(String filename) { - return FilenameUtils.getBaseName(filename).toUpperCase(); - } - - // generate an unique name for a folder with the name dirName - public static String generateUniqueFolderName(String dirName) { - DateFormat df = new SimpleDateFormat("-yyyy-MM-dd-HH-mm-ss-SSS-"); - Date today = Calendar.getInstance().getTime(); - String reportDate = df.format(today); - return dirName + reportDate + UUID.randomUUID(); - } - - /** - * Loads the properties from the given file into the given Properties object. - */ - public static void loadProperties(Properties properties, Class cls, String resourcePath) throws IOException { - try (InputStream inStream = cls.getResourceAsStream(resourcePath)) { - if (inStream == null) { - throw new IOException("Could not find resource " + resourcePath); - } - properties.load(inStream); - } - } - - public void saveToDefinitionDirectory(String file, String content) { - save(resolveDefinitionDirectory(file), content); - } - - public String loadFromWorkingDirectory(String file) { - return load(resolveWorkingDirectory(file)); - } - - public void saveToWorkingDirectory(String file, String content) { - save(resolveWorkingDirectory(file), content); - } - - public void saveToWorkingDirectory(String file, byte[] content) { - save(resolveWorkingDirectory(file), content); - } - - public String loadFromKompiled(String file) { - return load(resolveKompiled(file)); - } - - public void saveToKompiled(String file, String content) { - save(resolveKompiled(file), content); - } - - public String loadFromTemp(String file) { - return load(resolveTemp(file)); - } - - public byte[] loadBytesFromTemp(String file) { - return loadBytes(resolveTemp(file)); - } - - public void saveToTemp(String file, String content) { - save(resolveTemp(file), content); - } - - public void saveToTemp(String file, byte[] content) { - save(resolveTemp(file), content); - } - - public String loadFromKIncludeDir(String file) { - return load(resolveKInclude(file)); - } - - public File resolveTemp(String file) { - if (!tempDir.exists() && !tempDir.mkdirs()) { - throw KEMException.criticalError("Could not create temporary directory " + tempDir); - } - return new File(tempDir, file); - } - - public File resolveKompiled(String file) { - return new File(kompiledDir, file); - } - - public File resolveDefinitionDirectory(String file) { - return kompiledDir == null ? null : new File(kompiledDir.getParentFile(), file); - } - - public File resolveWorkingDirectory(String file) { - return resolveWorkingDirectory(new File(file)); - } - - public File resolveWorkingDirectory(File file) { - if (file.isAbsolute()) return file; - return new File(workingDir, file.getPath()); - } - - public File resolveKInclude(String file) { - return new File(JarInfo.getKIncludeDir().toFile(), file); - } - - // don't use this if you want a file in the include directory. Use resolveKInclude. - public File resolveKBase(String file) { - return new File(JarInfo.getKBase(), file); - } - - public void copyTempFileToDefinitionDirectory(String fromPath) { - copyFileToDirectory(resolveTemp(fromPath), resolveDefinitionDirectory(".")); - } - - public void copyTempFileToKompiledDirectory(String fromPath) { - copyFileToDirectory(resolveTemp(fromPath), resolveKompiled(".")); - } - - public void copyTempFileToKompiledFile(String fromPath, String toPath) { - copyFile(resolveTemp(fromPath), resolveKompiled(toPath)); - } - - private void copyFile(File from, File to) { - try { - FileUtils.copyFile(from, to); - } catch (IOException e) { - throw KEMException.criticalError("Could not copy " + from + " to " + to, e); - } - } - - public void copyFileToDirectory(File from, File toDir) { - try { - FileUtils.copyFileToDirectory(from, toDir); - } catch (IOException e) { - throw KEMException.criticalError("Could not copy " + from + " to directory " + toDir, e); - } - } - - public static void save(File file, String content) { - try { - File dir = file.getAbsoluteFile().getParentFile(); - if (!dir.exists() && !dir.mkdirs()) { - throw KEMException.criticalError("Could not create directory " + dir); - } - FileUtils.writeStringToFile(file, content); - } catch (IOException e) { - throw KEMException.criticalError("Could not write to file " + file.getAbsolutePath(), e); - } - } - - public static void save(File file, byte[] content) { - try { - File dir = file.getAbsoluteFile().getParentFile(); - if (!dir.exists() && !dir.mkdirs()) { - throw KEMException.criticalError("Could not create directory " + dir); - } - FileUtils.writeByteArrayToFile(file, content); - } catch (IOException e) { - throw KEMException.criticalError("Could not write to file " + file.getAbsolutePath(), e); - } - } - - public static String load(File file) { - try { - return FileUtils.readFileToString(file); - } catch (IOException e) { - throw KEMException.criticalError("Could not read from file " + file.getAbsolutePath(), e); - } - } - - /** - * Loads the given fragment of a file to String. - *

- * Source: https://stackoverflow.com/a/4305478/4182868 - */ - public static String loadFragment(File file, int pos, int len) { - try (FileInputStream stream = new FileInputStream(file)) { - stream.skip(pos); - return IOUtils.toString(new BoundedInputStream(stream, len)); - } catch (IOException e) { - throw KEMException.criticalError("Could not read from file " + file.getAbsolutePath(), e); - } - } - - public static String loadFragment(File file, Location location) { - try (Stream lines = new BufferedReader(new InputStreamReader(new FileInputStream(file))).lines() - .skip(location.startLine() - 1) - .limit(location.endLine() - location.startLine() + 1)) { - return lines.collect(Collectors.joining("\n")); - } catch (IOException e) { - throw KEMException.criticalError("Could not read from file " + file.getAbsolutePath(), e); - } - } - - public static byte[] loadBytes(File file) { - try { - return FileUtils.readFileToByteArray(file); - } catch (IOException e) { - throw KEMException.criticalError("Could not read from file " + file.getAbsolutePath(), e); - } - } - - public static Pair pipeOutputToInput() { - try { - PipedOutputStream out = new PipedOutputStream(); - PipedInputStream in = new PipedInputStream(out); - return Pair.of(in, out); - } catch (IOException e) { - throw KEMException.internalError("Error creating input/output pipe", e); - } - } - - public static Pair pipeInputToOutput() { - try { - PipedInputStream in = new PipedInputStream(); - PipedOutputStream out = new PipedOutputStream(in); - return Pair.of(out, in); - } catch (IOException e) { - throw KEMException.internalError("Error creating input/output pipe", e); - } - } - - public static String read(Reader reader) { - try { - return IOUtils.toString(reader); - } catch (IOException e) { - throw KEMException.internalError("Error reading from " + reader, e); - } - } - - public Reader readFromWorkingDirectory(String path) { - File f = resolveWorkingDirectory(path); - try { - return new FileReader(f); - } catch (FileNotFoundException e) { - throw KEMException.criticalError("Could not read from file " + f.getAbsolutePath(), e); - } - } + private final File tempDir; + private final File kompiledDir; + private final File workingDir; + private final GlobalOptions options; + private final Map env; + + @Inject + public FileUtil( + @TempDir File tempDir, + @WorkingDir File workingDir, + @KompiledDir @Nullable File kompiledDir, + GlobalOptions options, + @Environment Map env) { + this.tempDir = tempDir; + this.workingDir = workingDir; + this.kompiledDir = kompiledDir; + this.options = options; + this.env = env; + } + + public static FileUtil testFileUtil() { + File workingDir = new File("."); + return new FileUtil(workingDir, workingDir, workingDir, new GlobalOptions(), System.getenv()); + } + + public ProcessBuilder getProcessBuilder() { + ProcessBuilder pb = new ProcessBuilder().directory(workingDir); + pb.environment().clear(); + pb.environment().putAll(env); + return pb; + } + + public Map getEnv() { + return env; + } + + public void deleteTempDir(KExceptionManager kem) { + if (!options.debug()) { + try { + FileUtils.deleteDirectory(tempDir); + } catch (IOException e) { + kem.registerCriticalWarning( + ExceptionType.UNDELETED_TEMP_DIR, "Failed to delete temporary directory", e); + } + } + } + + /** Get language name in uppercase (main module name) given the filename of definition. */ + public static String getMainModule(String filename) { + return FilenameUtils.getBaseName(filename).toUpperCase(); + } + + // generate an unique name for a folder with the name dirName + public static String generateUniqueFolderName(String dirName) { + DateFormat df = new SimpleDateFormat("-yyyy-MM-dd-HH-mm-ss-SSS-"); + Date today = Calendar.getInstance().getTime(); + String reportDate = df.format(today); + return dirName + reportDate + UUID.randomUUID(); + } + + /** Loads the properties from the given file into the given Properties object. */ + public static void loadProperties(Properties properties, Class cls, String resourcePath) + throws IOException { + try (InputStream inStream = cls.getResourceAsStream(resourcePath)) { + if (inStream == null) { + throw new IOException("Could not find resource " + resourcePath); + } + properties.load(inStream); + } + } + + public void saveToDefinitionDirectory(String file, String content) { + save(resolveDefinitionDirectory(file), content); + } + + public String loadFromWorkingDirectory(String file) { + return load(resolveWorkingDirectory(file)); + } + + public void saveToWorkingDirectory(String file, String content) { + save(resolveWorkingDirectory(file), content); + } + + public void saveToWorkingDirectory(String file, byte[] content) { + save(resolveWorkingDirectory(file), content); + } + + public String loadFromKompiled(String file) { + return load(resolveKompiled(file)); + } + + public void saveToKompiled(String file, String content) { + save(resolveKompiled(file), content); + } + + public String loadFromTemp(String file) { + return load(resolveTemp(file)); + } + + public byte[] loadBytesFromTemp(String file) { + return loadBytes(resolveTemp(file)); + } + + public void saveToTemp(String file, String content) { + save(resolveTemp(file), content); + } + + public void saveToTemp(String file, byte[] content) { + save(resolveTemp(file), content); + } + + public String loadFromKIncludeDir(String file) { + return load(resolveKInclude(file)); + } + + public File resolveTemp(String file) { + if (!tempDir.exists() && !tempDir.mkdirs()) { + throw KEMException.criticalError("Could not create temporary directory " + tempDir); + } + return new File(tempDir, file); + } + + public File resolveKompiled(String file) { + return new File(kompiledDir, file); + } + + public File resolveDefinitionDirectory(String file) { + return kompiledDir == null ? null : new File(kompiledDir.getParentFile(), file); + } + + public File resolveWorkingDirectory(String file) { + return resolveWorkingDirectory(new File(file)); + } + + public File resolveWorkingDirectory(File file) { + if (file.isAbsolute()) return file; + return new File(workingDir, file.getPath()); + } + + public File resolveKInclude(String file) { + return new File(JarInfo.getKIncludeDir().toFile(), file); + } + + // don't use this if you want a file in the include directory. Use resolveKInclude. + public File resolveKBase(String file) { + return new File(JarInfo.getKBase(), file); + } + + public void copyTempFileToDefinitionDirectory(String fromPath) { + copyFileToDirectory(resolveTemp(fromPath), resolveDefinitionDirectory(".")); + } + + public void copyTempFileToKompiledDirectory(String fromPath) { + copyFileToDirectory(resolveTemp(fromPath), resolveKompiled(".")); + } + + public void copyTempFileToKompiledFile(String fromPath, String toPath) { + copyFile(resolveTemp(fromPath), resolveKompiled(toPath)); + } + + private void copyFile(File from, File to) { + try { + FileUtils.copyFile(from, to); + } catch (IOException e) { + throw KEMException.criticalError("Could not copy " + from + " to " + to, e); + } + } + + public void copyFileToDirectory(File from, File toDir) { + try { + FileUtils.copyFileToDirectory(from, toDir); + } catch (IOException e) { + throw KEMException.criticalError("Could not copy " + from + " to directory " + toDir, e); + } + } + + public static void save(File file, String content) { + try { + File dir = file.getAbsoluteFile().getParentFile(); + if (!dir.exists() && !dir.mkdirs()) { + throw KEMException.criticalError("Could not create directory " + dir); + } + FileUtils.writeStringToFile(file, content); + } catch (IOException e) { + throw KEMException.criticalError("Could not write to file " + file.getAbsolutePath(), e); + } + } + + public static void save(File file, byte[] content) { + try { + File dir = file.getAbsoluteFile().getParentFile(); + if (!dir.exists() && !dir.mkdirs()) { + throw KEMException.criticalError("Could not create directory " + dir); + } + FileUtils.writeByteArrayToFile(file, content); + } catch (IOException e) { + throw KEMException.criticalError("Could not write to file " + file.getAbsolutePath(), e); + } + } + + public static String load(File file) { + try { + return FileUtils.readFileToString(file); + } catch (IOException e) { + throw KEMException.criticalError("Could not read from file " + file.getAbsolutePath(), e); + } + } + + /** + * Loads the given fragment of a file to String. + * + *

Source: https://stackoverflow.com/a/4305478/4182868 + */ + public static String loadFragment(File file, int pos, int len) { + try (FileInputStream stream = new FileInputStream(file)) { + stream.skip(pos); + return IOUtils.toString(new BoundedInputStream(stream, len)); + } catch (IOException e) { + throw KEMException.criticalError("Could not read from file " + file.getAbsolutePath(), e); + } + } + + public static String loadFragment(File file, Location location) { + try (Stream lines = + new BufferedReader(new InputStreamReader(new FileInputStream(file))) + .lines() + .skip(location.startLine() - 1) + .limit(location.endLine() - location.startLine() + 1)) { + return lines.collect(Collectors.joining("\n")); + } catch (IOException e) { + throw KEMException.criticalError("Could not read from file " + file.getAbsolutePath(), e); + } + } + + public static byte[] loadBytes(File file) { + try { + return FileUtils.readFileToByteArray(file); + } catch (IOException e) { + throw KEMException.criticalError("Could not read from file " + file.getAbsolutePath(), e); + } + } + + public static Pair pipeOutputToInput() { + try { + PipedOutputStream out = new PipedOutputStream(); + PipedInputStream in = new PipedInputStream(out); + return Pair.of(in, out); + } catch (IOException e) { + throw KEMException.internalError("Error creating input/output pipe", e); + } + } + + public static Pair pipeInputToOutput() { + try { + PipedInputStream in = new PipedInputStream(); + PipedOutputStream out = new PipedOutputStream(in); + return Pair.of(out, in); + } catch (IOException e) { + throw KEMException.internalError("Error creating input/output pipe", e); + } + } + + public static String read(Reader reader) { + try { + return IOUtils.toString(reader); + } catch (IOException e) { + throw KEMException.internalError("Error reading from " + reader, e); + } + } + + public Reader readFromWorkingDirectory(String path) { + File f = resolveWorkingDirectory(path); + try { + return new FileReader(f); + } catch (FileNotFoundException e) { + throw KEMException.criticalError("Could not read from file " + f.getAbsolutePath(), e); + } + } } diff --git a/kernel/src/main/java/org/kframework/utils/file/JarInfo.java b/kernel/src/main/java/org/kframework/utils/file/JarInfo.java index b3b58d0b1bd..418ebcf343c 100644 --- a/kernel/src/main/java/org/kframework/utils/file/JarInfo.java +++ b/kernel/src/main/java/org/kframework/utils/file/JarInfo.java @@ -1,93 +1,98 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils.file; -import org.apache.commons.io.FileUtils; - -import org.kframework.utils.errorsystem.KEMException; -import org.kframework.utils.errorsystem.KExceptionManager; - import com.google.inject.Inject; - import java.io.File; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.net.*; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Date; import java.util.jar.Manifest; +import org.apache.commons.io.FileUtils; +import org.kframework.utils.errorsystem.KEMException; +import org.kframework.utils.errorsystem.KExceptionManager; public class JarInfo { - private static final String JAR_PATH = "k-distribution/target/release/k/lib/kframework/java/JAR_FILENAME_PLACEHOLDER.jar"; + private static final String JAR_PATH = + "k-distribution/target/release/k/lib/kframework/java/JAR_FILENAME_PLACEHOLDER.jar"; - /** - * Returns the K installation directory - * - * @return The path to the K installation - */ - public static String getKBase() { - // String env = System.getenv("K_BASE"); - String path = new File(JarInfo.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getAbsolutePath(); - if (!path.endsWith(".jar") || new File(path).getParentFile().getName().equals("target")) - path = new File(path).getParentFile().getParentFile().getParentFile().getAbsolutePath() + "/" + JAR_PATH; - String decodedPath = URLDecoder.decode(path, StandardCharsets.UTF_8); - File parent = new File(decodedPath).getParentFile().getParentFile().getParentFile().getParentFile(); - return parent.getAbsolutePath(); - } + /** + * Returns the K installation directory + * + * @return The path to the K installation + */ + public static String getKBase() { + // String env = System.getenv("K_BASE"); + String path = + new File(JarInfo.class.getProtectionDomain().getCodeSource().getLocation().getPath()) + .getAbsolutePath(); + if (!path.endsWith(".jar") || new File(path).getParentFile().getName().equals("target")) + path = + new File(path).getParentFile().getParentFile().getParentFile().getAbsolutePath() + + "/" + + JAR_PATH; + String decodedPath = URLDecoder.decode(path, StandardCharsets.UTF_8); + File parent = + new File(decodedPath).getParentFile().getParentFile().getParentFile().getParentFile(); + return parent.getAbsolutePath(); + } - /** - * Returns the absolute path of the includes directory. - * Paths are computed relative to the location this class is running from. - * When it is run from a jar file it assumes it is in a k installation - * at lib/java/*.jar. - * When it is run from a .class file it assumes it is running within the - * K source project, from a class in kernel/target/classes/, and - * returns a path to k-distribution/include - * - * @return - */ - public static Path getKIncludeDir() { - Path path; - try { - path = Paths.get(JarInfo.class.getProtectionDomain().getCodeSource().getLocation().toURI()); - } catch (URISyntaxException e) { - return null; - } - if (!path.toFile().getAbsolutePath().endsWith(".jar") || path.getParent().getFileName().toString().equals("target")) { - return path.getParent().resolve("../../k-distribution/include/kframework"); - } else { - return path.getParent().resolve("../../../include/kframework"); - } + /** + * Returns the absolute path of the includes directory. Paths are computed relative to the + * location this class is running from. When it is run from a jar file it assumes it is in a k + * installation at lib/java/*.jar. When it is run from a .class file it assumes it is running + * within the K source project, from a class in kernel/target/classes/, and returns a path to + * k-distribution/include + * + * @return + */ + public static Path getKIncludeDir() { + Path path; + try { + path = Paths.get(JarInfo.class.getProtectionDomain().getCodeSource().getLocation().toURI()); + } catch (URISyntaxException e) { + return null; + } + if (!path.toFile().getAbsolutePath().endsWith(".jar") + || path.getParent().getFileName().toString().equals("target")) { + return path.getParent().resolve("../../k-distribution/include/kframework"); + } else { + return path.getParent().resolve("../../../include/kframework"); } + } - private final KExceptionManager kem; + private final KExceptionManager kem; - @Inject - public JarInfo(KExceptionManager kem) { - this.kem = kem; - } + @Inject + public JarInfo(KExceptionManager kem) { + this.kem = kem; + } - public void printVersionMessage() { - String kBase = getKBase(); - URL url = JarInfo.class.getResource("versionMarker"); - try { - URLConnection conn = url.openConnection(); - Manifest mf = ((JarURLConnection)conn).getManifest(); + public void printVersionMessage() { + String kBase = getKBase(); + URL url = JarInfo.class.getResource("versionMarker"); + try { + URLConnection conn = url.openConnection(); + Manifest mf = ((JarURLConnection) conn).getManifest(); - String versionDate = new Date(Long.parseLong(mf.getMainAttributes().getValue("Implementation-Date"))).toString(); + String versionDate = + new Date(Long.parseLong(mf.getMainAttributes().getValue("Implementation-Date"))) + .toString(); - // Use the output of 'git describe' if we're building K from a Git repository, or fall back to - // the release version if we're not (e.g. from a release tarball). - String version = mf.getMainAttributes().getValue("Implementation-Git-Describe"); - if (version.isEmpty()) { - version = "v" + FileUtils.readFileToString(new File(kBase + "/lib/kframework/version")).trim(); - } + // Use the output of 'git describe' if we're building K from a Git repository, or fall back to + // the release version if we're not (e.g. from a release tarball). + String version = mf.getMainAttributes().getValue("Implementation-Git-Describe"); + if (version.isEmpty()) { + version = + "v" + FileUtils.readFileToString(new File(kBase + "/lib/kframework/version")).trim(); + } - System.out.println("K version: " + version); - System.out.println("Build date: " + versionDate); - } catch (IOException e) { - throw KEMException.internalError("Could not load version info."); - } + System.out.println("K version: " + version); + System.out.println("Build date: " + versionDate); + } catch (IOException e) { + throw KEMException.internalError("Could not load version info."); } + } } diff --git a/kernel/src/main/java/org/kframework/utils/file/KompiledDir.java b/kernel/src/main/java/org/kframework/utils/file/KompiledDir.java index 1727b5b79a0..a1fba22b674 100644 --- a/kernel/src/main/java/org/kframework/utils/file/KompiledDir.java +++ b/kernel/src/main/java/org/kframework/utils/file/KompiledDir.java @@ -1,15 +1,16 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils.file; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import com.google.inject.BindingAnnotation; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import com.google.inject.BindingAnnotation; - -@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME) +@BindingAnnotation +@Target({FIELD, PARAMETER, METHOD}) +@Retention(RUNTIME) public @interface KompiledDir {} diff --git a/kernel/src/main/java/org/kframework/utils/file/TTYInfo.java b/kernel/src/main/java/org/kframework/utils/file/TTYInfo.java index c0a9f6cc016..a0fc3e2ead9 100644 --- a/kernel/src/main/java/org/kframework/utils/file/TTYInfo.java +++ b/kernel/src/main/java/org/kframework/utils/file/TTYInfo.java @@ -2,10 +2,9 @@ package org.kframework.utils.file; /** - * Encapsulates information about whether stdin, stdout, and stderr are - * connected to terminals. Essentially, each boolean in this class is - * true if and only if the corresponding stream is one for which - * ultimately the isatty function returns true. + * Encapsulates information about whether stdin, stdout, and stderr are connected to terminals. + * Essentially, each boolean in this class is true if and only if the corresponding stream is one + * for which ultimately the isatty function returns true. * * @author dwightguth */ diff --git a/kernel/src/main/java/org/kframework/utils/file/TempDir.java b/kernel/src/main/java/org/kframework/utils/file/TempDir.java index 3795db45a43..84531c112ae 100644 --- a/kernel/src/main/java/org/kframework/utils/file/TempDir.java +++ b/kernel/src/main/java/org/kframework/utils/file/TempDir.java @@ -1,15 +1,16 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils.file; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import com.google.inject.BindingAnnotation; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import com.google.inject.BindingAnnotation; - -@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME) +@BindingAnnotation +@Target({FIELD, PARAMETER, METHOD}) +@Retention(RUNTIME) public @interface TempDir {} diff --git a/kernel/src/main/java/org/kframework/utils/file/WorkingDir.java b/kernel/src/main/java/org/kframework/utils/file/WorkingDir.java index 9e0702672f8..f22a7461d6c 100644 --- a/kernel/src/main/java/org/kframework/utils/file/WorkingDir.java +++ b/kernel/src/main/java/org/kframework/utils/file/WorkingDir.java @@ -1,15 +1,16 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils.file; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import com.google.inject.BindingAnnotation; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import com.google.inject.BindingAnnotation; - -@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME) +@BindingAnnotation +@Target({FIELD, PARAMETER, METHOD}) +@Retention(RUNTIME) public @interface WorkingDir {} diff --git a/kernel/src/main/java/org/kframework/utils/inject/Annotations.java b/kernel/src/main/java/org/kframework/utils/inject/Annotations.java index 585dfa0012b..364282bf226 100644 --- a/kernel/src/main/java/org/kframework/utils/inject/Annotations.java +++ b/kernel/src/main/java/org/kframework/utils/inject/Annotations.java @@ -5,87 +5,84 @@ public class Annotations { - public static Main main(Class annotation) { - return new Main() { - - @Override - public Class annotationType() { - return Main.class; - } - - @Override - public Class value() { - return annotation; - } - - public int hashCode() { - // This is specified in java.lang.Annotation. - return (127 * "value".hashCode()) ^ annotation.hashCode(); - } - - public boolean equals(Object o) { - if (!(o instanceof Main other)) { - return false; - } - return annotation.equals(other.value()); - } - - }; - } - - public static Spec1 spec1(Class annotation) { - return new Spec1() { - - @Override - public Class annotationType() { - return Spec1.class; - } - - @Override - public Class value() { - return annotation; - } - - public int hashCode() { - // This is specified in java.lang.Annotation. - return (127 * "value".hashCode()) ^ annotation.hashCode(); - } - - public boolean equals(Object o) { - if (!(o instanceof Spec1 other)) { - return false; - } - return annotation.equals(other.value()); - } - - }; - } - - public static Spec2 spec2(Class annotation) { - return new Spec2() { - - @Override - public Class annotationType() { - return Spec2.class; - } - - @Override - public Class value() { - return annotation; - } - - public int hashCode() { - // This is specified in java.lang.Annotation. - return (127 * "value".hashCode()) ^ annotation.hashCode(); - } - - public boolean equals(Object o) { - if (!(o instanceof Spec2 other)) { - return false; - } - return annotation.equals(other.value()); - } - - }; - } + public static Main main(Class annotation) { + return new Main() { + + @Override + public Class annotationType() { + return Main.class; + } + + @Override + public Class value() { + return annotation; + } + + public int hashCode() { + // This is specified in java.lang.Annotation. + return (127 * "value".hashCode()) ^ annotation.hashCode(); + } + + public boolean equals(Object o) { + if (!(o instanceof Main other)) { + return false; + } + return annotation.equals(other.value()); + } + }; + } + + public static Spec1 spec1(Class annotation) { + return new Spec1() { + + @Override + public Class annotationType() { + return Spec1.class; + } + + @Override + public Class value() { + return annotation; + } + + public int hashCode() { + // This is specified in java.lang.Annotation. + return (127 * "value".hashCode()) ^ annotation.hashCode(); + } + + public boolean equals(Object o) { + if (!(o instanceof Spec1 other)) { + return false; + } + return annotation.equals(other.value()); + } + }; + } + + public static Spec2 spec2(Class annotation) { + return new Spec2() { + + @Override + public Class annotationType() { + return Spec2.class; + } + + @Override + public Class value() { + return annotation; + } + + public int hashCode() { + // This is specified in java.lang.Annotation. + return (127 * "value".hashCode()) ^ annotation.hashCode(); + } + + public boolean equals(Object o) { + if (!(o instanceof Spec2 other)) { + return false; + } + return annotation.equals(other.value()); + } + }; + } } diff --git a/kernel/src/main/java/org/kframework/utils/inject/Builtins.java b/kernel/src/main/java/org/kframework/utils/inject/Builtins.java index cf10a7f1022..de7c3188d7f 100644 --- a/kernel/src/main/java/org/kframework/utils/inject/Builtins.java +++ b/kernel/src/main/java/org/kframework/utils/inject/Builtins.java @@ -1,15 +1,16 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils.inject; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import com.google.inject.BindingAnnotation; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import com.google.inject.BindingAnnotation; - -@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME) +@BindingAnnotation +@Target({FIELD, PARAMETER, METHOD}) +@Retention(RUNTIME) public @interface Builtins {} diff --git a/kernel/src/main/java/org/kframework/utils/inject/CommonModule.java b/kernel/src/main/java/org/kframework/utils/inject/CommonModule.java index bb1a47f6d2c..ea40ba31302 100644 --- a/kernel/src/main/java/org/kframework/utils/inject/CommonModule.java +++ b/kernel/src/main/java/org/kframework/utils/inject/CommonModule.java @@ -1,10 +1,14 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils.inject; +import static org.fusesource.jansi.internal.CLibrary.*; + import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.TypeLiteral; import com.google.inject.name.Names; +import java.io.File; +import java.util.Map; import org.kframework.main.GlobalOptions; import org.kframework.main.Tool; import org.kframework.utils.file.Environment; @@ -13,61 +17,67 @@ import org.kframework.utils.file.TempDir; import org.kframework.utils.file.WorkingDir; -import java.io.File; -import java.util.Map; +public class CommonModule extends AbstractModule { -import static org.fusesource.jansi.internal.CLibrary.*; + @Override + protected void configure() { + binder().requireAtInjectOnConstructors(); + SimpleScope requestScope = new SimpleScope(); + bindScope(RequestScoped.class, requestScope); + DefinitionScope definitionScope = new DefinitionScope(); + bindScope(DefinitionScoped.class, definitionScope); + bind(SimpleScope.class).annotatedWith(Names.named("requestScope")).toInstance(requestScope); + bind(DefinitionScope.class).toInstance(definitionScope); + bind(File.class) + .annotatedWith(WorkingDir.class) + .toProvider(SimpleScope.seededKeyProvider()) + .in(RequestScoped.class); + bind(new TypeLiteral>() {}) + .annotatedWith(Environment.class) + .toProvider(SimpleScope.seededKeyProvider()) + .in(RequestScoped.class); + bind(Long.class) + .annotatedWith(StartTime.class) + .toProvider(SimpleScope.seededKeyProvider()) + .in(RequestScoped.class); + } -public class CommonModule extends AbstractModule { + @Provides + @TempDir + @RequestScoped + File tempDir(Tool tool, GlobalOptions go) { + if (go.tempDir != null) + return new File( + go.tempDir, FileUtil.generateUniqueFolderName("." + tool.name().toLowerCase())); + return new File( + System.getProperty("java.io.tmpdir"), + FileUtil.generateUniqueFolderName("." + tool.name().toLowerCase())); + } - @Override - protected void configure() { - binder().requireAtInjectOnConstructors(); - SimpleScope requestScope = new SimpleScope(); - bindScope(RequestScoped.class, requestScope); - DefinitionScope definitionScope = new DefinitionScope(); - bindScope(DefinitionScoped.class, definitionScope); - bind(SimpleScope.class).annotatedWith(Names.named("requestScope")).toInstance(requestScope); - bind(DefinitionScope.class).toInstance(definitionScope); - bind(File.class).annotatedWith(WorkingDir.class) - .toProvider(SimpleScope.seededKeyProvider()).in(RequestScoped.class); - bind(new TypeLiteral>() {}).annotatedWith(Environment.class) - .toProvider(SimpleScope.seededKeyProvider()).in(RequestScoped.class); - bind(Long.class).annotatedWith(StartTime.class) - .toProvider(SimpleScope.seededKeyProvider()).in(RequestScoped.class); - } + @Provides + ProcessBuilder pb(@WorkingDir File workingDir, @Environment Map env) { + return new FileUtil(null, workingDir, null, null, env).getProcessBuilder(); + } - @Provides @TempDir @RequestScoped - File tempDir(Tool tool, GlobalOptions go) { - if (go.tempDir != null) - return new File(go.tempDir, FileUtil.generateUniqueFolderName("." + tool.name().toLowerCase())); - return new File(System.getProperty("java.io.tmpdir"), FileUtil.generateUniqueFolderName("." + tool.name().toLowerCase())); + @Provides + @RequestScoped + TTYInfo ttyInfo(@Environment Map env) { + boolean stdin, stdout, stderr; + if (env.containsKey("NAILGUN_TTY_0")) { + stdin = !env.get("NAILGUN_TTY_0").equals("0"); + } else { + stdin = isatty(0) != 0; } - - @Provides - ProcessBuilder pb(@WorkingDir File workingDir, @Environment Map env) { - return new FileUtil(null, workingDir, null, null, env).getProcessBuilder(); + if (env.containsKey("NAILGUN_TTY_1")) { + stdout = !env.get("NAILGUN_TTY_1").equals("0"); + } else { + stdout = isatty(1) != 0; } - - @Provides @RequestScoped - TTYInfo ttyInfo(@Environment Map env) { - boolean stdin, stdout, stderr; - if (env.containsKey("NAILGUN_TTY_0")) { - stdin = !env.get("NAILGUN_TTY_0").equals("0"); - } else { - stdin = isatty(0) != 0; - } - if (env.containsKey("NAILGUN_TTY_1")) { - stdout = !env.get("NAILGUN_TTY_1").equals("0"); - } else { - stdout = isatty(1) != 0; - } - if (env.containsKey("NAILGUN_TTY_2")) { - stderr = !env.get("NAILGUN_TTY_2").equals("0"); - } else { - stderr = isatty(2) != 0; - } - return new TTYInfo(stdin, stdout, stderr); + if (env.containsKey("NAILGUN_TTY_2")) { + stderr = !env.get("NAILGUN_TTY_2").equals("0"); + } else { + stderr = isatty(2) != 0; } - + return new TTYInfo(stdin, stdout, stderr); + } } diff --git a/kernel/src/main/java/org/kframework/utils/inject/Concrete.java b/kernel/src/main/java/org/kframework/utils/inject/Concrete.java index 7aad03e5728..bbcb74290be 100644 --- a/kernel/src/main/java/org/kframework/utils/inject/Concrete.java +++ b/kernel/src/main/java/org/kframework/utils/inject/Concrete.java @@ -1,15 +1,16 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils.inject; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import com.google.inject.BindingAnnotation; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import com.google.inject.BindingAnnotation; - -@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME) +@BindingAnnotation +@Target({FIELD, PARAMETER, METHOD}) +@Retention(RUNTIME) public @interface Concrete {} diff --git a/kernel/src/main/java/org/kframework/utils/inject/DefinitionLoadingModule.java b/kernel/src/main/java/org/kframework/utils/inject/DefinitionLoadingModule.java index 9f3f60084de..b003e200e0c 100644 --- a/kernel/src/main/java/org/kframework/utils/inject/DefinitionLoadingModule.java +++ b/kernel/src/main/java/org/kframework/utils/inject/DefinitionLoadingModule.java @@ -1,115 +1,124 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils.inject; +import static org.kframework.utils.errorsystem.KException.ExceptionType.*; + import com.google.inject.AbstractModule; import com.google.inject.Provider; import com.google.inject.Provides; +import java.io.File; +import java.util.Map; import org.kframework.kompile.CompiledDefinition; import org.kframework.kompile.KompileOptions; import org.kframework.main.GlobalOptions; import org.kframework.utils.BinaryLoader; import org.kframework.utils.errorsystem.KEMException; import org.kframework.utils.errorsystem.KExceptionManager; -import org.kframework.utils.file.DefinitionDir; import org.kframework.utils.file.Environment; import org.kframework.utils.file.FileUtil; import org.kframework.utils.file.KompiledDir; import org.kframework.utils.file.WorkingDir; import org.kframework.utils.options.DefinitionLoadingOptions; -import java.io.File; -import java.io.FilenameFilter; -import java.util.Map; - -import static org.kframework.utils.errorsystem.KException.ExceptionType.*; - public class DefinitionLoadingModule extends AbstractModule { - @Override - protected void configure() { - binder().requireAtInjectOnConstructors(); - } + @Override + protected void configure() { + binder().requireAtInjectOnConstructors(); + } - @Provides @DefinitionScoped - CompiledDefinition koreDefinition(BinaryLoader loader, FileUtil files) { - return loader.loadOrDie(CompiledDefinition.class, files.resolveKompiled("compiled.bin")); - } + @Provides + @DefinitionScoped + CompiledDefinition koreDefinition(BinaryLoader loader, FileUtil files) { + return loader.loadOrDie(CompiledDefinition.class, files.resolveKompiled("compiled.bin")); + } + @Provides + @RequestScoped + KompileOptions kompileOptions(Provider compiledDef) { + // a hack, but it's good enough for what we need from it, which is a temporary solution + KompileOptions res = compiledDef.get().kompileOptions; + return res; + } - @Provides @RequestScoped - KompileOptions kompileOptions(Provider compiledDef) { - // a hack, but it's good enough for what we need from it, which is a temporary solution - KompileOptions res = compiledDef.get().kompileOptions; - return res; - } - - @Provides @KompiledDir - File directory(DefinitionLoadingOptions options, @WorkingDir File workingDir, @Environment Map env) { - File directory = null; - if (options.inputDirectory != null) { - if (options.directory != null) - throw KEMException.criticalError("Cannot use both --directory and --definition at the same time."); - File f = new File(options.inputDirectory); - if (f.isAbsolute()) { - directory = f; - } else { - directory = new File(workingDir, options.inputDirectory); - } - } else if (env.get("KRUN_KOMPILED_DIR") != null) { - File f = new File(env.get("KRUN_KOMPILED_DIR")); - if (f.isAbsolute()) { - directory = f; - } else { - directory = new File(workingDir, env.get("KRUN_KOMPILED_DIR")); - } + @Provides + @KompiledDir + File directory( + DefinitionLoadingOptions options, + @WorkingDir File workingDir, + @Environment Map env) { + File directory = null; + if (options.inputDirectory != null) { + if (options.directory != null) + throw KEMException.criticalError( + "Cannot use both --directory and --definition at the same time."); + File f = new File(options.inputDirectory); + if (f.isAbsolute()) { + directory = f; + } else { + directory = new File(workingDir, options.inputDirectory); + } + } else if (env.get("KRUN_KOMPILED_DIR") != null) { + File f = new File(env.get("KRUN_KOMPILED_DIR")); + if (f.isAbsolute()) { + directory = f; + } else { + directory = new File(workingDir, env.get("KRUN_KOMPILED_DIR")); + } + } else { + File whereDir; + if (options.directory == null) { + if (env.get("KRUN_COMPILED_DEF") != null) { + File f = new File(env.get("KRUN_COMPILED_DEF")); + if (f.isAbsolute()) { + whereDir = f; + } else { + whereDir = new File(workingDir, env.get("KRUN_COMPILED_DEF")); + } } else { - File whereDir; - if (options.directory == null) { - if (env.get("KRUN_COMPILED_DEF") != null) { - File f = new File(env.get("KRUN_COMPILED_DEF")); - if (f.isAbsolute()) { - whereDir = f; - } else { - whereDir = new File(workingDir, env.get("KRUN_COMPILED_DEF")); - } - } else { - whereDir = workingDir; - } - } else { - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - kem.registerCriticalWarning(DEPRECATED_DIRECTORY_FLAG, "Using --directory is deprecated. Use --output-definition instead."); - kem.print(); - File f = new File(options.directory); - if (f.isAbsolute()) { - whereDir = f; - } else { - whereDir = new File(workingDir, options.directory); - } - } - File[] dirs = whereDir.listFiles((current, name) -> new File(current, name).isDirectory()); - - if (dirs != null) { - for (File dir : dirs) { - if (dir.getAbsolutePath().endsWith("-kompiled")) { - if (directory != null) { - throw KEMException.criticalError("Multiple compiled definitions found in the " - + "current working directory: " + directory.getAbsolutePath() + " and " + - dir.getAbsolutePath()); - } else { - directory = dir; - } - } - } - } + whereDir = workingDir; + } + } else { + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + kem.registerCriticalWarning( + DEPRECATED_DIRECTORY_FLAG, + "Using --directory is deprecated. Use --output-definition instead."); + kem.print(); + File f = new File(options.directory); + if (f.isAbsolute()) { + whereDir = f; + } else { + whereDir = new File(workingDir, options.directory); + } + } + File[] dirs = whereDir.listFiles((current, name) -> new File(current, name).isDirectory()); - if (directory == null) { - throw KEMException.criticalError("Could not find a compiled definition. " + - "Use --definition to specify one."); + if (dirs != null) { + for (File dir : dirs) { + if (dir.getAbsolutePath().endsWith("-kompiled")) { + if (directory != null) { + throw KEMException.criticalError( + "Multiple compiled definitions found in the " + + "current working directory: " + + directory.getAbsolutePath() + + " and " + + dir.getAbsolutePath()); + } else { + directory = dir; } + } } - if (!directory.isDirectory()) { - throw KEMException.criticalError("Does not exist or not a directory: " + directory.getAbsolutePath()); - } - return directory; + } + + if (directory == null) { + throw KEMException.criticalError( + "Could not find a compiled definition. " + "Use --definition to specify one."); + } + } + if (!directory.isDirectory()) { + throw KEMException.criticalError( + "Does not exist or not a directory: " + directory.getAbsolutePath()); } + return directory; + } } diff --git a/kernel/src/main/java/org/kframework/utils/inject/DefinitionScope.java b/kernel/src/main/java/org/kframework/utils/inject/DefinitionScope.java index 101a57145d9..35466b1c34c 100644 --- a/kernel/src/main/java/org/kframework/utils/inject/DefinitionScope.java +++ b/kernel/src/main/java/org/kframework/utils/inject/DefinitionScope.java @@ -3,76 +3,74 @@ import static com.google.common.base.Preconditions.checkState; -import java.io.File; -import java.util.LinkedHashMap; -import java.util.Map; - import com.google.common.collect.Maps; import com.google.inject.Key; import com.google.inject.OutOfScopeException; import com.google.inject.Provider; import com.google.inject.Scope; import com.google.inject.Scopes; +import java.io.File; +import java.util.LinkedHashMap; +import java.util.Map; public class DefinitionScope implements Scope { - private final InheritableThreadLocal currentDefinitionId = new InheritableThreadLocal<>(); + private final InheritableThreadLocal currentDefinitionId = new InheritableThreadLocal<>(); - private final LinkedHashMap, Object>> values = new LinkedHashMap, Object>>() { + private final LinkedHashMap, Object>> values = + new LinkedHashMap, Object>>() { protected boolean removeEldestEntry(Map.Entry, Object>> eldest) { - return size() > Runtime.getRuntime().availableProcessors() * 2; + return size() > Runtime.getRuntime().availableProcessors() * 2; } - }; - - public void enter(File definitionId) { - checkState(currentDefinitionId.get() == null, "A scoping block is already in progress"); - currentDefinitionId.set(definitionId); - } + }; - public void exit() { - checkState(currentDefinitionId.get() != null, "No scoping block in progress"); - currentDefinitionId.remove(); - } + public void enter(File definitionId) { + checkState(currentDefinitionId.get() == null, "A scoping block is already in progress"); + currentDefinitionId.set(definitionId); + } - @Override - public Provider scope(Key key, Provider unscoped) { - return new Provider() { - public T get() { - Map, Object> scopedObjects = getScopedObjectMap(key); + public void exit() { + checkState(currentDefinitionId.get() != null, "No scoping block in progress"); + currentDefinitionId.remove(); + } - synchronized(scopedObjects) { - @SuppressWarnings("unchecked") - T current = (T) scopedObjects.get(key); - if (current == null && !scopedObjects.containsKey(key)) { - current = unscoped.get(); + @Override + public Provider scope(Key key, Provider unscoped) { + return new Provider() { + public T get() { + Map, Object> scopedObjects = getScopedObjectMap(key); - // don't remember proxies; these exist only to serve circular dependencies - if (Scopes.isCircularProxy(current)) { - return current; - } + synchronized (scopedObjects) { + @SuppressWarnings("unchecked") + T current = (T) scopedObjects.get(key); + if (current == null && !scopedObjects.containsKey(key)) { + current = unscoped.get(); - scopedObjects.put(key, current); - } - return current; - } + // don't remember proxies; these exist only to serve circular dependencies + if (Scopes.isCircularProxy(current)) { + return current; } - }; - } - private Map, Object> getScopedObjectMap(Key key) { - File definitionId = currentDefinitionId.get(); - if (definitionId == null) { - throw new OutOfScopeException("Cannot access " + key - + " outside of a scoping block"); - } - synchronized(values) { - Map, Object> scopedObjects = values.get(definitionId); - if (scopedObjects == null) { - scopedObjects = Maps.newHashMap(); - values.put(definitionId, scopedObjects); - } - return scopedObjects; + scopedObjects.put(key, current); + } + return current; } } + }; + } + private Map, Object> getScopedObjectMap(Key key) { + File definitionId = currentDefinitionId.get(); + if (definitionId == null) { + throw new OutOfScopeException("Cannot access " + key + " outside of a scoping block"); + } + synchronized (values) { + Map, Object> scopedObjects = values.get(definitionId); + if (scopedObjects == null) { + scopedObjects = Maps.newHashMap(); + values.put(definitionId, scopedObjects); + } + return scopedObjects; + } + } } diff --git a/kernel/src/main/java/org/kframework/utils/inject/DefinitionScoped.java b/kernel/src/main/java/org/kframework/utils/inject/DefinitionScoped.java index f2ac29226d1..459a301a660 100644 --- a/kernel/src/main/java/org/kframework/utils/inject/DefinitionScoped.java +++ b/kernel/src/main/java/org/kframework/utils/inject/DefinitionScoped.java @@ -3,14 +3,13 @@ import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.TYPE; - -import java.lang.annotation.Retention; - import static java.lang.annotation.RetentionPolicy.RUNTIME; -import java.lang.annotation.Target; - import com.google.inject.ScopeAnnotation; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; -@Target({ TYPE, METHOD }) @Retention(RUNTIME) @ScopeAnnotation +@Target({TYPE, METHOD}) +@Retention(RUNTIME) +@ScopeAnnotation public @interface DefinitionScoped {} diff --git a/kernel/src/main/java/org/kframework/utils/inject/GraphInjector.java b/kernel/src/main/java/org/kframework/utils/inject/GraphInjector.java index b1f6b8383d4..a31dff0f33d 100644 --- a/kernel/src/main/java/org/kframework/utils/inject/GraphInjector.java +++ b/kernel/src/main/java/org/kframework/utils/inject/GraphInjector.java @@ -1,34 +1,32 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils.inject; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.grapher.graphviz.GraphvizGrapher; +import com.google.inject.grapher.graphviz.GraphvizModule; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.Arrays; - -import com.google.inject.Guice; -import com.google.inject.Injector; -import com.google.inject.grapher.graphviz.GraphvizGrapher; -import com.google.inject.grapher.graphviz.GraphvizModule; - import org.kframework.main.Main; public class GraphInjector { - public static void main(String[] args) throws IOException { - String[] args2 = Arrays.copyOfRange(args, 1, args.length); - Injector injector = Main.getInjector(args[0]); - graph("injector.dot", injector); - } + public static void main(String[] args) throws IOException { + String[] args2 = Arrays.copyOfRange(args, 1, args.length); + Injector injector = Main.getInjector(args[0]); + graph("injector.dot", injector); + } - private static void graph(String filename, Injector demoInjector) throws IOException { - PrintWriter out = new PrintWriter(new File(filename), StandardCharsets.UTF_8); + private static void graph(String filename, Injector demoInjector) throws IOException { + PrintWriter out = new PrintWriter(new File(filename), StandardCharsets.UTF_8); - Injector injector = Guice.createInjector(new GraphvizModule()); - GraphvizGrapher grapher = injector.getInstance(GraphvizGrapher.class); - grapher.setOut(out); - grapher.setRankdir("TB"); - grapher.graph(demoInjector); - } + Injector injector = Guice.createInjector(new GraphvizModule()); + GraphvizGrapher grapher = injector.getInstance(GraphvizGrapher.class); + grapher.setOut(out); + grapher.setRankdir("TB"); + grapher.graph(demoInjector); + } } diff --git a/kernel/src/main/java/org/kframework/utils/inject/JCommanderModule.java b/kernel/src/main/java/org/kframework/utils/inject/JCommanderModule.java index a8d4693ddc1..9d4b7978b4d 100644 --- a/kernel/src/main/java/org/kframework/utils/inject/JCommanderModule.java +++ b/kernel/src/main/java/org/kframework/utils/inject/JCommanderModule.java @@ -6,76 +6,86 @@ import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; +import com.beust.jcommander.JCommander; +import com.beust.jcommander.ParameterException; +import com.beust.jcommander.internal.Console; +import com.google.inject.AbstractModule; +import com.google.inject.BindingAnnotation; +import com.google.inject.Provides; import java.lang.annotation.Retention; import java.lang.annotation.Target; import java.util.Set; - import org.kframework.kompile.KompileUsageFormatter; import org.kframework.main.Tool; import org.kframework.utils.Stopwatch; -import org.kframework.utils.StringUtil; import org.kframework.utils.errorsystem.KEMException; import org.kframework.utils.errorsystem.KExceptionManager; -import com.beust.jcommander.internal.Console; -import com.beust.jcommander.JCommander; -import com.beust.jcommander.ParameterException; -import com.google.inject.AbstractModule; -import com.google.inject.BindingAnnotation; -import com.google.inject.Provides; +public class JCommanderModule extends AbstractModule { -public class JCommanderModule extends AbstractModule { + @BindingAnnotation + @Target({FIELD, PARAMETER, METHOD}) + @Retention(RUNTIME) + public @interface Usage {} - @BindingAnnotation @Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME) - public @interface Usage {} + @Override + protected void configure() { + binder().requireAtInjectOnConstructors(); + bind(String[].class) + .annotatedWith(Options.class) + .toProvider(SimpleScope.seededKeyProvider()) + .in(RequestScoped.class); + } - @Override - protected void configure() { - binder().requireAtInjectOnConstructors(); - bind(String[].class).annotatedWith(Options.class) - .toProvider(SimpleScope.seededKeyProvider()).in(RequestScoped.class); + @Provides + @RequestScoped + JCommander jcommander( + @Options String[] args, + Tool tool, + @Options Set options, + KExceptionManager kem, + Stopwatch sw) { + try { + JCommander jc = new JCommander(options.toArray(new Object[options.size()]), args); + jc.setProgramName(tool.name().toLowerCase()); + jc.setUsageFormatter(new KompileUsageFormatter(jc)); + sw.printIntermediate("Parse command line options"); + return jc; + } catch (ParameterException e) { + throw KEMException.criticalError(e.getMessage()); } + } - @Provides @RequestScoped - JCommander jcommander(@Options String[] args, Tool tool, @Options Set options, KExceptionManager kem, Stopwatch sw) { - try { - JCommander jc = new JCommander(options.toArray(new Object[options.size()]), args); - jc.setProgramName(tool.name().toLowerCase()); - jc.setUsageFormatter(new KompileUsageFormatter(jc)); - sw.printIntermediate("Parse command line options"); - return jc; - } catch (ParameterException e) { - throw KEMException.criticalError(e.getMessage()); - } - } - - void usage(JCommander jc, StringBuilder sb) { - Console defaultConsole = jc.getConsole(); - jc.setConsole(new Console() { - @Override - public void print(String msg) { - sb.append(msg); - } + void usage(JCommander jc, StringBuilder sb) { + Console defaultConsole = jc.getConsole(); + jc.setConsole( + new Console() { + @Override + public void print(String msg) { + sb.append(msg); + } - @Override - public void println(String msg) { - sb.append(msg).append('\n'); - } + @Override + public void println(String msg) { + sb.append(msg).append('\n'); + } - @Override - public char[] readPassword(boolean echoInput) { - return defaultConsole.readPassword(echoInput); - } + @Override + public char[] readPassword(boolean echoInput) { + return defaultConsole.readPassword(echoInput); + } }); - jc.usage(); - jc.setConsole(defaultConsole); - } + jc.usage(); + jc.setConsole(defaultConsole); + } - @Provides @Usage @RequestScoped - String usage(JCommander jc) { - StringBuilder sb = new StringBuilder(); - usage(jc, sb); - //for some reason the usage pattern indents commands inconsistently, so we need to adjust it - return sb.toString().replaceAll(" ", " "); - } + @Provides + @Usage + @RequestScoped + String usage(JCommander jc) { + StringBuilder sb = new StringBuilder(); + usage(jc, sb); + // for some reason the usage pattern indents commands inconsistently, so we need to adjust it + return sb.toString().replaceAll(" ", " "); + } } diff --git a/kernel/src/main/java/org/kframework/utils/inject/Main.java b/kernel/src/main/java/org/kframework/utils/inject/Main.java index 493268625f7..d05b74cf5dd 100644 --- a/kernel/src/main/java/org/kframework/utils/inject/Main.java +++ b/kernel/src/main/java/org/kframework/utils/inject/Main.java @@ -1,18 +1,19 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils.inject; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import com.google.inject.BindingAnnotation; import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import com.google.inject.BindingAnnotation; - -@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME) +@BindingAnnotation +@Target({FIELD, PARAMETER, METHOD}) +@Retention(RUNTIME) public @interface Main { - Class value() default Main.class; + Class value() default Main.class; } diff --git a/kernel/src/main/java/org/kframework/utils/inject/Options.java b/kernel/src/main/java/org/kframework/utils/inject/Options.java index d61a708ed2f..21c39d72ac2 100644 --- a/kernel/src/main/java/org/kframework/utils/inject/Options.java +++ b/kernel/src/main/java/org/kframework/utils/inject/Options.java @@ -1,15 +1,16 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils.inject; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import com.google.inject.BindingAnnotation; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import com.google.inject.BindingAnnotation; - -@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME) +@BindingAnnotation +@Target({FIELD, PARAMETER, METHOD}) +@Retention(RUNTIME) public @interface Options {} diff --git a/kernel/src/main/java/org/kframework/utils/inject/OuterParsingModule.java b/kernel/src/main/java/org/kframework/utils/inject/OuterParsingModule.java index cdb86466444..d35d7618580 100644 --- a/kernel/src/main/java/org/kframework/utils/inject/OuterParsingModule.java +++ b/kernel/src/main/java/org/kframework/utils/inject/OuterParsingModule.java @@ -1,8 +1,11 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils.inject; +import static org.kframework.utils.errorsystem.KException.ExceptionType.*; + import com.google.inject.AbstractModule; import com.google.inject.Provides; +import java.io.File; import org.apache.commons.io.FilenameUtils; import org.kframework.main.GlobalOptions; import org.kframework.utils.errorsystem.KEMException; @@ -14,48 +17,60 @@ import org.kframework.utils.options.OuterParsingOptions; import org.kframework.utils.options.OutputDirectoryOptions; -import java.io.File; - -import static org.kframework.utils.errorsystem.KException.ExceptionType.*; - /** - * Provides the information needed for tools that parse definitions from source to have access to {@link FileUtil}. + * Provides the information needed for tools that parse definitions from source to have access to + * {@link FileUtil}. * - * Used currently by kompile, and kdep. + *

Used currently by kompile, and kdep. */ public class OuterParsingModule extends AbstractModule { - @Override - protected void configure() { - binder().requireAtInjectOnConstructors(); - } + @Override + protected void configure() { + binder().requireAtInjectOnConstructors(); + } - @Provides - @DefinitionDir - File definitionDir(@KompiledDir File kDir) { - return kDir.getParentFile(); - } + @Provides + @DefinitionDir + File definitionDir(@KompiledDir File kDir) { + return kDir.getParentFile(); + } - @Provides @KompiledDir - File kompiledDir(OuterParsingOptions options, @WorkingDir File workingDir, OutputDirectoryOptions output) { - if (output.outputDirectory != null) { - if (output.directory != null) - throw KEMException.criticalError("Cannot use both --directory and --output-definition at the same time."); - File f = new File(output.outputDirectory); - if (f.isAbsolute()) - return f; - return new File(workingDir, output.outputDirectory); - } else if (output.directory != null) { - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - kem.registerCompilerWarning(DEPRECATED_DIRECTORY_FLAG, "The --directory option is deprecated. Please use --output-definition instead."); - kem.print(); - File f = new File(output.directory); - if (!f.isAbsolute()) - f = new File(workingDir, output.directory).getAbsoluteFile(); + @Provides + @KompiledDir + File kompiledDir( + OuterParsingOptions options, @WorkingDir File workingDir, OutputDirectoryOptions output) { + if (output.outputDirectory != null) { + if (output.directory != null) + throw KEMException.criticalError( + "Cannot use both --directory and --output-definition at the same time."); + File f = new File(output.outputDirectory); + if (f.isAbsolute()) return f; + return new File(workingDir, output.outputDirectory); + } else if (output.directory != null) { + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + kem.registerCompilerWarning( + DEPRECATED_DIRECTORY_FLAG, + "The --directory option is deprecated. Please use --output-definition instead."); + kem.print(); + File f = new File(output.directory); + if (!f.isAbsolute()) f = new File(workingDir, output.directory).getAbsoluteFile(); - return new File(f,FilenameUtils.removeExtension(options.mainDefinitionFile(new FileUtil(null, workingDir, null, null, null)).getName()) + "-kompiled"); - } - // bootstrap the part of FileUtil we need - return new File(workingDir, FilenameUtils.removeExtension(options.mainDefinitionFile(new FileUtil(null, workingDir, null, null, null)).getName()) + "-kompiled"); + return new File( + f, + FilenameUtils.removeExtension( + options + .mainDefinitionFile(new FileUtil(null, workingDir, null, null, null)) + .getName()) + + "-kompiled"); } + // bootstrap the part of FileUtil we need + return new File( + workingDir, + FilenameUtils.removeExtension( + options + .mainDefinitionFile(new FileUtil(null, workingDir, null, null, null)) + .getName()) + + "-kompiled"); + } } diff --git a/kernel/src/main/java/org/kframework/utils/inject/RequestScoped.java b/kernel/src/main/java/org/kframework/utils/inject/RequestScoped.java index 19ebe46e5fd..fc921c7156e 100644 --- a/kernel/src/main/java/org/kframework/utils/inject/RequestScoped.java +++ b/kernel/src/main/java/org/kframework/utils/inject/RequestScoped.java @@ -3,14 +3,13 @@ import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.TYPE; - -import java.lang.annotation.Retention; - import static java.lang.annotation.RetentionPolicy.RUNTIME; -import java.lang.annotation.Target; - import com.google.inject.ScopeAnnotation; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; -@Target({ TYPE, METHOD }) @Retention(RUNTIME) @ScopeAnnotation +@Target({TYPE, METHOD}) +@Retention(RUNTIME) +@ScopeAnnotation public @interface RequestScoped {} diff --git a/kernel/src/main/java/org/kframework/utils/inject/SimpleScope.java b/kernel/src/main/java/org/kframework/utils/inject/SimpleScope.java index 7555a85ade8..359744339b2 100644 --- a/kernel/src/main/java/org/kframework/utils/inject/SimpleScope.java +++ b/kernel/src/main/java/org/kframework/utils/inject/SimpleScope.java @@ -10,12 +10,10 @@ import com.google.inject.Provider; import com.google.inject.Scope; import com.google.inject.Scopes; - import java.util.Map; /** - * Scopes a single execution of a block of code. Apply this scope with a - * try/finally block: + * Scopes a single execution of a block of code. Apply this scope with a try/finally block: * *

  * 
@@ -31,12 +29,11 @@
  * 
  * 
* - * The scope can be initialized with one or more seed values by calling - * seed(key, value) before the injector will be called upon to - * provide for this key. A typical use is for a servlet filter to enter/exit the - * scope, representing a Request Scope, and seed HttpServletRequest and - * HttpServletResponse. For each key inserted with seed(), you must include a - * corresponding binding: + * The scope can be initialized with one or more seed values by calling seed(key, value) + * before the injector will be called upon to provide for this key. A typical use is for a + * servlet filter to enter/exit the scope, representing a Request Scope, and seed HttpServletRequest + * and HttpServletResponse. For each key inserted with seed(), you must include a corresponding + * binding: * *
  * 
@@ -51,84 +48,85 @@
  */
 public class SimpleScope implements Scope {
 
-    private static final Provider SEEDED_KEY_PROVIDER = new Provider() {
+  private static final Provider SEEDED_KEY_PROVIDER =
+      new Provider() {
         public Object get() {
-            throw new IllegalStateException(
-                    "If you got here then it means that"
-                            + " your code asked for scoped object which should have been"
-                            + " explicitly seeded in this scope by calling"
-                            + " SimpleScope.seed(), but was not.");
+          throw new IllegalStateException(
+              "If you got here then it means that"
+                  + " your code asked for scoped object which should have been"
+                  + " explicitly seeded in this scope by calling"
+                  + " SimpleScope.seed(), but was not.");
         }
-    };
-    private final InheritableThreadLocal, Object>> values = new InheritableThreadLocal, Object>>();
+      };
+  private final InheritableThreadLocal, Object>> values =
+      new InheritableThreadLocal, Object>>();
 
-    public void enter() {
-        checkState(values.get() == null,
-                "A scoping block is already in progress");
-        values.set(Maps.newHashMap());
-    }
+  public void enter() {
+    checkState(values.get() == null, "A scoping block is already in progress");
+    values.set(Maps.newHashMap());
+  }
 
-    public void exit() {
-        checkState(values.get() != null, "No scoping block in progress");
-        values.remove();
-    }
+  public void exit() {
+    checkState(values.get() != null, "No scoping block in progress");
+    values.remove();
+  }
 
-    public  void seed(Key key, T value) {
-        Map, Object> scopedObjects = getScopedObjectMap(key);
-        checkState(
-                !scopedObjects.containsKey(key),
-                "A value for the key %s was "
-                        + "already seeded in this scope. Old value: %s New value: %s",
-                key, scopedObjects.get(key), value);
-        scopedObjects.put(key, value);
-    }
+  public  void seed(Key key, T value) {
+    Map, Object> scopedObjects = getScopedObjectMap(key);
+    checkState(
+        !scopedObjects.containsKey(key),
+        "A value for the key %s was " + "already seeded in this scope. Old value: %s New value: %s",
+        key,
+        scopedObjects.get(key),
+        value);
+    scopedObjects.put(key, value);
+  }
 
-    public  void seed(Class clazz, T value) {
-        seed(Key.get(clazz), value);
-    }
+  public  void seed(Class clazz, T value) {
+    seed(Key.get(clazz), value);
+  }
 
-    public  Provider scope(final Key key, final Provider unscoped) {
-        return new Provider() {
-            public T get() {
-                Map, Object> scopedObjects = getScopedObjectMap(key);
-
-                synchronized (scopedObjects) {
-                    @SuppressWarnings("unchecked")
-                    T current = (T) scopedObjects.get(key);
-                    if (current == null && !scopedObjects.containsKey(key)) {
-                        current = unscoped.get();
+  public  Provider scope(final Key key, final Provider unscoped) {
+    return new Provider() {
+      public T get() {
+        Map, Object> scopedObjects = getScopedObjectMap(key);
 
-                        // don't remember proxies; these exist only to serve
-                        // circular dependencies
-                        if (Scopes.isCircularProxy(current)) {
-                            return current;
-                        }
+        synchronized (scopedObjects) {
+          @SuppressWarnings("unchecked")
+          T current = (T) scopedObjects.get(key);
+          if (current == null && !scopedObjects.containsKey(key)) {
+            current = unscoped.get();
 
-                        scopedObjects.put(key, current);
-                    }
-                    return current;
-                }
+            // don't remember proxies; these exist only to serve
+            // circular dependencies
+            if (Scopes.isCircularProxy(current)) {
+              return current;
             }
-        };
-    }
 
-    private  Map, Object> getScopedObjectMap(Key key) {
-        Map, Object> scopedObjects = values.get();
-        if (scopedObjects == null) {
-            throw new OutOfScopeException("Cannot access " + key
-                    + " outside of a scoping block");
+            scopedObjects.put(key, current);
+          }
+          return current;
         }
-        return scopedObjects;
-    }
+      }
+    };
+  }
 
-    /**
-     * Returns a provider that always throws exception complaining that the
-     * object in question must be seeded before it can be injected.
-     *
-     * @return typed provider
-     */
-    @SuppressWarnings({ "unchecked" })
-    public static  Provider seededKeyProvider() {
-        return (Provider) SEEDED_KEY_PROVIDER;
+  private  Map, Object> getScopedObjectMap(Key key) {
+    Map, Object> scopedObjects = values.get();
+    if (scopedObjects == null) {
+      throw new OutOfScopeException("Cannot access " + key + " outside of a scoping block");
     }
+    return scopedObjects;
+  }
+
+  /**
+   * Returns a provider that always throws exception complaining that the object in question must be
+   * seeded before it can be injected.
+   *
+   * @return typed provider
+   */
+  @SuppressWarnings({"unchecked"})
+  public static  Provider seededKeyProvider() {
+    return (Provider) SEEDED_KEY_PROVIDER;
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/utils/inject/Spec1.java b/kernel/src/main/java/org/kframework/utils/inject/Spec1.java
index 540294df69a..0ac4bff41fa 100644
--- a/kernel/src/main/java/org/kframework/utils/inject/Spec1.java
+++ b/kernel/src/main/java/org/kframework/utils/inject/Spec1.java
@@ -1,16 +1,17 @@
 // Copyright (c) K Team. All Rights Reserved.
 package org.kframework.utils.inject;
 
-import com.google.inject.BindingAnnotation;
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
 
+import com.google.inject.BindingAnnotation;
 import java.lang.annotation.Annotation;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.*;
-
-@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME)
+@BindingAnnotation
+@Target({FIELD, PARAMETER, METHOD})
+@Retention(RUNTIME)
 public @interface Spec1 {
-    Class value() default Spec1.class;
+  Class value() default Spec1.class;
 }
diff --git a/kernel/src/main/java/org/kframework/utils/inject/Spec2.java b/kernel/src/main/java/org/kframework/utils/inject/Spec2.java
index 0f87ff7cc4b..044d8843789 100644
--- a/kernel/src/main/java/org/kframework/utils/inject/Spec2.java
+++ b/kernel/src/main/java/org/kframework/utils/inject/Spec2.java
@@ -1,16 +1,17 @@
 // Copyright (c) K Team. All Rights Reserved.
 package org.kframework.utils.inject;
 
-import com.google.inject.BindingAnnotation;
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
 
+import com.google.inject.BindingAnnotation;
 import java.lang.annotation.Annotation;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.*;
-
-@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME)
+@BindingAnnotation
+@Target({FIELD, PARAMETER, METHOD})
+@Retention(RUNTIME)
 public @interface Spec2 {
-    Class value() default Spec2.class;
+  Class value() default Spec2.class;
 }
diff --git a/kernel/src/main/java/org/kframework/utils/inject/StartTime.java b/kernel/src/main/java/org/kframework/utils/inject/StartTime.java
index 937fdb0ac28..d140e00f271 100644
--- a/kernel/src/main/java/org/kframework/utils/inject/StartTime.java
+++ b/kernel/src/main/java/org/kframework/utils/inject/StartTime.java
@@ -1,17 +1,17 @@
 // Copyright (c) K Team. All Rights Reserved.
 package org.kframework.utils.inject;
 
-import com.google.inject.BindingAnnotation;
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
 
+import com.google.inject.BindingAnnotation;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.*;
-
 /**
- * @author Denis Bogdanas
- * Created on 20-Nov-19.
+ * @author Denis Bogdanas Created on 20-Nov-19.
  */
-@BindingAnnotation @Target({FIELD, PARAMETER, METHOD}) @Retention(RUNTIME)
+@BindingAnnotation
+@Target({FIELD, PARAMETER, METHOD})
+@Retention(RUNTIME)
 public @interface StartTime {}
diff --git a/kernel/src/main/java/org/kframework/utils/options/BackendOptions.java b/kernel/src/main/java/org/kframework/utils/options/BackendOptions.java
index 14c57e748d1..ef3ff409909 100644
--- a/kernel/src/main/java/org/kframework/utils/options/BackendOptions.java
+++ b/kernel/src/main/java/org/kframework/utils/options/BackendOptions.java
@@ -2,14 +2,15 @@
 package org.kframework.utils.options;
 
 import com.beust.jcommander.Parameter;
-import com.google.inject.Inject;
-
 import java.io.Serializable;
 
 public class BackendOptions implements Serializable {
 
-    public BackendOptions() {}
+  public BackendOptions() {}
 
-    @Parameter(names="--dry-run", description="Compile program into KORE format but do not run. Only used in Haskell and LLVM backend.")
-    public boolean dryRun = false;
+  @Parameter(
+      names = "--dry-run",
+      description =
+          "Compile program into KORE format but do not run. Only used in Haskell and LLVM backend.")
+  public boolean dryRun = false;
 }
diff --git a/kernel/src/main/java/org/kframework/utils/options/BaseEnumConverter.java b/kernel/src/main/java/org/kframework/utils/options/BaseEnumConverter.java
index c2407f6a6ed..b11ee946c7a 100644
--- a/kernel/src/main/java/org/kframework/utils/options/BaseEnumConverter.java
+++ b/kernel/src/main/java/org/kframework/utils/options/BaseEnumConverter.java
@@ -1,44 +1,42 @@
 // Copyright (c) K Team. All Rights Reserved.
 package org.kframework.utils.options;
 
+import com.beust.jcommander.ParameterException;
+import com.beust.jcommander.converters.BaseConverter;
 import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.Set;
 
-import com.beust.jcommander.ParameterException;
-import com.beust.jcommander.converters.BaseConverter;
-
 /**
- * Helper class used to abstract functionality of converting enums in JCommander.
- * We use this because JCommander has bugs and feature issues regarding casing and hyphens
- * in enum names.
- * @author dwightguth
+ * Helper class used to abstract functionality of converting enums in JCommander. We use this
+ * because JCommander has bugs and feature issues regarding casing and hyphens in enum names.
  *
+ * @author dwightguth
  */
 public abstract class BaseEnumConverter> extends BaseConverter {
 
-    public BaseEnumConverter(String optionName) {
-        super(optionName);
-    }
+  public BaseEnumConverter(String optionName) {
+    super(optionName);
+  }
 
-    @Override
-    public T convert(String arg) {
-        try {
-            return Enum.valueOf(enumClass(), arg.toUpperCase().replaceAll("-", "_"));
-        } catch (IllegalArgumentException e) {
-            EnumSet values = EnumSet.allOf(enumClass());
-            Set validValues = new HashSet<>();
-            for (T value : values) {
-                validValues.add(friendlyName(value));
-            }
-            throw new ParameterException("Invalid value for " + getOptionName() + " parameter. Allowed values:" +
-                    validValues);
-        }
+  @Override
+  public T convert(String arg) {
+    try {
+      return Enum.valueOf(enumClass(), arg.toUpperCase().replaceAll("-", "_"));
+    } catch (IllegalArgumentException e) {
+      EnumSet values = EnumSet.allOf(enumClass());
+      Set validValues = new HashSet<>();
+      for (T value : values) {
+        validValues.add(friendlyName(value));
+      }
+      throw new ParameterException(
+          "Invalid value for " + getOptionName() + " parameter. Allowed values:" + validValues);
     }
+  }
 
-    public abstract Class enumClass();
+  public abstract Class enumClass();
 
-    public static String friendlyName(Enum arg) {
-        return arg.name().toLowerCase().replaceAll("_", "-");
-    }
+  public static String friendlyName(Enum arg) {
+    return arg.name().toLowerCase().replaceAll("_", "-");
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/utils/options/DefinitionLoadingOptions.java b/kernel/src/main/java/org/kframework/utils/options/DefinitionLoadingOptions.java
index a4d1c9c4f8b..2f43e31aa4a 100644
--- a/kernel/src/main/java/org/kframework/utils/options/DefinitionLoadingOptions.java
+++ b/kernel/src/main/java/org/kframework/utils/options/DefinitionLoadingOptions.java
@@ -2,21 +2,28 @@
 package org.kframework.utils.options;
 
 import com.beust.jcommander.Parameter;
-import com.google.inject.Inject;
 
 public class DefinitionLoadingOptions {
 
-    public DefinitionLoadingOptions() {}
+  public DefinitionLoadingOptions() {}
 
-    @Parameter(names={"--directory", "-d"}, description="[DEPRECATED] Path to the directory in which the kompiled " +
-            "K definition resides. The default is the unique, only directory with the suffix '-kompiled' " +
-            "in the current directory.", descriptionKey = "path", hidden = true)
-    public String directory;
+  @Parameter(
+      names = {"--directory", "-d"},
+      description =
+          "[DEPRECATED] Path to the directory in which the kompiled K definition resides. The"
+              + " default is the unique, only directory with the suffix '-kompiled' in the current"
+              + " directory.",
+      descriptionKey = "path",
+      hidden = true)
+  public String directory;
 
-    @Parameter(names={"--definition"}, description="Exact path to the kompiled directory.", descriptionKey = "path")
-    public String inputDirectory;
+  @Parameter(
+      names = {"--definition"},
+      description = "Exact path to the kompiled directory.",
+      descriptionKey = "path")
+  public String inputDirectory;
 
-    public DefinitionLoadingOptions(String dir) {
-        this.directory = dir;
-    }
+  public DefinitionLoadingOptions(String dir) {
+    this.directory = dir;
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/utils/options/DurationConverter.java b/kernel/src/main/java/org/kframework/utils/options/DurationConverter.java
index 343a224ec16..7a70c12f621 100644
--- a/kernel/src/main/java/org/kframework/utils/options/DurationConverter.java
+++ b/kernel/src/main/java/org/kframework/utils/options/DurationConverter.java
@@ -3,7 +3,6 @@
 
 import com.beust.jcommander.IStringConverter;
 import com.beust.jcommander.ParameterException;
-
 import java.time.Duration;
 
 /**
@@ -13,30 +12,32 @@
  */
 public class DurationConverter implements IStringConverter {
 
-    @Override
-    public Duration convert(String value) {
-        int num;
-        String unit;
-        try {
-            //kudos to https://stackoverflow.com/a/3552805/4182868
-            String numPart = value.split("[a-z]")[0];
-            num = Integer.parseInt(numPart);
-            unit = value.substring(numPart.length());
-        } catch (ArrayIndexOutOfBoundsException | NumberFormatException e) {
-            throw durationException(value);
-        }
-        return switch (unit) {
-            case "ms" -> Duration.ofMillis(num);
-            case "s" -> Duration.ofSeconds(num);
-            case "m" -> Duration.ofMinutes(num);
-            case "h" -> Duration.ofHours(num);
-            case "d" -> Duration.ofDays(num);
-            default -> throw durationException(value);
-        };
+  @Override
+  public Duration convert(String value) {
+    int num;
+    String unit;
+    try {
+      // kudos to https://stackoverflow.com/a/3552805/4182868
+      String numPart = value.split("[a-z]")[0];
+      num = Integer.parseInt(numPart);
+      unit = value.substring(numPart.length());
+    } catch (ArrayIndexOutOfBoundsException | NumberFormatException e) {
+      throw durationException(value);
     }
+    return switch (unit) {
+      case "ms" -> Duration.ofMillis(num);
+      case "s" -> Duration.ofSeconds(num);
+      case "m" -> Duration.ofMinutes(num);
+      case "h" -> Duration.ofHours(num);
+      case "d" -> Duration.ofDays(num);
+      default -> throw durationException(value);
+    };
+  }
 
-    private ParameterException durationException(String value) {
-        return new ParameterException(String.format(
-                "Invalid value for duration '%s', valid value examples: 10ms, 10s, 10m, 10h or 10d", value));
-    }
+  private ParameterException durationException(String value) {
+    return new ParameterException(
+        String.format(
+            "Invalid value for duration '%s', valid value examples: 10ms, 10s, 10m, 10h or 10d",
+            value));
+  }
 }
diff --git a/kernel/src/main/java/org/kframework/utils/options/EnumSetConverter.java b/kernel/src/main/java/org/kframework/utils/options/EnumSetConverter.java
index 42a309496d4..d5a464fdcc2 100644
--- a/kernel/src/main/java/org/kframework/utils/options/EnumSetConverter.java
+++ b/kernel/src/main/java/org/kframework/utils/options/EnumSetConverter.java
@@ -1,31 +1,30 @@
 // Copyright (c) K Team. All Rights Reserved.
 package org.kframework.utils.options;
 
+import com.beust.jcommander.converters.BaseConverter;
 import java.util.EnumSet;
 import java.util.List;
 import java.util.Set;
 
-import com.beust.jcommander.converters.BaseConverter;
+public abstract class EnumSetConverter, C extends BaseEnumConverter>
+    extends BaseConverter> {
 
-public abstract class EnumSetConverter, C extends BaseEnumConverter> extends BaseConverter>{
+  public EnumSetConverter(String optionName) {
+    super(optionName);
+  }
 
-    public EnumSetConverter(String optionName) {
-        super(optionName);
-    }
+  private final StringListConverter converter = new StringListConverter();
 
-    private final StringListConverter converter = new StringListConverter();
-
-    @Override
-    public Set convert(String val) {
-        List values = converter.convert(val);
-        Set set = EnumSet.noneOf(enumConverter().enumClass());
-        for (String value : values) {
-            T t = enumConverter().convert(value);
-            set.add(t);
-        }
-        return set;
+  @Override
+  public Set convert(String val) {
+    List values = converter.convert(val);
+    Set set = EnumSet.noneOf(enumConverter().enumClass());
+    for (String value : values) {
+      T t = enumConverter().convert(value);
+      set.add(t);
     }
+    return set;
+  }
 
-    public abstract C enumConverter();
-
+  public abstract C enumConverter();
 }
diff --git a/kernel/src/main/java/org/kframework/utils/options/InnerParsingOptions.java b/kernel/src/main/java/org/kframework/utils/options/InnerParsingOptions.java
index 82f98421bc4..19dda052663 100644
--- a/kernel/src/main/java/org/kframework/utils/options/InnerParsingOptions.java
+++ b/kernel/src/main/java/org/kframework/utils/options/InnerParsingOptions.java
@@ -3,21 +3,24 @@
 
 import com.beust.jcommander.Parameter;
 import com.google.inject.Inject;
-
 import java.io.Serializable;
 
 /**
  * Provides the options needed for tools to perform inner parsing of definitions from source.
  *
- * Used currently by kompile, and kprove.
+ * 

Used currently by kompile, and kprove. */ public class InnerParsingOptions implements Serializable { - public InnerParsingOptions() {} + public InnerParsingOptions() {} - @Inject - public InnerParsingOptions(Void v) {} + @Inject + public InnerParsingOptions(Void v) {} - @Parameter(names="--profile-rule-parsing", description="Store in this file time taken in ms to parse each rule in the semantics.", descriptionKey = "file", hidden = true) - public String profileRules; + @Parameter( + names = "--profile-rule-parsing", + description = "Store in this file time taken in ms to parse each rule in the semantics.", + descriptionKey = "file", + hidden = true) + public String profileRules; } diff --git a/kernel/src/main/java/org/kframework/utils/options/OnOffConverter.java b/kernel/src/main/java/org/kframework/utils/options/OnOffConverter.java index 4e4f9f78ffa..1f8a5ec51d8 100644 --- a/kernel/src/main/java/org/kframework/utils/options/OnOffConverter.java +++ b/kernel/src/main/java/org/kframework/utils/options/OnOffConverter.java @@ -6,18 +6,19 @@ public class OnOffConverter extends BaseConverter { - public OnOffConverter(String optionName) { - super(optionName); - } + public OnOffConverter(String optionName) { + super(optionName); + } - @Override - public Boolean convert(String arg0) { - if (arg0.equals("on")) { - return true; - } else if (arg0.equals("off")) { - return false; - } else { - throw new ParameterException("\"" + getOptionName() + "\": must be either \"on\" or \"off\"."); - } + @Override + public Boolean convert(String arg0) { + if (arg0.equals("on")) { + return true; + } else if (arg0.equals("off")) { + return false; + } else { + throw new ParameterException( + "\"" + getOptionName() + "\": must be either \"on\" or \"off\"."); } + } } diff --git a/kernel/src/main/java/org/kframework/utils/options/OuterParsingOptions.java b/kernel/src/main/java/org/kframework/utils/options/OuterParsingOptions.java index f5f5b4c6b25..8600c2fa734 100644 --- a/kernel/src/main/java/org/kframework/utils/options/OuterParsingOptions.java +++ b/kernel/src/main/java/org/kframework/utils/options/OuterParsingOptions.java @@ -3,52 +3,62 @@ import com.beust.jcommander.Parameter; import com.google.inject.Inject; -import org.kframework.utils.errorsystem.KEMException; -import org.kframework.utils.file.FileUtil; - import java.io.File; import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import org.kframework.utils.errorsystem.KEMException; +import org.kframework.utils.file.FileUtil; /** * Provides the options needed for tools to perform outer parsing of definitions from source. * - * Used currently by kompile, and kdep. + *

Used currently by kompile, and kdep. */ public class OuterParsingOptions implements Serializable { - public OuterParsingOptions() {} + public OuterParsingOptions() {} - @Inject - public OuterParsingOptions(Void v) {} + @Inject + public OuterParsingOptions(Void v) {} - public OuterParsingOptions(File mainDefinitionFile) { - this.mainDefinitionFile = mainDefinitionFile; - } + public OuterParsingOptions(File mainDefinitionFile) { + this.mainDefinitionFile = mainDefinitionFile; + } - @Parameter(description="") - private String mainParameter; + @Parameter(description = "") + private String mainParameter; - private File mainDefinitionFile; + private File mainDefinitionFile; - public synchronized File mainDefinitionFile(FileUtil files) { - if (mainDefinitionFile == null) { - if (mainParameter == null) { - throw KEMException.criticalError("You have to provide exactly one main file in order to do outer parsing."); - } - mainDefinitionFile = files.resolveWorkingDirectory(mainParameter); - } - return mainDefinitionFile; + public synchronized File mainDefinitionFile(FileUtil files) { + if (mainDefinitionFile == null) { + if (mainParameter == null) { + throw KEMException.criticalError( + "You have to provide exactly one main file in order to do outer parsing."); + } + mainDefinitionFile = files.resolveWorkingDirectory(mainParameter); } + return mainDefinitionFile; + } + @Parameter( + names = "-I", + description = "Add a directory to the search path for requires statements.", + descriptionKey = "path") + public List includes = new ArrayList<>(); - @Parameter(names="-I", description="Add a directory to the search path for requires statements.", descriptionKey = "path") - public List includes = new ArrayList<>(); - - @Parameter(names="--no-prelude", description="Do not implicitly require prelude.md.", hidden = true) - public boolean noPrelude = false; + @Parameter( + names = "--no-prelude", + description = "Do not implicitly require prelude.md.", + hidden = true) + public boolean noPrelude = false; - @Parameter(names="--md-selector", description="Preprocessor: for .md files, select only the md code blocks that match the selector expression. Ex:'k&(!a|b)'.", descriptionKey = "expression") - public String mdSelector = "k"; + @Parameter( + names = "--md-selector", + description = + "Preprocessor: for .md files, select only the md code blocks that match the selector" + + " expression. Ex:'k&(!a|b)'.", + descriptionKey = "expression") + public String mdSelector = "k"; } diff --git a/kernel/src/main/java/org/kframework/utils/options/OutputDirectoryOptions.java b/kernel/src/main/java/org/kframework/utils/options/OutputDirectoryOptions.java index 6d2fb6bc82b..c13ca4e47fd 100644 --- a/kernel/src/main/java/org/kframework/utils/options/OutputDirectoryOptions.java +++ b/kernel/src/main/java/org/kframework/utils/options/OutputDirectoryOptions.java @@ -3,23 +3,28 @@ import com.beust.jcommander.Parameter; import com.google.inject.Inject; - import java.io.Serializable; public class OutputDirectoryOptions implements Serializable { - public OutputDirectoryOptions() {} + public OutputDirectoryOptions() {} - @Inject - public OutputDirectoryOptions(Void v) {} + @Inject + public OutputDirectoryOptions(Void v) {} - @Parameter(names={"--directory", "-d"}, - description="[DEPRECATED] Path to the directory in which the output resides. An output can be either a " + - "kompiled K definition or a document which depends on the type of backend. The default is the " + - "directory containing the main definition file.", descriptionKey = "path", hidden = true) - public String directory; + @Parameter( + names = {"--directory", "-d"}, + description = + "[DEPRECATED] Path to the directory in which the output resides. An output can be either" + + " a kompiled K definition or a document which depends on the type of backend. The" + + " default is the directory containing the main definition file.", + descriptionKey = "path", + hidden = true) + public String directory; - @Parameter(names={"--output-definition", "-o"}, - description="Path to the exact directory in which the output resides.", descriptionKey = "path") - public String outputDirectory; + @Parameter( + names = {"--output-definition", "-o"}, + description = "Path to the exact directory in which the output resides.", + descriptionKey = "path") + public String outputDirectory; } diff --git a/kernel/src/main/java/org/kframework/utils/options/SMTOptions.java b/kernel/src/main/java/org/kframework/utils/options/SMTOptions.java index 9fd7cc13a32..fea26d8858e 100644 --- a/kernel/src/main/java/org/kframework/utils/options/SMTOptions.java +++ b/kernel/src/main/java/org/kframework/utils/options/SMTOptions.java @@ -2,53 +2,76 @@ package org.kframework.utils.options; import com.beust.jcommander.Parameter; -import com.google.inject.Inject; - import java.io.Serializable; public class SMTOptions implements Serializable { - public SMTOptions() {} + public SMTOptions() {} - @Parameter(names="--smt", converter=SMTSolverConverter.class, description="SMT solver to use for checking constraints. is one of [z3|none].", descriptionKey = "executable", hidden = true) - public SMTSolver smt = SMTSolver.Z3; + @Parameter( + names = "--smt", + converter = SMTSolverConverter.class, + description = "SMT solver to use for checking constraints. is one of [z3|none].", + descriptionKey = "executable", + hidden = true) + public SMTSolver smt = SMTSolver.Z3; - public static class SMTSolverConverter extends BaseEnumConverter { + public static class SMTSolverConverter extends BaseEnumConverter { - public SMTSolverConverter(String optionName) { - super(optionName); - } + public SMTSolverConverter(String optionName) { + super(optionName); + } - @Override - public Class enumClass() { - return SMTSolver.class; - } + @Override + public Class enumClass() { + return SMTSolver.class; } + } - @Parameter(names="--ignore-missing-smtlib-warning", - description="Suppress warning when SMTLib translation fails.", hidden = true) - public boolean ignoreMissingSMTLibWarning = false; + @Parameter( + names = "--ignore-missing-smtlib-warning", + description = "Suppress warning when SMTLib translation fails.", + hidden = true) + public boolean ignoreMissingSMTLibWarning = false; - @Parameter(names="--floats-as-po", - description="Abstracts floating-point values as a partial order relation.", hidden = true) - public boolean floatsAsPO = false; + @Parameter( + names = "--floats-as-po", + description = "Abstracts floating-point values as a partial order relation.", + hidden = true) + public boolean floatsAsPO = false; - @Parameter(names="--maps-as-int-array", description="Abstracts map values as an array of ints.", hidden = true) - public boolean mapAsIntArray = false; + @Parameter( + names = "--maps-as-int-array", + description = "Abstracts map values as an array of ints.", + hidden = true) + public boolean mapAsIntArray = false; - @Parameter(names={"--smt-prelude", "--smt_prelude"}, - description="Path to the SMT prelude file.", descriptionKey = "path", hidden = true) - public String smtPrelude; + @Parameter( + names = {"--smt-prelude", "--smt_prelude"}, + description = "Path to the SMT prelude file.", + descriptionKey = "path", + hidden = true) + public String smtPrelude; - @Parameter(names="--smt-timeout", descriptionKey = "milliseconds", - description="Timeout for calls to the SMT solver, in milliseconds.", hidden = true) - public Integer smtTimeout = null; + @Parameter( + names = "--smt-timeout", + descriptionKey = "milliseconds", + description = "Timeout for calls to the SMT solver, in milliseconds.", + hidden = true) + public Integer smtTimeout = null; - @Parameter(names="--z3-jni", description="Invokes Z3 as JNI library. Default is external process. " + - "JNI is slightly faster, but can potentially lead to JVM crash.", hidden = true) - public boolean z3JNI = false; + @Parameter( + names = "--z3-jni", + description = + "Invokes Z3 as JNI library. Default is external process. " + + "JNI is slightly faster, but can potentially lead to JVM crash.", + hidden = true) + public boolean z3JNI = false; - @Parameter(names="--z3-tactic", descriptionKey = "solver", - description="The path to solver tactic to use to check satisfiability in Z3.", hidden = true) - public String z3Tactic; + @Parameter( + names = "--z3-tactic", + descriptionKey = "solver", + description = "The path to solver tactic to use to check satisfiability in Z3.", + hidden = true) + public String z3Tactic; } diff --git a/kernel/src/main/java/org/kframework/utils/options/SMTSolver.java b/kernel/src/main/java/org/kframework/utils/options/SMTSolver.java index 2980a32b7a0..3bdf859c5c0 100644 --- a/kernel/src/main/java/org/kframework/utils/options/SMTSolver.java +++ b/kernel/src/main/java/org/kframework/utils/options/SMTSolver.java @@ -4,18 +4,14 @@ /** * Enum storing information about which SMT solver is being used * - * TODO(dwightguth): create a proper SMT interface and put this class in that package + *

TODO(dwightguth): create a proper SMT interface and put this class in that package + * * @author dwightguth */ public enum SMTSolver { - /** - * No SMT. Interpreter may fail to reason about certain types of situations. - */ - NONE, - - /** - * The Microsoft Z3 SMT solver. - */ - Z3, + /** No SMT. Interpreter may fail to reason about certain types of situations. */ + NONE, + /** The Microsoft Z3 SMT solver. */ + Z3, } diff --git a/kernel/src/main/java/org/kframework/utils/options/StringListConverter.java b/kernel/src/main/java/org/kframework/utils/options/StringListConverter.java index 683fbac2979..02cda6c5210 100644 --- a/kernel/src/main/java/org/kframework/utils/options/StringListConverter.java +++ b/kernel/src/main/java/org/kframework/utils/options/StringListConverter.java @@ -1,36 +1,33 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils.options; +import com.beust.jcommander.IStringConverter; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import com.beust.jcommander.IStringConverter; - /** - * This class is designed to support the specification of a list of strings - * using a single string in cases where using a list of strings would be - * infeasible. For example, in krun, simulation options are passed by - * passing a set of krun options as parameter to a special krun option. - * We can't use variable arity because then --simulation --directory foo - * would be interpreted as two options instead of one with two words. - * @author dwightguth + * This class is designed to support the specification of a list of strings using a single string in + * cases where using a list of strings would be infeasible. For example, in krun, simulation options + * are passed by passing a set of krun options as parameter to a special krun option. We can't use + * variable arity because then --simulation --directory foo would be interpreted as two options + * instead of one with two words. * + * @author dwightguth */ public class StringListConverter implements IStringConverter> { - @Override - public List convert(String val) { - //split on whitespace not preceded by a backslash - String[] parts = val.split("(? result = new ArrayList<>(); - for (String part : parts) { - result.add(part.trim().replaceAll("\\\\(\\s)", "$1")); - } - return result; + @Override + public List convert(String val) { + // split on whitespace not preceded by a backslash + String[] parts = val.split("(? result = new ArrayList<>(); + for (String part : parts) { + result.add(part.trim().replaceAll("\\\\(\\s)", "$1")); + } + return result; + } } diff --git a/kernel/src/test/java/org/kframework/compile/ConstantFoldingTest.java b/kernel/src/test/java/org/kframework/compile/ConstantFoldingTest.java index 22d8697cc75..8b29224c6ea 100644 --- a/kernel/src/test/java/org/kframework/compile/ConstantFoldingTest.java +++ b/kernel/src/test/java/org/kframework/compile/ConstantFoldingTest.java @@ -1,19 +1,18 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.junit.Assert.*; +import static org.kframework.kore.KORE.*; + +import java.math.BigInteger; +import java.util.function.BiFunction; +import java.util.function.Function; import org.junit.Before; import org.junit.Test; import org.kframework.mpfr.BigFloat; import org.kframework.mpfr.BinaryMathContext; import org.kframework.utils.errorsystem.KEMException; -import java.math.BigInteger; -import java.util.function.BiFunction; -import java.util.function.Function; - -import static org.junit.Assert.*; -import static org.kframework.kore.KORE.*; - public class ConstantFoldingTest { private ConstantFolding cf; @@ -30,7 +29,8 @@ public void testNot() { assertEquals(false, cf.BOOL_not(true)); } - public void assertBinBoolOp(boolean a, boolean b, boolean c, boolean d, BiFunction f) { + public void assertBinBoolOp( + boolean a, boolean b, boolean c, boolean d, BiFunction f) { assertEquals(a, f.apply(false, false)); assertEquals(b, f.apply(false, true)); assertEquals(c, f.apply(true, false)); @@ -101,12 +101,12 @@ public void testChr() { assertEquals("\udbff\udfff", cf.STRING_chr(BigInteger.valueOf(0x10ffff))); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testChrError1() { cf.STRING_chr(BigInteger.valueOf(-1)); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testChrError2() { cf.STRING_chr(BigInteger.valueOf(0x110000)); } @@ -120,12 +120,12 @@ public void testOrd() { assertEquals(BigInteger.valueOf(0x10ffff), cf.STRING_ord("\udbff\udfff")); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testOrdError1() { cf.STRING_ord(""); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testOrdError2() { cf.STRING_ord("foo"); } @@ -143,22 +143,22 @@ public void testSubstr() { assertEquals("o", cf.STRING_substr("foo", BigInteger.valueOf(1), BigInteger.valueOf(2))); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testSubstrError1() { cf.STRING_substr("foo", BigInteger.valueOf(1), BigInteger.valueOf(4)); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testSubstrError2() { cf.STRING_substr("foo", BigInteger.valueOf(4), BigInteger.valueOf(5)); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testSubstrError3() { cf.STRING_substr("foo", BigInteger.valueOf(3), BigInteger.valueOf(1)); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testSubstError4() { cf.STRING_substr("foo", BigInteger.valueOf(-1), BigInteger.valueOf(1)); } @@ -174,12 +174,12 @@ public void testFind() { assertEquals(BigInteger.valueOf(-1), cf.STRING_find("foo", "oo", BigInteger.valueOf(2))); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testFindError1() { cf.STRING_find("foo", "f", BigInteger.valueOf(-1)); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testFindError2() { cf.STRING_find("foo", "f", BigInteger.valueOf(4)); } @@ -195,77 +195,100 @@ public void testRFind() { assertEquals(BigInteger.valueOf(1), cf.STRING_rfind("foo", "oo", BigInteger.valueOf(2))); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testRFindError1() { cf.STRING_rfind("foo", "f", BigInteger.valueOf(-1)); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testRFindError2() { cf.STRING_rfind("foo", "f", BigInteger.valueOf(4)); } @Test public void testFindChar() { - assertEquals(BigInteger.valueOf(0), cf.STRING_findChar("sandwich", "sw", BigInteger.valueOf(0))); - assertEquals(BigInteger.valueOf(4), cf.STRING_findChar("sandwich", "sw", BigInteger.valueOf(1))); - assertEquals(BigInteger.valueOf(-1), cf.STRING_findChar("sandwich", "s", BigInteger.valueOf(1))); + assertEquals( + BigInteger.valueOf(0), cf.STRING_findChar("sandwich", "sw", BigInteger.valueOf(0))); + assertEquals( + BigInteger.valueOf(4), cf.STRING_findChar("sandwich", "sw", BigInteger.valueOf(1))); + assertEquals( + BigInteger.valueOf(-1), cf.STRING_findChar("sandwich", "s", BigInteger.valueOf(1))); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testFindCharError1() { cf.STRING_findChar("foo", "f", BigInteger.valueOf(-1)); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testFindCharError2() { cf.STRING_findChar("foo", "f", BigInteger.valueOf(4)); } @Test public void testRFindChar() { - assertEquals(BigInteger.valueOf(0), cf.STRING_rfindChar("sandwich", "sw", BigInteger.valueOf(0))); - assertEquals(BigInteger.valueOf(0), cf.STRING_rfindChar("sandwich", "sw", BigInteger.valueOf(1))); - assertEquals(BigInteger.valueOf(4), cf.STRING_rfindChar("sandwich", "sw", BigInteger.valueOf(7))); - assertEquals(BigInteger.valueOf(-1), cf.STRING_rfindChar("sandwich", "w", BigInteger.valueOf(1))); + assertEquals( + BigInteger.valueOf(0), cf.STRING_rfindChar("sandwich", "sw", BigInteger.valueOf(0))); + assertEquals( + BigInteger.valueOf(0), cf.STRING_rfindChar("sandwich", "sw", BigInteger.valueOf(1))); + assertEquals( + BigInteger.valueOf(4), cf.STRING_rfindChar("sandwich", "sw", BigInteger.valueOf(7))); + assertEquals( + BigInteger.valueOf(-1), cf.STRING_rfindChar("sandwich", "w", BigInteger.valueOf(1))); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testRFindCharError1() { cf.STRING_rfindChar("foo", "f", BigInteger.valueOf(-1)); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testRFindCharError2() { cf.STRING_rfindChar("foo", "f", BigInteger.valueOf(4)); } @Test public void testFloat2String() { - assertEquals("Infinityp53x11", cf.STRING_float2string(FloatBuiltin.of(BigFloat.positiveInfinity(53), 11))); + assertEquals( + "Infinityp53x11", + cf.STRING_float2string(FloatBuiltin.of(BigFloat.positiveInfinity(53), 11))); assertEquals("NaNp53x11", cf.STRING_float2string(FloatBuiltin.of(BigFloat.NaN(53), 11))); assertEquals("0e+00p53x11", cf.STRING_float2string(FloatBuiltin.of(BigFloat.zero(53), 11))); - assertEquals("-0e+00p53x11", cf.STRING_float2string(FloatBuiltin.of(BigFloat.negativeZero(53), 11))); - assertEquals("-Infinityp53x11", cf.STRING_float2string(FloatBuiltin.of(BigFloat.negativeInfinity(53), 11))); - assertEquals("1.0000000000000001e-01p53x11", cf.STRING_float2string(FloatBuiltin.of(new BigFloat(0.1, BinaryMathContext.BINARY64), 11))); + assertEquals( + "-0e+00p53x11", cf.STRING_float2string(FloatBuiltin.of(BigFloat.negativeZero(53), 11))); + assertEquals( + "-Infinityp53x11", + cf.STRING_float2string(FloatBuiltin.of(BigFloat.negativeInfinity(53), 11))); + assertEquals( + "1.0000000000000001e-01p53x11", + cf.STRING_float2string(FloatBuiltin.of(new BigFloat(0.1, BinaryMathContext.BINARY64), 11))); } @Test public void testString2Float() { - assertEquals(FloatBuiltin.of(BigFloat.positiveInfinity(53), 11), cf.STRING_string2float("Infinity")); - assertEquals(FloatBuiltin.of(BigFloat.positiveInfinity(24), 8), cf.STRING_string2float("Infinityf")); - assertEquals(FloatBuiltin.of(BigFloat.positiveInfinity(10), 10), cf.STRING_string2float("Infinityp10x10")); + assertEquals( + FloatBuiltin.of(BigFloat.positiveInfinity(53), 11), cf.STRING_string2float("Infinity")); + assertEquals( + FloatBuiltin.of(BigFloat.positiveInfinity(24), 8), cf.STRING_string2float("Infinityf")); + assertEquals( + FloatBuiltin.of(BigFloat.positiveInfinity(10), 10), + cf.STRING_string2float("Infinityp10x10")); assertEquals(FloatBuiltin.of(BigFloat.NaN(53), 11), cf.STRING_string2float("NaN")); assertEquals(FloatBuiltin.of(BigFloat.zero(53), 11), cf.STRING_string2float("0.0")); assertEquals(FloatBuiltin.of(BigFloat.zero(53), 11), cf.STRING_string2float("0e+00")); assertEquals(FloatBuiltin.of(BigFloat.zero(53), 11), cf.STRING_string2float("0e-00")); assertEquals(FloatBuiltin.of(BigFloat.negativeZero(53), 11), cf.STRING_string2float("-0.0")); - assertEquals(FloatBuiltin.of(BigFloat.negativeInfinity(53), 11), cf.STRING_string2float("-Infinity")); - assertEquals(FloatBuiltin.of(new BigFloat(0.1, BinaryMathContext.BINARY64), 11), cf.STRING_string2float("0.1")); - assertEquals(FloatBuiltin.of(new BigFloat(0.1f, BinaryMathContext.BINARY32), 8), cf.STRING_string2float("0.1f")); + assertEquals( + FloatBuiltin.of(BigFloat.negativeInfinity(53), 11), cf.STRING_string2float("-Infinity")); + assertEquals( + FloatBuiltin.of(new BigFloat(0.1, BinaryMathContext.BINARY64), 11), + cf.STRING_string2float("0.1")); + assertEquals( + FloatBuiltin.of(new BigFloat(0.1f, BinaryMathContext.BINARY32), 8), + cf.STRING_string2float("0.1f")); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testString2FloatError() { cf.STRING_string2float("0.0.0"); } @@ -275,7 +298,8 @@ public void testInt2String() { assertEquals("0", cf.STRING_int2string(BigInteger.valueOf(0))); assertEquals("1", cf.STRING_int2string(BigInteger.valueOf(1))); assertEquals("-1", cf.STRING_int2string(BigInteger.valueOf(-1))); - assertEquals("100000000000000000000", cf.STRING_int2string(new BigInteger("100000000000000000000"))); + assertEquals( + "100000000000000000000", cf.STRING_int2string(new BigInteger("100000000000000000000"))); } @Test @@ -284,10 +308,11 @@ public void testString2Int() { assertEquals(BigInteger.valueOf(1), cf.STRING_string2int("1")); assertEquals(BigInteger.valueOf(1), cf.STRING_string2int("+1")); assertEquals(BigInteger.valueOf(-1), cf.STRING_string2int("-1")); - assertEquals(new BigInteger("100000000000000000000"), cf.STRING_string2int("100000000000000000000")); + assertEquals( + new BigInteger("100000000000000000000"), cf.STRING_string2int("100000000000000000000")); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testString2IntError() { cf.STRING_string2int("0.0"); } @@ -302,12 +327,12 @@ public void testBase2String() { assertEquals("10", cf.STRING_base2string(BigInteger.valueOf(36), BigInteger.valueOf(36))); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testBase2StringError1() { cf.STRING_base2string(BigInteger.valueOf(0), BigInteger.valueOf(1)); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testBase2StringError2() { cf.STRING_base2string(BigInteger.valueOf(0), BigInteger.valueOf(37)); } @@ -323,27 +348,27 @@ public void testString2Base() { assertEquals(BigInteger.valueOf(36), cf.STRING_string2base("10", BigInteger.valueOf(36))); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testString2BaseError1() { cf.STRING_string2base("0", BigInteger.valueOf(1)); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testString2BaseError2() { cf.STRING_string2base("0", BigInteger.valueOf(37)); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testString2BaseError3() { cf.STRING_string2base("8", BigInteger.valueOf(8)); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testString2BaseError4() { cf.STRING_string2base("g", BigInteger.valueOf(16)); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testString2BaseError5() { cf.STRING_string2base("0.0", BigInteger.valueOf(2)); } @@ -364,12 +389,12 @@ public void testReplace() { assertEquals("foo", cf.STRING_replace("foo", "bar", "baz", BigInteger.valueOf(0))); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testReplaceError1() { cf.STRING_replace("foo", "oo", "ar", BigInteger.valueOf(-1)); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testReplaceError2() { cf.STRING_replace("foo", "oo", "ar", BigInteger.valueOf(Long.MAX_VALUE)); } @@ -465,33 +490,42 @@ public void testIntNot() { public void testIntPow() { assertEquals(BigInteger.valueOf(1), cf.INT_pow(BigInteger.valueOf(10), BigInteger.valueOf(0))); assertEquals(BigInteger.valueOf(10), cf.INT_pow(BigInteger.valueOf(10), BigInteger.valueOf(1))); - assertEquals(BigInteger.valueOf(100), cf.INT_pow(BigInteger.valueOf(10), BigInteger.valueOf(2))); + assertEquals( + BigInteger.valueOf(100), cf.INT_pow(BigInteger.valueOf(10), BigInteger.valueOf(2))); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testIntPowError() { cf.INT_pow(BigInteger.valueOf(10), BigInteger.valueOf(-1)); } @Test public void testIntPowMod() { - assertEquals(BigInteger.valueOf(1), cf.INT_powmod(BigInteger.valueOf(10), BigInteger.valueOf(0), BigInteger.valueOf(7))); - assertEquals(BigInteger.valueOf(3), cf.INT_powmod(BigInteger.valueOf(10), BigInteger.valueOf(1), BigInteger.valueOf(7))); - assertEquals(BigInteger.valueOf(2), cf.INT_powmod(BigInteger.valueOf(10), BigInteger.valueOf(2), BigInteger.valueOf(7))); - assertEquals(BigInteger.valueOf(5), cf.INT_powmod(BigInteger.valueOf(10), BigInteger.valueOf(-1), BigInteger.valueOf(7))); - } - - @Test(expected=KEMException.class) + assertEquals( + BigInteger.valueOf(1), + cf.INT_powmod(BigInteger.valueOf(10), BigInteger.valueOf(0), BigInteger.valueOf(7))); + assertEquals( + BigInteger.valueOf(3), + cf.INT_powmod(BigInteger.valueOf(10), BigInteger.valueOf(1), BigInteger.valueOf(7))); + assertEquals( + BigInteger.valueOf(2), + cf.INT_powmod(BigInteger.valueOf(10), BigInteger.valueOf(2), BigInteger.valueOf(7))); + assertEquals( + BigInteger.valueOf(5), + cf.INT_powmod(BigInteger.valueOf(10), BigInteger.valueOf(-1), BigInteger.valueOf(7))); + } + + @Test(expected = KEMException.class) public void testIntPowModError1() { cf.INT_powmod(BigInteger.valueOf(10), BigInteger.valueOf(1), BigInteger.valueOf(0)); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testIntPowModError2() { cf.INT_powmod(BigInteger.valueOf(10), BigInteger.valueOf(1), BigInteger.valueOf(-1)); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testIntPowModError3() { cf.INT_powmod(BigInteger.valueOf(10), BigInteger.valueOf(-1), BigInteger.valueOf(5)); } @@ -505,12 +539,15 @@ public void testIntMul() { public void testIntTDiv() { assertEquals(BigInteger.valueOf(2), cf.INT_tdiv(BigInteger.valueOf(10), BigInteger.valueOf(5))); assertEquals(BigInteger.valueOf(2), cf.INT_tdiv(BigInteger.valueOf(7), BigInteger.valueOf(3))); - assertEquals(BigInteger.valueOf(-2), cf.INT_tdiv(BigInteger.valueOf(7), BigInteger.valueOf(-3))); - assertEquals(BigInteger.valueOf(-2), cf.INT_tdiv(BigInteger.valueOf(-7), BigInteger.valueOf(3))); - assertEquals(BigInteger.valueOf(2), cf.INT_tdiv(BigInteger.valueOf(-7), BigInteger.valueOf(-3))); + assertEquals( + BigInteger.valueOf(-2), cf.INT_tdiv(BigInteger.valueOf(7), BigInteger.valueOf(-3))); + assertEquals( + BigInteger.valueOf(-2), cf.INT_tdiv(BigInteger.valueOf(-7), BigInteger.valueOf(3))); + assertEquals( + BigInteger.valueOf(2), cf.INT_tdiv(BigInteger.valueOf(-7), BigInteger.valueOf(-3))); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testIntTDivError() { cf.INT_tdiv(BigInteger.valueOf(10), BigInteger.valueOf(0)); } @@ -519,12 +556,14 @@ public void testIntTDivError() { public void testIntTMod() { assertEquals(BigInteger.valueOf(0), cf.INT_tmod(BigInteger.valueOf(10), BigInteger.valueOf(5))); assertEquals(BigInteger.valueOf(1), cf.INT_tmod(BigInteger.valueOf(7), BigInteger.valueOf(3))); - assertEquals(BigInteger.valueOf(-1), cf.INT_tmod(BigInteger.valueOf(-7), BigInteger.valueOf(3))); + assertEquals( + BigInteger.valueOf(-1), cf.INT_tmod(BigInteger.valueOf(-7), BigInteger.valueOf(3))); assertEquals(BigInteger.valueOf(1), cf.INT_tmod(BigInteger.valueOf(7), BigInteger.valueOf(-3))); - assertEquals(BigInteger.valueOf(-1), cf.INT_tmod(BigInteger.valueOf(-7), BigInteger.valueOf(-3))); + assertEquals( + BigInteger.valueOf(-1), cf.INT_tmod(BigInteger.valueOf(-7), BigInteger.valueOf(-3))); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testIntTModError() { cf.INT_tmod(BigInteger.valueOf(10), BigInteger.valueOf(0)); } @@ -533,12 +572,15 @@ public void testIntTModError() { public void testIntEDiv() { assertEquals(BigInteger.valueOf(2), cf.INT_ediv(BigInteger.valueOf(10), BigInteger.valueOf(5))); assertEquals(BigInteger.valueOf(2), cf.INT_ediv(BigInteger.valueOf(7), BigInteger.valueOf(3))); - assertEquals(BigInteger.valueOf(-2), cf.INT_ediv(BigInteger.valueOf(7), BigInteger.valueOf(-3))); - assertEquals(BigInteger.valueOf(-3), cf.INT_ediv(BigInteger.valueOf(-7), BigInteger.valueOf(3))); - assertEquals(BigInteger.valueOf(3), cf.INT_ediv(BigInteger.valueOf(-7), BigInteger.valueOf(-3))); + assertEquals( + BigInteger.valueOf(-2), cf.INT_ediv(BigInteger.valueOf(7), BigInteger.valueOf(-3))); + assertEquals( + BigInteger.valueOf(-3), cf.INT_ediv(BigInteger.valueOf(-7), BigInteger.valueOf(3))); + assertEquals( + BigInteger.valueOf(3), cf.INT_ediv(BigInteger.valueOf(-7), BigInteger.valueOf(-3))); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testIntEDivError() { cf.INT_ediv(BigInteger.valueOf(10), BigInteger.valueOf(0)); } @@ -549,10 +591,11 @@ public void testIntEMod() { assertEquals(BigInteger.valueOf(1), cf.INT_emod(BigInteger.valueOf(7), BigInteger.valueOf(3))); assertEquals(BigInteger.valueOf(2), cf.INT_emod(BigInteger.valueOf(-7), BigInteger.valueOf(3))); assertEquals(BigInteger.valueOf(1), cf.INT_emod(BigInteger.valueOf(7), BigInteger.valueOf(-3))); - assertEquals(BigInteger.valueOf(2), cf.INT_emod(BigInteger.valueOf(-7), BigInteger.valueOf(-3))); + assertEquals( + BigInteger.valueOf(2), cf.INT_emod(BigInteger.valueOf(-7), BigInteger.valueOf(-3))); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testIntEModError() { cf.INT_emod(BigInteger.valueOf(10), BigInteger.valueOf(0)); } @@ -575,7 +618,7 @@ public void testShr() { assertEquals(BigInteger.valueOf(8), cf.INT_shr(BigInteger.valueOf(8), BigInteger.valueOf(0))); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testShrError() { cf.INT_shr(BigInteger.valueOf(8), BigInteger.valueOf(-2)); } @@ -586,7 +629,7 @@ public void testShl() { assertEquals(BigInteger.valueOf(8), cf.INT_shl(BigInteger.valueOf(8), BigInteger.valueOf(0))); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testShlError() { cf.INT_shl(BigInteger.valueOf(32), BigInteger.valueOf(-2)); } @@ -638,55 +681,99 @@ public void testIntLog2() { assertEquals(BigInteger.valueOf(3), cf.INT_log2(BigInteger.valueOf(8))); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testIntLog2Error1() { cf.INT_log2(BigInteger.valueOf(0)); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testIntLog2Error2() { cf.INT_log2(BigInteger.valueOf(-1)); } @Test public void testBitRange() { - assertEquals(BigInteger.valueOf(127), cf.INT_bitRange(BigInteger.valueOf(127), BigInteger.valueOf(0), BigInteger.valueOf(8))); - assertEquals(BigInteger.valueOf(255), cf.INT_bitRange(BigInteger.valueOf(255), BigInteger.valueOf(0), BigInteger.valueOf(8))); - assertEquals(BigInteger.valueOf(64), cf.INT_bitRange(BigInteger.valueOf(128), BigInteger.valueOf(1), BigInteger.valueOf(7))); - assertEquals(BigInteger.valueOf(0), cf.INT_bitRange(BigInteger.valueOf(129), BigInteger.valueOf(1), BigInteger.valueOf(5))); - assertEquals(BigInteger.valueOf(0), cf.INT_bitRange(BigInteger.valueOf(128), BigInteger.valueOf(0), BigInteger.valueOf(0))); - assertEquals(BigInteger.valueOf(0), cf.INT_bitRange(new BigInteger("8040201008040201", 16), BigInteger.valueOf(256), BigInteger.valueOf(8))); - assertEquals(BigInteger.valueOf(12), cf.INT_bitRange(new BigInteger("-710567042938717889665411037832208781722350888143921263584927239275773573551204588944105336352942349727184887589413944684473529682801526123805453895275517072855048781056"), BigInteger.valueOf(32), BigInteger.valueOf(8))); - assertEquals(BigInteger.valueOf(56), cf.INT_bitRange(new BigInteger("697754608693466068295273213726275558775348389513141500672185545754018175722916164768735179047222610843044264325669307777729891642448846794142000"), BigInteger.valueOf(64), BigInteger.valueOf(8))); - } - - @Test(expected=KEMException.class) + assertEquals( + BigInteger.valueOf(127), + cf.INT_bitRange(BigInteger.valueOf(127), BigInteger.valueOf(0), BigInteger.valueOf(8))); + assertEquals( + BigInteger.valueOf(255), + cf.INT_bitRange(BigInteger.valueOf(255), BigInteger.valueOf(0), BigInteger.valueOf(8))); + assertEquals( + BigInteger.valueOf(64), + cf.INT_bitRange(BigInteger.valueOf(128), BigInteger.valueOf(1), BigInteger.valueOf(7))); + assertEquals( + BigInteger.valueOf(0), + cf.INT_bitRange(BigInteger.valueOf(129), BigInteger.valueOf(1), BigInteger.valueOf(5))); + assertEquals( + BigInteger.valueOf(0), + cf.INT_bitRange(BigInteger.valueOf(128), BigInteger.valueOf(0), BigInteger.valueOf(0))); + assertEquals( + BigInteger.valueOf(0), + cf.INT_bitRange( + new BigInteger("8040201008040201", 16), + BigInteger.valueOf(256), + BigInteger.valueOf(8))); + assertEquals( + BigInteger.valueOf(12), + cf.INT_bitRange( + new BigInteger( + "-710567042938717889665411037832208781722350888143921263584927239275773573551204588944105336352942349727184887589413944684473529682801526123805453895275517072855048781056"), + BigInteger.valueOf(32), + BigInteger.valueOf(8))); + assertEquals( + BigInteger.valueOf(56), + cf.INT_bitRange( + new BigInteger( + "697754608693466068295273213726275558775348389513141500672185545754018175722916164768735179047222610843044264325669307777729891642448846794142000"), + BigInteger.valueOf(64), + BigInteger.valueOf(8))); + } + + @Test(expected = KEMException.class) public void testBitRangeError1() { cf.INT_bitRange(BigInteger.valueOf(128), BigInteger.valueOf(0), BigInteger.valueOf(-1)); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testBitRangeError2() { cf.INT_bitRange(BigInteger.valueOf(128), BigInteger.valueOf(-1), BigInteger.valueOf(8)); } @Test public void testSignExtendBitRange() { - assertEquals(BigInteger.valueOf(-1), cf.INT_signExtendBitRange(BigInteger.valueOf(255), BigInteger.valueOf(0), BigInteger.valueOf(8))); - assertEquals(BigInteger.valueOf(127), cf.INT_signExtendBitRange(BigInteger.valueOf(127), BigInteger.valueOf(0), BigInteger.valueOf(8))); - assertEquals(BigInteger.valueOf(-64), cf.INT_signExtendBitRange(BigInteger.valueOf(128), BigInteger.valueOf(1), BigInteger.valueOf(7))); - assertEquals(BigInteger.valueOf(0), cf.INT_signExtendBitRange(BigInteger.valueOf(129), BigInteger.valueOf(1), BigInteger.valueOf(5))); - assertEquals(BigInteger.valueOf(0), cf.INT_signExtendBitRange(BigInteger.valueOf(128), BigInteger.valueOf(0), BigInteger.valueOf(0))); - } - - @Test(expected=KEMException.class) + assertEquals( + BigInteger.valueOf(-1), + cf.INT_signExtendBitRange( + BigInteger.valueOf(255), BigInteger.valueOf(0), BigInteger.valueOf(8))); + assertEquals( + BigInteger.valueOf(127), + cf.INT_signExtendBitRange( + BigInteger.valueOf(127), BigInteger.valueOf(0), BigInteger.valueOf(8))); + assertEquals( + BigInteger.valueOf(-64), + cf.INT_signExtendBitRange( + BigInteger.valueOf(128), BigInteger.valueOf(1), BigInteger.valueOf(7))); + assertEquals( + BigInteger.valueOf(0), + cf.INT_signExtendBitRange( + BigInteger.valueOf(129), BigInteger.valueOf(1), BigInteger.valueOf(5))); + assertEquals( + BigInteger.valueOf(0), + cf.INT_signExtendBitRange( + BigInteger.valueOf(128), BigInteger.valueOf(0), BigInteger.valueOf(0))); + } + + @Test(expected = KEMException.class) public void testSignExtendBitRangeError1() { - cf.INT_signExtendBitRange(BigInteger.valueOf(128), BigInteger.valueOf(0), BigInteger.valueOf(-1)); + cf.INT_signExtendBitRange( + BigInteger.valueOf(128), BigInteger.valueOf(0), BigInteger.valueOf(-1)); } - @Test(expected=KEMException.class) + @Test(expected = KEMException.class) public void testSignExtendBitRangeError2() { - cf.INT_signExtendBitRange(BigInteger.valueOf(128), BigInteger.valueOf(-1), BigInteger.valueOf(8)); + cf.INT_signExtendBitRange( + BigInteger.valueOf(128), BigInteger.valueOf(-1), BigInteger.valueOf(8)); } @Test @@ -755,25 +842,49 @@ public void testIntNe() { @Test public void testPrecision() { - assertEquals(BigInteger.valueOf(2), cf.FLOAT_precision(FloatBuiltin.of(new BigFloat(1.0, new BinaryMathContext(2, 8)), 8))); - assertEquals(BigInteger.valueOf(24), cf.FLOAT_precision(FloatBuiltin.of(new BigFloat(1.0, BinaryMathContext.BINARY32), 8))); + assertEquals( + BigInteger.valueOf(2), + cf.FLOAT_precision(FloatBuiltin.of(new BigFloat(1.0, new BinaryMathContext(2, 8)), 8))); + assertEquals( + BigInteger.valueOf(24), + cf.FLOAT_precision(FloatBuiltin.of(new BigFloat(1.0, BinaryMathContext.BINARY32), 8))); } @Test public void testExponentBits() { - assertEquals(BigInteger.valueOf(8), cf.FLOAT_exponentBits(FloatBuiltin.of(new BigFloat(1.0, new BinaryMathContext(2, 8)), 8))); - assertEquals(BigInteger.valueOf(11), cf.FLOAT_exponentBits(FloatBuiltin.of(new BigFloat(1.0, BinaryMathContext.BINARY64), 11))); + assertEquals( + BigInteger.valueOf(8), + cf.FLOAT_exponentBits(FloatBuiltin.of(new BigFloat(1.0, new BinaryMathContext(2, 8)), 8))); + assertEquals( + BigInteger.valueOf(11), + cf.FLOAT_exponentBits(FloatBuiltin.of(new BigFloat(1.0, BinaryMathContext.BINARY64), 11))); } @Test public void testExponent() { - assertEquals(BigInteger.valueOf(-127), cf.FLOAT_exponent(FloatBuiltin.of(new BigFloat(0.0, BinaryMathContext.BINARY32), 8))); - assertEquals(BigInteger.valueOf(-127), cf.FLOAT_exponent(FloatBuiltin.of(new BigFloat(-0.0, BinaryMathContext.BINARY32), 8))); - assertEquals(BigInteger.valueOf(128), cf.FLOAT_exponent(FloatBuiltin.of(new BigFloat(1.0/0.0, BinaryMathContext.BINARY32), 8))); - assertEquals(BigInteger.valueOf(128), cf.FLOAT_exponent(FloatBuiltin.of(new BigFloat(0.0/0.0, BinaryMathContext.BINARY32), 8))); - assertEquals(BigInteger.valueOf(2), cf.FLOAT_exponent(FloatBuiltin.of(new BigFloat(4.0, BinaryMathContext.BINARY32), 8))); - assertEquals(BigInteger.valueOf(-127), cf.FLOAT_exponent(FloatBuiltin.of(BigFloat.minValue(24, BinaryMathContext.BINARY32.minExponent), 8))); - assertEquals(BigInteger.valueOf(-126), cf.FLOAT_exponent(FloatBuiltin.of(BigFloat.minNormal(24, BinaryMathContext.BINARY32.minExponent), 8))); + assertEquals( + BigInteger.valueOf(-127), + cf.FLOAT_exponent(FloatBuiltin.of(new BigFloat(0.0, BinaryMathContext.BINARY32), 8))); + assertEquals( + BigInteger.valueOf(-127), + cf.FLOAT_exponent(FloatBuiltin.of(new BigFloat(-0.0, BinaryMathContext.BINARY32), 8))); + assertEquals( + BigInteger.valueOf(128), + cf.FLOAT_exponent(FloatBuiltin.of(new BigFloat(1.0 / 0.0, BinaryMathContext.BINARY32), 8))); + assertEquals( + BigInteger.valueOf(128), + cf.FLOAT_exponent(FloatBuiltin.of(new BigFloat(0.0 / 0.0, BinaryMathContext.BINARY32), 8))); + assertEquals( + BigInteger.valueOf(2), + cf.FLOAT_exponent(FloatBuiltin.of(new BigFloat(4.0, BinaryMathContext.BINARY32), 8))); + assertEquals( + BigInteger.valueOf(-127), + cf.FLOAT_exponent( + FloatBuiltin.of(BigFloat.minValue(24, BinaryMathContext.BINARY32.minExponent), 8))); + assertEquals( + BigInteger.valueOf(-126), + cf.FLOAT_exponent( + FloatBuiltin.of(BigFloat.minNormal(24, BinaryMathContext.BINARY32.minExponent), 8))); } private FloatBuiltin _float(float f) { @@ -788,21 +899,21 @@ private FloatBuiltin _double(double f) { public void testSign() { assertEquals(false, cf.FLOAT_sign(_float(0.0f))); assertEquals(true, cf.FLOAT_sign(_float(-0.0f))); - assertEquals(false, cf.FLOAT_sign(_float(1.0f/0.0f))); - assertEquals(true, cf.FLOAT_sign(_float(-1.0f/0.0f))); + assertEquals(false, cf.FLOAT_sign(_float(1.0f / 0.0f))); + assertEquals(true, cf.FLOAT_sign(_float(-1.0f / 0.0f))); assertEquals(false, cf.FLOAT_sign(_float(1.0f))); assertEquals(true, cf.FLOAT_sign(_float(-1.0f))); assertEquals(false, cf.FLOAT_sign(_float(3.0f))); assertEquals(false, cf.FLOAT_sign(_float(0.5f))); - assertEquals(false, cf.FLOAT_sign(_float(0.0f/0.0f))); + assertEquals(false, cf.FLOAT_sign(_float(0.0f / 0.0f))); } @Test public void testIsNaN() { assertEquals(false, cf.FLOAT_isNaN(_float(0.0f))); assertEquals(false, cf.FLOAT_isNaN(_float(-0.0f))); - assertEquals(false, cf.FLOAT_isNaN(_float(1.0f/0.0f))); - assertEquals(true, cf.FLOAT_isNaN(_float(0.0f/0.0f))); + assertEquals(false, cf.FLOAT_isNaN(_float(1.0f / 0.0f))); + assertEquals(true, cf.FLOAT_isNaN(_float(0.0f / 0.0f))); } @Test @@ -810,13 +921,16 @@ public void testFloatNeg() { testUnaryOp(cf::FLOAT_neg, a -> -a); } - private final double[] refs = new double[] {0.0, -0.0, 1.0/0.0, -1.0/0.0, 1.0, -1.0, 3.0, 0.5, 0.0/0.0}; + private final double[] refs = + new double[] {0.0, -0.0, 1.0 / 0.0, -1.0 / 0.0, 1.0, -1.0, 3.0, 0.5, 0.0 / 0.0}; - private void testUnaryOp(Function op, Function refOp) { + private void testUnaryOp( + Function op, Function refOp) { testUnaryOp(op, refOp, Double.MIN_VALUE); } - private void testUnaryOp(Function op, Function refOp, Double epsilon) { + private void testUnaryOp( + Function op, Function refOp, Double epsilon) { for (int i = 0; i < refs.length; i++) { FloatBuiltin result = op.apply(_double(refs[i])); double ref = refOp.apply(refs[i]); @@ -824,12 +938,19 @@ private void testUnaryOp(Function op, Function op, BiFunction refOp) { + private void testBinaryOp( + String operator, + BiFunction op, + BiFunction refOp) { for (int i = 0; i < refs.length; i++) { for (int j = 0; j < refs.length; j++) { FloatBuiltin result = op.apply(_double(refs[i]), _double(refs[j])); double ref = refOp.apply(refs[i], refs[j]); - assertEquals("Operator " + operator + "(" + refs[i] + "," + refs[j] + ") failed", ref, result.doubleValue(), Double.MIN_VALUE); + assertEquals( + "Operator " + operator + "(" + refs[i] + "," + refs[j] + ") failed", + ref, + result.doubleValue(), + Double.MIN_VALUE); } } } @@ -851,27 +972,27 @@ public void testFloatPow() { @Test public void testFloatMul() { - testBinaryOp("*", cf::FLOAT_mul, (a, b) -> a*b); + testBinaryOp("*", cf::FLOAT_mul, (a, b) -> a * b); } @Test public void testFloatDiv() { - testBinaryOp("/", cf::FLOAT_div, (a, b) -> a/b); + testBinaryOp("/", cf::FLOAT_div, (a, b) -> a / b); } @Test public void testFloatRem() { - testBinaryOp("%", cf::FLOAT_rem, (a, b) -> a%b); + testBinaryOp("%", cf::FLOAT_rem, (a, b) -> a % b); } @Test public void testFloatAdd() { - testBinaryOp("+", cf::FLOAT_add, (a, b) -> a+b); + testBinaryOp("+", cf::FLOAT_add, (a, b) -> a + b); } @Test public void testFloatSub() { - testBinaryOp("-", cf::FLOAT_sub, (a, b) -> a-b); + testBinaryOp("-", cf::FLOAT_sub, (a, b) -> a - b); } @Test @@ -886,10 +1007,22 @@ public void testFloatAbs() { @Test public void testRound() { - assertEquals(12.0, cf.FLOAT_round(_double(10.5), BigInteger.valueOf(2), BigInteger.valueOf(8)).doubleValue(), Double.MIN_VALUE); - assertEquals(8.0, cf.FLOAT_round(_double(9.5), BigInteger.valueOf(2), BigInteger.valueOf(8)).doubleValue(), Double.MIN_VALUE); - assertEquals(10.5f, cf.FLOAT_round(_double(10.5), BigInteger.valueOf(24), BigInteger.valueOf(8)).floatValue(), Double.MIN_VALUE); - assertEquals(9.5f, cf.FLOAT_round(_double(9.5), BigInteger.valueOf(24), BigInteger.valueOf(8)).floatValue(), Double.MIN_VALUE); + assertEquals( + 12.0, + cf.FLOAT_round(_double(10.5), BigInteger.valueOf(2), BigInteger.valueOf(8)).doubleValue(), + Double.MIN_VALUE); + assertEquals( + 8.0, + cf.FLOAT_round(_double(9.5), BigInteger.valueOf(2), BigInteger.valueOf(8)).doubleValue(), + Double.MIN_VALUE); + assertEquals( + 10.5f, + cf.FLOAT_round(_double(10.5), BigInteger.valueOf(24), BigInteger.valueOf(8)).floatValue(), + Double.MIN_VALUE); + assertEquals( + 9.5f, + cf.FLOAT_round(_double(9.5), BigInteger.valueOf(24), BigInteger.valueOf(8)).floatValue(), + Double.MIN_VALUE); } @Test @@ -977,25 +1110,40 @@ private double max(double a, double b) { return Math.max(a, b); } - @Test public void testMaxValue() { - assertEquals(Float.MAX_VALUE, cf.FLOAT_maxValue(BigInteger.valueOf(24), BigInteger.valueOf(8)).floatValue(), Double.MIN_VALUE); - assertEquals(Double.MAX_VALUE, cf.FLOAT_maxValue(BigInteger.valueOf(53), BigInteger.valueOf(11)).doubleValue(), Double.MIN_VALUE); + assertEquals( + Float.MAX_VALUE, + cf.FLOAT_maxValue(BigInteger.valueOf(24), BigInteger.valueOf(8)).floatValue(), + Double.MIN_VALUE); + assertEquals( + Double.MAX_VALUE, + cf.FLOAT_maxValue(BigInteger.valueOf(53), BigInteger.valueOf(11)).doubleValue(), + Double.MIN_VALUE); } @Test public void testMinValue() { - assertEquals(Float.MIN_VALUE, cf.FLOAT_minValue(BigInteger.valueOf(24), BigInteger.valueOf(8)).floatValue(), Double.MIN_VALUE); - assertEquals(Double.MIN_VALUE, cf.FLOAT_minValue(BigInteger.valueOf(53), BigInteger.valueOf(11)).doubleValue(), Double.MIN_VALUE); - } - - private void testComparisonOp(String operator, BiFunction op, BiFunction refOp) { + assertEquals( + Float.MIN_VALUE, + cf.FLOAT_minValue(BigInteger.valueOf(24), BigInteger.valueOf(8)).floatValue(), + Double.MIN_VALUE); + assertEquals( + Double.MIN_VALUE, + cf.FLOAT_minValue(BigInteger.valueOf(53), BigInteger.valueOf(11)).doubleValue(), + Double.MIN_VALUE); + } + + private void testComparisonOp( + String operator, + BiFunction op, + BiFunction refOp) { for (int i = 0; i < refs.length; i++) { for (int j = 0; j < refs.length; j++) { boolean result = op.apply(_double(refs[i]), _double(refs[j])); boolean ref = refOp.apply(refs[i], refs[j]); - assertEquals("Operator " + operator + "(" + refs[i] + "," + refs[j] + ") failed", ref, result); + assertEquals( + "Operator " + operator + "(" + refs[i] + "," + refs[j] + ") failed", ref, result); } } } @@ -1032,10 +1180,26 @@ public void testFloatNe() { @Test public void testInt2Float() { - assertEquals(8.0, cf.FLOAT_int2float(BigInteger.valueOf(9), BigInteger.valueOf(2), BigInteger.valueOf(8)).doubleValue(), Double.MIN_VALUE); - assertEquals(12.0, cf.FLOAT_int2float(BigInteger.valueOf(11), BigInteger.valueOf(2), BigInteger.valueOf(8)).doubleValue(), Double.MIN_VALUE); - assertEquals(8.0, cf.FLOAT_int2float(BigInteger.valueOf(10), BigInteger.valueOf(2), BigInteger.valueOf(8)).doubleValue(), Double.MIN_VALUE); - assertEquals(10.0, cf.FLOAT_int2float(BigInteger.valueOf(10), BigInteger.valueOf(24), BigInteger.valueOf(8)).doubleValue(), Double.MIN_VALUE); + assertEquals( + 8.0, + cf.FLOAT_int2float(BigInteger.valueOf(9), BigInteger.valueOf(2), BigInteger.valueOf(8)) + .doubleValue(), + Double.MIN_VALUE); + assertEquals( + 12.0, + cf.FLOAT_int2float(BigInteger.valueOf(11), BigInteger.valueOf(2), BigInteger.valueOf(8)) + .doubleValue(), + Double.MIN_VALUE); + assertEquals( + 8.0, + cf.FLOAT_int2float(BigInteger.valueOf(10), BigInteger.valueOf(2), BigInteger.valueOf(8)) + .doubleValue(), + Double.MIN_VALUE); + assertEquals( + 10.0, + cf.FLOAT_int2float(BigInteger.valueOf(10), BigInteger.valueOf(24), BigInteger.valueOf(8)) + .doubleValue(), + Double.MIN_VALUE); } @Test diff --git a/kernel/src/test/java/org/kframework/kast/KastModuleTest.java b/kernel/src/test/java/org/kframework/kast/KastModuleTest.java index ff4bb0d5afb..8f868a2a4b3 100644 --- a/kernel/src/test/java/org/kframework/kast/KastModuleTest.java +++ b/kernel/src/test/java/org/kframework/kast/KastModuleTest.java @@ -3,24 +3,25 @@ import static org.junit.Assert.*; -import org.junit.Test; -import org.kframework.main.FrontEnd; -import org.kframework.utils.BaseTestCase; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Module; import com.google.inject.util.Modules; - import java.util.List; +import org.junit.Test; +import org.kframework.main.FrontEnd; +import org.kframework.utils.BaseTestCase; public class KastModuleTest extends BaseTestCase { - @Test - public void testCreateInjection() { - String[] argv = new String[] { "foo.c" }; - List modules = KastFrontEnd.getModules(); - Injector injector = Guice.createInjector(Modules.override(modules).with(new TestModule(), new DefinitionSpecificTestModule())); - prepInjector(injector, "-kast", argv); - assertTrue(injector.getInstance(FrontEnd.class) instanceof KastFrontEnd); - } + @Test + public void testCreateInjection() { + String[] argv = new String[] {"foo.c"}; + List modules = KastFrontEnd.getModules(); + Injector injector = + Guice.createInjector( + Modules.override(modules).with(new TestModule(), new DefinitionSpecificTestModule())); + prepInjector(injector, "-kast", argv); + assertTrue(injector.getInstance(FrontEnd.class) instanceof KastFrontEnd); + } } diff --git a/kernel/src/test/java/org/kframework/kompile/KompileFrontEndTest.java b/kernel/src/test/java/org/kframework/kompile/KompileFrontEndTest.java index 2f149f96629..d917d1fe3cd 100644 --- a/kernel/src/test/java/org/kframework/kompile/KompileFrontEndTest.java +++ b/kernel/src/test/java/org/kframework/kompile/KompileFrontEndTest.java @@ -1,7 +1,11 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.kompile; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + import com.google.inject.util.Providers; +import java.io.IOException; import org.junit.Test; import org.kframework.main.GlobalOptions; import org.kframework.utils.IOTestCase; @@ -11,38 +15,54 @@ import org.kframework.utils.options.OuterParsingOptions; import org.mockito.Mock; -import java.io.IOException; +public class KompileFrontEndTest extends IOTestCase { -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; + @Mock org.kframework.compile.Backend koreBackend; -public class KompileFrontEndTest extends IOTestCase { + @Mock JarInfo jarInfo; + + @Mock FileUtil files; + + KompileOptions options = new KompileOptions(); + OuterParsingOptions outerOptions = new OuterParsingOptions(); + InnerParsingOptions innerOptions = new InnerParsingOptions(); + GlobalOptions globalOptions = new GlobalOptions(); + + @Test + public void testHelp() throws IOException { + globalOptions.help = true; + new KompileFrontEnd( + options, + outerOptions, + innerOptions, + globalOptions, + "foo", + Providers.of(koreBackend), + sw, + kem, + loader, + jarInfo, + Providers.of(files)) + .main(); + assertEquals("foo", stdout.toString()); + } - @Mock - org.kframework.compile.Backend koreBackend; - - @Mock - JarInfo jarInfo; - - @Mock - FileUtil files; - - KompileOptions options = new KompileOptions(); - OuterParsingOptions outerOptions = new OuterParsingOptions(); - InnerParsingOptions innerOptions = new InnerParsingOptions(); - GlobalOptions globalOptions = new GlobalOptions(); - - @Test - public void testHelp() throws IOException { - globalOptions.help = true; - new KompileFrontEnd(options, outerOptions, innerOptions, globalOptions, "foo", Providers.of(koreBackend), sw, kem, loader, jarInfo, Providers.of(files)).main(); - assertEquals("foo", stdout.toString()); - } - - @Test - public void testVersion() { - globalOptions.version = true; - new KompileFrontEnd(options, outerOptions, innerOptions, globalOptions, "", Providers.of(koreBackend), sw, kem, loader, jarInfo, Providers.of(files)).main(); - verify(jarInfo).printVersionMessage(); - } + @Test + public void testVersion() { + globalOptions.version = true; + new KompileFrontEnd( + options, + outerOptions, + innerOptions, + globalOptions, + "", + Providers.of(koreBackend), + sw, + kem, + loader, + jarInfo, + Providers.of(files)) + .main(); + verify(jarInfo).printVersionMessage(); + } } diff --git a/kernel/src/test/java/org/kframework/kompile/KompileModuleTest.java b/kernel/src/test/java/org/kframework/kompile/KompileModuleTest.java index 8c06faf76b1..f274263bf32 100644 --- a/kernel/src/test/java/org/kframework/kompile/KompileModuleTest.java +++ b/kernel/src/test/java/org/kframework/kompile/KompileModuleTest.java @@ -1,6 +1,8 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.kompile; +import static org.junit.Assert.*; + import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.util.Modules; @@ -9,23 +11,27 @@ import org.kframework.main.FrontEnd; import org.kframework.utils.BaseTestCase; -import static org.junit.Assert.*; - public class KompileModuleTest extends BaseTestCase { - @Test - public void testCreateInjection() { - String[] argv = new String[] { "test.k", "--backend", "test" }; - Injector injector = Guice.createInjector(Modules.override(KompileFrontEnd.getModules()).with(new DefinitionSpecificTestModule(), new TestModule())); - prepInjector(injector, "-kompile", argv); - assertTrue(injector.getInstance(FrontEnd.class) instanceof KompileFrontEnd); - } + @Test + public void testCreateInjection() { + String[] argv = new String[] {"test.k", "--backend", "test"}; + Injector injector = + Guice.createInjector( + Modules.override(KompileFrontEnd.getModules()) + .with(new DefinitionSpecificTestModule(), new TestModule())); + prepInjector(injector, "-kompile", argv); + assertTrue(injector.getInstance(FrontEnd.class) instanceof KompileFrontEnd); + } - @Test - public void testKDep() { - String[] argv = new String[] { "test.k" }; - Injector injector = Guice.createInjector(Modules.override(KDepFrontEnd.getModules()).with(new DefinitionSpecificTestModule(), new TestModule())); - prepInjector(injector, "-kdep", argv); - assertTrue(injector.getInstance(FrontEnd.class) instanceof KDepFrontEnd); - } + @Test + public void testKDep() { + String[] argv = new String[] {"test.k"}; + Injector injector = + Guice.createInjector( + Modules.override(KDepFrontEnd.getModules()) + .with(new DefinitionSpecificTestModule(), new TestModule())); + prepInjector(injector, "-kdep", argv); + assertTrue(injector.getInstance(FrontEnd.class) instanceof KDepFrontEnd); + } } diff --git a/kernel/src/test/java/org/kframework/kompile/KompileOptionsTest.java b/kernel/src/test/java/org/kframework/kompile/KompileOptionsTest.java index dfba3480163..07af31229ff 100644 --- a/kernel/src/test/java/org/kframework/kompile/KompileOptionsTest.java +++ b/kernel/src/test/java/org/kframework/kompile/KompileOptionsTest.java @@ -1,11 +1,14 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.kompile; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + import com.beust.jcommander.JCommander; +import java.io.File; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.kframework.backend.Backends; import org.kframework.utils.errorsystem.KEMException; import org.kframework.utils.errorsystem.KExceptionManager; import org.kframework.utils.file.FileUtil; @@ -15,54 +18,49 @@ import org.mockito.runners.MockitoJUnitRunner; import org.mockito.stubbing.Answer; -import java.io.File; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - @RunWith(MockitoJUnitRunner.class) public class KompileOptionsTest { - private KompileOptions options; + private KompileOptions options; - @Mock - KExceptionManager kem; + @Mock KExceptionManager kem; - @Mock - FileUtil files; + @Mock FileUtil files; - @Before - public void setUp() { - options = new KompileOptions(); - when(files.resolveWorkingDirectory(Matchers.anyString())).thenAnswer(new Answer() { - @Override - public File answer(InvocationOnMock invocation) throws Throwable { + @Before + public void setUp() { + options = new KompileOptions(); + when(files.resolveWorkingDirectory(Matchers.anyString())) + .thenAnswer( + new Answer() { + @Override + public File answer(InvocationOnMock invocation) throws Throwable { return new File((String) invocation.getArguments()[0]); - } - }); - } + } + }); + } - private void parse(String... args) { - new JCommander(options, args); - options.outerParsing.mainDefinitionFile(files); - options.mainModule(files); - options.syntaxModule(files); - } + private void parse(String... args) { + new JCommander(options, args); + options.outerParsing.mainDefinitionFile(files); + options.mainModule(files); + options.syntaxModule(files); + } - @Test(expected=KEMException.class) - public void testNoDefinition() throws Exception { - parse(); - } + @Test(expected = KEMException.class) + public void testNoDefinition() throws Exception { + parse(); + } - @Test - public void testDefaultModuleName() { - parse("foo.k"); - assertEquals("FOO", options.mainModule(files)); - } + @Test + public void testDefaultModuleName() { + parse("foo.k"); + assertEquals("FOO", options.mainModule(files)); + } - @Test - public void testDefaultSyntaxModuleName() { - parse("--main-module", "BAR", "foo.k"); - assertEquals("BAR-SYNTAX", options.syntaxModule(files)); - } + @Test + public void testDefaultSyntaxModuleName() { + parse("--main-module", "BAR", "foo.k"); + assertEquals("BAR-SYNTAX", options.syntaxModule(files)); + } } diff --git a/kernel/src/test/java/org/kframework/kore/compile/AddParentsCellsTest.java b/kernel/src/test/java/org/kframework/kore/compile/AddParentsCellsTest.java index 42d2fe2bf30..e9b57e960b7 100644 --- a/kernel/src/test/java/org/kframework/kore/compile/AddParentsCellsTest.java +++ b/kernel/src/test/java/org/kframework/kore/compile/AddParentsCellsTest.java @@ -2,234 +2,340 @@ package org.kframework.compile; +import static org.kframework.compile.ConfigurationInfo.Multiplicity.*; +import static org.kframework.kore.KORE.*; import com.google.common.collect.Lists; +import java.util.Arrays; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.kframework.builtin.KLabels; -import org.kframework.compile.ConfigurationInfo; -import org.kframework.compile.LabelInfo; import org.kframework.kore.*; import org.kframework.utils.errorsystem.KEMException; -import java.util.Arrays; +public class AddParentsCellsTest { + @Rule public final ExpectedException exception = ExpectedException.none(); -import static org.kframework.kore.KORE.*; -import static org.kframework.compile.ConfigurationInfo.Multiplicity.*; + final ConfigurationInfo cfgInfo = + new TestConfiguration() { + { + addCell(null, "TCell", ""); + addCell("TCell", "TSCell", ""); + addCell("TCell", "StateCell", ""); + addCell("TSCell", "tCell", "", STAR); + addCell("TSCell", "SchedulerCell", ""); + addCell("tCell", "KCell", ""); + addCell("tCell", "EnvCell", ""); + addCell("tCell", "MsgCell", "", STAR); + addCell("MsgCell", "MsgIdCell", ""); + } + }; + final LabelInfo labelInfo = + new LabelInfo() { + { + addLabel(Sort("TCell"), ""); + addLabel(Sort("TSCell"), ""); + addLabel(Sort("tCell"), ""); + addLabel(Sort("StateCell"), ""); + addLabel(Sort("SchedulerCell"), ""); + addLabel(Sort("KCell"), ""); + addLabel(Sort("EnvCell"), ""); + addLabel(Sort("MsgCell"), ""); + addLabel(Sort("MsgIdCell"), ""); + } + }; + final AddParentCells pass = new AddParentCells(cfgInfo, labelInfo); -public class AddParentsCellsTest { - @Rule - public final ExpectedException exception = ExpectedException.none(); - - final ConfigurationInfo cfgInfo = new TestConfiguration() {{ - addCell(null, "TCell", ""); - addCell("TCell", "TSCell", ""); - addCell("TCell", "StateCell", ""); - addCell("TSCell", "tCell", "", STAR); - addCell("TSCell", "SchedulerCell", ""); - addCell("tCell", "KCell", ""); - addCell("tCell", "EnvCell", ""); - addCell("tCell", "MsgCell", "", STAR); - addCell("MsgCell", "MsgIdCell", ""); - }}; - final LabelInfo labelInfo = new LabelInfo() {{ - addLabel(Sort("TCell"),""); - addLabel(Sort("TSCell"),""); - addLabel(Sort("tCell"),""); - addLabel(Sort("StateCell"),""); - addLabel(Sort("SchedulerCell"),""); - addLabel(Sort("KCell"),""); - addLabel(Sort("EnvCell"),""); - addLabel(Sort("MsgCell"),""); - addLabel(Sort("MsgIdCell"),""); - }}; - final AddParentCells pass = new AddParentCells(cfgInfo, labelInfo); - - @Test - public void testOneLeafCellNoCompletion() { - K term = cell("", intToToken(2)); - K expected = cell("", intToToken(2)); - Assert.assertEquals(expected, pass.concretizeCell(term)); - } - - @Test - public void testTwoCellsNoCompletion() { - K term = cell("", cell("", intToToken(2))); - K expected = cell("", cell("", intToToken(2))); - Assert.assertEquals(expected, pass.concretizeCell(term)); - } - - @Test - public void testTwoCellsCompletion() { - K term = cell("", cell("", intToToken(2))); - K expected = cell("", cell("", cell("", intToToken(2)))); - Assert.assertEquals(expected, pass.concretizeCell(term)); - } - - @Test - public void testMultiplicitySeparate() { - K term = cell("", cell("", intToToken(1)), cell("", intToToken(2))); - K expected = cell("", cell("", cell("", intToToken(1))), - cell("", cell("", intToToken(2)))); - Assert.assertEquals(expected, pass.concretizeCell(term)); - } - - @Test - public void testMultiplicityShared() { - K term = cell("", cell("", intToToken(1)), cell("", intToToken(2))); - K expected = cell("", cell("", cell("", intToToken(1)), cell("", intToToken(2)))); - Assert.assertEquals(expected, pass.concretizeCell(term)); - } - - @Test(expected = KEMException.class) - public void testAmbiguityError() { - K term = cell("", cell("", intToToken(1)), cell("", intToToken(2)), cell("", intToToken(2))); - pass.concretizeCell(term); - } - - @Test - public void testDeep2() { - Assert.assertEquals(Lists.newArrayList(cell("", cell("", intToToken(1)), cell("", intToToken(2)))), - pass.makeParents(KLabel(""), false, Lists.newArrayList(cell("", intToToken(1)), cell("", intToToken(2))))); - } - - @Test - public void testDeep() { - K term = cell("", cell("", intToToken(1)), cell("", intToToken(2))); - K expected = cell("", cell("", cell("", cell("", intToToken(1))), + @Test + public void testOneLeafCellNoCompletion() { + K term = cell("", intToToken(2)); + K expected = cell("", intToToken(2)); + Assert.assertEquals(expected, pass.concretizeCell(term)); + } + + @Test + public void testTwoCellsNoCompletion() { + K term = cell("", cell("", intToToken(2))); + K expected = cell("", cell("", intToToken(2))); + Assert.assertEquals(expected, pass.concretizeCell(term)); + } + + @Test + public void testTwoCellsCompletion() { + K term = cell("", cell("", intToToken(2))); + K expected = cell("", cell("", cell("", intToToken(2)))); + Assert.assertEquals(expected, pass.concretizeCell(term)); + } + + @Test + public void testMultiplicitySeparate() { + K term = cell("", cell("", intToToken(1)), cell("", intToToken(2))); + K expected = + cell( + "", + cell("", cell("", intToToken(1))), + cell("", cell("", intToToken(2)))); + Assert.assertEquals(expected, pass.concretizeCell(term)); + } + + @Test + public void testMultiplicityShared() { + K term = cell("", cell("", intToToken(1)), cell("", intToToken(2))); + K expected = + cell("", cell("", cell("", intToToken(1)), cell("", intToToken(2)))); + Assert.assertEquals(expected, pass.concretizeCell(term)); + } + + @Test(expected = KEMException.class) + public void testAmbiguityError() { + K term = + cell( + "", + cell("", intToToken(1)), + cell("", intToToken(2)), + cell("", intToToken(2))); + pass.concretizeCell(term); + } + + @Test + public void testDeep2() { + Assert.assertEquals( + Lists.newArrayList(cell("", cell("", intToToken(1)), cell("", intToToken(2)))), + pass.makeParents( + KLabel(""), + false, + Lists.newArrayList(cell("", intToToken(1)), cell("", intToToken(2))))); + } + + @Test + public void testDeep() { + K term = cell("", cell("", intToToken(1)), cell("", intToToken(2))); + K expected = + cell( + "", + cell( + "", + cell("", cell("", intToToken(1))), cell("", cell("", intToToken(2))))); - Assert.assertEquals(expected, pass.concretizeCell(term)); - } + Assert.assertEquals(expected, pass.concretizeCell(term)); + } - @Test - public void testRewrites() { - K term = cell("", cell("", intToToken(1)), KRewrite(cell("", intToToken(2)), cell(""))); - K expected = cell("", cell("", + @Test + public void testRewrites() { + K term = + cell("", cell("", intToToken(1)), KRewrite(cell("", intToToken(2)), cell(""))); + K expected = + cell( + "", + cell( + "", cell("", cell("", intToToken(1))), cell("", KRewrite(cell("", intToToken(2)), cell(""))))); - Assert.assertEquals(expected, pass.concretizeCell(term)); - } + Assert.assertEquals(expected, pass.concretizeCell(term)); + } - @Test - public void testRewriteWithCells() { - K term = cell("", cell("", intToToken(1)), KRewrite(cells(cell("", intToToken(2)), cell("")), cell(""))); - K expected = cell("", cell("", + @Test + public void testRewriteWithCells() { + K term = + cell( + "", + cell("", intToToken(1)), + KRewrite(cells(cell("", intToToken(2)), cell("")), cell(""))); + K expected = + cell( + "", + cell( + "", cell("", cell("", intToToken(1))), - cell("", KRewrite(cells(cell("", intToToken(2)), cell("")), cell(""))))); - Assert.assertEquals(expected, pass.concretizeCell(term)); - } - - @Test - public void testRewriteWithCellVariable() { - K term = cell("", KRewrite(KVariable("KCell", Att().add(Sort.class, Sort("KCell"))), cell("", intToToken(1)))); - K expected = cell("", cell("", - cell("", KRewrite(KVariable("KCell", Att().add(Sort.class, Sort("KCell"))), cell("", intToToken(1)))))); - Assert.assertEquals(expected, pass.concretizeCell(term)); - } - - @Test - public void testEmptySide() { - K term = cell("", cell(""), KRewrite(cell(""), cells())); - K expected = cell("", cell("", cell("", cell(""), KRewrite(cell(""), cells())))); - Assert.assertEquals(expected, pass.concretizeCell(term)); - } - - @Test - public void testTwoRewritesFit() { - K term = cell("", KRewrite(cells(), cell("", intToToken(1))), - KRewrite(cell("", intToToken(2)), cells())); - K expected = cell("", cell("", cell("", - KRewrite(cells(), cell("", intToToken(1))), - KRewrite(cell("", intToToken(2)), cells())))); - Assert.assertEquals(expected, pass.concretizeCell(term)); - } - - @Test - public void testThreeRewritesSplit() { - K term = cell("", - KRewrite(cells(cell(""),cell("")), cells()), - KRewrite(cell(""), cell("")), - KRewrite(cell(""), cell(""))); - K expected = cell("", cell("", - cell("", KRewrite(cells(cell(""),cell("")), cells())), + cell( + "", + KRewrite(cells(cell("", intToToken(2)), cell("")), cell(""))))); + Assert.assertEquals(expected, pass.concretizeCell(term)); + } + + @Test + public void testRewriteWithCellVariable() { + K term = + cell( + "", + KRewrite( + KVariable("KCell", Att().add(Sort.class, Sort("KCell"))), + cell("", intToToken(1)))); + K expected = + cell( + "", + cell( + "", + cell( + "", + KRewrite( + KVariable("KCell", Att().add(Sort.class, Sort("KCell"))), + cell("", intToToken(1)))))); + Assert.assertEquals(expected, pass.concretizeCell(term)); + } + + @Test + public void testEmptySide() { + K term = cell("", cell(""), KRewrite(cell(""), cells())); + K expected = + cell("", cell("", cell("", cell(""), KRewrite(cell(""), cells())))); + Assert.assertEquals(expected, pass.concretizeCell(term)); + } + + @Test + public void testTwoRewritesFit() { + K term = + cell( + "", + KRewrite(cells(), cell("", intToToken(1))), + KRewrite(cell("", intToToken(2)), cells())); + K expected = + cell( + "", + cell( + "", + cell( + "", + KRewrite(cells(), cell("", intToToken(1))), + KRewrite(cell("", intToToken(2)), cells())))); + Assert.assertEquals(expected, pass.concretizeCell(term)); + } + + @Test + public void testThreeRewritesSplit() { + K term = + cell( + "", + KRewrite(cells(cell(""), cell("")), cells()), + KRewrite(cell(""), cell("")), + KRewrite(cell(""), cell(""))); + K expected = + cell( + "", + cell( + "", + cell("", KRewrite(cells(cell(""), cell("")), cells())), cell("", KRewrite(cell(""), cell(""))), cell("", KRewrite(cell(""), cell(""))))); - Assert.assertEquals(expected, pass.concretizeCell(term)); - } + Assert.assertEquals(expected, pass.concretizeCell(term)); + } - @Test - public void testDotsApart() { - K term = cell("", true, false, cell("", intToToken(1)), cell("", intToToken(2))); - K expected = cell("", true, true, cell("", true, true, + @Test + public void testDotsApart() { + K term = cell("", true, false, cell("", intToToken(1)), cell("", intToToken(2))); + K expected = + cell( + "", + true, + true, + cell( + "", + true, + true, cell("", true, true, cell("", intToToken(1))), cell("", true, true, cell("", intToToken(2))))); - Assert.assertEquals(expected, pass.concretizeCell(term)); - } - - @Test - public void testDotsTogether() { - K term = cell("", true, false, cell("", intToToken(0)), cell("",intToToken(2))); - K expected = cell("", true, true, cell("", true, true, - cell("", intToToken(0)), cell("",intToToken(2)))); - Assert.assertEquals(expected, pass.concretizeCell(term)); - } - - @Test - public void testNestedCompletion() { - K term = cell("", - cell("", cell("", intToToken(0)), cell("", intToToken(1))), - cell("", intToToken(2)), - cell("", intToToken(3)), - cell("", intToToken(4)), - cell("", intToToken(5)), - cell("", cell("", intToToken(6)))); - K expected = cell("",cell("", - cell("", cell("", intToToken(0)), cell("", cell("", intToToken(1)))), + Assert.assertEquals(expected, pass.concretizeCell(term)); + } + + @Test + public void testDotsTogether() { + K term = cell("", true, false, cell("", intToToken(0)), cell("", intToToken(2))); + K expected = + cell( + "", + true, + true, + cell("", true, true, cell("", intToToken(0)), cell("", intToToken(2)))); + Assert.assertEquals(expected, pass.concretizeCell(term)); + } + + @Test + public void testNestedCompletion() { + K term = + cell( + "", + cell("", cell("", intToToken(0)), cell("", intToToken(1))), + cell("", intToToken(2)), + cell("", intToToken(3)), + cell("", intToToken(4)), + cell("", intToToken(5)), + cell("", cell("", intToToken(6)))); + K expected = + cell( + "", + cell( + "", + cell( + "", + cell("", intToToken(0)), + cell("", cell("", intToToken(1)))), cell("", cell("", intToToken(6))), - cell("", cell("", intToToken(2)), cell("", intToToken(3)), + cell( + "", + cell("", intToToken(2)), + cell("", intToToken(3)), cell("", cell("", intToToken(4))), - cell("", cell("", intToToken(5)))) - )); - Assert.assertEquals(expected, pass.concretize(term)); - - } - - @Test - public void testLeafContent() { - K term = cell("", cell("", - KSequence(KApply(KLabel("_+_"), KVariable("I"), KVariable("J")), - KVariable("Rest")))); - K expected = cell("", cell("", cell("", cell("", - KSequence(KApply(KLabel("_+_"), KVariable("I"), KVariable("J")), - KVariable("Rest")))))); - Assert.assertEquals(expected, pass.concretize(term)); - } - - @Test - public void testNonCellItem() { - K term = cell("", KApply(KLabel(".K")), cell("",KVariable("X"))); - K expected = cell("",cells(KApply(KLabel(".K")), cell("", cell("", cell("", KVariable("X")))))); - Assert.assertEquals(expected, pass.concretize(term)); - } - - @Test - public void testNonCellItemRewrite() { - K term = cell("", KRewrite(KApply(KLabel("label")),cells(KApply(KLabel(".K")), cell("",KVariable("X"))))); - exception.expect(KEMException.class); - exception.expectMessage("Can't mix items with different parent cells under a rewrite"); - pass.concretize(term); - } - - KApply cell(String name, K... ks) { - return cell(name, false, false, ks); - } - KApply cell(String name, boolean openLeft, boolean openRight, K... ks) { - return IncompleteCellUtils.make(KLabel(name), openLeft, Arrays.asList(ks), openRight); - } - - KApply cells(K... ks) { - return KApply(KLabels.CELLS, ks); - } + cell("", cell("", intToToken(5)))))); + Assert.assertEquals(expected, pass.concretize(term)); + } + + @Test + public void testLeafContent() { + K term = + cell( + "", + cell( + "", + KSequence( + KApply(KLabel("_+_"), KVariable("I"), KVariable("J")), KVariable("Rest")))); + K expected = + cell( + "", + cell( + "", + cell( + "", + cell( + "", + KSequence( + KApply(KLabel("_+_"), KVariable("I"), KVariable("J")), + KVariable("Rest")))))); + Assert.assertEquals(expected, pass.concretize(term)); + } + + @Test + public void testNonCellItem() { + K term = cell("", KApply(KLabel(".K")), cell("", KVariable("X"))); + K expected = + cell( + "", + cells(KApply(KLabel(".K")), cell("", cell("", cell("", KVariable("X")))))); + Assert.assertEquals(expected, pass.concretize(term)); + } + + @Test + public void testNonCellItemRewrite() { + K term = + cell( + "", + KRewrite( + KApply(KLabel("label")), cells(KApply(KLabel(".K")), cell("", KVariable("X"))))); + exception.expect(KEMException.class); + exception.expectMessage("Can't mix items with different parent cells under a rewrite"); + pass.concretize(term); + } + + KApply cell(String name, K... ks) { + return cell(name, false, false, ks); + } + + KApply cell(String name, boolean openLeft, boolean openRight, K... ks) { + return IncompleteCellUtils.make(KLabel(name), openLeft, Arrays.asList(ks), openRight); + } + + KApply cells(K... ks) { + return KApply(KLabels.CELLS, ks); + } } diff --git a/kernel/src/test/java/org/kframework/kore/compile/CloseCellsTest.java b/kernel/src/test/java/org/kframework/kore/compile/CloseCellsTest.java index b3a02543a55..7bf6cda9d5c 100644 --- a/kernel/src/test/java/org/kframework/kore/compile/CloseCellsTest.java +++ b/kernel/src/test/java/org/kframework/kore/compile/CloseCellsTest.java @@ -1,122 +1,164 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.kore.KORE.*; + +import java.util.Arrays; +import org.junit.Assert; import org.junit.Rule; +import org.junit.Test; import org.junit.rules.ExpectedException; import org.kframework.builtin.KLabels; import org.kframework.builtin.Sorts; -import org.kframework.compile.LabelInfo; import org.kframework.kore.*; - -import org.junit.Test; -import org.junit.Assert; import org.kframework.utils.errorsystem.KEMException; -import java.util.Arrays; - -import static org.kframework.kore.KORE.*; - public class CloseCellsTest { - final SortInfo sortInfo = new SortInfo() {{ - addOp(Sort("Map"), "_Map_"); - addOp(Sort("List"), "_List_"); - }}; - final TestConfiguration cfgInfo = new TestConfiguration() {{ - addCell(null, "ThreadCell", "", Multiplicity.STAR); - addCell("ThreadCell", "KCell", "", Sorts.K()); - addCell("ThreadCell", "EnvCell", "", Sort("Map")); - addCell(null, "ListCell", "", Multiplicity.STAR, Sort("List")); - addDefault("EnvCell", cell("", KApply(KLabel(".Map")))); - addDefault("KCell", cell("", stringToToken("defaultK"))); - }}; - final LabelInfo labelInfo = new LabelInfo() {{ - addLabel(Sort("KCell"), ""); - addLabel(Sort("EnvCell"), ""); - addLabel(Sort("ThreadCell"), ""); - addLabel(Sort("ListCell"), ""); - addLabel(Sort("Map"), "_Map_", true, true, true); - addLabel(Sort("List"), "_List_", true, false, true); - }}; - - @Test - public void testSimpleClosure() { - K term = cell("", false, true, KApply(KLabel("_+_"), KVariable("I"), KVariable("J"))); - K expected = ccell("", KSequence(KApply(KLabel("_+_"), KVariable("I"), KVariable("J")), + final SortInfo sortInfo = + new SortInfo() { + { + addOp(Sort("Map"), "_Map_"); + addOp(Sort("List"), "_List_"); + } + }; + final TestConfiguration cfgInfo = + new TestConfiguration() { + { + addCell(null, "ThreadCell", "", Multiplicity.STAR); + addCell("ThreadCell", "KCell", "", Sorts.K()); + addCell("ThreadCell", "EnvCell", "", Sort("Map")); + addCell(null, "ListCell", "", Multiplicity.STAR, Sort("List")); + addDefault("EnvCell", cell("", KApply(KLabel(".Map")))); + addDefault("KCell", cell("", stringToToken("defaultK"))); + } + }; + final LabelInfo labelInfo = + new LabelInfo() { + { + addLabel(Sort("KCell"), ""); + addLabel(Sort("EnvCell"), ""); + addLabel(Sort("ThreadCell"), ""); + addLabel(Sort("ListCell"), ""); + addLabel(Sort("Map"), "_Map_", true, true, true); + addLabel(Sort("List"), "_List_", true, false, true); + } + }; + + @Test + public void testSimpleClosure() { + K term = cell("", false, true, KApply(KLabel("_+_"), KVariable("I"), KVariable("J"))); + K expected = + ccell( + "", + KSequence( + KApply(KLabel("_+_"), KVariable("I"), KVariable("J")), KVariable("_DotVar0"))); + Assert.assertEquals(expected, new CloseCells(cfgInfo, sortInfo, labelInfo).close(term)); + } + + @Test + public void testCloseMap() { + K term = cell("", true, false, KApply(KLabel("'_|=>_"), intToToken(1), intToToken(2))); + K expected = + ccell( + "", + KApply( + KLabel("_Map_"), + KApply(KLabel("'_|=>_"), intToToken(1), intToToken(2)), KVariable("_DotVar0"))); - Assert.assertEquals(expected, new CloseCells(cfgInfo, sortInfo, labelInfo).close(term)); - } - - @Test - public void testCloseMap() { - K term = cell("", true, false, KApply(KLabel("'_|=>_"), intToToken(1), intToToken(2))); - K expected = ccell("", KApply(KLabel("_Map_"), KApply(KLabel("'_|=>_"), intToToken(1), intToToken(2)), KVariable("_DotVar0"))); - Assert.assertEquals(expected, new CloseCells(cfgInfo, sortInfo, labelInfo).close(term)); - } - - @Test - public void testCloseList() { - K term = KApply(KLabels.CELLS, - cell("", true, false, intToToken(1)), - cell("", false, true, intToToken(2)), - cell("", true, true, intToToken(3))); - K expected = KApply(KLabels.CELLS, - ccell("", KApply(KLabel("_List_"), KVariable("_DotVar0"), intToToken(1))), - ccell("", KApply(KLabel("_List_"), intToToken(2), KVariable("_DotVar1"))), - ccell("", KApply(KLabel("_List_"), KVariable("_DotVar2"), KApply(KLabel("_List_"), intToToken(3), KVariable("_DotVar3"))))); - Assert.assertEquals(expected, new CloseCells(cfgInfo, sortInfo, labelInfo).close(term)); - } - - @Test - public void testCloseCellVar() { - K term = KApply(KLabels.CELLS, + Assert.assertEquals(expected, new CloseCells(cfgInfo, sortInfo, labelInfo).close(term)); + } + + @Test + public void testCloseList() { + K term = + KApply( + KLabels.CELLS, + cell("", true, false, intToToken(1)), + cell("", false, true, intToToken(2)), + cell("", true, true, intToToken(3))); + K expected = + KApply( + KLabels.CELLS, + ccell("", KApply(KLabel("_List_"), KVariable("_DotVar0"), intToToken(1))), + ccell("", KApply(KLabel("_List_"), intToToken(2), KVariable("_DotVar1"))), + ccell( + "", + KApply( + KLabel("_List_"), + KVariable("_DotVar2"), + KApply(KLabel("_List_"), intToToken(3), KVariable("_DotVar3"))))); + Assert.assertEquals(expected, new CloseCells(cfgInfo, sortInfo, labelInfo).close(term)); + } + + @Test + public void testCloseCellVar() { + K term = + KApply( + KLabels.CELLS, + cell("", true, false, cell("", intToToken(1))), + cell("", false, true, cell("", intToToken(2))), + cell("", true, true, cell("", intToToken(2)))); + K expected = + KApply( + KLabels.CELLS, + ccell("", ccell("", intToToken(1)), KVariable("_DotVar0")), + ccell("", ccell("", intToToken(2)), KVariable("_DotVar1")), + ccell("", ccell("", intToToken(2)), KVariable("_DotVar2"))); + Assert.assertEquals(expected, new CloseCells(cfgInfo, sortInfo, labelInfo).close(term)); + } + + @Rule public ExpectedException exception = ExpectedException.none(); + + @Test + public void testClosedCellError1() { + K term = cell("", cell("")); + exception.expect(KEMException.class); + exception.expectMessage("Closed parent cell missing required children [EnvCell]"); + new CloseCells(cfgInfo, sortInfo, labelInfo).close(term); + } + + @Test + public void testCloseCellTerm() { + K term = + KRewrite( + cells(), + cells( cell("", true, false, cell("", intToToken(1))), cell("", false, true, cell("", intToToken(2))), - cell("", true, true, cell("", intToToken(2)))); - K expected = KApply(KLabels.CELLS, - ccell("", ccell("", intToToken(1)), KVariable("_DotVar0")), - ccell("", ccell("", intToToken(2)), KVariable("_DotVar1")), - ccell("", ccell("", intToToken(2)), KVariable("_DotVar2"))); - Assert.assertEquals(expected, new CloseCells(cfgInfo, sortInfo, labelInfo).close(term)); - } - - @Rule - public ExpectedException exception = ExpectedException.none(); - - @Test - public void testClosedCellError1() { - K term = cell("", cell("")); - exception.expect(KEMException.class); - exception.expectMessage("Closed parent cell missing required children [EnvCell]"); - new CloseCells(cfgInfo, sortInfo, labelInfo).close(term); - } - - @Test - public void testCloseCellTerm() { - K term = KRewrite(cells(), - cells(cell("", true, false, cell("", intToToken(1))), - cell("", false, true, cell("", intToToken(2))), - cell("", true, true, cell("", intToToken(2))))); - K expected = KRewrite(cells(), - cells(ccell("", ccell("", intToToken(1)), ccell("", KApply(KLabel(".Map")))), - ccell("", ccell("", intToToken(2)), ccell("", KApply(KLabel(".Map")))), - ccell("", ccell("", intToToken(2)), ccell("", stringToToken("defaultK"))))); - Assert.assertEquals(expected, new CloseCells(cfgInfo, sortInfo, labelInfo).close(term)); - } - - KApply cell(String name, K... ks) { - return cell(name, false, false, ks); - } - KApply cell(String name, boolean openLeft, boolean openRight, K... ks) { - return IncompleteCellUtils.make(KLabel(name), openLeft, Arrays.asList(ks), openRight); - } - - KApply ccell(String name, K... ks) { - return KApply(KLabel(name), ks); - } - - - KApply cells(K... ks) { - return KApply(KLabels.CELLS, ks); - } + cell("", true, true, cell("", intToToken(2))))); + K expected = + KRewrite( + cells(), + cells( + ccell( + "", + ccell("", intToToken(1)), + ccell("", KApply(KLabel(".Map")))), + ccell( + "", + ccell("", intToToken(2)), + ccell("", KApply(KLabel(".Map")))), + ccell( + "", + ccell("", intToToken(2)), + ccell("", stringToToken("defaultK"))))); + Assert.assertEquals(expected, new CloseCells(cfgInfo, sortInfo, labelInfo).close(term)); + } + + KApply cell(String name, K... ks) { + return cell(name, false, false, ks); + } + + KApply cell(String name, boolean openLeft, boolean openRight, K... ks) { + return IncompleteCellUtils.make(KLabel(name), openLeft, Arrays.asList(ks), openRight); + } + + KApply ccell(String name, K... ks) { + return KApply(KLabel(name), ks); + } + + KApply cells(K... ks) { + return KApply(KLabels.CELLS, ks); + } } diff --git a/kernel/src/test/java/org/kframework/kore/compile/GenerateSentencesFromConfigDeclTest.java b/kernel/src/test/java/org/kframework/kore/compile/GenerateSentencesFromConfigDeclTest.java index 0d0da8fd386..a81b8d8d54e 100644 --- a/kernel/src/test/java/org/kframework/kore/compile/GenerateSentencesFromConfigDeclTest.java +++ b/kernel/src/test/java/org/kframework/kore/compile/GenerateSentencesFromConfigDeclTest.java @@ -1,7 +1,16 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.junit.Assert.*; +import static org.kframework.Collections.*; +import static org.kframework.definition.Constructors.*; +import static org.kframework.kore.KORE.*; + import com.google.common.collect.Lists; +import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; import org.junit.Before; import org.junit.Test; import org.kframework.attributes.Att; @@ -24,146 +33,290 @@ import org.kframework.utils.file.FileUtil; import scala.collection.Set; -import java.io.File; -import java.util.Arrays; -import java.util.Collections; -import java.util.Map; - -import static org.junit.Assert.*; -import static org.kframework.Collections.*; -import static org.kframework.definition.Constructors.*; -import static org.kframework.kore.KORE.*; - public class GenerateSentencesFromConfigDeclTest { - Definition def; - FileUtil files; + Definition def; + FileUtil files; - @Before - public void setUp() { - String definitionText; - files = FileUtil.testFileUtil(); - ParserUtils parser = new ParserUtils(files, new KExceptionManager(new GlobalOptions())); - File definitionFile = new File(Kompile.BUILTIN_DIRECTORY + "/prelude.md"); - definitionText = files.loadFromWorkingDirectory(definitionFile.getPath()); + @Before + public void setUp() { + String definitionText; + files = FileUtil.testFileUtil(); + ParserUtils parser = new ParserUtils(files, new KExceptionManager(new GlobalOptions())); + File definitionFile = new File(Kompile.BUILTIN_DIRECTORY + "/prelude.md"); + definitionText = files.loadFromWorkingDirectory(definitionFile.getPath()); - def = - parser.loadDefinition("K", "K", definitionText, - definitionFile, - definitionFile.getParentFile(), - Lists.newArrayList(Kompile.BUILTIN_DIRECTORY), - false, false, false); - } + def = + parser.loadDefinition( + "K", + "K", + definitionText, + definitionFile, + definitionFile.getParentFile(), + Lists.newArrayList(Kompile.BUILTIN_DIRECTORY), + false, + false, + false); + } - @Test - public void testSingleTop() { - Production prod = Production(KLabel(".Opt"), Sort("OptCellContent"), Seq(Terminal(""))); - Production prod2 = Production(KLabel("#SemanticCastToKItem"), Sort("KItem"), Seq(NonTerminal(Sort("KItem")))); - K configuration = cell("threads", Collections.emptyMap(), - cell("thread", Collections.singletonMap("multiplicity", "*"), - cells(cell("k", Collections.emptyMap(), KApply(KLabel("#SemanticCastToKItem"), KList(KToken("$PGM", Sorts.KConfigVar())), Att.empty().add(Production.class, prod2))), - cell("opt", Collections.singletonMap("multiplicity", "?"), - KApply(KLabel(".Opt"), KList(), Att.empty().add(Production.class, prod)))))); - Module m1 = Module("CONFIG", Set(Import(def.getModule("KSEQ").get(), true)), Set(prod), Att()); - RuleGrammarGenerator parserGen = new RuleGrammarGenerator(def); - Module m = RuleGrammarGenerator.getCombinedGrammar(parserGen.getConfigGrammar(m1), true, files).getExtensionModule(); - Set gen = GenerateSentencesFromConfigDecl.gen(configuration, BooleanUtils.FALSE, Att(), m); - Att initializerAtts = Att().add(Att.INITIALIZER()); - Att productionAtts = initializerAtts.add(Att.FUNCTION()); - Set reference = Set(Production(KLabel(""), Sort("ThreadsCell"), - Seq(Terminal(""), NonTerminal(Sort("ThreadCellBag")), Terminal("")), - Att().add(Att.CELL()).add(Att.CELL_NAME(), "threads").add(Att.FORMAT(), "%1%i%n%2%d%n%3")), - SyntaxSort(Seq(), Sort("ThreadCellBag"), Att().add(Att.HOOK(), "BAG.Bag").add(Att.CELL_COLLECTION())), - Production(KLabel("_ThreadCellBag_"), Sort("ThreadCellBag"), - Seq(NonTerminal(Sort("ThreadCellBag")), NonTerminal(Sort("ThreadCellBag"))), - Att().add(Att.ASSOC(),"").add(Att.COMM(),"").add(Att.UNIT(),".ThreadCellBag") - .add(Att.ELEMENT(),"ThreadCellBagItem").add(Att.WRAP_ELEMENT(),"") - .add(Att.FUNCTION()).add(Att.AVOID()).add(Att.BAG()).add(Att.CELL_COLLECTION()).add(Att.HOOK(),"BAG.concat")), - Production(KLabel(".ThreadCellBag"), Sort("ThreadCellBag"), - Seq(Terminal(".ThreadCellBag")), - Att().add(Att.FUNCTION()).add(Att.HOOK(),"BAG.unit")), - Production(Seq(), Sort("ThreadCellBag"), - Seq(NonTerminal(Sort("ThreadCell")))), - Production(KLabel("ThreadCellBagItem"), Sort("ThreadCellBag"), - Seq(Terminal("ThreadCellBagItem"), Terminal("("), NonTerminal(Sort("ThreadCell")), Terminal(")")), - Att().add(Att.FUNCTION()).add(Att.HOOK(),"BAG.element").add(Att.FORMAT(), "%3")), - Production(KLabel(""), Sort("ThreadCell"), - Seq(Terminal(""), NonTerminal(Sort("KCell")), NonTerminal(Sort("OptCell")), Terminal("")), - Att().add(Att.CELL()).add(Att.CELL_NAME(), "thread").add(Att.MULTIPLICITY(),"*").add(Att.FORMAT(), "%1%i%n%2%n%3%d%n%4")), - Production(KLabel(""), Sort("KCell"), - Seq(Terminal(""), NonTerminal(Sort("K")), Terminal("")), - Att().add(Att.CELL()).add(Att.CELL_NAME(), "k").add(Att.MAINCELL()).add(Att.FORMAT(), "%1%i%n%2%d%n%3")), - Production(KLabel(""), Sort("OptCell"), - Seq(Terminal(""), NonTerminal(Sort("OptCellContent")), Terminal("")), - Att().add(Att.CELL()).add(Att.CELL_NAME(), "opt").add(Att.MULTIPLICITY(),"?").add(Att.UNIT(),".OptCell").add(Att.FORMAT(), "%1%i%n%2%d%n%3")), - Production(KLabel(".OptCell"), Sort("OptCell"), - Seq(Terminal(".OptCell"))), - Production(KLabel("initThreadsCell"), Sort("ThreadsCell"), - Seq(Terminal("initThreadsCell"), Terminal("("), NonTerminal(Sort("Map")), Terminal(")")), - productionAtts), - Production(KLabel("initThreadCell"), Sort("ThreadCellBag"), - Seq(Terminal("initThreadCell"), Terminal("("), NonTerminal(Sort("Map")), Terminal(")")), - productionAtts), - Production(KLabel("initKCell"), Sort("KCell"), - Seq(Terminal("initKCell"), Terminal("("), NonTerminal(Sort("Map")), Terminal(")")), - productionAtts), - Production(KLabel("initOptCell"), Sort("OptCell"), - Seq(Terminal("initOptCell")), - productionAtts), - Rule(KRewrite(KApply(KLabel("initThreadsCell"), KVariable("Init")), - IncompleteCellUtils.make(KLabel(""), false, - KApply(KLabel("initThreadCell"), KVariable("Init")), false)), - BooleanUtils.TRUE, BooleanUtils.FALSE, initializerAtts), - Rule(KRewrite(KApply(KLabel("initThreadCell"), KVariable("Init")), - IncompleteCellUtils.make(KLabel(""), false, - Arrays.asList(KApply(KLabel("initKCell"), KVariable("Init")), - KApply(KLabels.CELLS)), false)), - BooleanUtils.TRUE, BooleanUtils.TRUE, initializerAtts), - Rule(KRewrite(KApply(KLabel("initKCell"), KVariable("Init")), - IncompleteCellUtils.make(KLabel(""), false, KApply(KLabel("#SemanticCastToKItem"), KApply(KLabel("project:KItem"), KApply(KLabel("Map:lookup"), - KVariable("Init"), - KToken("$PGM", Sorts.KConfigVar())))), false)), - BooleanUtils.TRUE, BooleanUtils.TRUE, initializerAtts), - Rule(KRewrite(KApply(KLabel("initOptCell")), - IncompleteCellUtils.make(KLabel(""), false, KApply(KLabel(".Opt")), false)), - BooleanUtils.TRUE, BooleanUtils.TRUE, initializerAtts), - Production(KLabel("-fragment"), Sort("ThreadsCellFragment"), - Seq(Terminal("-fragment"),NonTerminal(Sort("ThreadCellBag")),Terminal("-fragment")), - Att().add(Att.CELL_FRAGMENT(),Sort.class,Sort("ThreadsCell"))), - Production(KLabel("-fragment"), Sort("ThreadCellFragment"), - Seq(Terminal("-fragment"),NonTerminal(Sort("KCellOpt")),NonTerminal(Sort("OptCellOpt")),Terminal("-fragment")), - Att().add(Att.CELL_FRAGMENT(),Sort.class,Sort("ThreadCell"))), - Production(Seq(), Sort("OptCellOpt"), Seq(NonTerminal(Sort("OptCell")))), - Production(KLabel("noOptCell"), Sort("OptCellOpt"), Seq(Terminal("noOptCell")),Att().add(Att.CELL_OPT_ABSENT(), Sort.class, Sort("OptCell"))), - Production(Seq(), Sort("KCellOpt"), Seq(NonTerminal(Sort("KCell")))), - Production(KLabel("noKCell"), Sort("KCellOpt"), Seq(Terminal("noKCell")),Att().add(Att.CELL_OPT_ABSENT(), Sort.class, Sort("KCell"))) - ); + @Test + public void testSingleTop() { + Production prod = Production(KLabel(".Opt"), Sort("OptCellContent"), Seq(Terminal(""))); + Production prod2 = + Production(KLabel("#SemanticCastToKItem"), Sort("KItem"), Seq(NonTerminal(Sort("KItem")))); + K configuration = + cell( + "threads", + Collections.emptyMap(), + cell( + "thread", + Collections.singletonMap("multiplicity", "*"), + cells( + cell( + "k", + Collections.emptyMap(), + KApply( + KLabel("#SemanticCastToKItem"), + KList(KToken("$PGM", Sorts.KConfigVar())), + Att.empty().add(Production.class, prod2))), + cell( + "opt", + Collections.singletonMap("multiplicity", "?"), + KApply( + KLabel(".Opt"), KList(), Att.empty().add(Production.class, prod)))))); + Module m1 = Module("CONFIG", Set(Import(def.getModule("KSEQ").get(), true)), Set(prod), Att()); + RuleGrammarGenerator parserGen = new RuleGrammarGenerator(def); + Module m = + RuleGrammarGenerator.getCombinedGrammar(parserGen.getConfigGrammar(m1), true, files) + .getExtensionModule(); + Set gen = + GenerateSentencesFromConfigDecl.gen(configuration, BooleanUtils.FALSE, Att(), m); + Att initializerAtts = Att().add(Att.INITIALIZER()); + Att productionAtts = initializerAtts.add(Att.FUNCTION()); + Set reference = + Set( + Production( + KLabel(""), + Sort("ThreadsCell"), + Seq( + Terminal(""), + NonTerminal(Sort("ThreadCellBag")), + Terminal("")), + Att() + .add(Att.CELL()) + .add(Att.CELL_NAME(), "threads") + .add(Att.FORMAT(), "%1%i%n%2%d%n%3")), + SyntaxSort( + Seq(), + Sort("ThreadCellBag"), + Att().add(Att.HOOK(), "BAG.Bag").add(Att.CELL_COLLECTION())), + Production( + KLabel("_ThreadCellBag_"), + Sort("ThreadCellBag"), + Seq(NonTerminal(Sort("ThreadCellBag")), NonTerminal(Sort("ThreadCellBag"))), + Att() + .add(Att.ASSOC(), "") + .add(Att.COMM(), "") + .add(Att.UNIT(), ".ThreadCellBag") + .add(Att.ELEMENT(), "ThreadCellBagItem") + .add(Att.WRAP_ELEMENT(), "") + .add(Att.FUNCTION()) + .add(Att.AVOID()) + .add(Att.BAG()) + .add(Att.CELL_COLLECTION()) + .add(Att.HOOK(), "BAG.concat")), + Production( + KLabel(".ThreadCellBag"), + Sort("ThreadCellBag"), + Seq(Terminal(".ThreadCellBag")), + Att().add(Att.FUNCTION()).add(Att.HOOK(), "BAG.unit")), + Production(Seq(), Sort("ThreadCellBag"), Seq(NonTerminal(Sort("ThreadCell")))), + Production( + KLabel("ThreadCellBagItem"), + Sort("ThreadCellBag"), + Seq( + Terminal("ThreadCellBagItem"), + Terminal("("), + NonTerminal(Sort("ThreadCell")), + Terminal(")")), + Att().add(Att.FUNCTION()).add(Att.HOOK(), "BAG.element").add(Att.FORMAT(), "%3")), + Production( + KLabel(""), + Sort("ThreadCell"), + Seq( + Terminal(""), + NonTerminal(Sort("KCell")), + NonTerminal(Sort("OptCell")), + Terminal("")), + Att() + .add(Att.CELL()) + .add(Att.CELL_NAME(), "thread") + .add(Att.MULTIPLICITY(), "*") + .add(Att.FORMAT(), "%1%i%n%2%n%3%d%n%4")), + Production( + KLabel(""), + Sort("KCell"), + Seq(Terminal(""), NonTerminal(Sort("K")), Terminal("")), + Att() + .add(Att.CELL()) + .add(Att.CELL_NAME(), "k") + .add(Att.MAINCELL()) + .add(Att.FORMAT(), "%1%i%n%2%d%n%3")), + Production( + KLabel(""), + Sort("OptCell"), + Seq(Terminal(""), NonTerminal(Sort("OptCellContent")), Terminal("")), + Att() + .add(Att.CELL()) + .add(Att.CELL_NAME(), "opt") + .add(Att.MULTIPLICITY(), "?") + .add(Att.UNIT(), ".OptCell") + .add(Att.FORMAT(), "%1%i%n%2%d%n%3")), + Production(KLabel(".OptCell"), Sort("OptCell"), Seq(Terminal(".OptCell"))), + Production( + KLabel("initThreadsCell"), + Sort("ThreadsCell"), + Seq( + Terminal("initThreadsCell"), + Terminal("("), + NonTerminal(Sort("Map")), + Terminal(")")), + productionAtts), + Production( + KLabel("initThreadCell"), + Sort("ThreadCellBag"), + Seq( + Terminal("initThreadCell"), + Terminal("("), + NonTerminal(Sort("Map")), + Terminal(")")), + productionAtts), + Production( + KLabel("initKCell"), + Sort("KCell"), + Seq(Terminal("initKCell"), Terminal("("), NonTerminal(Sort("Map")), Terminal(")")), + productionAtts), + Production( + KLabel("initOptCell"), + Sort("OptCell"), + Seq(Terminal("initOptCell")), + productionAtts), + Rule( + KRewrite( + KApply(KLabel("initThreadsCell"), KVariable("Init")), + IncompleteCellUtils.make( + KLabel(""), + false, + KApply(KLabel("initThreadCell"), KVariable("Init")), + false)), + BooleanUtils.TRUE, + BooleanUtils.FALSE, + initializerAtts), + Rule( + KRewrite( + KApply(KLabel("initThreadCell"), KVariable("Init")), + IncompleteCellUtils.make( + KLabel(""), + false, + Arrays.asList( + KApply(KLabel("initKCell"), KVariable("Init")), KApply(KLabels.CELLS)), + false)), + BooleanUtils.TRUE, + BooleanUtils.TRUE, + initializerAtts), + Rule( + KRewrite( + KApply(KLabel("initKCell"), KVariable("Init")), + IncompleteCellUtils.make( + KLabel(""), + false, + KApply( + KLabel("#SemanticCastToKItem"), + KApply( + KLabel("project:KItem"), + KApply( + KLabel("Map:lookup"), + KVariable("Init"), + KToken("$PGM", Sorts.KConfigVar())))), + false)), + BooleanUtils.TRUE, + BooleanUtils.TRUE, + initializerAtts), + Rule( + KRewrite( + KApply(KLabel("initOptCell")), + IncompleteCellUtils.make( + KLabel(""), false, KApply(KLabel(".Opt")), false)), + BooleanUtils.TRUE, + BooleanUtils.TRUE, + initializerAtts), + Production( + KLabel("-fragment"), + Sort("ThreadsCellFragment"), + Seq( + Terminal("-fragment"), + NonTerminal(Sort("ThreadCellBag")), + Terminal("-fragment")), + Att().add(Att.CELL_FRAGMENT(), Sort.class, Sort("ThreadsCell"))), + Production( + KLabel("-fragment"), + Sort("ThreadCellFragment"), + Seq( + Terminal("-fragment"), + NonTerminal(Sort("KCellOpt")), + NonTerminal(Sort("OptCellOpt")), + Terminal("-fragment")), + Att().add(Att.CELL_FRAGMENT(), Sort.class, Sort("ThreadCell"))), + Production(Seq(), Sort("OptCellOpt"), Seq(NonTerminal(Sort("OptCell")))), + Production( + KLabel("noOptCell"), + Sort("OptCellOpt"), + Seq(Terminal("noOptCell")), + Att().add(Att.CELL_OPT_ABSENT(), Sort.class, Sort("OptCell"))), + Production(Seq(), Sort("KCellOpt"), Seq(NonTerminal(Sort("KCell")))), + Production( + KLabel("noKCell"), + Sort("KCellOpt"), + Seq(Terminal("noKCell")), + Att().add(Att.CELL_OPT_ABSENT(), Sort.class, Sort("KCell")))); - assertEquals("Produced unexpected productions", Set(), gen.$amp$tilde(reference)); - assertEquals("Missing expected productions", Set(), reference.$amp$tilde(gen)); - // Production.equals ignores attributes, but they are important here - nextgen: - for (Sentence g : iterable(gen)) { - for (Sentence r : iterable(reference)) { - if (g.equals(r)) { - assertEquals("Production "+r+" generated with incorrect attributes", r.att(), g.att()); - continue nextgen; - } - } - assert false; // We checked set equality above + assertEquals("Produced unexpected productions", Set(), gen.$amp$tilde(reference)); + assertEquals("Missing expected productions", Set(), reference.$amp$tilde(gen)); + // Production.equals ignores attributes, but they are important here + nextgen: + for (Sentence g : iterable(gen)) { + for (Sentence r : iterable(reference)) { + if (g.equals(r)) { + assertEquals( + "Production " + r + " generated with incorrect attributes", r.att(), g.att()); + continue nextgen; } + } + assert false; // We checked set equality above } + } - private KApply cells(K cell1, K cell2) { - return KApply(KLabels.CELLS, cell1, cell2); - } + private KApply cells(K cell1, K cell2) { + return KApply(KLabels.CELLS, cell1, cell2); + } - private KApply cell(String s, Map att, K body) { - K cellAtt = att.entrySet().stream() - .map(e -> KApply(KLabel("#cellProperty"), + private KApply cell(String s, Map att, K body) { + K cellAtt = + att.entrySet().stream() + .map( + e -> + KApply( + KLabel("#cellProperty"), KToken(e.getKey(), Sort("#CellName")), KToken(StringUtil.enquoteKString(e.getValue()), Sort("KString")))) - .reduce(KApply(KLabel("#cellPropertyListTerminator")), (k1, k2) -> KApply(KLabel("#cellPropertyList"), k2, k1)); - return KApply(KLabel("#configCell"), KToken(s, Sort("#CellName")), cellAtt, body, KToken(s, Sort("#CellName"))); - } + .reduce( + KApply(KLabel("#cellPropertyListTerminator")), + (k1, k2) -> KApply(KLabel("#cellPropertyList"), k2, k1)); + return KApply( + KLabel("#configCell"), + KToken(s, Sort("#CellName")), + cellAtt, + body, + KToken(s, Sort("#CellName"))); + } } diff --git a/kernel/src/test/java/org/kframework/kore/compile/SortCellsTest.java b/kernel/src/test/java/org/kframework/kore/compile/SortCellsTest.java index e74c58df737..ab45cea4e23 100644 --- a/kernel/src/test/java/org/kframework/kore/compile/SortCellsTest.java +++ b/kernel/src/test/java/org/kframework/kore/compile/SortCellsTest.java @@ -1,6 +1,8 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.kore.KORE.*; + import org.junit.Assert; import org.junit.Test; import org.kframework.builtin.BooleanUtils; @@ -14,311 +16,513 @@ import org.kframework.main.GlobalOptions; import org.kframework.utils.errorsystem.KExceptionManager; -import static org.kframework.kore.KORE.*; - /** * Rearrange partially-completed cells to follow the productions declaring them. * - * The main complexity here is eliminating cell fragment variables that might - * capture multiple cells. In the general case a variable needs to be - * replaced under cells with separate variables in several slots of the - * parent and in other places with an appropriate bag expression. + *

The main complexity here is eliminating cell fragment variables that might capture multiple + * cells. In the general case a variable needs to be replaced under cells with separate variables in + * several slots of the parent and in other places with an appropriate bag expression. */ public class SortCellsTest { - ConfigurationInfo cfgInfo = new TestConfiguration() {{ - addCell(null, "TopCell", ""); - addCell("TopCell", "ThreadCell", "", Multiplicity.STAR); - addCell("ThreadCell", "KCell", "", Sorts.K()); - addCell("ThreadCell", "EnvCell", "", Sort("Map")); - addCell("ThreadCell", "OptCell", "", Multiplicity.OPTIONAL, Sorts.K()); - addUnit("OptCell", KLabel(".OptCell")); - addUnit("ThreadCell", KLabel(".ThreadCellBag")); - addConcat("ThreadCell", KLabel("_ThreadCellBag_")); - }}; - LabelInfo labelInfo = new LabelInfo() {{ - addLabel(Sort("TopCell"), ""); - addLabel(Sort("ThreadCell"), ""); - addLabel(Sort("KCell"), ""); - addLabel(Sort("EnvCell"), ""); - addLabel(Sort("OptCell"), ""); - }}; - - @Test - public void testSimpleSplitting() { - KVariable Y = KVariable("Y", Att().add(Sort.class, Sort("OptCell"))); - K term = KRewrite(cell("", cell(""), KVariable("X"), Y), KVariable("X")); - K expected = KRewrite(cell("", KVariable("X"), cell(""), Y), cell("-fragment", KVariable("X"), app("noEnvCell"), app(".OptCell"))); - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); - Assert.assertEquals(0, kem.getExceptions().size()); - } + ConfigurationInfo cfgInfo = + new TestConfiguration() { + { + addCell(null, "TopCell", ""); + addCell("TopCell", "ThreadCell", "", Multiplicity.STAR); + addCell("ThreadCell", "KCell", "", Sorts.K()); + addCell("ThreadCell", "EnvCell", "", Sort("Map")); + addCell("ThreadCell", "OptCell", "", Multiplicity.OPTIONAL, Sorts.K()); + addUnit("OptCell", KLabel(".OptCell")); + addUnit("ThreadCell", KLabel(".ThreadCellBag")); + addConcat("ThreadCell", KLabel("_ThreadCellBag_")); + } + }; + LabelInfo labelInfo = + new LabelInfo() { + { + addLabel(Sort("TopCell"), ""); + addLabel(Sort("ThreadCell"), ""); + addLabel(Sort("KCell"), ""); + addLabel(Sort("EnvCell"), ""); + addLabel(Sort("OptCell"), ""); + } + }; - /** - * Ensure that a variable does not become a cell fragment if it is annotated - * with a single-cell sort. - */ - @Test - public void testSortedVar() { - KVariable Y = KVariable("Y", Att().add(Sort.class, Sort("OptCell"))); - K term = KRewrite(cell("", cell(""), KVariable("X"), Y), Y); - K expected = KRewrite(cell("", KVariable("X"), cell(""), Y), Y); - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); - Assert.assertEquals(0, kem.getExceptions().size()); - } + @Test + public void testSimpleSplitting() { + KVariable Y = KVariable("Y", Att().add(Sort.class, Sort("OptCell"))); + K term = KRewrite(cell("", cell(""), KVariable("X"), Y), KVariable("X")); + K expected = + KRewrite( + cell("", KVariable("X"), cell(""), Y), + cell("-fragment", KVariable("X"), app("noEnvCell"), app(".OptCell"))); + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); + Assert.assertEquals(0, kem.getExceptions().size()); + } - @Test - public void testUselessVariable() { - K term = cell("", cell(""), cell(""), cell(""), KVariable("X")); - K expected = cell("", cell(""), cell(""), cell("")); - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); - Assert.assertEquals(0, kem.getExceptions().size()); - } + /** + * Ensure that a variable does not become a cell fragment if it is annotated with a single-cell + * sort. + */ + @Test + public void testSortedVar() { + KVariable Y = KVariable("Y", Att().add(Sort.class, Sort("OptCell"))); + K term = KRewrite(cell("", cell(""), KVariable("X"), Y), Y); + K expected = KRewrite(cell("", KVariable("X"), cell(""), Y), Y); + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); + Assert.assertEquals(0, kem.getExceptions().size()); + } - @Test - public void testMultipleSplit() { - K term = KRewrite(cell("", KVariable("X")), KVariable("Y")); - K expected = KRewrite(cell("", KVariable("_Gen0"), KVariable("_Gen1"), KVariable("_Gen2")), KVariable("Y")); - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); - Assert.assertEquals(0, kem.getExceptions().size()); - } + @Test + public void testUselessVariable() { + K term = cell("", cell(""), cell(""), cell(""), KVariable("X")); + K expected = cell("", cell(""), cell(""), cell("")); + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); + Assert.assertEquals(0, kem.getExceptions().size()); + } - @Test - public void testAddOptCell() { - K term = cell("", KVariable("X"), KRewrite(cells(), cell(""))); - K expected = cell("", KVariable("_Gen0"), KVariable("_Gen1"), KRewrite(KApply(KLabel(".OptCell")), cell(""))); - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); - Assert.assertEquals(0, kem.getExceptions().size()); - } + @Test + public void testMultipleSplit() { + K term = KRewrite(cell("", KVariable("X")), KVariable("Y")); + K expected = + KRewrite( + cell("", KVariable("_Gen0"), KVariable("_Gen1"), KVariable("_Gen2")), + KVariable("Y")); + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); + Assert.assertEquals(0, kem.getExceptions().size()); + } - @Test - public void testRemoveOptCell() { - K term = cell("", KVariable("X"), KRewrite(cell(""), cells())); - K expected = cell("", KVariable("_Gen0"), KVariable("_Gen1"), KRewrite(cell(""), KApply(KLabel(".OptCell")))); - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); - Assert.assertEquals(0, kem.getExceptions().size()); - } + @Test + public void testAddOptCell() { + K term = cell("", KVariable("X"), KRewrite(cells(), cell(""))); + K expected = + cell( + "", + KVariable("_Gen0"), + KVariable("_Gen1"), + KRewrite(KApply(KLabel(".OptCell")), cell(""))); + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); + Assert.assertEquals(0, kem.getExceptions().size()); + } - @Test - public void testAddStarCell() { - K term = cell("", KRewrite(cells(), cell("", KVariable("X")))); - K expected = cell("", KRewrite(KApply(KLabel(".ThreadCellBag")), cell("", KVariable("_Gen0"), KVariable("_Gen1"), KVariable("_Gen2")))); - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); - Assert.assertEquals(0, kem.getExceptions().size()); - } + @Test + public void testRemoveOptCell() { + K term = cell("", KVariable("X"), KRewrite(cell(""), cells())); + K expected = + cell( + "", + KVariable("_Gen0"), + KVariable("_Gen1"), + KRewrite(cell(""), KApply(KLabel(".OptCell")))); + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); + Assert.assertEquals(0, kem.getExceptions().size()); + } - @Test - public void testRemoveStarCell() { - K term = cell("", KRewrite(cell("", KVariable("X")), cells())); - K expected = cell("", KRewrite(cell("", KVariable("_Gen0"), KVariable("_Gen1"), KVariable("_Gen2")), KApply(KLabel(".ThreadCellBag")))); - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); - Assert.assertEquals(0, kem.getExceptions().size()); - } + @Test + public void testAddStarCell() { + K term = cell("", KRewrite(cells(), cell("", KVariable("X")))); + K expected = + cell( + "", + KRewrite( + KApply(KLabel(".ThreadCellBag")), + cell("", KVariable("_Gen0"), KVariable("_Gen1"), KVariable("_Gen2")))); + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); + Assert.assertEquals(0, kem.getExceptions().size()); + } + @Test + public void testRemoveStarCell() { + K term = cell("", KRewrite(cell("", KVariable("X")), cells())); + K expected = + cell( + "", + KRewrite( + cell("", KVariable("_Gen0"), KVariable("_Gen1"), KVariable("_Gen2")), + KApply(KLabel(".ThreadCellBag")))); + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); + Assert.assertEquals(0, kem.getExceptions().size()); + } - @Test - public void testConcatStarCell() { - K term = cell("", KRewrite(KVariable("Y"), cells(KVariable("Y"), cell("", KVariable("X"))))); - K expected = cell("", KRewrite(KVariable("Y"), KApply(KLabel("_ThreadCellBag_"), KVariable("Y"), cell("", KVariable("_Gen0"), KVariable("_Gen1"), KVariable("_Gen2"))))); - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); - Assert.assertEquals(0, kem.getExceptions().size()); - } + @Test + public void testConcatStarCell() { + K term = + cell("", KRewrite(KVariable("Y"), cells(KVariable("Y"), cell("", KVariable("X"))))); + K expected = + cell( + "", + KRewrite( + KVariable("Y"), + KApply( + KLabel("_ThreadCellBag_"), + KVariable("Y"), + cell("", KVariable("_Gen0"), KVariable("_Gen1"), KVariable("_Gen2"))))); + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); + Assert.assertEquals(0, kem.getExceptions().size()); + } - @Test - public void testConcatStarCellEmptyl() { - K term = cell("", KRewrite(KVariable("Y"), cells())); - K expected = cell("", KRewrite(KVariable("Y"), KApply(KLabel(".ThreadCellBag")))); - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); - Assert.assertEquals(0, kem.getExceptions().size()); - } + @Test + public void testConcatStarCellEmptyl() { + K term = cell("", KRewrite(KVariable("Y"), cells())); + K expected = cell("", KRewrite(KVariable("Y"), KApply(KLabel(".ThreadCellBag")))); + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); + Assert.assertEquals(0, kem.getExceptions().size()); + } - @Test - public void testFragment1() { - K term = cell("",cell("",KVariable("F")), - cell("", cell("", KRewrite(KSequence(KVariable("Rest")), KSequence(KVariable("F"), KVariable("Rest")))), KVariable("F2"))); - K expected = cell("",app("_ThreadCellBag_", + @Test + public void testFragment1() { + K term = + cell( + "", + cell("", KVariable("F")), + cell( + "", + cell( + "", + KRewrite( + KSequence(KVariable("Rest")), + KSequence(KVariable("F"), KVariable("Rest")))), + KVariable("F2"))); + K expected = + cell( + "", + app( + "_ThreadCellBag_", cell("", KVariable("_Gen0"), KVariable("_Gen1"), KVariable("_Gen2")), - cell("", cell("", KRewrite(KSequence(KVariable("Rest")), - KSequence(cell("-fragment", KVariable("_Gen0"), KVariable("_Gen1"), KVariable("_Gen2")), KVariable("Rest")))), - KVariable("_Gen3"), - KVariable("_Gen4")))); - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); - Assert.assertEquals(0, kem.getExceptions().size()); - } - @Test - public void testFragment2() { - K term = cell("",cell("",cell("",KVariable("K")),KVariable("F")), - cell("", cell("", KRewrite(KSequence(KVariable("Rest")), KSequence(KVariable("F"), KVariable("Rest")))), KVariable("F2"))); - K expected = cell("",app("_ThreadCellBag_", + cell( + "", + cell( + "", + KRewrite( + KSequence(KVariable("Rest")), + KSequence( + cell( + "-fragment", + KVariable("_Gen0"), + KVariable("_Gen1"), + KVariable("_Gen2")), + KVariable("Rest")))), + KVariable("_Gen3"), + KVariable("_Gen4")))); + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); + Assert.assertEquals(0, kem.getExceptions().size()); + } + + @Test + public void testFragment2() { + K term = + cell( + "", + cell("", cell("", KVariable("K")), KVariable("F")), + cell( + "", + cell( + "", + KRewrite( + KSequence(KVariable("Rest")), + KSequence(KVariable("F"), KVariable("Rest")))), + KVariable("F2"))); + K expected = + cell( + "", + app( + "_ThreadCellBag_", cell("", cell("", KVariable("K")), KVariable("_Gen0"), KVariable("_Gen1")), - cell("", cell("", KRewrite(KSequence(KVariable("Rest")), - KSequence(cell("-fragment", app("noKCell"), KVariable("_Gen0"), KVariable("_Gen1")), KVariable("Rest")))), - KVariable("_Gen2"), - KVariable("_Gen3")))); - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); - Assert.assertEquals(0, kem.getExceptions().size()); - } - @Test - public void testFragment3() { - K term = cell("",cell("",cell("",KVariable("O")),KVariable("F")), - cell("", cell("", KRewrite(KSequence(KVariable("Rest")), KSequence(KVariable("F"), KVariable("Rest")))), KVariable("F2"))); - K expected = cell("",app("_ThreadCellBag_", + cell( + "", + cell( + "", + KRewrite( + KSequence(KVariable("Rest")), + KSequence( + cell( + "-fragment", + app("noKCell"), + KVariable("_Gen0"), + KVariable("_Gen1")), + KVariable("Rest")))), + KVariable("_Gen2"), + KVariable("_Gen3")))); + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); + Assert.assertEquals(0, kem.getExceptions().size()); + } + + @Test + public void testFragment3() { + K term = + cell( + "", + cell("", cell("", KVariable("O")), KVariable("F")), + cell( + "", + cell( + "", + KRewrite( + KSequence(KVariable("Rest")), + KSequence(KVariable("F"), KVariable("Rest")))), + KVariable("F2"))); + K expected = + cell( + "", + app( + "_ThreadCellBag_", cell("", KVariable("_Gen0"), KVariable("_Gen1"), cell("", KVariable("O"))), - cell("", cell("", KRewrite(KSequence(KVariable("Rest")), - KSequence(cell("-fragment", KVariable("_Gen0"), KVariable("_Gen1"), app(".OptCell")), KVariable("Rest")))), - KVariable("_Gen2"), - KVariable("_Gen3")))); - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); - Assert.assertEquals(0, kem.getExceptions().size()); - } - @Test - public void testFragment4() { - K term = cell("",cell("",cell("",KVariable("E")),KVariable("F")), - cell("", cell("", KRewrite(KSequence(KVariable("Rest")), KSequence(KVariable("F"), KVariable("Rest")))), KVariable("F2"))); - K expected = cell("",app("_ThreadCellBag_", + cell( + "", + cell( + "", + KRewrite( + KSequence(KVariable("Rest")), + KSequence( + cell( + "-fragment", + KVariable("_Gen0"), + KVariable("_Gen1"), + app(".OptCell")), + KVariable("Rest")))), + KVariable("_Gen2"), + KVariable("_Gen3")))); + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); + Assert.assertEquals(0, kem.getExceptions().size()); + } + + @Test + public void testFragment4() { + K term = + cell( + "", + cell("", cell("", KVariable("E")), KVariable("F")), + cell( + "", + cell( + "", + KRewrite( + KSequence(KVariable("Rest")), + KSequence(KVariable("F"), KVariable("Rest")))), + KVariable("F2"))); + K expected = + cell( + "", + app( + "_ThreadCellBag_", cell("", KVariable("_Gen0"), cell("", KVariable("E")), KVariable("_Gen1")), - cell("", cell("", KRewrite(KSequence(KVariable("Rest")), - KSequence(cell("-fragment", KVariable("_Gen0"), app("noEnvCell"), KVariable("_Gen1")), KVariable("Rest")))), - KVariable("_Gen2"), - KVariable("_Gen3")))); - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); - Assert.assertEquals(0, kem.getExceptions().size()); - } + cell( + "", + cell( + "", + KRewrite( + KSequence(KVariable("Rest")), + KSequence( + cell( + "-fragment", + KVariable("_Gen0"), + app("noEnvCell"), + KVariable("_Gen1")), + KVariable("Rest")))), + KVariable("_Gen2"), + KVariable("_Gen3")))); + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); + Assert.assertEquals(0, kem.getExceptions().size()); + } - ConfigurationInfo bagCfgInfo = new TestConfiguration() {{ - addCell(null, "TopCell", ""); - addCell("TopCell", "ThreadCell", "", Multiplicity.STAR, Sorts.K()); - addCell("TopCell", "ExtraCell", ""); - addUnit("ThreadCell", KLabel(".ThreadCellBag")); - addConcat("ThreadCell", KLabel("_ThreadCellBag_")); - }}; - LabelInfo bagLabelInfo = new LabelInfo() {{ - addLabel(Sort("TopCell"), ""); - addLabel(Sort("ThreadCell"), ""); - addLabel(Sort("ExtraCell"), ""); - addLabel(Sorts.K(), "restore"); - addLabel(Sort("ThreadCellBag"),"_ThreadCellBag_"); - }}; - @Test - public void testFragmentBag() { - K term = cell("", KVariable("F"),cell("", KRewrite(KVariable("Rest"),KSequence(KVariable("F"),KVariable("Rest"))))); - K expected = cell("", - app("_ThreadCellBag_", KVariable("_Gen0"), - cell("", KRewrite(KVariable("Rest"), - KSequence(app("-fragment",KVariable("_Gen0"),KVariable("_Gen1")),KVariable("Rest"))))), - KVariable("_Gen1")); - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - Assert.assertEquals(expected, new SortCells(bagCfgInfo, bagLabelInfo).sortCells(term)); - Assert.assertEquals(0, kem.getExceptions().size()); - } + ConfigurationInfo bagCfgInfo = + new TestConfiguration() { + { + addCell(null, "TopCell", ""); + addCell("TopCell", "ThreadCell", "", Multiplicity.STAR, Sorts.K()); + addCell("TopCell", "ExtraCell", ""); + addUnit("ThreadCell", KLabel(".ThreadCellBag")); + addConcat("ThreadCell", KLabel("_ThreadCellBag_")); + } + }; + LabelInfo bagLabelInfo = + new LabelInfo() { + { + addLabel(Sort("TopCell"), ""); + addLabel(Sort("ThreadCell"), ""); + addLabel(Sort("ExtraCell"), ""); + addLabel(Sorts.K(), "restore"); + addLabel(Sort("ThreadCellBag"), "_ThreadCellBag_"); + } + }; - @Test - public void testFragmentRewrite() { - K term = cell("", - cell("",KRewrite(app("restore",KVariable("Ctx")), - KVariable("Result"))), - KRewrite(cells(KVariable("_Gen1"), cell("",KVariable("Result"))), - KVariable("Ctx"))); - K expected = cell("", - app("_ThreadCellBag_", - cell("",KRewrite( - app("restore",app("-fragment",KVariable("_Gen0"),KVariable("_Gen2"))), - KVariable("Result"))), - KRewrite(app("_ThreadCellBag_",KVariable("_Gen3"), - cell("",KVariable("Result"))), - KVariable("_Gen0"))), - KRewrite(KVariable("_Gen4"),KVariable("_Gen2"))); - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - Assert.assertEquals(expected, new SortCells(bagCfgInfo, bagLabelInfo).sortCells(term)); - Assert.assertEquals(0, kem.getExceptions().size()); - } + @Test + public void testFragmentBag() { + K term = + cell( + "", + KVariable("F"), + cell("", KRewrite(KVariable("Rest"), KSequence(KVariable("F"), KVariable("Rest"))))); + K expected = + cell( + "", + app( + "_ThreadCellBag_", + KVariable("_Gen0"), + cell( + "", + KRewrite( + KVariable("Rest"), + KSequence( + app("-fragment", KVariable("_Gen0"), KVariable("_Gen1")), + KVariable("Rest"))))), + KVariable("_Gen1")); + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + Assert.assertEquals(expected, new SortCells(bagCfgInfo, bagLabelInfo).sortCells(term)); + Assert.assertEquals(0, kem.getExceptions().size()); + } - /** When a cell fragment variable occurs as an argument to the appropriate cell fragment sort predict, we - * specialize the expansion by splitting it into a conjunction of individual sort predicate tests on - * the variables the cell fragment variable splits into, rather than just using the generic replacement term. - * This is a very special case of statically simplifying predicate applications. - */ - @Test - public void testPredicateExpansion() { - Rule term = new Rule(KRewrite(cell("", cell(""), KVariable("X"), KVariable("Y", Att().add(Sort.class, Sort("OptCell")))), KVariable("X")) - , app("isThreadCellFragment",KVariable("X")) - , BooleanUtils.TRUE - , Att()); - K expectedBody = KRewrite(cell("", KVariable("X"), cell(""), KVariable("Y", Att().add(Sort.class, Sort("OptCell")))), - cell("-fragment", KVariable("X"), app("noEnvCell"), app(".OptCell"))); - Rule expected = new Rule(expectedBody - , BooleanUtils.and(BooleanUtils.TRUE, app("isKCell", KVariable("X"))) - , BooleanUtils.TRUE, Att()); - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); - Assert.assertEquals(0, kem.getExceptions().size()); - } + @Test + public void testFragmentRewrite() { + K term = + cell( + "", + cell("", KRewrite(app("restore", KVariable("Ctx")), KVariable("Result"))), + KRewrite( + cells(KVariable("_Gen1"), cell("", KVariable("Result"))), KVariable("Ctx"))); + K expected = + cell( + "", + app( + "_ThreadCellBag_", + cell( + "", + KRewrite( + app( + "restore", + app("-fragment", KVariable("_Gen0"), KVariable("_Gen2"))), + KVariable("Result"))), + KRewrite( + app("_ThreadCellBag_", KVariable("_Gen3"), cell("", KVariable("Result"))), + KVariable("_Gen0"))), + KRewrite(KVariable("_Gen4"), KVariable("_Gen2"))); + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + Assert.assertEquals(expected, new SortCells(bagCfgInfo, bagLabelInfo).sortCells(term)); + Assert.assertEquals(0, kem.getExceptions().size()); + } - /** Ensure that the splitting in {@linkplain #testPredicateExpansion()} does not happen - * in a term which applies the sort predicate for a different cell fragment sort - * to a cell fragment variable. - */ - @Test - public void testUnrelatedPredicate() { - Rule term = new Rule(KRewrite(cell("", cell(""), KVariable("X"), KVariable("Y", Att().add(Sort.class, Sort("OptCell")))), KVariable("X")) - , app("isTopCellFragment",KVariable("X")) - , BooleanUtils.TRUE - , Att()); - K replacement = app("-fragment", KVariable("X"), app("noEnvCell"), app(".OptCell")); - K expectedBody = KRewrite(cell("", KVariable("X"), cell(""), KVariable("Y", Att().add(Sort.class, Sort("OptCell")))), replacement); - Rule expected = new Rule(expectedBody - , app("isTopCellFragment", replacement) - , BooleanUtils.TRUE, Att()); - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); - Assert.assertEquals(0, kem.getExceptions().size()); - } + /** + * When a cell fragment variable occurs as an argument to the appropriate cell fragment sort + * predict, we specialize the expansion by splitting it into a conjunction of individual sort + * predicate tests on the variables the cell fragment variable splits into, rather than just using + * the generic replacement term. This is a very special case of statically simplifying predicate + * applications. + */ + @Test + public void testPredicateExpansion() { + Rule term = + new Rule( + KRewrite( + cell( + "", + cell(""), + KVariable("X"), + KVariable("Y", Att().add(Sort.class, Sort("OptCell")))), + KVariable("X")), + app("isThreadCellFragment", KVariable("X")), + BooleanUtils.TRUE, + Att()); + K expectedBody = + KRewrite( + cell( + "", + KVariable("X"), + cell(""), + KVariable("Y", Att().add(Sort.class, Sort("OptCell")))), + cell("-fragment", KVariable("X"), app("noEnvCell"), app(".OptCell"))); + Rule expected = + new Rule( + expectedBody, + BooleanUtils.and(BooleanUtils.TRUE, app("isKCell", KVariable("X"))), + BooleanUtils.TRUE, + Att()); + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); + Assert.assertEquals(0, kem.getExceptions().size()); + } - /** - * Test that splitting works properly if an item under a parent cell is - * already a collection of multiplicity * cells joined by the proper label. - */ - @Test - public void testMultipleCells() { - KVariable T1 = KVariable("T1",Att().add(Sort.class,Sort("ThreadCell"))); - KVariable T2 = KVariable("T2",Att().add(Sort.class,Sort("ThreadCell"))); - K term = cell("", - KVariable("F"), - cell("", KVariable("T")), - app("_ThreadCellBag_",T1,T2)); - K expected = cell("", - app("_ThreadCellBag_", - app("_ThreadCellBag_", KVariable("_Gen0"), cell("", KVariable("T"))), - app("_ThreadCellBag_", T1, T2)), - KVariable("_Gen1")); - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - Assert.assertEquals(expected, new SortCells(bagCfgInfo, bagLabelInfo).sortCells(term)); - Assert.assertEquals(0, kem.getExceptions().size()); + /** + * Ensure that the splitting in {@linkplain #testPredicateExpansion()} does not happen in a term + * which applies the sort predicate for a different cell fragment sort to a cell fragment + * variable. + */ + @Test + public void testUnrelatedPredicate() { + Rule term = + new Rule( + KRewrite( + cell( + "", + cell(""), + KVariable("X"), + KVariable("Y", Att().add(Sort.class, Sort("OptCell")))), + KVariable("X")), + app("isTopCellFragment", KVariable("X")), + BooleanUtils.TRUE, + Att()); + K replacement = app("-fragment", KVariable("X"), app("noEnvCell"), app(".OptCell")); + K expectedBody = + KRewrite( + cell( + "", + KVariable("X"), + cell(""), + KVariable("Y", Att().add(Sort.class, Sort("OptCell")))), + replacement); + Rule expected = + new Rule(expectedBody, app("isTopCellFragment", replacement), BooleanUtils.TRUE, Att()); + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + Assert.assertEquals(expected, new SortCells(cfgInfo, labelInfo).sortCells(term)); + Assert.assertEquals(0, kem.getExceptions().size()); + } - } + /** + * Test that splitting works properly if an item under a parent cell is already a collection of + * multiplicity * cells joined by the proper label. + */ + @Test + public void testMultipleCells() { + KVariable T1 = KVariable("T1", Att().add(Sort.class, Sort("ThreadCell"))); + KVariable T2 = KVariable("T2", Att().add(Sort.class, Sort("ThreadCell"))); + K term = + cell("", KVariable("F"), cell("", KVariable("T")), app("_ThreadCellBag_", T1, T2)); + K expected = + cell( + "", + app( + "_ThreadCellBag_", + app("_ThreadCellBag_", KVariable("_Gen0"), cell("", KVariable("T"))), + app("_ThreadCellBag_", T1, T2)), + KVariable("_Gen1")); + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + Assert.assertEquals(expected, new SortCells(bagCfgInfo, bagLabelInfo).sortCells(term)); + Assert.assertEquals(0, kem.getExceptions().size()); + } - KApply app(String name, K... ks) { - return KApply(KLabel(name), ks); - } + KApply app(String name, K... ks) { + return KApply(KLabel(name), ks); + } - KApply cell(String name, K... ks) { - return KApply(KLabel(name), ks); - } + KApply cell(String name, K... ks) { + return KApply(KLabel(name), ks); + } - KApply cells(K... ks) { - return KApply(KLabels.CELLS, ks); - } + KApply cells(K... ks) { + return KApply(KLabels.CELLS, ks); + } } diff --git a/kernel/src/test/java/org/kframework/kore/compile/TestConfiguration.java b/kernel/src/test/java/org/kframework/kore/compile/TestConfiguration.java index f94742d68a4..0a6f6f335d6 100644 --- a/kernel/src/test/java/org/kframework/kore/compile/TestConfiguration.java +++ b/kernel/src/test/java/org/kframework/kore/compile/TestConfiguration.java @@ -1,203 +1,218 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.Collections.*; +import static org.kframework.kore.KORE.*; + import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Maps; -import org.kframework.compile.ConfigurationInfo; +import java.util.List; +import java.util.Map; +import java.util.Set; import org.kframework.kore.KApply; import org.kframework.kore.KLabel; import org.kframework.kore.Sort; import scala.Option; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static org.kframework.Collections.*; -import static org.kframework.kore.KORE.*; - -/** - * Created by brandon on 3/24/15. - */ +/** Created by brandon on 3/24/15. */ class TestConfiguration implements ConfigurationInfo { - Map levels = Maps.newHashMap(); - Map parents = Maps.newHashMap(); - Map leafCellTypes = Maps.newHashMap(); - ListMultimap children = ArrayListMultimap.create(); - Sort topCell = null; - Sort computationCell = null; - - Map multiplicities = Maps.newHashMap(); - Map defaultCells = Maps.newHashMap(); - BiMap units = HashBiMap.create(); - BiMap concats = HashBiMap.create(); - Map cellLabels = Maps.newHashMap(); - Map cellFragmentLabels = Maps.newHashMap(); - Map cellAbsentLabels = Maps.newHashMap(); - Map cellCollectionSorts = Maps.newHashMap(); - - public void addCell(String parent, String child, String label) { - addCell(parent, child, label, Multiplicity.ONE); - } - public void addCell(String parent, String child, String label, Sort contents) { - addCell(parent, child, label, Multiplicity.ONE, contents); - } - public void addCell(String parent, String child, String label, Multiplicity m) { - addCell(parent, child, label, m, null); - } - public void addCell(String parent, String child, String label, Multiplicity m, Sort contents) { - if (parent != null) { - if (!children.containsKey(Sort(parent))) { - // create a fragment label for the parent cell. - cellFragmentLabels.put(Sort(parent),KLabel(cellLabels.get(Sort(parent)).name()+"-fragment")); - } - if (m != Multiplicity.STAR) { - cellAbsentLabels.put(Sort(child),KLabel("no"+child)); - } - if (m == Multiplicity.STAR) { - cellCollectionSorts.put(Sort(child+"Bag"),Sort(child)); - } - parents.put(Sort(child), Sort(parent)); - children.put(Sort(parent), Sort(child)); - levels.put(Sort(child), 1 + levels.get(Sort(parent))); - } else { - levels.put(Sort(child), 0); - } - if (contents != null) { - leafCellTypes.put(Sort(child), contents); - } - multiplicities.put(Sort(child), m); - cellLabels.put(Sort(child), KLabel(label)); - } - - public void addDefault(String cell, KApply term) { - defaultCells.put(Sort(cell), term); - } - - public void addUnit(String cell, KLabel label) { units.put(Sort(cell), label); } - - public void addConcat(String cell, KLabel label) { concats.put(Sort(cell), label); } - - public TestConfiguration() { - } - - @Override - public int getLevel(Sort k) { - return levels.getOrDefault(k, -1); - } - - @Override - public Sort getParent(Sort k) { - return parents.get(k); - } - - @Override - public List getChildren(Sort k) { - return children.get(k); - } - - @Override - public Multiplicity getMultiplicity(Sort k) { - return multiplicities.get(k); - } - - @Override - public boolean isCell(Sort k) { - return levels.containsKey(k); - } - - @Override - public boolean isCellCollection(Sort s) { - return cellCollectionSorts.containsKey(s); - } - - @Override - public boolean isCellLabel(KLabel kLabel) { - return getCellSort(kLabel) != null; - } - - @Override - public boolean isLeafCell(Sort k) { - return !children.containsKey(k) && isCell(k); - } - - @Override - public boolean isParentCell(Sort k) { - return children.containsKey(k); - } - - @Override - public Sort leafCellType(Sort k) { - return leafCellTypes.get(k); - } - - @Override - public KLabel getCellLabel(Sort k) { - return cellLabels.get(k); - } - - @Override - public Sort getCellSort(KLabel kLabel) { - if (kLabel != null) { - return cellLabels.entrySet().stream().filter(e -> kLabel.equals(e.getValue())).map(Map.Entry::getKey).findAny().orElseGet(null); - } else { - return null; - } - } - - @Override - public KLabel getCellFragmentLabel(Sort k) { return cellFragmentLabels.get(k); } - - @Override - public KLabel getCellAbsentLabel(Sort k) { return cellAbsentLabels.get(k); } - - @Override - public KApply getDefaultCell(Sort k) { - return defaultCells.get(k); - } - - @Override - public boolean isConstantInitializer(Sort k) { - return true; - } - - @Override - public Sort getRootCell() { - return topCell; - } - - @Override - public Sort getComputationCell() { - return computationCell; - } - - @Override - public Set getCellSorts() { - return cellLabels.keySet(); - } - - @Override - public KApply getUnit(Sort k) { return KApply(units.get(k)); } - - @Override - public KLabel getConcat(Sort k) { return concats.get(k); } - - @Override - public Option getCellForConcat(KLabel concat) { - return Option.apply(concats.inverse().get(concat)); - } - - @Override - public Option getCellForUnit(KLabel unit) { - return Option.apply(units.inverse().get(unit)); - } - - @Override - public scala.collection.Set getCellBagSortsOfCell(Sort k) { - return Set(Sort(k.name() + "Bag", k.params())); - } + Map levels = Maps.newHashMap(); + Map parents = Maps.newHashMap(); + Map leafCellTypes = Maps.newHashMap(); + ListMultimap children = ArrayListMultimap.create(); + Sort topCell = null; + Sort computationCell = null; + + Map multiplicities = Maps.newHashMap(); + Map defaultCells = Maps.newHashMap(); + BiMap units = HashBiMap.create(); + BiMap concats = HashBiMap.create(); + Map cellLabels = Maps.newHashMap(); + Map cellFragmentLabels = Maps.newHashMap(); + Map cellAbsentLabels = Maps.newHashMap(); + Map cellCollectionSorts = Maps.newHashMap(); + + public void addCell(String parent, String child, String label) { + addCell(parent, child, label, Multiplicity.ONE); + } + + public void addCell(String parent, String child, String label, Sort contents) { + addCell(parent, child, label, Multiplicity.ONE, contents); + } + + public void addCell(String parent, String child, String label, Multiplicity m) { + addCell(parent, child, label, m, null); + } + + public void addCell(String parent, String child, String label, Multiplicity m, Sort contents) { + if (parent != null) { + if (!children.containsKey(Sort(parent))) { + // create a fragment label for the parent cell. + cellFragmentLabels.put( + Sort(parent), KLabel(cellLabels.get(Sort(parent)).name() + "-fragment")); + } + if (m != Multiplicity.STAR) { + cellAbsentLabels.put(Sort(child), KLabel("no" + child)); + } + if (m == Multiplicity.STAR) { + cellCollectionSorts.put(Sort(child + "Bag"), Sort(child)); + } + parents.put(Sort(child), Sort(parent)); + children.put(Sort(parent), Sort(child)); + levels.put(Sort(child), 1 + levels.get(Sort(parent))); + } else { + levels.put(Sort(child), 0); + } + if (contents != null) { + leafCellTypes.put(Sort(child), contents); + } + multiplicities.put(Sort(child), m); + cellLabels.put(Sort(child), KLabel(label)); + } + + public void addDefault(String cell, KApply term) { + defaultCells.put(Sort(cell), term); + } + + public void addUnit(String cell, KLabel label) { + units.put(Sort(cell), label); + } + + public void addConcat(String cell, KLabel label) { + concats.put(Sort(cell), label); + } + + public TestConfiguration() {} + + @Override + public int getLevel(Sort k) { + return levels.getOrDefault(k, -1); + } + + @Override + public Sort getParent(Sort k) { + return parents.get(k); + } + + @Override + public List getChildren(Sort k) { + return children.get(k); + } + + @Override + public Multiplicity getMultiplicity(Sort k) { + return multiplicities.get(k); + } + + @Override + public boolean isCell(Sort k) { + return levels.containsKey(k); + } + + @Override + public boolean isCellCollection(Sort s) { + return cellCollectionSorts.containsKey(s); + } + + @Override + public boolean isCellLabel(KLabel kLabel) { + return getCellSort(kLabel) != null; + } + + @Override + public boolean isLeafCell(Sort k) { + return !children.containsKey(k) && isCell(k); + } + + @Override + public boolean isParentCell(Sort k) { + return children.containsKey(k); + } + + @Override + public Sort leafCellType(Sort k) { + return leafCellTypes.get(k); + } + + @Override + public KLabel getCellLabel(Sort k) { + return cellLabels.get(k); + } + + @Override + public Sort getCellSort(KLabel kLabel) { + if (kLabel != null) { + return cellLabels.entrySet().stream() + .filter(e -> kLabel.equals(e.getValue())) + .map(Map.Entry::getKey) + .findAny() + .orElseGet(null); + } else { + return null; + } + } + + @Override + public KLabel getCellFragmentLabel(Sort k) { + return cellFragmentLabels.get(k); + } + + @Override + public KLabel getCellAbsentLabel(Sort k) { + return cellAbsentLabels.get(k); + } + + @Override + public KApply getDefaultCell(Sort k) { + return defaultCells.get(k); + } + + @Override + public boolean isConstantInitializer(Sort k) { + return true; + } + + @Override + public Sort getRootCell() { + return topCell; + } + + @Override + public Sort getComputationCell() { + return computationCell; + } + + @Override + public Set getCellSorts() { + return cellLabels.keySet(); + } + + @Override + public KApply getUnit(Sort k) { + return KApply(units.get(k)); + } + + @Override + public KLabel getConcat(Sort k) { + return concats.get(k); + } + + @Override + public Option getCellForConcat(KLabel concat) { + return Option.apply(concats.inverse().get(concat)); + } + + @Override + public Option getCellForUnit(KLabel unit) { + return Option.apply(units.inverse().get(unit)); + } + + @Override + public scala.collection.Set getCellBagSortsOfCell(Sort k) { + return Set(Sort(k.name() + "Bag", k.params())); + } } diff --git a/kernel/src/test/java/org/kframework/lsp/LSPTests.java b/kernel/src/test/java/org/kframework/lsp/LSPTests.java index fd880e98e81..9e334c1620b 100644 --- a/kernel/src/test/java/org/kframework/lsp/LSPTests.java +++ b/kernel/src/test/java/org/kframework/lsp/LSPTests.java @@ -1,6 +1,16 @@ // Copyright (c) Runtime Verification, Inc. All Rights Reserved. package org.kframework.lsp; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicReference; import org.eclipse.lsp4j.*; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.junit.Assert; @@ -16,124 +26,153 @@ import org.kframework.utils.BinaryLoader; import org.kframework.utils.errorsystem.KExceptionManager; -import java.io.IOException; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicReference; - public class LSPTests { - String def = "//require \"Copy (10) test.k\"\n" + - "require \"spec.k\"\n" + - "\n" + - "module A\n" + - " syntax A ::= \"a\"\n" + - "endmodule\n" + - "\n" + - "module B\n" + - " imports private A\n" + - "\n" + - " syntax C ::= B \n" + - " | A\n" + - " syntax B ::= \"b\"\n" + - "\n" + - " // this is the ffunction function\n" + - " syntax B ::= affunction(C) [function, total]\n" + - "\n" + - "endmodule\n" + - "\n" + - "module TEST \n" + - " imports private B \n" + - " imports INT \n" + - "\n" + - " rule .asdfasdf adsf asdf 123454 \n" + - "// rule f(_) => \"a\" [owise] \n" + - "endmodule\n"; - String uri = LSPTests.class.toString(); + String def = + "//require \"Copy (10) test.k\"\n" + + "require \"spec.k\"\n" + + "\n" + + "module A\n" + + " syntax A ::= \"a\"\n" + + "endmodule\n" + + "\n" + + "module B\n" + + " imports private A\n" + + "\n" + + " syntax C ::= B \n" + + " | A\n" + + " syntax B ::= \"b\"\n" + + "\n" + + " // this is the ffunction function\n" + + " syntax B ::= affunction(C) [function, total]\n" + + "\n" + + "endmodule\n" + + "\n" + + "module TEST \n" + + " imports private B \n" + + " imports INT \n" + + "\n" + + " rule .asdfasdf adsf asdf 123454 \n" + + "// rule f(_) => \"a\" [owise] \n" + + "endmodule\n"; + String uri = LSPTests.class.toString(); - @Test - public void testGetContext() { - KTextDocument doc = new KTextDocument(); - doc.updateText(def); - Assert.assertEquals("require", doc.getContextAt(new KPos(2, 2))); - Assert.assertEquals("module", doc.getContextAt(new KPos(4, 8))); - Assert.assertEquals("imports", doc.getContextAt(new KPos(9, 21))); - Assert.assertEquals("syntax", doc.getContextAt(new KPos(11, 18))); - Assert.assertEquals("endmodule", doc.getContextAt(new KPos(19, 1))); - Assert.assertEquals("rule", doc.getContextAt(new KPos(24, 30))); - } + @Test + public void testGetContext() { + KTextDocument doc = new KTextDocument(); + doc.updateText(def); + Assert.assertEquals("require", doc.getContextAt(new KPos(2, 2))); + Assert.assertEquals("module", doc.getContextAt(new KPos(4, 8))); + Assert.assertEquals("imports", doc.getContextAt(new KPos(9, 21))); + Assert.assertEquals("syntax", doc.getContextAt(new KPos(11, 18))); + Assert.assertEquals("endmodule", doc.getContextAt(new KPos(19, 1))); + Assert.assertEquals("rule", doc.getContextAt(new KPos(24, 30))); + } - @Test - public void isPositionOverLocation() { - Assert.assertTrue(TextDocumentSyncHandler.isPositionOverLocation( - new KPos(9, 3), - new Location(9, 3, 12, 17))); - Assert.assertTrue(TextDocumentSyncHandler.isPositionOverLocation( - new KPos(10, 16), - new Location(9, 3, 12, 17))); - Assert.assertFalse(TextDocumentSyncHandler.isPositionOverLocation( - new KPos(9, 2), - new Location(9, 3, 12, 17))); - Assert.assertFalse(TextDocumentSyncHandler.isPositionOverLocation( - new KPos(12, 18), - new Location(9, 3, 12, 17))); - } + @Test + public void isPositionOverLocation() { + Assert.assertTrue( + TextDocumentSyncHandler.isPositionOverLocation(new KPos(9, 3), new Location(9, 3, 12, 17))); + Assert.assertTrue( + TextDocumentSyncHandler.isPositionOverLocation( + new KPos(10, 16), new Location(9, 3, 12, 17))); + Assert.assertFalse( + TextDocumentSyncHandler.isPositionOverLocation(new KPos(9, 2), new Location(9, 3, 12, 17))); + Assert.assertFalse( + TextDocumentSyncHandler.isPositionOverLocation( + new KPos(12, 18), new Location(9, 3, 12, 17))); + } - @Test - public void testKLS() throws ExecutionException, InterruptedException { - KLanguageServer kls = new KLanguageServer(); - KTextDocumentService.DELAY_EXECUTION_MS = 0; - kls.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(uri, "kframework", 1, def))); + @Test + public void testKLS() throws ExecutionException, InterruptedException { + KLanguageServer kls = new KLanguageServer(); + KTextDocumentService.DELAY_EXECUTION_MS = 0; + kls.getTextDocumentService() + .didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(uri, "kframework", 1, def))); - CompletableFuture diags = kls.getTextDocumentService().diagnostic(new DocumentDiagnosticParams(new TextDocumentIdentifier(uri))); - RelatedFullDocumentDiagnosticReport z = diags.get().getLeft(); - Assert.assertEquals(0, z.getItems().size()); - //System.out.println("Diags: " + z); + CompletableFuture diags = + kls.getTextDocumentService() + .diagnostic(new DocumentDiagnosticParams(new TextDocumentIdentifier(uri))); + RelatedFullDocumentDiagnosticReport z = diags.get().getLeft(); + Assert.assertEquals(0, z.getItems().size()); + // System.out.println("Diags: " + z); - CompletableFuture, CompletionList>> x = kls.getTextDocumentService().completion(new CompletionParams(new TextDocumentIdentifier(uri), new Position(10, 17))); - List y = x.get().getLeft(); - Assert.assertNotEquals(y.size(), 0); - //System.out.println("Completion: " + y.size()); + CompletableFuture, CompletionList>> x = + kls.getTextDocumentService() + .completion( + new CompletionParams(new TextDocumentIdentifier(uri), new Position(10, 17))); + List y = x.get().getLeft(); + Assert.assertNotEquals(y.size(), 0); + // System.out.println("Completion: " + y.size()); - CompletableFuture, List>> defin = kls.getTextDocumentService().definition(new DefinitionParams(new TextDocumentIdentifier(uri), new Position(21, 6))); - List defRez = defin.get().getRight(); - Assert.assertNotEquals(defRez.size(), 0); - //System.out.println("GoToDef: " + defRez); - } + CompletableFuture< + Either, List>> + defin = + kls.getTextDocumentService() + .definition( + new DefinitionParams(new TextDocumentIdentifier(uri), new Position(21, 6))); + List defRez = defin.get().getRight(); + Assert.assertNotEquals(defRez.size(), 0); + // System.out.println("GoToDef: " + defRez); + } - // useful for local testing - @Test @Ignore - public void testKLSPathK() throws IOException { - WorkspaceFolder workspaceFolder = new WorkspaceFolder("file:///home/radu/work/test", "test"); - BinaryLoader loader = new BinaryLoader(new KExceptionManager(new GlobalOptions())); - Map caches = null; + // useful for local testing + @Test + @Ignore + public void testKLSPathK() throws IOException { + WorkspaceFolder workspaceFolder = new WorkspaceFolder("file:///home/radu/work/test", "test"); + BinaryLoader loader = new BinaryLoader(new KExceptionManager(new GlobalOptions())); + Map caches = null; - Optional cacheFile = Files.walk(Path.of(URI.create(workspaceFolder.getUri()))).filter(p -> p.endsWith(Kompile.CACHE_FILE_NAME)).findFirst(); - if (cacheFile.isPresent()) - caches = loader.loadCache(java.util.Map.class, cacheFile.get().toFile()); + Optional cacheFile = + Files.walk(Path.of(URI.create(workspaceFolder.getUri()))) + .filter(p -> p.endsWith(Kompile.CACHE_FILE_NAME)) + .findFirst(); + if (cacheFile.isPresent()) + caches = loader.loadCache(java.util.Map.class, cacheFile.get().toFile()); - System.out.println(caches.size()); + System.out.println(caches.size()); - KPos pos = new KPos(10, 11); - Map ch = caches.entrySet().stream().filter(elm -> elm.getKey().startsWith("TEST-RULE-CELLS")).findFirst().get().getValue().cache(); + KPos pos = new KPos(10, 11); + Map ch = + caches.entrySet().stream() + .filter(elm -> elm.getKey().startsWith("TEST-RULE-CELLS")) + .findFirst() + .get() + .getValue() + .cache(); - ParseCache.ParsedSentence rl = ch.entrySet().stream().filter(r -> r.getKey().equals("1 => 2")).findFirst().get().getValue(); - K ast = rl.parse(); - AtomicReference x = new AtomicReference<>(); - KViz.from(t -> { - if (TextDocumentSyncHandler.isPositionOverLocation(pos, t.location().get())) { + ParseCache.ParsedSentence rl = + ch.entrySet().stream() + .filter(r -> r.getKey().equals("1 => 2")) + .findFirst() + .get() + .getValue(); + K ast = rl.parse(); + AtomicReference x = new AtomicReference<>(); + KViz.from( + t -> { + if (TextDocumentSyncHandler.isPositionOverLocation(pos, t.location().get())) { x.set(t); - System.out.println("Pos over loc: " + pos + " loc: " + t.location() + " trm: " + t.att().get(Production.class)); - } else - System.out.println("Pos out loc: " + pos + " loc: " + t.location() + " trm: " + t.att().get(Production.class)); - return t; - }, "test find").apply(ast); - System.out.println(x.get()); - } + System.out.println( + "Pos over loc: " + + pos + + " loc: " + + t.location() + + " trm: " + + t.att().get(Production.class)); + } else + System.out.println( + "Pos out loc: " + + pos + + " loc: " + + t.location() + + " trm: " + + t.att().get(Production.class)); + return t; + }, + "test find") + .apply(ast); + System.out.println(x.get()); + } } diff --git a/kernel/src/test/java/org/kframework/parser/inner/RuleGrammarTest.java b/kernel/src/test/java/org/kframework/parser/inner/RuleGrammarTest.java index 89c3e0f819e..b7879671df8 100644 --- a/kernel/src/test/java/org/kframework/parser/inner/RuleGrammarTest.java +++ b/kernel/src/test/java/org/kframework/parser/inner/RuleGrammarTest.java @@ -1,10 +1,14 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner; +import static org.kframework.kore.KORE.*; + import com.google.common.collect.Lists; import dk.brics.automaton.Automaton; import dk.brics.automaton.RegExp; import dk.brics.automaton.RunAutomaton; +import java.io.File; +import java.util.Set; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -26,503 +30,576 @@ import scala.Tuple2; import scala.util.Either; -import java.io.File; -import java.util.Set; - -import static org.kframework.kore.KORE.*; - public class RuleGrammarTest { - private final static Sort startSymbol = DefinitionParsing.START_SYMBOL; - private RuleGrammarGenerator gen; - private FileUtil files; - - @Before - public void setUp() throws Exception { - gen = makeRuleGrammarGenerator(); - } - - public RuleGrammarGenerator makeRuleGrammarGenerator() { - String definitionText; - files = FileUtil.testFileUtil(); - ParserUtils parser = new ParserUtils(files, new KExceptionManager(new GlobalOptions())); - File definitionFile = new File(Kompile.BUILTIN_DIRECTORY + "/prelude.md"); - definitionText = files.loadFromWorkingDirectory(definitionFile.getPath()); - - Definition baseK = - parser.loadDefinition("K", "K", definitionText, - definitionFile, - definitionFile.getParentFile(), - Lists.newArrayList(Kompile.BUILTIN_DIRECTORY), - false, false, false); - - return new RuleGrammarGenerator(baseK); - } - - private void parseRule(String input, String def, int warnings, boolean expectedError) { - Module test = ParserUtils.parseMainModuleOuterSyntax(def, Source.apply("generated by RuleGrammarTest"), "TEST"); - ParseInModule parser = RuleGrammarGenerator.getCombinedGrammar(gen.getRuleGrammar(test), true, files); - Tuple2, K>, Set> rule = parser.parseString(input, startSymbol, Source.apply("generated by RuleGrammarTest")); - printout(rule, warnings, expectedError); - } - - private void parseRule(String input, String def, int warnings, K expectedResult) { - Module test = ParserUtils.parseMainModuleOuterSyntax(def, Source.apply("generated by RuleGrammarTest"), "TEST"); - ParseInModule parser = RuleGrammarGenerator.getCombinedGrammar(gen.getRuleGrammar(test), true, files); - Tuple2, K>, Set> rule - = parser.parseString(input, startSymbol, Source.apply("generated by RuleGrammarTest")); - printout(rule, warnings, false); - Assert.assertEquals(expectedResult, rule._1().right().get()); - } - - private void parseConfig(String input, String def, int warnings, boolean expectedError) { - Module test = ParserUtils.parseMainModuleOuterSyntax(def, Source.apply("generated by RuleGrammarTest"), "TEST"); - ParseInModule parser = RuleGrammarGenerator.getCombinedGrammar(gen.getConfigGrammar(test), true, files); - Tuple2, K>, Set> rule = parser.parseString(input, startSymbol, Source.apply("generated by RuleGrammarTest")); - printout(rule, warnings, expectedError); - } - - private void parseProgram(String input, String def, String startSymbol, int warnings, boolean expectedError) { - Module test = ParserUtils.parseMainModuleOuterSyntax(def, Source.apply("generated by RuleGrammarTest"), "TEST"); - ParseInModule parser = RuleGrammarGenerator.getCombinedGrammar(gen.getProgramsGrammar(test), true, files); - Tuple2, K>, Set> rule = parser.parseString(input, Sort(startSymbol), Source.apply("generated by RuleGrammarTest")); - printout(rule, warnings, expectedError); - } - - private void parseProgram(String input, String def, String startSymbol, int warnings, K expectedResult) { - Module test = ParserUtils.parseMainModuleOuterSyntax(def, Source.apply("generated by RuleGrammarTest"), "TEST"); - ParseInModule parser = RuleGrammarGenerator.getCombinedGrammar(gen.getProgramsGrammar(test), true, files); - Tuple2, K>, Set> rule = parser.parseString(input, Sort(startSymbol), Source.apply("generated by RuleGrammarTest")); - printout(rule, warnings, false); - Assert.assertEquals(expectedResult, rule._1().right().get()); - } - - private void printout(Tuple2, K>, Set> rule, int warnings, boolean expectedError) { - if (false) { // true to print detailed results - KExceptionManager kem = new KExceptionManager(new GlobalOptions(true, Warnings.ALL, true)); - if (rule._1().isLeft()) { - for (KEMException x : rule._1().left().get()) { - kem.addKException(x.getKException()); - } - } else { - System.err.println("rule = " + rule._1().right().get()); - } - for (KEMException x : rule._2()) { - kem.addKException(x.getKException()); - } - kem.print(); + private static final Sort startSymbol = DefinitionParsing.START_SYMBOL; + private RuleGrammarGenerator gen; + private FileUtil files; + + @Before + public void setUp() throws Exception { + gen = makeRuleGrammarGenerator(); + } + + public RuleGrammarGenerator makeRuleGrammarGenerator() { + String definitionText; + files = FileUtil.testFileUtil(); + ParserUtils parser = new ParserUtils(files, new KExceptionManager(new GlobalOptions())); + File definitionFile = new File(Kompile.BUILTIN_DIRECTORY + "/prelude.md"); + definitionText = files.loadFromWorkingDirectory(definitionFile.getPath()); + + Definition baseK = + parser.loadDefinition( + "K", + "K", + definitionText, + definitionFile, + definitionFile.getParentFile(), + Lists.newArrayList(Kompile.BUILTIN_DIRECTORY), + false, + false, + false); + + return new RuleGrammarGenerator(baseK); + } + + private void parseRule(String input, String def, int warnings, boolean expectedError) { + Module test = + ParserUtils.parseMainModuleOuterSyntax( + def, Source.apply("generated by RuleGrammarTest"), "TEST"); + ParseInModule parser = + RuleGrammarGenerator.getCombinedGrammar(gen.getRuleGrammar(test), true, files); + Tuple2, K>, Set> rule = + parser.parseString(input, startSymbol, Source.apply("generated by RuleGrammarTest")); + printout(rule, warnings, expectedError); + } + + private void parseRule(String input, String def, int warnings, K expectedResult) { + Module test = + ParserUtils.parseMainModuleOuterSyntax( + def, Source.apply("generated by RuleGrammarTest"), "TEST"); + ParseInModule parser = + RuleGrammarGenerator.getCombinedGrammar(gen.getRuleGrammar(test), true, files); + Tuple2, K>, Set> rule = + parser.parseString(input, startSymbol, Source.apply("generated by RuleGrammarTest")); + printout(rule, warnings, false); + Assert.assertEquals(expectedResult, rule._1().right().get()); + } + + private void parseConfig(String input, String def, int warnings, boolean expectedError) { + Module test = + ParserUtils.parseMainModuleOuterSyntax( + def, Source.apply("generated by RuleGrammarTest"), "TEST"); + ParseInModule parser = + RuleGrammarGenerator.getCombinedGrammar(gen.getConfigGrammar(test), true, files); + Tuple2, K>, Set> rule = + parser.parseString(input, startSymbol, Source.apply("generated by RuleGrammarTest")); + printout(rule, warnings, expectedError); + } + + private void parseProgram( + String input, String def, String startSymbol, int warnings, boolean expectedError) { + Module test = + ParserUtils.parseMainModuleOuterSyntax( + def, Source.apply("generated by RuleGrammarTest"), "TEST"); + ParseInModule parser = + RuleGrammarGenerator.getCombinedGrammar(gen.getProgramsGrammar(test), true, files); + Tuple2, K>, Set> rule = + parser.parseString(input, Sort(startSymbol), Source.apply("generated by RuleGrammarTest")); + printout(rule, warnings, expectedError); + } + + private void parseProgram( + String input, String def, String startSymbol, int warnings, K expectedResult) { + Module test = + ParserUtils.parseMainModuleOuterSyntax( + def, Source.apply("generated by RuleGrammarTest"), "TEST"); + ParseInModule parser = + RuleGrammarGenerator.getCombinedGrammar(gen.getProgramsGrammar(test), true, files); + Tuple2, K>, Set> rule = + parser.parseString(input, Sort(startSymbol), Source.apply("generated by RuleGrammarTest")); + printout(rule, warnings, false); + Assert.assertEquals(expectedResult, rule._1().right().get()); + } + + private void printout( + Tuple2, K>, Set> rule, + int warnings, + boolean expectedError) { + if (false) { // true to print detailed results + KExceptionManager kem = new KExceptionManager(new GlobalOptions(true, Warnings.ALL, true)); + if (rule._1().isLeft()) { + for (KEMException x : rule._1().left().get()) { + kem.addKException(x.getKException()); } - if (expectedError) - Assert.assertTrue("Expected error here: ", rule._1().isLeft()); - else - Assert.assertTrue("Expected no errors here: ", rule._1().isRight()); - Assert.assertEquals("Expected " + warnings + " warnings: ", warnings, rule._2().size()); - } - - // test proper associativity for rewrite, ~> and cast - @Test - public void test2() { - String def = "" + - "module TEST " + - "syntax Exp ::= Exp \"+\" Exp [klabel('Plus), left] " + - "| r\"[0-9]+\" [token] " + - "syntax left 'Plus " + - "endmodule"; - parseRule("1+2=>A:Exp~>{B}:>Exp", def, 0, false); - } - - // test variable disambiguation when a variable is declared by the user - @Test - public void test3() { - String def = "" + - "module TEST " + - "syntax Exps ::= Exp \",\" Exps [klabel('Exps)] " + - "| Exp " + - "syntax Exp ::= Id " + - "syntax Stmt ::= \"val\" Exps \";\" Stmt [klabel('Decl)] " + - "syntax {Sort} Sort ::= \"(\" Sort \")\" [bracket] " + - "syntax KItem ::= (Id, Stmt) [klabel('tuple)] " + - "syntax Id " + - "syntax K " + - "endmodule"; - parseRule("val X ; S:Stmt => (X, S)", def, 0, false); - } - - // test variable disambiguation when all variables are being inferred - @Test - public void test4() { - String def = "" + - "module TEST " + - "syntax Exps ::= Exp \",\" Exps [klabel('Exps)] " + - "| Exp " + - "syntax Exp ::= Id " + - "syntax Stmt ::= \"val\" Exps \";\" Stmt [klabel('Decl)] " + - "syntax {Sort} Sort ::= \"(\" Sort \")\" [bracket] " + - "syntax KItem ::= (Id, Stmt) [klabel('tuple)] " + - "syntax Id " + - "syntax K " + - "endmodule"; - parseRule("val X ; S => (X, S)", def, 0, false); - } - - // test error reporting when + is non-associative - @Test - public void test5() { - String def = "" + - "module TEST " + - "syntax Exp ::= Exp \"+\" Exp [klabel('Plus), non-assoc] " + - "| r\"[0-9]+\" [token] " + - "syntax non-assoc 'Plus " + - "endmodule"; - parseRule("1+2+3", def, 0, true); - } - - // test AmbFilter which should report ambiguities as errors - @Test - public void test6() { - String def = "" + - "module TEST " + - "syntax Exp ::= Exp \"+\" Exp [klabel('Plus)] " + - "| r\"[0-9]+\" [token] " + - "endmodule"; - parseRule("1+2+3", def, 0, true); - } - - // test error reporting when rewrite priority is not met - @Test - public void test7() { - String def = "" + - "module TEST " + - "syntax A ::= \"foo\" A [klabel('foo)] " + - "syntax B ::= \"bar\" [klabel('bar)] " + - "endmodule"; - parseRule("foo bar => X", def, 0, true); - } - - // test prefer and avoid - @Test - public void test8() { - String def = "" + - "module TEST " + - "syntax Exp ::= Exp \"+\" Exp [klabel('Plus), prefer] " + - "| Exp \"*\" Exp [klabel('Mul)] " + - "| r\"[0-9]+\" [token] " + - "endmodule"; - parseRule("1+2*3", def, 0, false); - } - - // test cells - @Test - public void test9() { - String def = "" + - "module TEST " + - "syntax Exp ::= Exp \"+\" Exp [klabel('Plus), prefer] " + - "| Exp \"*\" Exp [klabel('Mul)] " + - "| r\"[0-9]+\" [token] " + - "syntax K " + - "syntax TopCell ::= \"\" KCell StateCell \"\" [klabel(), cell] " + - "syntax KCell ::= \"\" K \"\" [klabel(), cell] " + - "syntax StateCell ::= \"\" K \"\" [klabel(), cell] " + - "endmodule"; - parseRule(" ...1+2*3... ( A => .::K ... => .::Bag) ...", def, 0, false); - } - - // test rule cells - @Test - public void test10() { - String def = "" + - "module TEST " + - "syntax Exp ::= Exp \",\" Exp [klabel('Plus), prefer] " + - "| Exp \"*\" Exp [klabel('Mul)] " + - "| r\"[0-9]+\" [token] " + - "endmodule"; - parseRule("A::KLabel(B::K, C::K, D::K)", def, 0, false); - } - - // test config cells - @Test - public void test11() { - String def = "" + - "module TEST " + - "syntax Exp ::= Exp \"+\" Exp [klabel('Plus), prefer] " + - "| Exp \"*\" Exp [klabel('Mul)] " + - "| r\"[0-9]+\" [token] " + - "syntax K " + - "endmodule"; - parseConfig(" 1+2*3 ( A => .::K => .::Bag) ", def, 0, false); - } - - // test variable disambiguation when all variables are being inferred - @Test - public void test12() { - String def = "" + - "module TEST " + - "syntax Stmt ::= \"val\" Exp \";\" Stmt [klabel('Decl)] " + - "syntax Exp " + - "syntax Stmt " + - "endmodule"; - parseRule("val _:Exp ; _", def, 0, false); - } - - // test priority exceptions (requires and ensures) - @Test - public void test13() { - String def = "" + - "module TEST " + - "syntax Bool ::= \"true\" [token] " + - "endmodule"; - parseRule(".::K => .::K requires true", def, 0, false); - } - - // test automatic follow restriction for terminals - @Test - public void test14() { - String def = "" + - "module TEST " + - "syntax Stmt ::= Stmt Stmt [klabel('Stmt)] " + - "syntax Exp ::= K \"==\" K [klabel('Eq)] " + - "syntax Exp ::= K \":\" K [klabel('Colon)] " + - "syntax K " + - "syntax Exp ::= K \"==K\" K [klabel('EqK)] " + - "syntax Exp ::= K \"?\" K \":\" K " + - "endmodule"; - parseRule("A::K ==K A", def, 0, false); - parseRule("A::K == K A", def, 0, true); - parseRule("A:K", def, 0, false); - parseRule("A: K", def, 0, false); - parseRule("A:Stmt ?F : Stmt", def, 0, false); - parseRule("A:Stmt ? F : Stmt", def, 0, false); - } - - // test whitespace - @Test - public void test15() { - String def = "" + - "module TEST " + - "syntax Exp ::= Divide(K, K) [klabel('Divide)] " + - "syntax Exp ::= K \"/\" K [klabel('Div)] " + - "syntax K " + - "endmodule"; - parseRule("Divide(K1:K, K2:K) => K1:K / K2:K", def, 0, false); - } - - // test lexical to RegexTerminal extractor - @Test - public void test16() { - assertPatterns("", "#", "", "#"); - assertPatterns("abc", "#", "abc", "#"); - assertPatterns("(? .K", def, 0, false); - } - - // test special priority - @Test - public void test19() { - String def = "" + - "module TEST \n" + - " syntax K \n" + - " syntax KItem ::= foo(K) [klabel(foo), symbol]\n" + - " syntax Bar ::= bar() [klabel(bar), symbol]\n" + - "endmodule"; - parseRule("`foo`(`bar`(.KList) ~> .K , .KList)", def, 0, false); - parseRule("`foo`(`bar`(.KList) => .K , .KList)", def, 0, false); - } - - // test user lists - @Test - public void test20() { - String def = "" + - "module TEST " + - "syntax Exp ::= Int \"(\" Ints \")\" " + - "syntax Exp ::= Int \"[\" Ints2 \"]\" " + - "syntax Ints ::= List{Int, \",\"} " + - "syntax Ints2 ::= NeList{Int, \".\"} " + - "syntax Int ::= r\"[0-9]+\" [token] " + - "endmodule"; - parseRule("0, 1, 2", def, 0, false); - parseProgram("0, 1, 2", def, "Ints", 0, false); - parseProgram("0()", def, "Exp", 0, false); - parseProgram("0[]", def, "Exp", 0, true); - } - - // test inference with overloading. - // regression test for issue #1586 - @Test - public void test21() { - String def = "" + - "module TEST " + - "syntax A ::= B | \"a\" " + - "syntax B ::= \"b\" " + - "syntax TA ::= TB | t(A) [klabel(t)] " + - "syntax TB ::= t(B) [klabel(t)] " + - "endmodule"; - parseRule("t(Y) => .K", def, 0, false); - parseRule("t(_) => .K", def, 0, false); - } - - // test inference with complicated ambiguity - // regression test for issue #1603 - @Test - public void test22() { - String def = "" + - "module TEST\n" + - "syntax T ::= \"wrap\" M [klabel(wrapM)] | \"wrap\" X [klabel(wrapX)]\n" + - "syntax A\n" + - "syntax B\n" + - "syntax M ::= A | B\n" + - "syntax N ::= A | B | X // make A and B both maximal in the intersection of N and M\n" + - "syntax X\n" + - "syntax KItem ::= label(N,T)\n" + - "endmodule"; - parseRule("X => label(X,wrap X)", def, 0, true); - } - - // automatic follow restriction should not be added for empty terminals - // regression test for issue #1575 - @Test - public void test23() { - String def = "" + - "module TEST " + - "syntax Stmts ::= Stmt \"\" Stmts\n" + - " | \".Stmts\"\n" + - " syntax Stmt ::= \"a\"" + - "endmodule"; - parseRule("a .Stmts", def, 0, false); - } - - // Should not infer sort KList for variable in arity-1 application of a klabel - @Test - public void test24() { - String def = "" + - "module TEST\n" + - "syntax A\n" + - "syntax A ::= f(A) [klabel(l), symbol]\n" + - "endmodule"; - parseRule("l(_) => .K", def, 0, - KApply(KLabel("#ruleNoConditions"),KApply(KLabel("#KRewrite"), - KApply(KLabel("l"), KApply(KLabel("#SemanticCastToA"), KToken("_",Sort("#KVariable")))), - KApply(KLabel("#EmptyK")) - ))); - } - - @Test - public void testPriorityAssoc() throws Exception { - String def = "module TEST " + - "syntax Exp ::= Exp \"*\" Exp [left, klabel('Mul)] " + - "> Exp \"+\" Exp [left, klabel('Plus)] " + - "| r\"[0-9]+\" [token] " + - "syntax left 'Plus " + - "syntax left 'Mul " + - "endmodule"; - parseProgram("1+2", def, "Exp", 0, false); - //System.out.println("out1 = " + out1); - parseProgram("1+2*3", def, "Exp", 0, false); - //System.out.println("out2 = " + out2); - parseProgram("1+2+3", def, "Exp", 0, false); - //System.out.println("out3 = " + out3); - } - - // test default/custom layout - @Test - public void testLayout() { - String defaultLayout = "" + - "module TEST " + - "syntax Int ::= Int \"+\" Int " + - "syntax Int ::= r\"[0-9]+\" [token] " + - "endmodule"; - parseProgram("0 + 3 // some text" , defaultLayout, "Int", 0, false); - parseProgram("0 + 3 /* some text */", defaultLayout, "Int", 0, false); - parseProgram("0 /* some text */ + 3", defaultLayout, "Int", 0, false); - parseProgram("0 + 3 ;; some text" , defaultLayout, "Int", 0, true); - - String customLayout = "" + - "module TEST " + - "syntax #Layout ::= r\"(\\\\(;([^;]|(;+([^;\\\\)])))*;\\\\))\"" + - "| r\"(;;[^\\\\n\\\\r]*)\"" + - "| r\"([\\\\ \\\\n\\\\r\\\\t])\"" + - "// -------------------------------------\n" + // make sure standard layout still works in K defn - "syntax Int ::= Int \"+\" Int " + - "syntax Int ::= r\"[0-9]+\" [token] " + - "endmodule"; - parseProgram("0 + 3 ;; some text" , customLayout, "Int", 0, false); - parseProgram("0 + 3 (; some text ;)", customLayout, "Int", 0, false); - parseProgram("0 (; some text ;) + 3", customLayout, "Int", 0, false); - parseProgram("0 + 3 // some text" , customLayout, "Int", 0, true); - } - - // test KAppToTermConsVisitor for issue #985 - @Test - public void test25() { - String def = "" + - "module TEST\n" + - " syntax E ::= \"a\" [klabel(elma), symbol]\n" + - " syntax Lst ::= E \",\" Lst [klabel(constr), symbol]\n" + - " syntax Lst2 ::= E \",\" Lst2 [klabel(constr), symbol]\n" + - " | Lst \n" + - " syntax S ::= tuple(E, E, S) [klabel(tuplee), symbol]\n" + - "endmodule"; - parseRule("constr(I, L) => L", def, 0, - KApply(KLabel("#ruleNoConditions"),KApply(KLabel("#KRewrite"), - KApply(KLabel("constr"), - KApply(KLabel("#SemanticCastToE"), KToken("I",Sort("#KVariable"))), - KApply(KLabel("#SemanticCastToLst2"), KToken("L",Sort("#KVariable")))), - KApply(KLabel("#SemanticCastToLst2"), KToken("L",Sort("#KVariable"))) - ))); - parseRule("`constr`(I, (a, L)) => L", def, 0, - KApply(KLabel("#ruleNoConditions"),KApply(KLabel("#KRewrite"), - KApply(KLabel("constr"), - KApply(KLabel("#SemanticCastToE"), KToken("I",Sort("#KVariable"))), - KApply(KLabel("constr"), - KApply(KLabel("elma")), - KApply(KLabel("#SemanticCastToLst2"), KToken("L",Sort("#KVariable"))))), - KApply(KLabel("#SemanticCastToLst2"), KToken("L",Sort("#KVariable"))) - ))); - parseRule("tuplee(A, B, C) => C", def, 0, - KApply(KLabel("#ruleNoConditions"),KApply(KLabel("#KRewrite"), - KApply(KLabel("tuplee"), - KApply(KLabel("#SemanticCastToE"), KToken("A",Sort("#KVariable"))), - KApply(KLabel("#SemanticCastToE"), KToken("B",Sort("#KVariable"))), - KApply(KLabel("#SemanticCastToS"), KToken("C",Sort("#KVariable")))), - KApply(KLabel("#SemanticCastToS"), KToken("C",Sort("#KVariable"))) - ))); - parseRule("constr(A, B, C) => .K", def, 0, false); // 3 amb at top 8 amb final - parseRule("constr(A, B, C, D) => .K", def, 0, false); // 17 amb at top 16 final - parseRule("constr(a, a, a) => .K", def, 0, true); // 'a' is not subsorted to the list - } - - @Test - public void test26() { - String def = "" + - "module TEST " + - "syntax Exp ::= \"\" " + - "endmodule"; - parseProgram("", def, "Exp", 0, false); - } + } else { + System.err.println("rule = " + rule._1().right().get()); + } + for (KEMException x : rule._2()) { + kem.addKException(x.getKException()); + } + kem.print(); + } + if (expectedError) Assert.assertTrue("Expected error here: ", rule._1().isLeft()); + else Assert.assertTrue("Expected no errors here: ", rule._1().isRight()); + Assert.assertEquals("Expected " + warnings + " warnings: ", warnings, rule._2().size()); + } + + // test proper associativity for rewrite, ~> and cast + @Test + public void test2() { + String def = + "" + + "module TEST " + + "syntax Exp ::= Exp \"+\" Exp [klabel('Plus), left] " + + "| r\"[0-9]+\" [token] " + + "syntax left 'Plus " + + "endmodule"; + parseRule("1+2=>A:Exp~>{B}:>Exp", def, 0, false); + } + + // test variable disambiguation when a variable is declared by the user + @Test + public void test3() { + String def = + "" + + "module TEST " + + "syntax Exps ::= Exp \",\" Exps [klabel('Exps)] " + + "| Exp " + + "syntax Exp ::= Id " + + "syntax Stmt ::= \"val\" Exps \";\" Stmt [klabel('Decl)] " + + "syntax {Sort} Sort ::= \"(\" Sort \")\" [bracket] " + + "syntax KItem ::= (Id, Stmt) [klabel('tuple)] " + + "syntax Id " + + "syntax K " + + "endmodule"; + parseRule("val X ; S:Stmt => (X, S)", def, 0, false); + } + + // test variable disambiguation when all variables are being inferred + @Test + public void test4() { + String def = + "" + + "module TEST " + + "syntax Exps ::= Exp \",\" Exps [klabel('Exps)] " + + "| Exp " + + "syntax Exp ::= Id " + + "syntax Stmt ::= \"val\" Exps \";\" Stmt [klabel('Decl)] " + + "syntax {Sort} Sort ::= \"(\" Sort \")\" [bracket] " + + "syntax KItem ::= (Id, Stmt) [klabel('tuple)] " + + "syntax Id " + + "syntax K " + + "endmodule"; + parseRule("val X ; S => (X, S)", def, 0, false); + } + + // test error reporting when + is non-associative + @Test + public void test5() { + String def = + "" + + "module TEST " + + "syntax Exp ::= Exp \"+\" Exp [klabel('Plus), non-assoc] " + + "| r\"[0-9]+\" [token] " + + "syntax non-assoc 'Plus " + + "endmodule"; + parseRule("1+2+3", def, 0, true); + } + + // test AmbFilter which should report ambiguities as errors + @Test + public void test6() { + String def = + "" + + "module TEST " + + "syntax Exp ::= Exp \"+\" Exp [klabel('Plus)] " + + "| r\"[0-9]+\" [token] " + + "endmodule"; + parseRule("1+2+3", def, 0, true); + } + + // test error reporting when rewrite priority is not met + @Test + public void test7() { + String def = + "" + + "module TEST " + + "syntax A ::= \"foo\" A [klabel('foo)] " + + "syntax B ::= \"bar\" [klabel('bar)] " + + "endmodule"; + parseRule("foo bar => X", def, 0, true); + } + + // test prefer and avoid + @Test + public void test8() { + String def = + "" + + "module TEST " + + "syntax Exp ::= Exp \"+\" Exp [klabel('Plus), prefer] " + + "| Exp \"*\" Exp [klabel('Mul)] " + + "| r\"[0-9]+\" [token] " + + "endmodule"; + parseRule("1+2*3", def, 0, false); + } + + // test cells + @Test + public void test9() { + String def = + "" + + "module TEST " + + "syntax Exp ::= Exp \"+\" Exp [klabel('Plus), prefer] " + + "| Exp \"*\" Exp [klabel('Mul)] " + + "| r\"[0-9]+\" [token] " + + "syntax K " + + "syntax TopCell ::= \"\" KCell StateCell \"\" [klabel(), cell] " + + "syntax KCell ::= \"\" K \"\" [klabel(), cell] " + + "syntax StateCell ::= \"\" K \"\" [klabel(), cell] " + + "endmodule"; + parseRule( + " ...1+2*3... ( A => .::K ... => .::Bag) ...", def, 0, false); + } + + // test rule cells + @Test + public void test10() { + String def = + "" + + "module TEST " + + "syntax Exp ::= Exp \",\" Exp [klabel('Plus), prefer] " + + "| Exp \"*\" Exp [klabel('Mul)] " + + "| r\"[0-9]+\" [token] " + + "endmodule"; + parseRule("A::KLabel(B::K, C::K, D::K)", def, 0, false); + } + + // test config cells + @Test + public void test11() { + String def = + "" + + "module TEST " + + "syntax Exp ::= Exp \"+\" Exp [klabel('Plus), prefer] " + + "| Exp \"*\" Exp [klabel('Mul)] " + + "| r\"[0-9]+\" [token] " + + "syntax K " + + "endmodule"; + parseConfig( + " 1+2*3 ( A => .::K => .::Bag) ", + def, + 0, + false); + } + + // test variable disambiguation when all variables are being inferred + @Test + public void test12() { + String def = + "" + + "module TEST " + + "syntax Stmt ::= \"val\" Exp \";\" Stmt [klabel('Decl)] " + + "syntax Exp " + + "syntax Stmt " + + "endmodule"; + parseRule("val _:Exp ; _", def, 0, false); + } + + // test priority exceptions (requires and ensures) + @Test + public void test13() { + String def = "" + "module TEST " + "syntax Bool ::= \"true\" [token] " + "endmodule"; + parseRule(".::K => .::K requires true", def, 0, false); + } + + // test automatic follow restriction for terminals + @Test + public void test14() { + String def = + "" + + "module TEST " + + "syntax Stmt ::= Stmt Stmt [klabel('Stmt)] " + + "syntax Exp ::= K \"==\" K [klabel('Eq)] " + + "syntax Exp ::= K \":\" K [klabel('Colon)] " + + "syntax K " + + "syntax Exp ::= K \"==K\" K [klabel('EqK)] " + + "syntax Exp ::= K \"?\" K \":\" K " + + "endmodule"; + parseRule("A::K ==K A", def, 0, false); + parseRule("A::K == K A", def, 0, true); + parseRule("A:K", def, 0, false); + parseRule("A: K", def, 0, false); + parseRule("A:Stmt ?F : Stmt", def, 0, false); + parseRule("A:Stmt ? F : Stmt", def, 0, false); + } + + // test whitespace + @Test + public void test15() { + String def = + "" + + "module TEST " + + "syntax Exp ::= Divide(K, K) [klabel('Divide)] " + + "syntax Exp ::= K \"/\" K [klabel('Div)] " + + "syntax K " + + "endmodule"; + parseRule("Divide(K1:K, K2:K) => K1:K / K2:K", def, 0, false); + } + + // test lexical to RegexTerminal extractor + @Test + public void test16() { + assertPatterns("", "#", "", "#"); + assertPatterns("abc", "#", "abc", "#"); + assertPatterns("(? .K", def, 0, false); + } + + // test special priority + @Test + public void test19() { + String def = + "" + + "module TEST \n" + + " syntax K \n" + + " syntax KItem ::= foo(K) [klabel(foo), symbol]\n" + + " syntax Bar ::= bar() [klabel(bar), symbol]\n" + + "endmodule"; + parseRule("`foo`(`bar`(.KList) ~> .K , .KList)", def, 0, false); + parseRule("`foo`(`bar`(.KList) => .K , .KList)", def, 0, false); + } + + // test user lists + @Test + public void test20() { + String def = + "" + + "module TEST " + + "syntax Exp ::= Int \"(\" Ints \")\" " + + "syntax Exp ::= Int \"[\" Ints2 \"]\" " + + "syntax Ints ::= List{Int, \",\"} " + + "syntax Ints2 ::= NeList{Int, \".\"} " + + "syntax Int ::= r\"[0-9]+\" [token] " + + "endmodule"; + parseRule("0, 1, 2", def, 0, false); + parseProgram("0, 1, 2", def, "Ints", 0, false); + parseProgram("0()", def, "Exp", 0, false); + parseProgram("0[]", def, "Exp", 0, true); + } + + // test inference with overloading. + // regression test for issue #1586 + @Test + public void test21() { + String def = + "" + + "module TEST " + + "syntax A ::= B | \"a\" " + + "syntax B ::= \"b\" " + + "syntax TA ::= TB | t(A) [klabel(t)] " + + "syntax TB ::= t(B) [klabel(t)] " + + "endmodule"; + parseRule("t(Y) => .K", def, 0, false); + parseRule("t(_) => .K", def, 0, false); + } + + // test inference with complicated ambiguity + // regression test for issue #1603 + @Test + public void test22() { + String def = + "" + + "module TEST\n" + + "syntax T ::= \"wrap\" M [klabel(wrapM)] | \"wrap\" X [klabel(wrapX)]\n" + + "syntax A\n" + + "syntax B\n" + + "syntax M ::= A | B\n" + + "syntax N ::= A | B | X // make A and B both maximal in the intersection of N and M\n" + + "syntax X\n" + + "syntax KItem ::= label(N,T)\n" + + "endmodule"; + parseRule("X => label(X,wrap X)", def, 0, true); + } + + // automatic follow restriction should not be added for empty terminals + // regression test for issue #1575 + @Test + public void test23() { + String def = + "" + + "module TEST " + + "syntax Stmts ::= Stmt \"\" Stmts\n" + + " | \".Stmts\"\n" + + " syntax Stmt ::= \"a\"" + + "endmodule"; + parseRule("a .Stmts", def, 0, false); + } + + // Should not infer sort KList for variable in arity-1 application of a klabel + @Test + public void test24() { + String def = + "" + + "module TEST\n" + + "syntax A\n" + + "syntax A ::= f(A) [klabel(l), symbol]\n" + + "endmodule"; + parseRule( + "l(_) => .K", + def, + 0, + KApply( + KLabel("#ruleNoConditions"), + KApply( + KLabel("#KRewrite"), + KApply( + KLabel("l"), + KApply(KLabel("#SemanticCastToA"), KToken("_", Sort("#KVariable")))), + KApply(KLabel("#EmptyK"))))); + } + + @Test + public void testPriorityAssoc() throws Exception { + String def = + "module TEST " + + "syntax Exp ::= Exp \"*\" Exp [left, klabel('Mul)] " + + "> Exp \"+\" Exp [left, klabel('Plus)] " + + "| r\"[0-9]+\" [token] " + + "syntax left 'Plus " + + "syntax left 'Mul " + + "endmodule"; + parseProgram("1+2", def, "Exp", 0, false); + // System.out.println("out1 = " + out1); + parseProgram("1+2*3", def, "Exp", 0, false); + // System.out.println("out2 = " + out2); + parseProgram("1+2+3", def, "Exp", 0, false); + // System.out.println("out3 = " + out3); + } + + // test default/custom layout + @Test + public void testLayout() { + String defaultLayout = + "" + + "module TEST " + + "syntax Int ::= Int \"+\" Int " + + "syntax Int ::= r\"[0-9]+\" [token] " + + "endmodule"; + parseProgram("0 + 3 // some text", defaultLayout, "Int", 0, false); + parseProgram("0 + 3 /* some text */", defaultLayout, "Int", 0, false); + parseProgram("0 /* some text */ + 3", defaultLayout, "Int", 0, false); + parseProgram("0 + 3 ;; some text", defaultLayout, "Int", 0, true); + + String customLayout = + "" + + "module TEST " + + "syntax #Layout ::= r\"(\\\\(;([^;]|(;+([^;\\\\)])))*;\\\\))\"" + + "| r\"(;;[^\\\\n\\\\r]*)\"" + + "| r\"([\\\\ \\\\n\\\\r\\\\t])\"" + + "// -------------------------------------\n" + + // make sure standard layout still works in K defn + "syntax Int ::= Int \"+\" Int " + + "syntax Int ::= r\"[0-9]+\" [token] " + + "endmodule"; + parseProgram("0 + 3 ;; some text", customLayout, "Int", 0, false); + parseProgram("0 + 3 (; some text ;)", customLayout, "Int", 0, false); + parseProgram("0 (; some text ;) + 3", customLayout, "Int", 0, false); + parseProgram("0 + 3 // some text", customLayout, "Int", 0, true); + } + + // test KAppToTermConsVisitor for issue #985 + @Test + public void test25() { + String def = + "" + + "module TEST\n" + + " syntax E ::= \"a\" [klabel(elma), symbol]\n" + + " syntax Lst ::= E \",\" Lst [klabel(constr), symbol]\n" + + " syntax Lst2 ::= E \",\" Lst2 [klabel(constr), symbol]\n" + + " | Lst \n" + + " syntax S ::= tuple(E, E, S) [klabel(tuplee), symbol]\n" + + "endmodule"; + parseRule( + "constr(I, L) => L", + def, + 0, + KApply( + KLabel("#ruleNoConditions"), + KApply( + KLabel("#KRewrite"), + KApply( + KLabel("constr"), + KApply(KLabel("#SemanticCastToE"), KToken("I", Sort("#KVariable"))), + KApply(KLabel("#SemanticCastToLst2"), KToken("L", Sort("#KVariable")))), + KApply(KLabel("#SemanticCastToLst2"), KToken("L", Sort("#KVariable")))))); + parseRule( + "`constr`(I, (a, L)) => L", + def, + 0, + KApply( + KLabel("#ruleNoConditions"), + KApply( + KLabel("#KRewrite"), + KApply( + KLabel("constr"), + KApply(KLabel("#SemanticCastToE"), KToken("I", Sort("#KVariable"))), + KApply( + KLabel("constr"), + KApply(KLabel("elma")), + KApply(KLabel("#SemanticCastToLst2"), KToken("L", Sort("#KVariable"))))), + KApply(KLabel("#SemanticCastToLst2"), KToken("L", Sort("#KVariable")))))); + parseRule( + "tuplee(A, B, C) => C", + def, + 0, + KApply( + KLabel("#ruleNoConditions"), + KApply( + KLabel("#KRewrite"), + KApply( + KLabel("tuplee"), + KApply(KLabel("#SemanticCastToE"), KToken("A", Sort("#KVariable"))), + KApply(KLabel("#SemanticCastToE"), KToken("B", Sort("#KVariable"))), + KApply(KLabel("#SemanticCastToS"), KToken("C", Sort("#KVariable")))), + KApply(KLabel("#SemanticCastToS"), KToken("C", Sort("#KVariable")))))); + parseRule("constr(A, B, C) => .K", def, 0, false); // 3 amb at top 8 amb final + parseRule("constr(A, B, C, D) => .K", def, 0, false); // 17 amb at top 16 final + parseRule("constr(a, a, a) => .K", def, 0, true); // 'a' is not subsorted to the list + } + + @Test + public void test26() { + String def = "" + "module TEST " + "syntax Exp ::= \"\" " + "endmodule"; + parseProgram("", def, "Exp", 0, false); + } } diff --git a/kernel/src/test/java/org/kframework/parser/inner/disambiguation/AddEmptyListsTest.java b/kernel/src/test/java/org/kframework/parser/inner/disambiguation/AddEmptyListsTest.java index 3e24d2fce8f..6769747df8b 100644 --- a/kernel/src/test/java/org/kframework/parser/inner/disambiguation/AddEmptyListsTest.java +++ b/kernel/src/test/java/org/kframework/parser/inner/disambiguation/AddEmptyListsTest.java @@ -1,7 +1,11 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.inner.disambiguation; +import static org.kframework.kore.KORE.*; + import com.google.common.collect.Lists; +import java.io.File; +import java.util.Set; import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; @@ -16,9 +20,9 @@ import org.kframework.kore.KApply; import org.kframework.kore.KLabel; import org.kframework.main.GlobalOptions; +import org.kframework.parser.ParserUtils; import org.kframework.parser.TreeNodesToKORE; import org.kframework.parser.inner.ParseInModule; -import org.kframework.parser.ParserUtils; import org.kframework.parser.inner.RuleGrammarGenerator; import org.kframework.parser.outer.Outer; import org.kframework.utils.errorsystem.KEMException; @@ -27,194 +31,196 @@ import scala.Tuple2; import scala.util.Either; -import java.io.File; -import java.util.Set; - -import static org.kframework.kore.KORE.*; - public class AddEmptyListsTest { - private ParseInModule parser; - private FileUtil files; - - @Rule - public TestName testName = new TestName(); - - @Before - public void setUp() throws Exception { - RuleGrammarGenerator gen = makeRuleGrammarGenerator(); - Module test = ParserUtils.parseMainModuleOuterSyntax(DEF, Source.apply("AddEmptyListsTest test definition"), "TEST"); - parser = RuleGrammarGenerator.getCombinedGrammar(gen.getRuleGrammar(test), true, files); - } - - /* - * Return a RuleGrammarGenerator which uses the default K syntax as loaded from kast.k - */ - private RuleGrammarGenerator makeRuleGrammarGenerator() { - String definitionText; - files = FileUtil.testFileUtil(); - ParserUtils parser = new ParserUtils(files, new KExceptionManager(new GlobalOptions())); - File definitionFile = new File(Kompile.BUILTIN_DIRECTORY + "/prelude.md"); - definitionText = files.loadFromWorkingDirectory(definitionFile.getPath()); - - Definition baseK = - parser.loadDefinition("K", "K", definitionText, - definitionFile, - definitionFile.getParentFile(), - Lists.newArrayList(Kompile.BUILTIN_DIRECTORY), - false, false, false); - - return new RuleGrammarGenerator(baseK); - } - - private void parseTerm(String term, String sort, K expected) { - parseTerm(term, sort, expected, 0); - } - - private void parseTerm(String term, String sort, K expected, int expectWarnings) { - String source = "AddEmpytListsTest." + testName.getMethodName(); - final Tuple2, K>, Set> parseResult - = parser.parseString(term, Sort(sort), new Source(source)); - if (parseResult._1().isLeft()) { - Assert.assertTrue("Unexpected parse errors" + parseResult._1().left().get(), false); - } - K actual = new TreeNodesToKORE(Outer::parseSort, false).down(parseResult._1().right().get()); - Assert.assertEquals(expected, actual); - if (parseResult._2().size() != expectWarnings) { - Assert.assertTrue("Unexpected parse warnings" + parseResult._2(), false); - } - } - - private static final String DEF = - "module TEST\n" + - "syntax A ::= \"a\" [klabel(\"alabel\"), symbol]\n" + - "syntax B ::= \"b\" [klabel(\"blabel\"), symbol]\n" + - "syntax A ::= B\n" + - "syntax As ::= List{A,\",\"} [klabel(as)]\n" + - "syntax Bs ::= List{B,\",\"} [klabel(as)]\n" + - "syntax As ::= Bs\n" + - "syntax K ::= f(As) [symbol] | g(A) [symbol] | h(Bs) [symbol]" + - "endmodule\n"; - - public static final KApply NIL = KApply(KLabel(".List{\"_,__TEST_Bs_B_Bs\"}_Bs")); - public static final KLabel BS = KLabel("_,__TEST_Bs_B_Bs"); - public static final KLabel AS = KLabel("_,__TEST_As_A_As"); - public static final KLabel CONS = BS; - public static final KApply A = KApply(KLabel("alabel")); - public static final KApply B = KApply(KLabel("blabel")); - public static final KLabel F = KLabel("f"); - public static final KLabel G = KLabel("g"); - public static final KLabel H = KLabel("h"); - public static final KLabel CAST_A = KLabel("#SemanticCastToA"); - public static final KLabel CAST_B = KLabel("#SemanticCastToB"); - public static final KLabel CAST_AS = KLabel("#SemanticCastToAs"); - public static final KLabel CAST_BS = KLabel("#SemanticCastToBs"); - - @Test - public void testEmptyList1() { - parseTerm(".As", "As", NIL); - } - - @Ignore("The API of AddEmptyLists needs to change for this to be possible") - @Test - public void testItem() { - parseTerm("a", "As", KApply(BS, A, NIL)); - } - - @Test - public void testConcreteTop() { - parseTerm(".As", "As", NIL); - parseTerm("a,a", "As", KApply(AS, A, KApply(AS, A, NIL))); - parseTerm("a,.As", "As", KApply(AS, A, NIL)); - parseTerm("a,b", "As", KApply(AS, A, KApply(CONS, B, NIL))); - parseTerm("b,.Bs", "As", KApply(CONS, B, NIL)); - parseTerm("b,b", "As", KApply(CONS, B, KApply(CONS, B, NIL))); - } - - @Test - public void testConcreteArgument() { - parseTerm("f(.As)", "K", KApply(F, NIL)); - parseTerm("f(a)", "K", KApply(F, KApply(AS, A, NIL))); - parseTerm("f(a,a)", "K", KApply(F, KApply(AS, A, KApply(AS, A, NIL)))); - parseTerm("f(a,.As)", "K", KApply(F, KApply(AS, A, NIL))); - parseTerm("f(a,b)", "K", KApply(F, KApply(AS, A, KApply(CONS, B, NIL)))); - parseTerm("f(b,.Bs)", "K", KApply(F, KApply(CONS, B, NIL))); - parseTerm("f(b,b)", "K", KApply(F, KApply(CONS, B, KApply(CONS, B, NIL)))); - } - - @Ignore("BUG: need to also propagate correct sorts to arguments of labeled application") - @Test - public void testLabeledFunSingleItem() { - parseTerm("`f`(a)", "K", KApply(F, KApply(CONS, A, NIL))); - } - - @Test - public void testLabedFunConcreteArgument() { - parseTerm("`f`(.As)", "K", KApply(F, NIL)); - parseTerm("`f`((a,a))", "K", KApply(F, KApply(AS, A, KApply(AS, A, NIL)))); - parseTerm("`f`((a,.As))", "K", KApply(F, KApply(AS, A, NIL))); - parseTerm("`f`((a,b))", "K", KApply(F, KApply(AS, A, KApply(CONS, B, NIL)))); - parseTerm("`f`((b,.Bs))", "K", KApply(F, KApply(CONS, B, NIL))); - parseTerm("`f`((b,b))", "K", KApply(F, KApply(CONS, B, KApply(CONS, B, NIL)))); - } - - @Test - public void testAnnVar() { - parseTerm("V:As", "K", KApply(CAST_AS, KVariable("V"))); - } - - @Test - public void testArgumentLabeledCons() { - parseTerm("f(a,.As)", "K", KApply(F, KApply(AS, A, NIL))); - } - - @Test - public void testArgumentLabeledNil() { - parseTerm("f(.As)", "K", KApply(F, NIL)); - } - - @Test - public void testArgumentLabeledConsSub1() { - parseTerm("h(b,.Bs)", "K", KApply(H, KApply(CONS, B, NIL))); - } - - @Test - public void testArgumentLabeledNilSub1() { - parseTerm("h(.Bs)", "K", KApply(H, NIL)); - } - - @Test - public void testArgumentInferredListVar() { - // 1 warning from inference - parseTerm("f(V)", "K", KApply(F, KApply(CAST_AS, KVariable("V")))); - } - - @Test - public void testArgumentAnnListVar() { - parseTerm("f(V:As)", "K", KApply(F, KApply(CAST_AS, KVariable("V")))); - } - - @Test - public void testArgumentAnnSubListVar() { - parseTerm("f(V:Bs)", "K", KApply(F, KApply(CAST_BS, KVariable("V")))); - } - - @Test - public void testArgumentInferredItemVar() { - // 1 warning from inference - parseTerm("f(V)~>g(V)", "K", - KSequence(KApply(F, KApply(AS, KApply(CAST_A, KVariable("V")), NIL)), - KApply(G, KApply(CAST_A, KVariable("V"))))); - } - - @Test - public void testArgumentAnnItemVar() { - parseTerm("f(V:A)", "K", - KApply(F, KApply(AS, KApply(CAST_A, KVariable("V")), NIL))); - } - - @Test - public void testArgumentAnnSubItemVar() { - parseTerm("f(V:B)", "K", - KApply(F, KApply(CONS, KApply(CAST_B, KVariable("V")), NIL))); - } + private ParseInModule parser; + private FileUtil files; + + @Rule public TestName testName = new TestName(); + + @Before + public void setUp() throws Exception { + RuleGrammarGenerator gen = makeRuleGrammarGenerator(); + Module test = + ParserUtils.parseMainModuleOuterSyntax( + DEF, Source.apply("AddEmptyListsTest test definition"), "TEST"); + parser = RuleGrammarGenerator.getCombinedGrammar(gen.getRuleGrammar(test), true, files); + } + + /* + * Return a RuleGrammarGenerator which uses the default K syntax as loaded from kast.k + */ + private RuleGrammarGenerator makeRuleGrammarGenerator() { + String definitionText; + files = FileUtil.testFileUtil(); + ParserUtils parser = new ParserUtils(files, new KExceptionManager(new GlobalOptions())); + File definitionFile = new File(Kompile.BUILTIN_DIRECTORY + "/prelude.md"); + definitionText = files.loadFromWorkingDirectory(definitionFile.getPath()); + + Definition baseK = + parser.loadDefinition( + "K", + "K", + definitionText, + definitionFile, + definitionFile.getParentFile(), + Lists.newArrayList(Kompile.BUILTIN_DIRECTORY), + false, + false, + false); + + return new RuleGrammarGenerator(baseK); + } + + private void parseTerm(String term, String sort, K expected) { + parseTerm(term, sort, expected, 0); + } + + private void parseTerm(String term, String sort, K expected, int expectWarnings) { + String source = "AddEmpytListsTest." + testName.getMethodName(); + final Tuple2, K>, Set> parseResult = + parser.parseString(term, Sort(sort), new Source(source)); + if (parseResult._1().isLeft()) { + Assert.assertTrue("Unexpected parse errors" + parseResult._1().left().get(), false); + } + K actual = new TreeNodesToKORE(Outer::parseSort, false).down(parseResult._1().right().get()); + Assert.assertEquals(expected, actual); + if (parseResult._2().size() != expectWarnings) { + Assert.assertTrue("Unexpected parse warnings" + parseResult._2(), false); + } + } + + private static final String DEF = + "module TEST\n" + + "syntax A ::= \"a\" [klabel(\"alabel\"), symbol]\n" + + "syntax B ::= \"b\" [klabel(\"blabel\"), symbol]\n" + + "syntax A ::= B\n" + + "syntax As ::= List{A,\",\"} [klabel(as)]\n" + + "syntax Bs ::= List{B,\",\"} [klabel(as)]\n" + + "syntax As ::= Bs\n" + + "syntax K ::= f(As) [symbol] | g(A) [symbol] | h(Bs) [symbol]" + + "endmodule\n"; + + public static final KApply NIL = KApply(KLabel(".List{\"_,__TEST_Bs_B_Bs\"}_Bs")); + public static final KLabel BS = KLabel("_,__TEST_Bs_B_Bs"); + public static final KLabel AS = KLabel("_,__TEST_As_A_As"); + public static final KLabel CONS = BS; + public static final KApply A = KApply(KLabel("alabel")); + public static final KApply B = KApply(KLabel("blabel")); + public static final KLabel F = KLabel("f"); + public static final KLabel G = KLabel("g"); + public static final KLabel H = KLabel("h"); + public static final KLabel CAST_A = KLabel("#SemanticCastToA"); + public static final KLabel CAST_B = KLabel("#SemanticCastToB"); + public static final KLabel CAST_AS = KLabel("#SemanticCastToAs"); + public static final KLabel CAST_BS = KLabel("#SemanticCastToBs"); + + @Test + public void testEmptyList1() { + parseTerm(".As", "As", NIL); + } + + @Ignore("The API of AddEmptyLists needs to change for this to be possible") + @Test + public void testItem() { + parseTerm("a", "As", KApply(BS, A, NIL)); + } + + @Test + public void testConcreteTop() { + parseTerm(".As", "As", NIL); + parseTerm("a,a", "As", KApply(AS, A, KApply(AS, A, NIL))); + parseTerm("a,.As", "As", KApply(AS, A, NIL)); + parseTerm("a,b", "As", KApply(AS, A, KApply(CONS, B, NIL))); + parseTerm("b,.Bs", "As", KApply(CONS, B, NIL)); + parseTerm("b,b", "As", KApply(CONS, B, KApply(CONS, B, NIL))); + } + + @Test + public void testConcreteArgument() { + parseTerm("f(.As)", "K", KApply(F, NIL)); + parseTerm("f(a)", "K", KApply(F, KApply(AS, A, NIL))); + parseTerm("f(a,a)", "K", KApply(F, KApply(AS, A, KApply(AS, A, NIL)))); + parseTerm("f(a,.As)", "K", KApply(F, KApply(AS, A, NIL))); + parseTerm("f(a,b)", "K", KApply(F, KApply(AS, A, KApply(CONS, B, NIL)))); + parseTerm("f(b,.Bs)", "K", KApply(F, KApply(CONS, B, NIL))); + parseTerm("f(b,b)", "K", KApply(F, KApply(CONS, B, KApply(CONS, B, NIL)))); + } + + @Ignore("BUG: need to also propagate correct sorts to arguments of labeled application") + @Test + public void testLabeledFunSingleItem() { + parseTerm("`f`(a)", "K", KApply(F, KApply(CONS, A, NIL))); + } + + @Test + public void testLabedFunConcreteArgument() { + parseTerm("`f`(.As)", "K", KApply(F, NIL)); + parseTerm("`f`((a,a))", "K", KApply(F, KApply(AS, A, KApply(AS, A, NIL)))); + parseTerm("`f`((a,.As))", "K", KApply(F, KApply(AS, A, NIL))); + parseTerm("`f`((a,b))", "K", KApply(F, KApply(AS, A, KApply(CONS, B, NIL)))); + parseTerm("`f`((b,.Bs))", "K", KApply(F, KApply(CONS, B, NIL))); + parseTerm("`f`((b,b))", "K", KApply(F, KApply(CONS, B, KApply(CONS, B, NIL)))); + } + + @Test + public void testAnnVar() { + parseTerm("V:As", "K", KApply(CAST_AS, KVariable("V"))); + } + + @Test + public void testArgumentLabeledCons() { + parseTerm("f(a,.As)", "K", KApply(F, KApply(AS, A, NIL))); + } + + @Test + public void testArgumentLabeledNil() { + parseTerm("f(.As)", "K", KApply(F, NIL)); + } + + @Test + public void testArgumentLabeledConsSub1() { + parseTerm("h(b,.Bs)", "K", KApply(H, KApply(CONS, B, NIL))); + } + + @Test + public void testArgumentLabeledNilSub1() { + parseTerm("h(.Bs)", "K", KApply(H, NIL)); + } + + @Test + public void testArgumentInferredListVar() { + // 1 warning from inference + parseTerm("f(V)", "K", KApply(F, KApply(CAST_AS, KVariable("V")))); + } + + @Test + public void testArgumentAnnListVar() { + parseTerm("f(V:As)", "K", KApply(F, KApply(CAST_AS, KVariable("V")))); + } + + @Test + public void testArgumentAnnSubListVar() { + parseTerm("f(V:Bs)", "K", KApply(F, KApply(CAST_BS, KVariable("V")))); + } + + @Test + public void testArgumentInferredItemVar() { + // 1 warning from inference + parseTerm( + "f(V)~>g(V)", + "K", + KSequence( + KApply(F, KApply(AS, KApply(CAST_A, KVariable("V")), NIL)), + KApply(G, KApply(CAST_A, KVariable("V"))))); + } + + @Test + public void testArgumentAnnItemVar() { + parseTerm("f(V:A)", "K", KApply(F, KApply(AS, KApply(CAST_A, KVariable("V")), NIL))); + } + + @Test + public void testArgumentAnnSubItemVar() { + parseTerm("f(V:B)", "K", KApply(F, KApply(CONS, KApply(CAST_B, KVariable("V")), NIL))); + } } diff --git a/kernel/src/test/java/org/kframework/parser/json/JsonSerializationTests.java b/kernel/src/test/java/org/kframework/parser/json/JsonSerializationTests.java index ca9e9b7868b..f2253d41575 100644 --- a/kernel/src/test/java/org/kframework/parser/json/JsonSerializationTests.java +++ b/kernel/src/test/java/org/kframework/parser/json/JsonSerializationTests.java @@ -1,7 +1,15 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.json; +import static org.kframework.Collections.immutable; +import static org.kframework.kore.KORE.*; + import com.google.common.collect.Sets; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import javax.json.Json; +import javax.json.JsonObjectBuilder; +import javax.json.JsonStructure; import junit.framework.Assert; import org.junit.Test; import org.kframework.attributes.Att; @@ -12,74 +20,76 @@ import org.kframework.parser.ParserUtils; import org.kframework.unparser.ToJson; -import javax.json.Json; -import javax.json.JsonObjectBuilder; -import javax.json.JsonStructure; -import java.io.UnsupportedEncodingException; -import java.nio.charset.StandardCharsets; - -import static org.kframework.Collections.immutable; -import static org.kframework.kore.KORE.*; - public class JsonSerializationTests { - @Test - public void test1() { - K k = KApply(KLabel("#ruleNoConditions"),KApply(KLabel("#KRewrite"), - KApply(KLabel("tuplee"), - KApply(KLabel("#SemanticCastToE"), KToken("A",Sort("#KVariable"))), - KApply(KLabel("#SemanticCastToE"), KToken("B",Sort("#KVariable"))), - KApply(KLabel("#SemanticCastToS"), KToken("C",Sort("#KVariable")))), - KApply(KLabel("#SemanticCastToS"), KToken("C",Sort("#KVariable"))) - )); + @Test + public void test1() { + K k = + KApply( + KLabel("#ruleNoConditions"), + KApply( + KLabel("#KRewrite"), + KApply( + KLabel("tuplee"), + KApply(KLabel("#SemanticCastToE"), KToken("A", Sort("#KVariable"))), + KApply(KLabel("#SemanticCastToE"), KToken("B", Sort("#KVariable"))), + KApply(KLabel("#SemanticCastToS"), KToken("C", Sort("#KVariable")))), + KApply(KLabel("#SemanticCastToS"), KToken("C", Sort("#KVariable"))))); - JsonStructure js = ToJson.toJson(k); - JsonObjectBuilder term = Json.createObjectBuilder(); - term.add("format", "KAST"); - term.add("version", ToJson.version); - term.add("term", js); - K k2 = JsonParser.parseJson(term.build()); - Assert.assertEquals(k, k2); - } + JsonStructure js = ToJson.toJson(k); + JsonObjectBuilder term = Json.createObjectBuilder(); + term.add("format", "KAST"); + term.add("version", ToJson.version); + term.add("term", js); + K k2 = JsonParser.parseJson(term.build()); + Assert.assertEquals(k, k2); + } - @Test - public void test2() { - K k = KApply(KLabel("#ruleNoConditions", Sort("X", Sort("6"), Sort("Z"))),KApply(KLabel("#KRewrite", Sort("P")), - KApply(KLabel("tuplee"), - KApply(KLabel("#SemanticCastToE"), KToken("A",Sort("#KVariable"))), - KApply(KLabel("#SemanticCastToE"), KToken("B",Sort("#KVariable"))), - KApply(KLabel("#SemanticCastToS"), KToken("C",Sort("#KVariable")))), - KApply(KLabel("#SemanticCastToS"), KToken("C",Sort("#KVariable"))) - )); + @Test + public void test2() { + K k = + KApply( + KLabel("#ruleNoConditions", Sort("X", Sort("6"), Sort("Z"))), + KApply( + KLabel("#KRewrite", Sort("P")), + KApply( + KLabel("tuplee"), + KApply(KLabel("#SemanticCastToE"), KToken("A", Sort("#KVariable"))), + KApply(KLabel("#SemanticCastToE"), KToken("B", Sort("#KVariable"))), + KApply(KLabel("#SemanticCastToS"), KToken("C", Sort("#KVariable")))), + KApply(KLabel("#SemanticCastToS"), KToken("C", Sort("#KVariable"))))); - JsonStructure js = ToJson.toJson(k); - JsonObjectBuilder term = Json.createObjectBuilder(); - term.add("format", "KAST"); - term.add("version", ToJson.version); - term.add("term", js); - K k2 = JsonParser.parseJson(term.build()); - Assert.assertEquals(k, k2); - } + JsonStructure js = ToJson.toJson(k); + JsonObjectBuilder term = Json.createObjectBuilder(); + term.add("format", "KAST"); + term.add("version", ToJson.version); + term.add("term", js); + K k2 = JsonParser.parseJson(term.build()); + Assert.assertEquals(k, k2); + } - @Test - public void test3() throws UnsupportedEncodingException { - String defstr = "" + - "module TEST " + - "syntax Exp ::= Exp \"+\" Exp [klabel('Plus), prefer] " + - "| Exp \"*\" Exp [klabel('Mul)] " + - "| r\"[0-9]+\" [token] " + - "syntax K " + - "syntax TopCell ::= \"\" KCell StateCell \"\" [klabel(), cell] " + - "syntax KCell ::= \"\" K \"\" [klabel(), cell] " + - "syntax StateCell ::= \"\" K \"\" [klabel(), cell] " + - "endmodule"; + @Test + public void test3() throws UnsupportedEncodingException { + String defstr = + "" + + "module TEST " + + "syntax Exp ::= Exp \"+\" Exp [klabel('Plus), prefer] " + + "| Exp \"*\" Exp [klabel('Mul)] " + + "| r\"[0-9]+\" [token] " + + "syntax K " + + "syntax TopCell ::= \"\" KCell StateCell \"\" [klabel(), cell] " + + "syntax KCell ::= \"\" K \"\" [klabel(), cell] " + + "syntax StateCell ::= \"\" K \"\" [klabel(), cell] " + + "endmodule"; - Module mod = ParserUtils.parseMainModuleOuterSyntax(defstr, Source.apply("generated by RuleGrammarTest"), "TEST"); - Definition def1 = Definition.apply(mod, immutable(Sets.newHashSet(mod)), Att.empty()); + Module mod = + ParserUtils.parseMainModuleOuterSyntax( + defstr, Source.apply("generated by RuleGrammarTest"), "TEST"); + Definition def1 = Definition.apply(mod, immutable(Sets.newHashSet(mod)), Att.empty()); - String inputDefinition = new String(ToJson.apply(def1), StandardCharsets.UTF_8); - Definition def2 = JsonParser.parseDefinition(inputDefinition); + String inputDefinition = new String(ToJson.apply(def1), StandardCharsets.UTF_8); + Definition def2 = JsonParser.parseDefinition(inputDefinition); - Assert.assertEquals(def1, def2); - } + Assert.assertEquals(def1, def2); + } } diff --git a/kernel/src/test/java/org/kframework/parser/outer/MDsourceTest.java b/kernel/src/test/java/org/kframework/parser/outer/MDsourceTest.java index 756b27c281c..0a737d9180a 100644 --- a/kernel/src/test/java/org/kframework/parser/outer/MDsourceTest.java +++ b/kernel/src/test/java/org/kframework/parser/outer/MDsourceTest.java @@ -10,95 +10,109 @@ import org.kframework.utils.errorsystem.KExceptionManager; public class MDsourceTest { - @Test - public void test1() { - String selectExp = "k"; - String mdTxt = "1\n" + - " ```k\n" + - " // ignore indentation\n" + - "require \"a.md\"\n" + - " ```\n" + - "6\n" + - "```{a b}\n" + - "8\n" + - "```\n" + - "10\n" + - "```{k x}\n" + - "module TEST\n" + - "```\n" + - "\n" + - "``` { k hidden warning\n" + - "}\n" + - "```\n" + - "\n" + - "```{y k}\n" + - "endmodule"; - GlobalOptions go = new GlobalOptions(); - go.warnings = GlobalOptions.Warnings.ALL; - KExceptionManager kem = new KExceptionManager(go); - ExtractFencedKCodeFromMarkdown mdExtractor = new ExtractFencedKCodeFromMarkdown(kem, selectExp); - String kTxt = mdExtractor.extract(mdTxt, new Source(MDsourceTest.class.toString())); - String expectedK = "\n" + - " \n" + - " // ignore indentation\n" + - "require \"a.md\"\n" + - " \n" + - "\n" + - " \n" + - "\n" + - "\n" + - "\n" + - " \n" + - "module TEST\n" + - "\n" + - "\n" + - " \n" + - "\n" + - "\n" + - "\n" + - " \n" + - "endmodule"; - Assert.assertEquals(expectedK, kTxt); - Assert.assertEquals(1, kem.getExceptions().size()); - } + @Test + public void test1() { + String selectExp = "k"; + String mdTxt = + "1\n" + + " ```k\n" + + " // ignore indentation\n" + + "require \"a.md\"\n" + + " ```\n" + + "6\n" + + "```{a b}\n" + + "8\n" + + "```\n" + + "10\n" + + "```{k x}\n" + + "module TEST\n" + + "```\n" + + "\n" + + "``` { k hidden warning\n" + + "}\n" + + "```\n" + + "\n" + + "```{y k}\n" + + "endmodule"; + GlobalOptions go = new GlobalOptions(); + go.warnings = GlobalOptions.Warnings.ALL; + KExceptionManager kem = new KExceptionManager(go); + ExtractFencedKCodeFromMarkdown mdExtractor = new ExtractFencedKCodeFromMarkdown(kem, selectExp); + String kTxt = mdExtractor.extract(mdTxt, new Source(MDsourceTest.class.toString())); + String expectedK = + "\n" + + " \n" + + " // ignore indentation\n" + + "require \"a.md\"\n" + + " \n" + + "\n" + + " \n" + + "\n" + + "\n" + + "\n" + + " \n" + + "module TEST\n" + + "\n" + + "\n" + + " \n" + + "\n" + + "\n" + + "\n" + + " \n" + + "endmodule"; + Assert.assertEquals(expectedK, kTxt); + Assert.assertEquals(1, kem.getExceptions().size()); + } - @Test(expected = KEMException.class) - public void test2() { - String selectExp = "k|"; - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - ExtractFencedKCodeFromMarkdown mdExtractor = new ExtractFencedKCodeFromMarkdown(kem, selectExp); - } + @Test(expected = KEMException.class) + public void test2() { + String selectExp = "k|"; + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + ExtractFencedKCodeFromMarkdown mdExtractor = new ExtractFencedKCodeFromMarkdown(kem, selectExp); + } - @Test(expected = KEMException.class) - public void test3() { - String selectExp = "k|a b"; - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - ExtractFencedKCodeFromMarkdown mdExtractor = new ExtractFencedKCodeFromMarkdown(kem, selectExp); - } + @Test(expected = KEMException.class) + public void test3() { + String selectExp = "k|a b"; + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + ExtractFencedKCodeFromMarkdown mdExtractor = new ExtractFencedKCodeFromMarkdown(kem, selectExp); + } - @Test - public void test4() { - KExceptionManager kem = new KExceptionManager(new GlobalOptions(false, GlobalOptions.Warnings.ALL, false)); - TagSelector.parseTags("k a", new Source(MDsourceTest.class.toString()), kem); - Assert.assertEquals(1, kem.getExceptions().size()); - } + @Test + public void test4() { + KExceptionManager kem = + new KExceptionManager(new GlobalOptions(false, GlobalOptions.Warnings.ALL, false)); + TagSelector.parseTags("k a", new Source(MDsourceTest.class.toString()), kem); + Assert.assertEquals(1, kem.getExceptions().size()); + } - @Test - public void test5() { - KExceptionManager kem = new KExceptionManager(new GlobalOptions(false, GlobalOptions.Warnings.ALL, false)); - TagSelector.parseTags("{k a", new Source(MDsourceTest.class.toString()), kem); - Assert.assertEquals(1, kem.getExceptions().size()); - } + @Test + public void test5() { + KExceptionManager kem = + new KExceptionManager(new GlobalOptions(false, GlobalOptions.Warnings.ALL, false)); + TagSelector.parseTags("{k a", new Source(MDsourceTest.class.toString()), kem); + Assert.assertEquals(1, kem.getExceptions().size()); + } - @Test - public void test6() { - KExceptionManager kem = new KExceptionManager(new GlobalOptions()); - Assert.assertEquals(2, TagSelector.parseTags("{k a}", new Source(MDsourceTest.class.toString()), kem).size()); - Assert.assertEquals(2, TagSelector.parseTags("{.k .a}", new Source(MDsourceTest.class.toString()), kem).size()); - Assert.assertEquals(2, TagSelector.parseTags("{.k a}", new Source(MDsourceTest.class.toString()), kem).size()); - Assert.assertEquals(1, TagSelector.parseTags("k", new Source(MDsourceTest.class.toString()), kem).size()); - Assert.assertEquals(0, TagSelector.parseTags("", new Source(MDsourceTest.class.toString()), kem).size()); - Assert.assertEquals(0, TagSelector.parseTags(" ", new Source(MDsourceTest.class.toString()), kem).size()); - Assert.assertEquals(3, TagSelector.parseTags("{.k .numberLines startFrom=\"0\"}", new Source(MDsourceTest.class.toString()), kem).size()); - } + @Test + public void test6() { + KExceptionManager kem = new KExceptionManager(new GlobalOptions()); + Assert.assertEquals( + 2, TagSelector.parseTags("{k a}", new Source(MDsourceTest.class.toString()), kem).size()); + Assert.assertEquals( + 2, TagSelector.parseTags("{.k .a}", new Source(MDsourceTest.class.toString()), kem).size()); + Assert.assertEquals( + 2, TagSelector.parseTags("{.k a}", new Source(MDsourceTest.class.toString()), kem).size()); + Assert.assertEquals( + 1, TagSelector.parseTags("k", new Source(MDsourceTest.class.toString()), kem).size()); + Assert.assertEquals( + 0, TagSelector.parseTags("", new Source(MDsourceTest.class.toString()), kem).size()); + Assert.assertEquals( + 0, TagSelector.parseTags(" ", new Source(MDsourceTest.class.toString()), kem).size()); + Assert.assertEquals( + 3, + TagSelector.parseTags( + "{.k .numberLines startFrom=\"0\"}", new Source(MDsourceTest.class.toString()), kem) + .size()); + } } diff --git a/kernel/src/test/java/org/kframework/parser/outer/OuterParsingTests.java b/kernel/src/test/java/org/kframework/parser/outer/OuterParsingTests.java index ab0db546896..8f2031c28f2 100644 --- a/kernel/src/test/java/org/kframework/parser/outer/OuterParsingTests.java +++ b/kernel/src/test/java/org/kframework/parser/outer/OuterParsingTests.java @@ -1,10 +1,10 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser.outer; -import java.util.List; +import static org.kframework.kore.KORE.*; +import java.util.List; import junit.framework.Assert; - import org.junit.Test; import org.kframework.attributes.Source; import org.kframework.builtin.Sorts; @@ -12,38 +12,40 @@ import org.kframework.kil.Module; import org.kframework.kil.StringSentence; -import static org.kframework.kore.KORE.*; - public class OuterParsingTests { - @Test - public void testEmptyRules() { - String def = "module TEST rule endmodule"; - - List defItemList = Outer.parse(Source.apply("generated by OuterParsingTests"), def, null); - - Module mod = (Module) defItemList.get(0); - StringSentence sen = (StringSentence) mod.getItems().get(0); - Assert.assertEquals(sen.getContent(), ""); - } - - @Test - public void testClaimKeyword() throws Exception { - String def = "module TEST claim X => Y endmodule"; - - List defItemList = Outer.parse(Source.apply("generated by OuterParsingTests"), def, null); - - Module mod = (Module) defItemList.get(0); - StringSentence sen = (StringSentence) mod.getItems().get(0); - Assert.assertEquals(sen.getContent(), "X => Y"); - Assert.assertEquals(sen.getType(), "claim"); - } - - @Test - public void testSortParsing() { - Assert.assertEquals(Sorts.K().toString(), Outer.parseSort(Sorts.K().toString()).toString()); - Assert.assertEquals(Sorts.MInt().toString(), Outer.parseSort(Sorts.MInt().toString()).toString()); - Assert.assertEquals(Sort("X", Sort("6"), Sort("Z")).toString(), - Outer.parseSort(Sort("X", Sort("6"), Sort("Z")).toString()).toString()); - } + @Test + public void testEmptyRules() { + String def = "module TEST rule endmodule"; + + List defItemList = + Outer.parse(Source.apply("generated by OuterParsingTests"), def, null); + + Module mod = (Module) defItemList.get(0); + StringSentence sen = (StringSentence) mod.getItems().get(0); + Assert.assertEquals(sen.getContent(), ""); + } + + @Test + public void testClaimKeyword() throws Exception { + String def = "module TEST claim X => Y endmodule"; + + List defItemList = + Outer.parse(Source.apply("generated by OuterParsingTests"), def, null); + + Module mod = (Module) defItemList.get(0); + StringSentence sen = (StringSentence) mod.getItems().get(0); + Assert.assertEquals(sen.getContent(), "X => Y"); + Assert.assertEquals(sen.getType(), "claim"); + } + + @Test + public void testSortParsing() { + Assert.assertEquals(Sorts.K().toString(), Outer.parseSort(Sorts.K().toString()).toString()); + Assert.assertEquals( + Sorts.MInt().toString(), Outer.parseSort(Sorts.MInt().toString()).toString()); + Assert.assertEquals( + Sort("X", Sort("6"), Sort("Z")).toString(), + Outer.parseSort(Sort("X", Sort("6"), Sort("Z")).toString()).toString()); + } } diff --git a/kernel/src/test/java/org/kframework/unparser/AddBracketsTest.java b/kernel/src/test/java/org/kframework/unparser/AddBracketsTest.java index 9242ae61968..0a095e2c09d 100644 --- a/kernel/src/test/java/org/kframework/unparser/AddBracketsTest.java +++ b/kernel/src/test/java/org/kframework/unparser/AddBracketsTest.java @@ -1,7 +1,11 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.unparser; +import static org.junit.Assert.*; + import com.google.common.collect.Lists; +import java.io.File; +import java.util.Set; import org.junit.Before; import org.junit.Test; import org.kframework.attributes.Source; @@ -11,9 +15,9 @@ import org.kframework.kompile.Kompile; import org.kframework.kore.K; import org.kframework.main.GlobalOptions; +import org.kframework.parser.ParserUtils; import org.kframework.parser.TreeNodesToKORE; import org.kframework.parser.inner.ParseInModule; -import org.kframework.parser.ParserUtils; import org.kframework.parser.inner.RuleGrammarGenerator; import org.kframework.parser.outer.Outer; import org.kframework.utils.errorsystem.KEMException; @@ -22,85 +26,90 @@ import scala.Tuple2; import scala.util.Either; -import java.io.File; -import java.util.Set; - -import static org.junit.Assert.*; - public class AddBracketsTest { - private RuleGrammarGenerator gen; - private FileUtil files; - - @Before - public void setUp() throws Exception{ - gen = makeRuleGrammarGenerator(); - } + private RuleGrammarGenerator gen; + private FileUtil files; - public RuleGrammarGenerator makeRuleGrammarGenerator() { - String definitionText; - files = FileUtil.testFileUtil(); - ParserUtils parser = new ParserUtils(files, new KExceptionManager(new GlobalOptions())); - File definitionFile = new File(Kompile.BUILTIN_DIRECTORY + "/prelude.md"); - definitionText = files.loadFromWorkingDirectory(definitionFile.getPath()); + @Before + public void setUp() throws Exception { + gen = makeRuleGrammarGenerator(); + } - Definition baseK = - parser.loadDefinition("K", "K", definitionText, - definitionFile, - definitionFile.getParentFile(), - Lists.newArrayList(Kompile.BUILTIN_DIRECTORY), - false, false, false); + public RuleGrammarGenerator makeRuleGrammarGenerator() { + String definitionText; + files = FileUtil.testFileUtil(); + ParserUtils parser = new ParserUtils(files, new KExceptionManager(new GlobalOptions())); + File definitionFile = new File(Kompile.BUILTIN_DIRECTORY + "/prelude.md"); + definitionText = files.loadFromWorkingDirectory(definitionFile.getPath()); - return new RuleGrammarGenerator(baseK); - } + Definition baseK = + parser.loadDefinition( + "K", + "K", + definitionText, + definitionFile, + definitionFile.getParentFile(), + Lists.newArrayList(Kompile.BUILTIN_DIRECTORY), + false, + false, + false); - private Module parseModule(String def) { - return ParserUtils.parseMainModuleOuterSyntax(def, Source.apply("generated by AddBracketsTest"), "TEST"); - } + return new RuleGrammarGenerator(baseK); + } - @Test - public void testLambda() { - String def = "module TEST\n" + - " syntax Val ::= Id\n" + - " | \"lambda\" Id \".\" Exp\n" + - " syntax Exp ::= Val\n" + - " | Exp Exp [left]\n" + - " | \"(\" Exp \")\" [bracket]\n" + - " syntax Id ::= r\"(? _+__TEST\n" + - "endmodule\n"; - unparserTest(def, "1 + 1 + 1 + 1"); - unparserTest(def, "1 + ( 1 + 1 ) + 1"); - unparserTest(def, "1 + ( 1 + ( 1 + 1 ) )"); - unparserTest(def, "1 + 1 * 1"); - unparserTest(def, "( 1 + 1 ) * 1"); - } + @Test + public void testLambda() { + String def = + "module TEST\n" + + " syntax Val ::= Id\n" + + " | \"lambda\" Id \".\" Exp\n" + + " syntax Exp ::= Val\n" + + " | Exp Exp [left]\n" + + " | \"(\" Exp \")\" [bracket]\n" + + " syntax Id ::= r\"(? _+__TEST\n" + + "endmodule\n"; + unparserTest(def, "1 + 1 + 1 + 1"); + unparserTest(def, "1 + ( 1 + 1 ) + 1"); + unparserTest(def, "1 + ( 1 + ( 1 + 1 ) )"); + unparserTest(def, "1 + 1 * 1"); + unparserTest(def, "( 1 + 1 ) * 1"); + } - private K parseTerm(String pgm, ParseInModule parser) { - Tuple2, K>, Set> result = parser.parseString(pgm, Sorts.KItem(), Source.apply("generated by AddBracketsTest")); - assertEquals(0, result._2().size()); - return new TreeNodesToKORE(Outer::parseSort, false).down(result._1().right().get()); - } + private void unparserTest(String def, String pgm) { + Module test = parseModule(def); + ParseInModule parser = + RuleGrammarGenerator.getCombinedGrammar(gen.getProgramsGrammar(test), true, files); + K parsed = parseTerm(pgm, parser); + KPrint kprint = new KPrint(); + String unparsed = kprint.unparseTerm(parsed, test); + assertEquals(pgm, unparsed); + } + private K parseTerm(String pgm, ParseInModule parser) { + Tuple2, K>, Set> result = + parser.parseString(pgm, Sorts.KItem(), Source.apply("generated by AddBracketsTest")); + assertEquals(0, result._2().size()); + return new TreeNodesToKORE(Outer::parseSort, false).down(result._1().right().get()); + } } diff --git a/kernel/src/test/java/org/kframework/unparser/BinaryKASTTest.java b/kernel/src/test/java/org/kframework/unparser/BinaryKASTTest.java index eb5ccb4af26..192be2d42b3 100644 --- a/kernel/src/test/java/org/kframework/unparser/BinaryKASTTest.java +++ b/kernel/src/test/java/org/kframework/unparser/BinaryKASTTest.java @@ -1,50 +1,58 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.unparser; +import static org.junit.Assert.*; +import static org.kframework.kore.KORE.*; + +import java.io.File; import org.junit.Ignore; import org.junit.Test; import org.kframework.kore.K; import org.kframework.parser.binary.BinaryParser; import org.kframework.utils.file.FileUtil; -import java.io.File; - -import static org.junit.Assert.*; -import static org.kframework.kore.KORE.*; - public class BinaryKASTTest { - K sharedTerm = KApply(KLabel("_|->_"), KToken("x", Sort("Id")), KToken("1", Sort("Int"))); - K sharedTerm2 = KToken("foo", Sort("Bar")); - - K term = KApply(KLabel(""), KApply(KLabel(""), KSequence(sharedTerm2, - KRewrite(KVariable("Baz"), KVariable("Baz2")), InjectedKLabel(KLabel("_+_")), KApply(KLabel("foo")))), - KApply(KVariable("Lbl"), sharedTerm, sharedTerm, sharedTerm2, sharedTerm)); - - @Test - public void testWriteThenRead() throws Exception { - byte[] str = ToBinary.apply(term); - K result2 = BinaryParser.parse(str); - assertEquals(term, result2); - } - - @Test - public void testConcatenate() throws Exception { - byte[] str = ToBinary.apply(term); - byte[] krewrite = new byte[str.length * 2 - 8]; - System.arraycopy(str, 0, krewrite, 0, str.length - 1); - System.arraycopy(str, 8, krewrite, str.length - 1, str.length - 9); - krewrite[krewrite.length - 2] = BinaryParser.KREWRITE; - krewrite[krewrite.length - 1] = BinaryParser.END; - K result2 = BinaryParser.parse(krewrite); - assertEquals(KRewrite(term, term), result2); - } - - @Test @Ignore - public void testLarger() throws Exception { - FileUtil.testFileUtil(); - byte[] kast = FileUtil.loadBytes(new File("/home/dwightguth/c-semantics/tmp-kcc-FzjROvt")); - K result = BinaryParser.parse(kast); - System.out.println(ToKast.apply(result)); - } + K sharedTerm = KApply(KLabel("_|->_"), KToken("x", Sort("Id")), KToken("1", Sort("Int"))); + K sharedTerm2 = KToken("foo", Sort("Bar")); + + K term = + KApply( + KLabel(""), + KApply( + KLabel(""), + KSequence( + sharedTerm2, + KRewrite(KVariable("Baz"), KVariable("Baz2")), + InjectedKLabel(KLabel("_+_")), + KApply(KLabel("foo")))), + KApply(KVariable("Lbl"), sharedTerm, sharedTerm, sharedTerm2, sharedTerm)); + + @Test + public void testWriteThenRead() throws Exception { + byte[] str = ToBinary.apply(term); + K result2 = BinaryParser.parse(str); + assertEquals(term, result2); + } + + @Test + public void testConcatenate() throws Exception { + byte[] str = ToBinary.apply(term); + byte[] krewrite = new byte[str.length * 2 - 8]; + System.arraycopy(str, 0, krewrite, 0, str.length - 1); + System.arraycopy(str, 8, krewrite, str.length - 1, str.length - 9); + krewrite[krewrite.length - 2] = BinaryParser.KREWRITE; + krewrite[krewrite.length - 1] = BinaryParser.END; + K result2 = BinaryParser.parse(krewrite); + assertEquals(KRewrite(term, term), result2); + } + + @Test + @Ignore + public void testLarger() throws Exception { + FileUtil.testFileUtil(); + byte[] kast = FileUtil.loadBytes(new File("/home/dwightguth/c-semantics/tmp-kcc-FzjROvt")); + K result = BinaryParser.parse(kast); + System.out.println(ToKast.apply(result)); + } } diff --git a/kernel/src/test/java/org/kframework/unparser/KPrintTest.java b/kernel/src/test/java/org/kframework/unparser/KPrintTest.java index 05e7cd185d5..cc550fd6818 100644 --- a/kernel/src/test/java/org/kframework/unparser/KPrintTest.java +++ b/kernel/src/test/java/org/kframework/unparser/KPrintTest.java @@ -1,6 +1,12 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.unparser; +import static org.junit.Assert.*; +import static org.kframework.kore.KORE.*; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; import org.junit.Test; import org.kframework.attributes.Source; import org.kframework.kore.K; @@ -8,63 +14,55 @@ import org.kframework.parser.json.JsonParser; import org.kframework.parser.kast.KastParser; -import java.io.UnsupportedEncodingException; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; - -import static org.junit.Assert.*; -import static org.kframework.kore.KORE.*; - public class KPrintTest { - private K cell(String cellName, K cellContent) { - return KApply(KLabel(cellName), cellContent); - } + private K cell(String cellName, K cellContent) { + return KApply(KLabel(cellName), cellContent); + } - OutputModes[] outputModes = new OutputModes[] { OutputModes.JSON - , OutputModes.BINARY - , OutputModes.KAST - }; + OutputModes[] outputModes = + new OutputModes[] {OutputModes.JSON, OutputModes.BINARY, OutputModes.KAST}; - private String bytes2String(byte[] input) { - return new String(input, StandardCharsets.UTF_8); - } + private String bytes2String(byte[] input) { + return new String(input, StandardCharsets.UTF_8); + } - private String asKast(K term) { - return bytes2String(KPrint.serialize(term, OutputModes.KAST)); - } + private String asKast(K term) { + return bytes2String(KPrint.serialize(term, OutputModes.KAST)); + } - private K unparseThenParse(K origTerm, OutputModes outputMode) { - byte[] unparsed = KPrint.serialize(origTerm, outputMode); - return switch (outputMode) { - case JSON -> JsonParser.parse(unparsed); - case BINARY -> BinaryParser.parse(unparsed); - case KAST -> KastParser.parse(bytes2String(unparsed), new Source("KPrintTest")); - default -> KToken("###", Sort("UnsupportedOutputMode")); - }; - } + private K unparseThenParse(K origTerm, OutputModes outputMode) { + byte[] unparsed = KPrint.serialize(origTerm, outputMode); + return switch (outputMode) { + case JSON -> JsonParser.parse(unparsed); + case BINARY -> BinaryParser.parse(unparsed); + case KAST -> KastParser.parse(bytes2String(unparsed), new Source("KPrintTest")); + default -> KToken("###", Sort("UnsupportedOutputMode")); + }; + } - @Test - public void testUnparseThenParse() throws Exception { + @Test + public void testUnparseThenParse() throws Exception { - List terms = new ArrayList<>(); - terms.add(KApply(KLabel("_|->_"), KToken("x", Sort("Id")), KToken("1", Sort("Int")))); - terms.add( KToken("foo", Sort("Bar")) ); - terms.add( KApply(KLabel("_+_"), KVariable("Baz"), KVariable("Baz2")) ); - terms.add( cell("", KSequence( terms.get(1) - , terms.get(2) - , InjectedKLabel(KLabel("_+_")) - , KApply(KLabel("foo")) - ) - ) - ); - terms.add( KApply(KLabel(""), terms.get(3), KApply(KVariable("Lbl"), terms.get(0), terms.get(0), terms.get(1), terms.get(0))) ); + List terms = new ArrayList<>(); + terms.add(KApply(KLabel("_|->_"), KToken("x", Sort("Id")), KToken("1", Sort("Int")))); + terms.add(KToken("foo", Sort("Bar"))); + terms.add(KApply(KLabel("_+_"), KVariable("Baz"), KVariable("Baz2"))); + terms.add( + cell( + "", + KSequence( + terms.get(1), terms.get(2), InjectedKLabel(KLabel("_+_")), KApply(KLabel("foo"))))); + terms.add( + KApply( + KLabel(""), + terms.get(3), + KApply(KVariable("Lbl"), terms.get(0), terms.get(0), terms.get(1), terms.get(0)))); - for (K term: terms) { - for (OutputModes outputMode: outputModes) { - assertEquals(asKast(term), asKast(unparseThenParse(term, outputMode))); - } - } + for (K term : terms) { + for (OutputModes outputMode : outputModes) { + assertEquals(asKast(term), asKast(unparseThenParse(term, outputMode))); + } } + } } diff --git a/kernel/src/test/java/org/kframework/utils/BaseTestCase.java b/kernel/src/test/java/org/kframework/utils/BaseTestCase.java index 4ccafc4829a..aec5cb88aad 100644 --- a/kernel/src/test/java/org/kframework/utils/BaseTestCase.java +++ b/kernel/src/test/java/org/kframework/utils/BaseTestCase.java @@ -6,6 +6,7 @@ import com.google.inject.Key; import com.google.inject.Provides; import com.google.inject.name.Names; +import java.io.File; import org.junit.Before; import org.junit.runner.RunWith; import org.kframework.kast.KastOptions; @@ -14,8 +15,8 @@ import org.kframework.kompile.KompileOptions; import org.kframework.main.Main; import org.kframework.utils.errorsystem.KExceptionManager; -import org.kframework.utils.file.FileUtil; import org.kframework.utils.file.DefinitionDir; +import org.kframework.utils.file.FileUtil; import org.kframework.utils.file.KompiledDir; import org.kframework.utils.inject.Concrete; import org.kframework.utils.inject.DefinitionScope; @@ -23,84 +24,70 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import java.io.File; - @RunWith(MockitoJUnitRunner.class) public abstract class BaseTestCase { - @Mock - protected Context context; + @Mock protected Context context; - @Mock - protected Definition definition; + @Mock protected Definition definition; - @Mock - protected KExceptionManager kem; + @Mock protected KExceptionManager kem; - @Mock - protected Stopwatch sw; + @Mock protected Stopwatch sw; - @Mock - protected BinaryLoader loader; + @Mock protected BinaryLoader loader; - @Mock - protected RunProcess rp; + @Mock protected RunProcess rp; - @Mock - protected - File kompiledDir; + @Mock protected File kompiledDir; - @Mock - File definitionDir; + @Mock File definitionDir; - @Mock - File tempDir; + @Mock File tempDir; - @Mock - protected FileUtil files; + @Mock protected FileUtil files; - @Mock - protected DefinitionScope scope; + @Mock protected DefinitionScope scope; - @Before - public void setUpWiring() { - context.kompileOptions = new KompileOptions(); - } - - public class DefinitionSpecificTestModule extends AbstractModule { - - @Override - protected void configure() { - binder().requireAtInjectOnConstructors(); - bind(KompileOptions.class).toInstance(context.kompileOptions); - bind(Definition.class).toInstance(definition); - bind(File.class).annotatedWith(KompiledDir.class).toInstance(kompiledDir); - bind(File.class).annotatedWith(DefinitionDir.class).toInstance(definitionDir); - bind(Definition.class).annotatedWith(Concrete.class).toInstance(definition); - } - - @Provides - Context context() { - return context; - } - } + @Before + public void setUpWiring() { + context.kompileOptions = new KompileOptions(); + } - public class TestModule extends AbstractModule { + public class DefinitionSpecificTestModule extends AbstractModule { - @Override - protected void configure() { - binder().requireAtInjectOnConstructors(); - bind(RunProcess.class).toInstance(rp); - bind(KastOptions.class).toInstance(new KastOptions()); - } + @Override + protected void configure() { + binder().requireAtInjectOnConstructors(); + bind(KompileOptions.class).toInstance(context.kompileOptions); + bind(Definition.class).toInstance(definition); + bind(File.class).annotatedWith(KompiledDir.class).toInstance(kompiledDir); + bind(File.class).annotatedWith(DefinitionDir.class).toInstance(definitionDir); + bind(Definition.class).annotatedWith(Concrete.class).toInstance(definition); + } + @Provides + Context context() { + return context; } + } + + public class TestModule extends AbstractModule { - public void prepInjector(Injector injector, String tool, String[] args) { - SimpleScope scope = injector.getInstance(Key.get(SimpleScope.class, Names.named("requestScope"))); - scope.enter(); - DefinitionScope definitionScope = injector.getInstance(DefinitionScope.class); - definitionScope.enter(new File(".")); - Main.seedInjector(scope, tool, args, new File("."), System.getenv(), System.nanoTime()); + @Override + protected void configure() { + binder().requireAtInjectOnConstructors(); + bind(RunProcess.class).toInstance(rp); + bind(KastOptions.class).toInstance(new KastOptions()); } + } + + public void prepInjector(Injector injector, String tool, String[] args) { + SimpleScope scope = + injector.getInstance(Key.get(SimpleScope.class, Names.named("requestScope"))); + scope.enter(); + DefinitionScope definitionScope = injector.getInstance(DefinitionScope.class); + definitionScope.enter(new File(".")); + Main.seedInjector(scope, tool, args, new File("."), System.getenv(), System.nanoTime()); + } } diff --git a/kernel/src/test/java/org/kframework/utils/ColorUtilTest.java b/kernel/src/test/java/org/kframework/utils/ColorUtilTest.java index 8e75c06d4ad..371df2b4715 100644 --- a/kernel/src/test/java/org/kframework/utils/ColorUtilTest.java +++ b/kernel/src/test/java/org/kframework/utils/ColorUtilTest.java @@ -1,18 +1,18 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils; +import static org.junit.Assert.*; + import org.junit.Test; import org.kframework.unparser.ColorSetting; -import static org.junit.Assert.*; - public class ColorUtilTest { - @Test - public void testGetColor() { - // set java.awt.headless so that we don't activate the awt bug (see issue #982) - System.setProperty("java.awt.headless", "true"); - assertEquals("", ColorUtil.RgbToAnsi("red", ColorSetting.OFF)); - assertEquals("\u001b[31m", ColorUtil.RgbToAnsi("red", ColorSetting.ON)); - } + @Test + public void testGetColor() { + // set java.awt.headless so that we don't activate the awt bug (see issue #982) + System.setProperty("java.awt.headless", "true"); + assertEquals("", ColorUtil.RgbToAnsi("red", ColorSetting.OFF)); + assertEquals("\u001b[31m", ColorUtil.RgbToAnsi("red", ColorSetting.ON)); + } } diff --git a/kernel/src/test/java/org/kframework/utils/IOTestCase.java b/kernel/src/test/java/org/kframework/utils/IOTestCase.java index ce5559e5c95..ad827bd52d2 100644 --- a/kernel/src/test/java/org/kframework/utils/IOTestCase.java +++ b/kernel/src/test/java/org/kframework/utils/IOTestCase.java @@ -3,27 +3,26 @@ import java.io.ByteArrayOutputStream; import java.io.PrintStream; - import org.junit.After; import org.junit.Before; public abstract class IOTestCase extends BaseTestCase { - PrintStream oldOut, oldErr; - protected ByteArrayOutputStream stdout = new ByteArrayOutputStream(); - protected ByteArrayOutputStream stderr = new ByteArrayOutputStream(); + PrintStream oldOut, oldErr; + protected ByteArrayOutputStream stdout = new ByteArrayOutputStream(); + protected ByteArrayOutputStream stderr = new ByteArrayOutputStream(); - @Before - public void setUpIO() { - oldOut = System.out; - oldErr = System.err; - System.setOut(new PrintStream(stdout)); - System.setErr(new PrintStream(stderr)); - } + @Before + public void setUpIO() { + oldOut = System.out; + oldErr = System.err; + System.setOut(new PrintStream(stdout)); + System.setErr(new PrintStream(stderr)); + } - @After - public void tearDownIO() { - System.setOut(oldOut); - System.setErr(oldErr); - } + @After + public void tearDownIO() { + System.setOut(oldOut); + System.setErr(oldErr); + } } diff --git a/kernel/src/test/java/org/kframework/utils/StringUtilTest.java b/kernel/src/test/java/org/kframework/utils/StringUtilTest.java index 7ff9e1ca280..7e5a312049c 100644 --- a/kernel/src/test/java/org/kframework/utils/StringUtilTest.java +++ b/kernel/src/test/java/org/kframework/utils/StringUtilTest.java @@ -1,111 +1,102 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils; +import java.util.regex.Pattern; import junit.framework.Assert; import org.junit.Test; import org.kframework.backend.kore.ModuleToKORE; -import java.util.regex.Pattern; - public class StringUtilTest { - @Test - public void StringUtilUnquote() throws Exception { - Assert.assertTrue(StringUtil.unquoteKString("\"\\n\"").equals("\n")); - Assert.assertTrue(StringUtil.unquoteKString("\"\\r\"").equals("\r")); - Assert.assertTrue(StringUtil.unquoteKString("\"\\f\"").equals("\f")); - Assert.assertTrue(StringUtil.unquoteKString("\"\\t\"").equals("\t")); + @Test + public void StringUtilUnquote() throws Exception { + Assert.assertTrue(StringUtil.unquoteKString("\"\\n\"").equals("\n")); + Assert.assertTrue(StringUtil.unquoteKString("\"\\r\"").equals("\r")); + Assert.assertTrue(StringUtil.unquoteKString("\"\\f\"").equals("\f")); + Assert.assertTrue(StringUtil.unquoteKString("\"\\t\"").equals("\t")); - Assert.assertTrue(StringUtil.unquoteKString("\"\\x20\"").equals(" ")); - Assert.assertTrue(StringUtil.unquoteKString("\"\\u0020\"").equals(" ")); - Assert.assertTrue(StringUtil.unquoteKString("\"\\U00000020\"").equals(" ")); + Assert.assertTrue(StringUtil.unquoteKString("\"\\x20\"").equals(" ")); + Assert.assertTrue(StringUtil.unquoteKString("\"\\u0020\"").equals(" ")); + Assert.assertTrue(StringUtil.unquoteKString("\"\\U00000020\"").equals(" ")); - Assert.assertTrue(StringUtil.unquoteKString("\"\\U00000020\"").equals(" ")); + Assert.assertTrue(StringUtil.unquoteKString("\"\\U00000020\"").equals(" ")); - try { - StringUtil.unquoteKString("\"\\U00110000\""); - throw new AssertionError(); - } catch (IllegalArgumentException e) { - } - try { - StringUtil.unquoteKString("\"\\U0000d801\""); - throw new AssertionError(); - } catch (IllegalArgumentException e) { - } + try { + StringUtil.unquoteKString("\"\\U00110000\""); + throw new AssertionError(); + } catch (IllegalArgumentException e) { } - - @Test - public void decodeKoreString() { - Assert.assertEquals("_V_9", StringUtil.decodeKoreString("'Unds'V'Unds'9")); - Assert.assertEquals("?_0", StringUtil.decodeKoreString("'QuesUnds'0")); - Pattern identChar = Pattern.compile("[A-Za-z0-9\\-]"); - StringBuilder buffer = new StringBuilder(); - StringUtil.encodeStringToAlphanumeric(buffer, "?_0", ModuleToKORE.asciiReadableEncodingKore, identChar, "'"); - Assert.assertEquals("'QuesUnds'0", buffer.toString()); - buffer.setLength(0); - StringUtil.encodeStringToAlphanumeric(buffer, "_V_9", ModuleToKORE.asciiReadableEncodingKore, identChar, "'"); - Assert.assertEquals("'Unds'V'Unds'9", buffer.toString()); + try { + StringUtil.unquoteKString("\"\\U0000d801\""); + throw new AssertionError(); + } catch (IllegalArgumentException e) { } + } - @Test - public void testBijection() { - char[] all = new char[256]; - for (int i = 0; i < 256; i++) - all[i] = (char) i; - String str = String.valueOf(all); - String enquoted = StringUtil.enquoteCString(str); - String backToAll = StringUtil.unquoteCString(enquoted); - char[] all2 = backToAll.toCharArray(); + @Test + public void decodeKoreString() { + Assert.assertEquals("_V_9", StringUtil.decodeKoreString("'Unds'V'Unds'9")); + Assert.assertEquals("?_0", StringUtil.decodeKoreString("'QuesUnds'0")); + Pattern identChar = Pattern.compile("[A-Za-z0-9\\-]"); + StringBuilder buffer = new StringBuilder(); + StringUtil.encodeStringToAlphanumeric( + buffer, "?_0", ModuleToKORE.asciiReadableEncodingKore, identChar, "'"); + Assert.assertEquals("'QuesUnds'0", buffer.toString()); + buffer.setLength(0); + StringUtil.encodeStringToAlphanumeric( + buffer, "_V_9", ModuleToKORE.asciiReadableEncodingKore, identChar, "'"); + Assert.assertEquals("'Unds'V'Unds'9", buffer.toString()); + } - Assert.assertEquals("Different sizes after running quote unquote.", all.length , all2.length); - for (int i = 0; i < 256; i++) - Assert.assertEquals("Different values at position: " + i, all[i] , all2[i]); - } + @Test + public void testBijection() { + char[] all = new char[256]; + for (int i = 0; i < 256; i++) all[i] = (char) i; + String str = String.valueOf(all); + String enquoted = StringUtil.enquoteCString(str); + String backToAll = StringUtil.unquoteCString(enquoted); + char[] all2 = backToAll.toCharArray(); - @Test - public void testKBijection() { - char[] all = new char[257]; - for (int i = 0; i < 256; i++) - all[i] = (char) i; - all[256] = 0xffff; - String str = String.valueOf(all); - String enquoted = StringUtil.enquoteKString(str); - String backToAll = StringUtil.unquoteKString(enquoted); - char[] all2 = backToAll.toCharArray(); + Assert.assertEquals("Different sizes after running quote unquote.", all.length, all2.length); + for (int i = 0; i < 256; i++) + Assert.assertEquals("Different values at position: " + i, all[i], all2[i]); + } - Assert.assertEquals("Different sizes after running quote unquote.", all.length , all2.length); - for (int i = 0; i < 257; i++) - Assert.assertEquals("Different values at position: " + i, all[i] , all2[i]); - } + @Test + public void testKBijection() { + char[] all = new char[257]; + for (int i = 0; i < 256; i++) all[i] = (char) i; + all[256] = 0xffff; + String str = String.valueOf(all); + String enquoted = StringUtil.enquoteKString(str); + String backToAll = StringUtil.unquoteKString(enquoted); + char[] all2 = backToAll.toCharArray(); - @Test - public void testSplitLine() { - String longLine = "http://www.kframework.org should be splitted"; - Assert.assertEquals( - "http://www.kframework.org\nshould be\nsplitted", - StringUtil.splitLines(longLine, 10)); - Assert.assertEquals( - "http://www.kframework.org\nshould be splitted", - StringUtil.splitLines(longLine, 20)); - Assert.assertEquals( - "http://www.kframework.org\nshould\nbe\nsplitted", - StringUtil.splitLines(longLine, 0)); - Assert.assertEquals( - "http://www.kframework.org should be\nsplitted", - StringUtil.splitLines(longLine, longLine.length())); - Assert.assertEquals(longLine, StringUtil.splitLines(longLine, longLine.length() + 1)); + Assert.assertEquals("Different sizes after running quote unquote.", all.length, all2.length); + for (int i = 0; i < 257; i++) + Assert.assertEquals("Different values at position: " + i, all[i], all2[i]); + } - String multiLine = - "http://www.kframework.org\nshort enough line"; - Assert.assertEquals( - "http://www.kframework.org\nshort enough line", - StringUtil.splitLines(multiLine, 30)); - Assert.assertEquals( - "http://www.kframework.org\nshort enough\nline", - StringUtil.splitLines(multiLine, 13)); - Assert.assertEquals( - "http://www.kframework.org\nshort\nenough\nline", - StringUtil.splitLines(multiLine, 10)); - } -} + @Test + public void testSplitLine() { + String longLine = "http://www.kframework.org should be splitted"; + Assert.assertEquals( + "http://www.kframework.org\nshould be\nsplitted", StringUtil.splitLines(longLine, 10)); + Assert.assertEquals( + "http://www.kframework.org\nshould be splitted", StringUtil.splitLines(longLine, 20)); + Assert.assertEquals( + "http://www.kframework.org\nshould\nbe\nsplitted", StringUtil.splitLines(longLine, 0)); + Assert.assertEquals( + "http://www.kframework.org should be\nsplitted", + StringUtil.splitLines(longLine, longLine.length())); + Assert.assertEquals(longLine, StringUtil.splitLines(longLine, longLine.length() + 1)); + String multiLine = "http://www.kframework.org\nshort enough line"; + Assert.assertEquals( + "http://www.kframework.org\nshort enough line", StringUtil.splitLines(multiLine, 30)); + Assert.assertEquals( + "http://www.kframework.org\nshort enough\nline", StringUtil.splitLines(multiLine, 13)); + Assert.assertEquals( + "http://www.kframework.org\nshort\nenough\nline", StringUtil.splitLines(multiLine, 10)); + } +} diff --git a/kernel/src/test/java/org/kframework/utils/inject/DefinitionLoadingModuleTest.java b/kernel/src/test/java/org/kframework/utils/inject/DefinitionLoadingModuleTest.java index f4b8235e7d1..3aa712405df 100644 --- a/kernel/src/test/java/org/kframework/utils/inject/DefinitionLoadingModuleTest.java +++ b/kernel/src/test/java/org/kframework/utils/inject/DefinitionLoadingModuleTest.java @@ -1,81 +1,83 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils.inject; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import com.beust.jcommander.JCommander; +import java.io.File; +import java.io.IOException; import org.junit.Assert; import org.junit.Test; import org.kframework.utils.errorsystem.KEMException; -import org.kframework.utils.file.FileUtil; import org.kframework.utils.options.DefinitionLoadingOptions; import org.kframework.utils.options.OuterParsingOptions; import org.kframework.utils.options.OutputDirectoryOptions; -import java.io.File; -import java.io.IOException; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - public class DefinitionLoadingModuleTest { - @Test - public void testReadDefinition() throws IOException { - DefinitionLoadingOptions options = new DefinitionLoadingOptions(); - new JCommander(options, "--directory", "src/test/resources"); - DefinitionLoadingModule module = new DefinitionLoadingModule(); - File kompiledDir = module.directory(options, new File("."), System.getenv()); - assertEquals(new File("src/test/resources/test-kompiled").getCanonicalFile(), kompiledDir.getCanonicalFile()); - assertTrue(kompiledDir.exists()); - assertTrue(kompiledDir.isDirectory()); - } + @Test + public void testReadDefinition() throws IOException { + DefinitionLoadingOptions options = new DefinitionLoadingOptions(); + new JCommander(options, "--directory", "src/test/resources"); + DefinitionLoadingModule module = new DefinitionLoadingModule(); + File kompiledDir = module.directory(options, new File("."), System.getenv()); + assertEquals( + new File("src/test/resources/test-kompiled").getCanonicalFile(), + kompiledDir.getCanonicalFile()); + assertTrue(kompiledDir.exists()); + assertTrue(kompiledDir.isDirectory()); + } - @Test - public void testReadDefinition2() throws IOException { - DefinitionLoadingOptions options = new DefinitionLoadingOptions(); - new JCommander(options, "--definition", "src/test/resources/test-kompiled"); - DefinitionLoadingModule module = new DefinitionLoadingModule(); - File kompiledDir = module.directory(options, new File("."), System.getenv()); - assertEquals(new File("src/test/resources/test-kompiled").getCanonicalFile(), kompiledDir.getCanonicalFile()); - assertTrue(kompiledDir.exists()); - assertTrue(kompiledDir.isDirectory()); - } + @Test + public void testReadDefinition2() throws IOException { + DefinitionLoadingOptions options = new DefinitionLoadingOptions(); + new JCommander(options, "--definition", "src/test/resources/test-kompiled"); + DefinitionLoadingModule module = new DefinitionLoadingModule(); + File kompiledDir = module.directory(options, new File("."), System.getenv()); + assertEquals( + new File("src/test/resources/test-kompiled").getCanonicalFile(), + kompiledDir.getCanonicalFile()); + assertTrue(kompiledDir.exists()); + assertTrue(kompiledDir.isDirectory()); + } - @Test(expected = KEMException.class) - public void testReadDefinition3() throws KEMException { - DefinitionLoadingOptions options = new DefinitionLoadingOptions(); - new JCommander(options, "--definition", "src/test/resources/fail"); - DefinitionLoadingModule module = new DefinitionLoadingModule(); - File defDir = module.directory(options, new File("."), System.getenv()); - } + @Test(expected = KEMException.class) + public void testReadDefinition3() throws KEMException { + DefinitionLoadingOptions options = new DefinitionLoadingOptions(); + new JCommander(options, "--definition", "src/test/resources/fail"); + DefinitionLoadingModule module = new DefinitionLoadingModule(); + File defDir = module.directory(options, new File("."), System.getenv()); + } - @Test - public void testReadDefinition4() throws KEMException { - DefinitionLoadingOptions options = new DefinitionLoadingOptions(); - new JCommander(options, "--definition", "src/test/resources/kmp"); - DefinitionLoadingModule module = new DefinitionLoadingModule(); - File defDir = module.directory(options, new File("."), System.getenv()); - Assert.assertEquals(new File("./src/test/resources/kmp"), defDir); - } + @Test + public void testReadDefinition4() throws KEMException { + DefinitionLoadingOptions options = new DefinitionLoadingOptions(); + new JCommander(options, "--definition", "src/test/resources/kmp"); + DefinitionLoadingModule module = new DefinitionLoadingModule(); + File defDir = module.directory(options, new File("."), System.getenv()); + Assert.assertEquals(new File("./src/test/resources/kmp"), defDir); + } - @Test - public void testSaveDefinition() throws KEMException { - OutputDirectoryOptions options = new OutputDirectoryOptions(); - OuterParsingOptions outer = new OuterParsingOptions(); - new JCommander(options, "-o", "src/test/resources/kmp"); - OuterParsingModule module = new OuterParsingModule(); - File kmp = module.kompiledDir(outer, new File("."), options); + @Test + public void testSaveDefinition() throws KEMException { + OutputDirectoryOptions options = new OutputDirectoryOptions(); + OuterParsingOptions outer = new OuterParsingOptions(); + new JCommander(options, "-o", "src/test/resources/kmp"); + OuterParsingModule module = new OuterParsingModule(); + File kmp = module.kompiledDir(outer, new File("."), options); - Assert.assertEquals(new File("./src/test/resources/kmp"), kmp); - } + Assert.assertEquals(new File("./src/test/resources/kmp"), kmp); + } - @Test - public void testSaveDefinition2() throws KEMException { - OutputDirectoryOptions options = new OutputDirectoryOptions(); - OuterParsingOptions outer = new OuterParsingOptions(); - new JCommander(options, "-o", "src/test/resources/newkmp"); - OuterParsingModule module = new OuterParsingModule(); - File kmp = module.kompiledDir(outer, new File("."), options); + @Test + public void testSaveDefinition2() throws KEMException { + OutputDirectoryOptions options = new OutputDirectoryOptions(); + OuterParsingOptions outer = new OuterParsingOptions(); + new JCommander(options, "-o", "src/test/resources/newkmp"); + OuterParsingModule module = new OuterParsingModule(); + File kmp = module.kompiledDir(outer, new File("."), options); - Assert.assertEquals(new File("./src/test/resources/newkmp"), kmp); - } + Assert.assertEquals(new File("./src/test/resources/newkmp"), kmp); + } } diff --git a/kore/src/main/java/org/kframework/API.java b/kore/src/main/java/org/kframework/API.java index 77d3b544c16..7500aa32162 100644 --- a/kore/src/main/java/org/kframework/API.java +++ b/kore/src/main/java/org/kframework/API.java @@ -5,10 +5,8 @@ import java.lang.annotation.Target; /** - * Marker for classes we consider as part fo the K API. - * It is particularly important for these classes to be well-documented. + * Marker for classes we consider as part fo the K API. It is particularly important for these + * classes to be well-documented. */ @Target(ElementType.TYPE) -public @interface API { - -} +public @interface API {} diff --git a/kore/src/main/java/org/kframework/List.java b/kore/src/main/java/org/kframework/List.java index 025ba8b0176..658b6ea5af0 100644 --- a/kore/src/main/java/org/kframework/List.java +++ b/kore/src/main/java/org/kframework/List.java @@ -5,29 +5,29 @@ import java.util.Iterator; public class List implements Iterable, Serializable { - private final scala.collection.immutable.List list; + private final scala.collection.immutable.List list; - public List(scala.collection.immutable.List l) { - this.list = l; - } + public List(scala.collection.immutable.List l) { + this.list = l; + } - @Override - public Iterator iterator() { - return new Iterator() { + @Override + public Iterator iterator() { + return new Iterator() { - private scala.collection.immutable.List l = list; + private scala.collection.immutable.List l = list; - @Override - public boolean hasNext() { - return !l.isEmpty(); - } + @Override + public boolean hasNext() { + return !l.isEmpty(); + } - @Override - public T next() { - T head = l.head(); - l = (scala.collection.immutable.List) l.tail(); - return head; - } - }; - } + @Override + public T next() { + T head = l.head(); + l = (scala.collection.immutable.List) l.tail(); + return head; + } + }; + } } diff --git a/kore/src/main/java/org/kframework/Warning.java b/kore/src/main/java/org/kframework/Warning.java index 7840e88b20e..6f7182c6f7e 100644 --- a/kore/src/main/java/org/kframework/Warning.java +++ b/kore/src/main/java/org/kframework/Warning.java @@ -2,6 +2,4 @@ package org.kframework; @API -public interface Warning { - -} +public interface Warning {} diff --git a/kore/src/main/java/org/kframework/attributes/HasLocation.java b/kore/src/main/java/org/kframework/attributes/HasLocation.java index 2d4ba63220e..43aaff44b69 100644 --- a/kore/src/main/java/org/kframework/attributes/HasLocation.java +++ b/kore/src/main/java/org/kframework/attributes/HasLocation.java @@ -4,6 +4,7 @@ import java.util.Optional; public interface HasLocation { - Optional location(); - Optional source(); + Optional location(); + + Optional source(); } diff --git a/kore/src/main/java/org/kframework/builtin/BooleanUtils.java b/kore/src/main/java/org/kframework/builtin/BooleanUtils.java index 045c2fba1a9..6ff16e5bf21 100644 --- a/kore/src/main/java/org/kframework/builtin/BooleanUtils.java +++ b/kore/src/main/java/org/kframework/builtin/BooleanUtils.java @@ -1,25 +1,25 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.builtin; -import org.kframework.kore.K; -import org.kframework.kore.KApply; -import org.kframework.kore.KToken; - import static org.kframework.kore.KORE.KApply; import static org.kframework.kore.KORE.KLabel; import static org.kframework.kore.KORE.KToken; -import static org.kframework.kore.KORE.Sort; -/** - * Created by dwightguth on 4/17/15. - */ +import org.kframework.kore.K; +import org.kframework.kore.KApply; +import org.kframework.kore.KToken; + +/** Created by dwightguth on 4/17/15. */ public class BooleanUtils { - public static KApply and(K k1, K k2) { - return KApply (KLabel("_andBool_"), k1, k2); - } - public static KApply not(K k) { return KApply(KLabel("notBool_"), k); } + public static KApply and(K k1, K k2) { + return KApply(KLabel("_andBool_"), k1, k2); + } + + public static KApply not(K k) { + return KApply(KLabel("notBool_"), k); + } - public static final KToken TRUE = KToken("true", Sorts.Bool()); - public static final KToken FALSE = KToken("false", Sorts.Bool()); + public static final KToken TRUE = KToken("true", Sorts.Bool()); + public static final KToken FALSE = KToken("false", Sorts.Bool()); } diff --git a/kore/src/main/java/org/kframework/builtin/Hooks.java b/kore/src/main/java/org/kframework/builtin/Hooks.java index 62fe8062c7d..b93641db1f2 100644 --- a/kore/src/main/java/org/kframework/builtin/Hooks.java +++ b/kore/src/main/java/org/kframework/builtin/Hooks.java @@ -2,31 +2,53 @@ package org.kframework.builtin; +import java.util.Arrays; import java.util.Collections; -import java.util.Set; import java.util.HashSet; -import java.util.Arrays; +import java.util.Set; public class Hooks { - public static final String ARRAY = "ARRAY"; - public static final String BOOL = "BOOL"; - public static final String BUFFER = "BUFFER"; - public static final String BYTES = "BYTES"; - public static final String FFI = "FFI"; - public static final String FLOAT = "FLOAT"; - public static final String INT = "INT"; - public static final String IO = "IO"; - public static final String KEQUAL = "KEQUAL"; - public static final String KREFLECTION = "KREFLECTION"; - public static final String LIST = "LIST"; - public static final String MAP = "MAP"; - public static final String RANGEMAP = "RANGEMAP"; - public static final String MINT = "MINT"; - public static final String SET = "SET"; - public static final String STRING = "STRING"; - public static final String SUBSTITUTION = "SUBSTITUTION"; - public static final String UNIFICATION = "UNIFICATION"; - public static final String JSON = "JSON"; + public static final String ARRAY = "ARRAY"; + public static final String BOOL = "BOOL"; + public static final String BUFFER = "BUFFER"; + public static final String BYTES = "BYTES"; + public static final String FFI = "FFI"; + public static final String FLOAT = "FLOAT"; + public static final String INT = "INT"; + public static final String IO = "IO"; + public static final String KEQUAL = "KEQUAL"; + public static final String KREFLECTION = "KREFLECTION"; + public static final String LIST = "LIST"; + public static final String MAP = "MAP"; + public static final String RANGEMAP = "RANGEMAP"; + public static final String MINT = "MINT"; + public static final String SET = "SET"; + public static final String STRING = "STRING"; + public static final String SUBSTITUTION = "SUBSTITUTION"; + public static final String UNIFICATION = "UNIFICATION"; + public static final String JSON = "JSON"; - public static final Set namespaces = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(ARRAY, BOOL, BUFFER, BYTES, FFI, FLOAT, INT, IO, KEQUAL, KREFLECTION, LIST, MAP, RANGEMAP, MINT, SET, STRING, SUBSTITUTION, UNIFICATION, JSON))); + public static final Set namespaces = + Collections.unmodifiableSet( + new HashSet<>( + Arrays.asList( + ARRAY, + BOOL, + BUFFER, + BYTES, + FFI, + FLOAT, + INT, + IO, + KEQUAL, + KREFLECTION, + LIST, + MAP, + RANGEMAP, + MINT, + SET, + STRING, + SUBSTITUTION, + UNIFICATION, + JSON))); } diff --git a/kore/src/main/java/org/kframework/builtin/KLabels.java b/kore/src/main/java/org/kframework/builtin/KLabels.java index 7c985f2d322..9fd44d1d5ec 100644 --- a/kore/src/main/java/org/kframework/builtin/KLabels.java +++ b/kore/src/main/java/org/kframework/builtin/KLabels.java @@ -2,79 +2,79 @@ package org.kframework.builtin; -import org.kframework.kore.KLabel; - import static org.kframework.kore.KORE.KLabel; +import org.kframework.kore.KLabel; + public class KLabels { - public static final KLabel AND = KLabel("_andBool_"); - public static final KLabel OR = KLabel("_orBool_"); + public static final KLabel AND = KLabel("_andBool_"); + public static final KLabel OR = KLabel("_orBool_"); - public static final KLabel KSEQ = KLabel("#KSequence"); - public static final KLabel DOTK = KLabel("#EmptyK"); + public static final KLabel KSEQ = KLabel("#KSequence"); + public static final KLabel DOTK = KLabel("#EmptyK"); - public static final KLabel CELLS = KLabel("#cells"); + public static final KLabel CELLS = KLabel("#cells"); - public static final KLabel DOTS = KLabel("#dots"); - public static final KLabel NO_DOTS = KLabel("#noDots"); + public static final KLabel DOTS = KLabel("#dots"); + public static final KLabel NO_DOTS = KLabel("#noDots"); - public static final KLabel KREWRITE = KLabel("#KRewrite"); - public static final KLabel KLIST = KLabel("#KList"); - public static final KLabel EMPTYKLIST = KLabel("#EmptyKList"); - public static final KLabel KAPP = KLabel("#KApply"); + public static final KLabel KREWRITE = KLabel("#KRewrite"); + public static final KLabel KLIST = KLabel("#KList"); + public static final KLabel EMPTYKLIST = KLabel("#EmptyKList"); + public static final KLabel KAPP = KLabel("#KApply"); - public static final String GENERATED_TOP_CELL_NAME = "generatedTop"; - public static final KLabel GENERATED_TOP_CELL = KLabel(""); - public static final String GENERATED_COUNTER_CELL_NAME = "generatedCounter"; - public static final KLabel GENERATED_COUNTER_CELL = KLabel(""); - public static final KLabel INIT_GENERATED_TOP_CELL = KLabel("initGeneratedTopCell"); - public static final KLabel INIT_GENERATED_COUNTER_CELL = KLabel("initGeneratedCounterCell"); - public static final String THIS_CONFIGURATION = "THIS_CONFIGURATION"; + public static final String GENERATED_TOP_CELL_NAME = "generatedTop"; + public static final KLabel GENERATED_TOP_CELL = KLabel(""); + public static final String GENERATED_COUNTER_CELL_NAME = "generatedCounter"; + public static final KLabel GENERATED_COUNTER_CELL = KLabel(""); + public static final KLabel INIT_GENERATED_TOP_CELL = KLabel("initGeneratedTopCell"); + public static final KLabel INIT_GENERATED_COUNTER_CELL = KLabel("initGeneratedCounterCell"); + public static final String THIS_CONFIGURATION = "THIS_CONFIGURATION"; - public static final String STRATEGY_CELL_NAME = "s"; - public static final KLabel STRATEGY_CELL = KLabel(""); - public static final KLabel STUCK = KLabel("#STUCK"); + public static final String STRATEGY_CELL_NAME = "s"; + public static final KLabel STRATEGY_CELL = KLabel(""); + public static final KLabel STUCK = KLabel("#STUCK"); - public static final KLabel ML_FALSE = KLabel("#Bottom"); - public static final KLabel ML_TRUE = KLabel("#Top"); - public static final KLabel ML_OR = KLabel("#Or"); - public static final KLabel ML_AND = KLabel("#And"); - public static final KLabel ML_NOT = KLabel("#Not"); - public static final KLabel ML_CEIL = KLabel("#Ceil"); - public static final KLabel ML_FLOOR = KLabel("#Floor"); - public static final KLabel ML_EQUALS = KLabel("#Equals"); - public static final KLabel ML_IMPLIES = KLabel("#Implies"); - public static final KLabel ML_EXISTS = KLabel("#Exists"); - public static final KLabel ML_FORALL = KLabel("#Forall"); - public static final KLabel CTL_AG = KLabel("#AG"); - public static final KLabel RL_wEF = KLabel("weakExistsFinally"); - public static final KLabel RL_wAF = KLabel("weakAlwaysFinally"); + public static final KLabel ML_FALSE = KLabel("#Bottom"); + public static final KLabel ML_TRUE = KLabel("#Top"); + public static final KLabel ML_OR = KLabel("#Or"); + public static final KLabel ML_AND = KLabel("#And"); + public static final KLabel ML_NOT = KLabel("#Not"); + public static final KLabel ML_CEIL = KLabel("#Ceil"); + public static final KLabel ML_FLOOR = KLabel("#Floor"); + public static final KLabel ML_EQUALS = KLabel("#Equals"); + public static final KLabel ML_IMPLIES = KLabel("#Implies"); + public static final KLabel ML_EXISTS = KLabel("#Exists"); + public static final KLabel ML_FORALL = KLabel("#Forall"); + public static final KLabel CTL_AG = KLabel("#AG"); + public static final KLabel RL_wEF = KLabel("weakExistsFinally"); + public static final KLabel RL_wAF = KLabel("weakAlwaysFinally"); - public static final KLabel ListItem = KLabel("ListItem"); - public static final KLabel List = KLabel("_List_"); - public static final KLabel DotList = KLabel(".List"); - public static final KLabel MapItem = KLabel("_|->_"); - public static final KLabel Map = KLabel("_Map_"); - public static final KLabel DotMap = KLabel(".Map"); - public static final KLabel SetItem = KLabel("SetItem"); - public static final KLabel Set = KLabel("_Set_"); - public static final KLabel DotSet = KLabel(".Set"); + public static final KLabel ListItem = KLabel("ListItem"); + public static final KLabel List = KLabel("_List_"); + public static final KLabel DotList = KLabel(".List"); + public static final KLabel MapItem = KLabel("_|->_"); + public static final KLabel Map = KLabel("_Map_"); + public static final KLabel DotMap = KLabel(".Map"); + public static final KLabel SetItem = KLabel("SetItem"); + public static final KLabel Set = KLabel("_Set_"); + public static final KLabel DotSet = KLabel(".Set"); - public static final KLabel EQUALS_K = KLabel("_==K_"); - public static final KLabel NOT_EQUALS_K = KLabel("_=/=K_"); - public static final KLabel IN_K = KLabel("_:=K_"); - public static final KLabel NOT_IN_K = KLabel("_:/=K_"); + public static final KLabel EQUALS_K = KLabel("_==K_"); + public static final KLabel NOT_EQUALS_K = KLabel("_=/=K_"); + public static final KLabel IN_K = KLabel("_:=K_"); + public static final KLabel NOT_IN_K = KLabel("_:/=K_"); - public static final KLabel MAP_CHOICE = KLabel("Map:choice"); - public static final KLabel SET_CHOICE = KLabel("Set:choice"); - public static final KLabel LIST_GET = KLabel("List:get"); - public static final KLabel MAP_LOOKUP = KLabel("Map:lookup"); - public static final KLabel SET_MEMBERSHIP = KLabel("Set:in"); - public static final KLabel LIST_RANGE = KLabel("List:range"); - public static final KLabel MAP_UPDATE = KLabel("updateMap"); - public static final KLabel MAP_REMOVE_ALL = KLabel("removeAll"); - public static final KLabel SET_REMOVE_ALL = KLabel("Set:difference"); - public static final KLabel MAP_KEYS = KLabel("keys"); + public static final KLabel MAP_CHOICE = KLabel("Map:choice"); + public static final KLabel SET_CHOICE = KLabel("Set:choice"); + public static final KLabel LIST_GET = KLabel("List:get"); + public static final KLabel MAP_LOOKUP = KLabel("Map:lookup"); + public static final KLabel SET_MEMBERSHIP = KLabel("Set:in"); + public static final KLabel LIST_RANGE = KLabel("List:range"); + public static final KLabel MAP_UPDATE = KLabel("updateMap"); + public static final KLabel MAP_REMOVE_ALL = KLabel("removeAll"); + public static final KLabel SET_REMOVE_ALL = KLabel("Set:difference"); + public static final KLabel MAP_KEYS = KLabel("keys"); - public static final String INJ = "inj"; + public static final String INJ = "inj"; } diff --git a/kore/src/main/java/org/kframework/builtin/Rules.java b/kore/src/main/java/org/kframework/builtin/Rules.java index 07910ed34f6..3551db4b2e7 100644 --- a/kore/src/main/java/org/kframework/builtin/Rules.java +++ b/kore/src/main/java/org/kframework/builtin/Rules.java @@ -1,12 +1,20 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.builtin; -import org.kframework.kore.KRewrite; import static org.kframework.kore.KORE.*; +import org.kframework.kore.KRewrite; + public class Rules { - public static final KRewrite STUCK_RULE = KRewrite( - KApply(KLabels.STRATEGY_CELL, KList(KApply(KLabels.KSEQ, KList(KApply(KLabels.DOTK, KList()), KVariable("#REST"))))) - , KApply(KLabels.STRATEGY_CELL, KList(KApply(KLabels.KSEQ, KList(KApply(KLabels.STUCK, KList()), KVariable("#REST"))))) - ); + public static final KRewrite STUCK_RULE = + KRewrite( + KApply( + KLabels.STRATEGY_CELL, + KList( + KApply(KLabels.KSEQ, KList(KApply(KLabels.DOTK, KList()), KVariable("#REST"))))), + KApply( + KLabels.STRATEGY_CELL, + KList( + KApply( + KLabels.KSEQ, KList(KApply(KLabels.STUCK, KList()), KVariable("#REST")))))); } diff --git a/kore/src/main/java/org/kframework/compile/ConfigurationInfo.java b/kore/src/main/java/org/kframework/compile/ConfigurationInfo.java index f0d7e16df0c..6bc2ad93ece 100644 --- a/kore/src/main/java/org/kframework/compile/ConfigurationInfo.java +++ b/kore/src/main/java/org/kframework/compile/ConfigurationInfo.java @@ -1,116 +1,114 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import java.util.List; +import java.util.Set; import org.kframework.kore.KApply; import org.kframework.kore.KLabel; import org.kframework.kore.Sort; import scala.Option; -import java.util.List; -import java.util.Set; - /** - * Information about the configuration structure used - * in the configuration concretiziation passes. - * Cells are identified by the sort containing the production - * for the cell (this is more convenient when dealing also - * with variables or functions of cell sort). + * Information about the configuration structure used in the configuration concretiziation passes. + * Cells are identified by the sort containing the production for the cell (this is more convenient + * when dealing also with variables or functions of cell sort). */ public interface ConfigurationInfo { - /** Number of proper ancestors of cell k */ - int getLevel(Sort k); + /** Number of proper ancestors of cell k */ + int getLevel(Sort k); + + /** Parent of cell k, or null */ + Sort getParent(Sort k); + + /** Children of cell k */ + List getChildren(Sort k); - /** Parent of cell k, or null */ - Sort getParent(Sort k); + /** Declared multiplicity of a cell */ + Multiplicity getMultiplicity(Sort k); - /** Children of cell k */ - List getChildren(Sort k); + /** True if sort k is actually a cell sort */ + boolean isCell(Sort k); - /** Declared multiplicity of a cell */ - Multiplicity getMultiplicity(Sort k); + /** True if sort s is the collection sort for a multiplicity star cell. */ + boolean isCellCollection(Sort s); - /** True if sort k is actually a cell sort */ - boolean isCell(Sort k); + /** True if kLabel is the KLabel of a cell */ + boolean isCellLabel(KLabel kLabel); - /** True if sort s is the collection sort for a multiplicity star cell. */ - boolean isCellCollection(Sort s); + /** True for cells which contain a term rather than other cells */ + boolean isLeafCell(Sort k); - /** True if kLabel is the KLabel of a cell */ - boolean isCellLabel(KLabel kLabel); + /** True for cells which contain other cells */ + boolean isParentCell(Sort k); - /** True for cells which contain a term rather than other cells */ - boolean isLeafCell(Sort k); + /** + * Set of cell bag sorts (e.g. ThreadCellBag) associated with a multiplicity * cell (e.g. + * ThreadCell). Should not in most cases return more than one sort, but a user can write + * productions that would cause this method to return multiple sorts, e.g. if a particular * cell + * appears in multiple bags in different parts of a configuration. + */ + scala.collection.Set getCellBagSortsOfCell(Sort k); - /** True for cells which contain other cells */ - boolean isParentCell(Sort k); + /** The declared sort of the contents of a leaf cell */ + Sort leafCellType(Sort k); - /** Set of cell bag sorts (e.g. ThreadCellBag) associated with a multiplicity * cell (e.g. ThreadCell). - * Should not in most cases return more than one sort, but a user can write productions that would cause - * this method to return multiple sorts, e.g. if a particular * cell appears in multiple bags in different - * parts of a configuration. - */ - scala.collection.Set getCellBagSortsOfCell(Sort k); + /** The label for a cell */ + KLabel getCellLabel(Sort k); - /** The declared sort of the contents of a leaf cell */ - Sort leafCellType(Sort k); + /** The cell for a label */ + Sort getCellSort(KLabel kLabel); - /** The label for a cell */ - KLabel getCellLabel(Sort k); + /** The label for a fragment of a cell, only defined for parent cells. */ + KLabel getCellFragmentLabel(Sort k); - /** The cell for a label */ - Sort getCellSort(KLabel kLabel); + /** + * The constant label to use as an argument of a cell fragment, when the cell fragment did not + * capture cells of the argument type. Only defined for child cells of multiplicity other than *. + */ + KLabel getCellAbsentLabel(Sort cellSort); - /** The label for a fragment of a cell, only defined for parent cells. */ - KLabel getCellFragmentLabel(Sort k); + /** Returns a term which is the default cell of sort k, probably an initializer macro */ + KApply getDefaultCell(Sort k); - /** - * The constant label to use as an argument of a cell fragment, - * when the cell fragment did not capture cells of the argument type. - * Only defined for child cells of multiplicity other than *. - */ - KLabel getCellAbsentLabel(Sort cellSort); + boolean isConstantInitializer(Sort k); - /** Returns a term which is the default cell of sort k, - * probably an initializer macro */ - KApply getDefaultCell(Sort k); + /** Return the root cell of the configuration . */ + Sort getRootCell(); - boolean isConstantInitializer(Sort k); + /** Return the declared computation cell, by default the k cell */ + Sort getComputationCell(); - /** Return the root cell of the configuration . */ - Sort getRootCell(); - /** Return the declared computation cell, by default the k cell */ - Sort getComputationCell(); - /** Returns the set of cell sorts in this configuration */ - Set getCellSorts(); + /** Returns the set of cell sorts in this configuration */ + Set getCellSorts(); - /** Returns the unit of a * or ? cell. */ - KApply getUnit(Sort k); + /** Returns the unit of a * or ? cell. */ + KApply getUnit(Sort k); - /** Returns the concatenation operation of a multiplicity * cell. */ - KLabel getConcat(Sort k); + /** Returns the concatenation operation of a multiplicity * cell. */ + KLabel getConcat(Sort k); - /** Returns the cell associated with this concatenation label */ - Option getCellForConcat(KLabel concat); + /** Returns the cell associated with this concatenation label */ + Option getCellForConcat(KLabel concat); - /** Returns the cell associated with this unit */ - Option getCellForUnit(KLabel unit); + /** Returns the cell associated with this unit */ + Option getCellForUnit(KLabel unit); - /** Declared mulitplicitly of a cell */ - enum Multiplicity { - /** Exactly one instance of this cell is required */ - ONE, - /** This cell is optional but may not be repeated */ - OPTIONAL, - /** This cell may occur any number of times, zero or more */ - STAR; + /** Declared mulitplicitly of a cell */ + enum Multiplicity { + /** Exactly one instance of this cell is required */ + ONE, + /** This cell is optional but may not be repeated */ + OPTIONAL, + /** This cell may occur any number of times, zero or more */ + STAR; - public static Multiplicity of(String multiplicity) { - return switch (multiplicity) { - case "1" -> Multiplicity.ONE; - case "*" -> Multiplicity.STAR; - case "?" -> Multiplicity.OPTIONAL; - default -> throw new IllegalArgumentException(multiplicity); - }; - } + public static Multiplicity of(String multiplicity) { + return switch (multiplicity) { + case "1" -> Multiplicity.ONE; + case "*" -> Multiplicity.STAR; + case "?" -> Multiplicity.OPTIONAL; + default -> throw new IllegalArgumentException(multiplicity); + }; } + } } diff --git a/kore/src/main/java/org/kframework/compile/LabelInfo.java b/kore/src/main/java/org/kframework/compile/LabelInfo.java index bb747d4a021..4f0b64c6602 100644 --- a/kore/src/main/java/org/kframework/compile/LabelInfo.java +++ b/kore/src/main/java/org/kframework/compile/LabelInfo.java @@ -1,9 +1,16 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.compile; +import static org.kframework.kore.KORE.*; + import com.google.common.collect.HashMultimap; import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import org.kframework.definition.Production; import org.kframework.kore.K; import org.kframework.kore.KApply; @@ -11,103 +18,95 @@ import org.kframework.kore.KRewrite; import org.kframework.kore.Sort; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -import static org.kframework.kore.KORE.*; - -/** - * Basic label information for cell completion - */ +/** Basic label information for cell completion */ public class LabelInfo { - private final Multimap codomain = HashMultimap.create(); - - private final Map assocInfo = new HashMap<>(); - - private final Set functionLabels = new HashSet<>(); - - private final Map productions = new HashMap<>(); - - protected void addLabel(Sort result, String label) { - addLabel(result, label, false); + private final Multimap codomain = HashMultimap.create(); + + private final Map assocInfo = new HashMap<>(); + + private final Set functionLabels = new HashSet<>(); + + private final Map productions = new HashMap<>(); + + protected void addLabel(Sort result, String label) { + addLabel(result, label, false); + } + + protected void addLabel(Sort result, String label, boolean isAssoc) { + addLabel(result, label, isAssoc, false, false); + } + + protected void addLabel( + Sort result, String label, boolean isAssoc, boolean isComm, boolean isFunction) { + addLabel(result, label, isAssoc, isComm, isFunction, null); + } + + protected void addLabel( + Sort result, + String label, + boolean isAssoc, + boolean isComm, + boolean isFunction, + Production prod) { + KLabel kLabel = KLabel(label); + codomain.put(kLabel, result); + AssocInfo info = new AssocInfo(isAssoc, isComm); + if (assocInfo.containsKey(kLabel)) { + assert assocInfo.get(kLabel).equals(info); + } else { + assocInfo.put(kLabel, new AssocInfo(isAssoc, isComm)); } - - protected void addLabel(Sort result, String label, boolean isAssoc) { - addLabel(result, label, isAssoc, false, false); + if (isFunction) { + functionLabels.add(kLabel); } + productions.put(label, prod); + } - protected void addLabel(Sort result, String label, boolean isAssoc, boolean isComm, boolean isFunction) { - addLabel(result, label, isAssoc, isComm, isFunction, null); - } + public LabelInfo() {} - protected void addLabel(Sort result, String label, boolean isAssoc, boolean isComm, boolean isFunction, Production prod) { - KLabel kLabel = KLabel(label); - codomain.put(kLabel, result); - AssocInfo info = new AssocInfo(isAssoc, isComm); - if (assocInfo.containsKey(kLabel)) { - assert assocInfo.get(kLabel).equals(info); - } else { - assocInfo.put(kLabel, new AssocInfo(isAssoc, isComm)); - } - if (isFunction) { - functionLabels.add(kLabel); - } - productions.put(label, prod); - } + /** Get the sort for the KLabel. Could be moved to module eventually. */ + public Sort getCodomain(KLabel l) { + Collection sorts = codomain.get(l); + return Iterables.getOnlyElement(sorts, null); + } - public LabelInfo() { - } + public boolean isFunction(KLabel l) { + return functionLabels.contains(l); + } - /** - * Get the sort for the KLabel. Could be moved to module eventually. - */ - public Sort getCodomain(KLabel l) { - Collection sorts = codomain.get(l); - return Iterables.getOnlyElement(sorts, null); + public boolean isFunction(K term) { + if (term instanceof KApply && isFunction(((KApply) term).klabel())) { + return true; } - - public boolean isFunction(KLabel l) { - return functionLabels.contains(l); + return term instanceof KRewrite + && ((KRewrite) term).left() instanceof KApply + && isFunction(((KApply) ((KRewrite) term).left()).klabel()); + } + + /** Get the AC status of the KLabel. Could be moved to module eventually. */ + public AssocInfo getAssocInfo(KLabel l) { + return assocInfo.get(l); + } + + public static class AssocInfo { + public AssocInfo(boolean isAssoc, boolean isComm) { + this.isAssoc = isAssoc; + this.isComm = isComm; } - public boolean isFunction(K term) { - if (term instanceof KApply && isFunction(((KApply) term).klabel())) { - return true; - } - return term instanceof KRewrite && ((KRewrite) term).left() instanceof KApply - && isFunction(((KApply) ((KRewrite) term).left()).klabel()); - } + private final boolean isAssoc; + private final boolean isComm; - /** - * Get the AC status of the KLabel. Could be moved to module eventually. - */ - public AssocInfo getAssocInfo(KLabel l) { - return assocInfo.get(l); + public boolean isAssoc() { + return isAssoc; } - public static class AssocInfo { - public AssocInfo(boolean isAssoc, boolean isComm) { - this.isAssoc = isAssoc; - this.isComm = isComm; - } - - private final boolean isAssoc; - private final boolean isComm; - - public boolean isAssoc() { - return isAssoc; - } - - public boolean isComm() { - return isComm; - } + public boolean isComm() { + return isComm; } + } - public Production getProduction(String label) { - return productions.get(label); - } + public Production getProduction(String label) { + return productions.get(label); + } } diff --git a/kore/src/main/java/org/kframework/definition/Associativity.java b/kore/src/main/java/org/kframework/definition/Associativity.java index 759dec74580..e0bf5840376 100644 --- a/kore/src/main/java/org/kframework/definition/Associativity.java +++ b/kore/src/main/java/org/kframework/definition/Associativity.java @@ -2,5 +2,8 @@ package org.kframework.definition; public enum Associativity { - Left, Right, NonAssoc, Unspecified + Left, + Right, + NonAssoc, + Unspecified } diff --git a/kore/src/main/java/org/kframework/definition/UserList.java b/kore/src/main/java/org/kframework/definition/UserList.java index e10e37bb19b..24d104353fc 100644 --- a/kore/src/main/java/org/kframework/definition/UserList.java +++ b/kore/src/main/java/org/kframework/definition/UserList.java @@ -1,81 +1,83 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.definition; -import org.kframework.Collections; -import org.kframework.attributes.Att; -import org.kframework.kore.KLabel; -import org.kframework.kore.Sort; - import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import org.kframework.Collections; +import org.kframework.attributes.Att; +import org.kframework.kore.KLabel; +import org.kframework.kore.Sort; -/** - * Class to hold easy to access information about user defined lists. - */ +/** Class to hold easy to access information about user defined lists. */ public class UserList { - public Sort sort = null; - public Sort childSort = null; - public String separator = null; - public KLabel terminatorKLabel = null; - public KLabel klabel = null; - public boolean nonEmpty = false; - public boolean leftAssoc = false; - public Production pList = null, pTerminator = null; - public org.kframework.attributes.Att attrs = null; + public Sort sort = null; + public Sort childSort = null; + public String separator = null; + public KLabel terminatorKLabel = null; + public KLabel klabel = null; + public boolean nonEmpty = false; + public boolean leftAssoc = false; + public Production pList = null, pTerminator = null; + public org.kframework.attributes.Att attrs = null; - public static scala.collection.Set apply(scala.collection.Set sentences) { - return Collections.immutable(getLists(Collections.mutable(sentences))).toSet(); - } + public static scala.collection.Set apply(scala.collection.Set sentences) { + return Collections.immutable(getLists(Collections.mutable(sentences))).toSet(); + } - // find all productions annotated with 'userList' - // expecting to always find 2 of them of the form: - // Es ::= E "," Es [right, userList, klabel(...)] - // Es ::= ".Es" [userList, klabel(...)] - public static java.util.List getLists(Set sentences) { - Map> separatedProds - = sentences.stream().collect(Collectors.groupingBy(p -> p instanceof Production && p.att().contains(Att.USER_LIST()))); - Map> listsMap = separatedProds.getOrDefault(true, new LinkedList<>()) - .stream().collect(Collectors.groupingBy(s -> ((Production) s).sort())); + // find all productions annotated with 'userList' + // expecting to always find 2 of them of the form: + // Es ::= E "," Es [right, userList, klabel(...)] + // Es ::= ".Es" [userList, klabel(...)] + public static java.util.List getLists(Set sentences) { + Map> separatedProds = + sentences.stream() + .collect( + Collectors.groupingBy( + p -> p instanceof Production && p.att().contains(Att.USER_LIST()))); + Map> listsMap = + separatedProds.getOrDefault(true, new LinkedList<>()).stream() + .collect(Collectors.groupingBy(s -> ((Production) s).sort())); - java.util.List res = new ArrayList<>(); - for (Map.Entry> x : listsMap.entrySet()) { - UserList ul = new UserList(); - ul.sort = x.getKey(); - assert x.getValue().size() == 2; - for (Sentence s : x.getValue()) { - Production p = (Production) s; - if (p.items().size() == 3) { - Terminal t = (Terminal) p.items().tail().head(); - ul.separator = t.value(); - ul.klabel = p.klabel().get(); - ul.attrs = p.att().remove(Att.KLABEL()); - // should work without the Att.userList() att, i.e. for any list -- see #1892 - ul.nonEmpty = ul.attrs.get(Att.USER_LIST()).equals("+"); - if (!((NonTerminal)p.items().tail().tail().head()).sort().equals(p.sort())) { - ul.leftAssoc = true; - } - if (ul.leftAssoc) { - ul.childSort = ((NonTerminal) p.items().tail().tail().head()).sort(); - } else { - ul.childSort = ((NonTerminal) p.items().head()).sort(); - } - ul.pList = p; - } else if (p.items().size() == 1 && p.items().head() instanceof Terminal) { - ul.terminatorKLabel = p.klabel().get(); - ul.pTerminator = p; - } else - throw new AssertionError("Didn't expect this type of production when recognizing userList patterns!"); - } - assert ul.attrs != null; - assert ul.klabel != null; - assert ul.terminatorKLabel != null; - assert ul.childSort != null; - res.add(ul); - } - return res; + java.util.List res = new ArrayList<>(); + for (Map.Entry> x : listsMap.entrySet()) { + UserList ul = new UserList(); + ul.sort = x.getKey(); + assert x.getValue().size() == 2; + for (Sentence s : x.getValue()) { + Production p = (Production) s; + if (p.items().size() == 3) { + Terminal t = (Terminal) p.items().tail().head(); + ul.separator = t.value(); + ul.klabel = p.klabel().get(); + ul.attrs = p.att().remove(Att.KLABEL()); + // should work without the Att.userList() att, i.e. for any list -- see #1892 + ul.nonEmpty = ul.attrs.get(Att.USER_LIST()).equals("+"); + if (!((NonTerminal) p.items().tail().tail().head()).sort().equals(p.sort())) { + ul.leftAssoc = true; + } + if (ul.leftAssoc) { + ul.childSort = ((NonTerminal) p.items().tail().tail().head()).sort(); + } else { + ul.childSort = ((NonTerminal) p.items().head()).sort(); + } + ul.pList = p; + } else if (p.items().size() == 1 && p.items().head() instanceof Terminal) { + ul.terminatorKLabel = p.klabel().get(); + ul.pTerminator = p; + } else + throw new AssertionError( + "Didn't expect this type of production when recognizing userList patterns!"); + } + assert ul.attrs != null; + assert ul.klabel != null; + assert ul.terminatorKLabel != null; + assert ul.childSort != null; + res.add(ul); } + return res; + } } diff --git a/kore/src/main/java/org/kframework/kore/AddAtt.java b/kore/src/main/java/org/kframework/kore/AddAtt.java index 68e2a4e3880..0a65fd0beab 100644 --- a/kore/src/main/java/org/kframework/kore/AddAtt.java +++ b/kore/src/main/java/org/kframework/kore/AddAtt.java @@ -1,55 +1,50 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.kore; -import org.kframework.attributes.Att; -import org.kframework.kore.K; -import org.kframework.kore.KApply; -import org.kframework.kore.KAs; -import org.kframework.kore.KRewrite; -import org.kframework.kore.KSequence; -import org.kframework.kore.KToken; -import org.kframework.kore.KVariable; -import org.kframework.kore.TransformK; +import static org.kframework.kore.KORE.*; import java.util.function.UnaryOperator; - -import static org.kframework.kore.KORE.*; +import org.kframework.attributes.Att; public class AddAtt extends TransformK { - private final UnaryOperator f; - - public AddAtt(UnaryOperator f) { - this.f = f; - } - - @Override - public K apply(KApply kapp) { - return KApply(kapp.klabel(), kapp.klist(), f.apply(kapp.att())); - } - @Override - public K apply(KRewrite rew) { - return KRewrite(rew.left(), rew.right(), f.apply(rew.att())); - } - @Override - public K apply(KToken tok) { - return KToken(tok.s(), tok.sort(), f.apply(tok.att())); - } - @Override - public K apply(KVariable var) { - return KVariable(var.name(), f.apply(var.att())); - } - @Override - public K apply(KSequence kseq) { - return KSequence(kseq.items(), f.apply(kseq.att())); - } - @Override - public K apply(InjectedKLabel lbl) { - return InjectedKLabel(lbl.klabel(), f.apply(lbl.att())); - } - @Override - public K apply(KAs kas) { - return KAs(kas.pattern(), kas.alias(), f.apply(kas.att())); - } + private final UnaryOperator f; + + public AddAtt(UnaryOperator f) { + this.f = f; + } + + @Override + public K apply(KApply kapp) { + return KApply(kapp.klabel(), kapp.klist(), f.apply(kapp.att())); + } + + @Override + public K apply(KRewrite rew) { + return KRewrite(rew.left(), rew.right(), f.apply(rew.att())); + } + + @Override + public K apply(KToken tok) { + return KToken(tok.s(), tok.sort(), f.apply(tok.att())); + } + + @Override + public K apply(KVariable var) { + return KVariable(var.name(), f.apply(var.att())); + } + + @Override + public K apply(KSequence kseq) { + return KSequence(kseq.items(), f.apply(kseq.att())); + } + + @Override + public K apply(InjectedKLabel lbl) { + return InjectedKLabel(lbl.klabel(), f.apply(lbl.att())); + } + + @Override + public K apply(KAs kas) { + return KAs(kas.pattern(), kas.alias(), f.apply(kas.att())); + } } - - diff --git a/kore/src/main/java/org/kframework/kore/AddAttRec.java b/kore/src/main/java/org/kframework/kore/AddAttRec.java index 443805d3046..5a6a8c4be5d 100644 --- a/kore/src/main/java/org/kframework/kore/AddAttRec.java +++ b/kore/src/main/java/org/kframework/kore/AddAttRec.java @@ -1,10 +1,6 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.kore; -import org.kframework.attributes.Att; - -import java.util.function.UnaryOperator; - import static org.kframework.kore.KORE.InjectedKLabel; import static org.kframework.kore.KORE.KApply; import static org.kframework.kore.KORE.KAs; @@ -13,41 +9,48 @@ import static org.kframework.kore.KORE.KToken; import static org.kframework.kore.KORE.KVariable; +import java.util.function.UnaryOperator; +import org.kframework.attributes.Att; + public class AddAttRec extends TransformK { - private final UnaryOperator f; - - public AddAttRec(UnaryOperator f) { - this.f = f; - } - - @Override - public K apply(KApply kapp) { - return super.apply(KApply(kapp.klabel(), kapp.klist(), f.apply(kapp.att()))); - } - @Override - public K apply(KRewrite rew) { - return super.apply(KRewrite(rew.left(), rew.right(), f.apply(rew.att()))); - } - @Override - public K apply(KToken tok) { - return super.apply(KToken(tok.s(), tok.sort(), f.apply(tok.att()))); - } - @Override - public K apply(KVariable var) { - return super.apply(KVariable(var.name(), f.apply(var.att()))); - } - @Override - public K apply(KSequence kseq) { - return super.apply(KSequence(kseq.items(), f.apply(kseq.att()))); - } - @Override - public K apply(InjectedKLabel lbl) { - return super.apply(InjectedKLabel(lbl.klabel(), f.apply(lbl.att()))); - } - @Override - public K apply(KAs kas) { - return super.apply(KAs(kas.pattern(), kas.alias(), f.apply(kas.att()))); - } + private final UnaryOperator f; + + public AddAttRec(UnaryOperator f) { + this.f = f; + } + + @Override + public K apply(KApply kapp) { + return super.apply(KApply(kapp.klabel(), kapp.klist(), f.apply(kapp.att()))); + } + + @Override + public K apply(KRewrite rew) { + return super.apply(KRewrite(rew.left(), rew.right(), f.apply(rew.att()))); + } + + @Override + public K apply(KToken tok) { + return super.apply(KToken(tok.s(), tok.sort(), f.apply(tok.att()))); + } + + @Override + public K apply(KVariable var) { + return super.apply(KVariable(var.name(), f.apply(var.att()))); + } + + @Override + public K apply(KSequence kseq) { + return super.apply(KSequence(kseq.items(), f.apply(kseq.att()))); + } + + @Override + public K apply(InjectedKLabel lbl) { + return super.apply(InjectedKLabel(lbl.klabel(), f.apply(lbl.att()))); + } + + @Override + public K apply(KAs kas) { + return super.apply(KAs(kas.pattern(), kas.alias(), f.apply(kas.att()))); + } } - - diff --git a/kore/src/main/java/org/kframework/kore/AttCompare.java b/kore/src/main/java/org/kframework/kore/AttCompare.java index 7d2ad93715d..35b4ed82d04 100644 --- a/kore/src/main/java/org/kframework/kore/AttCompare.java +++ b/kore/src/main/java/org/kframework/kore/AttCompare.java @@ -1,82 +1,79 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.kore; +import java.util.Arrays; +import java.util.List; import org.kframework.attributes.Att; import scala.Tuple2; import scala.collection.Map; -import java.util.Arrays; -import java.util.List; - -/** - * Created by dwightguth on 6/17/15. - */ +/** Created by dwightguth on 6/17/15. */ public class AttCompare { - private final K k; - private final List attNames; + private final K k; + private final List attNames; - public AttCompare(K k, Att.Key... attNames) { - this.k = k; - this.attNames = Arrays.asList(attNames); - } + public AttCompare(K k, Att.Key... attNames) { + this.k = k; + this.attNames = Arrays.asList(attNames); + } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; - AttCompare that = (AttCompare) o; + AttCompare that = (AttCompare) o; - return attEquals(this.k, that.k); + return attEquals(this.k, that.k); + } + private boolean attEquals(K thisK, K thatK) { + if (!filterAtt(thisK.att()).equals(filterAtt(thatK.att()))) { + return false; } - - private boolean attEquals(K thisK, K thatK) { - if (!filterAtt(thisK.att()).equals(filterAtt(thatK.att()))) { - return false; - } - if ((thisK instanceof KToken && thatK instanceof KToken) - || (thisK instanceof KVariable && thatK instanceof KVariable) - || (thisK instanceof InjectedKLabel && thatK instanceof InjectedKLabel)) { - return thisK.equals(thatK); - } else if (thisK instanceof KApply thisKItem && thatK instanceof KApply thatKItem) { - return thisKItem.klabel().equals(thatKItem.klabel()) && attEquals(thisKItem.klist().items(), thatKItem.klist().items()); - } else if (thisK instanceof KSequence && thatK instanceof KSequence) { - return attEquals(((KSequence) thisK).items(), ((KSequence) thatK).items()); - } else if (thisK instanceof KRewrite thisKR && thatK instanceof KRewrite thatKR) { - return attEquals(thisKR.left(), thatKR.left()) && attEquals(thisKR.right(), thatKR.right()); - } else { - return false; - } + if ((thisK instanceof KToken && thatK instanceof KToken) + || (thisK instanceof KVariable && thatK instanceof KVariable) + || (thisK instanceof InjectedKLabel && thatK instanceof InjectedKLabel)) { + return thisK.equals(thatK); + } else if (thisK instanceof KApply thisKItem && thatK instanceof KApply thatKItem) { + return thisKItem.klabel().equals(thatKItem.klabel()) + && attEquals(thisKItem.klist().items(), thatKItem.klist().items()); + } else if (thisK instanceof KSequence && thatK instanceof KSequence) { + return attEquals(((KSequence) thisK).items(), ((KSequence) thatK).items()); + } else if (thisK instanceof KRewrite thisKR && thatK instanceof KRewrite thatKR) { + return attEquals(thisKR.left(), thatKR.left()) && attEquals(thisKR.right(), thatKR.right()); + } else { + return false; } + } - private boolean attEquals(List thisK, List thatK) { - if (thisK.size() != thatK.size()) { - return false; - } - for (int i = 0; i < thisK.size(); i++) { - if (!attEquals(thisK.get(i), thatK.get(i))) { - return false; - } - } - return true; + private boolean attEquals(List thisK, List thatK) { + if (thisK.size() != thatK.size()) { + return false; } - - private Map, Object> filterAtt(Att att) { - return att.att().filterKeys(tuple -> attNames.contains(tuple._1())); + for (int i = 0; i < thisK.size(); i++) { + if (!attEquals(thisK.get(i), thatK.get(i))) { + return false; + } } + return true; + } - @Override - public String toString() { - return k.toString(); - } + private Map, Object> filterAtt(Att att) { + return att.att().filterKeys(tuple -> attNames.contains(tuple._1())); + } - @Override - public int hashCode() { - return k.hashCode(); - } + @Override + public String toString() { + return k.toString(); + } - public K get() { - return k; - } + @Override + public int hashCode() { + return k.hashCode(); + } + + public K get() { + return k; + } } diff --git a/kore/src/main/java/org/kframework/kore/ExistsK.java b/kore/src/main/java/org/kframework/kore/ExistsK.java index 95c121a007a..b8966e1bbb3 100644 --- a/kore/src/main/java/org/kframework/kore/ExistsK.java +++ b/kore/src/main/java/org/kframework/kore/ExistsK.java @@ -1,17 +1,15 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.kore; -/** - * Checks whether particular K pattern given as a visitor exists. - */ +/** Checks whether particular K pattern given as a visitor exists. */ public class ExistsK extends AbstractFoldK { - @Override - public Boolean unit() { - return false; - } + @Override + public Boolean unit() { + return false; + } - @Override - public Boolean merge(Boolean a, Boolean b) { - return a || b; - } + @Override + public Boolean merge(Boolean a, Boolean b) { + return a || b; + } } diff --git a/kore/src/main/java/org/kframework/kore/FindK.java b/kore/src/main/java/org/kframework/kore/FindK.java index 1df92bdfe57..243527da102 100644 --- a/kore/src/main/java/org/kframework/kore/FindK.java +++ b/kore/src/main/java/org/kframework/kore/FindK.java @@ -4,17 +4,15 @@ import org.kframework.Collections; import scala.collection.Set; -/** - * Finds all patterns described by the visitor - */ +/** Finds all patterns described by the visitor */ public class FindK extends AbstractFoldK> { - @Override - public Set unit() { - return Collections.Set(); - } + @Override + public Set unit() { + return Collections.Set(); + } - @Override - public Set merge(Set a, Set b) { - return Collections.or(a, b); - } + @Override + public Set merge(Set a, Set b) { + return Collections.or(a, b); + } } diff --git a/kore/src/main/java/org/kframework/kore/TransformK.java b/kore/src/main/java/org/kframework/kore/TransformK.java index 0dcb79d8be7..fe106f08c27 100644 --- a/kore/src/main/java/org/kframework/kore/TransformK.java +++ b/kore/src/main/java/org/kframework/kore/TransformK.java @@ -1,91 +1,89 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.kore; -import java.util.ArrayList; - import static org.kframework.kore.KORE.KApply; import static org.kframework.kore.KORE.KAs; import static org.kframework.kore.KORE.KList; import static org.kframework.kore.KORE.KRewrite; import static org.kframework.kore.KORE.KSequence; -/** - * Abstract K to K transformer. - */ +import java.util.ArrayList; + +/** Abstract K to K transformer. */ public class TransformK extends AbstractKTransformer { - @Override - public K apply(KApply k) { - ArrayList newItems = new ArrayList<>(k.klist().items()); - boolean change = false; - for (int i = 0; i < newItems.size(); ++i) { - K in = newItems.get(i); - K out = apply(in); - newItems.set(i, out); - change = change || (in != out); - } - if (change) { - return KApply(apply(k.klabel()), KList(newItems), k.att()); - } else { - return k; - } + @Override + public K apply(KApply k) { + ArrayList newItems = new ArrayList<>(k.klist().items()); + boolean change = false; + for (int i = 0; i < newItems.size(); ++i) { + K in = newItems.get(i); + K out = apply(in); + newItems.set(i, out); + change = change || (in != out); } - - private KLabel apply(KLabel klabel) { - return klabel; + if (change) { + return KApply(apply(k.klabel()), KList(newItems), k.att()); + } else { + return k; } + } - @Override - public K apply(KRewrite k) { - K l = apply(k.left()); - K r = apply(k.right()); - if (l != k.left() || r != k.right()) { - return KRewrite(l, r, k.att()); - } else { - return k; - } - } + private KLabel apply(KLabel klabel) { + return klabel; + } - @Override - public K apply(KAs k) { - K l = apply(k.pattern()); - K r = apply(k.alias()); - if (l != k.pattern() || r != k.alias()) { - return KAs(l, r, k.att()); - } else { - return k; - } + @Override + public K apply(KRewrite k) { + K l = apply(k.left()); + K r = apply(k.right()); + if (l != k.left() || r != k.right()) { + return KRewrite(l, r, k.att()); + } else { + return k; } + } - @Override - public K apply(KToken k) { - return k; + @Override + public K apply(KAs k) { + K l = apply(k.pattern()); + K r = apply(k.alias()); + if (l != k.pattern() || r != k.alias()) { + return KAs(l, r, k.att()); + } else { + return k; } + } - @Override - public K apply(KVariable k) { - return k; - } + @Override + public K apply(KToken k) { + return k; + } - @Override - public K apply(KSequence k) { - ArrayList newItems = new ArrayList<>(k.items()); - boolean change = false; - for (int i = 0; i < newItems.size(); ++i) { - K in = newItems.get(i); - K out = apply(newItems.get(i)); - newItems.set(i, out); - change = change || (in != out); - } - if (change) { - return KSequence(newItems, k.att()); - } else { - return k; - } - } + @Override + public K apply(KVariable k) { + return k; + } - @Override - public K apply(InjectedKLabel k) { - return k; + @Override + public K apply(KSequence k) { + ArrayList newItems = new ArrayList<>(k.items()); + boolean change = false; + for (int i = 0; i < newItems.size(); ++i) { + K in = newItems.get(i); + K out = apply(newItems.get(i)); + newItems.set(i, out); + change = change || (in != out); } + if (change) { + return KSequence(newItems, k.att()); + } else { + return k; + } + } + + @Override + public K apply(InjectedKLabel k) { + return k; + } } diff --git a/kore/src/main/java/org/kframework/kore/VisitK.java b/kore/src/main/java/org/kframework/kore/VisitK.java index bea4065a83f..e122e05cf5a 100644 --- a/kore/src/main/java/org/kframework/kore/VisitK.java +++ b/kore/src/main/java/org/kframework/kore/VisitK.java @@ -1,9 +1,5 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.kore; -/** - * Abstract visitor for K. - */ -public class VisitK extends AbstractKVisitor { - -} +/** Abstract visitor for K. */ +public class VisitK extends AbstractKVisitor {} diff --git a/kore/src/main/java/org/kframework/parser/KoreParser.java b/kore/src/main/java/org/kframework/parser/KoreParser.java index da57aa8ca3c..32455e1e69a 100644 --- a/kore/src/main/java/org/kframework/parser/KoreParser.java +++ b/kore/src/main/java/org/kframework/parser/KoreParser.java @@ -1,6 +1,9 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.parser; +import static org.kframework.Collections.*; + +import java.io.File; import org.kframework.Collections; import org.kframework.attributes.Att; import org.kframework.kore.K; @@ -8,43 +11,39 @@ import org.kframework.parser.kore.Pattern; import org.kframework.parser.kore.parser.ParseError; import org.kframework.parser.kore.parser.TextToKore; -import org.kframework.utils.StringUtil; import org.kframework.utils.errorsystem.KEMException; -import scala.collection.Map; import scala.Tuple2; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.Properties; - -import static org.kframework.Collections.*; +import scala.collection.Map; public class KoreParser { - private final TextToKore textToKore; - private final org.kframework.parser.kore.parser.KoreToK koreToK; - - public KoreParser(Map sortAttMap) { - textToKore = new TextToKore(); - koreToK = new org.kframework.parser.kore.parser.KoreToK(stream(sortAttMap).map(t -> Tuple2.apply(t._1().name(), t._2().getOptional(Att.HOOK()).orElse(""))).collect(Collections.toMap())); + private final TextToKore textToKore; + private final org.kframework.parser.kore.parser.KoreToK koreToK; + + public KoreParser(Map sortAttMap) { + textToKore = new TextToKore(); + koreToK = + new org.kframework.parser.kore.parser.KoreToK( + stream(sortAttMap) + .map(t -> Tuple2.apply(t._1().name(), t._2().getOptional(Att.HOOK()).orElse(""))) + .collect(Collections.toMap())); + } + + public K parseString(String koreString) { + try { + Pattern kore = textToKore.parsePattern(koreString); + return koreToK.apply(kore); + } catch (ParseError parseError) { + throw KEMException.criticalError("Parse error\n", parseError); } + } - public K parseString(String koreString) { - try { - Pattern kore = textToKore.parsePattern(koreString); - return koreToK.apply(kore); - } catch (ParseError parseError) { - throw KEMException.criticalError("Parse error\n", parseError ); - } - } + public K parseFile(File koreFile) throws ParseError { + Pattern kore = textToKore.parsePattern(koreFile, 0); + return koreToK.apply(kore); + } - public K parseFile(File koreFile) throws ParseError { - Pattern kore = textToKore.parsePattern(koreFile, 0); - return koreToK.apply(kore); - } - - public K parseFile(File koreFile, int line) throws ParseError { - Pattern kore = textToKore.parsePattern(koreFile, line); - return koreToK.apply(kore); - } + public K parseFile(File koreFile, int line) throws ParseError { + Pattern kore = textToKore.parsePattern(koreFile, line); + return koreToK.apply(kore); + } } diff --git a/kore/src/main/java/org/kframework/rewriter/SearchType.java b/kore/src/main/java/org/kframework/rewriter/SearchType.java index 74a2b98f33f..21497fac24a 100644 --- a/kore/src/main/java/org/kframework/rewriter/SearchType.java +++ b/kore/src/main/java/org/kframework/rewriter/SearchType.java @@ -1,12 +1,10 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.rewriter; -/** - * Created by manasvi on 9/11/15. - */ +/** Created by manasvi on 9/11/15. */ public enum SearchType { - ONE, - PLUS, - STAR, - FINAL + ONE, + PLUS, + STAR, + FINAL } diff --git a/kore/src/main/java/org/kframework/utils/OS.java b/kore/src/main/java/org/kframework/utils/OS.java index 71dd5315006..09e82cc3c24 100644 --- a/kore/src/main/java/org/kframework/utils/OS.java +++ b/kore/src/main/java/org/kframework/utils/OS.java @@ -1,55 +1,57 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils; -import org.kframework.utils.errorsystem.KEMException; - import java.util.Arrays; import java.util.List; +import org.kframework.utils.errorsystem.KEMException; public enum OS { - OSX(true), LINUX(true), UNKNOWN(false), WINDOWS(false); - - OS(boolean isPosix) { - this.isPosix = isPosix; - } - - public final boolean isPosix; - - public static OS current() { - String osString = System.getProperty("os.name").toLowerCase(); - if (osString.contains("nix") || osString.contains("nux")) - return OS.LINUX; - else if (osString.contains("win")) - return OS.WINDOWS; - else if (osString.contains("mac")) - return OS.OSX; - else - return OS.UNKNOWN; + OSX(true), + LINUX(true), + UNKNOWN(false), + WINDOWS(false); + + OS(boolean isPosix) { + this.isPosix = isPosix; + } + + public final boolean isPosix; + + public static OS current() { + String osString = System.getProperty("os.name").toLowerCase(); + if (osString.contains("nix") || osString.contains("nux")) return OS.LINUX; + else if (osString.contains("win")) return OS.WINDOWS; + else if (osString.contains("mac")) return OS.OSX; + else return OS.UNKNOWN; + } + + public String getSharedLibraryExtension() { + if (this == OSX) { + return ".dylib"; + } else if (this == LINUX) { + return ".so"; + } else { + throw KEMException.internalError( + "Shared libraries are not supported on: " + System.getProperty("os.name")); } - - public String getSharedLibraryExtension() { - if (this == OSX) { - return ".dylib"; - } else if (this == LINUX) { - return ".so"; - } else { - throw KEMException.internalError("Shared libraries are not supported on: " + System.getProperty("os.name")); - } + } + + public List getSharedLibraryCompilerFlags() { + return Arrays.asList("-fPIC", "-shared"); + } + + public String getNativeExecutable(String executable) { + if (this == UNKNOWN) { + throw KEMException.internalError( + "Unknown OS type. " + + System.getProperty("os.name") + + " not recognized. " + + "Please contact K developers with details of your OS."); } - - public List getSharedLibraryCompilerFlags() { - return Arrays.asList("-fPIC", "-shared"); - } - - public String getNativeExecutable(String executable) { - if (this == UNKNOWN) { - throw KEMException.internalError( - "Unknown OS type. " + System.getProperty("os.name") + " not recognized. " + - "Please contact K developers with details of your OS."); - } - if (this == WINDOWS) { - throw KEMException.internalError("K is not supported on native windows. Please use the Windows Subsystem for Linux."); - } - return executable; + if (this == WINDOWS) { + throw KEMException.internalError( + "K is not supported on native windows. Please use the Windows Subsystem for Linux."); } + return executable; + } } diff --git a/kore/src/main/java/org/kframework/utils/StringUtil.java b/kore/src/main/java/org/kframework/utils/StringUtil.java index b2361b6cfc5..e148a98afea 100644 --- a/kore/src/main/java/org/kframework/utils/StringUtil.java +++ b/kore/src/main/java/org/kframework/utils/StringUtil.java @@ -1,744 +1,747 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils; -import org.apache.commons.lang3.StringUtils; -import com.beust.jcommander.JCommander; -import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.Set; import java.util.regex.Pattern; -import java.util.stream.Collectors; public class StringUtil { - /** - * Unescape the textual representation of a string specific to SDF and Maude. - * It removes the double quote at the beginning and end, and transforms special sequence - * of characters like "\n" into the newline character. - */ - public static String unquoteCString(String str) { - return unquoteCString(str, '"'); - } - public static String unquoteCString(String str, char delimiter) { - StringBuilder sb = new StringBuilder(); - if (str.charAt(0) != delimiter) { - throw new IllegalArgumentException("Expected to find " + delimiter + " at the beginning of string: " + str); - } - if (str.charAt(str.length() - 1) != delimiter) { - throw new IllegalArgumentException("Expected to find " + delimiter + " at the end of string: " + str); - } - for (int i = 1; i < str.length() - 1; i++) { - if (str.charAt(i) == '\\') { - if (str.charAt(i + 1) == '\\') - sb.append('\\'); - else if (str.charAt(i + 1) == 'n') - sb.append('\n'); - else if (str.charAt(i + 1) == 'r') - sb.append('\r'); - else if (str.charAt(i + 1) == 't') - sb.append('\t'); - else if (str.charAt(i + 1) == 'f') - sb.append('\f'); - else if (str.charAt(i + 1) == delimiter) - sb.append(delimiter); - else if (str.charAt(i + 1) >= '0' && str.charAt(i + 1) <= '9') { - // found an octal value - int a2 = str.charAt(i + 1) - '0'; - int a1 = str.charAt(i + 2) - '0'; - if (a1 < 0 || a1 > 9) - throw new IllegalArgumentException("Malformed octal value in string:" + str); - int a0 = str.charAt(i + 3) - '0'; - if (a0 < 0 || a0 > 9) - throw new IllegalArgumentException("Malformed octal value in string:" + str); - int decimal = a2 * 8 * 8 + a1 * 8 + a0; - sb.append((char) decimal); - i++; i++; - } - i++; - } else - sb.append(str.charAt(i)); - } + /** + * Unescape the textual representation of a string specific to SDF and Maude. It removes the + * double quote at the beginning and end, and transforms special sequence of characters like "\n" + * into the newline character. + */ + public static String unquoteCString(String str) { + return unquoteCString(str, '"'); + } - return sb.toString(); - } - - /** - * Takes the internal representation of a string, and creates the textual representation - * that is ready to be printed. - * It adds double quote at the beginning and end, and transforms special characters into - * the textual representation (ex: newline becomes "\n"). - */ - public static String enquoteCString(String value) { - return enquoteCString(value, '"'); - } - public static String enquoteCString(String value, char delimiter) { - final int length = value.length(); - StringBuilder result = new StringBuilder(); - result.append(delimiter); - for (int offset = 0, codepoint; offset < length; offset += Character.charCount(codepoint)) { - codepoint = value.codePointAt(offset); - if (codepoint > 0xFF) { - result.appendCodePoint(codepoint); - } else if (codepoint == delimiter) { - result.append("\\" + delimiter); - } else if (codepoint == '\\') { - result.append("\\\\"); - } else if (codepoint == '\n') { - result.append("\\n"); - } else if (codepoint == '\t') { - result.append("\\t"); - } else if (codepoint == '\r') { - result.append("\\r"); - } else if (codepoint == '\f') { - result.append("\\f"); - } else if (codepoint >= 32 && codepoint < 127) { - result.append((char)codepoint); - } else if (codepoint <= 0xff) { - result.append("\\"); - result.append(String.format("%03o", codepoint)); - } - } - result.append(delimiter); - return result.toString(); + public static String unquoteCString(String str, char delimiter) { + StringBuilder sb = new StringBuilder(); + if (str.charAt(0) != delimiter) { + throw new IllegalArgumentException( + "Expected to find " + delimiter + " at the beginning of string: " + str); + } + if (str.charAt(str.length() - 1) != delimiter) { + throw new IllegalArgumentException( + "Expected to find " + delimiter + " at the end of string: " + str); + } + for (int i = 1; i < str.length() - 1; i++) { + if (str.charAt(i) == '\\') { + if (str.charAt(i + 1) == '\\') sb.append('\\'); + else if (str.charAt(i + 1) == 'n') sb.append('\n'); + else if (str.charAt(i + 1) == 'r') sb.append('\r'); + else if (str.charAt(i + 1) == 't') sb.append('\t'); + else if (str.charAt(i + 1) == 'f') sb.append('\f'); + else if (str.charAt(i + 1) == delimiter) sb.append(delimiter); + else if (str.charAt(i + 1) >= '0' && str.charAt(i + 1) <= '9') { + // found an octal value + int a2 = str.charAt(i + 1) - '0'; + int a1 = str.charAt(i + 2) - '0'; + if (a1 < 0 || a1 > 9) + throw new IllegalArgumentException("Malformed octal value in string:" + str); + int a0 = str.charAt(i + 3) - '0'; + if (a0 < 0 || a0 > 9) + throw new IllegalArgumentException("Malformed octal value in string:" + str); + int decimal = a2 * 8 * 8 + a1 * 8 + a0; + sb.append((char) decimal); + i++; + i++; + } + i++; + } else sb.append(str.charAt(i)); } - public static void throwIfSurrogatePair(int codePoint) { - if (codePoint >= 0xd800 && codePoint <= 0xdfff) { - //we are trying to encode a surrogate pair, which the unicode - //standard forbids - throw new IllegalArgumentException(Integer.toHexString(codePoint) + - " is not in the accepted unicode range."); - } - if (codePoint >= 0x110000) - throw new IllegalArgumentException(Integer.toHexString(codePoint) + - " is not in the accepted unicode range."); + return sb.toString(); + } + + /** + * Takes the internal representation of a string, and creates the textual representation that is + * ready to be printed. It adds double quote at the beginning and end, and transforms special + * characters into the textual representation (ex: newline becomes "\n"). + */ + public static String enquoteCString(String value) { + return enquoteCString(value, '"'); + } + + public static String enquoteCString(String value, char delimiter) { + final int length = value.length(); + StringBuilder result = new StringBuilder(); + result.append(delimiter); + for (int offset = 0, codepoint; offset < length; offset += Character.charCount(codepoint)) { + codepoint = value.codePointAt(offset); + if (codepoint > 0xFF) { + result.appendCodePoint(codepoint); + } else if (codepoint == delimiter) { + result.append("\\" + delimiter); + } else if (codepoint == '\\') { + result.append("\\\\"); + } else if (codepoint == '\n') { + result.append("\\n"); + } else if (codepoint == '\t') { + result.append("\\t"); + } else if (codepoint == '\r') { + result.append("\\r"); + } else if (codepoint == '\f') { + result.append("\\f"); + } else if (codepoint >= 32 && codepoint < 127) { + result.append((char) codepoint); + } else if (codepoint <= 0xff) { + result.append("\\"); + result.append(String.format("%03o", codepoint)); + } } + result.append(delimiter); + return result.toString(); + } - public static int lastIndexOfAny(String str, String search, int offset) { - if (str.equals("") || search.equals("")) { - return -1; - } - int start = str.offsetByCodePoints(0, offset); - for (int i = start, strCodepoint; i >= 0; i -= Character.charCount(strCodepoint)) { - strCodepoint = str.codePointAt(i); - for (int j = search.length() - 1, searchCodepoint; j >= 0; j -= Character.charCount(searchCodepoint)) { - searchCodepoint = search.codePointAt(j); - if (strCodepoint == searchCodepoint) { - return i; - } - } - } - return -1; + public static void throwIfSurrogatePair(int codePoint) { + if (codePoint >= 0xd800 && codePoint <= 0xdfff) { + // we are trying to encode a surrogate pair, which the unicode + // standard forbids + throw new IllegalArgumentException( + Integer.toHexString(codePoint) + " is not in the accepted unicode range."); } + if (codePoint >= 0x110000) + throw new IllegalArgumentException( + Integer.toHexString(codePoint) + " is not in the accepted unicode range."); + } - public static int indexOfAny(String str, String search, int offset) { - if (str.equals("") || search.equals("")) { - return -1; - } - int start = str.offsetByCodePoints(0, offset); - for (int i = start, strCodepoint; i < str.length(); i += Character.charCount(strCodepoint)) { - strCodepoint = str.codePointAt(i); - for (int j = 0, searchCodepoint; j < search.length(); j += Character.charCount(searchCodepoint)) { - searchCodepoint = search.codePointAt(j); - if (strCodepoint == searchCodepoint) { - return i; - } - } - } - return -1; - } - - /** - * Removes the first and last double-quote characters and unescapes special characters - * that start with backslash: newline, carriage return, line feed, tab and backslash. - * Characters between 127 and 255 are stored as \xFF - * Characters between 256 and 65535 are stored as \uFFFF - * Characters above 65536 are stored as \u0010FFFF - * @param str Python like double-quoted string - * @return unescaped and unquoted string - */ - public static String unquoteKString(String str) { - StringBuilder sb = new StringBuilder(); - if (str.charAt(0) != '"') { - throw new IllegalArgumentException("Expected to find double quote at the beginning of string: " + str); - } - if (str.charAt(str.length() - 1) != '"') { - throw new IllegalArgumentException("Expected to find double quote at the end of string: " + str); - } - for (int i = 1; i < str.length() - 1; i++) { - if (str.charAt(i) == '\\') { - if (str.charAt(i + 1) == '"') { - sb.append('"'); - i++; - } else if (str.charAt(i + 1) == '\\') { - sb.append('\\'); - i++; - } else if (str.charAt(i + 1) == 'n') { - sb.append('\n'); - i++; - } else if (str.charAt(i + 1) == 'r') { - sb.append('\r'); - i++; - } else if (str.charAt(i + 1) == 't') { - sb.append('\t'); - i++; - } else if (str.charAt(i + 1) == 'f') { - sb.append('\f'); - i++; - } else if (str.charAt(i + 1) == 'x') { - String arg = str.substring(i + 2, i + 4); - sb.append((char)Integer.parseInt(arg, 16)); - i += 3; - } else if (str.charAt(i + 1) == 'u') { - String arg = str.substring(i + 2, i + 6); - int codePoint = Integer.parseInt(arg, 16); - StringUtil.throwIfSurrogatePair(codePoint); - sb.append((char)codePoint); - i += 5; - } else if (str.charAt(i + 1) == 'U') { - String arg = str.substring(i + 2, i + 10); - int codePoint = Integer.parseInt(arg, 16); - StringUtil.throwIfSurrogatePair(codePoint); - sb.append(Character.toChars(codePoint)); - i += 9; - } - } else { - sb.append(str.charAt(i)); - } - } - return sb.toString(); - } - - /** - * Get the escaped string for a Unicode codepoint: - * Codepoints between 32 and 126 are stored directly as the character - * Codepoints between 0 and 31 and between 127 and 255 are stored as \xFF - * Codepoints between 256 and 65535 are stored as \uFFFF - * Codepoints above 65536 are stored as \u0010FFFF - * @param value a Unicode codepoint - * @return representation of the codepoint as an escaped string - */ - public static String getUnicodeEscape(int codepoint) { - if (32 <= codepoint && codepoint < 127) { - return String.valueOf((char) codepoint); - } - if (codepoint <= 0xff) { - return "\\x" + String.format("%02x", codepoint); - } - if (codepoint <= 0xffff) { - return "\\u" + String.format("%04x", codepoint); - } - return "\\U" + String.format("%08x", codepoint); - } - - /** - * Adds double-quote at the beginning and end of the string and escapes special characters - * with backslash: newline, carriage return, line feed, tab and backslash. - * Characters between 127 and 255 are stored as \xFF - * Characters between 256 and 65535 are stored as \uFFFF - * Characters above 65536 are stored as \u0010FFFF - * @param value any string - * @return Python like textual representation of the string - */ - public static String enquoteKString(String value) { - final int length = value.length(); - StringBuilder result = new StringBuilder(); - result.append("\""); - for (int offset = 0, codepoint; offset < length; offset += Character.charCount(codepoint)) { - codepoint = value.codePointAt(offset); - if (codepoint == '"') { - result.append("\\\""); - } else if (codepoint == '\\') { - result.append("\\\\"); - } else if (codepoint == '\n') { - result.append("\\n"); - } else if (codepoint == '\t') { - result.append("\\t"); - } else if (codepoint == '\r') { - result.append("\\r"); - } else if (codepoint == '\f') { - result.append("\\f"); - } else { - result.append(StringUtil.getUnicodeEscape(codepoint)); - } - } - result.append("\""); - return result.toString(); - } - - /** - * Escapes all non-ASCII characters as follows: - * Characters between 32 and 126 are stored directly as the character - * Characters between 0 and 31 and between 127 and 255 are stored as \xFF - * Characters between 256 and 65535 are stored as \uFFFF - * Characters above 65536 are stored as \u0010FFFF - * @param value any string - * @return representation of the string with non-ASCII characters escaped - */ - public static String escapeNonASCII(String value) { - final int length = value.length(); - StringBuilder result = new StringBuilder(); - for (int offset = 0, codepoint; offset < length; offset += Character.charCount(codepoint)) { - codepoint = value.codePointAt(offset); - result.append(StringUtil.getUnicodeEscape(codepoint)); - } - return result.toString(); - } - - /** - * Returns the two-letter code for a general category of Unicode code point. - */ - public static String getCategoryCode(byte cat) { - switch(cat) { - case Character.COMBINING_SPACING_MARK: - return "Mc"; - case Character.CONNECTOR_PUNCTUATION: - return "Pc"; - case Character.CONTROL: - return "Cc"; - case Character.CURRENCY_SYMBOL: - return "Sc"; - case Character.DASH_PUNCTUATION: - return "Pd"; - case Character.DECIMAL_DIGIT_NUMBER: - return "Nd"; - case Character.ENCLOSING_MARK: - return "Me"; - case Character.END_PUNCTUATION: - return "Pe"; - case Character.FINAL_QUOTE_PUNCTUATION: - return "Pf"; - case Character.FORMAT: - return "Cf"; - case Character.INITIAL_QUOTE_PUNCTUATION: - return "Pi"; - case Character.LETTER_NUMBER: - return "Nl"; - case Character.LINE_SEPARATOR: - return "Zl"; - case Character.LOWERCASE_LETTER: - return "Ll"; - case Character.MATH_SYMBOL: - return "Sm"; - case Character.MODIFIER_LETTER: - return "Lm"; - case Character.MODIFIER_SYMBOL: - return "Sk"; - case Character.NON_SPACING_MARK: - return "Mn"; - case Character.OTHER_LETTER: - return "Lo"; - case Character.OTHER_NUMBER: - return "No"; - case Character.OTHER_PUNCTUATION: - return "Po"; - case Character.OTHER_SYMBOL: - return "So"; - case Character.PARAGRAPH_SEPARATOR: - return "Zp"; - case Character.PRIVATE_USE: - return "Co"; - case Character.SPACE_SEPARATOR: - return "Zs"; - case Character.START_PUNCTUATION: - return "Ps"; - case Character.SURROGATE: - return "Cs"; - case Character.TITLECASE_LETTER: - return "Lt"; - case Character.UNASSIGNED: - return "Cn"; - case Character.UPPERCASE_LETTER: - return "Lu"; - default: - assert false: "should be exhaustive list of categories"; - return null; //unreachable - } + public static int lastIndexOfAny(String str, String search, int offset) { + if (str.equals("") || search.equals("")) { + return -1; } + int start = str.offsetByCodePoints(0, offset); + for (int i = start, strCodepoint; i >= 0; i -= Character.charCount(strCodepoint)) { + strCodepoint = str.codePointAt(i); + for (int j = search.length() - 1, searchCodepoint; + j >= 0; + j -= Character.charCount(searchCodepoint)) { + searchCodepoint = search.codePointAt(j); + if (strCodepoint == searchCodepoint) { + return i; + } + } + } + return -1; + } - public static String getDirectionalityCode(byte cat) { - switch(cat) { - case Character.DIRECTIONALITY_ARABIC_NUMBER: - return "AN"; - case Character.DIRECTIONALITY_BOUNDARY_NEUTRAL: - return "BN"; - case Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR: - return "CS"; - case Character.DIRECTIONALITY_EUROPEAN_NUMBER: - return "EN"; - case Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR: - return "ES"; - case Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR: - return "ET"; - case Character.DIRECTIONALITY_LEFT_TO_RIGHT: - return "L"; - case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING: - return "LRE"; - case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE: - return "LRO"; - case Character.DIRECTIONALITY_NONSPACING_MARK: - return "NSM"; - case Character.DIRECTIONALITY_OTHER_NEUTRALS: - return "ON"; - case Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR: - return "B"; - case Character.DIRECTIONALITY_POP_DIRECTIONAL_FORMAT: - return "PDF"; - case Character.DIRECTIONALITY_RIGHT_TO_LEFT: - return "R"; - case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: - return "AL"; - case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING: - return "RLE"; - case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE: - return "RLO"; - case Character.DIRECTIONALITY_SEGMENT_SEPARATOR: - return "S"; - case Character.DIRECTIONALITY_UNDEFINED: - throw new IllegalArgumentException(); - case Character.DIRECTIONALITY_WHITESPACE: - return "WS"; - default: - assert false: "should be exhaustive list of directionalities"; - return null; //unreachable - } + public static int indexOfAny(String str, String search, int offset) { + if (str.equals("") || search.equals("")) { + return -1; } + int start = str.offsetByCodePoints(0, offset); + for (int i = start, strCodepoint; i < str.length(); i += Character.charCount(strCodepoint)) { + strCodepoint = str.codePointAt(i); + for (int j = 0, searchCodepoint; + j < search.length(); + j += Character.charCount(searchCodepoint)) { + searchCodepoint = search.codePointAt(j); + if (strCodepoint == searchCodepoint) { + return i; + } + } + } + return -1; + } - /** - * split string to lines in a way that no lines will exceed 80 columns - * NOTE: strings split only at whitespace character ' ', if string contains no ' ', it's returned as is - * @param str string to split - * @return new string with newlines added - */ - public static String splitLines(String str) { - return splitLines(str, 80); - } - - /** - * split string to lines in a way that no lines will exceed `col` columns - * NOTE: strings split only at whitespace character ' ', if string contains no ' ', it's returned as is - * @param str string to split - * @param col rightmost column - * @return new string with newlines added - */ - public static String splitLines(String str, final int col) { - String[] lines = str.split("\n"); - StringBuilder builder = new StringBuilder(); - String nl = ""; - for (String line : lines) { - builder.append(nl); - if (line.length() < col) { - builder.append(line); - } else { - builder.append(splitLine(line, col)); - } - nl = "\n"; - } - return builder.toString(); + /** + * Removes the first and last double-quote characters and unescapes special characters that start + * with backslash: newline, carriage return, line feed, tab and backslash. Characters between 127 + * and 255 are stored as \xFF Characters between 256 and 65535 are stored as \uFFFF Characters + * above 65536 are stored as \u0010FFFF + * + * @param str Python like double-quoted string + * @return unescaped and unquoted string + */ + public static String unquoteKString(String str) { + StringBuilder sb = new StringBuilder(); + if (str.charAt(0) != '"') { + throw new IllegalArgumentException( + "Expected to find double quote at the beginning of string: " + str); } + if (str.charAt(str.length() - 1) != '"') { + throw new IllegalArgumentException( + "Expected to find double quote at the end of string: " + str); + } + for (int i = 1; i < str.length() - 1; i++) { + if (str.charAt(i) == '\\') { + if (str.charAt(i + 1) == '"') { + sb.append('"'); + i++; + } else if (str.charAt(i + 1) == '\\') { + sb.append('\\'); + i++; + } else if (str.charAt(i + 1) == 'n') { + sb.append('\n'); + i++; + } else if (str.charAt(i + 1) == 'r') { + sb.append('\r'); + i++; + } else if (str.charAt(i + 1) == 't') { + sb.append('\t'); + i++; + } else if (str.charAt(i + 1) == 'f') { + sb.append('\f'); + i++; + } else if (str.charAt(i + 1) == 'x') { + String arg = str.substring(i + 2, i + 4); + sb.append((char) Integer.parseInt(arg, 16)); + i += 3; + } else if (str.charAt(i + 1) == 'u') { + String arg = str.substring(i + 2, i + 6); + int codePoint = Integer.parseInt(arg, 16); + StringUtil.throwIfSurrogatePair(codePoint); + sb.append((char) codePoint); + i += 5; + } else if (str.charAt(i + 1) == 'U') { + String arg = str.substring(i + 2, i + 10); + int codePoint = Integer.parseInt(arg, 16); + StringUtil.throwIfSurrogatePair(codePoint); + sb.append(Character.toChars(codePoint)); + i += 9; + } + } else { + sb.append(str.charAt(i)); + } + } + return sb.toString(); + } - private static String splitLine(String str, final int col) { - if (str.length() < col) { - return str; - } + /** + * Get the escaped string for a Unicode codepoint: Codepoints between 32 and 126 are stored + * directly as the character Codepoints between 0 and 31 and between 127 and 255 are stored as + * \xFF Codepoints between 256 and 65535 are stored as \uFFFF Codepoints above 65536 are stored as + * \u0010FFFF + * + * @param value a Unicode codepoint + * @return representation of the codepoint as an escaped string + */ + public static String getUnicodeEscape(int codepoint) { + if (32 <= codepoint && codepoint < 127) { + return String.valueOf((char) codepoint); + } + if (codepoint <= 0xff) { + return "\\x" + String.format("%02x", codepoint); + } + if (codepoint <= 0xffff) { + return "\\u" + String.format("%04x", codepoint); + } + return "\\U" + String.format("%08x", codepoint); + } - // keep indentation of long lines (like term ambiguities) - int firstChar = 0; - while (str.charAt(firstChar) == ' ') - firstChar++; - // scan from `col` to left - for (int i = col - 1; i > firstChar; i--) { - if (str.charAt(i) == ' ') { - return str.substring(0, i) + "\n" + splitLine(str.substring(i + 1), col); - } - } + /** + * Adds double-quote at the beginning and end of the string and escapes special characters with + * backslash: newline, carriage return, line feed, tab and backslash. Characters between 127 and + * 255 are stored as \xFF Characters between 256 and 65535 are stored as \uFFFF Characters above + * 65536 are stored as \u0010FFFF + * + * @param value any string + * @return Python like textual representation of the string + */ + public static String enquoteKString(String value) { + final int length = value.length(); + StringBuilder result = new StringBuilder(); + result.append("\""); + for (int offset = 0, codepoint; offset < length; offset += Character.charCount(codepoint)) { + codepoint = value.codePointAt(offset); + if (codepoint == '"') { + result.append("\\\""); + } else if (codepoint == '\\') { + result.append("\\\\"); + } else if (codepoint == '\n') { + result.append("\\n"); + } else if (codepoint == '\t') { + result.append("\\t"); + } else if (codepoint == '\r') { + result.append("\\r"); + } else if (codepoint == '\f') { + result.append("\\f"); + } else { + result.append(StringUtil.getUnicodeEscape(codepoint)); + } + } + result.append("\""); + return result.toString(); + } - // we reached the beginning of the string and it contains no whitespaces before the `col` - // but it's longer than `col` so we should replace first space after rightmost column - // with a newline to make it shorter - for (int i = col; i < str.length(); i++) { - if (str.charAt(i) == ' ') { - return str.substring(0, i) + "\n" + splitLine(str.substring(i + 1), col); - } - } + /** + * Escapes all non-ASCII characters as follows: Characters between 32 and 126 are stored directly + * as the character Characters between 0 and 31 and between 127 and 255 are stored as \xFF + * Characters between 256 and 65535 are stored as \uFFFF Characters above 65536 are stored as + * \u0010FFFF + * + * @param value any string + * @return representation of the string with non-ASCII characters escaped + */ + public static String escapeNonASCII(String value) { + final int length = value.length(); + StringBuilder result = new StringBuilder(); + for (int offset = 0, codepoint; offset < length; offset += Character.charCount(codepoint)) { + codepoint = value.codePointAt(offset); + result.append(StringUtil.getUnicodeEscape(codepoint)); + } + return result.toString(); + } - // string has no spaces to split - return str; - } - - /** - * Takes a textual representation of a KLabel using backticks to delimit - * and returns the string representation of the KLabel that it corresponds to - * - * Used by the KAST parser. - * - * @param str An image of a parser token corresponding to a KLabel in KORE which - * begins and ends with backtick - * @return The string value of the KLabel - */ - public static String unescapeKoreKLabel(String str) { - char delimiter = '`'; - StringBuilder sb = new StringBuilder(); - if (str.charAt(0) != delimiter) { - throw new IllegalArgumentException("Expected to find " + delimiter + " at the beginning of string: " + str); - } - if (str.charAt(str.length() - 1) != delimiter) { - throw new IllegalArgumentException("Expected to find " + delimiter + " at the end of string: " + str); - } - for (int i = 1; i < str.length() - 1; i++) { - if (str.charAt(i) == 0x7F || str.charAt(i) < 32) - throw new IllegalArgumentException("Special characters not supported here:" + str); - if (str.charAt(i) == '\\') { - if (str.charAt(i + 1) == '\\') - sb.append('\\'); - else if (str.charAt(i + 1) == delimiter) - sb.append(delimiter); - i++; - } else - sb.append(str.charAt(i)); - } + /** Returns the two-letter code for a general category of Unicode code point. */ + public static String getCategoryCode(byte cat) { + switch (cat) { + case Character.COMBINING_SPACING_MARK: + return "Mc"; + case Character.CONNECTOR_PUNCTUATION: + return "Pc"; + case Character.CONTROL: + return "Cc"; + case Character.CURRENCY_SYMBOL: + return "Sc"; + case Character.DASH_PUNCTUATION: + return "Pd"; + case Character.DECIMAL_DIGIT_NUMBER: + return "Nd"; + case Character.ENCLOSING_MARK: + return "Me"; + case Character.END_PUNCTUATION: + return "Pe"; + case Character.FINAL_QUOTE_PUNCTUATION: + return "Pf"; + case Character.FORMAT: + return "Cf"; + case Character.INITIAL_QUOTE_PUNCTUATION: + return "Pi"; + case Character.LETTER_NUMBER: + return "Nl"; + case Character.LINE_SEPARATOR: + return "Zl"; + case Character.LOWERCASE_LETTER: + return "Ll"; + case Character.MATH_SYMBOL: + return "Sm"; + case Character.MODIFIER_LETTER: + return "Lm"; + case Character.MODIFIER_SYMBOL: + return "Sk"; + case Character.NON_SPACING_MARK: + return "Mn"; + case Character.OTHER_LETTER: + return "Lo"; + case Character.OTHER_NUMBER: + return "No"; + case Character.OTHER_PUNCTUATION: + return "Po"; + case Character.OTHER_SYMBOL: + return "So"; + case Character.PARAGRAPH_SEPARATOR: + return "Zp"; + case Character.PRIVATE_USE: + return "Co"; + case Character.SPACE_SEPARATOR: + return "Zs"; + case Character.START_PUNCTUATION: + return "Ps"; + case Character.SURROGATE: + return "Cs"; + case Character.TITLECASE_LETTER: + return "Lt"; + case Character.UNASSIGNED: + return "Cn"; + case Character.UPPERCASE_LETTER: + return "Lu"; + default: + assert false : "should be exhaustive list of categories"; + return null; // unreachable + } + } - return sb.toString(); - } - - /** - * Takes the value of a KLabel and returns a string representation, delimited with - * backticks, of the syntax of that KLabel in KORE. - * - * Used by the KAST pretty printer. - * - * @param str A string value corresponding to a KLabel. - * @return A string which can be parsed back by a KORE parser to reach the original KLabel. - */ - public static String escapeKoreKLabel(String value) { - char delimiter = '`'; - final int length = value.length(); - StringBuilder result = new StringBuilder(); - result.append(delimiter); - for (int offset = 0, codepoint; offset < length; offset += Character.charCount(codepoint)) { - codepoint = value.codePointAt(offset); - if (codepoint == 0x7F || codepoint < 32) { - throw new IllegalArgumentException("Special characters not supported here:" + value); - } else if (codepoint == delimiter) { - result.append("\\" + delimiter); - } else if (codepoint == '\\') { - result.append("\\\\"); - } else { - result.appendCodePoint(codepoint); - } - } - result.append(delimiter); - return result.toString(); - } - - public static String[] asciiReadableEncodingDefault = new String[] { - null,// 00 - null,// 01 - null,// 02 - null,// 03 - null,// 04 - null,// 05 - null,// 06 - null,// 07 - null,// 08 - null,// 09 - null,// 0a - null,// 0b - null,// 0c - null,// 0d - null,// 0e - null,// 0f - null,// 10 - null,// 11 - null,// 12 - null,// 13 - null,// 14 - null,// 15 - null,// 16 - null,// 17 - null,// 18 - null,// 19 - null,// 1a - null,// 1b - null,// 1c - null,// 1d - null,// 1e - null,// 1f - "Spce",// 20 - "Bang",// 21 - "Quot",// 22 - "Hash",// 23 - "Dolr",// 24 - "Perc",// 25 - "And",// 26 - "Apos",// 27 - "LPar",// 28 - "RPar",// 29 - "Star",// 2a - "Plus",// 2b - "Comm",// 2c - "-",// 2d - "Stop",// 2e - "Slsh",// 2f - "0",// 30 - "1",// 31 - "2",// 32 - "3",// 33 - "4",// 34 - "5",// 35 - "6",// 36 - "7",// 37 - "8",// 38 - "9",// 39 - "Coln",// 3a - "SCln",// 3b - "LT",// 3c - "Eqls",// 3d - "GT",// 3e - "Ques",// 3f - "AT",// 40 - "A",// 41 - "B",// 42 - "C",// 43 - "D",// 44 - "E",// 45 - "F",// 46 - "G",// 47 - "H",// 48 - "I",// 49 - "J",// 4a - "K",// 4b - "L",// 4c - "M",// 4d - "N",// 4e - "O",// 4f - "P",// 50 - "Q",// 51 - "R",// 52 - "S",// 53 - "T",// 54 - "U",// 55 - "V",// 56 - "W",// 57 - "X",// 58 - "Y",// 59 - "Z",// 5a - "LSqB",// 5b - "Bash",// 5c - "RSqB",// 5d - "Xor",// 5e - "Unds",// 5f - "BQuo",// 60 - "a",// 61 - "b",// 62 - "c",// 63 - "d",// 64 - "e",// 65 - "f",// 66 - "g",// 67 - "h",// 68 - "i",// 69 - "j",// 6a - "k",// 6b - "l",// 6c - "m",// 6d - "n",// 6e - "o",// 6f - "p",// 70 - "q",// 71 - "r",// 72 - "s",// 73 - "t",// 74 - "u",// 75 - "v",// 76 - "w",// 77 - "x",// 78 - "y",// 79 - "z",// 7a - "LBra",// 7b - "Pipe",// 7c - "RBra",// 7d - "Tild",// 7e - null// 7f - }; - private static final Map asciiReadableEncodingDefaultMap = new HashMap<>(); - static { - for (int i = 0; i < asciiReadableEncodingDefault.length; i++) - if (asciiReadableEncodingDefault[i] != null && asciiReadableEncodingDefault[i].length() > 1) - asciiReadableEncodingDefaultMap.put(asciiReadableEncodingDefault[i], (char) i); - } - - /** - * Encode special characters depending on context. - * @param asciiReadableEncodingTable Override the default `asciiReadableEncodingDefault` depending on language requirements - * @param identChar which characters to replace - */ - public static void encodeStringToAlphanumeric(StringBuilder sb, String name, String[] asciiReadableEncodingTable, Pattern identChar, String escapeChar) { - boolean inIdent = true; - for (int i = 0; i < name.length(); i++) { - if (identChar.matcher(name).region(i, name.length()).lookingAt()) { - if (!inIdent) { - inIdent = true; - sb.append(escapeChar); - } - sb.append(name.charAt(i)); - } else { - if (inIdent) { - inIdent = false; - sb.append(escapeChar); - } - int charAt = name.charAt(i); - if (charAt < 128 && asciiReadableEncodingTable[charAt] != null) { - sb.append(asciiReadableEncodingTable[charAt]); - } else { - sb.append(String.format("%04x", charAt)); - } - } - } + public static String getDirectionalityCode(byte cat) { + switch (cat) { + case Character.DIRECTIONALITY_ARABIC_NUMBER: + return "AN"; + case Character.DIRECTIONALITY_BOUNDARY_NEUTRAL: + return "BN"; + case Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR: + return "CS"; + case Character.DIRECTIONALITY_EUROPEAN_NUMBER: + return "EN"; + case Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR: + return "ES"; + case Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR: + return "ET"; + case Character.DIRECTIONALITY_LEFT_TO_RIGHT: + return "L"; + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING: + return "LRE"; + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE: + return "LRO"; + case Character.DIRECTIONALITY_NONSPACING_MARK: + return "NSM"; + case Character.DIRECTIONALITY_OTHER_NEUTRALS: + return "ON"; + case Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR: + return "B"; + case Character.DIRECTIONALITY_POP_DIRECTIONAL_FORMAT: + return "PDF"; + case Character.DIRECTIONALITY_RIGHT_TO_LEFT: + return "R"; + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: + return "AL"; + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING: + return "RLE"; + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE: + return "RLO"; + case Character.DIRECTIONALITY_SEGMENT_SEPARATOR: + return "S"; + case Character.DIRECTIONALITY_UNDEFINED: + throw new IllegalArgumentException(); + case Character.DIRECTIONALITY_WHITESPACE: + return "WS"; + default: + assert false : "should be exhaustive list of directionalities"; + return null; // unreachable + } + } + + /** + * split string to lines in a way that no lines will exceed 80 columns NOTE: strings split only at + * whitespace character ' ', if string contains no ' ', it's returned as is + * + * @param str string to split + * @return new string with newlines added + */ + public static String splitLines(String str) { + return splitLines(str, 80); + } + + /** + * split string to lines in a way that no lines will exceed `col` columns NOTE: strings split only + * at whitespace character ' ', if string contains no ' ', it's returned as is + * + * @param str string to split + * @param col rightmost column + * @return new string with newlines added + */ + public static String splitLines(String str, final int col) { + String[] lines = str.split("\n"); + StringBuilder builder = new StringBuilder(); + String nl = ""; + for (String line : lines) { + builder.append(nl); + if (line.length() < col) { + builder.append(line); + } else { + builder.append(splitLine(line, col)); + } + nl = "\n"; + } + return builder.toString(); + } + + private static String splitLine(String str, final int col) { + if (str.length() < col) { + return str; + } + + // keep indentation of long lines (like term ambiguities) + int firstChar = 0; + while (str.charAt(firstChar) == ' ') firstChar++; + // scan from `col` to left + for (int i = col - 1; i > firstChar; i--) { + if (str.charAt(i) == ' ') { + return str.substring(0, i) + "\n" + splitLine(str.substring(i + 1), col); + } + } + + // we reached the beginning of the string and it contains no whitespaces before the `col` + // but it's longer than `col` so we should replace first space after rightmost column + // with a newline to make it shorter + for (int i = col; i < str.length(); i++) { + if (str.charAt(i) == ' ') { + return str.substring(0, i) + "\n" + splitLine(str.substring(i + 1), col); + } + } + + // string has no spaces to split + return str; + } + + /** + * Takes a textual representation of a KLabel using backticks to delimit and returns the string + * representation of the KLabel that it corresponds to + * + *

Used by the KAST parser. + * + * @param str An image of a parser token corresponding to a KLabel in KORE which begins and ends + * with backtick + * @return The string value of the KLabel + */ + public static String unescapeKoreKLabel(String str) { + char delimiter = '`'; + StringBuilder sb = new StringBuilder(); + if (str.charAt(0) != delimiter) { + throw new IllegalArgumentException( + "Expected to find " + delimiter + " at the beginning of string: " + str); + } + if (str.charAt(str.length() - 1) != delimiter) { + throw new IllegalArgumentException( + "Expected to find " + delimiter + " at the end of string: " + str); + } + for (int i = 1; i < str.length() - 1; i++) { + if (str.charAt(i) == 0x7F || str.charAt(i) < 32) + throw new IllegalArgumentException("Special characters not supported here:" + str); + if (str.charAt(i) == '\\') { + if (str.charAt(i + 1) == '\\') sb.append('\\'); + else if (str.charAt(i + 1) == delimiter) sb.append(delimiter); + i++; + } else sb.append(str.charAt(i)); + } + + return sb.toString(); + } + + /** + * Takes the value of a KLabel and returns a string representation, delimited with backticks, of + * the syntax of that KLabel in KORE. + * + *

Used by the KAST pretty printer. + * + * @param str A string value corresponding to a KLabel. + * @return A string which can be parsed back by a KORE parser to reach the original KLabel. + */ + public static String escapeKoreKLabel(String value) { + char delimiter = '`'; + final int length = value.length(); + StringBuilder result = new StringBuilder(); + result.append(delimiter); + for (int offset = 0, codepoint; offset < length; offset += Character.charCount(codepoint)) { + codepoint = value.codePointAt(offset); + if (codepoint == 0x7F || codepoint < 32) { + throw new IllegalArgumentException("Special characters not supported here:" + value); + } else if (codepoint == delimiter) { + result.append("\\" + delimiter); + } else if (codepoint == '\\') { + result.append("\\\\"); + } else { + result.appendCodePoint(codepoint); + } + } + result.append(delimiter); + return result.toString(); + } + + public static String[] asciiReadableEncodingDefault = + new String[] { + null, // 00 + null, // 01 + null, // 02 + null, // 03 + null, // 04 + null, // 05 + null, // 06 + null, // 07 + null, // 08 + null, // 09 + null, // 0a + null, // 0b + null, // 0c + null, // 0d + null, // 0e + null, // 0f + null, // 10 + null, // 11 + null, // 12 + null, // 13 + null, // 14 + null, // 15 + null, // 16 + null, // 17 + null, // 18 + null, // 19 + null, // 1a + null, // 1b + null, // 1c + null, // 1d + null, // 1e + null, // 1f + "Spce", // 20 + "Bang", // 21 + "Quot", // 22 + "Hash", // 23 + "Dolr", // 24 + "Perc", // 25 + "And", // 26 + "Apos", // 27 + "LPar", // 28 + "RPar", // 29 + "Star", // 2a + "Plus", // 2b + "Comm", // 2c + "-", // 2d + "Stop", // 2e + "Slsh", // 2f + "0", // 30 + "1", // 31 + "2", // 32 + "3", // 33 + "4", // 34 + "5", // 35 + "6", // 36 + "7", // 37 + "8", // 38 + "9", // 39 + "Coln", // 3a + "SCln", // 3b + "LT", // 3c + "Eqls", // 3d + "GT", // 3e + "Ques", // 3f + "AT", // 40 + "A", // 41 + "B", // 42 + "C", // 43 + "D", // 44 + "E", // 45 + "F", // 46 + "G", // 47 + "H", // 48 + "I", // 49 + "J", // 4a + "K", // 4b + "L", // 4c + "M", // 4d + "N", // 4e + "O", // 4f + "P", // 50 + "Q", // 51 + "R", // 52 + "S", // 53 + "T", // 54 + "U", // 55 + "V", // 56 + "W", // 57 + "X", // 58 + "Y", // 59 + "Z", // 5a + "LSqB", // 5b + "Bash", // 5c + "RSqB", // 5d + "Xor", // 5e + "Unds", // 5f + "BQuo", // 60 + "a", // 61 + "b", // 62 + "c", // 63 + "d", // 64 + "e", // 65 + "f", // 66 + "g", // 67 + "h", // 68 + "i", // 69 + "j", // 6a + "k", // 6b + "l", // 6c + "m", // 6d + "n", // 6e + "o", // 6f + "p", // 70 + "q", // 71 + "r", // 72 + "s", // 73 + "t", // 74 + "u", // 75 + "v", // 76 + "w", // 77 + "x", // 78 + "y", // 79 + "z", // 7a + "LBra", // 7b + "Pipe", // 7c + "RBra", // 7d + "Tild", // 7e + null // 7f + }; + private static final Map asciiReadableEncodingDefaultMap = new HashMap<>(); + + static { + for (int i = 0; i < asciiReadableEncodingDefault.length; i++) + if (asciiReadableEncodingDefault[i] != null && asciiReadableEncodingDefault[i].length() > 1) + asciiReadableEncodingDefaultMap.put(asciiReadableEncodingDefault[i], (char) i); + } + + /** + * Encode special characters depending on context. + * + * @param asciiReadableEncodingTable Override the default `asciiReadableEncodingDefault` depending + * on language requirements + * @param identChar which characters to replace + */ + public static void encodeStringToAlphanumeric( + StringBuilder sb, + String name, + String[] asciiReadableEncodingTable, + Pattern identChar, + String escapeChar) { + boolean inIdent = true; + for (int i = 0; i < name.length(); i++) { + if (identChar.matcher(name).region(i, name.length()).lookingAt()) { if (!inIdent) { - sb.append("'"); - } + inIdent = true; + sb.append(escapeChar); + } + sb.append(name.charAt(i)); + } else { + if (inIdent) { + inIdent = false; + sb.append(escapeChar); + } + int charAt = name.charAt(i); + if (charAt < 128 && asciiReadableEncodingTable[charAt] != null) { + sb.append(asciiReadableEncodingTable[charAt]); + } else { + sb.append(String.format("%04x", charAt)); + } + } } + if (!inIdent) { + sb.append("'"); + } + } - public static String decodeKoreString(String encoded) { - boolean quotedState = false; - StringBuilder resultedEncoding = new StringBuilder(); - for (int i = 0; i < encoded.length(); i++) { - if (quotedState) { - if (encoded.charAt(i) == '\'') { - quotedState = false; - } else { - resultedEncoding.append(asciiReadableEncodingDefaultMap.get(encoded.substring(i, i + 4))); - i += 3; - } - } else { - if (encoded.charAt(i) == '\'') { - quotedState = true; - } else - resultedEncoding.append(encoded.charAt(i)); - } - } - return resultedEncoding.toString(); + public static String decodeKoreString(String encoded) { + boolean quotedState = false; + StringBuilder resultedEncoding = new StringBuilder(); + for (int i = 0; i < encoded.length(); i++) { + if (quotedState) { + if (encoded.charAt(i) == '\'') { + quotedState = false; + } else { + resultedEncoding.append(asciiReadableEncodingDefaultMap.get(encoded.substring(i, i + 4))); + i += 3; + } + } else { + if (encoded.charAt(i) == '\'') { + quotedState = true; + } else resultedEncoding.append(encoded.charAt(i)); + } } + return resultedEncoding.toString(); + } - public static String[] splitOneDimensionalAtt(String att) { - String[] splitted = att.trim().split(","); - for (int i = 0; i < splitted.length; i++) { - splitted[i] = splitted[i].trim(); - } - return splitted; + public static String[] splitOneDimensionalAtt(String att) { + String[] splitted = att.trim().split(","); + for (int i = 0; i < splitted.length; i++) { + splitted[i] = splitted[i].trim(); } + return splitted; + } - public static String[][] splitTwoDimensionalAtt(String att) { - String[] parts = att.trim().split(";"); - String[][] splitted = new String[parts.length][]; - for (int i = 0; i < parts.length; i++) { - String[] subparts = splitOneDimensionalAtt(parts[i]); - splitted[i] = subparts; - } - return splitted; + public static String[][] splitTwoDimensionalAtt(String att) { + String[] parts = att.trim().split(";"); + String[][] splitted = new String[parts.length][]; + for (int i = 0; i < parts.length; i++) { + String[] subparts = splitOneDimensionalAtt(parts[i]); + splitted[i] = subparts; } + return splitted; + } } diff --git a/kore/src/main/java/org/kframework/utils/errorsystem/KEMException.java b/kore/src/main/java/org/kframework/utils/errorsystem/KEMException.java index db2ee4a320b..fcb85e7cd25 100644 --- a/kore/src/main/java/org/kframework/utils/errorsystem/KEMException.java +++ b/kore/src/main/java/org/kframework/utils/errorsystem/KEMException.java @@ -1,6 +1,7 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils.errorsystem; +import java.util.Objects; import org.kframework.attributes.HasLocation; import org.kframework.attributes.Location; import org.kframework.attributes.Source; @@ -8,161 +9,231 @@ import org.kframework.utils.errorsystem.KException.ExceptionType; import org.kframework.utils.errorsystem.KException.KExceptionGroup; -import java.util.Objects; -import java.util.Optional; - /** * Thrown to indicate that the K Exception manager has terminated the application due to an error. * * @author dwightguth */ public class KEMException extends RuntimeException { - public static final int TERMINATED_WITH_ERRORS_EXIT_CODE = 113; - - public final KException exception; - - KEMException(KException e) { - super(e.toString(), e.getException()); - this.exception = e; - } - - KEMException(KException e, ExceptionType type) { - super(e.toString(), e.getException()); - this.exception = new KException(type, e.exceptionGroup, e.getMessage(), e.getSource(), e.getLocation(), e.getException()); - } - - public static KEMException debuggerError(String message) { - return create(ExceptionType.ERROR, KExceptionGroup.DEBUGGER, message, null, null, null); - } - - public static KEMException criticalError(String message) { - return create(ExceptionType.ERROR, KExceptionGroup.CRITICAL, message, null, null, null); - } - - public static KEMException criticalError(String message, Throwable e) { - return create(ExceptionType.ERROR, KExceptionGroup.CRITICAL, message, e, null, null); - } - - public static KEMException criticalError(String message, HasLocation node) { - return create(ExceptionType.ERROR, KExceptionGroup.CRITICAL, message, null, node.location().orElse(null), node.source().orElse(null)); - } - - public static KEMException criticalError(String message, Throwable e, HasLocation node) { - return create(ExceptionType.ERROR, KExceptionGroup.CRITICAL, message, e, node.location().orElse(null), node.source().orElse(null)); - } - - - public static KEMException criticalError(String message, Throwable e, Location loc, Source source) { - return create(ExceptionType.ERROR, KExceptionGroup.CRITICAL, message, e, loc, source); - } - - public static KEMException internalError(String message) { - return create(ExceptionType.ERROR, KExceptionGroup.INTERNAL, message, null, null, null); - } - - public static KEMException internalError(String message, Throwable e) { - return create(ExceptionType.ERROR, KExceptionGroup.INTERNAL, message, e, null, null); - } - - public static KEMException internalError(String message, HasLocation node) { - return create(ExceptionType.ERROR, KExceptionGroup.INTERNAL, message, null, node.location().orElse(null), node.source().orElse(null)); - } - - public static KEMException internalError(String message, Throwable e, HasLocation node) { - return create(ExceptionType.ERROR, KExceptionGroup.INTERNAL, message, e, node.location().orElse(null), node.source().orElse(null)); - } - - public static KEMException compilerError(String message) { - return create(ExceptionType.ERROR, KExceptionGroup.COMPILER, message, null, null, null); - } - - public static KEMException compilerError(String message, Throwable e) { - return create(ExceptionType.ERROR, KExceptionGroup.COMPILER, message, e, null, null); - } - - public static KEMException compilerError(String message, HasLocation node) { - return create(ExceptionType.ERROR, KExceptionGroup.COMPILER, message, null, node.location().orElse(null), node.source().orElse(null)); - } - - public static KEMException compilerError(String message, Throwable e, HasLocation node) { - return create(ExceptionType.ERROR, KExceptionGroup.COMPILER, message, e, node.location().orElse(null), node.source().orElse(null)); - } - - public static KEMException innerParserError(String message) { - return create(ExceptionType.ERROR, KExceptionGroup.INNER_PARSER, message, null, null, null); - } - - public static KEMException innerParserError(String message, Source source, Location location) { - return create(ExceptionType.ERROR, KExceptionGroup.INNER_PARSER, message, null, location, source); - } - - public static KEMException innerParserError(String message, Term t) { - return create(ExceptionType.ERROR, KExceptionGroup.INNER_PARSER, message, null, t.location().orElse(null), t.source().orElse(null)); - } - - public static KEMException innerParserError(String message, Throwable e, Source source, Location location) { - return create(ExceptionType.ERROR, KExceptionGroup.INNER_PARSER, message, e, location, source); - } - - public static KEMException outerParserError(String message) { - return create(ExceptionType.ERROR, KExceptionGroup.OUTER_PARSER, message, null, null, null); - } - - public static KEMException outerParserError(String message, Source source, Location location) { - return create(ExceptionType.ERROR, KExceptionGroup.OUTER_PARSER, message, null, location, source); - } - - public static KEMException outerParserError(String message, Throwable e, Source source, Location location) { - return create(ExceptionType.ERROR, KExceptionGroup.OUTER_PARSER, message, e, location, source); - } - - public static KEMException outerParserError(String message, Throwable e, Source source, Location location, boolean printException) { - return create(ExceptionType.ERROR, KExceptionGroup.OUTER_PARSER, message, e, location, source, printException); - } - - public static KEMException asError(KEMException warning) { - return new KEMException(warning.exception, ExceptionType.ERROR); - } - - public KEMException withLocation(Location loc, Source source) { - return create(this.exception.getType(), - exception.getExceptionGroup(), - exception.getMessage(), - exception.getException(), - loc, - source); - } - - @Override - public String getMessage() { - return exception.toString(); - } - - private static KEMException create(ExceptionType type, KExceptionGroup group, String message, - Throwable e, Location location, Source source) { - return new KEMException(new KException(type, group, message, source, location, e)); - } - - private static KEMException create(ExceptionType type, KExceptionGroup group, String message, - Throwable e, Location location, Source source, boolean printException) { - return new KEMException(new KException(type, group, message, source, location, e, printException)); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - KEMException that = (KEMException) o; - return Objects.equals(exception, that.exception); - } - - @Override - public int hashCode() { - return Objects.hash(exception); - } - - public KException getKException() { - return exception; - } - + public static final int TERMINATED_WITH_ERRORS_EXIT_CODE = 113; + + public final KException exception; + + KEMException(KException e) { + super(e.toString(), e.getException()); + this.exception = e; + } + + KEMException(KException e, ExceptionType type) { + super(e.toString(), e.getException()); + this.exception = + new KException( + type, + e.exceptionGroup, + e.getMessage(), + e.getSource(), + e.getLocation(), + e.getException()); + } + + public static KEMException debuggerError(String message) { + return create(ExceptionType.ERROR, KExceptionGroup.DEBUGGER, message, null, null, null); + } + + public static KEMException criticalError(String message) { + return create(ExceptionType.ERROR, KExceptionGroup.CRITICAL, message, null, null, null); + } + + public static KEMException criticalError(String message, Throwable e) { + return create(ExceptionType.ERROR, KExceptionGroup.CRITICAL, message, e, null, null); + } + + public static KEMException criticalError(String message, HasLocation node) { + return create( + ExceptionType.ERROR, + KExceptionGroup.CRITICAL, + message, + null, + node.location().orElse(null), + node.source().orElse(null)); + } + + public static KEMException criticalError(String message, Throwable e, HasLocation node) { + return create( + ExceptionType.ERROR, + KExceptionGroup.CRITICAL, + message, + e, + node.location().orElse(null), + node.source().orElse(null)); + } + + public static KEMException criticalError( + String message, Throwable e, Location loc, Source source) { + return create(ExceptionType.ERROR, KExceptionGroup.CRITICAL, message, e, loc, source); + } + + public static KEMException internalError(String message) { + return create(ExceptionType.ERROR, KExceptionGroup.INTERNAL, message, null, null, null); + } + + public static KEMException internalError(String message, Throwable e) { + return create(ExceptionType.ERROR, KExceptionGroup.INTERNAL, message, e, null, null); + } + + public static KEMException internalError(String message, HasLocation node) { + return create( + ExceptionType.ERROR, + KExceptionGroup.INTERNAL, + message, + null, + node.location().orElse(null), + node.source().orElse(null)); + } + + public static KEMException internalError(String message, Throwable e, HasLocation node) { + return create( + ExceptionType.ERROR, + KExceptionGroup.INTERNAL, + message, + e, + node.location().orElse(null), + node.source().orElse(null)); + } + + public static KEMException compilerError(String message) { + return create(ExceptionType.ERROR, KExceptionGroup.COMPILER, message, null, null, null); + } + + public static KEMException compilerError(String message, Throwable e) { + return create(ExceptionType.ERROR, KExceptionGroup.COMPILER, message, e, null, null); + } + + public static KEMException compilerError(String message, HasLocation node) { + return create( + ExceptionType.ERROR, + KExceptionGroup.COMPILER, + message, + null, + node.location().orElse(null), + node.source().orElse(null)); + } + + public static KEMException compilerError(String message, Throwable e, HasLocation node) { + return create( + ExceptionType.ERROR, + KExceptionGroup.COMPILER, + message, + e, + node.location().orElse(null), + node.source().orElse(null)); + } + + public static KEMException innerParserError(String message) { + return create(ExceptionType.ERROR, KExceptionGroup.INNER_PARSER, message, null, null, null); + } + + public static KEMException innerParserError(String message, Source source, Location location) { + return create( + ExceptionType.ERROR, KExceptionGroup.INNER_PARSER, message, null, location, source); + } + + public static KEMException innerParserError(String message, Term t) { + return create( + ExceptionType.ERROR, + KExceptionGroup.INNER_PARSER, + message, + null, + t.location().orElse(null), + t.source().orElse(null)); + } + + public static KEMException innerParserError( + String message, Throwable e, Source source, Location location) { + return create(ExceptionType.ERROR, KExceptionGroup.INNER_PARSER, message, e, location, source); + } + + public static KEMException outerParserError(String message) { + return create(ExceptionType.ERROR, KExceptionGroup.OUTER_PARSER, message, null, null, null); + } + + public static KEMException outerParserError(String message, Source source, Location location) { + return create( + ExceptionType.ERROR, KExceptionGroup.OUTER_PARSER, message, null, location, source); + } + + public static KEMException outerParserError( + String message, Throwable e, Source source, Location location) { + return create(ExceptionType.ERROR, KExceptionGroup.OUTER_PARSER, message, e, location, source); + } + + public static KEMException outerParserError( + String message, Throwable e, Source source, Location location, boolean printException) { + return create( + ExceptionType.ERROR, + KExceptionGroup.OUTER_PARSER, + message, + e, + location, + source, + printException); + } + + public static KEMException asError(KEMException warning) { + return new KEMException(warning.exception, ExceptionType.ERROR); + } + + public KEMException withLocation(Location loc, Source source) { + return create( + this.exception.getType(), + exception.getExceptionGroup(), + exception.getMessage(), + exception.getException(), + loc, + source); + } + + @Override + public String getMessage() { + return exception.toString(); + } + + private static KEMException create( + ExceptionType type, + KExceptionGroup group, + String message, + Throwable e, + Location location, + Source source) { + return new KEMException(new KException(type, group, message, source, location, e)); + } + + private static KEMException create( + ExceptionType type, + KExceptionGroup group, + String message, + Throwable e, + Location location, + Source source, + boolean printException) { + return new KEMException( + new KException(type, group, message, source, location, e, printException)); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + KEMException that = (KEMException) o; + return Objects.equals(exception, that.exception); + } + + @Override + public int hashCode() { + return Objects.hash(exception); + } + + public KException getKException() { + return exception; + } } diff --git a/kore/src/main/java/org/kframework/utils/errorsystem/KException.java b/kore/src/main/java/org/kframework/utils/errorsystem/KException.java index b36dd587828..7bc0a208eed 100755 --- a/kore/src/main/java/org/kframework/utils/errorsystem/KException.java +++ b/kore/src/main/java/org/kframework/utils/errorsystem/KException.java @@ -1,305 +1,333 @@ // Copyright (c) K Team. All Rights Reserved. package org.kframework.utils.errorsystem; -import org.apache.commons.lang3.StringUtils; -import org.kframework.attributes.HasLocation; -import org.kframework.attributes.Location; -import org.kframework.attributes.Source; - import java.io.Serializable; import java.nio.file.Files; import java.nio.file.Paths; import java.util.*; import java.util.stream.Stream; +import org.apache.commons.lang3.StringUtils; +import org.kframework.attributes.HasLocation; +import org.kframework.attributes.Location; +import org.kframework.attributes.Source; public class KException implements Serializable, HasLocation { - protected final ExceptionType type; - final KExceptionGroup exceptionGroup; - private final Source source; - private final Location location; - private final String message; - private final Throwable exception; - private final boolean printException; - private final String sourceText; - private final StringBuilder trace = new StringBuilder(); - - private static final Map labels; - static { - labels = new HashMap(); - labels.put(KExceptionGroup.COMPILER, "Compiler"); - labels.put(KExceptionGroup.OUTER_PARSER, "Outer Parser"); - labels.put(KExceptionGroup.INNER_PARSER, "Inner Parser"); - labels.put(KExceptionGroup.LISTS, "Lists"); - labels.put(KExceptionGroup.INTERNAL, "Internal"); - labels.put(KExceptionGroup.CRITICAL, "Critical"); - labels.put(KExceptionGroup.DEBUGGER, "Debugger"); - labels.put(KExceptionGroup.PROVER, "Prover"); + protected final ExceptionType type; + final KExceptionGroup exceptionGroup; + private final Source source; + private final Location location; + private final String message; + private final Throwable exception; + private final boolean printException; + private final String sourceText; + private final StringBuilder trace = new StringBuilder(); + + private static final Map labels; + + static { + labels = new HashMap(); + labels.put(KExceptionGroup.COMPILER, "Compiler"); + labels.put(KExceptionGroup.OUTER_PARSER, "Outer Parser"); + labels.put(KExceptionGroup.INNER_PARSER, "Inner Parser"); + labels.put(KExceptionGroup.LISTS, "Lists"); + labels.put(KExceptionGroup.INTERNAL, "Internal"); + labels.put(KExceptionGroup.CRITICAL, "Critical"); + labels.put(KExceptionGroup.DEBUGGER, "Debugger"); + labels.put(KExceptionGroup.PROVER, "Prover"); + } + + public static KException criticalError(String message) { + return new KException(ExceptionType.ERROR, KExceptionGroup.CRITICAL, message); + } + + public KException(ExceptionType type, KExceptionGroup label, String message) { + this(type, label, message, null, null, null); + } + + public KException(ExceptionType type, KExceptionGroup label, String message, Throwable e) { + this(type, label, message, null, null, e); + } + + public KException( + ExceptionType type, KExceptionGroup label, String message, Source source, Location location) { + this(type, label, message, source, location, null, true); + } + + public KException( + ExceptionType type, + KExceptionGroup label, + String message, + Source source, + Location location, + Throwable exception) { + this(type, label, message, source, location, exception, true); + } + + public KException( + ExceptionType type, + KExceptionGroup label, + String message, + Source source, + Location location, + Throwable exception, + boolean printException) { + super(); + this.type = type; + this.exceptionGroup = label; + this.message = message; + this.source = source; + this.location = location; + this.exception = exception; + this.sourceText = getSourceLineText(); + this.printException = printException; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + KException that = (KException) o; + return type == that.type + && exceptionGroup == that.exceptionGroup + && Objects.equals(source, that.source) + && Objects.equals(location, that.location) + && Objects.equals(message, that.message) + && Objects.equals(exception, that.exception) + && Objects.equals(trace.toString(), that.trace.toString()); + } + + @Override + public int hashCode() { + return Objects.hash( + type, exceptionGroup, source, location, message, exception, trace.toString()); + } + + @Override + public Optional location() { + return Optional.ofNullable(location); + } + + @Override + public Optional source() { + return Optional.ofNullable(source); + } + + public enum KExceptionGroup { + OUTER_PARSER, + INNER_PARSER, + COMPILER, + LISTS, + INTERNAL, + CRITICAL, + DEBUGGER, + PROVER + } + + public enum ExceptionType { + ERROR, + NON_EXHAUSTIVE_MATCH, + UNDELETED_TEMP_DIR, + MISSING_SYNTAX_MODULE, + INVALID_EXIT_CODE, + INVALID_CONFIG_VAR, + INVALID_ASSOCIATIVITY, + FUTURE_ERROR, + UNUSED_VAR, + PROOF_LINT, + NON_LR_GRAMMAR, + IGNORED_ATTRIBUTE, + REMOVED_ANYWHERE, + DEPRECATED_DIRECTORY_FLAG, + MISSING_HOOK, + FIRST_HIDDEN, // warnings below here are hidden by default + USELESS_RULE, + UNRESOLVED_FUNCTION_SYMBOL, + MALFORMED_MARKDOWN, + INVALIDATED_CACHE, + UNUSED_SYMBOL, + } + + @Override + public String toString() { + return toString(false); + } + + public String toString(boolean verbose) { + return "[" + + (type == ExceptionType.ERROR ? "Error" : "Warning") + + "] " + + labels.get(exceptionGroup) + + ": " + + message + + (exception != null && printException + ? " (" + exception.getClass().getSimpleName() + ": " + exception.getMessage() + ")" + : "") + + trace.toString() + + traceTail() + + (source == null ? "" : "\n\t" + source) + + (location == null ? "" : "\n\t" + location) + + (sourceText == null ? "" : sourceText); + } + + public String getMessage() { + return message; + } + + public Throwable getException() { + return exception; + } + + public ExceptionType getType() { + return type; + } + + private String traceTail() { + if (identicalFrames > 1) { + return " * " + identicalFrames; } - - public static KException criticalError(String message) { - return new KException(ExceptionType.ERROR, KExceptionGroup.CRITICAL, message); + return ""; + } + + private int frames = 0; + private int identicalFrames = 1; + private CharSequence lastFrame; + + public void addTraceFrame(CharSequence frame) { + if (frames < 1024) { + if (frame.equals(lastFrame)) { + identicalFrames++; + } else { + if (identicalFrames > 1) { + trace.append(" * ").append(identicalFrames); + identicalFrames = 1; + } + trace.append("\n ").append(frame); + lastFrame = frame; + frames++; + } } + } - public KException(ExceptionType type, KExceptionGroup label, String message) { - this(type, label, message, null, null, null); - } + public void formatTraceFrame(String format, Object... args) { + StringBuilder sb = new StringBuilder(); + new Formatter(sb).format(format, args); + addTraceFrame(sb); + } - public KException(ExceptionType type, KExceptionGroup label, String message, Throwable e) { - this(type, label, message, null, null, e); - } + private boolean locationIsValid() { + return (location != null && location.startLine() > 0 && location.endLine() > 0); + } - public KException(ExceptionType type, KExceptionGroup label, String message, Source source, Location location) { - this(type, label, message, source, location, null, true); + private String getSourceLineText() { + if (!locationIsValid()) { + return null; } - - public KException(ExceptionType type, KExceptionGroup label, String message, Source source, Location location, Throwable exception) { - this(type, label, message, source, location, exception, true); + try { + String sourceLineText; + int errorLineCount = location.endLine() - location.startLine() + 1; + + if (errorLineCount == 1) { + sourceLineText = getSourceLine(); + } else if (errorLineCount > 1) { + // generate line info for multiple lines + sourceLineText = getSourceLine(errorLineCount); + } else { + sourceLineText = null; + } + return sourceLineText; + } catch (java.io.IOException e) { + return null; } + } - public KException( - ExceptionType type, - KExceptionGroup label, - String message, - Source source, - Location location, - Throwable exception, - boolean printException) { - super(); - this.type = type; - this.exceptionGroup = label; - this.message = message; - this.source = source; - this.location = location; - this.exception = exception; - this.sourceText = getSourceLineText(); - this.printException = printException; - } + private String getSourceLine() throws java.io.IOException { + int lineNumberPadding = String.valueOf(location.startLine()).length(); + StringBuilder sourceText = new StringBuilder(); - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - KException that = (KException) o; - return type == that.type && - exceptionGroup == that.exceptionGroup && - Objects.equals(source, that.source) && - Objects.equals(location, that.location) && - Objects.equals(message, that.message) && - Objects.equals(exception, that.exception) && - Objects.equals(trace.toString(), that.trace.toString()); - } + sourceText.append("\n\t"); + sourceText.append(location.startLine() + " |\t"); + Stream lines = Files.lines(Paths.get(getSource().source())); + sourceText.append((String) lines.skip(location.startLine() - 1).findFirst().orElse("")); - @Override - public int hashCode() { - return Objects.hash(type, exceptionGroup, source, location, message, exception, trace.toString()); - } + /* generate a line below the source file that underlines the location of the error */ - @Override - public Optional location() { - return Optional.ofNullable(location); + sourceText.append("\n\t" + StringUtils.repeat(' ', lineNumberPadding) + " .\t"); + sourceText.append(StringUtils.repeat(' ', location.startColumn() - 1)); + sourceText.append('^'); + if (location.endColumn() > location.startColumn()) { + sourceText.append(StringUtils.repeat('~', location.endColumn() - location.startColumn() - 1)); } - @Override - public Optional source() { - return Optional.ofNullable(source); - } + return sourceText.toString(); + } - public enum KExceptionGroup { - OUTER_PARSER, INNER_PARSER, COMPILER, LISTS, INTERNAL, CRITICAL, DEBUGGER, PROVER - } + private String getSourceLine(int errorLineCount) throws java.io.IOException { - public enum ExceptionType { - ERROR, - NON_EXHAUSTIVE_MATCH, - UNDELETED_TEMP_DIR, - MISSING_SYNTAX_MODULE, - INVALID_EXIT_CODE, - INVALID_CONFIG_VAR, - INVALID_ASSOCIATIVITY, - FUTURE_ERROR, - UNUSED_VAR, - PROOF_LINT, - NON_LR_GRAMMAR, - IGNORED_ATTRIBUTE, - REMOVED_ANYWHERE, - DEPRECATED_DIRECTORY_FLAG, - MISSING_HOOK, - FIRST_HIDDEN, // warnings below here are hidden by default - USELESS_RULE, - UNRESOLVED_FUNCTION_SYMBOL, - MALFORMED_MARKDOWN, - INVALIDATED_CACHE, - UNUSED_SYMBOL, - } + /* The line number padding is based on the endline because this is the largest number of padding needed */ - @Override - public String toString() { - return toString(false); - } + int lineNumberPadding = String.valueOf(location.endLine()).length(); + StringBuilder sourceText = new StringBuilder(); - public String toString(boolean verbose) { - return "[" + (type == ExceptionType.ERROR ? "Error" : "Warning") + "] " + labels.get(exceptionGroup) + ": " + message - + (exception != null && printException ? " (" + exception.getClass().getSimpleName() + ": " + exception.getMessage() + ")" : "") - + trace.toString() + traceTail() - + (source == null ? "" : "\n\t" + source) - + (location == null ? "" : "\n\t" + location) - + (sourceText == null ? "" : sourceText); - } + /* generate a line above the source file that indicates the location of the error */ - public String getMessage() { - return message; - } + sourceText.append("\n\t" + StringUtils.repeat(' ', lineNumberPadding) + " .\t"); + sourceText.append(StringUtils.repeat(' ', location.startColumn() - 1)); + sourceText.append('v'); - public Throwable getException() { - return exception; - } + Stream lines = Files.lines(Paths.get(getSource().source())); + String firstLine = (String) lines.skip(location.startLine() - 1).findFirst().get(); - public ExceptionType getType() { - return type; + if (firstLine.length() - location.startColumn() > 0) { + sourceText.append(StringUtils.repeat('~', firstLine.length() - location.startColumn())); } - - private String traceTail() { - if (identicalFrames > 1) { - return " * " + identicalFrames; - } - return ""; + sourceText.append("\n\t"); + int padding = + String.valueOf(location.endLine()).length() - String.valueOf(location.startLine()).length(); + sourceText.append(StringUtils.repeat(' ', padding) + location.startLine() + " |\t"); + sourceText.append(firstLine); + + if (errorLineCount == 3) { + sourceText.append("\n\t"); + int padding2 = + String.valueOf(location.endLine()).length() + - String.valueOf(location.startLine() + 1).length(); + sourceText.append(StringUtils.repeat(' ', padding2) + (location.startLine() + 1) + " |\t"); + Stream secondline = Files.lines(Paths.get(getSource().source())); + sourceText.append((String) secondline.skip(location.startLine()).findFirst().get()); + } else if (errorLineCount > 3) { + /* + * If the error message spans more than 3 lines, indicate this line with "...". + * For errors that span many lines, this is sufficient to get the point across. + */ + sourceText.append("\n\t" + StringUtils.repeat(' ', lineNumberPadding) + " |\t\t..."); } - private int frames = 0; - private int identicalFrames = 1; - private CharSequence lastFrame; - public void addTraceFrame(CharSequence frame) { - if (frames < 1024) { - if (frame.equals(lastFrame)) { - identicalFrames++; - } else { - if (identicalFrames > 1) { - trace.append(" * ").append(identicalFrames); - identicalFrames = 1; - } - trace.append("\n ").append(frame); - lastFrame = frame; - frames++; - } - } - } + sourceText.append("\n\t"); + sourceText.append(location.endLine() + " |\t"); + String lastLine = + Files.lines(Paths.get(getSource().source())).skip(location.endLine() - 1).findFirst().get(); + int firstCharIndex = lastLine.indexOf(lastLine.trim()); + sourceText.append(lastLine); - public void formatTraceFrame(String format, Object... args) { - StringBuilder sb = new StringBuilder(); - new Formatter(sb).format(format, args); - addTraceFrame(sb); - } - - private boolean locationIsValid() { - return (location != null && location.startLine() > 0 && location.endLine() > 0); - } - - private String getSourceLineText() { - if (!locationIsValid()) { - return null; - } - try { - String sourceLineText; - int errorLineCount = location.endLine() - location.startLine() + 1; - - if (errorLineCount == 1) { - sourceLineText = getSourceLine(); - } else if (errorLineCount > 1) { - // generate line info for multiple lines - sourceLineText = getSourceLine(errorLineCount); - } else { - sourceLineText = null; - } - return sourceLineText; - } catch (java.io.IOException e) { - return null; - } - } - - private String getSourceLine() throws java.io.IOException { - int lineNumberPadding = String.valueOf(location.startLine()).length(); - StringBuilder sourceText = new StringBuilder(); - - sourceText.append("\n\t"); - sourceText.append(location.startLine() + " |\t"); - Stream lines = Files.lines(Paths.get(getSource().source())); - sourceText.append((String) lines.skip(location.startLine() - 1).findFirst().orElse("")); - - /* generate a line below the source file that underlines the location of the error */ - - sourceText.append("\n\t" + StringUtils.repeat(' ', lineNumberPadding) + " .\t"); - sourceText.append(StringUtils.repeat(' ', location.startColumn() - 1)); - sourceText.append('^'); - if (location.endColumn() > location.startColumn()) { - sourceText.append(StringUtils.repeat('~', location.endColumn() - location.startColumn() - 1)); - } - - return sourceText.toString(); - } - - private String getSourceLine(int errorLineCount) throws java.io.IOException { + /* generate a line below the source file that underlines the location of the error */ - /* The line number padding is based on the endline because this is the largest number of padding needed */ + sourceText.append("\n\t" + StringUtils.repeat(' ', lineNumberPadding) + " .\t"); + sourceText.append(StringUtils.repeat(' ', firstCharIndex)); + sourceText.append(StringUtils.repeat('~', location.endColumn() - firstCharIndex - 2)); + sourceText.append('^'); - int lineNumberPadding = String.valueOf(location.endLine()).length(); - StringBuilder sourceText = new StringBuilder(); + return sourceText.toString(); + } - /* generate a line above the source file that indicates the location of the error */ + public Source getSource() { + return source; + } - sourceText.append("\n\t" + StringUtils.repeat(' ', lineNumberPadding) + " .\t"); - sourceText.append(StringUtils.repeat(' ', location.startColumn() - 1)); - sourceText.append('v'); + public Location getLocation() { + return location; + } - Stream lines = Files.lines(Paths.get(getSource().source())); - String firstLine = (String) lines.skip(location.startLine() - 1).findFirst().get(); - - if (firstLine.length() - location.startColumn() > 0) { - sourceText.append(StringUtils.repeat('~', firstLine.length() - location.startColumn())); - } - sourceText.append("\n\t"); - int padding = String.valueOf(location.endLine()).length() - String.valueOf(location.startLine()).length(); - sourceText.append(StringUtils.repeat(' ', padding) + location.startLine() + " |\t"); - sourceText.append(firstLine); - - if (errorLineCount == 3) { - sourceText.append("\n\t"); - int padding2 = String.valueOf(location.endLine()).length() - String.valueOf(location.startLine() + 1).length(); - sourceText.append(StringUtils.repeat(' ', padding2) + (location.startLine() + 1) + " |\t"); - Stream secondline = Files.lines(Paths.get(getSource().source())); - sourceText.append((String) secondline.skip(location.startLine()).findFirst().get()); - } else if (errorLineCount > 3) { - /* - * If the error message spans more than 3 lines, indicate this line with "...". - * For errors that span many lines, this is sufficient to get the point across. - */ - sourceText.append("\n\t" + StringUtils.repeat(' ', lineNumberPadding) + " |\t\t..."); - } - - sourceText.append("\n\t"); - sourceText.append(location.endLine() + " |\t"); - String lastLine = Files.lines(Paths.get(getSource().source())).skip(location.endLine() -1).findFirst().get(); - int firstCharIndex = lastLine.indexOf(lastLine.trim()); - sourceText.append(lastLine); - - /* generate a line below the source file that underlines the location of the error */ - - sourceText.append("\n\t" + StringUtils.repeat(' ', lineNumberPadding) + " .\t"); - sourceText.append(StringUtils.repeat(' ', firstCharIndex)); - sourceText.append(StringUtils.repeat('~', location.endColumn() - firstCharIndex - 2)); - sourceText.append('^'); - - return sourceText.toString(); - } - - public Source getSource() { - return source; - } - - public Location getLocation() { - return location; - } - - public KExceptionGroup getExceptionGroup() { - return exceptionGroup; - } + public KExceptionGroup getExceptionGroup() { + return exceptionGroup; + } } diff --git a/kore/src/test/java/org/kframework/CollectionsTest.java b/kore/src/test/java/org/kframework/CollectionsTest.java index 0f6beaf0a08..96a1bdcc39e 100644 --- a/kore/src/test/java/org/kframework/CollectionsTest.java +++ b/kore/src/test/java/org/kframework/CollectionsTest.java @@ -2,51 +2,49 @@ package org.kframework; +import static org.junit.Assert.*; +import static org.kframework.Collections.*; +import static org.kframework.Collections.List; + +import java.util.stream.Stream; import org.junit.Test; import scala.collection.Set; import scala.collection.immutable.List; -import java.util.stream.Stream; - -import static org.junit.Assert.*; -import static org.kframework.Collections.List; -import static org.kframework.Collections.*; - public class CollectionsTest { - @Test - public void testList() { - // creating a List - List aList = List(1, 2, 3); - - // getting a Stream from a list - Stream s = stream(aList); + @Test + public void testList() { + // creating a List + List aList = List(1, 2, 3); - // usual Java 8 manipulation - Stream l = s.map(x -> x.toString()); + // getting a Stream from a list + Stream s = stream(aList); - // and back to an immutable List - List collectedList = l.collect(toList()); + // usual Java 8 manipulation + Stream l = s.map(x -> x.toString()); - // which has the expected value - assertEquals(List("1", "2", "3"), collectedList); - } + // and back to an immutable List + List collectedList = l.collect(toList()); - @Test - public void testSet() { - // creating a Set - Set aList = Set(1, 2, 3); + // which has the expected value + assertEquals(List("1", "2", "3"), collectedList); + } - // getting a Stream from a Set - Stream s = stream(aList); + @Test + public void testSet() { + // creating a Set + Set aList = Set(1, 2, 3); - // usual Java 8 manipulation - Stream l = s.map(x -> x / 2); + // getting a Stream from a Set + Stream s = stream(aList); - // and back to an immutable Set - Set collectedList = l.collect(toSet()); + // usual Java 8 manipulation + Stream l = s.map(x -> x / 2); - // which has the expected value - assertEquals(Set(0, 1), collectedList); - } + // and back to an immutable Set + Set collectedList = l.collect(toSet()); + // which has the expected value + assertEquals(Set(0, 1), collectedList); + } } diff --git a/kore/src/test/java/org/kframework/kore/InterfaceTest.java b/kore/src/test/java/org/kframework/kore/InterfaceTest.java index a36ea1faee9..f1bf71b747f 100644 --- a/kore/src/test/java/org/kframework/kore/InterfaceTest.java +++ b/kore/src/test/java/org/kframework/kore/InterfaceTest.java @@ -2,41 +2,44 @@ package org.kframework.kore; -import org.junit.Test; -import org.kframework.builtin.Sorts; - import static org.junit.Assert.*; -import static org.kframework.kore.KORE.*; import static org.kframework.Collections.*; +import static org.kframework.kore.KORE.*; + +import org.junit.Test; +import org.kframework.builtin.Sorts; public class InterfaceTest { - @Test - public void example() { - // Creating "A + 0 => A" programmatically - - KRewrite k = KRewrite( - KApply(KLabel("_+_"), KVariable("A"), KToken("0", Sort("Int"))), - KVariable("A")); - - // Navigating it - KLabel theLabel = ((KApply) k.left()).klabel(); - theLabel.name(); - } - - @Test - public void kListIsAssociative() { - // assertEquals(KList(KToken(Sorts.Int(), "1"), KToken(Sorts.Int(), "2")), KList(KToken(Sorts.Int(), "1"), KList(KToken(Sorts.Int(), "2")))); - } - - @Test - public void kSeqIsAssociative() { - assertEquals(KSequence(Seq(KToken("1", Sorts.Int()), KToken("2", Sorts.Int()))), KSequence(Seq(KToken("1", Sorts.Int()), KSequence(Seq(KToken("2", Sorts.Int())))))); - } - -// @Test -// public void manipulatingKSeq() { -// KSequence l = stream(KSequence(KToken("1", Sorts.Int()), KToken("2", Sorts.Int()))).map(x -> KToken("3", Sorts.Int())).collect(toKSequence()); -// assertEquals(KSequence(KToken("3", Sorts.Int()), KToken("3", Sorts.Int())), l); -// } + @Test + public void example() { + // Creating "A + 0 => A" programmatically + + KRewrite k = + KRewrite(KApply(KLabel("_+_"), KVariable("A"), KToken("0", Sort("Int"))), KVariable("A")); + + // Navigating it + KLabel theLabel = ((KApply) k.left()).klabel(); + theLabel.name(); + } + + @Test + public void kListIsAssociative() { + // assertEquals(KList(KToken(Sorts.Int(), "1"), KToken(Sorts.Int(), "2")), + // KList(KToken(Sorts.Int(), "1"), KList(KToken(Sorts.Int(), "2")))); + } + + @Test + public void kSeqIsAssociative() { + assertEquals( + KSequence(Seq(KToken("1", Sorts.Int()), KToken("2", Sorts.Int()))), + KSequence(Seq(KToken("1", Sorts.Int()), KSequence(Seq(KToken("2", Sorts.Int())))))); + } + + // @Test + // public void manipulatingKSeq() { + // KSequence l = stream(KSequence(KToken("1", Sorts.Int()), KToken("2", + // Sorts.Int()))).map(x -> KToken("3", Sorts.Int())).collect(toKSequence()); + // assertEquals(KSequence(KToken("3", Sorts.Int()), KToken("3", Sorts.Int())), l); + // } } diff --git a/kore/src/test/java/org/kframework/kore/VisitorTest.java b/kore/src/test/java/org/kframework/kore/VisitorTest.java index f81d062539e..2b35ec29816 100644 --- a/kore/src/test/java/org/kframework/kore/VisitorTest.java +++ b/kore/src/test/java/org/kframework/kore/VisitorTest.java @@ -5,78 +5,76 @@ import static org.junit.Assert.*; import static org.kframework.kore.KORE.*; -import org.junit.Test; - import java.util.List; import java.util.stream.Collectors; +import org.junit.Test; public class VisitorTest { - class FooTransformer extends TransformK { - - @Override - public K apply(KToken k) { - return KVariable("T"); - } - - @Override - public K apply(KVariable k) { - return k; - } - - @Override - public K apply(KSequence k) { - List newItems = k.items().stream().map(this).collect(Collectors.toList()); - return KORE.KSequence(newItems, k.att()); - } - - @Override - public K apply(InjectedKLabel k) { - return k; - } - - } - - @Test - public void testTopLevel() { - FooTransformer fooTransformer = new FooTransformer(); - K t = fooTransformer.apply(KToken("bla", Sort("foo"))); - - assertEquals(KVariable("T"), t); - } + class FooTransformer extends TransformK { - @Test - public void testTopLevelNoTransoformation() { - FooTransformer fooTransformer = new FooTransformer(); - KVariable term = KVariable("X"); - K t = fooTransformer.apply(term); - - assertEquals(term, t); + @Override + public K apply(KToken k) { + return KVariable("T"); } - @Test - public void testGenericK() { - FooTransformer fooTransformer = new FooTransformer(); - K term = KVariable("X"); - K t = fooTransformer.apply(term); - - assertEquals(term, t); + @Override + public K apply(KVariable k) { + return k; } - @Test - public void testTopLevelNoTransoformationOnCollection() { - FooTransformer fooTransformer = new FooTransformer(); - KRewrite term = KRewrite(KVariable("X"), KVariable("Y")); - KRewrite t = (KRewrite) fooTransformer.apply(term); - - assertEquals(term, t); + @Override + public K apply(KSequence k) { + List newItems = k.items().stream().map(this).collect(Collectors.toList()); + return KORE.KSequence(newItems, k.att()); } - @Test - public void testNested() { - FooTransformer fooTransformer = new FooTransformer(); - KRewrite t = (KRewrite) fooTransformer.apply(KRewrite(KToken("bla", Sort("foo")), - KVariable("U"))); - - assertEquals(KRewrite(KVariable("T"), KVariable("U")), t); + @Override + public K apply(InjectedKLabel k) { + return k; } + } + + @Test + public void testTopLevel() { + FooTransformer fooTransformer = new FooTransformer(); + K t = fooTransformer.apply(KToken("bla", Sort("foo"))); + + assertEquals(KVariable("T"), t); + } + + @Test + public void testTopLevelNoTransoformation() { + FooTransformer fooTransformer = new FooTransformer(); + KVariable term = KVariable("X"); + K t = fooTransformer.apply(term); + + assertEquals(term, t); + } + + @Test + public void testGenericK() { + FooTransformer fooTransformer = new FooTransformer(); + K term = KVariable("X"); + K t = fooTransformer.apply(term); + + assertEquals(term, t); + } + + @Test + public void testTopLevelNoTransoformationOnCollection() { + FooTransformer fooTransformer = new FooTransformer(); + KRewrite term = KRewrite(KVariable("X"), KVariable("Y")); + KRewrite t = (KRewrite) fooTransformer.apply(term); + + assertEquals(term, t); + } + + @Test + public void testNested() { + FooTransformer fooTransformer = new FooTransformer(); + KRewrite t = + (KRewrite) fooTransformer.apply(KRewrite(KToken("bla", Sort("foo")), KVariable("U"))); + + assertEquals(KRewrite(KVariable("T"), KVariable("U")), t); + } } diff --git a/llvm-backend/src/main/java/org/kframework/backend/llvm/LLVMBackend.java b/llvm-backend/src/main/java/org/kframework/backend/llvm/LLVMBackend.java index 2e0e4b1c692..3d0ad8fa45f 100644 --- a/llvm-backend/src/main/java/org/kframework/backend/llvm/LLVMBackend.java +++ b/llvm-backend/src/main/java/org/kframework/backend/llvm/LLVMBackend.java @@ -2,179 +2,183 @@ package org.kframework.backend.llvm; import com.google.inject.Inject; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.mutable.MutableInt; import org.kframework.attributes.Att; -import org.kframework.backend.llvm.matching.Matching; import org.kframework.backend.kore.KoreBackend; +import org.kframework.backend.llvm.matching.Matching; import org.kframework.compile.Backend; -import org.kframework.kompile.CompiledDefinition; import org.kframework.kompile.KompileOptions; import org.kframework.main.GlobalOptions; import org.kframework.main.Tool; import org.kframework.utils.Stopwatch; -import org.kframework.utils.errorsystem.KExceptionManager; import org.kframework.utils.errorsystem.KEMException; import org.kframework.utils.errorsystem.KException.ExceptionType; +import org.kframework.utils.errorsystem.KExceptionManager; import org.kframework.utils.file.FileUtil; -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; - public class LLVMBackend extends KoreBackend { - private final GlobalOptions globalOptions; - private final LLVMKompileOptions options; - private final KExceptionManager kem; - private final KompileOptions kompileOptions; - private final Tool tool; - - @Inject - public LLVMBackend( - KompileOptions kompileOptions, - GlobalOptions globalOptions, - FileUtil files, - KExceptionManager kem, - LLVMKompileOptions options, - Tool tool) { - super(kompileOptions, files, kem, tool); - this.globalOptions = globalOptions; - this.options = options; - this.kompileOptions = kompileOptions; - this.kem = kem; - this.tool = tool; - } - - - @Override - public void accept(Backend.Holder h) { - Stopwatch sw = new Stopwatch(globalOptions); - String kore = getKompiledString(h.def, true); - h.def = null; - files.saveToKompiled("definition.kore", kore); - sw.printIntermediate(" Print definition.kore"); - FileUtils.deleteQuietly(files.resolveKompiled("dt")); - MutableInt warnings = new MutableInt(); - boolean optimize = kompileOptions.optimize1 || kompileOptions.optimize2 || kompileOptions.optimize3; - Matching.writeDecisionTreeToFile( - files.resolveKompiled("definition.kore"), - options.heuristic, - files.resolveKompiled("dt"), - Matching.getThreshold(getThreshold()), - !optimize, - globalOptions.includesExceptionType(ExceptionType.USELESS_RULE), - options.enableSearch, - ex -> { + private final GlobalOptions globalOptions; + private final LLVMKompileOptions options; + private final KExceptionManager kem; + private final KompileOptions kompileOptions; + private final Tool tool; + + @Inject + public LLVMBackend( + KompileOptions kompileOptions, + GlobalOptions globalOptions, + FileUtil files, + KExceptionManager kem, + LLVMKompileOptions options, + Tool tool) { + super(kompileOptions, files, kem, tool); + this.globalOptions = globalOptions; + this.options = options; + this.kompileOptions = kompileOptions; + this.kem = kem; + this.tool = tool; + } + + @Override + public void accept(Backend.Holder h) { + Stopwatch sw = new Stopwatch(globalOptions); + String kore = getKompiledString(h.def, true); + h.def = null; + files.saveToKompiled("definition.kore", kore); + sw.printIntermediate(" Print definition.kore"); + FileUtils.deleteQuietly(files.resolveKompiled("dt")); + MutableInt warnings = new MutableInt(); + boolean optimize = + kompileOptions.optimize1 || kompileOptions.optimize2 || kompileOptions.optimize3; + Matching.writeDecisionTreeToFile( + files.resolveKompiled("definition.kore"), + options.heuristic, + files.resolveKompiled("dt"), + Matching.getThreshold(getThreshold()), + !optimize, + globalOptions.includesExceptionType(ExceptionType.USELESS_RULE), + options.enableSearch, + ex -> { kem.addKException(ex); if (globalOptions.includesExceptionType(ex.getType())) { - warnings.increment(); + warnings.increment(); } return null; }); - sw.printIntermediate(" Write decision tree"); - if (warnings.intValue() > 0 && kem.options.warnings2errors) { - throw KEMException.compilerError("Had " + warnings.intValue() + " pattern matching errors."); - } - if (options.noLLVMKompile) { - return; - } - if (options.enableSearch && options.llvmKompileOutput != null) { - throw KEMException.criticalError("Can't use --llvm-kompile-output with --enable-search."); - } - if (options.llvmKompileType.equals("python") && options.llvmKompileOutput != null) { - throw KEMException.criticalError("Can't use --llvm-kompile-output with --llvm-kompile-type python"); - } - String llvmType = switch (options.llvmKompileType) { - case "main", "search", "static", "library", "python", "c" -> options.llvmKompileType; - default -> throw KEMException.criticalError("Non-valid argument for --llvm-kompile-type: " + options.llvmKompileType + ". Expected [main|search|library|static|python|c]"); + sw.printIntermediate(" Write decision tree"); + if (warnings.intValue() > 0 && kem.options.warnings2errors) { + throw KEMException.compilerError("Had " + warnings.intValue() + " pattern matching errors."); + } + if (options.noLLVMKompile) { + return; + } + if (options.enableSearch && options.llvmKompileOutput != null) { + throw KEMException.criticalError("Can't use --llvm-kompile-output with --enable-search."); + } + if (options.llvmKompileType.equals("python") && options.llvmKompileOutput != null) { + throw KEMException.criticalError( + "Can't use --llvm-kompile-output with --llvm-kompile-type python"); + } + String llvmType = + switch (options.llvmKompileType) { + case "main", "search", "static", "library", "python", "c" -> options.llvmKompileType; + default -> throw KEMException.criticalError( + "Non-valid argument for --llvm-kompile-type: " + + options.llvmKompileType + + ". Expected [main|search|library|static|python|c]"); }; - String llvmOutput = "interpreter"; - if (options.llvmKompileOutput != null) { - llvmOutput = options.llvmKompileOutput; - } + String llvmOutput = "interpreter"; + if (options.llvmKompileOutput != null) { + llvmOutput = options.llvmKompileOutput; + } - if (options.llvmKompileType.equals("python")) { - llvmOutput = null; - } + if (options.llvmKompileType.equals("python")) { + llvmOutput = null; + } - llvmKompile(llvmType, llvmOutput); + llvmKompile(llvmType, llvmOutput); - if (options.enableSearch) { - llvmKompile("search", "search"); - } + if (options.enableSearch) { + llvmKompile("search", "search"); } - - private void llvmKompile(String type, String executable) { - Stopwatch sw = new Stopwatch(globalOptions); - ProcessBuilder pb = files.getProcessBuilder(); - List args = new ArrayList<>(); - - try { - args.add("llvm-kompile"); - args.add(files.resolveKompiled("definition.kore").getCanonicalPath()); - args.add(files.resolveKompiled("dt").getCanonicalPath()); - args.add(type); - - // Arguments after this point are passed on to Clang. - args.add("--"); - - if (options.debug) { - args.add("-g"); - args.add("-O1"); - } - - // For Python bindings, we explicitly leave this unset so that python3-config - // can decide the proper filename. - if (executable != null) { - args.add("-o"); - - File outputFile = new File(executable); - if(!new File(executable).isAbsolute()) { - outputFile = files.resolveKompiled(executable); - } - - args.add(outputFile.getCanonicalPath()); - } - - if (!options.debug) { - if (kompileOptions.optimize1) args.add("-O1"); - if (kompileOptions.optimize2) args.add("-O2"); - if (kompileOptions.optimize3) args.add("-O2"); // clang -O3 does not make the llvm backend any faster - } - - args.addAll(options.ccopts); - - if (globalOptions.verbose) { - System.out.println(" \u250cExecuting: " + String.join(" ", args)); - } - - Process p = pb.command(args).inheritIO().start(); - int exit = p.waitFor(); - if (exit != 0) { - throw KEMException.criticalError("llvm-kompile returned nonzero exit code: " + exit + "\nExamine output to see errors."); - } - } catch (IOException | InterruptedException e) { - throw KEMException.criticalError("Error with I/O while executing llvm-kompile", e); + } + + private void llvmKompile(String type, String executable) { + Stopwatch sw = new Stopwatch(globalOptions); + ProcessBuilder pb = files.getProcessBuilder(); + List args = new ArrayList<>(); + + try { + args.add("llvm-kompile"); + args.add(files.resolveKompiled("definition.kore").getCanonicalPath()); + args.add(files.resolveKompiled("dt").getCanonicalPath()); + args.add(type); + + // Arguments after this point are passed on to Clang. + args.add("--"); + + if (options.debug) { + args.add("-g"); + args.add("-O1"); + } + + // For Python bindings, we explicitly leave this unset so that python3-config + // can decide the proper filename. + if (executable != null) { + args.add("-o"); + + File outputFile = new File(executable); + if (!new File(executable).isAbsolute()) { + outputFile = files.resolveKompiled(executable); } - sw.printIntermediate(" \u2514" + executable + ": " + type); - } - private String getThreshold() { - if (!options.iterated && !kompileOptions.optimize3) { - return "0"; - } - return options.iteratedThreshold; + args.add(outputFile.getCanonicalPath()); + } + + if (!options.debug) { + if (kompileOptions.optimize1) args.add("-O1"); + if (kompileOptions.optimize2) args.add("-O2"); + if (kompileOptions.optimize3) + args.add("-O2"); // clang -O3 does not make the llvm backend any faster + } + + args.addAll(options.ccopts); + + if (globalOptions.verbose) { + System.out.println(" \u250cExecuting: " + String.join(" ", args)); + } + + Process p = pb.command(args).inheritIO().start(); + int exit = p.waitFor(); + if (exit != 0) { + throw KEMException.criticalError( + "llvm-kompile returned nonzero exit code: " + exit + "\nExamine output to see errors."); + } + } catch (IOException | InterruptedException e) { + throw KEMException.criticalError("Error with I/O while executing llvm-kompile", e); } + sw.printIntermediate(" \u2514" + executable + ": " + type); + } - @Override - public Set excludedModuleTags() { - return new HashSet<>(Arrays.asList(Att.SYMBOLIC(), Att.KAST())); + private String getThreshold() { + if (!options.iterated && !kompileOptions.optimize3) { + return "0"; } + return options.iteratedThreshold; + } + + @Override + public Set excludedModuleTags() { + return new HashSet<>(Arrays.asList(Att.SYMBOLIC(), Att.KAST())); + } } diff --git a/llvm-backend/src/main/java/org/kframework/backend/llvm/LLVMBackendKModule.java b/llvm-backend/src/main/java/org/kframework/backend/llvm/LLVMBackendKModule.java index 593ac782be4..0db0dd9a277 100644 --- a/llvm-backend/src/main/java/org/kframework/backend/llvm/LLVMBackendKModule.java +++ b/llvm-backend/src/main/java/org/kframework/backend/llvm/LLVMBackendKModule.java @@ -3,40 +3,35 @@ import com.google.inject.AbstractModule; import com.google.inject.Module; -import com.google.inject.TypeLiteral; import com.google.inject.multibindings.MapBinder; -import org.apache.commons.lang3.tuple.Pair; -import org.kframework.definition.Definition; -import org.kframework.main.AbstractKModule; -import org.kframework.rewriter.Rewriter; - import java.util.Collections; import java.util.List; -import java.util.function.Function; +import org.apache.commons.lang3.tuple.Pair; +import org.kframework.main.AbstractKModule; -/** - * Created by traiansf on 9/13/18. - */ +/** Created by traiansf on 9/13/18. */ public class LLVMBackendKModule extends AbstractKModule { - @Override - public List getKompileModules() { - List mods = super.getKompileModules(); - mods.add(new AbstractModule() { - @Override - protected void configure() { - bindOptions(LLVMBackendKModule.this::kompileOptions, binder()); + @Override + public List getKompileModules() { + List mods = super.getKompileModules(); + mods.add( + new AbstractModule() { + @Override + protected void configure() { + bindOptions(LLVMBackendKModule.this::kompileOptions, binder()); - MapBinder mapBinder = MapBinder.newMapBinder( - binder(), String.class, org.kframework.compile.Backend.class); - mapBinder.addBinding("llvm").to(LLVMBackend.class); - } + MapBinder mapBinder = + MapBinder.newMapBinder( + binder(), String.class, org.kframework.compile.Backend.class); + mapBinder.addBinding("llvm").to(LLVMBackend.class); + } }); - return mods; - } + return mods; + } - @Override - public List, Boolean>> kompileOptions() { - return Collections.singletonList(Pair.of(LLVMKompileOptions.class, true)); - } + @Override + public List, Boolean>> kompileOptions() { + return Collections.singletonList(Pair.of(LLVMKompileOptions.class, true)); + } } diff --git a/llvm-backend/src/main/java/org/kframework/backend/llvm/LLVMKompileOptions.java b/llvm-backend/src/main/java/org/kframework/backend/llvm/LLVMKompileOptions.java index bb757de59a8..5a1ea131b5a 100644 --- a/llvm-backend/src/main/java/org/kframework/backend/llvm/LLVMKompileOptions.java +++ b/llvm-backend/src/main/java/org/kframework/backend/llvm/LLVMKompileOptions.java @@ -1,52 +1,103 @@ // Copyright (c) Runtime Verification, Inc. All Rights Reserved. package org.kframework.backend.llvm; -import com.beust.jcommander.Parameter; import com.beust.jcommander.IStringConverter; +import com.beust.jcommander.Parameter; import com.google.inject.Inject; -import org.kframework.utils.inject.RequestScoped; - -import java.util.Arrays; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import org.kframework.utils.inject.RequestScoped; @RequestScoped public class LLVMKompileOptions { - @Inject - public LLVMKompileOptions() {} + @Inject + public LLVMKompileOptions() {} - @Parameter(names="--enable-llvm-debug", description="Enable debugging support for the LLVM backend.") - public boolean debug = false; + @Parameter( + names = "--enable-llvm-debug", + description = "Enable debugging support for the LLVM backend.") + public boolean debug = false; - @Parameter(names="-ccopt", description="Add a command line option to the compiler invocation for the llvm backend.", descriptionKey = "options", listConverter=SingletonListConverter.class, hidden = true) - public List ccopts = new ArrayList<>(); + @Parameter( + names = "-ccopt", + description = "Add a command line option to the compiler invocation for the llvm backend.", + descriptionKey = "options", + listConverter = SingletonListConverter.class, + hidden = true) + public List ccopts = new ArrayList<>(); - public static class SingletonListConverter implements IStringConverter> { - @Override - public List convert(String str) { - return Arrays.asList(str); - } + public static class SingletonListConverter implements IStringConverter> { + @Override + public List convert(String str) { + return Arrays.asList(str); } + } - @Parameter(names="--heuristic", description="A string of single characters representing a sequence of heuristics to use during pattern matching compilation. Valid choices are f, d, b, a, l, r, n, p, q, _, N, L, R.", descriptionKey = "heuristics", hidden = true) - public String heuristic = "qbaL"; + @Parameter( + names = "--heuristic", + description = + "A string of single characters representing a sequence of heuristics to use during" + + " pattern matching compilation. Valid choices are f, d, b, a, l, r, n, p, q, _, N," + + " L, R.", + descriptionKey = "heuristics", + hidden = true) + public String heuristic = "qbaL"; - @Parameter(names="--iterated", description="Generate iterated pattern matching optimization; time-consuming but significantly reduces matching time.", hidden = true) - public boolean iterated = false; + @Parameter( + names = "--iterated", + description = + "Generate iterated pattern matching optimization; time-consuming but significantly" + + " reduces matching time.", + hidden = true) + public boolean iterated = false; - @Parameter(names="--iterated-threshold", description="Threshold heuristic to use when choosing which axioms to optimize. A value of 0 turns the optimization off; a value of 1 turns the optimization on for every axiom. Values in between (expressed as a fraction of two integers, e.g. 1/2), control the aggressiveness of the optimization. Higher values increase compilation times extremely, but also increase the effectiveness of the optimization. Consider decreasing this threshold if compilation is too slow.", descriptionKey = "value", hidden = true) - public String iteratedThreshold = "1/2"; + @Parameter( + names = "--iterated-threshold", + description = + "Threshold heuristic to use when choosing which axioms to optimize. A value of 0 turns" + + " the optimization off; a value of 1 turns the optimization on for every axiom." + + " Values in between (expressed as a fraction of two integers, e.g. 1/2), control" + + " the aggressiveness of the optimization. Higher values increase compilation times" + + " extremely, but also increase the effectiveness of the optimization. Consider" + + " decreasing this threshold if compilation is too slow.", + descriptionKey = "value", + hidden = true) + public String iteratedThreshold = "1/2"; - @Parameter(names="--no-llvm-kompile", description="Do not invoke llvm-kompile. Useful if you want to do it yourself when building with the LLVM backend.", hidden = true) - public boolean noLLVMKompile; + @Parameter( + names = "--no-llvm-kompile", + description = + "Do not invoke llvm-kompile. Useful if you want to do it yourself when building with the" + + " LLVM backend.", + hidden = true) + public boolean noLLVMKompile; - @Parameter(names="--enable-search", description="By default, to reduce compilation time, `krun --search` is disabled on the LLVM backend. Pass this flag to enable it.") - public boolean enableSearch; + @Parameter( + names = "--enable-search", + description = + "By default, to reduce compilation time, `krun --search` is disabled on the LLVM backend." + + " Pass this flag to enable it.") + public boolean enableSearch; - @Parameter(names="--llvm-kompile-type", description="Specifies the llvm backend's output type. Valid choices are main (build an interpreter), library (build an interpreter, but don't link a main function), search (same as main, but the interpreter does search instead of single-path execution), static (same as library, but no '-l' flags are passed during linking. Used for making a partially linked object file) and python (build a Python bindings module for this definition)", descriptionKey = "type", hidden = true) - public String llvmKompileType = "main"; + @Parameter( + names = "--llvm-kompile-type", + description = + "Specifies the llvm backend's output type. Valid choices are main (build an interpreter)," + + " library (build an interpreter, but don't link a main function), search (same as" + + " main, but the interpreter does search instead of single-path execution), static" + + " (same as library, but no '-l' flags are passed during linking. Used for making a" + + " partially linked object file) and python (build a Python bindings module for this" + + " definition)", + descriptionKey = "type", + hidden = true) + public String llvmKompileType = "main"; - @Parameter(names="--llvm-kompile-output", description="Name of the output binary from the llvm backend.", descriptionKey = "file", hidden = true) - public String llvmKompileOutput = null; + @Parameter( + names = "--llvm-kompile-output", + description = "Name of the output binary from the llvm backend.", + descriptionKey = "file", + hidden = true) + public String llvmKompileOutput = null; }