package org.simantics.scl.compiler.compilation; import java.util.ArrayList; import java.util.Arrays; import org.simantics.scl.compiler.common.exceptions.InternalCompilerError; import org.simantics.scl.compiler.constants.StringConstant; import org.simantics.scl.compiler.elaboration.errors.NotPatternException; import org.simantics.scl.compiler.elaboration.expressions.ELiteral; import org.simantics.scl.compiler.elaboration.expressions.EVar; import org.simantics.scl.compiler.elaboration.expressions.Expression; import org.simantics.scl.compiler.elaboration.java.JavaMethodDeclaration; import org.simantics.scl.compiler.errors.ErrorLog; import org.simantics.scl.compiler.errors.Locations; import org.simantics.scl.compiler.internal.parsing.Token; 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.DDataAst; import org.simantics.scl.compiler.internal.parsing.declarations.DDerivingInstanceAst; import org.simantics.scl.compiler.internal.parsing.declarations.DDocumentationAst; import org.simantics.scl.compiler.internal.parsing.declarations.DEffectAst; import org.simantics.scl.compiler.internal.parsing.declarations.DFixityAst; import org.simantics.scl.compiler.internal.parsing.declarations.DImportJavaAst; import org.simantics.scl.compiler.internal.parsing.declarations.DInstanceAst; import org.simantics.scl.compiler.internal.parsing.declarations.DMappingRelationAst; import org.simantics.scl.compiler.internal.parsing.declarations.DRelationAst; import org.simantics.scl.compiler.internal.parsing.declarations.DRuleAst; import org.simantics.scl.compiler.internal.parsing.declarations.DRulesetAst; 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.declarations.DeclarationAst; 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; import org.simantics.scl.compiler.internal.parsing.translation.ValueRepository; 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(); THashMap valueDocumentation = new THashMap(); THashMap relationDocumentation = new THashMap(); THashMap typeDocumentation = new THashMap(); THashMap classDocumentation = new THashMap(); ArrayList javaMethodDeclarations = new ArrayList(); StringBuilder moduleDocumentation = new StringBuilder(); ArrayList currentAnnotations = new ArrayList(2); DDocumentationAst documentation; String inJavaClass; ArrayList defaultAnnotations = new ArrayList(); ErrorLog errorLog; public DeclarationClassification(CompilationContext compilationContext) { this.errorLog = compilationContext.errorLog; } public void handle(DeclarationAst declaration) { if(declaration instanceof DValueAst) handle((DValueAst)declaration); else if(declaration instanceof DValueTypeAst) handle((DValueTypeAst)declaration); else if(declaration instanceof DDataAst) handle((DDataAst)declaration); else if(declaration instanceof DTypeAst) handle((DTypeAst)declaration); else if(declaration instanceof DInstanceAst) handle((DInstanceAst)declaration); else if(declaration instanceof DDerivingInstanceAst) handle((DDerivingInstanceAst)declaration); else if(declaration instanceof ImportDeclaration) handle((ImportDeclaration)declaration); else if(declaration instanceof DClassAst) handle((DClassAst)declaration); else if(declaration instanceof DFixityAst) handle((DFixityAst)declaration); else if(declaration instanceof DDocumentationAst) handle((DDocumentationAst)declaration); else if(declaration instanceof DAnnotationAst) handle((DAnnotationAst)declaration); else if(declaration instanceof DEffectAst) handle((DEffectAst)declaration); else if(declaration instanceof DImportJavaAst) handle((DImportJavaAst)declaration); else if(declaration instanceof DRuleAst) handle((DRuleAst)declaration); else if(declaration instanceof DMappingRelationAst) handle((DMappingRelationAst)declaration); else if(declaration instanceof DRelationAst) handle((DRelationAst)declaration); else if(declaration instanceof DRulesetAst) handle((DRulesetAst)declaration); else throw new InternalCompilerError("Unknown declaration " + declaration.getClass().getSimpleName()); } public void handle(DFixityAst declaration) { if(!currentAnnotations.isEmpty()) { errorLog.log(declaration.location, "Annotations not supported."); currentAnnotations = new ArrayList(2); } fixityAst.add(declaration); } public void handle(DEffectAst declaration) { if(!currentAnnotations.isEmpty()) { errorLog.log(declaration.location, "Annotations not supported."); currentAnnotations = new ArrayList(2); } if(documentation != null) documentation = null; effectsAst.add(declaration); } public void handle(DClassAst declaration) { // TODO errorLog.log(declaration, "Class " + declaration.name + " has already been defined in this module."); if(documentation != null) { String name = declaration.name; addClassDocumentation(name, documentation); documentation = null; } ArrayList typeDeclarations = new ArrayList(); ValueRepository defaultValues = new ValueRepository(); DDocumentationAst curDocumentation = null; for(DeclarationAst decl : declaration.declarations) { if(decl instanceof DValueTypeAst) { DValueTypeAst valueType = (DValueTypeAst)decl; for(EVar name : valueType.names) valueDocumentation.put(name.name, curDocumentation); curDocumentation = null; typeDeclarations.add(valueType); } else if(decl instanceof DValueAst) { curDocumentation = null; try { defaultValues.add((DValueAst)decl); } catch (NotPatternException e) { errorLog.log(e.getExpression().location, "Not a pattern."); } } else if(decl instanceof DDocumentationAst) { DDocumentationAst documentation = (DDocumentationAst)decl; if(curDocumentation != null) { errorLog.log(documentation.location, "Invalid documentation string. It precedes another documentation string."); } curDocumentation = documentation; } else errorLog.log(decl.location, "Invalid declaration under class definition."); } if(!currentAnnotations.isEmpty()) { declaration.setAnnotations(currentAnnotations); currentAnnotations = new ArrayList(2); } typeClassesAst.add(new ProcessedDClassAst(declaration, typeDeclarations, defaultValues)); } public void handle(ImportDeclaration declaration) { if(!currentAnnotations.isEmpty()) { errorLog.log(declaration.location, "Annotations not supported."); currentAnnotations = new ArrayList(2); } importsAst.add(declaration); } public void handle(DImportJavaAst declaration) { if(!currentAnnotations.isEmpty()) { for(DAnnotationAst annotation : currentAnnotations) { String name = annotation.id.text; if(name.equals("@private")) { defaultAnnotations.add(annotation); } else errorLog.log(declaration.location, "The importJava declaration does not support annotation " + name + "."); } currentAnnotations = new ArrayList(2); } try { inJavaClass = declaration.className; for(DeclarationAst innerDeclaration : declaration.declarations) handle(innerDeclaration); } finally { defaultAnnotations.clear(); inJavaClass = null; } } public void handle(DInstanceAst declaration) { ValueRepository valueDefs = new ValueRepository(); ArrayList localAnnotations = new ArrayList(2); for(DeclarationAst decl : declaration.declarations) { if(decl instanceof DValueAst) { try { String name = valueDefs.add((DValueAst)decl); if(!localAnnotations.isEmpty()) { valueDefs.addAnnotations(name, localAnnotations); localAnnotations = new ArrayList(2); } } catch (NotPatternException e) { errorLog.log(e.getExpression().location, "Not a pattern."); } } else if(decl instanceof DAnnotationAst) localAnnotations.add((DAnnotationAst)decl); else errorLog.log(decl.location, "Invalid declaration under instance definition."); } for(DAnnotationAst decl : localAnnotations) errorLog.log(decl.location, "Annotation is at invalid location."); if(!currentAnnotations.isEmpty()) { errorLog.log(declaration.location, "Annotations not supported."); currentAnnotations = new ArrayList(2); } if(declaration.name.name.equals("Eq") || declaration.name.name.equals("Hashable")) { errorLog.logWarning(declaration.location, "Skipped instance definition for " + declaration.name + " for " + declaration.types[0]); return; } instancesAst.add(new ProcessedDInstanceAst( declaration, valueDefs)); } public void handle(DDerivingInstanceAst declaration) { if(!currentAnnotations.isEmpty()) { errorLog.log(declaration.location, "Annotations not supported."); currentAnnotations = new ArrayList(2); } if(declaration.name.name.equals("Eq") || declaration.name.name.equals("Hashable")) { errorLog.logWarning(declaration.location, "Skipped instance definition for " + declaration.name + " for " + declaration.types[0]); return; } derivingInstancesAst.add(declaration); } public void handle(DTypeAst declaration) { if(!currentAnnotations.isEmpty()) { errorLog.log(declaration.location, "Annotations not supported."); currentAnnotations = new ArrayList(2); } typeAliasesAst.add(declaration); } public void handle(DDataAst declaration) { if(documentation != null) { String name = declaration.name; addTypeDocumentation(name, documentation); documentation = null; } if(inJavaClass != null) currentAnnotations.add(new DAnnotationAst(new Token(0, Locations.NO_LOCATION, "@JavaType"), Arrays.asList(new ELiteral(new StringConstant(inJavaClass))))); if(!currentAnnotations.isEmpty()) { declaration.setAnnotations(currentAnnotations); currentAnnotations = new ArrayList(2); } dataTypesAst.add(declaration); } public void handle(DValueTypeAst declaration) { if(documentation != null) { for(EVar name : declaration.names) { addValueDocumentation(name.name, documentation); } documentation = null; } if(inJavaClass != null) { for(EVar name : declaration.names) javaMethodDeclarations.add(new JavaMethodDeclaration( declaration.location, inJavaClass, name, declaration.type)); } else typeAnnotationsAst.add(declaration); if(!currentAnnotations.isEmpty()) { for(EVar name : declaration.names) valueDefinitionsAst.addAnnotations(name.name, currentAnnotations); currentAnnotations = new ArrayList(2); } if(!defaultAnnotations.isEmpty()) { for(EVar name : declaration.names) valueDefinitionsAst.addAnnotations(name.name, defaultAnnotations); } } public void handle(DValueAst declaration) { String name; try { name = valueDefinitionsAst.add(declaration); } catch (NotPatternException e) { errorLog.log(e.getExpression().location, "Illegal left hand side of the definition."); return; } if(documentation != null) { addValueDocumentation(name, documentation); documentation = null; } if(!currentAnnotations.isEmpty()) { valueDefinitionsAst.addAnnotations(name, currentAnnotations); currentAnnotations = new ArrayList(2); } } public void handle(DRelationAst declaration) { String name; try { name = relationDefinitionsAst.add(declaration); } catch (NotPatternException e) { errorLog.log(e.getExpression().location, "Not a pattern."); return; } if(documentation != null) { addRelationDocumentation(name, documentation); documentation = null; } if(!currentAnnotations.isEmpty()) { relationDefinitionsAst.addAnnotations(name, currentAnnotations); currentAnnotations = new ArrayList(2); } } public void handle(DRulesetAst declaration) { if(documentation != null) { declaration.documentation = documentation; documentation = null; } rulesetsAst.add(declaration); } public void handle(DDocumentationAst declaration) { if(documentation != null) { errorLog.log(documentation.location, "Invalid documentation string. It precedes another documentation string."); } documentation = declaration; } public void handle(DAnnotationAst declaration) { if(declaration.id.text.equals("@documentation")) { if(declaration.parameters.length != 1) { errorLog.log(documentation.location, "One parameter, a documentation string, expected after @documentation."); return; } if(!(declaration.parameters[0] instanceof ELiteral)) { errorLog.log(documentation.location, "A documentation string expected after @documentation."); return; } ELiteral lit = (ELiteral)declaration.parameters[0]; if(!(lit.getValue() instanceof StringConstant)) { errorLog.log(documentation.location, "A documentation string expected after @documentation."); return; } String text = ((StringConstant)lit.getValue()).getValue(); moduleDocumentation.append(text); moduleDocumentation.append("\n\n"); } else currentAnnotations.add(declaration); } public void handle(DRuleAst declaration) { rulesAst.add(declaration); } public void handle(DMappingRelationAst declaration) { mappingRelationsAst.add(declaration); } public void addValueDocumentation(String valueName, DDocumentationAst documentation) { DDocumentationAst oldDoc = valueDocumentation.put(valueName, documentation); if(oldDoc != null) { errorLog.log(oldDoc.location, "Multiple documentation strings given to " + valueName + "."); } } public void addRelationDocumentation(String relationName, DDocumentationAst documentation) { DDocumentationAst oldDoc = relationDocumentation.put(relationName, documentation); if(oldDoc != null) { errorLog.log(oldDoc.location, "Multiple documentation strings given to " + relationName + "."); } } public void addTypeDocumentation(String valueName, DDocumentationAst documentation) { DDocumentationAst oldDoc = typeDocumentation.put(valueName, documentation); if(oldDoc != null) { errorLog.log(oldDoc.location, "Multiple documentation strings given to the same type."); } } public void addClassDocumentation(String valueName, DDocumentationAst documentation) { DDocumentationAst oldDoc = classDocumentation.put(valueName, documentation); if(oldDoc != null) { errorLog.log(oldDoc.location, "Multiple documentation strings given to the same class."); } } }