Compare commits

...

10 Commits

Author SHA1 Message Date
Arpad Ryszka
a8bec182b1 add go modules 2025-08-19 00:56:08 +02:00
Arpad Ryszka
a96580b2fd fix: all keywords need to be generated 2019-02-02 21:27:01 +01:00
Arpad Ryszka
7b2eaa5c7a add keyword commit type 2019-02-02 18:07:10 +01:00
Arpad Ryszka
19050f317b fix: use sequences from previous matches when failing and available 2019-01-27 19:06:44 +01:00
Arpad Ryszka
03acd8dea4 fix generated builder: builders didn't have the generalizations registered 2019-01-27 18:29:30 +01:00
Arpad Ryszka
505061f31a fix Makefile: no bash expansion 2019-01-27 16:08:39 +01:00
Arpad Ryszka
146315f495 fix formatting 2018-10-21 19:49:48 +02:00
Arpad Ryszka
3327e758c5 drop unused code 2018-10-11 23:48:58 +02:00
Arpad Ryszka
a7dc82ff12 fix test syntax 2018-10-11 23:48:00 +02:00
Arpad Ryszka
a31134291d test for bug in mml syntax: a.b[c] 2018-10-11 23:36:17 +02:00
30 changed files with 1216 additions and 1097 deletions

View File

@ -35,15 +35,15 @@ head: $(SOURCES) fmt
generate: $(SOURCES) $(PARSERS) fmt head install
treerack generate -export -package-name self < syntax.treerack > self/self.go.next
@mv self/self.go{.next,}
@mv self/self.go.next self/self.go
@gofmt -s -w self/self.go
regenerate: $(SOURCES) $(PARSERS) fmt head install
treerack generate -export -package-name self < syntax.treerack > self/self.go.next
@mv self/self.go{.next,}
@mv self/self.go.next self/self.go
go install ./cmd/treerack
treerack generate -export -package-name self < syntax.treerack > self/self.go.next
@mv self/self.go{.next,}
@mv self/self.go.next self/self.go
@gofmt -s -w self/self.go
check-generate: $(SOURCES) $(PARSERS)
@ -68,7 +68,7 @@ check-generate: $(SOURCES) $(PARSERS)
@echo checking self
@mv self/self.go self/self.go.backup
@treerack generate -export -package-name self < syntax.treerack > self/self.go.next
@mv self/self.go{.next,}
@mv self/self.go.next self/self.go
@gofmt -s -w self/self.go
@if ! diff self/self.go self/self.go.backup > /dev/null; then \
mv self/self.go.backup self/self.go; \

View File

@ -8,6 +8,6 @@
### Examples
- JSON: https://github.com/aryszka/treerack/blob/master/examples/json.treerack
- Scheme: https://github.com/aryszka/treerack/blob/master/examples/scheme.treerack
- Treerack (itself): https://github.com/aryszka/treerack/blob/master/syntax.treerack
- JSON: https://code.squareroundforest.org/arpio/treerack/blob/master/examples/json.treerack
- Scheme: https://code.squareroundforest.org/arpio/treerack/blob/master/examples/scheme.treerack
- Treerack (itself): https://code.squareroundforest.org/arpio/treerack/blob/master/syntax.treerack

View File

@ -78,6 +78,19 @@ func (p *choiceParser) parse(c *context) {
}
if match {
if p.commit&NoKeyword != 0 && c.isKeyword(from, to) {
if c.failingParser == nil &&
p.commit&userDefined != 0 &&
p.commit&Whitespace == 0 &&
p.commit&FailPass == 0 {
c.failingParser = p
}
c.fail(from)
c.results.unmarkPending(from, p.id)
return
}
if failOffset > to {
c.failOffset = failOffset
c.failingParser = failingParser

View File

@ -227,6 +227,15 @@ func (b *choiceBuilder) generate(w io.Writer, done map[string]bool) error {
fprintf("name: \"%s\",", b.name)
}
if len(b.generalizations) > 0 {
fprintf("generalizations: []int{")
for i := range b.generalizations {
fprintf("%d,", b.generalizations[i])
}
fprintf("},")
}
fprintf("};")
if len(b.options) > 0 {

View File

@ -5,7 +5,7 @@ import (
"unicode/utf8"
)
const summary = `treerack - parser generator - https://github.com/aryszka/treerack`
const summary = `treerack - parser generator - https://code.squareroundforest.org/aryszka/treerack`
const commandsHelp = `Available commands:
check validates an arbitrary input against a syntax definition
@ -18,7 +18,7 @@ See more details about a particular command by calling:
treerack <command> -help`
const docRef = `See more documentation about the definition syntax and the parser output at
https://github.com/aryszka/treerack.`
https://code.squareroundforest.org/arpio/treerack.`
const positionalSyntaxUsage = "The path to the syntax file is accepted as a positional argument."

View File

@ -1,6 +1,6 @@
package main
import "github.com/aryszka/treerack"
import "code.squareroundforest.org/arpio/treerack"
type generateOptions struct {
command *commandOptions

View File

@ -6,8 +6,7 @@ import (
"io"
"io/ioutil"
"os"
"github.com/aryszka/treerack"
"code.squareroundforest.org/arpio/treerack"
"golang.org/x/crypto/ssh/terminal"
)

View File

@ -2,8 +2,7 @@ package main
import (
"encoding/json"
"github.com/aryszka/treerack"
"code.squareroundforest.org/arpio/treerack"
)
type showOptions struct {

View File

@ -8,9 +8,11 @@ import (
type context struct {
reader io.RuneReader
keywords []parser
offset int
readOffset int
consumed int
offsetLimit int
failOffset int
failingParser parser
readErr error
@ -20,10 +22,12 @@ type context struct {
matchLast bool
}
func newContext(r io.RuneReader) *context {
func newContext(r io.RuneReader, keywords []parser) *context {
return &context{
reader: r,
keywords: keywords,
results: &results{},
offsetLimit: -1,
failOffset: -1,
}
}
@ -58,6 +62,10 @@ func (c *context) read() bool {
}
func (c *context) token() (rune, bool) {
if c.offset == c.offsetLimit {
return 0, false
}
if c.offset == c.readOffset {
if !c.read() {
return 0, false
@ -82,6 +90,21 @@ func (c *context) fromResults(p parser) bool {
return true
}
func (c *context) isKeyword(from, to int) bool {
ol := c.offsetLimit
c.offsetLimit = to
defer func() { c.offsetLimit = ol }()
for _, kw := range c.keywords {
c.offset = from
kw.parse(c)
if c.matchLast && c.offset == to {
return true
}
}
return false
}
func (c *context) success(to int) {
c.offset = to
c.matchLast = true

View File

@ -20,6 +20,10 @@ func flagsToCommitType(n []*Node) CommitType {
ct |= Whitespace
case "nows":
ct |= NoWhitespace
case "kw":
ct |= Keyword
case "nokw":
ct |= NoKeyword
case "failpass":
ct |= FailPass
case "root":

View File

@ -1,750 +0,0 @@
package treerack
import (
"bytes"
"errors"
"fmt"
"math/rand"
"strconv"
"strings"
"testing"
"github.com/zalando/skipper/eskip"
)
const (
maxID = 27
meanID = 9
setPathChance = 0.72
maxPathTags = 12
meanPathTags = 2
maxPathTag = 24
meanPathTag = 9
setHostChance = 0.5
maxHost = 48
meanHost = 24
setPathRegexpChance = 0.45
maxPathRegexp = 36
meanPathRegexp = 12
setMethodChance = 0.1
setHeadersChance = 0.3
maxHeadersLength = 6
meanHeadersLength = 1
maxHeaderKeyLength = 18
meanHeaderKeyLength = 12
maxHeaderValueLength = 48
meanHeaderValueLength = 6
setHeaderRegexpChance = 0.05
maxHeaderRegexpsLength = 3
meanHeaderRegexpsLength = 1
maxHeaderRegexpLength = 12
meanHeaderRegexpLength = 6
maxTermNameLength = 15
meanTermNameLength = 6
maxTermArgsLength = 6
meanTermArgsLength = 1
floatArgChance = 0.1
intArgChance = 0.3
maxTermStringLength = 24
meanTermStringLength = 6
maxPredicatesLength = 4
meanPredicatesLength = 1
maxFiltersLength = 18
meanFiltersLength = 3
loopBackendChance = 0.05
shuntBackendChance = 0.1
maxBackend = 48
meanBackend = 15
)
func takeChance(c float64) bool {
return rand.Float64() < c
}
func generateID() string {
return generateString(maxID, meanID)
}
func generatePath() string {
if !takeChance(setPathChance) {
return ""
}
l := randomLength(maxPathTags, meanPathTags)
p := append(make([]string, 0, l+1), "")
for i := 0; i < l; i++ {
p = append(p, generateString(maxPathTag, meanPathTag))
}
return strings.Join(p, "/")
}
func generateHostRegexps() []string {
if !takeChance(setHostChance) {
return nil
}
return []string{generateString(maxHost, meanHost)}
}
func generatePathRegexps() []string {
if !takeChance(setPathRegexpChance) {
return nil
}
return []string{generateString(maxPathRegexp, meanPathRegexp)}
}
func generateMethod() string {
if !takeChance(setMethodChance) {
return ""
}
methods := []string{"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"}
return methods[rand.Intn(len(methods))]
}
func generateHeaders() map[string]string {
if !takeChance(setHeadersChance) {
return nil
}
h := make(map[string]string)
for i := 0; i < randomLength(maxHeadersLength, meanHeadersLength); i++ {
h[generateString(maxHeaderKeyLength, meanHeaderKeyLength)] =
generateString(maxHeaderValueLength, meanHeaderValueLength)
}
return h
}
func generateHeaderRegexps() map[string][]string {
if !takeChance(setHeaderRegexpChance) {
return nil
}
h := make(map[string][]string)
for i := 0; i < randomLength(maxHeaderRegexpsLength, meanHeaderRegexpsLength); i++ {
k := generateString(maxHeaderKeyLength, meanHeaderKeyLength)
for i := 0; i < randomLength(maxHeaderRegexpLength, meanHeaderRegexpLength); i++ {
h[k] = append(h[k], generateString(maxHeaderValueLength, meanHeaderValueLength))
}
}
return h
}
func generateTerm() (string, []interface{}) {
n := generateString(maxTermNameLength, meanTermNameLength)
al := randomLength(maxTermArgsLength, meanTermArgsLength)
a := make([]interface{}, 0, al)
for i := 0; i < al; i++ {
at := rand.Float64()
switch {
case at < floatArgChance:
a = append(a, rand.NormFloat64())
case at < intArgChance:
a = append(a, rand.Int())
default:
a = append(a, generateString(maxTermStringLength, meanTermStringLength))
}
}
return n, a
}
func generatePredicates() []*eskip.Predicate {
l := randomLength(maxPredicatesLength, meanPredicatesLength)
p := make([]*eskip.Predicate, 0, l)
for i := 0; i < l; i++ {
pi := &eskip.Predicate{}
pi.Name, pi.Args = generateTerm()
p = append(p, pi)
}
return p
}
func generateFilters() []*eskip.Filter {
l := randomLength(maxFiltersLength, meanFiltersLength)
f := make([]*eskip.Filter, 0, l)
for i := 0; i < l; i++ {
fi := &eskip.Filter{}
fi.Name, fi.Args = generateTerm()
f = append(f, fi)
}
return f
}
func generateBackend() (eskip.BackendType, string) {
t := rand.Float64()
switch {
case t < loopBackendChance:
return eskip.LoopBackend, ""
case t < loopBackendChance+shuntBackendChance:
return eskip.ShuntBackend, ""
default:
return eskip.NetworkBackend, generateString(maxBackend, meanBackend)
}
}
func generateRoute() *eskip.Route {
r := &eskip.Route{}
r.Id = generateID()
r.Path = generatePath()
r.HostRegexps = generateHostRegexps()
r.PathRegexps = generatePathRegexps()
r.Method = generateMethod()
r.Headers = generateHeaders()
r.HeaderRegexps = generateHeaderRegexps()
r.Predicates = generatePredicates()
r.Filters = generateFilters()
r.BackendType, r.Backend = generateBackend()
return r
}
func generateEskip(l int) []*eskip.Route {
r := make([]*eskip.Route, 0, l)
for i := 0; i < l; i++ {
r = append(r, generateRoute())
}
return r
}
func parseEskipInt(s string) (int, error) {
i, err := strconv.ParseInt(s, 0, 64)
return int(i), err
}
func parseEskipFloat(s string) (float64, error) {
f, err := strconv.ParseFloat(s, 64)
return f, err
}
func unquote(s string, escapedChars string) (string, error) {
if len(s) < 2 {
return "", nil
}
b := make([]byte, 0, len(s)-2)
var escaped bool
for _, bi := range []byte(s[1 : len(s)-1]) {
if escaped {
switch bi {
case 'b':
bi = '\b'
case 'f':
bi = '\f'
case 'n':
bi = '\n'
case 'r':
bi = '\r'
case 't':
bi = '\t'
case 'v':
bi = '\v'
}
b = append(b, bi)
escaped = false
continue
}
for _, ec := range []byte(escapedChars) {
if ec == bi {
return "", errors.New("invalid quote")
}
}
if bi == '\\' {
escaped = true
continue
}
b = append(b, bi)
}
return string(b), nil
}
func unquoteString(s string) (string, error) {
return unquote(s, "\"")
}
func unquoteRegexp(s string) (string, error) {
return unquote(s, "/")
}
func nodeToArg(n *Node) (interface{}, error) {
switch n.Name {
case "int":
return parseEskipInt(n.Text())
case "float":
return parseEskipFloat(n.Text())
case "string":
return unquoteString(n.Text())
case "regexp":
return unquoteRegexp(n.Text())
default:
return nil, errors.New("invalid arg")
}
}
func nodeToTerm(n *Node) (string, []interface{}, error) {
if len(n.Nodes) < 1 || n.Nodes[0].Name != "symbol" {
return "", nil, errors.New("invalid term")
}
name := n.Nodes[0].Text()
var args []interface{}
for _, ni := range n.Nodes[1:] {
a, err := nodeToArg(ni)
if err != nil {
return "", nil, err
}
args = append(args, a)
}
return name, args, nil
}
func nodeToPredicate(r *eskip.Route, n *Node) error {
name, args, err := nodeToTerm(n)
if err != nil {
return err
}
switch name {
case "Path":
if len(args) != 1 {
return errors.New("invalid path predicate")
}
p, ok := args[0].(string)
if !ok {
return errors.New("invalid path predicate")
}
r.Path = p
case "Host":
if len(args) != 1 {
return errors.New("invalid host predicate")
}
h, ok := args[0].(string)
if !ok {
return errors.New("invalid host predicate")
}
r.HostRegexps = append(r.HostRegexps, h)
case "PathRegexp":
if len(args) != 1 {
return errors.New("invalid path regexp predicate")
}
p, ok := args[0].(string)
if !ok {
return errors.New("invalid path regexp predicate")
}
r.PathRegexps = append(r.PathRegexps, p)
case "Method":
if len(args) != 1 {
return errors.New("invalid method predicate")
}
m, ok := args[0].(string)
if !ok {
return errors.New("invalid method predicate")
}
r.Method = m
case "Header":
if len(args) != 2 {
return errors.New("invalid header predicate")
}
name, ok := args[0].(string)
if !ok {
return errors.New("invalid header predicate")
}
value, ok := args[1].(string)
if !ok {
return errors.New("invalid header predicate")
}
if r.Headers == nil {
r.Headers = make(map[string]string)
}
r.Headers[name] = value
case "HeaderRegexp":
if len(args) != 2 {
return errors.New("invalid header regexp predicate")
}
name, ok := args[0].(string)
if !ok {
return errors.New("invalid header regexp predicate")
}
value, ok := args[1].(string)
if !ok {
return errors.New("invalid header regexp predicate")
}
if r.HeaderRegexps == nil {
r.HeaderRegexps = make(map[string][]string)
}
r.HeaderRegexps[name] = append(r.HeaderRegexps[name], value)
default:
r.Predicates = append(r.Predicates, &eskip.Predicate{Name: name, Args: args})
}
return nil
}
func nodeToFilter(n *Node) (*eskip.Filter, error) {
name, args, err := nodeToTerm(n)
if err != nil {
return nil, err
}
return &eskip.Filter{Name: name, Args: args}, nil
}
func nodeToBackend(r *eskip.Route, n *Node) error {
switch n.Name {
case "string":
b, err := unquoteString(n.Text())
if err != nil {
return err
}
r.BackendType = eskip.NetworkBackend
r.Backend = b
case "shunt":
r.BackendType = eskip.ShuntBackend
case "loopback":
r.BackendType = eskip.LoopBackend
default:
return errors.New("invalid backend type")
}
return nil
}
func nodeToEskipDefinition(n *Node) (*eskip.Route, error) {
ns := n.Nodes
if len(ns) < 2 || len(ns[1].Nodes) == 0 {
return nil, fmt.Errorf("invalid definition length: %d", len(ns))
}
r := &eskip.Route{}
if ns[0].Name != "symbol" {
return nil, errors.New("invalid definition id")
}
r.Id, ns = ns[0].Text(), ns[1].Nodes
predicates:
for i, ni := range ns {
switch ni.Name {
case "predicate":
if err := nodeToPredicate(r, ni); err != nil {
return nil, err
}
case "filter", "string", "shunt", "loopback":
ns = ns[i:]
break predicates
default:
return nil, errors.New("invalid definition item among predicates")
}
}
filters:
for i, ni := range ns {
switch ni.Name {
case "filter":
f, err := nodeToFilter(ni)
if err != nil {
return nil, err
}
r.Filters = append(r.Filters, f)
case "string", "shunt", "loopback":
ns = ns[i:]
break filters
default:
return nil, errors.New("invalid definition item among filters")
}
}
if len(ns) != 1 {
return nil, fmt.Errorf("invalid definition backend, remaining definition length: %d, %s",
len(ns), n.Text())
}
if err := nodeToBackend(r, ns[0]); err != nil {
return nil, err
}
return r, nil
}
func eskipTreeToEskip(n []*Node) ([]*eskip.Route, error) {
r := make([]*eskip.Route, 0, len(n))
for _, ni := range n {
d, err := nodeToEskipDefinition(ni)
if err != nil {
return nil, err
}
r = append(r, d)
}
return r, nil
}
func checkTerm(t *testing.T, gotName, expectedName string, gotArgs, expectedArgs []interface{}) {
if gotName != expectedName {
t.Error("invalid term name")
return
}
if len(gotArgs) != len(expectedArgs) {
t.Error("invalid term args length in:", gotName, len(gotArgs), len(expectedArgs))
return
}
// legacy bug support, dropping numeric arguments:
for i, a := range gotArgs {
ea := expectedArgs[i]
switch a.(type) {
case int, float64:
switch ea.(type) {
case int, float64:
gotArgs = append(gotArgs[:i], gotArgs[i+1:]...)
expectedArgs = append(expectedArgs[:i], expectedArgs[i+1:]...)
default:
t.Error("invalid argument type at:", i)
}
}
}
for i, a := range gotArgs {
if a != expectedArgs[i] {
t.Error("invalid term arg")
return
}
}
}
func checkPredicates(t *testing.T, got, expected *eskip.Route) {
if got.Path != expected.Path {
t.Error("invalid path")
return
}
if len(got.HostRegexps) != len(expected.HostRegexps) {
t.Error("invalid host length")
return
}
for i, h := range got.HostRegexps {
if h != expected.HostRegexps[i] {
t.Error("invalid host")
return
}
}
if len(got.PathRegexps) != len(expected.PathRegexps) {
t.Error("invalid path regexp length", len(got.PathRegexps), len(expected.PathRegexps))
return
}
for i, h := range got.PathRegexps {
if h != expected.PathRegexps[i] {
t.Error("invalid path regexp")
return
}
}
if got.Method != expected.Method {
t.Error("invalid method")
return
}
if len(got.Headers) != len(expected.Headers) {
t.Error("invalid headers length")
return
}
for n, h := range got.Headers {
he, ok := expected.Headers[n]
if !ok {
t.Error("invalid header name")
return
}
if he != h {
t.Error("invalid header")
return
}
}
if len(got.HeaderRegexps) != len(expected.HeaderRegexps) {
t.Error("invalid header regexp length")
return
}
for n, h := range got.HeaderRegexps {
he, ok := expected.HeaderRegexps[n]
if !ok {
t.Error("invalid header regexp name")
return
}
if len(h) != len(he) {
t.Error("invalid header regexp item length")
return
}
for i, hi := range h {
if hi != he[i] {
t.Error("invalid header regexp")
return
}
}
}
if len(got.Predicates) != len(expected.Predicates) {
t.Error("invalid predicates length")
return
}
for i, p := range got.Predicates {
checkTerm(
t,
p.Name, expected.Predicates[i].Name,
p.Args, expected.Predicates[i].Args,
)
if t.Failed() {
t.Log(p.Name, expected.Predicates[i].Name)
t.Log(p.Args, expected.Predicates[i].Args)
return
}
}
}
func checkFilters(t *testing.T, got, expected []*eskip.Filter) {
if len(got) != len(expected) {
t.Error("invalid filters length")
return
}
for i, f := range got {
checkTerm(
t,
f.Name, expected[i].Name,
f.Args, expected[i].Args,
)
if t.Failed() {
return
}
}
}
func checkBackend(t *testing.T, got, expected *eskip.Route) {
if got.BackendType != expected.BackendType {
t.Error("invalid backend type")
return
}
if got.Backend != expected.Backend {
t.Error("invalid backend")
return
}
}
func checkRoute(t *testing.T, got, expected *eskip.Route) {
if got.Id != expected.Id {
t.Error("invalid route id")
return
}
checkPredicates(t, got, expected)
if t.Failed() {
return
}
checkFilters(t, got.Filters, expected.Filters)
if t.Failed() {
return
}
checkBackend(t, got, expected)
}
func checkEskip(t *testing.T, got, expected []*eskip.Route) {
if len(got) != len(expected) {
t.Error("invalid length", len(got), len(expected))
return
}
for i, ri := range got {
checkRoute(t, ri, expected[i])
if t.Failed() {
t.Log(ri.String())
t.Log(expected[i].String())
return
}
}
}
func TestEskip(t *testing.T) {
const count = 1 << 9
r := generateEskip(count)
e := eskip.Print(eskip.PrettyPrintInfo{Pretty: true}, r...)
b := bytes.NewBufferString(e)
s, err := openSyntaxFile("examples/eskip.treerack")
if err != nil {
t.Error(err)
return
}
n, err := s.Parse(b)
if err != nil {
t.Error(err)
return
}
rback, err := eskipTreeToEskip(n.Nodes)
if err != nil {
t.Error(err)
return
}
checkEskip(t, rback, r)
}

View File

@ -1,50 +0,0 @@
/*
Eskip routing configuration format for Skipper: https://github.com/zalando/skipper
*/
eskip:root = (expression | definitions)?;
space:ws = [ \n\b\f\r\t\v];
comment:ws = "//" [^\n]*;
decimal-digit:alias = [0-9];
octal-digit:alias = [0-7];
hexa-digit:alias = [0-9a-fA-F];
decimal:alias:nows = [1-9] decimal-digit*;
octal:alias:nows = "0" octal-digit*;
hexa:alias:nows = "0" [xX] hexa-digit+;
int = decimal | octal | hexa;
exponent:alias:nows = [eE] [+\-]? decimal-digit+;
float:nows = decimal-digit+ "." decimal-digit* exponent?
| "." decimal-digit+ exponent?
| decimal-digit+ exponent;
number:alias:nows = "-"? (int | float);
string:nows = "\"" ([^\\"] | "\\" .)* "\"";
regexp:nows = "/" ([^\\/] | "\\" .)* "/";
symbol:nows = [a-zA-Z_] [a-zA-z0-9_]*;
arg:alias = number | string | regexp;
args:alias = arg ("," arg)*;
term:alias = symbol "(" args? ")";
predicate = term;
predicates:alias = "*" | predicate ("&&" predicate)*;
filter = term;
filters:alias = filter ("->" filter)*;
address:alias = string;
shunt = "<shunt>";
loopback = "<loopback>";
backend:alias = address | shunt | loopback;
expression = predicates ("->" filters)? "->" backend;
id:alias = symbol;
definition = id ":" expression;
definitions:alias = ";"* definition (";"+ definition)* ";"*;

271
examples/mml-exp2.treerack Normal file
View File

@ -0,0 +1,271 @@
ws:ws = " " | "\b" | "\f" | "\r" | "\t" | "\v";
wsc:ws = comment;
nl:alias = "\n";
line-comment-content:nows = [^\n]*;
line-comment:alias:nows = "//" line-comment-content;
block-comment-content:nows = ([^*] | "*" [^/])*;
block-comment:alias:nows = "/*" block-comment-content "*/";
comment-part:alias = line-comment | block-comment;
comment:alias = comment-part (nl? comment-part)*;
decimal-digit:alias = [0-9];
octal-digit:alias = [0-7];
hexa-digit:alias = [0-9a-fA-F];
decimal:alias:nows = [1-9] decimal-digit*;
octal:alias:nows = "0" octal-digit*;
hexa:alias:nows = "0" [xX] hexa-digit+;
int = decimal | octal | hexa;
exponent:alias:nows = [eE] [+\-]? decimal-digit+;
float:nows = decimal-digit+ "." decimal-digit* exponent?
| "." decimal-digit+ exponent?
| decimal-digit+ exponent;
string:nows = "\"" ([^\\"] | "\\" .)* "\"";
true = "true";
false = "false";
bool:alias = true | false;
symbol:nows = [a-zA-Z_][a-zA-Z_0-9]*;
spread-expression = primary-expression "...";
list-sep:alias = (nl | ",")+;
list-item:alias = expression | spread-expression;
expression-list:alias = list-item (list-sep list-item)*;
list-fact:alias = "[" list-sep? expression-list? list-sep? "]";
list = list-fact;
mutable-list = "~" nl* list-fact;
expression-key = "[" nl* expression nl* "]";
entry = (symbol | string | expression-key) nl* ":" nl* expression;
entry-list:alias = (entry | spread-expression) (list-sep (entry | spread-expression))*;
struct-fact:alias = "{" list-sep? entry-list? list-sep? "}";
struct = struct-fact;
mutable-struct = "~" nl* struct-fact;
parameter-list:alias = symbol (list-sep symbol)*;
collect-parameter = "..." nl* symbol;
return = "return" (nl* expression)?;
block = "{" sep? statement-list? sep? "}";
function-fact:alias = "(" list-sep?
(parameter-list
| parameter-list list-sep collect-parameter
| collect-parameter)?
list-sep? ")" nl*
(simple-statement | block);
function = "fn" nl* function-fact;
effect = "fn" nl* "~" nl* function-fact;
range-from = expression;
range-to = expression;
range:alias = range-from? nl* ":" nl* range-to?;
symbol-index = "." nl* symbol;
expression-index = "[" nl* expression nl* "]";
index:alias = symbol-index | expression-index;
index-list:alias = index (nl* index)?;
indexer = primary-expression nl* index-list;
function-application = primary-expression "(" list-sep? expression-list? list-sep? ")";
expression-group:alias = "(" nl* expression nl* ")";
primary-expression:alias = int
| float
| string
| bool
| receive
| symbol
| list
| mutable-list
| struct
| mutable-struct
| function
| effect
| indexer
| function-application
| expression-group;
binary-not = "^";
binary-and = "&";
binary-or = "|";
xor = "^";
and-not = "&^";
lshift = "<<";
rshift = ">>";
plus = "+";
minus = "-";
mul = "*";
div = "/";
mod = "%";
add = "+";
sub = "-";
logical-not = "!";
eq = "==";
not-eq = "!=";
less = "<";
less-or-eq = "<=";
greater = ">";
greater-or-eq = ">=";
logical-and = "&&";
logical-or = "||";
chain:alias = "->";
unary-operator:alias = plus | minus | binary-not | logical-not;
unary-expression = unary-operator primary-expression;
binary-op0:alias = binary-and | and-not | lshift | rshift | mul | div | mod;
binary-op1:alias = binary-or | xor | add | sub;
binary-op2:alias = eq | not-eq | less | less-or-eq | greater | greater-or-eq;
binary-op3:alias = logical-and;
binary-op4:alias = logical-or;
operand0:alias = primary-expression | unary-expression;
operand1:alias = operand0 | binary0;
operand2:alias = operand1 | binary1;
operand3:alias = operand2 | binary2;
operand4:alias = operand3 | binary3;
operand5:alias = operand4 | binary4;
binary0 = operand0 (nl* binary-op0 nl* operand0)+;
binary1 = operand1 (nl* binary-op1 nl* operand1)+;
binary2 = operand2 (nl* binary-op2 nl* operand2)+;
binary3 = operand3 (nl* binary-op3 nl* operand3)+;
binary4 = operand4 (nl* binary-op4 nl* operand4)+;
binary-expression:alias = binary0
| binary1
| binary2
| binary3
| binary4;
ternary-expression = expression nl* "?" nl* expression nl* ":" nl* expression;
chainingOperand:alias = primary-expression
| unary-expression
| binary-expression
| ternary-expression;
chaining = chainingOperand (nl* chain nl* chainingOperand)+;
expression:alias = primary-expression
| unary-expression
| binary-expression
| ternary-expression
| chaining;
if = "if" nl* expression nl* block
(nl* "else" nl* "if" nl* expression nl* block)*
(nl* "else" nl* block)?;
// TODO: empty switch not parsed
default = "default" nl* ":";
default-line:alias = default ";"* statement?;
case = "case" nl* expression nl* ":";
case-line:alias = case ";"* statement?;
switch = "switch" nl* expression? nl* "{" sep?
((case-line | default-line) (sep (case-line | default-line | statement))*)?
sep? "}";
send = "send" nl* primary-expression nl* primary-expression;
receive = "receive" nl* primary-expression;
receive-definition = symbol nl* receive;
communication:alias = send | receive | receive-definition;
select-case = "case" nl* communication nl* ":";
select-case-line:alias = select-case ";"* statement?;
select = "select" nl* "{" sep?
((select-case-line | default-line)
(sep (select-case-line | default-line | statement))*)?
sep? "}";
go = "go" nl* function-application;
defer = "defer" nl* function-application;
range-over-expression = symbol nl* "in" nl* (expression | range) | range;
loop-expression:alias = expression | range-over-expression;
loop = "for" ((nl* loop-expression)? nl* block | nl* block);
// TODO: set(a b)
assign-capture:alias = primary-expression (nl* "=")? nl* expression;
assign-capture-list:alias = assign-capture (list-sep assign-capture)*;
assign-set:alias = "set" nl* assign-capture;
assign-eq:alias = primary-expression nl* "=" nl* expression;
assign-group:alias = "set" nl* "(" (list-sep assign-capture-list)? list-sep? ")";
assignment = assign-set | assign-eq | assign-group;
value-capture-fact:alias = symbol (nl* "=")? nl* expression;
value-capture = value-capture-fact;
mutable-capture = "~" nl* value-capture-fact;
value-definition = "let" nl* (value-capture | mutable-capture);
mixed-capture-list:alias = (value-capture | mutable-capture) (list-sep (value-capture | mutable-capture))*;
value-capture-list:alias = value-capture (list-sep value-capture)*;
value-definition-group = "let" nl* "(" list-sep? mixed-capture-list? list-sep? ")";
mutable-definition-group = "let" nl* "~" nl* "(" list-sep? value-capture-list? list-sep? ")";
function-definition-fact:alias = symbol nl* function-fact;
function-capture = function-definition-fact;
effect-capture = "~" nl* function-definition-fact;
function-definition = "fn" nl* (function-capture | effect-capture);
function-capture-list:alias = function-capture (list-sep function-capture)*;
mixed-function-capture-list:alias = (function-capture | effect-capture)
(list-sep (function-capture | effect-capture))*;
function-definition-group = "fn" nl* "(" list-sep?
mixed-function-capture-list?
list-sep? ")";
effect-definition-group = "fn" nl* "~" nl* "(" list-sep?
function-capture-list?
list-sep? ")";
definition:alias = value-definition
| value-definition-group
| mutable-definition-group
| function-definition
| function-definition-group
| effect-definition-group;
// TODO:
// - use effect
// - rename to 'use'
use-inline = ".";
use-fact = string
| (symbol | use-inline) (nl* "=")? nl* string;
use-fact-list:alias = use-fact (list-sep use-fact)*;
use-statement:alias = "use" nl* use-fact;
use-statement-group:alias = "use" nl* "(" list-sep?
use-fact-list?
list-sep? ")";
use = use-statement | use-statement-group;
export = "export" nl* definition;
simple-statement:alias = send
| go
| defer
| assignment
| simple-statement-group
| expression;
simple-statement-group:alias = "(" nl* simple-statement nl* ")";
statement:alias = return
| if
| switch
| select
| loop
| definition
| use
| export
| statement-group
| simple-statement;
statement-group:alias = "(" nl* statement nl* ")";
sep:alias = (";" | nl)+;
statement-list:alias = statement (sep statement)*;
shebang-command = [^\n]*;
shebang = "#!" shebang-command "\n";
mml:root = shebang? sep? statement-list? sep?;

271
examples/mml-exp3.treerack Normal file
View File

@ -0,0 +1,271 @@
ws:ws = " " | "\b" | "\f" | "\r" | "\t" | "\v";
wsc:ws = comment;
nl:alias = "\n";
wsep:alias = ws | nl;
line-comment-content:nows = [^\n]*;
line-comment:alias:nows = "//" line-comment-content;
block-comment-content:nows = ([^*] | "*" [^/])*;
block-comment:alias:nows = "/*" block-comment-content "*/";
comment-part:alias = line-comment | block-comment;
comment:alias = comment-part (nl? comment-part)*;
decimal-digit:alias = [0-9];
octal-digit:alias = [0-7];
hexa-digit:alias = [0-9a-fA-F];
decimal:alias:nows = [1-9] decimal-digit*;
octal:alias:nows = "0" octal-digit*;
hexa:alias:nows = "0" [xX] hexa-digit+;
int = decimal | octal | hexa;
exponent:alias:nows = [eE] [+\-]? decimal-digit+;
float:nows = decimal-digit+ "." decimal-digit* exponent?
| "." decimal-digit+ exponent?
| decimal-digit+ exponent;
string:nows = "\"" ([^\\"] | "\\" .)* "\"";
true = "true";
false = "false";
bool:alias = true | false;
symbol:nows = [a-zA-Z_][a-zA-Z_0-9]*;
spread-expression = primary-expression "...";
list-sep:alias = (nl | ",")+;
list-item:alias = expression | spread-expression;
expression-list:alias = list-item (list-sep list-item)*;
list-fact:alias = "[" list-sep? expression-list? list-sep? "]";
list = list-fact;
mutable-list = "~" nl* list-fact;
expression-key = "[" nl* expression nl* "]";
entry = (symbol | string | expression-key) nl* ":" nl* expression;
entry-list:alias = (entry | spread-expression) (list-sep (entry | spread-expression))*;
struct-fact:alias = "{" list-sep? entry-list? list-sep? "}";
struct = struct-fact;
mutable-struct = "~" nl* struct-fact;
parameter-list:alias = symbol (list-sep symbol)*;
collect-parameter = "..." nl* symbol;
return = "return" | "return" nl* expression;
block = "{" sep? statement-list? sep? "}";
function-fact:alias = "(" list-sep?
(parameter-list
| parameter-list list-sep collect-parameter
| collect-parameter)?
list-sep? ")" nl*
(expression | simple-statement | block);
function = "fn" nl* function-fact;
effect = "fn" nl* "~" nl* function-fact;
range-from = expression;
range-to = expression;
range:alias = range-from? nl* ":" nl* range-to?;
symbol-index = "." nl* symbol;
expression-index = "[" nl* expression nl* "]";
range-index = "[" nl* range nl* "]";
index:alias = symbol-index | expression-index | range-index;
index-list:alias = index (nl* index)?;
indexer = primary-expression nl* index-list;
application = primary-expression "(" list-sep? expression-list? list-sep? ")";
expression-group:alias = "(" nl* expression nl* ")";
primary-expression:alias = int
| float
| string
| bool
| symbol
| list
| mutable-list
| struct
| mutable-struct
| function
| effect
| indexer
| application
| receive
| expression-group;
binary-not = "^";
binary-and = "&";
binary-or = "|";
xor = "^";
and-not = "&^";
lshift = "<<";
rshift = ">>";
plus = "+";
minus = "-";
mul = "*";
div = "/";
mod = "%";
add = "+";
sub = "-";
logical-not = "!";
eq = "==";
not-eq = "!=";
less = "<";
less-or-eq = "<=";
greater = ">";
greater-or-eq = ">=";
logical-and = "&&";
logical-or = "||";
chain:alias = "->";
unary-operator:alias = plus | minus | binary-not | logical-not;
unary-expression = unary-operator primary-expression;
binary-op0:alias = binary-and | and-not | lshift | rshift | mul | div | mod;
binary-op1:alias = binary-or | xor | add | sub;
binary-op2:alias = eq | not-eq | less | less-or-eq | greater | greater-or-eq;
binary-op3:alias = logical-and;
binary-op4:alias = logical-or;
operand0:alias = primary-expression | unary-expression;
operand1:alias = operand0 | binary0;
operand2:alias = operand1 | binary1;
operand3:alias = operand2 | binary2;
operand4:alias = operand3 | binary3;
operand5:alias = operand4 | binary4;
binary0 = operand0 (nl* binary-op0 nl* operand0)+;
binary1 = operand1 (nl* binary-op1 nl* operand1)+;
binary2 = operand2 (nl* binary-op2 nl* operand2)+;
binary3 = operand3 (nl* binary-op3 nl* operand3)+;
binary4 = operand4 (nl* binary-op4 nl* operand4)+;
binary-expression:alias = binary0
| binary1
| binary2
| binary3
| binary4;
ternary-expression = expression nl* "?" nl* expression nl* ":" nl* expression;
chainingOperand:alias = primary-expression
| unary-expression
| binary-expression
| ternary-expression;
chaining = chainingOperand (nl* chain nl* chainingOperand)+;
expression:alias = primary-expression
| unary-expression
| binary-expression
| ternary-expression
| chaining;
if = "if" nl* expression nl* block
(nl* "else" nl* "if" nl* expression nl* block)*
(nl* "else" nl* block)?;
// TODO: empty switch not parsed
default = "default" nl* ":";
default-line:alias = default ";"* statement?;
case = "case" nl* expression nl* ":";
case-line:alias = case ";"* statement?;
switch = "switch" nl* expression? nl* "{" sep?
((case-line | default-line) (sep (case-line | default-line | statement))*)?
sep? "}";
send = "send" nl* primary-expression nl* primary-expression;
receive = "receive" nl* primary-expression;
receive-definition = symbol nl* receive;
communication:alias = send | receive | receive-definition;
select-case = "case" nl* communication nl* ":";
select-case-line:alias = select-case ";"* statement?;
select = "select" nl* "{" sep?
((select-case-line | default-line)
(sep (select-case-line | default-line | statement))*)?
sep? "}";
go = "go" nl* application;
defer = "defer" nl* application;
break = "break";
continue = "continue";
loop-control:alias = break | continue;
range-over = symbol nl* "in" nl* (expression | range) | range;
loop-expression:alias = expression | range-over;
loop = "for" ((nl* loop-expression)? nl* block | nl* block);
assignment = (symbol | indexer) nl* "=" nl* expression;
value-capture-fact:alias = symbol (nl* "=")? nl* expression;
value-capture = value-capture-fact;
mutable-capture = "~" nl* value-capture-fact;
value-definition = "let" nl* (value-capture | mutable-capture);
mixed-capture-list:alias = (value-capture | mutable-capture) (list-sep (value-capture | mutable-capture))*;
value-capture-list:alias = value-capture (list-sep value-capture)*;
value-definition-group = "let" nl* "(" list-sep? mixed-capture-list? list-sep? ")";
mutable-definition-group = "let" nl* "~" nl* "(" list-sep? value-capture-list? list-sep? ")";
function-definition-fact:alias = symbol nl* function-fact;
function-capture = function-definition-fact;
effect-capture = "~" nl* function-definition-fact;
function-definition = "fn" nl* (function-capture | effect-capture);
function-capture-list:alias = function-capture (list-sep function-capture)*;
mixed-function-capture-list:alias = (function-capture | effect-capture)
(list-sep (function-capture | effect-capture))*;
function-definition-group = "fn" nl* "(" list-sep?
mixed-function-capture-list?
list-sep? ")";
effect-definition-group = "fn" nl* "~" nl* "(" list-sep?
function-capture-list?
list-sep? ")";
definition:alias = value-definition
| value-definition-group
| mutable-definition-group
| function-definition
| function-definition-group
| effect-definition-group;
// TODO:
// - use effect
use-inline = ".";
use-fact = string
| (symbol | use-inline) (nl* "=")? nl* string;
use-fact-list:alias = use-fact (list-sep use-fact)*;
use-statement:alias = "use" nl* use-fact;
use-statement-group:alias = "use" nl* "(" list-sep?
use-fact-list?
list-sep? ")";
use = use-statement | use-statement-group;
export = "export" nl* definition;
simple-statement:alias = send
| go
| defer
| assignment
| simple-statement-group;
simple-statement-group:alias = "(" nl* simple-statement nl* ")";
statement:alias = simple-statement
| return
| if
| switch
| select
| loop
| loop-control
| definition
| use
| export
| application // TODO: sendSomething() and sendSomething
| chaining
| statement-group;
statement-group:alias = "(" nl* statement nl* ")";
sep:alias = (";" | nl)+;
statement-list:alias = statement (sep statement)*;
shebang-command = [^\n]*;
shebang = "#!" shebang-command "\n";
mml:root = shebang? sep? statement-list? sep?;

View File

@ -4,12 +4,12 @@ package treerack
// only to the source code generated with treerack.
const gendoc = `
/*
This file was generated with treerack (https://github.com/aryszka/treerack).
This file was generated with treerack (https://code.squareroundforest.org/arpio/treerack).
The contents of this file fall under different licenses.
The code between the "// head" and "// eo head" lines falls under the same
license as the source code of treerack (https://github.com/aryszka/treerack),
license as the source code of treerack (https://code.squareroundforest.org/arpio/treerack),
unless explicitly stated otherwise, if treerack's license allows changing the
license of this source code.

10
go.mod Normal file
View File

@ -0,0 +1,10 @@
module code.squareroundforest.org/arpio/treerack
go 1.24.6
require golang.org/x/crypto v0.41.0
require (
golang.org/x/sys v0.35.0 // indirect
golang.org/x/term v0.34.0 // indirect
)

6
go.sum Normal file
View File

@ -0,0 +1,6 @@
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=

File diff suppressed because one or more lines are too long

23
keyword_test.go Normal file
View File

@ -0,0 +1,23 @@
package treerack
import "testing"
func TestKeyword(t *testing.T) {
const syntax = `
keywords:kw = "foo" | "bar";
symbol:nokw = [a-z]+;
`
runTests(t, syntax, []testItem{{
title: "keyword",
text: "foo",
fail: true,
}, {
title: "not keyword",
text: "baz",
ignorePosition: true,
node: &Node{
Name: "symbol",
},
}})
}

59
mmlexp2_test.go Normal file
View File

@ -0,0 +1,59 @@
package treerack
import (
"testing"
)
func TestMMLExp2(t *testing.T) {
s, err := openSyntaxFile("examples/mml-exp2.treerack")
if err != nil {
t.Error(err)
return
}
t.Run("indexer", func(t *testing.T) {
runTestsSyntax(t, s, []testItem{{
title: "mixed indexer",
text: "a.b[c]",
ignorePosition: true,
nodes: []*Node{{
Name: "indexer",
Nodes: []*Node{{
Name: "symbol",
}, {
Name: "symbol-index",
Nodes: []*Node{{
Name: "symbol",
}},
}, {
Name: "expression-index",
Nodes: []*Node{{
Name: "symbol",
}},
}},
}},
}})
runTestsSyntax(t, s, []testItem{{
title: "mixed indexer inverted",
text: "a[b].c",
ignorePosition: true,
nodes: []*Node{{
Name: "indexer",
Nodes: []*Node{{
Name: "symbol",
}, {
Name: "expression-index",
Nodes: []*Node{{
Name: "symbol",
}},
}, {
Name: "symbol-index",
Nodes: []*Node{{
Name: "symbol",
}},
}},
}},
}})
})
}

45
mmlexp3_test.go Normal file
View File

@ -0,0 +1,45 @@
package treerack
import (
"testing"
)
func TestMMLExp3(t *testing.T) {
s, err := openSyntaxFile("examples/mml-exp3.treerack")
if err != nil {
t.Error(err)
return
}
t.Run("indexer", func(t *testing.T) {
runTestsSyntax(t, s, []testItem{{
title: "assignment",
text: "fn f() a.b = c",
ignorePosition: true,
nodes: []*Node{{
Name: "function-definition",
Nodes: []*Node{{
Name: "function-capture",
Nodes: []*Node{{
Name: "symbol",
}, {
Name: "assignment",
Nodes: []*Node{{
Name: "indexer",
Nodes: []*Node{{
Name: "symbol",
}, {
Name: "symbol-index",
Nodes: []*Node{{
Name: "symbol",
}},
}},
}, {
Name: "symbol",
}},
}},
}},
}},
}})
})
}

View File

@ -1,6 +1,6 @@
package treerack
import "github.com/aryszka/treerack/self"
import "code.squareroundforest.org/arpio/treerack/self"
func mapNodes(m func(n *Node) *Node, n []*Node) []*Node {
var nn []*Node

View File

@ -742,7 +742,7 @@ func TestCharBuildNoop(t *testing.T) {
c := newChar("foo", false, nil, nil)
c.init(newRegistry())
b := c.builder()
ctx := newContext(bufio.NewReader(bytes.NewBuffer(nil)))
ctx := newContext(bufio.NewReader(bytes.NewBuffer(nil)), nil)
if n, ok := b.build(ctx); len(n) != 0 || ok {
t.Error("char build not noop")
}

View File

@ -1,10 +1,10 @@
/*
This file was generated with treerack (https://github.com/aryszka/treerack).
This file was generated with treerack (https://code.squareroundforest.org/arpio/treerack).
The contents of this file fall under different licenses.
The code between the "// head" and "// eo head" lines falls under the same
license as the source code of treerack (https://github.com/aryszka/treerack),
license as the source code of treerack (https://code.squareroundforest.org/arpio/treerack),
unless explicitly stated otherwise, if treerack's license allows changing the
license of this source code.
@ -137,6 +137,17 @@ func (p *sequenceParser) parse(c *context) {
currentCount = 0
continue
}
c.offset = from
if c.fromResults(p) {
if to > c.failOffset {
c.failOffset = -1
c.failingParser = nil
}
if !p.allChars {
c.results.unmarkPending(from, p.id)
}
return
}
if c.failingParser == nil && p.commit&userDefined != 0 && p.commit&Whitespace == 0 && p.commit&FailPass == 0 {
c.failingParser = p
}
@ -156,6 +167,16 @@ func (p *sequenceParser) parse(c *context) {
currentCount = 0
}
}
if p.commit&NoKeyword != 0 && c.isKeyword(from, to) {
if c.failingParser == nil && p.commit&userDefined != 0 && p.commit&Whitespace == 0 && p.commit&FailPass == 0 {
c.failingParser = p
}
c.fail(from)
if !p.allChars {
c.results.unmarkPending(from, p.id)
}
return
}
for _, g := range p.generalizations {
if c.results.pending(from, g) {
c.results.setMatch(from, g, to)
@ -317,6 +338,14 @@ func (p *choiceParser) parse(c *context) {
}
}
if match {
if p.commit&NoKeyword != 0 && c.isKeyword(from, to) {
if c.failingParser == nil && p.commit&userDefined != 0 && p.commit&Whitespace == 0 && p.commit&FailPass == 0 {
c.failingParser = p
}
c.fail(from)
c.results.unmarkPending(from, p.id)
return
}
if failOffset > to {
c.failOffset = failOffset
c.failingParser = failingParser
@ -566,9 +595,11 @@ func (r *results) unmarkPending(offset, id int) {
type context struct {
reader io.RuneReader
keywords []parser
offset int
readOffset int
consumed int
offsetLimit int
failOffset int
failingParser parser
readErr error
@ -578,8 +609,8 @@ type context struct {
matchLast bool
}
func newContext(r io.RuneReader) *context {
return &context{reader: r, results: &results{}, failOffset: -1}
func newContext(r io.RuneReader, keywords []parser) *context {
return &context{reader: r, keywords: keywords, results: &results{}, offsetLimit: -1, failOffset: -1}
}
func (c *context) read() bool {
if c.eof || c.readErr != nil {
@ -606,6 +637,9 @@ func (c *context) read() bool {
return true
}
func (c *context) token() (rune, bool) {
if c.offset == c.offsetLimit {
return 0, false
}
if c.offset == c.readOffset {
if !c.read() {
return 0, false
@ -625,6 +659,21 @@ func (c *context) fromResults(p parser) bool {
}
return true
}
func (c *context) isKeyword(from, to int) bool {
ol := c.offsetLimit
c.offsetLimit = to
defer func() {
c.offsetLimit = ol
}()
for _, kw := range c.keywords {
c.offset = from
kw.parse(c)
if c.matchLast && c.offset == to {
return true
}
}
return false
}
func (c *context) success(to int) {
c.offset = to
c.matchLast = true
@ -702,6 +751,8 @@ const (
Alias CommitType = 1 << iota
Whitespace
NoWhitespace
Keyword
NoKeyword
FailPass
Root
userDefined
@ -739,8 +790,8 @@ var ErrInvalidUnicodeCharacter = errors.New("invalid unicode character")
func (pe *ParseError) Error() string {
return fmt.Sprintf("%s:%d:%d:parse failed, parsing: %s", pe.Input, pe.Line+1, pe.Column+1, pe.Definition)
}
func parseInput(r io.Reader, p parser, b builder) (*Node, error) {
c := newContext(bufio.NewReader(r))
func parseInput(r io.Reader, p parser, b builder, kw []parser) (*Node, error) {
c := newContext(bufio.NewReader(r), kw)
p.parse(c)
if c.readErr != nil {
return nil, c.readErr
@ -758,35 +809,36 @@ func parseInput(r io.Reader, p parser, b builder) (*Node, error) {
}
func Parse(r io.Reader) (*Node, error) {
var p188 = sequenceParser{id: 188, commit: 32, ranges: [][]int{{0, -1}, {1, 1}, {0, -1}}}
var p186 = choiceParser{id: 186, commit: 2}
var p185 = choiceParser{id: 185, commit: 70, name: "wsc", generalizations: []int{186}}
var p15 = choiceParser{id: 15, commit: 66, name: "wschar", generalizations: []int{185, 186}}
var p2 = sequenceParser{id: 2, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 185, 186}}
var p196 = sequenceParser{id: 196, commit: 128, ranges: [][]int{{0, -1}, {1, 1}, {0, -1}}}
var p194 = choiceParser{id: 194, commit: 2}
var p193 = choiceParser{id: 193, commit: 262, name: "wsc", generalizations: []int{194}}
var p15 = choiceParser{id: 15, commit: 258, name: "wschar", generalizations: []int{193, 194}}
var p2 = sequenceParser{id: 2, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 193, 194}}
var p1 = charParser{id: 1, chars: []rune{32}}
p2.items = []parser{&p1}
var p4 = sequenceParser{id: 4, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 185, 186}}
var p4 = sequenceParser{id: 4, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 193, 194}}
var p3 = charParser{id: 3, chars: []rune{9}}
p4.items = []parser{&p3}
var p6 = sequenceParser{id: 6, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 185, 186}}
var p6 = sequenceParser{id: 6, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 193, 194}}
var p5 = charParser{id: 5, chars: []rune{10}}
p6.items = []parser{&p5}
var p8 = sequenceParser{id: 8, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 185, 186}}
var p8 = sequenceParser{id: 8, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 193, 194}}
var p7 = charParser{id: 7, chars: []rune{8}}
p8.items = []parser{&p7}
var p10 = sequenceParser{id: 10, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 185, 186}}
var p10 = sequenceParser{id: 10, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 193, 194}}
var p9 = charParser{id: 9, chars: []rune{12}}
p10.items = []parser{&p9}
var p12 = sequenceParser{id: 12, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 185, 186}}
var p12 = sequenceParser{id: 12, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 193, 194}}
var p11 = charParser{id: 11, chars: []rune{13}}
p12.items = []parser{&p11}
var p14 = sequenceParser{id: 14, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 185, 186}}
var p14 = sequenceParser{id: 14, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 193, 194}}
var p13 = charParser{id: 13, chars: []rune{11}}
p14.items = []parser{&p13}
p15.options = []parser{&p2, &p4, &p6, &p8, &p10, &p12, &p14}
var p54 = sequenceParser{id: 54, commit: 72, name: "comment", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}}, generalizations: []int{185, 186}}
var p37 = choiceParser{id: 37, commit: 74, name: "comment-segment"}
var p36 = sequenceParser{id: 36, commit: 74, name: "line-comment", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}}, generalizations: []int{37}}
var p54 = sequenceParser{id: 54, commit: 264, name: "comment", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}}, generalizations: []int{193, 194}}
var p37 = choiceParser{id: 37, commit: 266, name: "comment-segment"}
var p36 = sequenceParser{id: 36, commit: 266, name: "line-comment", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}}, generalizations: []int{37}}
var p33 = sequenceParser{id: 33, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}}}
var p31 = charParser{id: 31, chars: []rune{47}}
var p32 = charParser{id: 32, chars: []rune{47}}
@ -795,7 +847,7 @@ func Parse(r io.Reader) (*Node, error) {
var p34 = charParser{id: 34, not: true, chars: []rune{10}}
p35.items = []parser{&p34}
p36.items = []parser{&p33, &p35}
var p30 = sequenceParser{id: 30, commit: 74, name: "block-comment", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {1, 1}, {0, -1}, {1, 1}}, generalizations: []int{37}}
var p30 = sequenceParser{id: 30, commit: 266, name: "block-comment", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {1, 1}, {0, -1}, {1, 1}}, generalizations: []int{37}}
var p18 = sequenceParser{id: 18, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}}}
var p16 = charParser{id: 16, chars: []rune{47}}
var p17 = charParser{id: 17, chars: []rune{42}}
@ -820,7 +872,7 @@ func Parse(r io.Reader) (*Node, error) {
p30.items = []parser{&p18, &p26, &p29}
p37.options = []parser{&p36, &p30}
var p53 = sequenceParser{id: 53, commit: 10, ranges: [][]int{{0, -1}, {0, 1}, {0, -1}, {1, 1}, {0, -1}, {0, 1}, {0, -1}, {1, 1}}}
var p50 = choiceParser{id: 50, commit: 74, name: "ws-no-nl"}
var p50 = choiceParser{id: 50, commit: 266, name: "ws-no-nl"}
var p39 = sequenceParser{id: 39, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{50}}
var p38 = charParser{id: 38, chars: []rune{32}}
p39.items = []parser{&p38}
@ -845,82 +897,92 @@ func Parse(r io.Reader) (*Node, error) {
p52.items = []parser{&p51}
p53.items = []parser{&p50, &p52, &p50, &p37}
p54.items = []parser{&p37, &p53}
p185.options = []parser{&p15, &p54}
p186.options = []parser{&p185}
var p187 = sequenceParser{id: 187, commit: 66, name: "syntax:wsroot", ranges: [][]int{{0, 1}, {0, -1}, {0, 1}, {0, 1}}}
var p182 = sequenceParser{id: 182, commit: 2, ranges: [][]int{{1, 1}, {0, -1}}}
var p178 = sequenceParser{id: 178, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p177 = charParser{id: 177, chars: []rune{59}}
p178.items = []parser{&p177}
var p181 = sequenceParser{id: 181, commit: 2, ranges: [][]int{{0, -1}, {1, 1}}}
p181.items = []parser{&p186, &p178}
p182.items = []parser{&p178, &p181}
var p176 = sequenceParser{id: 176, commit: 66, name: "definitions", ranges: [][]int{{1, 1}, {0, 1}}}
var p169 = sequenceParser{id: 169, commit: 64, name: "definition", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}, {1, 1}}}
var p166 = sequenceParser{id: 166, commit: 74, name: "definition-name", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}}}
var p92 = sequenceParser{id: 92, commit: 72, name: "symbol", ranges: [][]int{{1, -1}, {1, -1}}, generalizations: []int{133, 123, 127}}
p193.options = []parser{&p15, &p54}
p194.options = []parser{&p193}
var p195 = sequenceParser{id: 195, commit: 258, name: "syntax:wsroot", ranges: [][]int{{0, 1}, {0, -1}, {0, 1}, {0, 1}}}
var p190 = sequenceParser{id: 190, commit: 2, ranges: [][]int{{1, 1}, {0, -1}}}
var p186 = sequenceParser{id: 186, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p185 = charParser{id: 185, chars: []rune{59}}
p186.items = []parser{&p185}
var p189 = sequenceParser{id: 189, commit: 2, ranges: [][]int{{0, -1}, {1, 1}}}
p189.items = []parser{&p194, &p186}
p190.items = []parser{&p186, &p189}
var p184 = sequenceParser{id: 184, commit: 258, name: "definitions", ranges: [][]int{{1, 1}, {0, 1}}}
var p177 = sequenceParser{id: 177, commit: 256, name: "definition", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}, {1, 1}}}
var p174 = sequenceParser{id: 174, commit: 266, name: "definition-name", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}}}
var p92 = sequenceParser{id: 92, commit: 264, name: "symbol", ranges: [][]int{{1, -1}, {1, -1}}, generalizations: []int{133, 123, 127}}
var p91 = sequenceParser{id: 91, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p90 = charParser{id: 90, not: true, chars: []rune{92, 32, 10, 9, 8, 12, 13, 11, 47, 46, 91, 93, 34, 123, 125, 94, 43, 42, 63, 124, 40, 41, 58, 61, 59}}
p91.items = []parser{&p90}
p92.items = []parser{&p91}
var p165 = sequenceParser{id: 165, commit: 10, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}}}
var p164 = sequenceParser{id: 164, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p163 = charParser{id: 163, chars: []rune{58}}
p164.items = []parser{&p163}
var p162 = choiceParser{id: 162, commit: 66, name: "flag"}
var p139 = sequenceParser{id: 139, commit: 72, name: "alias", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{162}}
var p173 = sequenceParser{id: 173, commit: 10, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}}}
var p172 = sequenceParser{id: 172, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p171 = charParser{id: 171, chars: []rune{58}}
p172.items = []parser{&p171}
var p170 = choiceParser{id: 170, commit: 258, name: "flag"}
var p139 = sequenceParser{id: 139, commit: 264, name: "alias", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{170}}
var p134 = charParser{id: 134, chars: []rune{97}}
var p135 = charParser{id: 135, chars: []rune{108}}
var p136 = charParser{id: 136, chars: []rune{105}}
var p137 = charParser{id: 137, chars: []rune{97}}
var p138 = charParser{id: 138, chars: []rune{115}}
p139.items = []parser{&p134, &p135, &p136, &p137, &p138}
var p142 = sequenceParser{id: 142, commit: 72, name: "ws", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{162}}
var p142 = sequenceParser{id: 142, commit: 264, name: "ws", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{170}}
var p140 = charParser{id: 140, chars: []rune{119}}
var p141 = charParser{id: 141, chars: []rune{115}}
p142.items = []parser{&p140, &p141}
var p147 = sequenceParser{id: 147, commit: 72, name: "nows", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{162}}
var p147 = sequenceParser{id: 147, commit: 264, name: "nows", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{170}}
var p143 = charParser{id: 143, chars: []rune{110}}
var p144 = charParser{id: 144, chars: []rune{111}}
var p145 = charParser{id: 145, chars: []rune{119}}
var p146 = charParser{id: 146, chars: []rune{115}}
p147.items = []parser{&p143, &p144, &p145, &p146}
var p156 = sequenceParser{id: 156, commit: 72, name: "failpass", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{162}}
var p148 = charParser{id: 148, chars: []rune{102}}
var p149 = charParser{id: 149, chars: []rune{97}}
var p150 = charParser{id: 150, chars: []rune{105}}
var p151 = charParser{id: 151, chars: []rune{108}}
var p152 = charParser{id: 152, chars: []rune{112}}
var p153 = charParser{id: 153, chars: []rune{97}}
var p154 = charParser{id: 154, chars: []rune{115}}
var p155 = charParser{id: 155, chars: []rune{115}}
p156.items = []parser{&p148, &p149, &p150, &p151, &p152, &p153, &p154, &p155}
var p161 = sequenceParser{id: 161, commit: 72, name: "root", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{162}}
var p157 = charParser{id: 157, chars: []rune{114}}
var p158 = charParser{id: 158, chars: []rune{111}}
var p159 = charParser{id: 159, chars: []rune{111}}
var p160 = charParser{id: 160, chars: []rune{116}}
p161.items = []parser{&p157, &p158, &p159, &p160}
p162.options = []parser{&p139, &p142, &p147, &p156, &p161}
p165.items = []parser{&p164, &p162}
p166.items = []parser{&p92, &p165}
var p168 = sequenceParser{id: 168, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p167 = charParser{id: 167, chars: []rune{61}}
p168.items = []parser{&p167}
var p133 = choiceParser{id: 133, commit: 66, name: "expression"}
var p89 = choiceParser{id: 89, commit: 66, name: "terminal", generalizations: []int{133, 123, 127}}
var p56 = sequenceParser{id: 56, commit: 72, name: "any-char", allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{89, 133, 123, 127}}
var p150 = sequenceParser{id: 150, commit: 264, name: "kw", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{170}}
var p148 = charParser{id: 148, chars: []rune{107}}
var p149 = charParser{id: 149, chars: []rune{119}}
p150.items = []parser{&p148, &p149}
var p155 = sequenceParser{id: 155, commit: 264, name: "nokw", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{170}}
var p151 = charParser{id: 151, chars: []rune{110}}
var p152 = charParser{id: 152, chars: []rune{111}}
var p153 = charParser{id: 153, chars: []rune{107}}
var p154 = charParser{id: 154, chars: []rune{119}}
p155.items = []parser{&p151, &p152, &p153, &p154}
var p164 = sequenceParser{id: 164, commit: 264, name: "failpass", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{170}}
var p156 = charParser{id: 156, chars: []rune{102}}
var p157 = charParser{id: 157, chars: []rune{97}}
var p158 = charParser{id: 158, chars: []rune{105}}
var p159 = charParser{id: 159, chars: []rune{108}}
var p160 = charParser{id: 160, chars: []rune{112}}
var p161 = charParser{id: 161, chars: []rune{97}}
var p162 = charParser{id: 162, chars: []rune{115}}
var p163 = charParser{id: 163, chars: []rune{115}}
p164.items = []parser{&p156, &p157, &p158, &p159, &p160, &p161, &p162, &p163}
var p169 = sequenceParser{id: 169, commit: 264, name: "root", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{170}}
var p165 = charParser{id: 165, chars: []rune{114}}
var p166 = charParser{id: 166, chars: []rune{111}}
var p167 = charParser{id: 167, chars: []rune{111}}
var p168 = charParser{id: 168, chars: []rune{116}}
p169.items = []parser{&p165, &p166, &p167, &p168}
p170.options = []parser{&p139, &p142, &p147, &p150, &p155, &p164, &p169}
p173.items = []parser{&p172, &p170}
p174.items = []parser{&p92, &p173}
var p176 = sequenceParser{id: 176, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p175 = charParser{id: 175, chars: []rune{61}}
p176.items = []parser{&p175}
var p133 = choiceParser{id: 133, commit: 258, name: "expression"}
var p89 = choiceParser{id: 89, commit: 258, name: "terminal", generalizations: []int{133, 123, 127}}
var p56 = sequenceParser{id: 56, commit: 264, name: "any-char", allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{89, 133, 123, 127}}
var p55 = charParser{id: 55, chars: []rune{46}}
p56.items = []parser{&p55}
var p75 = sequenceParser{id: 75, commit: 72, name: "char-class", ranges: [][]int{{1, 1}, {0, 1}, {0, -1}, {1, 1}, {1, 1}, {0, 1}, {0, -1}, {1, 1}}, generalizations: []int{89, 133, 123, 127}}
var p75 = sequenceParser{id: 75, commit: 264, name: "char-class", ranges: [][]int{{1, 1}, {0, 1}, {0, -1}, {1, 1}, {1, 1}, {0, 1}, {0, -1}, {1, 1}}, generalizations: []int{89, 133, 123, 127}}
var p71 = sequenceParser{id: 71, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p70 = charParser{id: 70, chars: []rune{91}}
p71.items = []parser{&p70}
var p58 = sequenceParser{id: 58, commit: 72, name: "class-not", allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p58 = sequenceParser{id: 58, commit: 264, name: "class-not", allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p57 = charParser{id: 57, chars: []rune{94}}
p58.items = []parser{&p57}
var p72 = choiceParser{id: 72, commit: 10}
var p66 = choiceParser{id: 66, commit: 72, name: "class-char", generalizations: []int{72}}
var p66 = choiceParser{id: 66, commit: 264, name: "class-char", generalizations: []int{72}}
var p60 = sequenceParser{id: 60, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{66, 72}}
var p59 = charParser{id: 59, not: true, chars: []rune{92, 91, 93, 94, 45}}
p60.items = []parser{&p59}
@ -933,7 +995,7 @@ func Parse(r io.Reader) (*Node, error) {
p64.items = []parser{&p63}
p65.items = []parser{&p62, &p64}
p66.options = []parser{&p60, &p65}
var p69 = sequenceParser{id: 69, commit: 72, name: "char-range", ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{72}}
var p69 = sequenceParser{id: 69, commit: 264, name: "char-range", ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{72}}
var p68 = sequenceParser{id: 68, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p67 = charParser{id: 67, chars: []rune{45}}
p68.items = []parser{&p67}
@ -943,11 +1005,11 @@ func Parse(r io.Reader) (*Node, error) {
var p73 = charParser{id: 73, chars: []rune{93}}
p74.items = []parser{&p73}
p75.items = []parser{&p71, &p58, &p72, &p74}
var p88 = sequenceParser{id: 88, commit: 72, name: "char-sequence", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {1, 1}, {0, -1}, {1, 1}}, generalizations: []int{89, 133, 123, 127}}
var p88 = sequenceParser{id: 88, commit: 264, name: "char-sequence", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {1, 1}, {0, -1}, {1, 1}}, generalizations: []int{89, 133, 123, 127}}
var p85 = sequenceParser{id: 85, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p84 = charParser{id: 84, chars: []rune{34}}
p85.items = []parser{&p84}
var p83 = choiceParser{id: 83, commit: 72, name: "sequence-char"}
var p83 = choiceParser{id: 83, commit: 264, name: "sequence-char"}
var p77 = sequenceParser{id: 77, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{83}}
var p76 = charParser{id: 76, not: true, chars: []rune{92, 34}}
p77.items = []parser{&p76}
@ -965,25 +1027,25 @@ func Parse(r io.Reader) (*Node, error) {
p87.items = []parser{&p86}
p88.items = []parser{&p85, &p83, &p87}
p89.options = []parser{&p56, &p75, &p88}
var p97 = sequenceParser{id: 97, commit: 66, name: "group", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}, {1, 1}}, generalizations: []int{133, 123, 127}}
var p97 = sequenceParser{id: 97, commit: 258, name: "group", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}, {1, 1}}, generalizations: []int{133, 123, 127}}
var p94 = sequenceParser{id: 94, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p93 = charParser{id: 93, chars: []rune{40}}
p94.items = []parser{&p93}
var p96 = sequenceParser{id: 96, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p95 = charParser{id: 95, chars: []rune{41}}
p96.items = []parser{&p95}
p97.items = []parser{&p94, &p186, &p133, &p186, &p96}
var p126 = sequenceParser{id: 126, commit: 64, name: "sequence", ranges: [][]int{{1, 1}, {0, -1}}, generalizations: []int{133, 127}}
var p124 = sequenceParser{id: 124, commit: 72, name: "item", ranges: [][]int{{1, 1}, {0, 1}, {1, 1}, {0, 1}}}
p97.items = []parser{&p94, &p194, &p133, &p194, &p96}
var p126 = sequenceParser{id: 126, commit: 256, name: "sequence", ranges: [][]int{{1, 1}, {0, -1}}, generalizations: []int{133, 127}}
var p124 = sequenceParser{id: 124, commit: 264, name: "item", ranges: [][]int{{1, 1}, {0, 1}, {1, 1}, {0, 1}}}
var p123 = choiceParser{id: 123, commit: 10}
p123.options = []parser{&p89, &p92, &p97}
var p122 = choiceParser{id: 122, commit: 66, name: "quantity"}
var p106 = sequenceParser{id: 106, commit: 64, name: "count-quantifier", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}, {1, 1}}, generalizations: []int{122}}
var p122 = choiceParser{id: 122, commit: 258, name: "quantity"}
var p106 = sequenceParser{id: 106, commit: 256, name: "count-quantifier", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}, {1, 1}}, generalizations: []int{122}}
var p103 = sequenceParser{id: 103, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p102 = charParser{id: 102, chars: []rune{123}}
p103.items = []parser{&p102}
var p101 = sequenceParser{id: 101, commit: 64, name: "count", ranges: [][]int{{1, 1}}}
var p100 = sequenceParser{id: 100, commit: 74, name: "number", ranges: [][]int{{1, -1}, {1, -1}}}
var p101 = sequenceParser{id: 101, commit: 256, name: "count", ranges: [][]int{{1, 1}}}
var p100 = sequenceParser{id: 100, commit: 266, name: "number", ranges: [][]int{{1, -1}, {1, -1}}}
var p99 = sequenceParser{id: 99, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p98 = charParser{id: 98, ranges: [][]rune{{48, 57}}}
p99.items = []parser{&p98}
@ -992,99 +1054,99 @@ func Parse(r io.Reader) (*Node, error) {
var p105 = sequenceParser{id: 105, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p104 = charParser{id: 104, chars: []rune{125}}
p105.items = []parser{&p104}
p106.items = []parser{&p103, &p186, &p101, &p186, &p105}
var p115 = sequenceParser{id: 115, commit: 64, name: "range-quantifier", ranges: [][]int{{1, 1}, {0, -1}, {0, 1}, {0, -1}, {1, 1}, {0, -1}, {0, 1}, {0, -1}, {1, 1}}, generalizations: []int{122}}
p106.items = []parser{&p103, &p194, &p101, &p194, &p105}
var p115 = sequenceParser{id: 115, commit: 256, name: "range-quantifier", ranges: [][]int{{1, 1}, {0, -1}, {0, 1}, {0, -1}, {1, 1}, {0, -1}, {0, 1}, {0, -1}, {1, 1}}, generalizations: []int{122}}
var p110 = sequenceParser{id: 110, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p109 = charParser{id: 109, chars: []rune{123}}
p110.items = []parser{&p109}
var p107 = sequenceParser{id: 107, commit: 64, name: "range-from", ranges: [][]int{{1, 1}}}
var p107 = sequenceParser{id: 107, commit: 256, name: "range-from", ranges: [][]int{{1, 1}}}
p107.items = []parser{&p100}
var p112 = sequenceParser{id: 112, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p111 = charParser{id: 111, chars: []rune{44}}
p112.items = []parser{&p111}
var p108 = sequenceParser{id: 108, commit: 64, name: "range-to", ranges: [][]int{{1, 1}}}
var p108 = sequenceParser{id: 108, commit: 256, name: "range-to", ranges: [][]int{{1, 1}}}
p108.items = []parser{&p100}
var p114 = sequenceParser{id: 114, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p113 = charParser{id: 113, chars: []rune{125}}
p114.items = []parser{&p113}
p115.items = []parser{&p110, &p186, &p107, &p186, &p112, &p186, &p108, &p186, &p114}
var p117 = sequenceParser{id: 117, commit: 72, name: "one-or-more", allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{122}}
p115.items = []parser{&p110, &p194, &p107, &p194, &p112, &p194, &p108, &p194, &p114}
var p117 = sequenceParser{id: 117, commit: 264, name: "one-or-more", allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{122}}
var p116 = charParser{id: 116, chars: []rune{43}}
p117.items = []parser{&p116}
var p119 = sequenceParser{id: 119, commit: 72, name: "zero-or-more", allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{122}}
var p119 = sequenceParser{id: 119, commit: 264, name: "zero-or-more", allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{122}}
var p118 = charParser{id: 118, chars: []rune{42}}
p119.items = []parser{&p118}
var p121 = sequenceParser{id: 121, commit: 72, name: "zero-or-one", allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{122}}
var p121 = sequenceParser{id: 121, commit: 264, name: "zero-or-one", allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{122}}
var p120 = charParser{id: 120, chars: []rune{63}}
p121.items = []parser{&p120}
p122.options = []parser{&p106, &p115, &p117, &p119, &p121}
p124.items = []parser{&p123, &p122}
var p125 = sequenceParser{id: 125, commit: 2, ranges: [][]int{{0, -1}, {1, 1}}}
p125.items = []parser{&p186, &p124}
p125.items = []parser{&p194, &p124}
p126.items = []parser{&p124, &p125}
var p132 = sequenceParser{id: 132, commit: 64, name: "choice", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}}, generalizations: []int{133}}
var p127 = choiceParser{id: 127, commit: 66, name: "option"}
var p132 = sequenceParser{id: 132, commit: 256, name: "choice", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}}, generalizations: []int{133}}
var p127 = choiceParser{id: 127, commit: 258, name: "option"}
p127.options = []parser{&p89, &p92, &p97, &p126}
var p130 = sequenceParser{id: 130, commit: 2, ranges: [][]int{{1, 1}, {0, -1}, {1, 1}}}
var p129 = sequenceParser{id: 129, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p128 = charParser{id: 128, chars: []rune{124}}
p129.items = []parser{&p128}
p130.items = []parser{&p129, &p186, &p127}
p130.items = []parser{&p129, &p194, &p127}
var p131 = sequenceParser{id: 131, commit: 2, ranges: [][]int{{0, -1}, {1, 1}}}
p131.items = []parser{&p186, &p130}
p132.items = []parser{&p127, &p186, &p130, &p131}
p131.items = []parser{&p194, &p130}
p132.items = []parser{&p127, &p194, &p130, &p131}
p133.options = []parser{&p89, &p92, &p97, &p126, &p132}
p169.items = []parser{&p166, &p186, &p168, &p186, &p133}
var p175 = sequenceParser{id: 175, commit: 2, ranges: [][]int{{0, -1}, {1, 1}, {0, -1}}}
var p173 = sequenceParser{id: 173, commit: 2, ranges: [][]int{{1, 1}, {0, -1}, {0, -1}, {1, 1}}}
var p171 = sequenceParser{id: 171, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p170 = charParser{id: 170, chars: []rune{59}}
p171.items = []parser{&p170}
var p172 = sequenceParser{id: 172, commit: 2, ranges: [][]int{{0, -1}, {1, 1}}}
p172.items = []parser{&p186, &p171}
p173.items = []parser{&p171, &p172, &p186, &p169}
var p174 = sequenceParser{id: 174, commit: 2, ranges: [][]int{{0, -1}, {1, 1}}}
p174.items = []parser{&p186, &p173}
p175.items = []parser{&p186, &p173, &p174}
p176.items = []parser{&p169, &p175}
var p184 = sequenceParser{id: 184, commit: 2, ranges: [][]int{{0, -1}, {1, 1}, {0, -1}}}
var p180 = sequenceParser{id: 180, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p179 = charParser{id: 179, chars: []rune{59}}
p180.items = []parser{&p179}
var p183 = sequenceParser{id: 183, commit: 2, ranges: [][]int{{0, -1}, {1, 1}}}
p183.items = []parser{&p186, &p180}
p184.items = []parser{&p186, &p180, &p183}
p187.items = []parser{&p182, &p186, &p176, &p184}
p188.items = []parser{&p186, &p187, &p186}
var b188 = sequenceBuilder{id: 188, commit: 32, name: "syntax", ranges: [][]int{{0, -1}, {1, 1}, {0, -1}}}
var b186 = choiceBuilder{id: 186, commit: 2}
var b185 = choiceBuilder{id: 185, commit: 70}
var b15 = choiceBuilder{id: 15, commit: 66}
var b2 = sequenceBuilder{id: 2, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
p177.items = []parser{&p174, &p194, &p176, &p194, &p133}
var p183 = sequenceParser{id: 183, commit: 2, ranges: [][]int{{0, -1}, {1, 1}, {0, -1}}}
var p181 = sequenceParser{id: 181, commit: 2, ranges: [][]int{{1, 1}, {0, -1}, {0, -1}, {1, 1}}}
var p179 = sequenceParser{id: 179, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p178 = charParser{id: 178, chars: []rune{59}}
p179.items = []parser{&p178}
var p180 = sequenceParser{id: 180, commit: 2, ranges: [][]int{{0, -1}, {1, 1}}}
p180.items = []parser{&p194, &p179}
p181.items = []parser{&p179, &p180, &p194, &p177}
var p182 = sequenceParser{id: 182, commit: 2, ranges: [][]int{{0, -1}, {1, 1}}}
p182.items = []parser{&p194, &p181}
p183.items = []parser{&p194, &p181, &p182}
p184.items = []parser{&p177, &p183}
var p192 = sequenceParser{id: 192, commit: 2, ranges: [][]int{{0, -1}, {1, 1}, {0, -1}}}
var p188 = sequenceParser{id: 188, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var p187 = charParser{id: 187, chars: []rune{59}}
p188.items = []parser{&p187}
var p191 = sequenceParser{id: 191, commit: 2, ranges: [][]int{{0, -1}, {1, 1}}}
p191.items = []parser{&p194, &p188}
p192.items = []parser{&p194, &p188, &p191}
p195.items = []parser{&p190, &p194, &p184, &p192}
p196.items = []parser{&p194, &p195, &p194}
var b196 = sequenceBuilder{id: 196, commit: 128, name: "syntax", ranges: [][]int{{0, -1}, {1, 1}, {0, -1}}}
var b194 = choiceBuilder{id: 194, commit: 2}
var b193 = choiceBuilder{id: 193, commit: 262, generalizations: []int{194}}
var b15 = choiceBuilder{id: 15, commit: 258, generalizations: []int{193, 194}}
var b2 = sequenceBuilder{id: 2, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 193, 194}}
var b1 = charBuilder{}
b2.items = []builder{&b1}
var b4 = sequenceBuilder{id: 4, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b4 = sequenceBuilder{id: 4, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 193, 194}}
var b3 = charBuilder{}
b4.items = []builder{&b3}
var b6 = sequenceBuilder{id: 6, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b6 = sequenceBuilder{id: 6, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 193, 194}}
var b5 = charBuilder{}
b6.items = []builder{&b5}
var b8 = sequenceBuilder{id: 8, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b8 = sequenceBuilder{id: 8, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 193, 194}}
var b7 = charBuilder{}
b8.items = []builder{&b7}
var b10 = sequenceBuilder{id: 10, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b10 = sequenceBuilder{id: 10, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 193, 194}}
var b9 = charBuilder{}
b10.items = []builder{&b9}
var b12 = sequenceBuilder{id: 12, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b12 = sequenceBuilder{id: 12, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 193, 194}}
var b11 = charBuilder{}
b12.items = []builder{&b11}
var b14 = sequenceBuilder{id: 14, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b14 = sequenceBuilder{id: 14, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{15, 193, 194}}
var b13 = charBuilder{}
b14.items = []builder{&b13}
b15.options = []builder{&b2, &b4, &b6, &b8, &b10, &b12, &b14}
var b54 = sequenceBuilder{id: 54, commit: 72, name: "comment", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}}}
var b37 = choiceBuilder{id: 37, commit: 74}
var b36 = sequenceBuilder{id: 36, commit: 74, ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}}}
var b54 = sequenceBuilder{id: 54, commit: 264, name: "comment", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}}, generalizations: []int{193, 194}}
var b37 = choiceBuilder{id: 37, commit: 266}
var b36 = sequenceBuilder{id: 36, commit: 266, ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}}, generalizations: []int{37}}
var b33 = sequenceBuilder{id: 33, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}}}
var b31 = charBuilder{}
var b32 = charBuilder{}
@ -1093,13 +1155,13 @@ func Parse(r io.Reader) (*Node, error) {
var b34 = charBuilder{}
b35.items = []builder{&b34}
b36.items = []builder{&b33, &b35}
var b30 = sequenceBuilder{id: 30, commit: 74, ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {1, 1}, {0, -1}, {1, 1}}}
var b30 = sequenceBuilder{id: 30, commit: 266, ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {1, 1}, {0, -1}, {1, 1}}, generalizations: []int{37}}
var b18 = sequenceBuilder{id: 18, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}}}
var b16 = charBuilder{}
var b17 = charBuilder{}
b18.items = []builder{&b16, &b17}
var b26 = choiceBuilder{id: 26, commit: 10}
var b23 = sequenceBuilder{id: 23, commit: 10, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}}}
var b23 = sequenceBuilder{id: 23, commit: 10, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{26}}
var b20 = sequenceBuilder{id: 20, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b19 = charBuilder{}
b20.items = []builder{&b19}
@ -1107,7 +1169,7 @@ func Parse(r io.Reader) (*Node, error) {
var b21 = charBuilder{}
b22.items = []builder{&b21}
b23.items = []builder{&b20, &b22}
var b25 = sequenceBuilder{id: 25, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b25 = sequenceBuilder{id: 25, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{26}}
var b24 = charBuilder{}
b25.items = []builder{&b24}
b26.options = []builder{&b23, &b25}
@ -1118,23 +1180,23 @@ func Parse(r io.Reader) (*Node, error) {
b30.items = []builder{&b18, &b26, &b29}
b37.options = []builder{&b36, &b30}
var b53 = sequenceBuilder{id: 53, commit: 10, ranges: [][]int{{0, -1}, {0, 1}, {0, -1}, {1, 1}, {0, -1}, {0, 1}, {0, -1}, {1, 1}}}
var b50 = choiceBuilder{id: 50, commit: 74}
var b39 = sequenceBuilder{id: 39, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b50 = choiceBuilder{id: 50, commit: 266}
var b39 = sequenceBuilder{id: 39, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{50}}
var b38 = charBuilder{}
b39.items = []builder{&b38}
var b41 = sequenceBuilder{id: 41, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b41 = sequenceBuilder{id: 41, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{50}}
var b40 = charBuilder{}
b41.items = []builder{&b40}
var b43 = sequenceBuilder{id: 43, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b43 = sequenceBuilder{id: 43, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{50}}
var b42 = charBuilder{}
b43.items = []builder{&b42}
var b45 = sequenceBuilder{id: 45, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b45 = sequenceBuilder{id: 45, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{50}}
var b44 = charBuilder{}
b45.items = []builder{&b44}
var b47 = sequenceBuilder{id: 47, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b47 = sequenceBuilder{id: 47, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{50}}
var b46 = charBuilder{}
b47.items = []builder{&b46}
var b49 = sequenceBuilder{id: 49, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b49 = sequenceBuilder{id: 49, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{50}}
var b48 = charBuilder{}
b49.items = []builder{&b48}
b50.options = []builder{&b39, &b41, &b43, &b45, &b47, &b49}
@ -1143,86 +1205,96 @@ func Parse(r io.Reader) (*Node, error) {
b52.items = []builder{&b51}
b53.items = []builder{&b50, &b52, &b50, &b37}
b54.items = []builder{&b37, &b53}
b185.options = []builder{&b15, &b54}
b186.options = []builder{&b185}
var b187 = sequenceBuilder{id: 187, commit: 66, ranges: [][]int{{0, 1}, {0, -1}, {0, 1}, {0, 1}}}
var b182 = sequenceBuilder{id: 182, commit: 2, ranges: [][]int{{1, 1}, {0, -1}}}
var b178 = sequenceBuilder{id: 178, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b177 = charBuilder{}
b178.items = []builder{&b177}
var b181 = sequenceBuilder{id: 181, commit: 2, ranges: [][]int{{0, -1}, {1, 1}}}
b181.items = []builder{&b186, &b178}
b182.items = []builder{&b178, &b181}
var b176 = sequenceBuilder{id: 176, commit: 66, ranges: [][]int{{1, 1}, {0, 1}}}
var b169 = sequenceBuilder{id: 169, commit: 64, name: "definition", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}, {1, 1}}}
var b166 = sequenceBuilder{id: 166, commit: 74, ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}}}
var b92 = sequenceBuilder{id: 92, commit: 72, name: "symbol", ranges: [][]int{{1, -1}, {1, -1}}}
b193.options = []builder{&b15, &b54}
b194.options = []builder{&b193}
var b195 = sequenceBuilder{id: 195, commit: 258, ranges: [][]int{{0, 1}, {0, -1}, {0, 1}, {0, 1}}}
var b190 = sequenceBuilder{id: 190, commit: 2, ranges: [][]int{{1, 1}, {0, -1}}}
var b186 = sequenceBuilder{id: 186, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b185 = charBuilder{}
b186.items = []builder{&b185}
var b189 = sequenceBuilder{id: 189, commit: 2, ranges: [][]int{{0, -1}, {1, 1}}}
b189.items = []builder{&b194, &b186}
b190.items = []builder{&b186, &b189}
var b184 = sequenceBuilder{id: 184, commit: 258, ranges: [][]int{{1, 1}, {0, 1}}}
var b177 = sequenceBuilder{id: 177, commit: 256, name: "definition", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}, {1, 1}}}
var b174 = sequenceBuilder{id: 174, commit: 266, ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}}}
var b92 = sequenceBuilder{id: 92, commit: 264, name: "symbol", ranges: [][]int{{1, -1}, {1, -1}}, generalizations: []int{133, 123, 127}}
var b91 = sequenceBuilder{id: 91, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b90 = charBuilder{}
b91.items = []builder{&b90}
b92.items = []builder{&b91}
var b165 = sequenceBuilder{id: 165, commit: 10, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}}}
var b164 = sequenceBuilder{id: 164, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b163 = charBuilder{}
b164.items = []builder{&b163}
var b162 = choiceBuilder{id: 162, commit: 66}
var b139 = sequenceBuilder{id: 139, commit: 72, name: "alias", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}}
var b173 = sequenceBuilder{id: 173, commit: 10, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}}}
var b172 = sequenceBuilder{id: 172, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b171 = charBuilder{}
b172.items = []builder{&b171}
var b170 = choiceBuilder{id: 170, commit: 258}
var b139 = sequenceBuilder{id: 139, commit: 264, name: "alias", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{170}}
var b134 = charBuilder{}
var b135 = charBuilder{}
var b136 = charBuilder{}
var b137 = charBuilder{}
var b138 = charBuilder{}
b139.items = []builder{&b134, &b135, &b136, &b137, &b138}
var b142 = sequenceBuilder{id: 142, commit: 72, name: "ws", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}}}
var b142 = sequenceBuilder{id: 142, commit: 264, name: "ws", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{170}}
var b140 = charBuilder{}
var b141 = charBuilder{}
b142.items = []builder{&b140, &b141}
var b147 = sequenceBuilder{id: 147, commit: 72, name: "nows", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}}
var b147 = sequenceBuilder{id: 147, commit: 264, name: "nows", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{170}}
var b143 = charBuilder{}
var b144 = charBuilder{}
var b145 = charBuilder{}
var b146 = charBuilder{}
b147.items = []builder{&b143, &b144, &b145, &b146}
var b156 = sequenceBuilder{id: 156, commit: 72, name: "failpass", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}}
var b150 = sequenceBuilder{id: 150, commit: 264, name: "kw", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{170}}
var b148 = charBuilder{}
var b149 = charBuilder{}
var b150 = charBuilder{}
b150.items = []builder{&b148, &b149}
var b155 = sequenceBuilder{id: 155, commit: 264, name: "nokw", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{170}}
var b151 = charBuilder{}
var b152 = charBuilder{}
var b153 = charBuilder{}
var b154 = charBuilder{}
var b155 = charBuilder{}
b156.items = []builder{&b148, &b149, &b150, &b151, &b152, &b153, &b154, &b155}
var b161 = sequenceBuilder{id: 161, commit: 72, name: "root", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}}
b155.items = []builder{&b151, &b152, &b153, &b154}
var b164 = sequenceBuilder{id: 164, commit: 264, name: "failpass", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{170}}
var b156 = charBuilder{}
var b157 = charBuilder{}
var b158 = charBuilder{}
var b159 = charBuilder{}
var b160 = charBuilder{}
b161.items = []builder{&b157, &b158, &b159, &b160}
b162.options = []builder{&b139, &b142, &b147, &b156, &b161}
b165.items = []builder{&b164, &b162}
b166.items = []builder{&b92, &b165}
var b168 = sequenceBuilder{id: 168, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b161 = charBuilder{}
var b162 = charBuilder{}
var b163 = charBuilder{}
b164.items = []builder{&b156, &b157, &b158, &b159, &b160, &b161, &b162, &b163}
var b169 = sequenceBuilder{id: 169, commit: 264, name: "root", allChars: true, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{170}}
var b165 = charBuilder{}
var b166 = charBuilder{}
var b167 = charBuilder{}
b168.items = []builder{&b167}
var b133 = choiceBuilder{id: 133, commit: 66}
var b89 = choiceBuilder{id: 89, commit: 66}
var b56 = sequenceBuilder{id: 56, commit: 72, name: "any-char", allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b168 = charBuilder{}
b169.items = []builder{&b165, &b166, &b167, &b168}
b170.options = []builder{&b139, &b142, &b147, &b150, &b155, &b164, &b169}
b173.items = []builder{&b172, &b170}
b174.items = []builder{&b92, &b173}
var b176 = sequenceBuilder{id: 176, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b175 = charBuilder{}
b176.items = []builder{&b175}
var b133 = choiceBuilder{id: 133, commit: 258}
var b89 = choiceBuilder{id: 89, commit: 258, generalizations: []int{133, 123, 127}}
var b56 = sequenceBuilder{id: 56, commit: 264, name: "any-char", allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{89, 133, 123, 127}}
var b55 = charBuilder{}
b56.items = []builder{&b55}
var b75 = sequenceBuilder{id: 75, commit: 72, name: "char-class", ranges: [][]int{{1, 1}, {0, 1}, {0, -1}, {1, 1}, {1, 1}, {0, 1}, {0, -1}, {1, 1}}}
var b75 = sequenceBuilder{id: 75, commit: 264, name: "char-class", ranges: [][]int{{1, 1}, {0, 1}, {0, -1}, {1, 1}, {1, 1}, {0, 1}, {0, -1}, {1, 1}}, generalizations: []int{89, 133, 123, 127}}
var b71 = sequenceBuilder{id: 71, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b70 = charBuilder{}
b71.items = []builder{&b70}
var b58 = sequenceBuilder{id: 58, commit: 72, name: "class-not", allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b58 = sequenceBuilder{id: 58, commit: 264, name: "class-not", allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b57 = charBuilder{}
b58.items = []builder{&b57}
var b72 = choiceBuilder{id: 72, commit: 10}
var b66 = choiceBuilder{id: 66, commit: 72, name: "class-char"}
var b60 = sequenceBuilder{id: 60, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b66 = choiceBuilder{id: 66, commit: 264, name: "class-char", generalizations: []int{72}}
var b60 = sequenceBuilder{id: 60, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{66, 72}}
var b59 = charBuilder{}
b60.items = []builder{&b59}
var b65 = sequenceBuilder{id: 65, commit: 10, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}}}
var b65 = sequenceBuilder{id: 65, commit: 10, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{66, 72}}
var b62 = sequenceBuilder{id: 62, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b61 = charBuilder{}
b62.items = []builder{&b61}
@ -1231,7 +1303,7 @@ func Parse(r io.Reader) (*Node, error) {
b64.items = []builder{&b63}
b65.items = []builder{&b62, &b64}
b66.options = []builder{&b60, &b65}
var b69 = sequenceBuilder{id: 69, commit: 72, name: "char-range", ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}}
var b69 = sequenceBuilder{id: 69, commit: 264, name: "char-range", ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{72}}
var b68 = sequenceBuilder{id: 68, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b67 = charBuilder{}
b68.items = []builder{&b67}
@ -1241,15 +1313,15 @@ func Parse(r io.Reader) (*Node, error) {
var b73 = charBuilder{}
b74.items = []builder{&b73}
b75.items = []builder{&b71, &b58, &b72, &b74}
var b88 = sequenceBuilder{id: 88, commit: 72, name: "char-sequence", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {1, 1}, {0, -1}, {1, 1}}}
var b88 = sequenceBuilder{id: 88, commit: 264, name: "char-sequence", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {1, 1}, {0, -1}, {1, 1}}, generalizations: []int{89, 133, 123, 127}}
var b85 = sequenceBuilder{id: 85, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b84 = charBuilder{}
b85.items = []builder{&b84}
var b83 = choiceBuilder{id: 83, commit: 72, name: "sequence-char"}
var b77 = sequenceBuilder{id: 77, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b83 = choiceBuilder{id: 83, commit: 264, name: "sequence-char"}
var b77 = sequenceBuilder{id: 77, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{83}}
var b76 = charBuilder{}
b77.items = []builder{&b76}
var b82 = sequenceBuilder{id: 82, commit: 10, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}}}
var b82 = sequenceBuilder{id: 82, commit: 10, ranges: [][]int{{1, 1}, {1, 1}, {1, 1}, {1, 1}}, generalizations: []int{83}}
var b79 = sequenceBuilder{id: 79, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b78 = charBuilder{}
b79.items = []builder{&b78}
@ -1263,25 +1335,25 @@ func Parse(r io.Reader) (*Node, error) {
b87.items = []builder{&b86}
b88.items = []builder{&b85, &b83, &b87}
b89.options = []builder{&b56, &b75, &b88}
var b97 = sequenceBuilder{id: 97, commit: 66, ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}, {1, 1}}}
var b97 = sequenceBuilder{id: 97, commit: 258, ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}, {1, 1}}, generalizations: []int{133, 123, 127}}
var b94 = sequenceBuilder{id: 94, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b93 = charBuilder{}
b94.items = []builder{&b93}
var b96 = sequenceBuilder{id: 96, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b95 = charBuilder{}
b96.items = []builder{&b95}
b97.items = []builder{&b94, &b186, &b133, &b186, &b96}
var b126 = sequenceBuilder{id: 126, commit: 64, name: "sequence", ranges: [][]int{{1, 1}, {0, -1}}}
var b124 = sequenceBuilder{id: 124, commit: 72, name: "item", ranges: [][]int{{1, 1}, {0, 1}, {1, 1}, {0, 1}}}
b97.items = []builder{&b94, &b194, &b133, &b194, &b96}
var b126 = sequenceBuilder{id: 126, commit: 256, name: "sequence", ranges: [][]int{{1, 1}, {0, -1}}, generalizations: []int{133, 127}}
var b124 = sequenceBuilder{id: 124, commit: 264, name: "item", ranges: [][]int{{1, 1}, {0, 1}, {1, 1}, {0, 1}}}
var b123 = choiceBuilder{id: 123, commit: 10}
b123.options = []builder{&b89, &b92, &b97}
var b122 = choiceBuilder{id: 122, commit: 66}
var b106 = sequenceBuilder{id: 106, commit: 64, name: "count-quantifier", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}, {1, 1}}}
var b122 = choiceBuilder{id: 122, commit: 258}
var b106 = sequenceBuilder{id: 106, commit: 256, name: "count-quantifier", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}, {1, 1}}, generalizations: []int{122}}
var b103 = sequenceBuilder{id: 103, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b102 = charBuilder{}
b103.items = []builder{&b102}
var b101 = sequenceBuilder{id: 101, commit: 64, name: "count", ranges: [][]int{{1, 1}}}
var b100 = sequenceBuilder{id: 100, commit: 74, ranges: [][]int{{1, -1}, {1, -1}}}
var b101 = sequenceBuilder{id: 101, commit: 256, name: "count", ranges: [][]int{{1, 1}}}
var b100 = sequenceBuilder{id: 100, commit: 266, ranges: [][]int{{1, -1}, {1, -1}}}
var b99 = sequenceBuilder{id: 99, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b98 = charBuilder{}
b99.items = []builder{&b98}
@ -1290,70 +1362,72 @@ func Parse(r io.Reader) (*Node, error) {
var b105 = sequenceBuilder{id: 105, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b104 = charBuilder{}
b105.items = []builder{&b104}
b106.items = []builder{&b103, &b186, &b101, &b186, &b105}
var b115 = sequenceBuilder{id: 115, commit: 64, name: "range-quantifier", ranges: [][]int{{1, 1}, {0, -1}, {0, 1}, {0, -1}, {1, 1}, {0, -1}, {0, 1}, {0, -1}, {1, 1}}}
b106.items = []builder{&b103, &b194, &b101, &b194, &b105}
var b115 = sequenceBuilder{id: 115, commit: 256, name: "range-quantifier", ranges: [][]int{{1, 1}, {0, -1}, {0, 1}, {0, -1}, {1, 1}, {0, -1}, {0, 1}, {0, -1}, {1, 1}}, generalizations: []int{122}}
var b110 = sequenceBuilder{id: 110, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b109 = charBuilder{}
b110.items = []builder{&b109}
var b107 = sequenceBuilder{id: 107, commit: 64, name: "range-from", ranges: [][]int{{1, 1}}}
var b107 = sequenceBuilder{id: 107, commit: 256, name: "range-from", ranges: [][]int{{1, 1}}}
b107.items = []builder{&b100}
var b112 = sequenceBuilder{id: 112, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b111 = charBuilder{}
b112.items = []builder{&b111}
var b108 = sequenceBuilder{id: 108, commit: 64, name: "range-to", ranges: [][]int{{1, 1}}}
var b108 = sequenceBuilder{id: 108, commit: 256, name: "range-to", ranges: [][]int{{1, 1}}}
b108.items = []builder{&b100}
var b114 = sequenceBuilder{id: 114, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b113 = charBuilder{}
b114.items = []builder{&b113}
b115.items = []builder{&b110, &b186, &b107, &b186, &b112, &b186, &b108, &b186, &b114}
var b117 = sequenceBuilder{id: 117, commit: 72, name: "one-or-more", allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
b115.items = []builder{&b110, &b194, &b107, &b194, &b112, &b194, &b108, &b194, &b114}
var b117 = sequenceBuilder{id: 117, commit: 264, name: "one-or-more", allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{122}}
var b116 = charBuilder{}
b117.items = []builder{&b116}
var b119 = sequenceBuilder{id: 119, commit: 72, name: "zero-or-more", allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b119 = sequenceBuilder{id: 119, commit: 264, name: "zero-or-more", allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{122}}
var b118 = charBuilder{}
b119.items = []builder{&b118}
var b121 = sequenceBuilder{id: 121, commit: 72, name: "zero-or-one", allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b121 = sequenceBuilder{id: 121, commit: 264, name: "zero-or-one", allChars: true, ranges: [][]int{{1, 1}, {1, 1}}, generalizations: []int{122}}
var b120 = charBuilder{}
b121.items = []builder{&b120}
b122.options = []builder{&b106, &b115, &b117, &b119, &b121}
b124.items = []builder{&b123, &b122}
var b125 = sequenceBuilder{id: 125, commit: 2, ranges: [][]int{{0, -1}, {1, 1}}}
b125.items = []builder{&b186, &b124}
b125.items = []builder{&b194, &b124}
b126.items = []builder{&b124, &b125}
var b132 = sequenceBuilder{id: 132, commit: 64, name: "choice", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}}}
var b127 = choiceBuilder{id: 127, commit: 66}
var b132 = sequenceBuilder{id: 132, commit: 256, name: "choice", ranges: [][]int{{1, 1}, {0, -1}, {1, 1}, {0, -1}}, generalizations: []int{133}}
var b127 = choiceBuilder{id: 127, commit: 258}
b127.options = []builder{&b89, &b92, &b97, &b126}
var b130 = sequenceBuilder{id: 130, commit: 2, ranges: [][]int{{1, 1}, {0, -1}, {1, 1}}}
var b129 = sequenceBuilder{id: 129, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b128 = charBuilder{}
b129.items = []builder{&b128}
b130.items = []builder{&b129, &b186, &b127}
b130.items = []builder{&b129, &b194, &b127}
var b131 = sequenceBuilder{id: 131, commit: 2, ranges: [][]int{{0, -1}, {1, 1}}}
b131.items = []builder{&b186, &b130}
b132.items = []builder{&b127, &b186, &b130, &b131}
b131.items = []builder{&b194, &b130}
b132.items = []builder{&b127, &b194, &b130, &b131}
b133.options = []builder{&b89, &b92, &b97, &b126, &b132}
b169.items = []builder{&b166, &b186, &b168, &b186, &b133}
var b175 = sequenceBuilder{id: 175, commit: 2, ranges: [][]int{{0, -1}, {1, 1}, {0, -1}}}
var b173 = sequenceBuilder{id: 173, commit: 2, ranges: [][]int{{1, 1}, {0, -1}, {0, -1}, {1, 1}}}
var b171 = sequenceBuilder{id: 171, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b170 = charBuilder{}
b171.items = []builder{&b170}
var b172 = sequenceBuilder{id: 172, commit: 2, ranges: [][]int{{0, -1}, {1, 1}}}
b172.items = []builder{&b186, &b171}
b173.items = []builder{&b171, &b172, &b186, &b169}
var b174 = sequenceBuilder{id: 174, commit: 2, ranges: [][]int{{0, -1}, {1, 1}}}
b174.items = []builder{&b186, &b173}
b175.items = []builder{&b186, &b173, &b174}
b176.items = []builder{&b169, &b175}
var b184 = sequenceBuilder{id: 184, commit: 2, ranges: [][]int{{0, -1}, {1, 1}, {0, -1}}}
var b180 = sequenceBuilder{id: 180, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b179 = charBuilder{}
b180.items = []builder{&b179}
var b183 = sequenceBuilder{id: 183, commit: 2, ranges: [][]int{{0, -1}, {1, 1}}}
b183.items = []builder{&b186, &b180}
b184.items = []builder{&b186, &b180, &b183}
b187.items = []builder{&b182, &b186, &b176, &b184}
b188.items = []builder{&b186, &b187, &b186}
b177.items = []builder{&b174, &b194, &b176, &b194, &b133}
var b183 = sequenceBuilder{id: 183, commit: 2, ranges: [][]int{{0, -1}, {1, 1}, {0, -1}}}
var b181 = sequenceBuilder{id: 181, commit: 2, ranges: [][]int{{1, 1}, {0, -1}, {0, -1}, {1, 1}}}
var b179 = sequenceBuilder{id: 179, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b178 = charBuilder{}
b179.items = []builder{&b178}
var b180 = sequenceBuilder{id: 180, commit: 2, ranges: [][]int{{0, -1}, {1, 1}}}
b180.items = []builder{&b194, &b179}
b181.items = []builder{&b179, &b180, &b194, &b177}
var b182 = sequenceBuilder{id: 182, commit: 2, ranges: [][]int{{0, -1}, {1, 1}}}
b182.items = []builder{&b194, &b181}
b183.items = []builder{&b194, &b181, &b182}
b184.items = []builder{&b177, &b183}
var b192 = sequenceBuilder{id: 192, commit: 2, ranges: [][]int{{0, -1}, {1, 1}, {0, -1}}}
var b188 = sequenceBuilder{id: 188, commit: 10, allChars: true, ranges: [][]int{{1, 1}, {1, 1}}}
var b187 = charBuilder{}
b188.items = []builder{&b187}
var b191 = sequenceBuilder{id: 191, commit: 2, ranges: [][]int{{0, -1}, {1, 1}}}
b191.items = []builder{&b194, &b188}
b192.items = []builder{&b194, &b188, &b191}
b195.items = []builder{&b190, &b194, &b184, &b192}
b196.items = []builder{&b194, &b195, &b194}
return parseInput(r, &p188, &b188)
var keywords = []parser{}
return parseInput(r, &p196, &b196, keywords)
}

View File

@ -52,6 +52,20 @@ func (p *sequenceParser) parse(c *context) {
continue
}
c.offset = from
if c.fromResults(p) {
if to > c.failOffset {
c.failOffset = -1
c.failingParser = nil
}
if !p.allChars {
c.results.unmarkPending(from, p.id)
}
return
}
if c.failingParser == nil &&
p.commit&userDefined != 0 &&
p.commit&Whitespace == 0 &&
@ -80,6 +94,22 @@ func (p *sequenceParser) parse(c *context) {
}
}
if p.commit&NoKeyword != 0 && c.isKeyword(from, to) {
if c.failingParser == nil &&
p.commit&userDefined != 0 &&
p.commit&Whitespace == 0 &&
p.commit&FailPass == 0 {
c.failingParser = p
}
c.fail(from)
if !p.allChars {
c.results.unmarkPending(from, p.id)
}
return
}
for _, g := range p.generalizations {
if c.results.pending(from, g) {
c.results.setMatch(from, g, to)

View File

@ -394,6 +394,15 @@ func (b *sequenceBuilder) generate(w io.Writer, done map[string]bool) error {
fprintf("},")
}
if len(b.generalizations) > 0 {
fprintf("generalizations: []int{")
for i := range b.generalizations {
fprintf("%d,", b.generalizations[i])
}
fprintf("},")
}
fprintf("};")
if len(b.items) > 0 {

130
syntax.go
View File

@ -4,8 +4,7 @@ import (
"errors"
"fmt"
"io"
"github.com/aryszka/treerack/self"
"code.squareroundforest.org/arpio/treerack/self"
)
// if min=0&&max=0, it means min=1,max=1
@ -21,9 +20,8 @@ type Syntax struct {
initialized bool
initFailed bool
explicitRoot bool
keywords []definition
root definition
parser parser
builder builder
}
type GeneratorOptions struct {
@ -57,13 +55,33 @@ var (
ErrInitFailed = errors.New("init failed")
ErrNoParsersDefined = errors.New("no parsers defined")
ErrInvalidEscapeCharacter = errors.New("invalid escape character")
ErrRootAlias = errors.New("root node cannot be an alias")
ErrRootWhitespace = errors.New("root node cannot be a whitespace")
ErrRootFailPass = errors.New("root node cannot pass failing definition")
ErrMultipleRoots = errors.New("multiple roots")
ErrInvalidSymbolName = errors.New("invalid symbol name")
)
func (ct CommitType) String() string {
switch ct {
case None:
return "none"
case Alias:
return "alias"
case Whitespace:
return "whitespace"
case NoWhitespace:
return "no-whitespace"
case Keyword:
return "keyword"
case NoKeyword:
return "no-keyword"
case FailPass:
return "fail-pass"
case Root:
return "root"
default:
return "unknown"
}
}
func duplicateDefinition(name string) error {
return fmt.Errorf("duplicate definition: %s", name)
}
@ -100,6 +118,36 @@ func intsContain(is []int, i int) bool {
return false
}
var incompatibleCommitTypes = map[CommitType][]CommitType{
Alias: {Root},
Whitespace: {Keyword, NoKeyword, FailPass, Root},
Keyword: {NoKeyword, Root},
FailPass: {Root},
}
func (s *Syntax) checkCommitType(d definition) error {
for ct, ict := range incompatibleCommitTypes {
if d.commitType()&ct == 0 {
continue
}
for _, cti := range ict {
if d.commitType()&cti == 0 {
continue
}
return fmt.Errorf(
"incompatible commit types in %s: %v and %v",
d.nodeName(),
ct,
cti,
)
}
}
return nil
}
func (s *Syntax) applyRoot(d definition) error {
explicitRoot := d.commitType()&Root != 0
if explicitRoot && s.explicitRoot {
@ -131,10 +179,18 @@ func (s *Syntax) register(d definition) error {
s.registry = newRegistry()
}
if err := s.checkCommitType(d); err != nil {
return err
}
if err := s.applyRoot(d); err != nil {
return err
}
if d.commitType()&Keyword != 0 {
s.keywords = append(s.keywords, d)
}
return s.registry.setDefinition(d)
}
@ -252,20 +308,12 @@ func (s *Syntax) Init() error {
return ErrNoParsersDefined
}
if s.root.commitType()&Alias != 0 {
return ErrRootAlias
}
if s.root.commitType()&Whitespace != 0 {
return ErrRootWhitespace
}
if s.root.commitType()&FailPass != 0 {
return ErrRootFailPass
if err := s.checkCommitType(s.root); err != nil {
return err
}
defs := s.registry.definitions
for i := range s.registry.definitions {
for i := range defs {
defs[i].preinit()
}
@ -274,19 +322,36 @@ func (s *Syntax) Init() error {
s.registry = newRegistry(defs...)
}
for i := range s.keywords {
if err := s.keywords[i].validate(s.registry); err != nil {
s.initFailed = true
return err
}
}
if err := s.root.validate(s.registry); err != nil {
s.initFailed = true
return err
}
s.root.init(s.registry)
s.parser = s.root.parser()
s.builder = s.root.builder()
for i := range s.keywords {
s.keywords[i].init(s.registry)
}
s.root.init(s.registry)
s.initialized = true
return nil
}
func (s *Syntax) keywordParsers() []parser {
var p []parser
for _, kw := range s.keywords {
p = append(p, kw.parser())
}
return p
}
func (s *Syntax) Generate(o GeneratorOptions, w io.Writer) error {
if err := s.Init(); err != nil {
return err
@ -339,18 +404,33 @@ func (s *Syntax) Generate(o GeneratorOptions, w io.Writer) error {
fprintln()
done := make(map[string]bool)
if err := s.parser.(generator).generate(w, done); err != nil {
for _, p := range s.keywordParsers() {
if err := p.(generator).generate(w, done); err != nil {
return err
}
}
fprintln()
if err := s.root.parser().(generator).generate(w, done); err != nil {
return err
}
done = make(map[string]bool)
if err := s.builder.(generator).generate(w, done); err != nil {
if err := s.root.builder().(generator).generate(w, done); err != nil {
return err
}
fprintln()
fprintln()
fprintf(`return parseInput(r, &p%d, &b%d)`, s.parser.nodeID(), s.builder.nodeID())
fprint(`var keywords = []parser{`)
for i := range s.keywords {
fprintf(`&p%d, `, s.keywords[i].nodeID())
}
fprint(`}`)
fprintln()
fprintln()
fprintf(`return parseInput(r, &p%d, &b%d, keywords)`, s.root.parser().nodeID(), s.root.builder().nodeID())
fprintln()
fprint(`}`)
fprintln()
@ -363,5 +443,5 @@ func (s *Syntax) Parse(r io.Reader) (*Node, error) {
return nil, err
}
return parseInput(r, s.parser, s.builder)
return parseInput(r, s.root.parser(), s.root.builder(), s.keywordParsers())
}

View File

@ -59,9 +59,11 @@ expression:alias = terminal
alias = "alias";
ws = "ws";
nows = "nows";
kw = "kw";
nokw = "nokw";
failpass = "failpass";
root = "root";
flag:alias = alias | ws | nows | failpass | root;
flag:alias = alias | ws | nows | kw | nokw | failpass | root;
definition-name:alias:nows = symbol (":" flag)*;
definition = definition-name "=" expression;

View File

@ -137,24 +137,14 @@ func TestInit(t *testing.T) {
t.Run("root is an alias", func(t *testing.T) {
s := &Syntax{}
if err := s.AnyChar("a", Root|Alias); err != nil {
t.Error(err)
return
}
if err := s.Init(); err == nil {
if err := s.AnyChar("a", Root|Alias); err == nil {
t.Error("failed to fail")
}
})
t.Run("root is whitespace", func(t *testing.T) {
s := &Syntax{}
if err := s.AnyChar("a", Root|Whitespace); err != nil {
t.Error(err)
return
}
if err := s.Init(); err == nil {
if err := s.AnyChar("a", Root|Whitespace); err == nil {
t.Error("failed to fail")
}
})

View File

@ -14,6 +14,8 @@ const (
Alias CommitType = 1 << iota
Whitespace
NoWhitespace
Keyword
NoKeyword
FailPass
Root
@ -80,8 +82,8 @@ func (pe *ParseError) Error() string {
)
}
func parseInput(r io.Reader, p parser, b builder) (*Node, error) {
c := newContext(bufio.NewReader(r))
func parseInput(r io.Reader, p parser, b builder, kw []parser) (*Node, error) {
c := newContext(bufio.NewReader(r), kw)
p.parse(c)
if c.readErr != nil {
return nil, c.readErr