package me.tomassetti.turin.resolvers; import me.tomassetti.jvm.JvmMethodDefinition; import me.tomassetti.jvm.JvmNameUtils; import me.tomassetti.turin.compiler.errorhandling.SemanticErrorException; import me.tomassetti.turin.definitions.ContextDefinition; import me.tomassetti.turin.definitions.TypeDefinition; import me.tomassetti.turin.parser.ast.Node; import me.tomassetti.turin.parser.ast.context.ContextDefinitionNode; import me.tomassetti.turin.parser.ast.expressions.ActualParam; import me.tomassetti.turin.parser.ast.expressions.Expression; import me.tomassetti.turin.parser.ast.expressions.FunctionCall; import me.tomassetti.turin.parser.ast.expressions.StaticFieldAccess; import me.tomassetti.turin.parser.ast.imports.ImportDeclaration; import me.tomassetti.turin.parser.ast.invokables.FunctionDefinitionNode; import me.tomassetti.turin.parser.ast.properties.PropertyDefinition; import me.tomassetti.turin.parser.ast.properties.PropertyReference; import me.tomassetti.turin.symbols.Symbol; import me.tomassetti.turin.typesystem.UnsignedPrimitiveTypeUsage; import me.tomassetti.turin.typesystem.PrimitiveTypeUsage; import me.tomassetti.turin.typesystem.ReferenceTypeUsage; import me.tomassetti.turin.typesystem.TypeUsage; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; /** * Resolve symbols by looking in the file where the context node is contained. */ public class InFileSymbolResolver implements SymbolResolver { private TypeResolver typeResolver; @Override public String toString() { return "InFileSymbolResolver{" + "typeResolver=" + typeResolver + ", parent=" + parent + '}'; } private SymbolResolver parent = null; @Override public SymbolResolver getParent() { return parent; } @Override public void setParent(SymbolResolver parent) { this.parent = parent; } public InFileSymbolResolver(TypeResolver typeResolver) { this.typeResolver = typeResolver; this.typeResolver.setSymbolResolver(this); } @Override public Optional<PropertyDefinition> findDefinition(PropertyReference propertyReference) { return findDefinitionIn(propertyReference, propertyReference.getParent()); } private Optional<PropertyDefinition> findDefinitionIn(PropertyReference propertyReference, Node context) { for (Node child : context.getChildren()) { if (child instanceof PropertyDefinition) { PropertyDefinition propertyDefinition = (PropertyDefinition)child; if (propertyDefinition.getName().equals(propertyReference.getName())) { return Optional.of(propertyDefinition); } } } if (context.getParent() == null) { return Optional.empty(); } return findDefinitionIn(propertyReference, context.getParent()); } @Override public Optional<TypeDefinition> findTypeDefinitionIn(String typeName, Node context, SymbolResolver resolver) { // primitive names are not valid here if (!JvmNameUtils.isValidQualifiedName(typeName)) { throw new IllegalArgumentException(typeName); } return findTypeDefinitionInHelper(typeName, context, null, resolver); } @Override public Optional<TypeUsage> findTypeUsageIn(String typeName, Node context, SymbolResolver resolver) { if (PrimitiveTypeUsage.isPrimitiveTypeName(typeName)){ return Optional.of(PrimitiveTypeUsage.getByName(typeName)); } // note that this check has to come after the check for primitive types if (!JvmNameUtils.isValidQualifiedName(typeName)) { throw new IllegalArgumentException(typeName); } // Note that our Turin basic types could shadow other types Optional<UnsignedPrimitiveTypeUsage> basicType = UnsignedPrimitiveTypeUsage.findByName(typeName); if (basicType.isPresent()) { return Optional.of(basicType.get()); } Optional<TypeDefinition> typeDefinition = findTypeDefinitionIn(typeName, context, resolver.getRoot()); if (typeDefinition.isPresent()) { ReferenceTypeUsage ref = new ReferenceTypeUsage(typeDefinition.get()); return Optional.of(ref); } else { return Optional.empty(); } } @Override public Optional<JvmMethodDefinition> findJvmDefinition(FunctionCall functionCall) { List<ActualParam> argsTypes = functionCall.getActualParamValuesInOrder().stream() .map((e) -> { ActualParam ap = new ActualParam (e); ap.setParent(functionCall); return ap; }) .collect(Collectors.toList()); Expression function = functionCall.getFunction(); boolean staticContext = function.isType(this) || (function instanceof StaticFieldAccess); return Optional.of(function.findMethodFor(argsTypes, this, staticContext)); } @Override public Optional<Symbol> findSymbol(String name, Node context) { if (context == null) { Optional<TypeDefinition> typeDefinition = typeResolver.resolveAbsoluteTypeName(name); if (typeDefinition.isPresent()) { return Optional.of(typeDefinition.get()); } Optional<FunctionDefinitionNode> functionDefinition = typeResolver.resolveAbsoluteFunctionName(name); if (functionDefinition.isPresent()) { return Optional.of(functionDefinition.get()); } return Optional.empty(); } else { return context.findSymbol(name, this); } } @Override public boolean existPackage(String packageName) { return typeResolver.existPackage(packageName); } @Override public Optional<ContextDefinition> findContextSymbol(String contextName, Node context) { return findContextSymbolHelper(contextName, context, null); } public Optional<ContextDefinition> findContextSymbolHelper(String contextName, Node context, Node previousContext) { if (!JvmNameUtils.isValidQualifiedName(contextName)) { throw new IllegalArgumentException(contextName); } if (context == null) { return Optional.empty(); } for (Node child : context.getChildren()) { if (child instanceof ContextDefinitionNode) { ContextDefinitionNode contextDefinition = (ContextDefinitionNode)child; if (contextDefinition.getName().equals(contextName) || contextDefinition.getQualifiedName().equals(contextName)) { return Optional.of(contextDefinition); } } else if (child instanceof ImportDeclaration) { // this is necessary to avoid infinite recursion if (child != previousContext) { ImportDeclaration importDeclaration = (ImportDeclaration) child; Optional<Symbol> resolvedNode = importDeclaration.findAmongImported(contextName, this.getRoot()); if (resolvedNode.isPresent()) { if (resolvedNode.get() instanceof ContextDefinition) { return Optional.of((ContextDefinition) resolvedNode.get()); } else { throw new SemanticErrorException(context, "" + contextName + " is not a context"); } } } } } if (!context.contextName().isEmpty()) { String qName = context.contextName() + "." + contextName; Optional<ContextDefinition> partial = getRoot().findContextSymbol(qName, null); if (partial.isPresent()) { return partial; } } return findContextSymbolHelper(contextName, context.getParent(), context); } private Optional<TypeDefinition> findTypeDefinitionInHelper(String typeName, Node context, Node previousContext, SymbolResolver resolver) { if (!JvmNameUtils.isValidQualifiedName(typeName)) { throw new IllegalArgumentException(typeName); } if (context == null) { // implicitly look into java.lang package Optional<TypeDefinition> result = typeResolver.resolveAbsoluteTypeName("java.lang." + typeName); if (result.isPresent()) { return result; } return typeResolver.resolveAbsoluteTypeName(typeName); } for (Node child : context.getChildren()) { if (child instanceof TypeDefinition) { TypeDefinition typeDefinition = (TypeDefinition)child; if (typeDefinition.getName().equals(typeName) || typeDefinition.getQualifiedName().equals(typeName)) { return Optional.of(typeDefinition); } } else if (child instanceof ImportDeclaration) { // this is necessary to avoid infinite recursion if (child != previousContext) { ImportDeclaration importDeclaration = (ImportDeclaration) child; Optional<Symbol> resolvedNode = importDeclaration.findAmongImported(typeName, resolver); if (resolvedNode.isPresent()) { if (resolvedNode.get() instanceof TypeDefinition) { return Optional.of((TypeDefinition) resolvedNode.get()); } else { throw new SemanticErrorException(context, "" + typeName + " is not a type"); } } } } } if (!context.contextName().isEmpty()) { String qName = context.contextName() + "." + typeName; Optional<TypeDefinition> partial = getRoot().findTypeDefinitionIn(qName, null, getRoot()); if (partial.isPresent()) { return partial; } } return findTypeDefinitionInHelper(typeName, context.getParent(), context, resolver); } }