From: Hannu Niemistö Date: Thu, 23 Nov 2017 11:19:00 +0000 (+0200) Subject: New SCL completion implementation X-Git-Tag: v1.31.0~22 X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F56%2F1256%2F1;p=simantics%2Fplatform.git New SCL completion implementation refs #7638 Change-Id: I57d2ddc1628ff8da2421c4eda41ce5b83187543e --- diff --git a/bundles/org.simantics.scl.compiler/META-INF/MANIFEST.MF b/bundles/org.simantics.scl.compiler/META-INF/MANIFEST.MF index a09f40cd5..7f76ebc10 100644 --- a/bundles/org.simantics.scl.compiler/META-INF/MANIFEST.MF +++ b/bundles/org.simantics.scl.compiler/META-INF/MANIFEST.MF @@ -18,6 +18,8 @@ Export-Package: org.cojen.classfile, org.simantics.scl.compiler.common.names, org.simantics.scl.compiler.common.precedence, org.simantics.scl.compiler.compilation, + org.simantics.scl.compiler.completions, + org.simantics.scl.compiler.completions.parsing, org.simantics.scl.compiler.constants, org.simantics.scl.compiler.constants.generic, org.simantics.scl.compiler.elaboration.chr, diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/compilation/DeclarationClassification.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/compilation/DeclarationClassification.java index 4bf5d847e..ed21f04b2 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/compilation/DeclarationClassification.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/compilation/DeclarationClassification.java @@ -39,37 +39,37 @@ import org.simantics.scl.compiler.module.ImportDeclaration; import gnu.trove.map.hash.THashMap; public class DeclarationClassification { - ArrayList importsAst = new ArrayList(); - ArrayList dataTypesAst = new ArrayList(); - ArrayList typeAliasesAst = new ArrayList(); - ValueRepository valueDefinitionsAst = new ValueRepository(); - RelationRepository relationDefinitionsAst = new RelationRepository(); - ArrayList typeAnnotationsAst = new ArrayList(); - ArrayList fixityAst = new ArrayList(); - ArrayList typeClassesAst = new ArrayList(); - ArrayList instancesAst = new ArrayList(); - ArrayList derivingInstancesAst = new ArrayList(); - ArrayList effectsAst = new ArrayList(); - ArrayList rulesAst = new ArrayList(); - ArrayList mappingRelationsAst = new ArrayList(); - ArrayList rulesetsAst = new ArrayList(); + public ArrayList importsAst = new ArrayList(); + public ArrayList dataTypesAst = new ArrayList(); + public ArrayList typeAliasesAst = new ArrayList(); + public ValueRepository valueDefinitionsAst = new ValueRepository(); + public RelationRepository relationDefinitionsAst = new RelationRepository(); + public ArrayList typeAnnotationsAst = new ArrayList(); + public ArrayList fixityAst = new ArrayList(); + public ArrayList typeClassesAst = new ArrayList(); + public ArrayList instancesAst = new ArrayList(); + public ArrayList derivingInstancesAst = new ArrayList(); + public ArrayList effectsAst = new ArrayList(); + public ArrayList rulesAst = new ArrayList(); + public ArrayList mappingRelationsAst = new ArrayList(); + public ArrayList rulesetsAst = new ArrayList(); - THashMap valueDocumentation = new THashMap(); - THashMap relationDocumentation = new THashMap(); - THashMap typeDocumentation = new THashMap(); - THashMap classDocumentation = new THashMap(); + public THashMap valueDocumentation = new THashMap(); + public THashMap relationDocumentation = new THashMap(); + public THashMap typeDocumentation = new THashMap(); + public THashMap classDocumentation = new THashMap(); - ArrayList javaMethodDeclarations = new ArrayList(); + public ArrayList javaMethodDeclarations = new ArrayList(); - StringBuilder moduleDocumentation = new StringBuilder(); + public StringBuilder moduleDocumentation = new StringBuilder(); - ArrayList currentAnnotations = new ArrayList(2); - DDocumentationAst documentation; - String inJavaClass; + public ArrayList currentAnnotations = new ArrayList(2); + public DDocumentationAst documentation; + public String inJavaClass; - ArrayList defaultAnnotations = new ArrayList(); + public ArrayList defaultAnnotations = new ArrayList(); - ErrorLog errorLog; + public ErrorLog errorLog; public DeclarationClassification(CompilationContext compilationContext) { this.errorLog = compilationContext.errorLog; @@ -190,7 +190,7 @@ public class DeclarationClassification { importsAst.add(declaration); } - public void handle(DImportJavaAst declaration) { + public void handle(DImportJavaAst declaration) { if(!currentAnnotations.isEmpty()) { for(DAnnotationAst annotation : currentAnnotations) { String name = annotation.id.text; diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/compilation/Elaboration.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/compilation/Elaboration.java index 26bd31ec2..799b07268 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/compilation/Elaboration.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/compilation/Elaboration.java @@ -79,6 +79,7 @@ import org.simantics.scl.compiler.internal.deriving.InstanceDerivers; import org.simantics.scl.compiler.internal.elaboration.profiling.BranchPointInjector; import org.simantics.scl.compiler.internal.elaboration.utils.StronglyConnectedComponents; import org.simantics.scl.compiler.internal.header.ModuleHeader; +import org.simantics.scl.compiler.internal.parsing.Token; import org.simantics.scl.compiler.internal.parsing.declarations.ConstructorAst; import org.simantics.scl.compiler.internal.parsing.declarations.DAnnotationAst; import org.simantics.scl.compiler.internal.parsing.declarations.DClassAst; @@ -95,6 +96,7 @@ import org.simantics.scl.compiler.internal.parsing.declarations.DTypeAst; import org.simantics.scl.compiler.internal.parsing.declarations.DValueAst; import org.simantics.scl.compiler.internal.parsing.declarations.DValueTypeAst; import org.simantics.scl.compiler.internal.parsing.exceptions.SCLSyntaxErrorException; +import org.simantics.scl.compiler.internal.parsing.parser.SCLTerminals; import org.simantics.scl.compiler.internal.parsing.translation.ProcessedDClassAst; import org.simantics.scl.compiler.internal.parsing.translation.ProcessedDInstanceAst; import org.simantics.scl.compiler.internal.parsing.translation.RelationRepository; @@ -156,7 +158,8 @@ public class Elaboration { this.errorLog = compilationContext.errorLog; this.moduleName = moduleName; this.moduleHeader = moduleHeader; - importsAst = processRelativeImports(importsAst); + if(moduleName != null) + importsAst = processRelativeImports(compilationContext.errorLog, moduleName, importsAst); this.importsAst = importsAst; this.jrvFactory = jrvFactory; this.javaReferenceValidator = moduleHeader == null || moduleHeader.classLoader == null @@ -200,7 +203,7 @@ public class Elaboration { compilationContext.namingPolicy = new JavaNamingPolicy(moduleName); } - private ArrayList processRelativeImports(ArrayList relativeImports) { + public static ArrayList processRelativeImports(ErrorLog errorLog, String moduleName, ArrayList relativeImports) { ArrayList absoluteImports = new ArrayList(relativeImports.size()); for(ImportDeclaration relativeImport : relativeImports) { if(relativeImport.moduleName.startsWith(".")) { @@ -212,7 +215,8 @@ public class Elaboration { relativeImport.reexport, relativeImport.spec); absoluteImports.add(absoluteImport); } catch (InvalidModulePathException e) { - errorLog.log(relativeImport.location, e.getMessage()); + if(errorLog != null) + errorLog.log(relativeImport.location, e.getMessage()); } } else @@ -524,9 +528,8 @@ public class Elaboration { method.setDefaultImplementation(Name.create(moduleName, fullName)); valueDefinitionsAst.addDefinitions(fullName, defs); - /*valueDefinitionsAst.addAnnotation(fullName, new DAnnotationAst(new EVar("@private"), - Collections.emptyList()));*/ - supplementedTypeAnnotations.add(new SupplementedValueType(defs.get(0).location, fullName, method.getType())); + supplementedTypeAnnotations.add(new SupplementedValueType(defs.get(0).location, fullName, method.getType())); + valueDefinitionsAst.setDerived(fullName); } module.addTypeClass(classAst.name, typeClass); diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/compilation/NamespaceOfModule.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/compilation/NamespaceOfModule.java index 8621283b7..4de6cac81 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/compilation/NamespaceOfModule.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/compilation/NamespaceOfModule.java @@ -1,6 +1,7 @@ package org.simantics.scl.compiler.compilation; import java.util.Arrays; +import java.util.Collection; import java.util.function.Consumer; import org.simantics.scl.compiler.elaboration.chr.CHRRuleset; @@ -35,6 +36,11 @@ public class NamespaceOfModule implements Namespace { public Namespace getNamespace(String name) { return base.getNamespace(name); } + + @Override + public Collection getNamespaces() { + return base.getNamespaces(); + } @Override public SCLValue getValue(String name) throws AmbiguousNameException { diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/Completion.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/Completion.java new file mode 100644 index 000000000..dc21c6c55 --- /dev/null +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/Completion.java @@ -0,0 +1,32 @@ +package org.simantics.scl.compiler.completions; + +public class Completion { + public final int startOfReplacedText; + public final int lengthOfReplacedText; + public final String replacement; + public final int cursorPositionAfterReplacement; + + public final CompletionType completionType; + public final String module; + public final String name; + public final String type; // may be null + public final String documentation; // may be null + + public Completion(int startOfReplacedText, int lengthOfReplacedText, String replacement, + int cursorPositionAfterReplacement, CompletionType completionType, String definingModule, String name, + String type, String documentation) { + this.startOfReplacedText = startOfReplacedText; + this.lengthOfReplacedText = lengthOfReplacedText; + this.replacement = replacement; + this.cursorPositionAfterReplacement = cursorPositionAfterReplacement; + this.completionType = completionType; + this.module = definingModule; + this.name = name; + this.type = type; + this.documentation = "".equals(documentation) ? null : documentation; + } + + public String getLabel() { + return name + (type != null ? " :: " + type : "") + (module != null ? " (" + module + ")" : "") ; + } +} diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/CompletionComparator.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/CompletionComparator.java new file mode 100644 index 000000000..aff7552c6 --- /dev/null +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/CompletionComparator.java @@ -0,0 +1,12 @@ +package org.simantics.scl.compiler.completions; + +import java.util.Comparator; + +public class CompletionComparator implements Comparator { + + @Override + public int compare(Completion o1, Completion o2) { + return o1.name.toLowerCase().compareTo(o2.name.toLowerCase()); + } + +} diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/CompletionRequest.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/CompletionRequest.java new file mode 100644 index 000000000..84a828f52 --- /dev/null +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/CompletionRequest.java @@ -0,0 +1,43 @@ +package org.simantics.scl.compiler.completions; + +import org.simantics.scl.compiler.environment.EnvironmentFactory; +import org.simantics.scl.compiler.module.repository.ModuleRepository; +import org.simantics.scl.compiler.source.TextualModuleSource; + +public class CompletionRequest { + public static final int DEFAULT_MAX_NUMBER_OF_COMPLETIONS = 1000; + + public ModuleRepository repository; + + public TextualModuleSource moduleSource; + + /** + * Source text of the SCL module. + */ + public String sourceText; + + /** + * Cursor position. Cursor is between characters (position-1) and position. + */ + public int position; + + /** + * Maximum number of completions that will be returned + */ + public int maxNumberOfCompletions; + public CompletionRequest() { + this.maxNumberOfCompletions = DEFAULT_MAX_NUMBER_OF_COMPLETIONS; + } + + public CompletionRequest(ModuleRepository repository, TextualModuleSource moduleSource, String sourceText, int position, int maxNumberOfCompletions) { + this.repository = repository; + this.moduleSource = moduleSource; + this.sourceText = sourceText; + this.position = position; + this.maxNumberOfCompletions = maxNumberOfCompletions; + } + + public CompletionRequest(ModuleRepository repository, TextualModuleSource moduleSource, String sourceText, int position) { + this(repository, moduleSource, sourceText, position, DEFAULT_MAX_NUMBER_OF_COMPLETIONS); + } +} diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/CompletionType.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/CompletionType.java new file mode 100644 index 000000000..b69abb1c8 --- /dev/null +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/CompletionType.java @@ -0,0 +1,7 @@ +package org.simantics.scl.compiler.completions; + +public enum CompletionType { + Value, + Type, + Namespace +} diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/Completions.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/Completions.java new file mode 100644 index 000000000..aa435c6fd --- /dev/null +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/Completions.java @@ -0,0 +1,207 @@ +package org.simantics.scl.compiler.completions; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import org.simantics.scl.compiler.common.names.Name; +import org.simantics.scl.compiler.compilation.CompilationContext; +import org.simantics.scl.compiler.compilation.DeclarationClassification; +import org.simantics.scl.compiler.compilation.Elaboration; +import org.simantics.scl.compiler.completions.parsing.RobustModuleParser; +import org.simantics.scl.compiler.elaboration.expressions.EVar; +import org.simantics.scl.compiler.elaboration.modules.SCLValue; +import org.simantics.scl.compiler.environment.Environment; +import org.simantics.scl.compiler.environment.EnvironmentFactory; +import org.simantics.scl.compiler.environment.EnvironmentFactoryImpl; +import org.simantics.scl.compiler.environment.Namespace; +import org.simantics.scl.compiler.environment.filter.AcceptAllNamespaceFilter; +import org.simantics.scl.compiler.internal.parsing.declarations.ConstructorAst; +import org.simantics.scl.compiler.internal.parsing.declarations.DDataAst; +import org.simantics.scl.compiler.internal.parsing.declarations.DDocumentationAst; +import org.simantics.scl.compiler.internal.parsing.declarations.DTypeAst; +import org.simantics.scl.compiler.internal.parsing.declarations.DValueTypeAst; +import org.simantics.scl.compiler.internal.parsing.types.TypeAst; +import org.simantics.scl.compiler.module.ImportDeclaration; +import org.simantics.scl.compiler.types.TCon; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import gnu.trove.procedure.TObjectProcedure; +import gnu.trove.set.hash.THashSet; + +public class Completions { + private static final Logger LOGGER = LoggerFactory.getLogger(Completions.class); + + public static List findCompletions(CompletionRequest request) { + //LOGGER.debug("findCompletions"); + AtomicInteger maxNumberOfCompletions = new AtomicInteger(request.maxNumberOfCompletions); + ArrayList completions = new ArrayList(); + + String moduleName = request.moduleSource.getModuleName(); + CompilationContext context = new CompilationContext(); + + String prefix = PrefixUtil.findPrefix(request.sourceText, request.position).toLowerCase(); + //LOGGER.debug(" prefix = {}", prefix); + + DeclarationClassification declarations = RobustModuleParser.parse(context, request.sourceText); + + + // Find local completions + localDefs: if(!prefix.contains(".")) { + THashSet valuesAlreadyAdded = new THashSet(); + for(DDataAst dataAst : declarations.dataTypesAst) { + if(dataAst.name.toLowerCase().startsWith(prefix)) { + completions.add(createLocalTypeCompletion(request.position, prefix, declarations, moduleName, dataAst.name)); + if(maxNumberOfCompletions.decrementAndGet() <= 0) + break localDefs; + } + for(ConstructorAst constructor : dataAst.constructors) { + if(constructor.name.text.toLowerCase().startsWith(prefix) && valuesAlreadyAdded.add(constructor.name.text)) { + completions.add(createLocalValueCompletion(request.position, prefix, declarations, moduleName, constructor.name.text, null)); + if(maxNumberOfCompletions.decrementAndGet() <= 0) + break localDefs; + } + } + } + for(DTypeAst typeAst : declarations.typeAliasesAst) { + if(typeAst.name.toLowerCase().startsWith(prefix)) { + completions.add(createLocalTypeCompletion(request.position, prefix, declarations, moduleName, typeAst.name)); + if(maxNumberOfCompletions.decrementAndGet() <= 0) + break localDefs; + } + } + + for(DValueTypeAst valueType : declarations.typeAnnotationsAst) { + for(EVar var : valueType.names) { + if(var.name.toLowerCase().startsWith(prefix) && valuesAlreadyAdded.add(var.name)) { + completions.add(createLocalValueCompletion(request.position, prefix, declarations, moduleName, var.name, valueType.type)); + if(maxNumberOfCompletions.decrementAndGet() <= 0) + break localDefs; + } + } + } + for(String valueName : declarations.valueDefinitionsAst.getValueNames()) + if(valueName.toLowerCase().startsWith(prefix) && valuesAlreadyAdded.add(valueName)) { + completions.add(createLocalValueCompletion(request.position, prefix, declarations, moduleName, valueName, null)); + if(maxNumberOfCompletions.decrementAndGet() <= 0) + break localDefs; + } + } + + // Find completions from dependencies + ArrayList imports = Elaboration.processRelativeImports(null, + request.moduleSource.getModuleName(), declarations.importsAst); + //for(ImportDeclaration import_ : imports) + // LOGGER.debug(" import {} as {}", import_.moduleName, import_.localName); + + EnvironmentFactory environmentFactory = new EnvironmentFactoryImpl(request.repository, + request.moduleSource.getBuiltinImports(null), null); + Environment environment = environmentFactory.createEnvironmentRobustly( + context, + imports.toArray(new ImportDeclaration[imports.size()])); + + Namespace namespace = environment.getLocalNamespace(); + int pos = 0; + while(true) { + int p = prefix.indexOf('.', pos); + if(p == -1) { + prefix = prefix.substring(pos); + break; + } + String namespaceName = prefix.substring(pos, p); + Namespace temp = namespace.getNamespace(namespaceName); + //LOGGER.debug(" found namespace {}", namespaceName); + + if(temp == null) { + prefix = prefix.substring(pos); + break; + } + else { + namespace = temp; + pos = p+1; + } + } + String prefix_ = prefix; + //LOGGER.debug(" prefix = {}", prefix); + + THashSet namespacesAlreadyAdded = new THashSet(); + namespacesAlreadyAdded.add(""); + + for(String localNamespace : namespace.getNamespaces()) + if(localNamespace.toLowerCase().startsWith(prefix) && namespacesAlreadyAdded.add(localNamespace)) + completions.add(createNamespaceCompletion(request.position, prefix, localNamespace)); + + namespace.findTypesForPrefix(prefix, AcceptAllNamespaceFilter.INSTANCE, (TCon type) -> { + completions.add(createImportedTypeCompletion(request.position, prefix_, type)); + maxNumberOfCompletions.decrementAndGet(); + }); + // TODO effects + namespace.findValuesForPrefix(prefix, AcceptAllNamespaceFilter.INSTANCE, new TObjectProcedure() { + @Override + public boolean execute(SCLValue value) { + String name = value.getName().name; + int dp = name.indexOf('.', prefix_.length()); + if(dp != -1) { + String localNamespace = name.substring(0, dp); + if(namespacesAlreadyAdded.add(localNamespace)) { + completions.add(createNamespaceCompletion(request.position, prefix_, localNamespace)); + return maxNumberOfCompletions.decrementAndGet() > 0; + } + else + return true; + } + else { + completions.add(createImportedValueCompletion(request.position, prefix_, value)); + return maxNumberOfCompletions.decrementAndGet() > 0; + } + } + }); + + Collections.sort(completions, new CompletionComparator()); + return completions; + } + + private static Completion createNamespaceCompletion(int position, String prefix, String localNamespace) { + int length = prefix.length(); + int begin = position - length; + return new Completion(position-length, length, localNamespace, begin + localNamespace.length(), + CompletionType.Namespace, null, localNamespace, null, null); + } + + private static Completion createLocalValueCompletion(int position, String prefix, DeclarationClassification declarations, + String moduleName, String valueName, TypeAst type) { + int length = prefix.length(); + int begin = position - length; + DDocumentationAst documentationAst = declarations.valueDocumentation.get(valueName); + String documentation = documentationAst!=null ? documentationAst.documentation : null; + return new Completion(position-length, length, valueName, begin + valueName.length(), + CompletionType.Value, moduleName, valueName, type != null ? type.toString() : null, documentation); + } + + private static Completion createLocalTypeCompletion(int position, String prefix, DeclarationClassification declarations, + String moduleName, String typeName) { + int length = prefix.length(); + int begin = position - length; + return new Completion(position-length, length, typeName, begin + typeName.length(), + CompletionType.Type, moduleName, typeName, null, null); + } + + private static Completion createImportedValueCompletion(int position, String prefix, SCLValue value) { + int length = prefix.length(); + int begin = position - length; + String documentation = value.documentation; + Name name = value.getName(); + return new Completion(position-length, length, name.name, begin + name.name.length(), + CompletionType.Value, name.module, name.name, value.getType().toString(), documentation); + } + + private static Completion createImportedTypeCompletion(int position, String prefix, TCon type) { + int length = prefix.length(); + int begin = position - length; + return new Completion(position-length, length, type.name, begin + type.name.length(), + CompletionType.Type, type.module, type.name, null, ""); + } + +} diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/PrefixUtil.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/PrefixUtil.java new file mode 100644 index 000000000..a4ce9adb7 --- /dev/null +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/PrefixUtil.java @@ -0,0 +1,37 @@ +package org.simantics.scl.compiler.completions; + +public class PrefixUtil { + public static String findPrefix(String sourceText, int end) { + int position; + for(position=end-1;position >= 0 && isPrefixChar(sourceText.charAt(position));--position); + ++position; + while(position < end && !isPrefixStart(sourceText.charAt(position))) + ++position; + return sourceText.substring(position, end); + } + + private static boolean isPrefixStart(char c) { + return Character.isJavaIdentifierStart(c); + } + + private static boolean isPrefixChar(char c) { + return Character.isJavaIdentifierPart(c) || c=='.'; + } + + public static String[] splitPrefix(String prefix) { + int partCount = 1; + for(int i=0;i parseSegment(CompilationContext context, AtomicBoolean firstToken, Reader reader) { + SCLParserImpl parser = new SCLParserImpl(reader); + parser.getLexer().isFirstToken = firstToken.get(); + parser.setCompilationContext(context); + parser.setParserOptions(SCLParserOptions.MODULE_DEFAULT); + List result = (List)parser.parseModule(); + firstToken.set(parser.getLexer().isFirstToken); + return result; + } + + public static DeclarationClassification parse(CompilationContext context, String sourceText) { + DeclarationClassification declarations = new DeclarationClassification(context); + AtomicBoolean firstToken = new AtomicBoolean(false); + for(ModuleSegment segment : RobustModuleSplitter.split(sourceText)) { + //System.out.println("----------------------------------------------------------"); + //System.out.println(sourceText.substring(segment.begin, segment.end)); + + if(segment.hasErrors) { + System.out.println(" has errors"); + // TODO we could try some special parsing here + } + else { + Reader reader = new SubstringReader(sourceText, segment.begin, segment.end); + try { + for(DeclarationAst declaration : parseSegment(context, firstToken, reader)) + declarations.handle(declaration); + } catch(Exception e) { + // This is normal + //e.printStackTrace(System.out); + } + } + } + return declarations; + } +} diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/parsing/RobustModuleSplitter.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/parsing/RobustModuleSplitter.java new file mode 100644 index 000000000..283af6140 --- /dev/null +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/parsing/RobustModuleSplitter.java @@ -0,0 +1,254 @@ +package org.simantics.scl.compiler.completions.parsing; + +import java.util.ArrayList; +import java.util.List; + +public class RobustModuleSplitter { + // state ids + private static final int NORMAL_START_OF_LINE = 0; + private static final int NORMAL = 1; + private static final int NORMAL_1QUOTE = 2; + private static final int NORMAL_2QUOTE = 3; + private static final int SHORT_STRING = 4; + private static final int SHORT_STRING_BACKSLASH = 5; + private static final int LONG_STRING = 6; + private static final int LONG_STRING_1QUOTE = 7; + private static final int LONG_STRING_2QUOTE = 8; + private static final int CHAR_LITERAL = 9; + private static final int CHAR_LITERAL_BACKSLASH = 10; + private static final int NORMAL_1SLASH = 11; + private static final int C_COMMENT = 12; + private static final int C_COMMENT_STAR = 13; + private static final int CPP_COMMENT = 14; + + private final String sourceText; + private ArrayList segments = new ArrayList(); + + private RobustModuleSplitter(String sourceText) { + this.sourceText = sourceText; + } + + private static boolean isLineEnd(char c) { + return c == '\n' || c == 0; + } + + private void split() { + int state = NORMAL; + int begin = 0, pos = 0, curEntityBegin = 0, parenthesesBalance = 0; + boolean hasErrors = false; + int length = sourceText.length(); + loop: while(true) { + char c = pos == length ? 0 : sourceText.charAt(pos++); + if(c == '\r') + c = '\n'; + switch(state) { + case NORMAL_START_OF_LINE: + if(c == '\n') // Don't care about empty lines + break; + if(c != ' ') { + int end = c == 0 ? pos : pos-1; + segments.add(new ModuleSegment(begin, end, parenthesesBalance, hasErrors)); + parenthesesBalance = 0; + hasErrors = false; + begin = end; + } + state = NORMAL; + case NORMAL: + if(c == '"') + state = NORMAL_1QUOTE; + else if(c == '/') + state = NORMAL_1SLASH; + else if(c == '\'') + state = CHAR_LITERAL; + else if(c == '(' || c == '[' || c == '{') + ++parenthesesBalance; + else if(c == ')' || c == ']' || c == '}') + --parenthesesBalance; + else if(c == '\n') + state = NORMAL_START_OF_LINE; + else if(c == 0) + break loop; + break; + case NORMAL_1QUOTE: + if(c == '"') + state = NORMAL_2QUOTE; + else if(c == '\\') + state = SHORT_STRING_BACKSLASH; + else if(c == 0) + break loop; + else + state = SHORT_STRING; + break; + case NORMAL_2QUOTE: + if(c == '"') + state = LONG_STRING; + else { + state = NORMAL; + if(c != 0) + --pos; + } + break; + case SHORT_STRING: + if(c == '\\') + state = SHORT_STRING_BACKSLASH; + else if(c == '"' || isLineEnd(c) /* unclosed string */) { + if(c == '\n') + state = NORMAL_START_OF_LINE; + else + state = NORMAL; + hasErrors = c != '"'; + } + break; + case SHORT_STRING_BACKSLASH: + if(isLineEnd(c) /* unclosed string */) + state = NORMAL_START_OF_LINE; + else + state = SHORT_STRING; + break; + case LONG_STRING: + if(c == '"') + state = LONG_STRING_1QUOTE; + else if(c == 0) { + // Unclosed long string + curEntityBegin = pos; + state = NORMAL; + hasErrors = true; + } + break; + case LONG_STRING_1QUOTE: + if(c == '"') + state = LONG_STRING_2QUOTE; + else + state = LONG_STRING; + break; + case LONG_STRING_2QUOTE: + if(c == '"') + state = NORMAL; + else + state = LONG_STRING; + break; + case CHAR_LITERAL: + if(c == '\'' || isLineEnd(c) /* unclosed char literal */) { + if(c == '\n') + state = NORMAL_START_OF_LINE; + else + state = NORMAL; + hasErrors = c != '\''; + } + else if(c == '\\') + state = CHAR_LITERAL_BACKSLASH; + break; + case CHAR_LITERAL_BACKSLASH: + if(isLineEnd(c) /* unclosed char literal */) { + state = NORMAL_START_OF_LINE; + hasErrors = true; + } + else + state = CHAR_LITERAL; + break; + case NORMAL_1SLASH: + if(c == '/') + state = CPP_COMMENT; + else if(c == '*') { + state = C_COMMENT; + curEntityBegin = pos; + } + else { + state = NORMAL; + if(c != 0) + --pos; + } + break; + case C_COMMENT: + if(c == '*') + state = C_COMMENT_STAR; + else if(c == 0) { + // Unclosed C comment + pos = curEntityBegin; + state = NORMAL; + hasErrors = true; + } + break; + case C_COMMENT_STAR: + if(c == '/') { + state = NORMAL; + } + else + state = C_COMMENT; + break; + case CPP_COMMENT: + if(isLineEnd(c)) + state = NORMAL_START_OF_LINE; + break; + } + } + if(begin != length) + segments.add(new ModuleSegment(begin, length, parenthesesBalance, hasErrors)); + } + + private void combineByParenthesesBalance() { + ArrayList segmentStack = null; + for(ModuleSegment segment : segments) + if(segment.parenthesesBalance > 0) { + if(segmentStack == null) + segmentStack = new ArrayList(); + for(int i=0;i 0 && !segmentStack.isEmpty()) { + segmentStack.remove(segmentStack.size()-1); + --r; + } + if(r > 0) { + segment.parenthesesBalance += r; + segment.hasErrors = true; + } + } + } + if(segmentStack == null) + return; + for(ModuleSegment segment : segmentStack) { + --segment.parenthesesBalance; + segment.hasErrors = true; + } + + ArrayList oldSegments = segments; + segments = new ArrayList(oldSegments.size()); + + int currentBalance = 0; + int begin = 0; + boolean hasErrors = false; + for(ModuleSegment segment : oldSegments) { + if(currentBalance == 0) { + if(segment.parenthesesBalance == 0) + segments.add(segment); + else { + begin = segment.begin; + currentBalance = segment.parenthesesBalance; + hasErrors = segment.hasErrors; + } + } + else { + currentBalance += segment.parenthesesBalance; + hasErrors |= segment.hasErrors; + if(currentBalance == 0) + segments.add(new ModuleSegment(begin, segment.end, 0, hasErrors)); + } + } + } + + public static List split(String sourceText) { + RobustModuleSplitter splitter = new RobustModuleSplitter(sourceText); + splitter.split(); + splitter.combineByParenthesesBalance(); + return splitter.segments; + } +} + diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/parsing/SubstringReader.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/parsing/SubstringReader.java new file mode 100644 index 000000000..b74a0b363 --- /dev/null +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/completions/parsing/SubstringReader.java @@ -0,0 +1,74 @@ +package org.simantics.scl.compiler.completions.parsing; + +import java.io.IOException; +import java.io.Reader; + +/** + * Based on java.io.StringReader. + */ + +public class SubstringReader extends Reader { + + private String str; + private int end; + private int pos; + private int mark; + + public SubstringReader(String str, int begin, int end) { + this.str = str; + this.pos = begin; + this.mark = begin; + this.end = end; + } + + public int read() throws IOException { + if (pos >= end) + return -1; + return str.charAt(pos++); + } + + public int read(char cbuf[], int off, int len) throws IOException { + if ((off < 0) || (off > cbuf.length) || (len < 0) || + ((off + len) > cbuf.length) || ((off + len) < 0)) + throw new IndexOutOfBoundsException(); + else if (len == 0) + return 0; + if (pos >= end) + return -1; + int n = Math.min(end - pos, len); + str.getChars(pos, pos + n, cbuf, off); + pos += n; + return n; + } + + public long skip(long ns) throws IOException { + if (pos >= end) + return 0; + // Bound skip by beginning and end of the source + long n = Math.min(end - pos, ns); + n = Math.max(-pos, n); + pos += n; + return n; + } + + public boolean ready() throws IOException { + return true; + } + + public boolean markSupported() { + return true; + } + + public void mark(int readAheadLimit) throws IOException { + if (readAheadLimit < 0) + throw new IllegalArgumentException("Read-ahead limit < 0"); + mark = pos; + } + + public void reset() throws IOException { + pos = mark; + } + + public void close() { + } +} diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/java/Builtins.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/java/Builtins.java index 4061b8652..e3b429133 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/java/Builtins.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/java/Builtins.java @@ -24,6 +24,7 @@ import org.simantics.scl.compiler.constants.singletons.TypeOfProxyConstant; import org.simantics.scl.compiler.constants.singletons.TypeProxyConstant; import org.simantics.scl.compiler.elaboration.fundeps.Fundep; import org.simantics.scl.compiler.elaboration.modules.Documentation; +import org.simantics.scl.compiler.elaboration.modules.PrivateProperty; import org.simantics.scl.compiler.elaboration.modules.SCLValue; import org.simantics.scl.compiler.elaboration.modules.TypeClass; import org.simantics.scl.compiler.errors.Locations; @@ -154,10 +155,12 @@ public class Builtins extends ConcreteModule { // *** Lists *** for(int arity=0;arity<=Constants.MAX_LIST_LITERAL_LENGTH;++arity) { - LIST_CONSTRUCTORS[arity] = addValue("_list_literal_" + arity + "_", + SCLValue value = addValue("_list_literal_" + arity + "_", arity == 0 ? new EmptyListConstructor() : new ListConstructor(arity) ); + value.addProperty(PrivateProperty.INSTANCE); + LIST_CONSTRUCTORS[arity] = value; } // *** Boolean *** diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/modules/SCLValue.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/modules/SCLValue.java index 408eb01e5..0d4651d48 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/modules/SCLValue.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/modules/SCLValue.java @@ -159,4 +159,11 @@ public final class SCLValue implements Typed { public String getDocumentation() { return documentation; } + + public boolean isPrivateOrDerived() { + for(SCLValueProperty property : properties) + if(property == PrivateProperty.INSTANCE || property == DerivedProperty.INSTANCE) + return true; + return false; + } } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EmptyEnvironment.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EmptyEnvironment.java new file mode 100644 index 000000000..ff751e3f3 --- /dev/null +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EmptyEnvironment.java @@ -0,0 +1,69 @@ +package org.simantics.scl.compiler.environment; + +import java.util.Collection; +import java.util.List; + +import org.simantics.scl.compiler.common.names.Name; +import org.simantics.scl.compiler.constants.Constant; +import org.simantics.scl.compiler.elaboration.modules.SCLValue; +import org.simantics.scl.compiler.elaboration.modules.TypeClass; +import org.simantics.scl.compiler.elaboration.modules.TypeClassInstance; +import org.simantics.scl.compiler.elaboration.modules.TypeDescriptor; +import org.simantics.scl.compiler.elaboration.relations.SCLEntityType; +import org.simantics.scl.compiler.elaboration.relations.SCLRelation; +import org.simantics.scl.compiler.elaboration.rules.TransformationRule; +import org.simantics.scl.compiler.internal.codegen.effects.EffectConstructor; +import org.simantics.scl.compiler.types.TCon; + +public enum EmptyEnvironment implements Environment { + INSTANCE; + + @Override + public Namespace getLocalNamespace() { + return EmptyNamespace.INSTANCE; + } + + @Override + public SCLValue getValue(Name name) { + return null; + } + + @Override + public List getFieldAccessors(String name) { + return null; + } + + @Override + public SCLRelation getRelation(Name name) { + return null; + } + + @Override + public SCLEntityType getEntityType(Name name) { + return null; + } + + @Override + public TypeDescriptor getTypeDescriptor(TCon type) { + return null; + } + + @Override + public EffectConstructor getEffectConstructor(TCon type) { + return null; + } + + @Override + public TypeClass getTypeClass(TCon type) { + return null; + } + + @Override + public Collection getInstances(TCon typeClass) { + return null; + } + + @Override + public void collectRules(Collection rules) { + } +} diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EmptyNamespace.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EmptyNamespace.java index c5cd410f4..9e86a7e9d 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EmptyNamespace.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EmptyNamespace.java @@ -1,5 +1,7 @@ package org.simantics.scl.compiler.environment; +import java.util.Collection; +import java.util.Collections; import java.util.function.Consumer; import org.simantics.scl.compiler.elaboration.chr.CHRRuleset; @@ -23,6 +25,11 @@ public enum EmptyNamespace implements Namespace { public Namespace getNamespace(String name) { return null; } + + @Override + public Collection getNamespaces() { + return Collections.emptyList(); + } @Override public SCLValue getValue(String name) throws AmbiguousNameException { diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EnvironmentFactory.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EnvironmentFactory.java index 3f714b497..911e4b28d 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EnvironmentFactory.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EnvironmentFactory.java @@ -9,6 +9,7 @@ import org.simantics.scl.compiler.module.repository.ModuleRepository; public interface EnvironmentFactory { Environment createEnvironment(CompilationContext context, ImportDeclaration[] array) throws ImportFailureException; + Environment createEnvironmentRobustly(CompilationContext context, ImportDeclaration[] array); void addBuiltinDependencies(ConcreteModule module); ModuleRepository getModuleRepository(); diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EnvironmentFactoryImpl.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EnvironmentFactoryImpl.java index 044087260..8940b7a82 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EnvironmentFactoryImpl.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/EnvironmentFactoryImpl.java @@ -12,20 +12,20 @@ import org.simantics.scl.compiler.module.repository.UpdateListener; public class EnvironmentFactoryImpl implements EnvironmentFactory { - private final ModuleRepository environment; + private final ModuleRepository repository; private final ImportDeclaration[] builtinImports; private final UpdateListener listener; - public EnvironmentFactoryImpl(ModuleRepository environment, + public EnvironmentFactoryImpl(ModuleRepository repository, ImportDeclaration[] builtinImports, UpdateListener listener) { - this.environment = environment; + this.repository = repository; this.builtinImports = builtinImports; this.listener = listener; } @Override public ModuleRepository getModuleRepository() { - return environment; + return repository; } @Override @@ -43,7 +43,25 @@ public class EnvironmentFactoryImpl implements EnvironmentFactory { acceptedBuiltinImports.toArray(new ImportDeclaration[acceptedBuiltinImports.size()]), acceptedBuiltinImports.size() + imports.length); System.arraycopy(imports, 0, is, acceptedBuiltinImports.size(), imports.length); - return environment.createEnvironment(context, is, listener); + return repository.createEnvironment(context, is, listener); + } + + @Override + public Environment createEnvironmentRobustly(CompilationContext context, ImportDeclaration[] imports) { + ArrayList acceptedBuiltinImports = + new ArrayList(builtinImports.length); + loop: for(ImportDeclaration builtinImport : builtinImports) { + for(ImportDeclaration decl : imports) + if(decl.moduleName.equals(builtinImport.moduleName) && + decl.localName.equals(builtinImport.localName)) + continue loop; + acceptedBuiltinImports.add(builtinImport); + } + ImportDeclaration[] is = Arrays.copyOf( + acceptedBuiltinImports.toArray(new ImportDeclaration[acceptedBuiltinImports.size()]), + acceptedBuiltinImports.size() + imports.length); + System.arraycopy(imports, 0, is, acceptedBuiltinImports.size(), imports.length); + return repository.createEnvironmentRobustly(context, is, listener); } @Override diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/Namespace.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/Namespace.java index e5af69204..792734c5d 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/Namespace.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/Namespace.java @@ -1,5 +1,6 @@ package org.simantics.scl.compiler.environment; +import java.util.Collection; import java.util.function.Consumer; import org.simantics.scl.compiler.elaboration.chr.CHRRuleset; @@ -24,6 +25,8 @@ public interface Namespace { */ Namespace getNamespace(String name); + Collection getNamespaces(); + /** * Get an SCLValue for a given name. The same instance is returned on each call. * @param name the name of a defined value diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/NamespaceImpl.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/NamespaceImpl.java index 214749bcb..0dd144053 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/NamespaceImpl.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/environment/NamespaceImpl.java @@ -1,6 +1,7 @@ package org.simantics.scl.compiler.environment; import java.util.ArrayList; +import java.util.Collection; import java.util.function.Consumer; import org.simantics.scl.compiler.elaboration.chr.CHRRuleset; @@ -45,6 +46,11 @@ public class NamespaceImpl implements Namespace { public Namespace getNamespace(String name) { return namespaceMap.get(name); } + + @Override + public Collection getNamespaces() { + return namespaceMap.keySet(); + } @Override public SCLValue getValue(String name) throws AmbiguousNameException { diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/declarations/DAnnotationAst.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/declarations/DAnnotationAst.java index 608f405b8..785b2c44f 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/declarations/DAnnotationAst.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/declarations/DAnnotationAst.java @@ -15,6 +15,11 @@ public class DAnnotationAst extends DeclarationAst { this.id = id; this.parameters = parameters.toArray(new Expression[parameters.size()]); } + + public DAnnotationAst(Token id, Expression ... parameters) { + this.id = id; + this.parameters = parameters; + } @Override public void toString(int indentation, StringBuilder b) { diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/parser/SCLParserImpl.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/parser/SCLParserImpl.java index 7cf1fb754..9c1a61535 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/parser/SCLParserImpl.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/parser/SCLParserImpl.java @@ -129,6 +129,10 @@ public class SCLParserImpl extends SCLParser { this.lexer = new SCLPostLexer(reader); } + public SCLPostLexer getLexer() { + return lexer; + } + public void setCompilationContext(CompilationContext context) { this.context = context; lexer.setCompilationContext(context); diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/parser/SCLPostLexer.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/parser/SCLPostLexer.java index 9598f49d5..96d3bbc06 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/parser/SCLPostLexer.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/parsing/parser/SCLPostLexer.java @@ -62,7 +62,7 @@ public class SCLPostLexer { int lineStart = 0; boolean firstTokenOfLine = true; private SCLParserOptions options; - private boolean isFirstToken = true; + public boolean isFirstToken = true; private CompilationContext context; /** diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/ConcreteModule.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/ConcreteModule.java index b92f3dff6..e52420925 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/ConcreteModule.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/ConcreteModule.java @@ -5,6 +5,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.SynchronousQueue; import java.util.function.Consumer; import org.simantics.scl.compiler.common.names.Name; @@ -249,7 +250,7 @@ public class ConcreteModule implements Module { this.values.forEachEntry(new TObjectObjectProcedure() { @Override public boolean execute(String name, SCLValue value) { - if(value.isPrivate()) + if(value.isPrivateOrDerived()) return true; String lowerPrefix = prefix.toLowerCase(); String lowerName = name.toLowerCase(); diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/repository/ModuleRepository.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/repository/ModuleRepository.java index 7c3de572f..f2e4e97ae 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/repository/ModuleRepository.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/repository/ModuleRepository.java @@ -10,6 +10,7 @@ import org.simantics.scl.compiler.common.exceptions.InternalCompilerError; import org.simantics.scl.compiler.compilation.CompilationContext; import org.simantics.scl.compiler.elaboration.modules.SCLValue; import org.simantics.scl.compiler.environment.ConcreteEnvironment; +import org.simantics.scl.compiler.environment.EmptyEnvironment; import org.simantics.scl.compiler.environment.Environment; import org.simantics.scl.compiler.environment.NamespaceImpl.ModuleImport; import org.simantics.scl.compiler.environment.NamespaceSpec; @@ -162,7 +163,7 @@ public class ModuleRepository { Collection dependencies = module.getDependencies(); THashMap moduleEntries; try { - moduleEntries = getModuleEntries(null, dependencies.toArray(new ImportDeclaration[dependencies.size()]), null); + moduleEntries = getModuleEntries(null, dependencies.toArray(new ImportDeclaration[dependencies.size()]), null, false); } catch (ImportFailureException e) { throw new InternalCompilerError(e); } @@ -259,7 +260,8 @@ public class ModuleRepository { private THashMap getModuleEntries( CompilationContext compilationContext, ImportDeclaration[] imports, - UpdateListener listener) throws ImportFailureException { + UpdateListener listener, + boolean robustly) throws ImportFailureException { THashMap result = new THashMap(); Collection failures = null; @@ -297,7 +299,7 @@ public class ModuleRepository { } } - if(failures != null) + if(failures != null && !robustly) throw new ImportFailureException(failures); return result; @@ -337,11 +339,25 @@ public class ModuleRepository { CompilationContext compilationContext, ImportDeclaration[] imports, UpdateListener listener) throws ImportFailureException { - THashMap entries = getModuleEntries(compilationContext, imports, listener); + THashMap entries = getModuleEntries(compilationContext, imports, listener, false); THashMap moduleMap = mapEntriesToModules(entries); return createEnvironment(moduleMap, imports); } + public Environment createEnvironmentRobustly( + CompilationContext compilationContext, + ImportDeclaration[] imports, + UpdateListener listener) { + try { + THashMap entries = getModuleEntries(compilationContext, imports, listener, true); + THashMap moduleMap = mapEntriesToModules(entries); + return createEnvironment(moduleMap, imports); + } catch(ImportFailureException e) { + // Should not happen because of robust flag + return EmptyEnvironment.INSTANCE; + } + } + public Environment createEnvironment( EnvironmentSpecification specification, UpdateListener listener) throws ImportFailureException { @@ -367,7 +383,7 @@ public class ModuleRepository { ImportDeclaration[] imports, ClassLoader parentClassLoader, UpdateListener listener) throws ImportFailureException { - THashMap entries = getModuleEntries(null, imports, listener); + THashMap entries = getModuleEntries(null, imports, listener, false); THashMap moduleMap = mapEntriesToModules(entries); Environment environment = createEnvironment(moduleMap, imports); THashMap runtimeModuleMap = mapEntriesToRuntimeModules(entries); diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/source/ClassModuleSource.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/source/ClassModuleSource.java index 1316ee5d7..fce9fbf4f 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/source/ClassModuleSource.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/source/ClassModuleSource.java @@ -34,7 +34,7 @@ public class ClassModuleSource extends EncodedTextualModuleSource { } @Override - protected ImportDeclaration[] getBuiltinImports(UpdateListener listener) { + public ImportDeclaration[] getBuiltinImports(UpdateListener listener) { return builtinImports; } } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/source/FileModuleSource.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/source/FileModuleSource.java index 5bdb533ba..19f50a6a9 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/source/FileModuleSource.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/source/FileModuleSource.java @@ -36,7 +36,7 @@ public class FileModuleSource extends EncodedTextualModuleSource { } @Override - protected ImportDeclaration[] getBuiltinImports(UpdateListener listener) { + public ImportDeclaration[] getBuiltinImports(UpdateListener listener) { return builtinImports; } } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/source/TextualModuleSource.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/source/TextualModuleSource.java index 50cbb1ac4..e0d1c91e4 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/source/TextualModuleSource.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/source/TextualModuleSource.java @@ -69,7 +69,7 @@ public abstract class TextualModuleSource implements ModuleSource { } } - protected ImportDeclaration[] getBuiltinImports(UpdateListener listener) { + public ImportDeclaration[] getBuiltinImports(UpdateListener listener) { return DEFAULT_IMPORTS; } diff --git a/bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/internal/BundleModuleSource.java b/bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/internal/BundleModuleSource.java index 92385a1ed..af6af0872 100644 --- a/bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/internal/BundleModuleSource.java +++ b/bundles/org.simantics.scl.osgi/src/org/simantics/scl/osgi/internal/BundleModuleSource.java @@ -54,7 +54,7 @@ public class BundleModuleSource extends EncodedTextualModuleSource implements Up } @Override - protected ImportDeclaration[] getBuiltinImports(UpdateListener listener) { + public ImportDeclaration[] getBuiltinImports(UpdateListener listener) { if(bundle.getSymbolicName().equals("org.simantics.scl.runtime")) return DEFAULT_IMPORTS_FOR_STANDARD_LIBRARY; else diff --git a/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/TestBase.java b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/TestBase.java index a662791dd..ed7b7250d 100644 --- a/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/TestBase.java +++ b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/TestBase.java @@ -95,7 +95,7 @@ public class TestBase { moduleSources[i] = new StringModuleSource( moduleNames[i], getClass().getClassLoader(), moduleTexts[i]) { @Override - protected ImportDeclaration[] getBuiltinImports(UpdateListener listener) { + public ImportDeclaration[] getBuiltinImports(UpdateListener listener) { return ImportDeclaration.ONLY_BUILTINS; } }; diff --git a/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/TestClassNaming.java b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/TestClassNaming.java index 9d501c16b..5d6d166b2 100644 --- a/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/TestClassNaming.java +++ b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/TestClassNaming.java @@ -22,7 +22,7 @@ public class TestClassNaming { } @Override - protected ImportDeclaration[] getBuiltinImports(UpdateListener listener) { + public ImportDeclaration[] getBuiltinImports(UpdateListener listener) { return new ImportDeclaration[] {new ImportDeclaration("Builtin", "")}; } } diff --git a/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/completions/SplittingExample1.scl b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/completions/SplittingExample1.scl new file mode 100644 index 000000000..27964ba12 --- /dev/null +++ b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/completions/SplittingExample1.scl @@ -0,0 +1,22 @@ +ASD ( +a +b +) + +/* +a +b +*/ + +""" +a +b +""" + +"ab"// ( + +a " + +b /* + +c \ No newline at end of file diff --git a/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/completions/TestPrefixUtil.java b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/completions/TestPrefixUtil.java new file mode 100644 index 000000000..e61d22a97 --- /dev/null +++ b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/completions/TestPrefixUtil.java @@ -0,0 +1,22 @@ +package org.simantics.scl.compiler.tests.completions; + +import org.junit.Assert; +import org.junit.Test; +import org.simantics.scl.compiler.completions.PrefixUtil; + + +public class TestPrefixUtil { + @Test + public void testFindPrefix() { + Assert.assertEquals("aaa", PrefixUtil.findPrefix("aaa aaab bbb", 7)); + Assert.assertEquals("aaa.aaa", PrefixUtil.findPrefix("aaa.aaab bbb", 7)); + Assert.assertEquals("a3a.a3a", PrefixUtil.findPrefix("a3a.a3ab bbb", 7)); + Assert.assertEquals("aaa", PrefixUtil.findPrefix("aa .aaab bbb", 7)); + } + + @Test + public void testSplitPrefix() { + Assert.assertArrayEquals(new String[] {"aa", "bb", "cc"}, PrefixUtil.splitPrefix("aa.bb.cc")); + Assert.assertArrayEquals(new String[] {"aa", "bb", ""}, PrefixUtil.splitPrefix("aa.bb.")); + } +} diff --git a/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/completions/TestRobustModuleSplitter.java b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/completions/TestRobustModuleSplitter.java new file mode 100644 index 000000000..a795754f6 --- /dev/null +++ b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/completions/TestRobustModuleSplitter.java @@ -0,0 +1,34 @@ +package org.simantics.scl.compiler.tests.completions; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.Charset; + +import org.junit.Test; +import org.simantics.scl.compiler.completions.parsing.ModuleSegment; +import org.simantics.scl.compiler.completions.parsing.RobustModuleSplitter; + +public class TestRobustModuleSplitter { + private String readSource(String name) throws IOException { + InputStreamReader reader = new InputStreamReader( + TestRobustModuleSplitter.class.getResourceAsStream(name), + Charset.forName("UTF-8")); + StringBuilder b = new StringBuilder(); + while(true) { + int c = reader.read(); + if(c < 0) + break; + b.append((char)c); + } + return b.toString(); + } + + @Test + public void example1() throws IOException { + String sourceText = readSource("SplittingExample1.scl"); + for(ModuleSegment segment : RobustModuleSplitter.split(sourceText)) { + System.out.println("---- ("+segment.parenthesesBalance+") ----------------------------------------------"); + System.out.println(sourceText.substring(segment.begin, segment.end).trim()); + } + } +}