/*
* 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.parser;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.antlr.netbeans.editor.text.DocumentSnapshot;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.RuleContext;
import org.antlr.v4.runtime.RuleDependencies;
import org.antlr.v4.runtime.RuleDependency;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.tree.RuleNode;
import org.antlr.works.editor.antlr4.classification.DocumentSnapshotCharStream;
import org.tvl.goworks.editor.go.highlighter.SemanticHighlighter;
import org.tvl.goworks.editor.go.parser.generated.AbstractGoParser;
import org.tvl.goworks.editor.go.parser.generated.GoParserBaseVisitor;
/**
*
* @author Sam Harwell
*/
public class GoParser extends AbstractGoParser {
private final Set<String> packageNames = new HashSet<>();
private DocumentSnapshot snapshot;
public GoParser(TokenStream input) {
super(input);
CharStream charStream = input != null ? input.getTokenSource().getInputStream() : null;
if (charStream instanceof DocumentSnapshotCharStream) {
DocumentSnapshotCharStream documentSnapshotCharStream = (DocumentSnapshotCharStream)charStream;
this.snapshot = documentSnapshotCharStream.getSnapshot();
}
}
@Override
public void reset() {
super.reset();
// must check for null because reset() is called from the Parser constructor, before fields of GoParser are initialized
if (packageNames != null) {
packageNames.clear();
}
}
@Override
public void setInputStream(TokenStream input) {
super.setInputStream(input);
CharStream charStream = input != null ? input.getTokenSource().getInputStream() : null;
if (charStream instanceof DocumentSnapshotCharStream) {
DocumentSnapshotCharStream documentSnapshotCharStream = (DocumentSnapshotCharStream)charStream;
this.snapshot = documentSnapshotCharStream.getSnapshot();
} else {
this.snapshot = null;
}
}
@Override
protected void addPackageName(Token token) {
String name = getPackageName(token);
if (name == null || name.isEmpty()) {
return;
}
packageNames.add(name);
}
@Override
protected boolean isPackageName(Token token) {
return token != null && packageNames.contains(token.getText());
}
public Collection<? extends String> getPackageNames() {
return packageNames;
}
public void setPackageNames(Collection<? extends String> names) {
packageNames.clear();
packageNames.addAll(names);
}
@RuleDependency(recognizer=GoParser.class, rule=RULE_operand, version=0)
@Override
protected boolean isLiteralAllowed(Token nextToken, OperandContext context) {
if (nextToken.getType() != IDENTIFIER) {
return true;
}
for (ParserRuleContext current = context; current != null; current = current.getParent()) {
Boolean allowed = CompositeLiteralAllowedVisitor.INSTANCE.visit(current);
if (allowed == null) {
continue;
}
return allowed;
}
return true;
}
@Override
protected boolean isBuiltInMethodName(Token token) {
return token != null && SemanticHighlighter.PREDEFINED_FUNCTIONS.contains(token.getText());
}
protected static final class CompositeLiteralAllowedVisitor extends GoParserBaseVisitor<Boolean> {
protected static final CompositeLiteralAllowedVisitor INSTANCE = new CompositeLiteralAllowedVisitor();
@RuleDependency(recognizer=GoParser.class, rule=RULE_expression, version=1)
@Override
public Boolean visitChildren(RuleNode node) {
RuleContext ruleContext = node.getRuleContext();
if (ruleContext instanceof ExpressionContext) {
return visitAnyExpression((ExpressionContext)ruleContext);
}
return super.visitChildren(node);
}
@Override
protected Boolean defaultResult() {
return null;
}
@Override
protected boolean shouldVisitNextChild(RuleNode node, Boolean currentResult) {
return false;
}
@RuleDependencies({
@RuleDependency(recognizer=GoParser.class, rule=RULE_expression, version=1),
@RuleDependency(recognizer=GoParser.class, rule=RULE_exprSwitchStmt, version=0),
@RuleDependency(recognizer=GoParser.class, rule=RULE_arrayLength, version=0),
@RuleDependency(recognizer=GoParser.class, rule=RULE_elementNameOrIndex, version=1),
@RuleDependency(recognizer=GoParser.class, rule=RULE_channel, version=1),
@RuleDependency(recognizer=GoParser.class, rule=RULE_incDecStmt, version=1),
@RuleDependency(recognizer=GoParser.class, rule=RULE_ifStmt, version=0),
@RuleDependency(recognizer=GoParser.class, rule=RULE_condition, version=0),
@RuleDependency(recognizer=GoParser.class, rule=RULE_rangeClause, version=0),
@RuleDependency(recognizer=GoParser.class, rule=RULE_recvStmt, version=0),
@RuleDependency(recognizer=GoParser.class, rule=RULE_recvExpr, version=0),
@RuleDependency(recognizer=GoParser.class, rule=RULE_deferStmt, version=0),
@RuleDependency(recognizer=GoParser.class, rule=RULE_expressionList, version=1),
@RuleDependency(recognizer=GoParser.class, rule=RULE_operand, version=0),
@RuleDependency(recognizer=GoParser.class, rule=RULE_value, version=0),
@RuleDependency(recognizer=GoParser.class, rule=RULE_conversion, version=0),
@RuleDependency(recognizer=GoParser.class, rule=RULE_expressionStmt, version=1),
@RuleDependency(recognizer=GoParser.class, rule=RULE_sendStmt, version=1),
@RuleDependency(recognizer=GoParser.class, rule=RULE_simpleStmt, version=1),
@RuleDependency(recognizer=GoParser.class, rule=RULE_typeSwitchGuard, version=0),
@RuleDependency(recognizer=GoParser.class, rule=RULE_goStmt, version=0),
})
private Boolean visitAnyExpression(ExpressionContext ctx) {
ParserRuleContext parent = ctx.getParent();
if (parent == null) {
return null;
}
switch (parent.getRuleIndex()) {
case RULE_exprSwitchStmt:
case RULE_arrayLength:
case RULE_channel:
case RULE_incDecStmt:
case RULE_ifStmt:
case RULE_condition:
case RULE_rangeClause:
case RULE_recvStmt:
case RULE_recvExpr:
case RULE_deferStmt:
return false;
case RULE_elementNameOrIndex:
case RULE_expressionList:
case RULE_operand:
case RULE_value:
case RULE_conversion:
case RULE_expressionStmt:
case RULE_sendStmt:
case RULE_typeSwitchGuard:
case RULE_goStmt:
return true;
// this one is needed due to explicit left factoring of expression
// into simpleStmt, and this is called before the parse tree is
// restored to the intended shape
case RULE_simpleStmt:
return true;
case RULE_expression:
if (parent instanceof SelectorExprContext
|| parent instanceof IndexExprContext
|| parent instanceof SliceExprContext
|| parent instanceof TypeAssertionExprContext) {
return true;
} else if (parent instanceof UnaryExprContext) {
UnaryExprContext unaryParent = (UnaryExprContext)parent;
if (unaryParent.op != null) {
switch (unaryParent.op.getType()) {
case Amp:
// address of, but still has to be in a valid context
return null;
default:
return false;
}
}
// not building parse trees, assume was a valid op
return true;
} else if (parent instanceof CompareExprContext) {
CompareExprContext compareParent = (CompareExprContext)parent;
if (compareParent.op != null) {
switch (compareParent.op.getType()) {
case EqualEqual:
case BangEqual:
// equality comparison is allowed, but still has to be in a valid context
return null;
default:
return false;
}
}
// not building parse trees, assume was a valid op
return true;
}
return false;
}
return null;
}
}
}