package grammar.java; import static trees.MatchSpec.anySpec; import static trees.MatchSpec.or; import static trees.MatchSpec.rule; import static trees.MatchSpec.str; import static util.StringUtils.unescape; import java.util.Arrays; import java.util.List; import java.util.Stack; import driver.Context; import grammar.Expression; import grammar.GrammarDSL; import grammar.MatchCallbacks; import parser.Match; import trees.MatchSpec; /******************************************************************************* * The base class for callbacks made upon encountering a parsing expression. * * All the callbacks for the different kind of expressions notations are meant * to share a single expression stack ({@link Context#expressionStack}). When * the callback for an expression notation is invoked, it will pop the * expression specified by its children from the stack, then push the resulting * expression on the stack. * * How spacing works: unless a trailing dash ("-") is present, optional spacing * is inserted after all string literals. References to Java non-literals retain * any associated whitespace they might have. The rules "spacing" (optional * whitespace) and "fspacing" (mandatory whitespace) can also be used. * Whitespace inside string literals is matched literally. Whitespace between * expression and operators is non-significant. */ class CallbacksExpression extends MatchCallbacks { /****************************************************************************/ Stack<Expression> stack() { return Context.get().expressionStack; } /***************************************************************************** * Pick the childs that are placed in reverse order on the stack. */ List<Expression> getChilds(int n) { Expression[] childs = new Expression[n]; for (int i = n - 1 ; i >= 0 ; --i) { childs[i] = stack().pop(); } return Arrays.asList(childs); } /****************************************************************************/ public static String unescapeChars(String chars) { return unescape(chars.replaceAll("\\\\]", "]")); } } //////////////////////////////////////////////////////////////////////////////// /******************************************************************************/ class ChoiceCallbacks extends CallbacksExpression { @Override public void parseDo(Match match) { int n = match.all(rule("sequenceParsingExpression")).length; stack().push(new Expression.Choice(getChilds(n))); } } /******************************************************************************/ class SequenceCallbacks extends CallbacksExpression { @Override public void parseDo(Match match) { int n = match.all(rule("binaryParsingExpression")).length; if (n == 1) { return; } stack().push(new Expression.Sequence(getChilds(n))); } } /******************************************************************************/ class AndCallbacks extends CallbacksExpression { @Override public void parseDo(Match match) { stack().push(new Expression.And(stack().pop())); } } /******************************************************************************/ class NotCallbacks extends CallbacksExpression { @Override public void parseDo(Match match) { stack().push(new Expression.Not(stack().pop())); } } /******************************************************************************/ class CaptureCallbacks extends CallbacksExpression { @Override public void parseDo(Match match) { Match captureNameM = match.firstBeforeFirst(rule("identifier"), str(":")); String captureName = captureNameM != null ? captureNameM.string() : match.first(rule("referenceParsingExpression")).string(); Context.get().captureNames.add(captureName); stack().push(new Expression.Capture(captureName, stack().pop())); } } /******************************************************************************/ class UntilCallbacks extends CallbacksExpression { @Override public void parseDo(Match match) { Expression two = stack().pop(), one = stack().pop(); stack().push(GrammarDSL.until(one, two)); } } /******************************************************************************/ class UntilOnceCallbacks extends CallbacksExpression { @Override public void parseDo(Match match) { Expression two = stack().pop(), one = stack().pop(); stack().push(GrammarDSL.untilOnce(one, two)); } } /******************************************************************************/ class ListCallbacks extends CallbacksExpression { @Override public void parseDo(Match match) { Expression two = stack().pop(), one = stack().pop(); stack().push(GrammarDSL.list(two, one)); } } /******************************************************************************/ class SuffixCallbacks extends CallbacksExpression { @Override public void parseDo(Match match) { Expression expr = stack().pop(); Match suffix = match.firstAfterFirst( or(rule("pegStar"), rule("pegPlus"), rule("qMark")), rule("primaryParsingExpression")); if (suffix != null) switch(suffix.expr.toString()) { case "pegStar": expr = new Expression.Star(expr); break; case "pegPlus": expr = new Expression.Plus(expr); break; case "qMark": expr = new Expression.Optional(expr); break; } stack().push(expr); } } /******************************************************************************/ class AnyCallbacks extends CallbacksExpression { @Override public void parseDo(Match match) { stack().push(Expression.Any.GET); } } /******************************************************************************/ class LiteralCallbacks extends CallbacksExpression { @Override public void parseDo(Match match) { String withQuotes = match.first(rule("stringLiteral")).string(); String noQuotes = withQuotes.substring(1, withQuotes.length() - 1); stack().push(new Expression.StringLiteral(unescape(noQuotes))); if (!match.has(MatchSpec.str("-"))) { stack().push(new Expression.Reference("spacing")); stack().push(new Expression.Sequence(getChilds(2))); } } } /******************************************************************************/ class ReferenceCallbacks extends CallbacksExpression { @Override public void parseDo(Match match) { stack().push(new Expression.Reference(match.string())); } } /******************************************************************************/ class CharClassCallbacks extends CallbacksExpression { @Override public void parseDo(Match match) { boolean negated = match.has(match.firstBeforeFirst( str("^"), rule("lSqBra"))); String chars = match.firstAfterFirst(anySpec, rule("lSqBra")).string(); chars = unescapeChars(chars); stack().push(new Expression.CharClass(chars, negated)); } } /******************************************************************************/ class CharRangeCallbacks extends CallbacksExpression { @Override public void parseDo(Match match) { boolean negated = match.has(match.first(str("^"))); char start = unescapeChars(match.firstAfterFirst( rule("pegChar"), rule("lSqBra")).string()).charAt(0); char end = unescapeChars(match.lastBeforeFirst( rule("pegChar"), rule("rSqBra")).string()).charAt(0); stack().push(new Expression.Range(start, end, negated)); } }