diff --git a/src/main/java/raylras/zen/code/CompilationEnvironment.java b/src/main/java/raylras/zen/code/CompilationEnvironment.java index 6a9ce720..fb2966f2 100644 --- a/src/main/java/raylras/zen/code/CompilationEnvironment.java +++ b/src/main/java/raylras/zen/code/CompilationEnvironment.java @@ -13,6 +13,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.*; +import java.util.function.Function; import java.util.stream.Collectors; public class CompilationEnvironment { @@ -67,6 +68,13 @@ public Map getClassTypeMap() { .map(symbol -> (ClassSymbol) symbol) .collect(Collectors.toMap(ClassSymbol::getQualifiedName, ClassSymbol::getType)); } + public Map getClassSymbolMap() { + return getUnits().stream() + .flatMap(unit -> unit.getTopLevelSymbols().stream()) + .filter(symbol -> symbol instanceof ClassSymbol) + .map(symbol -> (ClassSymbol) symbol) + .collect(Collectors.toMap(ClassSymbol::getQualifiedName, Function.identity())); + } public Path getRoot() { return root; diff --git a/src/main/java/raylras/zen/code/NonRecursionVisitor.java b/src/main/java/raylras/zen/code/NonRecursionVisitor.java new file mode 100644 index 00000000..9f360357 --- /dev/null +++ b/src/main/java/raylras/zen/code/NonRecursionVisitor.java @@ -0,0 +1,38 @@ +package raylras.zen.code; + +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.ErrorNode; +import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.RuleNode; +import org.antlr.v4.runtime.tree.TerminalNode; +import raylras.zen.code.parser.ZenScriptParserBaseVisitor; + +public abstract class NonRecursionVisitor extends ZenScriptParserBaseVisitor { + + public T visitDefault(ParseTree tree) { + return defaultResult(); + } + + @Override + public final T visitChildren(RuleNode node) { + return visitDefault(node); + } + @Override + public T visitTerminal(TerminalNode node) { + return visitDefault(node); + } + + @Override + public T visitErrorNode(ErrorNode node) { + return visitDefault(node); + } + @Override + protected final T aggregateResult(T aggregate, T nextResult) { + throw new IllegalStateException("do not support"); + } + + @Override + protected final boolean shouldVisitNextChild(RuleNode node, T currentResult) { + throw new IllegalStateException("do not support"); + } +} diff --git a/src/main/java/raylras/zen/code/common/MemberProvider.java b/src/main/java/raylras/zen/code/common/MemberProvider.java new file mode 100644 index 00000000..95c820ed --- /dev/null +++ b/src/main/java/raylras/zen/code/common/MemberProvider.java @@ -0,0 +1,9 @@ +package raylras.zen.code.common; + +import raylras.zen.code.symbol.Symbol; + +import java.util.List; + +public interface MemberProvider { + List getMembers(); +} diff --git a/src/main/java/raylras/zen/code/resolve/SymbolResolver.java b/src/main/java/raylras/zen/code/resolve/SymbolResolver.java new file mode 100644 index 00000000..80952933 --- /dev/null +++ b/src/main/java/raylras/zen/code/resolve/SymbolResolver.java @@ -0,0 +1,274 @@ +package raylras.zen.code.resolve; + +import org.antlr.v4.runtime.tree.ParseTree; +import raylras.zen.code.CompilationUnit; +import raylras.zen.code.NonRecursionVisitor; +import raylras.zen.code.common.MemberProvider; +import raylras.zen.util.ResolveUtils; +import raylras.zen.code.parser.ZenScriptParser; +import raylras.zen.code.symbol.*; +import raylras.zen.code.type.Type; +import raylras.zen.util.*; + +import java.util.*; +import java.util.stream.Collectors; + +public final class SymbolResolver { + + public static Optional lookupSimpleSymbol(ParseTree cst, CompilationUnit unit, boolean expandImport) { + return lookupSymbols(cst, unit, expandImport) + .stream().filter(it -> it.getKind() != Symbol.Kind.FUNCTION) + .findFirst(); + } + + public static Optional lookupSimpleExpression(ZenScriptParser.ExpressionContext expression, CompilationUnit unit, boolean expandImport) { + if (expression instanceof ZenScriptParser.MemberAccessExprContext memberAccessExpr) { + return SymbolResolver.lookupSimpleSymbol(memberAccessExpr.simpleName(), unit, true); + } else if (expression instanceof ZenScriptParser.SimpleNameExprContext simpleNameExpr) { + return SymbolResolver.lookupSimpleSymbol(simpleNameExpr.simpleName(), unit, true); + } + return Optional.empty(); + } + + + public static List lookupSymbols(ParseTree cst, CompilationUnit unit, boolean expandImport) { + List result = lookupSymbols(cst, unit); + if (expandImport) { + Optional importSymbol = result.stream() + .filter(it -> it.getKind() == Symbol.Kind.IMPORT && it instanceof ImportSymbol) + .map(it -> (ImportSymbol) it).findFirst(); + if (importSymbol.isPresent()) { + return importSymbol.get().getTargets(); + } + } + return result; + } + + public static List lookupSymbols(ParseTree cst, CompilationUnit unit) { + if (cst.getParent() == null) { + return Collections.emptyList(); + } + SymbolResolverVisitor visitor = new SymbolResolverVisitor(unit, cst); + return cst.getParent().accept(visitor); + } + + private static class SymbolResolverVisitor extends NonRecursionVisitor> { + + private final CompilationUnit unit; + + private final ParseTree cst; + + public SymbolResolverVisitor(CompilationUnit unit, ParseTree cst) { + this.unit = unit; + this.cst = cst; + } + + + private List doResolveQualifiedName(List names, int lastNameIndex) { + // first + Symbol symbol = ResolveUtils.getRootPackage(unit, names.get(0).getText()); + // center + for (int i = 1; i < lastNameIndex; i++) { + if ((!(symbol instanceof MemberProvider memberProvider))) { + return Collections.emptyList(); + } + String simpleName = names.get(i).getText(); + Optional found = memberProvider.getMembers() + .stream() + .filter(it -> it.getName().equals(simpleName)) + .findFirst(); + symbol = found.orElse(null); + } + // last + + String simpleName = names.get(lastNameIndex).getText(); + + if ((!(symbol instanceof MemberProvider memberProvider))) { + return Collections.emptyList(); + } + return memberProvider.getMembers() + .stream() + .filter(it -> it.getName().equals(simpleName)) + .collect(Collectors.toList()); + + } + + @Override + public List visitDefault(ParseTree tree) { + return Collections.emptyList(); + } + + @Override + public List visitQualifiedName(ZenScriptParser.QualifiedNameContext ctx) { + List names = ctx.simpleName(); + if (names.isEmpty()) { + return Collections.emptyList(); + } + if (names.size() == 1) { + // resolve directly + return ResolveUtils.lookupSymbols(unit, ctx, names.get(0).getText()); + } + + + for (int i = 0; i < names.size(); i++) { + if (names.get(i) == cst) { + return doResolveQualifiedName(names, i + 1); + } + } + return Collections.emptyList(); + } + + @Override + public List visitSimpleNameExpr(ZenScriptParser.SimpleNameExprContext ctx) { + // data.foo(); + // ¯¯¯¯ + return ResolveUtils.lookupSymbols(unit, ctx, ctx.simpleName().getText()); + } + + @Override + public List visitMemberAccessExpr(ZenScriptParser.MemberAccessExprContext ctx) { + // data.foo; + // ¯¯¯ + String simpleName = null; + if (ctx.simpleName() != null) { + simpleName = ctx.simpleName().getText(); + } else if (ctx.STRING_LITERAL() != null) { + String literalString = ctx.STRING_LITERAL().getText(); + if (literalString.length() >= 2) { + simpleName = literalString.substring(1, literalString.length() - 1); + } + } + if (simpleName == null) { + return Collections.emptyList(); + } + List symbols = new ArrayList<>(); + Optional leftSymbol = lookupSimpleExpression(ctx.expression(), unit, true); + Type leftType = TypeResolver.getType(ctx.expression(), unit); + if (leftSymbol.isPresent() && leftSymbol.get() instanceof MemberProvider memberProvider) { + symbols.addAll(memberProvider.getMembers()); + // kind == class means static access + if(leftSymbol.get().getKind() == Symbol.Kind.CLASS) { + return symbols.stream().filter(it -> it.isModifiedBy(Symbol.Modifier.STATIC)) + .collect(Collectors.toList()); + } + } + if (leftType != null) { + if (leftSymbol.isEmpty()) { + List members = Symbols.getMembersByName(leftType, simpleName, Symbol.class); + symbols.addAll(members); + } + List operators = Operators.find(leftType, Operator.MEMBER_GET, Operator.MEMBER_SET); + symbols.addAll(operators); + } + + + return symbols; + + } + + @Override + public List visitMemberIndexExpr(ZenScriptParser.MemberIndexExprContext ctx) { + // data["foo"]; + // ¯¯¯ + ZenScriptParser.ExpressionContext indexExpr = ctx.index; + // only process with literal index + if (!(indexExpr instanceof ZenScriptParser.LiteralExprContext literal)) { + return Collections.emptyList(); + } + Type leftType = TypeResolver.getType(ctx.left, unit); + if (leftType == null) { + return Collections.emptyList(); + } + + List operators = Operators.find(leftType, Operator.INDEX_GET, Operator.INDEX_SET); + return new ArrayList<>(operators); + } + + + // below are for operators, all range are for operator + @Override + public List visitIntRangeExpr(ZenScriptParser.IntRangeExprContext ctx) { + Type leftType = TypeResolver.getType(ctx.expression(0), unit); + if (leftType == null) { + return Collections.emptyList(); + } + List operators = Operators.find(leftType, Operator.RANGE); + return new ArrayList<>(operators); + } + + @Override + public List visitBinaryExpr(ZenScriptParser.BinaryExprContext ctx) { + Type leftType = TypeResolver.getType(ctx.left, unit); + if (leftType == null) { + return Collections.emptyList(); + } + Operator operator = Operators.literal(ctx.op.getText(), 2); + List operators = Operators.find(leftType, operator); + return new ArrayList<>(operators); + } + + @Override + public List visitCompareExpr(ZenScriptParser.CompareExprContext ctx) { + Type leftType = TypeResolver.getType(ctx.left, unit); + + if (leftType == null) { + return Collections.emptyList(); + } + List operators = Operators.find(leftType, Operator.COMPARE); + List symbols = new ArrayList<>(operators); + + if (ctx.EQUAL() != null || ctx.NOT_EQUAL() != null) { + symbols.addAll(Operators.find(leftType, Operator.EQUALS)); + } + return symbols; + } + + @Override + public List visitTypeCastExpr(ZenScriptParser.TypeCastExprContext ctx) { + Type leftType = TypeResolver.getType(ctx.expression(), unit); + if (leftType == null) { + return Collections.emptyList(); + } + List operators = Operators.find(leftType, Operator.AS); + return new ArrayList<>(operators); + } + + @Override + public List visitInstanceOfExpr(ZenScriptParser.InstanceOfExprContext ctx) { + Type leftType = TypeResolver.getType(ctx.left, unit); + if (leftType == null) { + return Collections.emptyList(); + } + List operators = Operators.find(leftType, Operator.AS); + return new ArrayList<>(operators); + } + + @Override + public List visitUnaryExpr(ZenScriptParser.UnaryExprContext ctx) { + Type leftType = TypeResolver.getType(ctx.expression(), unit); + if (leftType == null) { + return Collections.emptyList(); + } + if (ctx.NOT() != null) { + return new ArrayList<>(Operators.find(leftType, Operator.NOT)); + } else if (ctx.SUB() != null) { + return new ArrayList<>(Operators.find(leftType, Operator.NEG)); + } + + return Collections.emptyList(); + } + + @Override + public List visitForeachStatement(ZenScriptParser.ForeachStatementContext ctx) { + Type iterableType = TypeResolver.getType(ctx.expression(), unit); + if (iterableType == null) { + return Collections.emptyList(); + } + List operators = Operators.find(iterableType, Operator.ITERATOR); + return new ArrayList<>(operators); + } + + + } + +} diff --git a/src/main/java/raylras/zen/code/resolve/TypeResolver.java b/src/main/java/raylras/zen/code/resolve/TypeResolver.java index 30073fd3..7a05d7ca 100644 --- a/src/main/java/raylras/zen/code/resolve/TypeResolver.java +++ b/src/main/java/raylras/zen/code/resolve/TypeResolver.java @@ -5,6 +5,8 @@ import org.antlr.v4.runtime.tree.RuleNode; import raylras.zen.code.CompilationUnit; import raylras.zen.code.Visitor; +import raylras.zen.code.common.MemberProvider; +import raylras.zen.util.ResolveUtils; import raylras.zen.code.parser.ZenScriptLexer; import raylras.zen.code.parser.ZenScriptParser.*; import raylras.zen.code.scope.Scope; @@ -16,9 +18,7 @@ import raylras.zen.util.Operators; import raylras.zen.util.Symbols; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; public final class TypeResolver { @@ -52,22 +52,6 @@ private List toTypeList(TypeLiteralListContext ctx) { .collect(Collectors.toList()); } - private Symbol lookupSymbol(ParseTree cst, String simpleName) { - Scope scope = unit.lookupScope(cst); - Symbol symbol = null; - if (scope != null) { - symbol = scope.lookupSymbol(simpleName); - } - if (symbol == null) { - for (Symbol globalSymbol : unit.getEnv().getGlobalSymbols()) { - if (simpleName.equals(globalSymbol.getName())) { - symbol = globalSymbol; - } - } - } - return symbol; - } - @Override public Type visitImportDeclaration(ImportDeclarationContext ctx) { ImportSymbol symbol = unit.getSymbol(ctx, ImportSymbol.class); @@ -222,7 +206,7 @@ public Type visitIntRangeExpr(IntRangeExprContext ctx) { @Override public Type visitSimpleNameExpr(SimpleNameExprContext ctx) { - Symbol symbol = lookupSymbol(ctx, ctx.simpleName().getText()); + Symbol symbol = ResolveUtils.lookupSymbol(unit, ctx, ctx.simpleName().getText()); if (symbol != null) { return symbol.getType(); } else { @@ -362,19 +346,28 @@ public Type visitLiteralExpr(LiteralExprContext ctx) { } } + @Override public Type visitMemberAccessExpr(MemberAccessExprContext ctx) { - Type leftType = visit(ctx.expression()); - if (leftType == null) { - return null; + List symbols = SymbolResolver.lookupSymbols(ctx.simpleName(), unit); + if (symbols.isEmpty()) { + Type leftType = visit(ctx.expression()); + symbols.addAll(Operators.find(leftType, Operator.MEMBER_GET, Operator.MEMBER_SET)); } - String simpleName = ctx.simpleName().getText(); - for (Symbol member : leftType.getMembers()) { - if (Objects.equals(member.getName(), simpleName)) { - return member.getType(); + + Type fallbackType = AnyType.INSTANCE; + for (Symbol member : symbols) { + if (member instanceof OperatorFunctionSymbol op) { + if (op.getOperator() == Operator.MEMBER_GET) { + fallbackType = op.getReturnType(); + } else if (op.getOperator() == Operator.MEMBER_SET) { + fallbackType = op.getType().getParameterTypes().get(2); + } + continue; } + return member.getType(); } - return leftType; + return fallbackType; } @Override @@ -389,31 +382,34 @@ public Type visitArrayLiteralExpr(ArrayLiteralExprContext ctx) { @Override public Type visitCallExpr(CallExprContext ctx) { - if (ctx.expression() instanceof MemberAccessExprContext) { - MemberAccessExprContext memberAccessExpr = (MemberAccessExprContext) ctx.expression(); + List symbols = SymbolResolver.lookupSymbols(ctx.expression(), unit, true); + List functions = Collections.emptyList(); + if (!symbols.isEmpty()) { + functions = symbols.stream().filter(it -> it instanceof FunctionSymbol) + .map(it -> (FunctionSymbol) it) + .toList(); + } else if (ctx.expression() instanceof MemberAccessExprContext memberAccessExpr) { Type owner = visit(memberAccessExpr.expression()); - if (owner == null) { - return null; - } - List argumentTypes = new ArrayList<>(); - for (ExpressionContext expressionContext : ctx.expressionList().expression()) { - Type argumentType = visit(expressionContext); - if (argumentType == null) { - argumentType = AnyType.INSTANCE; - } - argumentTypes.add(argumentType); + if (owner != null) { + functions = Symbols.getMembersByName(owner, memberAccessExpr.simpleName().getText(), FunctionSymbol.class); } - List functions = Symbols.getMembersByName(owner, memberAccessExpr.simpleName().getText(), FunctionSymbol.class); - FunctionSymbol matchedFunction = Functions.findBestMatch(functions, argumentTypes); - return matchedFunction == null ? null : matchedFunction.getReturnType(); - } else { - Type leftType = visit(ctx.expression()); - if (leftType instanceof FunctionType) { - return ((FunctionType) leftType).getReturnType(); - } else { - return null; + } + + if (functions.isEmpty()) { + return null; + } + + List argumentTypes = new ArrayList<>(); + for (ExpressionContext expressionContext : ctx.expressionList().expression()) { + Type argumentType = visit(expressionContext); + if (argumentType == null) { + argumentType = AnyType.INSTANCE; } + argumentTypes.add(argumentType); } + + FunctionSymbol matchedFunction = Functions.findBestMatch(functions, argumentTypes); + return matchedFunction == null ? null : matchedFunction.getReturnType(); } @Override @@ -450,40 +446,19 @@ public Type visitListType(ListTypeContext ctx) { @Override public Type visitPrimitiveType(PrimitiveTypeContext ctx) { - switch (CSTNodes.getTokenType(ctx.start)) { - case ZenScriptLexer.ANY: - return AnyType.INSTANCE; - - case ZenScriptLexer.BYTE: - return ByteType.INSTANCE; - - case ZenScriptLexer.SHORT: - return ShortType.INSTANCE; - - case ZenScriptLexer.INT: - return IntType.INSTANCE; - - case ZenScriptLexer.LONG: - return LongType.INSTANCE; - - case ZenScriptLexer.FLOAT: - return FloatType.INSTANCE; - - case ZenScriptLexer.DOUBLE: - return DoubleType.INSTANCE; - - case ZenScriptLexer.BOOL: - return BoolType.INSTANCE; - - case ZenScriptLexer.VOID: - return VoidType.INSTANCE; - - case ZenScriptLexer.STRING: - return StringType.INSTANCE; - - default: - return null; - } + return switch (CSTNodes.getTokenType(ctx.start)) { + case ZenScriptLexer.ANY -> AnyType.INSTANCE; + case ZenScriptLexer.BYTE -> ByteType.INSTANCE; + case ZenScriptLexer.SHORT -> ShortType.INSTANCE; + case ZenScriptLexer.INT -> IntType.INSTANCE; + case ZenScriptLexer.LONG -> LongType.INSTANCE; + case ZenScriptLexer.FLOAT -> FloatType.INSTANCE; + case ZenScriptLexer.DOUBLE -> DoubleType.INSTANCE; + case ZenScriptLexer.BOOL -> BoolType.INSTANCE; + case ZenScriptLexer.VOID -> VoidType.INSTANCE; + case ZenScriptLexer.STRING -> StringType.INSTANCE; + default -> null; + }; } @Override diff --git a/src/main/java/raylras/zen/code/scope/Scope.java b/src/main/java/raylras/zen/code/scope/Scope.java index 3caaaef2..54488a4a 100644 --- a/src/main/java/raylras/zen/code/scope/Scope.java +++ b/src/main/java/raylras/zen/code/scope/Scope.java @@ -29,6 +29,23 @@ public void removeSymbol(Symbol symbol) { public Symbol lookupSymbol(String simpleName) { return lookupSymbol(Symbol.class, simpleName); } + public List lookupSymbols(String simpleName) { + Scope scope = this; + List result = new ArrayList<>(); + while (scope != null) { + + for (Symbol symbol : scope.getSymbols()) { + if (Objects.equals(symbol.getName(), simpleName)) { + result.add(symbol); + } + } + if(!result.isEmpty()) { + return result; + } + scope = scope.parent; + } + return result; + } public T lookupSymbol(Class clazz, String simpleName) { Scope scope = this; diff --git a/src/main/java/raylras/zen/code/symbol/ClassSymbol.java b/src/main/java/raylras/zen/code/symbol/ClassSymbol.java index 8c0f57e5..64457191 100644 --- a/src/main/java/raylras/zen/code/symbol/ClassSymbol.java +++ b/src/main/java/raylras/zen/code/symbol/ClassSymbol.java @@ -1,15 +1,17 @@ package raylras.zen.code.symbol; +import raylras.zen.code.common.MemberProvider; import raylras.zen.code.type.ClassType; import java.util.List; -public interface ClassSymbol extends Symbol { +public interface ClassSymbol extends Symbol, MemberProvider { String getQualifiedName(); List getDeclaredMembers(); + @Override List getMembers(); List getInterfaces(); diff --git a/src/main/java/raylras/zen/code/symbol/ImportSymbol.java b/src/main/java/raylras/zen/code/symbol/ImportSymbol.java index 3a8a4996..3a19226d 100644 --- a/src/main/java/raylras/zen/code/symbol/ImportSymbol.java +++ b/src/main/java/raylras/zen/code/symbol/ImportSymbol.java @@ -1,7 +1,11 @@ package raylras.zen.code.symbol; +import java.util.List; + public interface ImportSymbol extends Symbol { String getQualifiedName(); + List getTargets(); + } diff --git a/src/main/java/raylras/zen/code/symbol/PackageSymbol.java b/src/main/java/raylras/zen/code/symbol/PackageSymbol.java new file mode 100644 index 00000000..69a9d7d4 --- /dev/null +++ b/src/main/java/raylras/zen/code/symbol/PackageSymbol.java @@ -0,0 +1,13 @@ +package raylras.zen.code.symbol; + +import raylras.zen.code.common.MemberProvider; + +import java.util.List; + +public interface PackageSymbol extends Symbol, MemberProvider { + + String getQualifiedName(); + + @Override + List getMembers(); +} diff --git a/src/main/java/raylras/zen/code/symbol/SymbolFactory.java b/src/main/java/raylras/zen/code/symbol/SymbolFactory.java index 26cfba3b..371927de 100644 --- a/src/main/java/raylras/zen/code/symbol/SymbolFactory.java +++ b/src/main/java/raylras/zen/code/symbol/SymbolFactory.java @@ -5,6 +5,7 @@ import raylras.zen.code.parser.ZenScriptParser.*; import raylras.zen.code.resolve.FormalParameterResolver; import raylras.zen.code.resolve.ModifierResolver; +import raylras.zen.code.resolve.SymbolResolver; import raylras.zen.code.resolve.TypeResolver; import raylras.zen.code.scope.Scope; import raylras.zen.code.type.*; @@ -18,7 +19,8 @@ public class SymbolFactory { - private SymbolFactory() {} + private SymbolFactory() { + } public static ImportSymbol createImportSymbol(String name, ImportDeclarationContext cst, CompilationUnit unit) { class ImportSymbolImpl implements ImportSymbol, Locatable { @@ -27,6 +29,11 @@ public String getQualifiedName() { return cst.qualifiedName().getText(); } + @Override + public List getTargets() { + return SymbolResolver.lookupSymbols(cst.qualifiedName(), unit); + } + @Override public String getName() { return name; @@ -39,8 +46,11 @@ public Kind getKind() { @Override public Type getType() { - // TODO: import static members - return unit.getEnv().getClassTypeMap().get(getQualifiedName()); + List targets = getTargets(); + if(targets.size() > 1) { + return AnyType.INSTANCE; + } + return targets.get(0).getType(); } @Override @@ -80,7 +90,6 @@ public String getQualifiedName() { public List getDeclaredMembers() { return unit.getScope(cst).getSymbols(); } - @Override public List getMembers() { List symbols = new ArrayList<>(getDeclaredMembers()); diff --git a/src/main/java/raylras/zen/code/type/ArrayType.java b/src/main/java/raylras/zen/code/type/ArrayType.java index 4924b67f..6fff79fe 100644 --- a/src/main/java/raylras/zen/code/type/ArrayType.java +++ b/src/main/java/raylras/zen/code/type/ArrayType.java @@ -1,5 +1,6 @@ package raylras.zen.code.type; +import raylras.zen.code.common.MemberProvider; import raylras.zen.code.symbol.Operator; import raylras.zen.code.symbol.Symbol; import raylras.zen.code.symbol.SymbolFactory; @@ -8,7 +9,7 @@ import java.util.Objects; import java.util.function.UnaryOperator; -public class ArrayType extends Type { +public class ArrayType extends Type implements MemberProvider { private final Type elementType; diff --git a/src/main/java/raylras/zen/code/type/BoolType.java b/src/main/java/raylras/zen/code/type/BoolType.java index cad8c3e4..3e892011 100644 --- a/src/main/java/raylras/zen/code/type/BoolType.java +++ b/src/main/java/raylras/zen/code/type/BoolType.java @@ -1,5 +1,6 @@ package raylras.zen.code.type; +import raylras.zen.code.common.MemberProvider; import raylras.zen.code.symbol.Operator; import raylras.zen.code.symbol.Symbol; import raylras.zen.code.symbol.SymbolFactory; @@ -7,7 +8,7 @@ import java.util.List; import java.util.function.UnaryOperator; -public class BoolType extends Type { +public class BoolType extends Type implements MemberProvider { public static final BoolType INSTANCE = new BoolType(); diff --git a/src/main/java/raylras/zen/code/type/ClassType.java b/src/main/java/raylras/zen/code/type/ClassType.java index 660a61b7..a32b9115 100644 --- a/src/main/java/raylras/zen/code/type/ClassType.java +++ b/src/main/java/raylras/zen/code/type/ClassType.java @@ -1,12 +1,13 @@ package raylras.zen.code.type; +import raylras.zen.code.common.MemberProvider; import raylras.zen.code.symbol.ClassSymbol; import raylras.zen.code.symbol.Symbol; import raylras.zen.util.Operators; import java.util.List; -public class ClassType extends Type { +public class ClassType extends Type implements MemberProvider { private final ClassSymbol symbol; diff --git a/src/main/java/raylras/zen/code/type/IntRangeType.java b/src/main/java/raylras/zen/code/type/IntRangeType.java index 29a429d5..0f3f3ce2 100644 --- a/src/main/java/raylras/zen/code/type/IntRangeType.java +++ b/src/main/java/raylras/zen/code/type/IntRangeType.java @@ -1,5 +1,6 @@ package raylras.zen.code.type; +import raylras.zen.code.common.MemberProvider; import raylras.zen.code.symbol.Operator; import raylras.zen.code.symbol.Symbol; import raylras.zen.code.symbol.SymbolFactory; @@ -7,7 +8,7 @@ import java.util.List; import java.util.function.UnaryOperator; -public class IntRangeType extends Type { +public class IntRangeType extends Type implements MemberProvider { public static final IntRangeType INSTANCE = new IntRangeType(); diff --git a/src/main/java/raylras/zen/code/type/ListType.java b/src/main/java/raylras/zen/code/type/ListType.java index 61db0501..c07759d0 100644 --- a/src/main/java/raylras/zen/code/type/ListType.java +++ b/src/main/java/raylras/zen/code/type/ListType.java @@ -1,5 +1,6 @@ package raylras.zen.code.type; +import raylras.zen.code.common.MemberProvider; import raylras.zen.code.symbol.Operator; import raylras.zen.code.symbol.Symbol; import raylras.zen.code.symbol.SymbolFactory; @@ -7,7 +8,7 @@ import java.util.List; import java.util.function.UnaryOperator; -public class ListType extends Type { +public class ListType extends Type implements MemberProvider { private final Type elementType; diff --git a/src/main/java/raylras/zen/code/type/MapEntryType.java b/src/main/java/raylras/zen/code/type/MapEntryType.java index 79d67fd7..a4db46ed 100644 --- a/src/main/java/raylras/zen/code/type/MapEntryType.java +++ b/src/main/java/raylras/zen/code/type/MapEntryType.java @@ -1,11 +1,12 @@ package raylras.zen.code.type; +import raylras.zen.code.common.MemberProvider; import raylras.zen.code.symbol.Symbol; import raylras.zen.code.symbol.SymbolFactory; import java.util.List; -public class MapEntryType extends Type { +public class MapEntryType extends Type implements MemberProvider { private final Type keyType; private final Type valueType; diff --git a/src/main/java/raylras/zen/code/type/MapType.java b/src/main/java/raylras/zen/code/type/MapType.java index 4692c185..40884636 100644 --- a/src/main/java/raylras/zen/code/type/MapType.java +++ b/src/main/java/raylras/zen/code/type/MapType.java @@ -1,5 +1,6 @@ package raylras.zen.code.type; +import raylras.zen.code.common.MemberProvider; import raylras.zen.code.symbol.Operator; import raylras.zen.code.symbol.Symbol; import raylras.zen.code.symbol.SymbolFactory; @@ -7,7 +8,7 @@ import java.util.List; import java.util.function.UnaryOperator; -public class MapType extends Type { +public class MapType extends Type implements MemberProvider { private final Type keyType; private final Type valueType; diff --git a/src/main/java/raylras/zen/code/type/NumberType.java b/src/main/java/raylras/zen/code/type/NumberType.java index 32b04728..74bee7fc 100644 --- a/src/main/java/raylras/zen/code/type/NumberType.java +++ b/src/main/java/raylras/zen/code/type/NumberType.java @@ -1,5 +1,6 @@ package raylras.zen.code.type; +import raylras.zen.code.common.MemberProvider; import raylras.zen.code.symbol.Operator; import raylras.zen.code.symbol.Symbol; import raylras.zen.code.symbol.SymbolFactory; @@ -7,7 +8,7 @@ import java.util.List; import java.util.function.UnaryOperator; -public abstract class NumberType extends Type { +public abstract class NumberType extends Type implements MemberProvider { @Override public SubtypeResult isSubtypeOf(Type type) { diff --git a/src/main/java/raylras/zen/code/type/StringType.java b/src/main/java/raylras/zen/code/type/StringType.java index a2596272..b54e2bcd 100644 --- a/src/main/java/raylras/zen/code/type/StringType.java +++ b/src/main/java/raylras/zen/code/type/StringType.java @@ -1,12 +1,13 @@ package raylras.zen.code.type; +import raylras.zen.code.common.MemberProvider; import raylras.zen.code.symbol.Operator; import raylras.zen.code.symbol.Symbol; import raylras.zen.code.symbol.SymbolFactory; import java.util.List; -public class StringType extends Type { +public class StringType extends Type implements MemberProvider { public static final StringType INSTANCE = new StringType(); diff --git a/src/main/java/raylras/zen/code/type/Type.java b/src/main/java/raylras/zen/code/type/Type.java index b38e4d71..b7b57ceb 100644 --- a/src/main/java/raylras/zen/code/type/Type.java +++ b/src/main/java/raylras/zen/code/type/Type.java @@ -7,10 +7,6 @@ public abstract class Type { - public List getMembers() { - return Collections.emptyList(); - } - public boolean isAssignableTo(Type type) { return isSubtypeOf(type).matched(); } diff --git a/src/main/java/raylras/zen/langserver/provider/CompletionProvider.java b/src/main/java/raylras/zen/langserver/provider/CompletionProvider.java index e0dc2cf1..67927369 100644 --- a/src/main/java/raylras/zen/langserver/provider/CompletionProvider.java +++ b/src/main/java/raylras/zen/langserver/provider/CompletionProvider.java @@ -11,8 +11,10 @@ import org.eclipse.lsp4j.CompletionParams; import raylras.zen.code.CompilationUnit; import raylras.zen.code.Visitor; +import raylras.zen.code.common.MemberProvider; import raylras.zen.code.parser.ZenScriptParser; import raylras.zen.code.parser.ZenScriptParser.*; +import raylras.zen.code.resolve.SymbolResolver; import raylras.zen.code.resolve.TypeResolver; import raylras.zen.code.scope.Scope; import raylras.zen.code.symbol.Executable; @@ -23,14 +25,12 @@ import raylras.zen.code.type.FunctionType; import raylras.zen.code.type.Type; import raylras.zen.langserver.provider.data.Keywords; -import raylras.zen.util.CSTNodes; -import raylras.zen.util.PackageTree; -import raylras.zen.util.Range; -import raylras.zen.util.Ranges; +import raylras.zen.util.*; import raylras.zen.util.l10n.L10N; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; public final class CompletionProvider { @@ -327,16 +327,14 @@ public Void visitMemberAccessExpr(MemberAccessExprContext ctx) { // expr.text| // ^____ if (containsLeading(ctx.DOT())) { - Type type = TypeResolver.getType(ctx.expression(), unit); - completeMemberSymbols(text, type); + doCompleteMemberAccessExpr(text, ctx.expression()); return null; } // expr.| // ^^^^_ if (containsLeading(ctx.expression())) { - Type type = TypeResolver.getType(ctx.expression(), unit); - completeMemberSymbols("", type); + doCompleteMemberAccessExpr("", ctx.expression()); return null; } @@ -344,6 +342,16 @@ public Void visitMemberAccessExpr(MemberAccessExprContext ctx) { return null; } + private void doCompleteMemberAccessExpr(String text, ExpressionContext expression) { + Optional symbol = SymbolResolver.lookupSimpleExpression(expression, unit, true); + if(symbol.isPresent() && symbol.get() instanceof MemberProvider memberProvider) { + completeMemberSymbols(text, memberProvider.getMembers()); + } else { + Type type = TypeResolver.getType(expression, unit); + completeMemberSymbols(text, Symbols.getMember(type, Symbol.class, it -> it.getKind() != Symbol.Kind.FUNCTION)); + } + } + @Override public Void visitCallExpr(CallExprContext ctx) { // expr(text|) @@ -506,8 +514,8 @@ private void completeGlobalSymbols(String text) { } } - private void completeMemberSymbols(String text, Type type) { - for (Symbol member : type.getMembers()) { + private void completeMemberSymbols(String text, List members) { + for (Symbol member : members) { if (member.getName().startsWith(text) && !(member instanceof OperatorFunctionSymbol)) { addToCompletionList(member); } diff --git a/src/main/java/raylras/zen/util/Operators.java b/src/main/java/raylras/zen/util/Operators.java index 1a132474..5cd7d2fa 100644 --- a/src/main/java/raylras/zen/util/Operators.java +++ b/src/main/java/raylras/zen/util/Operators.java @@ -3,19 +3,25 @@ import raylras.zen.code.symbol.Operator; import raylras.zen.code.symbol.OperatorFunctionSymbol; import raylras.zen.code.symbol.ParameterSymbol; +import raylras.zen.code.symbol.Symbol; import raylras.zen.code.type.AnyType; import raylras.zen.code.type.SubtypeResult; import raylras.zen.code.type.Type; import raylras.zen.code.type.UnionType; -import java.util.Comparator; -import java.util.List; +import java.util.*; +import java.util.stream.Collectors; public class Operators { public static List find(Type type, Operator operator) { return Symbols.getMember(type, OperatorFunctionSymbol.class, it -> it.getOperator() == operator); } + public static List find(Type type, Operator... operators) { + Set candidates = Arrays.stream(operators).collect(Collectors.toSet()); + return Symbols.getMember(type, OperatorFunctionSymbol.class, it -> candidates.contains(it.getOperator())); + } + public static Type getBinaryOperatorResult(Type type, Operator operator, Type rightType) { return find(type, operator).stream() .min(Comparator.comparing(it -> rightType.isSubtypeOf(it.getParameterList().get(0).getType()), SubtypeResult.PRIORITY_COMPARATOR)) @@ -30,6 +36,13 @@ public static Type getUnaryOperatorResult(Type type, Operator operator) { .orElse(AnyType.INSTANCE); } + public static Optional findBestBinaryOperator(List symbols, Type rightType) { + return symbols.stream() + .filter(it -> it instanceof OperatorFunctionSymbol) + .map(it -> (OperatorFunctionSymbol) it) + .min(Comparator.comparing(it -> rightType.isSubtypeOf(it.getParameterList().get(0).getType()), SubtypeResult.PRIORITY_COMPARATOR)); + } + public static Type getTrinaryOperatorResult(Type type, Operator operator, Type rightType1, Type rightType2) { return find(type, operator).stream() .min(Comparator.comparing(it -> { diff --git a/src/main/java/raylras/zen/util/ResolveUtils.java b/src/main/java/raylras/zen/util/ResolveUtils.java new file mode 100644 index 00000000..1687b3da --- /dev/null +++ b/src/main/java/raylras/zen/util/ResolveUtils.java @@ -0,0 +1,53 @@ +package raylras.zen.util; + +import org.antlr.v4.runtime.tree.ParseTree; +import raylras.zen.code.CompilationUnit; +import raylras.zen.code.scope.Scope; +import raylras.zen.code.symbol.PackageSymbol; +import raylras.zen.code.symbol.Symbol; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ResolveUtils { + + public static List lookupSymbols(CompilationUnit unit, ParseTree cst, String simpleName) { + Scope scope = unit.lookupScope(cst); + List symbols = new ArrayList<>(); + if (scope != null) { + symbols = scope.lookupSymbols(simpleName); + } + if (symbols.isEmpty()) { + for (Symbol globalSymbol : unit.getEnv().getGlobalSymbols()) { + if (simpleName.equals(globalSymbol.getName())) { + // assume global symbols can not overload, so return directly + return Collections.singletonList(globalSymbol); + } + } + } + return symbols; + } + public static Symbol lookupSymbol(CompilationUnit unit, ParseTree cst, String simpleName) { + Scope scope = unit.lookupScope(cst); + Symbol symbol = null; + if (scope != null) { + symbol = scope.lookupSymbol(simpleName); + } + if (symbol != null) { + for (Symbol globalSymbol : unit.getEnv().getGlobalSymbols()) { + if (simpleName.equals(globalSymbol.getName())) { + return globalSymbol; + } + } + } + return symbol; + } + public static PackageSymbol getRootPackage(CompilationUnit unit, String packageName) { + + // TODO:xxx + return null; + } + + +} diff --git a/src/main/java/raylras/zen/util/Symbols.java b/src/main/java/raylras/zen/util/Symbols.java index f933d1c0..a24172ee 100644 --- a/src/main/java/raylras/zen/util/Symbols.java +++ b/src/main/java/raylras/zen/util/Symbols.java @@ -1,8 +1,10 @@ package raylras.zen.util; +import raylras.zen.code.common.MemberProvider; import raylras.zen.code.symbol.Symbol; import raylras.zen.code.type.Type; +import java.util.Collections; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -14,7 +16,11 @@ public static List getMembersByName(Type type, String simp } public static List getMember(Type type, Class clazz, Predicate filter) { - return type.getMembers().stream() + + if (!(type instanceof MemberProvider memberProvider)) { + return Collections.emptyList(); + } + return memberProvider.getMembers().stream() .filter(clazz::isInstance) .map(clazz::cast) .filter(filter)