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,
import gnu.trove.map.hash.THashMap;
public class DeclarationClassification {
- ArrayList<ImportDeclaration> importsAst = new ArrayList<ImportDeclaration>();
- ArrayList<DDataAst> dataTypesAst = new ArrayList<DDataAst>();
- ArrayList<DTypeAst> typeAliasesAst = new ArrayList<DTypeAst>();
- ValueRepository valueDefinitionsAst = new ValueRepository();
- RelationRepository relationDefinitionsAst = new RelationRepository();
- ArrayList<DValueTypeAst> typeAnnotationsAst = new ArrayList<DValueTypeAst>();
- ArrayList<DFixityAst> fixityAst = new ArrayList<DFixityAst>();
- ArrayList<ProcessedDClassAst> typeClassesAst = new ArrayList<ProcessedDClassAst>();
- ArrayList<ProcessedDInstanceAst> instancesAst = new ArrayList<ProcessedDInstanceAst>();
- ArrayList<DDerivingInstanceAst> derivingInstancesAst = new ArrayList<DDerivingInstanceAst>();
- ArrayList<DEffectAst> effectsAst = new ArrayList<DEffectAst>();
- ArrayList<DRuleAst> rulesAst = new ArrayList<DRuleAst>();
- ArrayList<DMappingRelationAst> mappingRelationsAst = new ArrayList<DMappingRelationAst>();
- ArrayList<DRulesetAst> rulesetsAst = new ArrayList<DRulesetAst>();
+ public ArrayList<ImportDeclaration> importsAst = new ArrayList<ImportDeclaration>();
+ public ArrayList<DDataAst> dataTypesAst = new ArrayList<DDataAst>();
+ public ArrayList<DTypeAst> typeAliasesAst = new ArrayList<DTypeAst>();
+ public ValueRepository valueDefinitionsAst = new ValueRepository();
+ public RelationRepository relationDefinitionsAst = new RelationRepository();
+ public ArrayList<DValueTypeAst> typeAnnotationsAst = new ArrayList<DValueTypeAst>();
+ public ArrayList<DFixityAst> fixityAst = new ArrayList<DFixityAst>();
+ public ArrayList<ProcessedDClassAst> typeClassesAst = new ArrayList<ProcessedDClassAst>();
+ public ArrayList<ProcessedDInstanceAst> instancesAst = new ArrayList<ProcessedDInstanceAst>();
+ public ArrayList<DDerivingInstanceAst> derivingInstancesAst = new ArrayList<DDerivingInstanceAst>();
+ public ArrayList<DEffectAst> effectsAst = new ArrayList<DEffectAst>();
+ public ArrayList<DRuleAst> rulesAst = new ArrayList<DRuleAst>();
+ public ArrayList<DMappingRelationAst> mappingRelationsAst = new ArrayList<DMappingRelationAst>();
+ public ArrayList<DRulesetAst> rulesetsAst = new ArrayList<DRulesetAst>();
- THashMap<String, DDocumentationAst> valueDocumentation = new THashMap<String, DDocumentationAst>();
- THashMap<String, DDocumentationAst> relationDocumentation = new THashMap<String, DDocumentationAst>();
- THashMap<String, DDocumentationAst> typeDocumentation = new THashMap<String, DDocumentationAst>();
- THashMap<String, DDocumentationAst> classDocumentation = new THashMap<String, DDocumentationAst>();
+ public THashMap<String, DDocumentationAst> valueDocumentation = new THashMap<String, DDocumentationAst>();
+ public THashMap<String, DDocumentationAst> relationDocumentation = new THashMap<String, DDocumentationAst>();
+ public THashMap<String, DDocumentationAst> typeDocumentation = new THashMap<String, DDocumentationAst>();
+ public THashMap<String, DDocumentationAst> classDocumentation = new THashMap<String, DDocumentationAst>();
- ArrayList<JavaMethodDeclaration> javaMethodDeclarations = new ArrayList<JavaMethodDeclaration>();
+ public ArrayList<JavaMethodDeclaration> javaMethodDeclarations = new ArrayList<JavaMethodDeclaration>();
- StringBuilder moduleDocumentation = new StringBuilder();
+ public StringBuilder moduleDocumentation = new StringBuilder();
- ArrayList<DAnnotationAst> currentAnnotations = new ArrayList<DAnnotationAst>(2);
- DDocumentationAst documentation;
- String inJavaClass;
+ public ArrayList<DAnnotationAst> currentAnnotations = new ArrayList<DAnnotationAst>(2);
+ public DDocumentationAst documentation;
+ public String inJavaClass;
- ArrayList<DAnnotationAst> defaultAnnotations = new ArrayList<DAnnotationAst>();
+ public ArrayList<DAnnotationAst> defaultAnnotations = new ArrayList<DAnnotationAst>();
- ErrorLog errorLog;
+ public ErrorLog errorLog;
public DeclarationClassification(CompilationContext compilationContext) {
this.errorLog = compilationContext.errorLog;
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;
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;
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;
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
compilationContext.namingPolicy = new JavaNamingPolicy(moduleName);
}
- private ArrayList<ImportDeclaration> processRelativeImports(ArrayList<ImportDeclaration> relativeImports) {
+ public static ArrayList<ImportDeclaration> processRelativeImports(ErrorLog errorLog, String moduleName, ArrayList<ImportDeclaration> relativeImports) {
ArrayList<ImportDeclaration> absoluteImports = new ArrayList<ImportDeclaration>(relativeImports.size());
for(ImportDeclaration relativeImport : relativeImports) {
if(relativeImport.moduleName.startsWith(".")) {
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
method.setDefaultImplementation(Name.create(moduleName, fullName));
valueDefinitionsAst.addDefinitions(fullName, defs);
- /*valueDefinitionsAst.addAnnotation(fullName, new DAnnotationAst(new EVar("@private"),
- Collections.<Expression>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);
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;
public Namespace getNamespace(String name) {
return base.getNamespace(name);
}
+
+ @Override
+ public Collection<String> getNamespaces() {
+ return base.getNamespaces();
+ }
@Override
public SCLValue getValue(String name) throws AmbiguousNameException {
--- /dev/null
+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 + ")" : "") ;
+ }
+}
--- /dev/null
+package org.simantics.scl.compiler.completions;
+
+import java.util.Comparator;
+
+public class CompletionComparator implements Comparator<Completion> {
+
+ @Override
+ public int compare(Completion o1, Completion o2) {
+ return o1.name.toLowerCase().compareTo(o2.name.toLowerCase());
+ }
+
+}
--- /dev/null
+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);
+ }
+}
--- /dev/null
+package org.simantics.scl.compiler.completions;
+
+public enum CompletionType {
+ Value,
+ Type,
+ Namespace
+}
--- /dev/null
+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<Completion> findCompletions(CompletionRequest request) {
+ //LOGGER.debug("findCompletions");
+ AtomicInteger maxNumberOfCompletions = new AtomicInteger(request.maxNumberOfCompletions);
+ ArrayList<Completion> completions = new ArrayList<Completion>();
+
+ 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<String> valuesAlreadyAdded = new THashSet<String>();
+ 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<ImportDeclaration> 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<String> namespacesAlreadyAdded = new THashSet<String>();
+ 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<SCLValue>() {
+ @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, "");
+ }
+
+}
--- /dev/null
+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<prefix.length();++i)
+ if(prefix.charAt(i) == '.')
+ ++partCount;
+ String[] result = new String[partCount];
+ int partId = 0;
+ int begin = 0;
+ for(int i=0;i<prefix.length();++i)
+ if(prefix.charAt(i) == '.') {
+ result[partId++] = prefix.substring(begin, i);
+ begin = i+1;
+ }
+ result[partId] = prefix.substring(begin);
+ return result;
+ }
+}
--- /dev/null
+package org.simantics.scl.compiler.completions.parsing;
+
+public class ModuleSegment {
+ public final int begin;
+ public final int end;
+ public int parenthesesBalance;
+ public boolean hasErrors;
+
+ public ModuleSegment(int begin, int end, int parenthesesBalance, boolean hasErrors) {
+ this.begin = begin;
+ this.end = end;
+ this.parenthesesBalance = parenthesesBalance;
+ this.hasErrors = hasErrors;
+ }
+}
--- /dev/null
+package org.simantics.scl.compiler.completions.parsing;
+
+import java.io.Reader;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.simantics.scl.compiler.compilation.CompilationContext;
+import org.simantics.scl.compiler.compilation.DeclarationClassification;
+import org.simantics.scl.compiler.internal.parsing.declarations.DeclarationAst;
+import org.simantics.scl.compiler.internal.parsing.parser.SCLParserImpl;
+import org.simantics.scl.compiler.internal.parsing.parser.SCLParserOptions;
+
+public class RobustModuleParser {
+
+ private static List<DeclarationAst> 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<DeclarationAst> result = (List<DeclarationAst>)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;
+ }
+}
--- /dev/null
+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<ModuleSegment> segments = new ArrayList<ModuleSegment>();
+
+ 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<ModuleSegment> segmentStack = null;
+ for(ModuleSegment segment : segments)
+ if(segment.parenthesesBalance > 0) {
+ if(segmentStack == null)
+ segmentStack = new ArrayList<ModuleSegment>();
+ for(int i=0;i<segment.parenthesesBalance;++i)
+ segmentStack.add(segment);
+ }
+ else if(segment.parenthesesBalance < 0) {
+ if(segmentStack == null) {
+ segment.parenthesesBalance = 0;
+ segment.hasErrors = true;
+ }
+ else {
+ int r = -segment.parenthesesBalance;
+ while(r > 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<ModuleSegment> oldSegments = segments;
+ segments = new ArrayList<ModuleSegment>(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<ModuleSegment> split(String sourceText) {
+ RobustModuleSplitter splitter = new RobustModuleSplitter(sourceText);
+ splitter.split();
+ splitter.combineByParenthesesBalance();
+ return splitter.segments;
+ }
+}
+
--- /dev/null
+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() {
+ }
+}
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;
// *** 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 ***
public String getDocumentation() {
return documentation;
}
+
+ public boolean isPrivateOrDerived() {
+ for(SCLValueProperty property : properties)
+ if(property == PrivateProperty.INSTANCE || property == DerivedProperty.INSTANCE)
+ return true;
+ return false;
+ }
}
--- /dev/null
+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<Constant> 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<TypeClassInstance> getInstances(TCon typeClass) {
+ return null;
+ }
+
+ @Override
+ public void collectRules(Collection<TransformationRule> rules) {
+ }
+}
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;
public Namespace getNamespace(String name) {
return null;
}
+
+ @Override
+ public Collection<String> getNamespaces() {
+ return Collections.emptyList();
+ }
@Override
public SCLValue getValue(String name) throws AmbiguousNameException {
public interface EnvironmentFactory {
Environment createEnvironment(CompilationContext context, ImportDeclaration[] array) throws ImportFailureException;
+ Environment createEnvironmentRobustly(CompilationContext context, ImportDeclaration[] array);
void addBuiltinDependencies(ConcreteModule module);
ModuleRepository getModuleRepository();
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
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<ImportDeclaration> acceptedBuiltinImports =
+ new ArrayList<ImportDeclaration>(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
package org.simantics.scl.compiler.environment;
+import java.util.Collection;
import java.util.function.Consumer;
import org.simantics.scl.compiler.elaboration.chr.CHRRuleset;
*/
Namespace getNamespace(String name);
+ Collection<String> getNamespaces();
+
/**
* Get an SCLValue for a given name. The same instance is returned on each call.
* @param name the name of a defined value
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;
public Namespace getNamespace(String name) {
return namespaceMap.get(name);
}
+
+ @Override
+ public Collection<String> getNamespaces() {
+ return namespaceMap.keySet();
+ }
@Override
public SCLValue getValue(String name) throws AmbiguousNameException {
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) {
this.lexer = new SCLPostLexer(reader);
}
+ public SCLPostLexer getLexer() {
+ return lexer;
+ }
+
public void setCompilationContext(CompilationContext context) {
this.context = context;
lexer.setCompilationContext(context);
int lineStart = 0;
boolean firstTokenOfLine = true;
private SCLParserOptions options;
- private boolean isFirstToken = true;
+ public boolean isFirstToken = true;
private CompilationContext context;
/**
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;
this.values.forEachEntry(new TObjectObjectProcedure<String,SCLValue>() {
@Override
public boolean execute(String name, SCLValue value) {
- if(value.isPrivate())
+ if(value.isPrivateOrDerived())
return true;
String lowerPrefix = prefix.toLowerCase();
String lowerName = name.toLowerCase();
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;
Collection<ImportDeclaration> dependencies = module.getDependencies();
THashMap<String, ModuleEntry> 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);
}
private THashMap<String, ModuleEntry> getModuleEntries(
CompilationContext compilationContext,
ImportDeclaration[] imports,
- UpdateListener listener) throws ImportFailureException {
+ UpdateListener listener,
+ boolean robustly) throws ImportFailureException {
THashMap<String, ModuleEntry> result = new THashMap<String, ModuleEntry>();
Collection<ImportFailure> failures = null;
}
}
- if(failures != null)
+ if(failures != null && !robustly)
throw new ImportFailureException(failures);
return result;
CompilationContext compilationContext,
ImportDeclaration[] imports,
UpdateListener listener) throws ImportFailureException {
- THashMap<String, ModuleEntry> entries = getModuleEntries(compilationContext, imports, listener);
+ THashMap<String, ModuleEntry> entries = getModuleEntries(compilationContext, imports, listener, false);
THashMap<String, Module> moduleMap = mapEntriesToModules(entries);
return createEnvironment(moduleMap, imports);
}
+ public Environment createEnvironmentRobustly(
+ CompilationContext compilationContext,
+ ImportDeclaration[] imports,
+ UpdateListener listener) {
+ try {
+ THashMap<String, ModuleEntry> entries = getModuleEntries(compilationContext, imports, listener, true);
+ THashMap<String, Module> 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 {
ImportDeclaration[] imports,
ClassLoader parentClassLoader,
UpdateListener listener) throws ImportFailureException {
- THashMap<String, ModuleEntry> entries = getModuleEntries(null, imports, listener);
+ THashMap<String, ModuleEntry> entries = getModuleEntries(null, imports, listener, false);
THashMap<String, Module> moduleMap = mapEntriesToModules(entries);
Environment environment = createEnvironment(moduleMap, imports);
THashMap<String, RuntimeModule> runtimeModuleMap = mapEntriesToRuntimeModules(entries);
}
@Override
- protected ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
+ public ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
return builtinImports;
}
}
}
@Override
- protected ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
+ public ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
return builtinImports;
}
}
}
}
- protected ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
+ public ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
return DEFAULT_IMPORTS;
}
}
@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
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;
}
};
}
@Override
- protected ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
+ public ImportDeclaration[] getBuiltinImports(UpdateListener listener) {
return new ImportDeclaration[] {new ImportDeclaration("Builtin", "")};
}
}
--- /dev/null
+ASD (
+a
+b
+)
+
+/*
+a
+b
+*/
+
+"""
+a
+b
+"""
+
+"ab"// (
+
+a "
+
+b /*
+
+c
\ No newline at end of file
--- /dev/null
+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."));
+ }
+}
--- /dev/null
+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());
+ }
+ }
+}