package grammar; import java.util.Collections; import java.util.List; import compiler.Macro; import static java.util.Arrays.asList; /** * This class was greatly inspired by the Expr class from the parser generator * Mouse, by Roman R. Redziejowski (www.romanredz.se). * * This abstract class represent a parsing expression (the kind that underpins * parsing expression grammars (PEG)). * * Its concrete (inner) subclasses represent all the kinds of parsing expression * supported by this framework (syntactic sugar notwithstanding). * * The class concerns itself with modeling the relation between expressions and * sub-expressions, keeping track of which expression are also named grammar * rules and building textual representation for expressions. */ public abstract class Expression { //============================================================================ // FIELDS //============================================================================ /***************************************************************************** * The grammar this expression belongs to. */ public Grammar grammar; /***************************************************************************** * Holds a textual representation of the expression, generated using rule * names or textual representations of sub-expressions. Expressions whose * textual representation are equivalent are guaranteed to be semantically * equivalent. */ protected String repr; /***************************************************************************** * Array of sub-expressions. null for expressions that have no children, and * potentially empty for expressions with a variable number of children. */ private final List<Expression> children; /***************************************************************************** * Operator precedence, higher means more precedence. */ final int precedence; /***************************************************************************** * Is the expression atomic? An atomic expression does generate a leaf in the * match tree, the errors encountered while parsing it are not logged, leaving * a single error at the start of the whole expression in case it fails to * parse. * * Said otherwise, an atomic expression is parsed as tough the parser was a * Recognizer, even if it isn't. * * Literals expressions (characters and strings) are naturally atomic. So is * the Not expression. Composite expressions can also be made atomic, in order * to avoid polluting the match tree with irrelevant information. */ public boolean atomic = false; /****************************************************************************/ public MatchCallbacks callbacks; //============================================================================ // CONSTRUCTOR //============================================================================ /****************************************************************************/ Expression(int precedence) { this.precedence = precedence; this.children = Collections.<Expression>emptyList(); } /****************************************************************************/ Expression(int precedence, List<Expression> children) { this.precedence = precedence; this.children = children; } //============================================================================ // METHODS //============================================================================ /***************************************************************************** * Returns the rule name if the expression is a rule, else a generated name. * Expressions whose name are equivalent are guaranteed to be semantically * equivalent. */ public String toString() { return repr; } /****************************************************************************/ public MatchCallbacks callbacks() { return (callbacks == null) ? MatchCallbacks.DEFAULT : callbacks; } /***************************************************************************** * @see ExpressionVisitor */ public abstract void accept(ExpressionVisitor visitor); /****************************************************************************/ public List<Expression> children() { return children; } /****************************************************************************/ public Expression child() { return children.get(0); } //============================================================================ // CLASSES //============================================================================ /***************************************************************************** * Represents a reference to a named grammar rule: '<' <ruleName> '>' */ public static class Reference extends Expression { public final String referencedRule; public Reference(final String referencedRule) { super(4); this.referencedRule = referencedRule; } // References get eliminated, so we don't want to visit them. public void accept(ExpressionVisitor visitor) { } } /***************************************************************************** * Represent a grammar rule. A rule works just like a choice in that that it * can have multiple alternatives. This enables modifications of the grammar * at compile time. */ public static class Rule extends Expression { /* Final field might be changed by grammar.Grammar. */ public final String name; /** Unique ID */ public int id; public Rule(String ruleName, List<Expression> children) { super(4, children); this.name = ruleName; } public void accept(ExpressionVisitor visitor) { visitor.visit(this); } } /***************************************************************************** * A rule generated by a macro definition. */ public static class MacroRule extends Rule { public final Macro macro; public MacroRule(String name, Macro macro, Expression child) { super(name, asList(child)); this.macro = macro; } public void accept(ExpressionVisitor visitor) { visitor.visit(this); } } /***************************************************************************** * Represent a named-capture of an expression in a user-specified rule. */ public static class Capture extends Expression { public final String captureName; public Capture(final String captureName, final Expression child) { super(3 , asList(child)); this.captureName = captureName; } public void accept(ExpressionVisitor visitor) { visitor.visit(this); } } /***************************************************************************** * Represents a choice amongst alternatives: <expr> (| <expr>)* */ public static class Choice extends Expression { public Choice(final List<Expression> children) { super(0, children); } public void accept(ExpressionVisitor visitor) { visitor.visit(this); } } /***************************************************************************** * Represents a sequence of expressions: <expr>* */ public static class Sequence extends Expression { public Sequence(final List<Expression> children) { super(1, children); } public void accept(ExpressionVisitor visitor) { visitor.visit(this); } } /***************************************************************************** * Represents an additional check for expression conformance: &<expr> */ public static class And extends Expression { public And(final Expression child) { super(2, asList(child)); } public void accept(ExpressionVisitor visitor) { visitor.visit(this); } } /***************************************************************************** * Represents a check for lack of conformance to an expression: !<expr> */ public static class Not extends Expression { public Not(final Expression child) { super(2, asList(child)); this.atomic = true; } public void accept(ExpressionVisitor visitor) { visitor.visit(this); } } /***************************************************************************** * Represents a sequence of one or more expression of the same kind: <expr>+ */ public static class Plus extends Expression { public Plus(final Expression child) { super(3, asList(child)); } public void accept(ExpressionVisitor visitor) { visitor.visit(this); } } /***************************************************************************** * Represents a sequence of zero or more expression of the same kind: <expr>* */ public static class Star extends Expression { public Star(final Expression child) { super(3, asList(child)); } public void accept(ExpressionVisitor visitor) { visitor.visit(this); } } /***************************************************************************** * Represents an optional expression: <expr>? */ public static class Optional extends Expression { public Optional(final Expression child) { super(3, asList(child)); } public void accept(ExpressionVisitor visitor) { visitor.visit(this); } } /***************************************************************************** * Represents a String literal, such as "potato" */ public static class StringLiteral extends Expression { public final String string; public StringLiteral(final String string) { super(4); this.atomic = true; this.string = string; } public void accept(ExpressionVisitor visitor) { visitor.visit(this); } } /***************************************************************************** * Represents a character range, such as [a - z] */ public static class Range extends Expression { public final char first; public final char last; public final boolean negated; public Range(final char first, final char last, final boolean negated) { super(4); this.atomic = true; this.first = first; this.last = last; this.negated = negated; } public void accept(ExpressionVisitor visitor) { visitor.visit(this); } } /***************************************************************************** * Represents a character class, such as [aeiouy] or [^aeiouy]. The leading * caret (^) indicates negation, so the class represented is the one that * contains all the characters that do not follow the caret. */ public static class CharClass extends Expression { public final String chars; public final boolean negated; public CharClass(final String chars, final boolean negated) { super(4); this.atomic = true; this.chars = chars; this.negated = negated; } public void accept(ExpressionVisitor visitor) { visitor.visit(this); } } /***************************************************************************** * Represents any character. */ public static class Any extends Expression { public static Any GET = new Any(); private Any() { super(4); this.atomic = true; } public void accept(ExpressionVisitor visitor) { visitor.visit(this); } } }