package org.deved.antlride.core.model.ast;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.deved.antlride.core.model.IAlternative;
import org.deved.antlride.core.model.IBlock;
import org.deved.antlride.core.model.ICallExpression;
import org.deved.antlride.core.model.IGrammar;
import org.deved.antlride.core.model.IModelElement;
import org.deved.antlride.core.model.INotOperator;
import org.deved.antlride.core.model.IRange;
import org.deved.antlride.core.model.IRule;
import org.deved.antlride.core.model.ISemanticPredicate;
import org.deved.antlride.core.model.IStatement;
import org.deved.antlride.core.model.ISyntacticPredicate;
import org.deved.antlride.core.model.ISemanticPredicate.PredicateType;
public class ToEbnfVisitor extends AAbstractModelElementVisitor {
private static final String RP = ")";
private static final String LP = "(";
private static final String SPACE = " ";
private static final String RANGE = "..";
private static final String QUOTE = "\'";
private static final String ASSIGN = " ::= ";
private static final String ALT = " | ";
private static final String PREDICATE = "??";
private static final String NOT = "~";
private static final String PREDICATE_OP = "=>";
private StringBuilder buff;
private Map<String, String> ebnfMap;
private Set<IModelElement> visited;
private boolean joinRules = false;
public ToEbnfVisitor(IRule rule) {
this(new HashSet<IModelElement>(), new HashMap<String, String>());
buff.append(rule.getElementName()).append(ASSIGN);
accept(rule);
}
private ToEbnfVisitor(Set<IModelElement> visited,
Map<String, String> ebnfMap) {
buff = new StringBuilder();
this.visited = visited;
this.ebnfMap = ebnfMap;
}
@Override
public void accept(IModelElement node) {
visited.add(node);
super.accept(node);
}
public String toEbnf() {
return buff.toString();
}
@Override
public boolean visitSemanticPredicate(ISemanticPredicate node) {
if (node.hasPredicate()) {
// ignore validating semantic predicates
String elementName = quoteChars(normalize(node.getCondition()
.getText()));
buff.append(PREDICATE).append(QUOTE).append(elementName);
if (node.getPredicateType() == PredicateType.GATED_SEMPRED) {
buff.append(PREDICATE_OP);
}
buff.append(QUOTE).append(" (");
}
return true;
}
private String normalize(String text) {
StringBuilder buff = new StringBuilder();
StringTokenizer tokenizer = new StringTokenizer(text);
while (tokenizer.hasMoreElements()) {
buff.append(tokenizer.nextToken()).append(SPACE);
}
buff.setLength(buff.length() - SPACE.length());
return buff.toString().trim();
}
@Override
public boolean visitNotOperator(INotOperator node) {
buff.append(NOT).append(LP);
return true;
}
@Override
public void endvisitNotOperator(INotOperator node) {
buff.append(RP);
}
@Override
public void endvisitSemanticPredicate(ISemanticPredicate node) {
if (node.hasPredicate()) {
buff.append(RP);
}
}
@Override
public boolean visitSyntacticPredicate(ISyntacticPredicate node) {
ToEbnfVisitor visitor = new ToEbnfVisitor(visited, ebnfMap);
visitor.accept(node.getCondition());
buff.append(PREDICATE).append(QUOTE).append(
quoteChars(visitor.toEbnf())).append("=>").append(QUOTE);
return true;
}
private String quoteChars(String text) {
return text.trim().replace("'", "\\'");
}
@Override
public boolean visitSyntacticPredicateCondition(IStatement node) {
return false;
}
@Override
public void endvisitSyntacticPredicate(ISyntacticPredicate node) {
}
@Override
public boolean visitRange(IRange node) {
buff.append(QUOTE)
.append(removeQuotes(node.getLeft().getElementName())).append(
RANGE).append(
removeQuotes(node.getRight().getElementName())).append(
QUOTE).append(node.getEbnfOperator().description())
.append(SPACE);
return false;
}
private String removeQuotes(String statement) {
return statement.replace("\'", "");
}
@Override
public boolean visitCallExpression(ICallExpression node) {
if (joinRules) {
// TODO: this need more work
if (node.isStringLiteralCall() || node.isLexerRuleCall()) {
buff.append(node.getElementName());
buff.append(node.getEbnfOperator().description()).append(SPACE);
} else {
IGrammar grammar = node.getAdapter(IGrammar.class);
IRule rule = grammar.findRule(node.getElementName());
if (rule == null || visited.contains(rule)) {
// just a token ref
buff.append(node.getElementName());
buff.append(node.getEbnfOperator().description()).append(
SPACE);
} else {
String ebnf = ebnfMap.get(node.getElementName());
if (ebnf == null) {
ToEbnfVisitor ebnfVisitor = new ToEbnfVisitor(visited,
ebnfMap);
ebnfVisitor.accept(rule);
ebnf = ebnfVisitor.toEbnf();
ebnfMap.put(node.getElementName(), ebnf);
}
buff.append(SPACE).append(ebnf).append(SPACE);
}
}
} else {
buff.append(node.getElementName());
buff.append(node.getEbnfOperator().description()).append(SPACE);
}
return true;
}
@Override
public boolean visitBlock(IBlock node) {
visitBlock(node, false);
return true;
}
private void visitBlock(IBlock node, boolean ruleBody) {
if (!ruleBody) {
buff.append(LP);
}
}
@Override
public boolean visitAlternative(IAlternative node) {
boolean treatAsEmpty = false;
// check for validating semantic predicates
// their are excluded from the ebnf notation
boolean validatingPredicates = true;
for (int i = 0; i < node.size(); i++) {
IStatement statement = node.get(i);
if (statement instanceof ISemanticPredicate) {
ISemanticPredicate predicate = (ISemanticPredicate) statement;
validatingPredicates = validatingPredicates
&& predicate.isValidating();
} else {
validatingPredicates = false;
}
}
treatAsEmpty = validatingPredicates;
return !treatAsEmpty;
}
@Override
public void endvisitAlternative(IAlternative node) {
buff.append(ALT);
}
private void endvisitBlock(IBlock node, boolean ruleBody) {
if (buff.toString().endsWith(ALT)) {
buff.setLength(buff.length() - ALT.length());
}
if (!ruleBody) {
buff.append(RP).append(node.getEbnfOperator().description())
.append(SPACE);
}
super.endvisitBlock(node);
}
@Override
public void endvisitBlock(IBlock node) {
endvisitBlock(node, false);
}
@Override
public void endvisitRuleBody(IBlock node) {
endvisitBlock(node, true);
}
@Override
public boolean visitRuleBody(IBlock node) {
visitBlock(node, true);
return true;
}
public boolean visitRewriteAlternative(IAlternative node) {
return false;
}
@Override
public boolean visitRewriteBlock(IBlock node) {
return false;
}
}