/*
* Copyright (c) 2012 Sam Harwell, Tunnel Vision Laboratories LLC
* All rights reserved.
*
* The source code of this document is proprietary work, and is not licensed for
* distribution. For information about licensing, contact Sam Harwell at:
* sam@tunnelvisionlabs.com
*/
package org.tvl.goworks.editor.go.semantics;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.antlr.netbeans.editor.text.VersionedDocument;
import org.antlr.v4.runtime.tree.ParseTree;
import org.tvl.goworks.editor.go.codemodel.CodeElementModel;
import org.tvl.goworks.editor.go.codemodel.FieldModel;
import org.tvl.goworks.editor.go.codemodel.InterfaceModel;
import org.tvl.goworks.editor.go.codemodel.StructModel;
import org.tvl.goworks.editor.go.codemodel.TypeAliasModel;
import org.tvl.goworks.editor.go.codemodel.TypeModel;
import org.tvl.goworks.editor.go.codemodel.TypePointerModel;
import org.tvl.goworks.editor.go.codemodel.TypeReferenceModel;
import org.tvl.goworks.editor.go.codemodel.VarModel;
import org.tvl.goworks.editor.go.codemodel.impl.TypeModelImpl;
import org.tvl.goworks.editor.go.codemodel.impl.TypePointerModelImpl;
/**
*
* @author Sam Harwell
*/
public final class SemanticAnalyzer {
public static GoAnnotatedParseTree analyze(VersionedDocument document, ParseTree parseTree) {
GoAnnotatedParseTree annotatedParseTree = new GoAnnotatedParseTree(parseTree);
SemanticAnalyzerListener listener = new SemanticAnalyzerListener(document, annotatedParseTree);
boolean background = document.getDocument() == null;
SemanticAnalyzerParseTreeWalker walker = new SemanticAnalyzerParseTreeWalker(background);
walker.walk(listener, parseTree);
listener.resolveReferences();
return annotatedParseTree;
}
public static Collection<? extends CodeElementModel> getSelectableMembers(CodeElementModel model) {
return getSelectableMembers(model, "", true);
}
public static Collection<? extends CodeElementModel> getSelectableMembers(CodeElementModel model, String name) {
return getSelectableMembers(model, name, true);
}
public static Collection<? extends CodeElementModel> getSelectableMembers(CodeElementModel model, String name, boolean includeExtendedMembers) {
if (name == null) {
name = "";
}
if (model instanceof BundledReturnTypeModel) {
model = ((BundledReturnTypeModel)model).getReturnValues().get(0);
}
CodeElementModel source = model;
if (source instanceof VarModel) {
source = ((VarModel)source).getVarType();
}
if (source instanceof TypeReferenceModel) {
Collection<? extends CodeElementModel> resolved = ((TypeReferenceModel)source).resolve();
if (resolved.isEmpty()) {
return resolved;
} else if (resolved.size() == 1) {
return getSelectableMembers(resolved.iterator().next(), name);
} else {
List<CodeElementModel> result = new ArrayList<>();
for (CodeElementModel i : resolved) {
result.addAll(getSelectableMembers(i, name));
}
return result;
}
}
boolean usePointer = source instanceof TypeModelImpl;
if (source instanceof TypePointerModel) {
usePointer = false;
} else if (source instanceof InterfaceModel) {
usePointer = false;
} else if (source instanceof TypeAliasModel) {
usePointer = !(((TypeAliasModel)source).getType() instanceof InterfaceModel);
}
if (usePointer) {
assert source instanceof TypeModelImpl;
// resolve using &x to get all possibilities
TypeModelImpl typeSource = (TypeModelImpl)source;
source = new TypePointerModelImpl(typeSource);
}
Collection<? extends CodeElementModel> members = name.isEmpty() ? source.getMembers() : source.getMembers(name);
if (!includeExtendedMembers) {
return members;
}
List<CodeElementModel> allMembers = new ArrayList<>();
allMembers.addAll(members);
if (source instanceof TypePointerModel) {
source = ((TypePointerModel)source).getElementType();
}
if (source instanceof TypeAliasModel) {
// work with the underlying type for the extended portion
source = ((TypeAliasModel)source).getType();
}
List<CodeElementModel> extendedSources = new ArrayList<>();
if (source instanceof TypeReferenceModel) {
// work with the underlying type for the extended portion
extendedSources.addAll(((TypeReferenceModel)source).resolve());
for (int i = 0; i < extendedSources.size(); i++) {
CodeElementModel extendedSource = extendedSources.get(i);
if (extendedSource instanceof TypePointerModel) {
extendedSource = ((TypePointerModel)extendedSource).getElementType();
}
if (extendedSource instanceof TypeAliasModel) {
// work with the underlying type for the extended portion
extendedSource = ((TypeAliasModel)extendedSource).getType();
}
extendedSources.set(i, extendedSource);
}
} else {
extendedSources.add(source);
}
for (CodeElementModel extendedSource : extendedSources) {
if (extendedSource instanceof InterfaceModel) {
Set<TypeModel> visitedInterfaces = new HashSet<>();
Deque<TypeModel> remainingInterfaces = new ArrayDeque<>(((InterfaceModel)extendedSource).getImplementedInterfaces());
while (!remainingInterfaces.isEmpty()) {
TypeModel current = remainingInterfaces.pop();
if (current instanceof TypeReferenceModel) {
remainingInterfaces.addAll(((TypeReferenceModel)current).resolve());
continue;
} else if (current instanceof TypeAliasModel) {
remainingInterfaces.add(((TypeAliasModel)current).getType());
continue;
}
if (!visitedInterfaces.add(current)) {
continue;
}
if (current instanceof InterfaceModel) {
InterfaceModel currentInterface = (InterfaceModel)current;
remainingInterfaces.addAll(currentInterface.getImplementedInterfaces());
}
allMembers.addAll(getSelectableMembers(current, name, false));
}
} else if (extendedSource instanceof StructModel) {
Set<TypeModel> visitedStructures = new HashSet<>();
Deque<TypeModel> remainingStructures = new ArrayDeque<>(getAnonymousFieldTypes((StructModel)extendedSource));
while (!remainingStructures.isEmpty()) {
TypeModel current = remainingStructures.pop();
if (current instanceof TypeReferenceModel) {
remainingStructures.addAll(((TypeReferenceModel)current).resolve());
continue;
} else if (current instanceof TypeAliasModel) {
remainingStructures.add(((TypeAliasModel)current).getType());
// Don't continue here or it won't pick up the method set
//continue;
}
if (!visitedStructures.add(current)) {
continue;
}
if (current instanceof StructModel) {
StructModel currentInterface = (StructModel)current;
remainingStructures.addAll(getAnonymousFieldTypes(currentInterface));
}
allMembers.addAll(getSelectableMembers(current, name, false));
}
}
}
return allMembers;
}
public static Collection<? extends TypeModel> getAnonymousFieldTypes(StructModel struct) {
Collection<TypeModel> types = new ArrayList<>();
for (FieldModel field : struct.getFields()) {
if (field.isAnonymous()) {
types.add(field.getVarType());
}
}
return types;
}
private SemanticAnalyzer() {
}
}