Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 84 additions & 40 deletions src/main/java/raylras/zen/code/resolve/SymbolResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,56 @@
import raylras.zen.code.CompilationUnit;
import raylras.zen.code.SymbolProvider;
import raylras.zen.code.Visitor;
import raylras.zen.code.parser.ZenScriptParser;
import raylras.zen.code.parser.ZenScriptParser.MemberAccessExprContext;
import raylras.zen.code.parser.ZenScriptParser.SimpleNameExprContext;
import raylras.zen.code.parser.ZenScriptParser.StatementContext;
import raylras.zen.code.scope.Scope;
import raylras.zen.code.symbol.ClassSymbol;
import raylras.zen.code.symbol.Symbol;
import raylras.zen.util.PackageTree;
import raylras.zen.code.symbol.*;
import raylras.zen.util.CSTNodes;
import raylras.zen.util.Ranges;
import raylras.zen.util.Symbols;

import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class SymbolResolver {


public static Collection<Symbol> lookupSymbol(ParseTree cst, CompilationUnit unit) {
ParseTree statement = findCurrentStatement(cst);
if (statement == null) {
return Collections.emptyList();
}
SymbolVisitor visitor = new SymbolVisitor(unit, cst);
visitor.visit(statement);
return visitor.result;
return lookupSymbol(cst, unit, true);
}

private static ParseTree findCurrentStatement(ParseTree cst) {
ParseTree current = cst;
while (current != null) {
if (current instanceof StatementContext) {
return current;
} else {
current = current.getParent();
}
public static Collection<Symbol> lookupSymbol(ParseTree cst, CompilationUnit unit, boolean expandImport) {
ParseTree owner = CSTNodes.findParentOfTypes(cst,
StatementContext.class,
ZenScriptParser.QualifiedNameContext.class,
ZenScriptParser.InitializerContext.class,
ZenScriptParser.DefaultValueContext.class
);

if (owner == null) {
return Collections.emptyList();
}
return null;
SymbolVisitor visitor = new SymbolVisitor(unit, cst, expandImport);
visitor.visit(owner);
return visitor.result;
}

private static class SymbolVisitor extends Visitor<SymbolProvider> {
private final CompilationUnit unit;
private final ParseTree cst;
private final boolean expandImport;
private Collection<Symbol> result = Collections.emptyList();

public SymbolVisitor(CompilationUnit unit, ParseTree cst) {
public SymbolVisitor(CompilationUnit unit, ParseTree cst, boolean expandImport) {
this.unit = unit;
this.cst = cst;
this.expandImport = expandImport;
}

@Override
Expand All @@ -64,27 +68,54 @@ public SymbolProvider visitSimpleNameExpr(SimpleNameExprContext ctx) {
@Override
public SymbolProvider visitMemberAccessExpr(MemberAccessExprContext ctx) {
SymbolProvider leftPossibles = visit(ctx.expression());
SymbolProvider foundResults = findMembers(ctx.simpleName(), leftPossibles);
if (Ranges.contains(cst, ctx.simpleName())) {
result = foundResults.getSymbols();
}
return findMembers(ctx.simpleName(), leftPossibles);
}

private SymbolProvider findMembers(ZenScriptParser.SimpleNameContext simpleName, SymbolProvider leftPossibles) {
if (leftPossibles.size() != 1) {
return SymbolProvider.EMPTY;
}

Symbol leftSymbol = leftPossibles.getFirst();
SymbolProvider foundResults;
if (leftSymbol instanceof ClassSymbol classSymbol) {
if (leftSymbol instanceof PackageSymbol packageSymbol) {
foundResults = packageSymbol
.filter(isSymbolNameEquals(simpleName));
} else if (leftSymbol instanceof ClassSymbol classSymbol) {
foundResults = classSymbol
.filter(Symbol::isStatic)
.filter(isSymbolNameEquals(ctx.simpleName()));
.filter(isSymbolNameEquals(simpleName));
} else if (leftSymbol.getType() instanceof SymbolProvider type) {
foundResults = type.withExpands(unit.getEnv()).filter(isSymbolNameEquals(ctx.simpleName()));
foundResults = type.withExpands(unit.getEnv()).filter(isSymbolNameEquals(simpleName));
} else {
foundResults = SymbolProvider.EMPTY;
}
if (Ranges.contains(cst, ctx.simpleName())) {
result = foundResults.getSymbols();
}
return foundResults;
}

@Override
public SymbolProvider visitQualifiedName(ZenScriptParser.QualifiedNameContext ctx) {
List<ZenScriptParser.SimpleNameContext> simpleNames = ctx.simpleName();
if (simpleNames.isEmpty()) {
return SymbolProvider.EMPTY;
}

SymbolProvider possibles = lookupSymbol(ctx, simpleNames.get(0));
if (Ranges.contains(cst, simpleNames.get(0))) {
result = possibles.getSymbols();
}
for (int i = 1; i < simpleNames.size(); i++) {
possibles = findMembers(simpleNames.get(i), possibles);
if (Ranges.contains(cst, simpleNames.get(i))) {
result = possibles.getSymbols();
}
}
return possibles;
}

@Override
protected SymbolProvider defaultResult() {
return SymbolProvider.EMPTY;
Expand All @@ -96,22 +127,35 @@ private SymbolProvider lookupSymbol(ParseTree cst, ParseTree name) {

private SymbolProvider lookupSymbol(ParseTree cst, String name) {
Scope scope = unit.lookupScope(cst);
if (scope != null) {
return scope.filter(isSymbolNameEquals(name))
.orElse(() -> lookupGlobalSymbols(name));
} else {
return () -> lookupGlobalSymbols(name);
if(scope == null) {
return SymbolProvider.EMPTY;
}
List<Symbol> result = scope.lookupSymbols(name);

if (result.isEmpty()) {
return SymbolProvider.of(Symbols.lookupGlobalSymbols(unit, name));
}
if (expandImport) {
result = result.stream().flatMap(it -> {
// try to expand import
Collection<Symbol> importTargets = tryExpandImport(it);
if (!importTargets.isEmpty()) {
return importTargets.stream();
}
return Stream.of(it);
}).collect(Collectors.toList());
}
return SymbolProvider.of(result);
}

private Collection<Symbol> lookupGlobalSymbols(String name) {
Collection<Symbol> globals = unit.getEnv().getGlobalSymbols().stream().filter(it -> Objects.equals(it.getName(), name)).toList();
if (globals.isEmpty()) {
// TODO: find package
PackageTree<ClassSymbol> packageTree = PackageTree.of(".", unit.getEnv().getClassSymbolMap());
return Collections.emptyList();
private Collection<Symbol> tryExpandImport(Symbol symbol) {
if (symbol instanceof ImportSymbol && symbol instanceof ParseTreeLocatable locatable && locatable.getCst() instanceof ZenScriptParser.ImportDeclarationContext importCtx) {
Collection<Symbol> importTargets = this.visit(importCtx.qualifiedName()).getSymbols();
if (!importTargets.isEmpty()) {
return importTargets;
}
}
return globals;
return Collections.emptyList();
}

private static Predicate<Symbol> isSymbolNameEquals(ParseTree name) {
Expand Down
4 changes: 0 additions & 4 deletions src/main/java/raylras/zen/code/symbol/ImportSymbol.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package raylras.zen.code.symbol;

import java.util.List;

public interface ImportSymbol extends Symbol {

String getQualifiedName();

List<Symbol> getTargets();

}
106 changes: 96 additions & 10 deletions src/main/java/raylras/zen/code/symbol/SymbolFactory.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
package raylras.zen.code.symbol;

import org.antlr.v4.runtime.tree.ParseTree;
import raylras.zen.code.CompilationEnvironment;
import raylras.zen.code.CompilationUnit;
import raylras.zen.code.parser.ZenScriptParser.*;
import raylras.zen.code.resolve.FormalParameterResolver;
import raylras.zen.code.resolve.ModifierResolver;
import raylras.zen.code.resolve.TypeResolver;
import raylras.zen.code.scope.Scope;
import raylras.zen.code.type.*;
import raylras.zen.code.type.AnyType;
import raylras.zen.code.type.ClassType;
import raylras.zen.code.type.FunctionType;
import raylras.zen.code.type.Type;
import raylras.zen.util.CSTNodes;
import raylras.zen.util.Operators;
import raylras.zen.util.PackageTree;
import raylras.zen.util.Range;

import java.nio.file.Path;
import java.util.*;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
Expand All @@ -31,13 +37,6 @@ class ImportSymbolImpl implements ImportSymbol, ParseTreeLocatable {
public String getQualifiedName() {
return cst.qualifiedName().getText();
}

@Override
public List<Symbol> getTargets() {
// TODO: import static members and implement this
return Collections.emptyList();
}

@Override
public String getName() {
return name;
Expand All @@ -50,8 +49,7 @@ public Kind getKind() {

@Override
public Type getType() {
// TODO: import static members
return unit.getEnv().getClassTypeMap().get(getQualifiedName());
return AnyType.INSTANCE;
}

@Override
Expand Down Expand Up @@ -614,6 +612,94 @@ public Modifier getModifier() {
return new ParameterSymbolImpl();
}

public static PackageSymbol createPackageSymbol(String name, String qualifiedName, PackageTree<ClassSymbol> packageTree, CompilationEnvironment environment, boolean isGenerated) {
Path root;
if (isGenerated) {
root = environment.getGeneratedRoot();
} else {
root = environment.getRoot();
}
if (packageTree.hasElement()) {
String relativePath = qualifiedName.replace(".", "/");
if (isGenerated) {
relativePath += ".dzs";
} else {
relativePath += ".zs";
}
root = root.resolve(relativePath).normalize();
} else {
root = root.resolve(qualifiedName.replace(".", "/")).normalize();
}

final Path rootFinal = root;
class PackageSymbolImpl implements PackageSymbol, Locatable {

@Override
public String getName() {
return name;
}

@Override
public Kind getKind() {
return Kind.PACKAGE;
}

@Override
public Type getType() {
return AnyType.INSTANCE;
}

@Override
public Modifier getModifier() {
return Modifier.NONE;
}

@Override
public Path getPath() {
return rootFinal;
}

@Override
public String getUri() {
return getPath().toUri().toString();
}

@Override
public Range getRange() {
return Range.NO_RANGE;
}

@Override
public Range getSelectionRange() {
return Range.NO_RANGE;
}

@Override
public String getQualifiedName() {
return qualifiedName;
}

@Override
public List<Symbol> getSymbols() {
List<Symbol> result = new ArrayList<>();
for (Map.Entry<String, PackageTree<ClassSymbol>> entry : packageTree.getSubTrees().entrySet()) {
PackageTree<ClassSymbol> tree = entry.getValue();
if (tree.hasElement()) {
result.add(tree.getElement());
} else {
String name = entry.getKey();
PackageSymbol packageSymbol = createPackageSymbol(name, qualifiedName + "." + name, entry.getValue(), environment, isGenerated);
result.add(packageSymbol);
}
}
return result;
}
}

return new PackageSymbolImpl();
}


public static SymbolsBuilder builtinSymbols() {
return new SymbolsBuilder();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import raylras.zen.code.resolve.SymbolResolver;
import raylras.zen.code.symbol.ParseTreeLocatable;
import raylras.zen.code.symbol.Locatable;
import raylras.zen.code.symbol.Symbol;
import raylras.zen.langserver.Document;
import raylras.zen.util.CSTNodes;
Expand All @@ -27,10 +27,14 @@ public static CompletableFuture<Either<List<? extends Location>, List<? extends
return doc.getUnit().map(unit -> CompletableFuture.supplyAsync(() -> {
Position cursor = Position.of(params.getPosition());
ParseTree cst = CSTNodes.getCstAtPosition(unit.getParseTree(), cursor);
if(cst == null) {
logger.warn("Could not get symbol at ({}, {}), skipping goto definition", params.getPosition().getLine(), params.getPosition().getCharacter());
return null;
}
org.eclipse.lsp4j.Range originSelectionRange = Range.of(cst).toLspRange();
Collection<Symbol> symbols = SymbolResolver.lookupSymbol(cst, unit);
return Either.<List<? extends Location>, List<? extends LocationLink>>forRight(symbols.stream()
.filter(symbol -> symbol instanceof ParseTreeLocatable)
.filter(symbol -> symbol instanceof Locatable)
.map(symbol -> toLocationLink(symbol, originSelectionRange))
.toList());
})).orElseGet(DefinitionProvider::empty);
Expand All @@ -41,7 +45,7 @@ public static CompletableFuture<Either<List<? extends Location>, List<? extends
}

private static LocationLink toLocationLink(Symbol symbol, org.eclipse.lsp4j.Range originSelectionRange) {
ParseTreeLocatable locatable = ((ParseTreeLocatable) symbol);
Locatable locatable = ((Locatable) symbol);
String uri = locatable.getUri();
org.eclipse.lsp4j.Range range = locatable.getRange().toLspRange();
org.eclipse.lsp4j.Range selectionRange = locatable.getSelectionRange().toLspRange();
Expand Down
Loading