package grammar;
import grammar.Expression.And;
import grammar.Expression.Any;
import grammar.Expression.CharClass;
import grammar.Expression.Choice;
import grammar.Expression.Not;
import grammar.Expression.Optional;
import grammar.Expression.Plus;
import grammar.Expression.Range;
import grammar.Expression.Reference;
import grammar.Expression.Rule;
import grammar.Expression.Sequence;
import grammar.Expression.Star;
import grammar.Expression.StringLiteral;
import java.util.ArrayList;
import java.util.Arrays;
/**
* A Domain Specific Language (DSL) to clearly express PEG grammar rules.
*
* Some of the construct directly map to a subclass of Expression. In those
* case, report to the documentation of the appropriate class. If you are
* familiar with PEG, it should all be pretty self-evident.
*/
public class GrammarDSL
{
/****************************************************************************/
public static Choice choice(Expression... exprs)
{
return new Choice(Arrays.asList(exprs));
}
/****************************************************************************/
public static Sequence seq(Expression... exprs)
{
return new Sequence(Arrays.asList(exprs));
}
/****************************************************************************/
public static And and(Expression expr)
{
return new And(expr);
}
/****************************************************************************/
public static And and(Expression... exprs)
{
return new And(new Sequence(Arrays.asList(exprs)));
}
/****************************************************************************/
public static Not not(Expression expr)
{
return new Not(expr);
}
/****************************************************************************/
public static Not not(Expression... exprs)
{
return new Not(new Sequence(Arrays.asList(exprs)));
}
/****************************************************************************/
public static Plus plus(Expression expr)
{
return new Plus(expr);
}
/****************************************************************************/
public static Plus plus(Expression... exprs)
{
return new Plus(new Sequence(Arrays.asList(exprs)));
}
/****************************************************************************/
public static Star star(Expression expr)
{
return new Star(expr);
}
/****************************************************************************/
public static Star star(Expression... exprs)
{
return new Star(new Sequence(Arrays.asList(exprs)));
}
/****************************************************************************/
public static Optional opt(Expression expr)
{
return new Optional(expr);
}
/****************************************************************************/
public static Optional opt(Expression... exprs)
{
return new Optional(new Sequence(Arrays.asList(exprs)));
}
/****************************************************************************/
public static Reference ref(String name)
{
return new Reference(name);
}
/****************************************************************************/
public static StringLiteral str(String str)
{
return new StringLiteral(str);
}
/****************************************************************************/
public static CharClass chars(String str)
{
return new CharClass(str, false);
}
/****************************************************************************/
public static CharClass notChars(String str)
{
return new CharClass(str, true);
}
/****************************************************************************/
public static Range range(char start, char end)
{
return new Range(start, end, false);
}
/****************************************************************************/
public static Range notRange(char start, char end)
{
return new Range(start, end, true);
}
/*****************************************************************************
* Iterates iterateThis until untilThis is encountered.
*/
public static Sequence until(Expression iterateThis, Expression untilThis)
{
return seq(star(not(untilThis), iterateThis), untilThis);
}
/*****************************************************************************
* Iterates iterateThis until untilThis is encountered. iterateThis must
* appear at least one time.
*/
public static Sequence untilOnce(Expression iterateThis, Expression untilThis)
{
return seq(star(not(untilThis), iterateThis), untilThis);
}
/*****************************************************************************
* A non-empty sequence of expr, separated by sep.
*/
public static Sequence list(Expression sep, Expression expr)
{
return seq(expr, star(sep, expr));
}
/*****************************************************************************
* A rule to express direct (within the same rule) left recursion. The first
* parameter is the base (non-left-recursive) expression. The second parameter
* is an implicitly left-recursive alternative. Said otherwise, the second
* parameter represents all possible suffix that can be applied to the base
* expression.
*
* Currently, the left recursion is translated to right recursion. The
* construct is at the very least useful to clearly mark uses of
* left-recursion in the code for what they are.
*/
public static Sequence lrecur(Expression base, Expression suffix)
{
return seq(base, star(suffix));
}
/*****************************************************************************
* Direct left recursion, with at least one suffix.
*/
public static Sequence lrecurPlus(Expression base, Expression suffix)
{
return seq(base, plus(suffix));
}
/*****************************************************************************
* Apply to an item "repeat" that is inside a repeating construct to ensure
* that the last repeated item does not match "end".
*
* e.g. star(doesntEndWith(c, choice(a, b, c))) will match any sequence of a,
* b or c that does not end with a c.
*
* Usually "repeat" is a choice from which we want to exclude an alternative.
* In those cases, make sure that "end" is not a potential prefix of some
* alternatives you don't want to exclude.
*/
public static final Expression doesntEndWith(Expression end, Expression repeat)
{
// If this didn't to work in lRecur, it would have been:
// return star/plus(star(end), repeat);
return seq(not(end, not(repeat)), repeat);
}
/*****************************************************************************
* Apply to an item "repeat" that is inside a repeating construct to ensure
* that the last repeated item does match "end".
*
* e.g. star(endsWith(c, choice(a, b, c))) will match any sequence of a, b or
* c that does end with a c.
*
* Same remark as in {@link #doesntEndWith}.
*/
public static final Expression endsWith(Expression end, Expression repeat)
{
// If this didn't to work in lRecur, it would have been:
// return star/plus(until(repeat, end));
return seq(not(not(end), repeat, not(repeat)), repeat);
}
/*****************************************************************************
* Marks the expression as atomic.
* @see Expression
*/
public static final Expression atomic(Expression expr)
{
expr.atomic = true;
return expr;
}
/*****************************************************************************
* Trick: If you want to make an alias for a rule name, just define a new rule
* like below. But be aware that the old rule will still appear in the match
* tree.
*
* Rule rule2 = rule(newName, rule1);
*/
public static final Rule rule(String name, Expression... exprs)
{
return new Rule(name, new ArrayList<Expression>(Arrays.asList(exprs)));
}
/*****************************************************************************
* The point of omitting the rule name (by setting it to null) is to affect it
* later (in grammar.Grammar, through reflection since the field is final), to
* the name of the class field containing the returned Rule. So always affect
* the result of this method to a public class field.
*/
public static final Rule rule(Expression... exprs)
{
return new Rule(null, new ArrayList<Expression>(Arrays.asList(exprs)));
}
/*****************************************************************************
* Makes a new rule with a single alternative which is a sequence of the
* passed expressions.
*/
public static final Rule rule_seq(String name, Expression... exprs)
{
return rule(name, seq(exprs));
}
/*****************************************************************************
* Like rule_seq(String, Expression...), but omitting the rule name for the
* same reason as rule(Expression...).
*/
public static final Rule rule_seq(Expression... exprs)
{
/** Same remark as rule(Expression...) */
return rule(seq(exprs));
}
/****************************************************************************/
public static final Any any = Any.GET;
/****************************************************************************/
public static final Expression endOfInput = new Not(any);
}