** (Advanced)
** A collection of utility methods that return PEG rules.
**
** Use when programmatically creating PEG grammar.
@Js
mixin Rules {
// ---- Str rules -----------------------------------------------------------------------------
** Matches the given string.
**
** Example PEG notation:
**
** "ChuckNorris"
static Rule str(Str string, Bool ignoreCase := false) {
StrRule(string, ignoreCase)
}
** Matches one or more characters up to (but not including) the given string.
**
** Example PEG notation:
**
** (!"ChuckNorris" .)+
static Rule strNot(Str string, Bool ignoreCase := false) {
StrNotRule(string, ignoreCase)
}
// ---- Char rules ----------------------------------------------------------------------------
** Matches any character.
**
** PEG notation:
**
** .
static Rule anyChar() {
AnyCharRule()
}
** Multi-purpose char class rule.
**
** Example PEG notation:
**
** [a-z_]i
static Rule charRule(Str charClass) {
CharRule.fromStr(charClass)
}
** Matches the given character.
**
** Example PEG notation:
**
** [C]
static Rule char(Int char, Bool not := false) {
not ? CharRule(char.toChar.toCode(null).replace("]", "\\]").replace("-", "\\-"), not) |Int peek->Bool| { char == peek }
: StrMimickCharRule(char, false) // this looks better than char rule & parses faster than a str rule
}
** Matches any character except the given one.
**
** Example PEG notation:
**
** [^C]
static Rule charNot(Int ch) {
char(ch, true)
}
** Matches any character in the given list.
**
** Example PEG notation:
**
** [ChukNoris]
static Rule charOf(Int[] chars, Bool not := false) {
if (chars.isEmpty) throw ArgErr("Chars may not be empty")
return CharRule(Str.fromChars(chars).toCode(null), not) |Int peek->Bool| { chars.contains(peek) }
}
** Matches any character *not* in the given list.
**
** Example PEG notation:
**
** [^ChukNoris]
static Rule charNotOf(Int[] chars) {
charOf(chars, true)
}
** Matches any character in the given range.
**
** Example PEG notation:
**
** [C-N]
static Rule charIn(Range charRange, Bool not := false) {
CharRule(charRange.min.toChar + "-" + charRange.last.toChar, not) |Int peek->Bool| { charRange.contains(peek) }
}
** Matches any alphabetical ASCII character.
**
** PEG notation:
**
** [a-zA-Z]
static Rule alphaChar(Bool not := false) {
CharRule("a-zA-Z", not) |Int peek->Bool| { peek.isAlpha }
}
** Matches any alphabetical ASCII or numerical character.
**
** PEG notation:
**
** [a-zA-Z0-9]
static Rule alphaNumChar(Bool not := false) {
CharRule("a-zA-Z0-9", not) |Int peek->Bool| { peek.isAlphaNum }
}
** Matches any numerical character.
**
** PEG notation:
**
** [0-9]
static Rule numChar(Bool not := false) {
CharRule("0-9", not) |Int peek->Bool| { peek.isDigit }
}
** Matches any hexadecimal character.
**
** PEG notation:
**
** [a-fA-F0-9]
static Rule hexChar(Bool not := false) {
CharRule("0-9A-F", not) |Int peek->Bool| { peek.isDigit || ('A'..'F').contains(peek) || ('a'..'f').contains(peek) } { it.ignoresCase = true }
}
** Matches any whitespace character, including new lines.
**
** PEG notation:
**
** [ \t\n]
static Rule whitespaceChar(Bool not := false) {
CharRule(" \t\n".toCode(null), not) |Int peek->Bool| { peek.isSpace }
}
** Matches any space character (excluding new line chars).
**
** PEG notation:
**
** [ \t]
static Rule spaceChar(Bool not := false) {
CharRule(" \t".toCode(null), not) |Int peek->Bool| { peek == ' ' || peek == '\t' }
}
** Matches any word character, used as a Fantom identifier.
**
** PEG notation:
**
** [a-zA-Z0-9_]
static Rule wordChar(Bool not := false) {
CharRule("a-zA-Z0-9_".toCode(null), not) |Int peek->Bool| { peek.isAlphaNum || peek == '_' }
}
** Matches the new line character.
** This assumes all new lines have been normalised to '\n'.
**
** PEG notation:
**
** [\n]
static Rule newLineChar(Bool not := false) {
char('\n', not)
}
// ---- Repetition rules ----------------------------------------------------------------------
** Processes the given rule and returns success whether it passes or not.
**
** Example PEG notation:
**
** (rule)?
static Rule optional(Rule rule) {
RepetitionRule(0, 1, rule)
}
** Processes the given rule repeatedly until it fails.
**
** Example PEG notation:
**
** (rule)*
static Rule zeroOrMore(Rule rule) {
RepetitionRule(0, null, rule)
}
** Processes the given rule repeatedly until it fails.
** Returns success if it succeeded at least once.
**
** Example PEG notation:
**
** (rule)+
static Rule oneOrMore(Rule rule) {
RepetitionRule(1, null, rule)
}
** Processes the given rule repeatedly until it fails.
** Returns success if it succeeded at least 'n' times.
**
** Example PEG notation:
**
** rule{3,}
static Rule atLeast(Int n, Rule rule) {
RepetitionRule(n, null, rule)
}
** Processes the given rule repeatedly, but at most 'n' times.
** Returns success always.
**
** Example PEG notation:
**
** rule{,5}
static Rule atMost(Int n, Rule rule) {
RepetitionRule(null, n, rule)
}
** Processes the given rule 'n' times.
** Returns success if the it always succeeded.
**
** Example PEG notation:
**
** rule{5,5}
static Rule nTimes(Int times, Rule rule) {
RepetitionRule(times, times, rule)
}
** Processes the given rule between the given values (inclusive).
** If 'min' is 'null' it is taken to be 0.
** If 'max' is 'null' it is taken to be infinity.
**
** Example PEG notation:
**
** rule{2,6}
static Rule between(Int? min, Int? max, Rule rule) {
RepetitionRule(min, max, rule)
}
// ---- Expression rules ----------------------------------------------------------------------
** Processes the given rules in order until.
** Returns success if they all succeeded.
**
** sequence([rule1, rule2, rule3])
**
** Example PEG notation:
**
** rule1 rule2 rule3 ... rule4
**
** When defining a 'sequence()' rule you may also use it-block syntax:
**
** sequence { rule1, rule2, rule3, }
static Rule sequence(Rule[] rules := Rule[,]) {
SequenceRule(rules)
}
** Processes the given rules in order until one succeeds.
** Returns success any succeeded.
**
** firstOf([rule1, rule2, rule3])
**
** Example PEG notation:
**
** rule1 / rule2 / rule3 / ... / rule4
**
** When defining a 'firstOf()' rule you may also use it-block syntax:
**
** firstOf { rule1, rule2, rule3, }
static Rule firstOf(Rule[] rules := Rule[,]) {
FirstOfRule(rules)
}
// ---- Predicate rules -----------------------------------------------------------------------
** Processes the given rule and return success if it succeeded.
** The rule is always rolled back so the characters may be subsequently re-read.
**
** Example PEG notation:
**
** &(rule)
static Rule onlyIf(Rule rule) {
PredicateRule(rule, false)
}
** Processes the given rule and return success if it failed.
** The rule is always rolled back so the characters may be subsequently re-read.
**
** Example PEG notation:
**
** !(rule)
static Rule onlyIfNot(Rule rule) {
PredicateRule(rule, true)
}
// ---- Macro rules ---------------------------------------------------------------------------
** Matches if at the Start-Of-Stream (SOS).
** (Does not consume an characters.)
**
** Example PEG notation:
**
** \sos
static Rule sos() {
SosRule()
}
** Matches if at the End-Of-Stream (EOS).
** (Does not consume an characters.)
**
** Example PEG notation:
**
** \eos
static Rule eos() {
EosRule()
}
** Matches if at the Start-Of-Line (SOL) (or at the start of stream).
**
** Example PEG notation:
**
** \sos
static Rule sol() {
SolRule()
}
** Matches if at the End-Of-Line (EOL) (or at the end of stream).
**
** Example PEG notation:
**
** \eol
static Rule eol() {
EolRule()
}
** Matches an uppercase character in the current locale.
** See `sys::Locale` for details.
**
** Example PEG notation:
**
** \upper
static Rule upper() {
UpperRule()
}
** Matches a lowercase character in the current locale.
** See `sys::Locale` for details.
**
** Example PEG notation:
**
** \lower
static Rule lower() {
LowerRule()
}
** Matches any character in the current locale.
** See `sys::Locale` for details.
**
** Example PEG notation:
**
** \aplha
static Rule alpha() {
AlphaRule()
}
** A rule that will *always* pass.
** Does not consume any characters.
**
** Example PEG notation:
**
** \pass
static Rule pass() {
PassRule()
}
** A rule that will *always* fail.
** Does not consume any characters.
**
** Example PEG notation:
**
** \fail
static Rule fail() {
FailRule()
}
// ** No operation - a placeholder.
// ** This rule consumes no input and will always succeed if 'pass' is 'true', and always fail if 'pass' is 'false'.
// **
// ** Example PEG notation:
// **
// ** \noop(true)
// static Rule noop(Bool pass := true) {
// NoOpRule(pass)
// }
// ** Dump the message and parsing stacktrace to std-out.
// ** Use to aid the debugging of PEG grammar.
// **
// ** Example PEG notation:
// **
// ** \dump(TODO)
// static Rule dump(Str? msg := null) {
// DumpRule(msg)
// }
// ** Logs a message to PEG degbug.
// ** Use to place marker text in the reams of PEG debug output..
// **
// ** Example PEG notation:
// **
// ** \log(TODO)
// static Rule log(Str? msg := null) {
// LogRule(msg)
// }
** This rule throws an error if processed.
**
** Example PEG notation:
**
** \fail(FAIL)
static Rule err(Str msg := "FAIL") {
ErrRule(msg)
}
}