// Package ast defines the abstract syntax tree for the PEG grammar.
//
// The parser generator's PEG grammar generates a tree using this package
// that is then converted by the builder to the simplified AST used in
// the generated parser.
package ast

import (
	"bytes"
	"fmt"
	"strconv"
	"strings"
)

// Pos represents a position in a source file.
type Pos struct {
	Filename string
	Line     int
	Col      int
	Off      int
}

// String returns the textual representation of a position.
func (p Pos) String() string {
	if p.Filename != "" {
		return fmt.Sprintf("%s:%d:%d (%d)", p.Filename, p.Line, p.Col, p.Off)
	}
	return fmt.Sprintf("%d:%d (%d)", p.Line, p.Col, p.Off)
}

// Grammar is the top-level node of the AST for the PEG grammar.
type Grammar struct {
	p     Pos
	Init  *CodeBlock
	Rules []*Rule
}

var _ Expression = (*Grammar)(nil)

// NewGrammar creates a new grammar at the specified position.
func NewGrammar(p Pos) *Grammar {
	return &Grammar{p: p}
}

// Pos returns the starting position of the node.
func (g *Grammar) Pos() Pos { return g.p }

// String returns the textual representation of a node.
func (g *Grammar) String() string {
	var buf bytes.Buffer

	buf.WriteString(fmt.Sprintf("%s: %T{Init: %v, Rules: [\n",
		g.p, g, g.Init))
	for _, r := range g.Rules {
		buf.WriteString(fmt.Sprintf("%s,\n", r))
	}
	buf.WriteString("]}")
	return buf.String()
}

// NullableVisit recursively determines whether an object is nullable.
func (g *Grammar) NullableVisit(rules map[string]*Rule) bool {
	panic("NullableVisit should not be called on the Grammar")
}

// IsNullable returns the nullable attribute of the node.
func (g *Grammar) IsNullable() bool {
	panic("IsNullable should not be called on the Grammar")
}

// InitialNames returns names of nodes with which an expression can begin.
func (g *Grammar) InitialNames() map[string]struct{} {
	panic("InitialNames should not be called on the Grammar")
}

// Rule represents a rule in the PEG grammar. It has a name, an optional
// display name to be used in error messages, and an expression.
type Rule struct {
	p           Pos
	Name        *Identifier
	DisplayName *StringLit
	Expr        Expression

	// Fields below to work with left recursion.
	Visited       bool
	Nullable      bool
	LeftRecursive bool
	Leader        bool
}

var _ Expression = (*Rule)(nil)

// NewRule creates a rule with at the specified position and with the
// specified name as identifier.
func NewRule(p Pos, name *Identifier) *Rule {
	return &Rule{p: p, Name: name}
}

// Pos returns the starting position of the node.
func (r *Rule) Pos() Pos { return r.p }

// String returns the textual representation of a node.
func (r *Rule) String() string {
	return fmt.Sprintf("%s: %T{Name: %v, DisplayName: %v, Expr: %v}",
		r.p, r, r.Name, r.DisplayName, r.Expr)
}

// NullableVisit recursively determines whether an object is nullable.
func (r *Rule) NullableVisit(rules map[string]*Rule) bool {
	if r.Visited {
		// A left-recursive rule is considered non-nullable.
		return false
	}
	r.Visited = true
	r.Nullable = r.Expr.NullableVisit(rules)
	r.Visited = false
	return r.Nullable
}

// IsNullable returns the nullable attribute of the node.
func (r *Rule) IsNullable() bool {
	return r.Nullable
}

// InitialNames returns names of nodes with which an expression can begin.
func (r *Rule) InitialNames() map[string]struct{} {
	return r.Expr.InitialNames()
}

// Expression is the interface implemented by all expression types.
type Expression interface {
	Pos() Pos

	// for work with left recursion
	NullableVisit(rules map[string]*Rule) bool
	IsNullable() bool
	InitialNames() map[string]struct{}
}

// ChoiceExpr is an ordered sequence of expressions. The parser tries to
// match any of the alternatives in sequence and stops at the first one
// that matches.
type ChoiceExpr struct {
	p            Pos
	Alternatives []Expression

	Nullable bool
}

var _ Expression = (*ChoiceExpr)(nil)

// NewChoiceExpr creates a choice expression at the specified position.
func NewChoiceExpr(p Pos) *ChoiceExpr {
	return &ChoiceExpr{p: p}
}

// Pos returns the starting position of the node.
func (c *ChoiceExpr) Pos() Pos { return c.p }

// String returns the textual representation of a node.
func (c *ChoiceExpr) String() string {
	var buf bytes.Buffer

	buf.WriteString(fmt.Sprintf("%s: %T{Alternatives: [\n", c.p, c))
	for _, e := range c.Alternatives {
		buf.WriteString(fmt.Sprintf("%s,\n", e))
	}
	buf.WriteString("]}")
	return buf.String()
}

// NullableVisit recursively determines whether an object is nullable.
func (c *ChoiceExpr) NullableVisit(rules map[string]*Rule) bool {
	for _, alt := range c.Alternatives {
		if alt.NullableVisit(rules) {
			c.Nullable = true
			return true
		}
	}
	c.Nullable = false
	return false
}

// IsNullable returns the nullable attribute of the node.
func (c *ChoiceExpr) IsNullable() bool {
	return c.Nullable
}

// InitialNames returns names of nodes with which an expression can begin.
func (c *ChoiceExpr) InitialNames() map[string]struct{} {
	names := make(map[string]struct{})
	for _, alt := range c.Alternatives {
		for name := range alt.InitialNames() {
			names[name] = struct{}{}
		}
	}
	return names
}

// FailureLabel is an identifier, which can by thrown and recovered in a grammar.
type FailureLabel string

// RecoveryExpr is an ordered sequence of expressions. The parser tries to
// match any of the alternatives in sequence and stops at the first one
// that matches.
type RecoveryExpr struct {
	p           Pos
	Expr        Expression
	RecoverExpr Expression
	Labels      []FailureLabel

	Nullable bool
}

var _ Expression = (*RecoveryExpr)(nil)

// NewRecoveryExpr creates a choice expression at the specified position.
func NewRecoveryExpr(p Pos) *RecoveryExpr {
	return &RecoveryExpr{p: p}
}

// Pos returns the starting position of the node.
func (r *RecoveryExpr) Pos() Pos { return r.p }

// String returns the textual representation of a node.
func (r *RecoveryExpr) String() string {
	var buf bytes.Buffer

	buf.WriteString(fmt.Sprintf("%s: %T{Expr: %v, RecoverExpr: %v", r.p, r, r.Expr, r.RecoverExpr))
	buf.WriteString(", Labels: [\n")
	for _, e := range r.Labels {
		buf.WriteString(fmt.Sprintf("%s,\n", e))
	}
	buf.WriteString("]}")
	return buf.String()
}

// NullableVisit recursively determines whether an object is nullable.
func (r *RecoveryExpr) NullableVisit(rules map[string]*Rule) bool {
	r.Nullable = r.Expr.NullableVisit(rules) || r.RecoverExpr.NullableVisit(rules)
	return r.Nullable
}

// IsNullable returns the nullable attribute of the node.
func (r *RecoveryExpr) IsNullable() bool {
	return r.Nullable
}

// InitialNames returns names of nodes with which an expression can begin.
func (r *RecoveryExpr) InitialNames() map[string]struct{} {
	names := make(map[string]struct{})
	for name := range r.Expr.InitialNames() {
		names[name] = struct{}{}
	}
	for name := range r.RecoverExpr.InitialNames() {
		names[name] = struct{}{}
	}
	return names
}

// ActionExpr is an expression that has an associated block of code to
// execute when the expression matches.
type ActionExpr struct {
	p      Pos
	Expr   Expression
	Code   *CodeBlock
	FuncIx int

	Nullable bool
}

var _ Expression = (*ActionExpr)(nil)

// NewActionExpr creates a new action expression at the specified position.
func NewActionExpr(p Pos) *ActionExpr {
	return &ActionExpr{p: p}
}

// Pos returns the starting position of the node.
func (a *ActionExpr) Pos() Pos { return a.p }

// String returns the textual representation of a node.
func (a *ActionExpr) String() string {
	return fmt.Sprintf("%s: %T{Expr: %v, Code: %v}", a.p, a, a.Expr, a.Code)
}

// NullableVisit recursively determines whether an object is nullable.
func (a *ActionExpr) NullableVisit(rules map[string]*Rule) bool {
	a.Nullable = a.Expr.NullableVisit(rules)
	return a.Nullable
}

// IsNullable returns the nullable attribute of the node.
func (a *ActionExpr) IsNullable() bool {
	return a.Nullable
}

// InitialNames returns names of nodes with which an expression can begin.
func (a *ActionExpr) InitialNames() map[string]struct{} {
	names := make(map[string]struct{})
	for name := range a.Expr.InitialNames() {
		names[name] = struct{}{}
	}
	return names
}

// ThrowExpr is an expression that throws an FailureLabel to be caught by a
// RecoveryChoiceExpr.
type ThrowExpr struct {
	p     Pos
	Label string
}

var _ Expression = (*ThrowExpr)(nil)

// NewThrowExpr creates a new throw expression at the specified position.
func NewThrowExpr(p Pos) *ThrowExpr {
	return &ThrowExpr{p: p}
}

// Pos returns the starting position of the node.
func (t *ThrowExpr) Pos() Pos { return t.p }

// String returns the textual representation of a node.
func (t *ThrowExpr) String() string {
	return fmt.Sprintf("%s: %T{Label: %v}", t.p, t, t.Label)
}

// NullableVisit recursively determines whether an object is nullable.
func (t *ThrowExpr) NullableVisit(rules map[string]*Rule) bool {
	return true
}

// IsNullable returns the nullable attribute of the node.
func (t *ThrowExpr) IsNullable() bool {
	return true
}

// InitialNames returns names of nodes with which an expression can begin.
func (t *ThrowExpr) InitialNames() map[string]struct{} {
	return make(map[string]struct{})
}

// SeqExpr is an ordered sequence of expressions, all of which must match
// if the SeqExpr is to be a match itself.
type SeqExpr struct {
	p     Pos
	Exprs []Expression

	Nullable bool
}

var _ Expression = (*SeqExpr)(nil)

// NewSeqExpr creates a new sequence expression at the specified position.
func NewSeqExpr(p Pos) *SeqExpr {
	return &SeqExpr{p: p}
}

// Pos returns the starting position of the node.
func (s *SeqExpr) Pos() Pos { return s.p }

// String returns the textual representation of a node.
func (s *SeqExpr) String() string {
	var buf bytes.Buffer

	buf.WriteString(fmt.Sprintf("%s: %T{Exprs: [\n", s.p, s))
	for _, e := range s.Exprs {
		buf.WriteString(fmt.Sprintf("%s,\n", e))
	}
	buf.WriteString("]}")
	return buf.String()
}

// NullableVisit recursively determines whether an object is nullable.
func (s *SeqExpr) NullableVisit(rules map[string]*Rule) bool {
	for _, item := range s.Exprs {
		if !item.NullableVisit(rules) {
			s.Nullable = false
			return false
		}
	}
	s.Nullable = true
	return true
}

// IsNullable returns the nullable attribute of the node.
func (s *SeqExpr) IsNullable() bool {
	return s.Nullable
}

// InitialNames returns names of nodes with which an expression can begin.
func (s *SeqExpr) InitialNames() map[string]struct{} {
	names := make(map[string]struct{})
	for _, item := range s.Exprs {
		for name := range item.InitialNames() {
			names[name] = struct{}{}
		}
		if !item.IsNullable() {
			break
		}
	}
	return names
}

// LabeledExpr is an expression that has an associated label. Code blocks
// can access the value of the expression using that label, that becomes
// a local variable in the code.
type LabeledExpr struct {
	p     Pos
	Label *Identifier
	Expr  Expression
}

var _ Expression = (*LabeledExpr)(nil)

// NewLabeledExpr creates a new labeled expression at the specified position.
func NewLabeledExpr(p Pos) *LabeledExpr {
	return &LabeledExpr{p: p}
}

// Pos returns the starting position of the node.
func (l *LabeledExpr) Pos() Pos { return l.p }

// String returns the textual representation of a node.
func (l *LabeledExpr) String() string {
	return fmt.Sprintf("%s: %T{Label: %v, Expr: %v}", l.p, l, l.Label, l.Expr)
}

// NullableVisit recursively determines whether an object is nullable.
func (l *LabeledExpr) NullableVisit(rules map[string]*Rule) bool {
	return l.Expr.NullableVisit(rules)
}

// IsNullable returns the nullable attribute of the node.
func (l *LabeledExpr) IsNullable() bool {
	return l.Expr.IsNullable()
}

// InitialNames returns names of nodes with which an expression can begin.
func (l *LabeledExpr) InitialNames() map[string]struct{} {
	return l.Expr.InitialNames()
}

// AndExpr is a zero-length matcher that is considered a match if the
// expression it contains is a match.
type AndExpr struct {
	p    Pos
	Expr Expression
}

// NewAndExpr creates a new and (&) expression at the specified position.
func NewAndExpr(p Pos) *AndExpr {
	return &AndExpr{p: p}
}

var _ Expression = (*AndExpr)(nil)

// Pos returns the starting position of the node.
func (a *AndExpr) Pos() Pos { return a.p }

// String returns the textual representation of a node.
func (a *AndExpr) String() string {
	return fmt.Sprintf("%s: %T{Expr: %v}", a.p, a, a.Expr)
}

// NullableVisit recursively determines whether an object is nullable.
func (a *AndExpr) NullableVisit(rules map[string]*Rule) bool {
	return true
}

// IsNullable returns the nullable attribute of the node.
func (a *AndExpr) IsNullable() bool {
	return true
}

// InitialNames returns names of nodes with which an expression can begin.
func (a *AndExpr) InitialNames() map[string]struct{} {
	return make(map[string]struct{})
}

// NotExpr is a zero-length matcher that is considered a match if the
// expression it contains is not a match.
type NotExpr struct {
	p    Pos
	Expr Expression
}

var _ Expression = (*NotExpr)(nil)

// NewNotExpr creates a new not (!) expression at the specified position.
func NewNotExpr(p Pos) *NotExpr {
	return &NotExpr{p: p}
}

// Pos returns the starting position of the node.
func (n *NotExpr) Pos() Pos { return n.p }

// String returns the textual representation of a node.
func (n *NotExpr) String() string {
	return fmt.Sprintf("%s: %T{Expr: %v}", n.p, n, n.Expr)
}

// NullableVisit recursively determines whether an object is nullable.
func (n *NotExpr) NullableVisit(rules map[string]*Rule) bool {
	return true
}

// IsNullable returns the nullable attribute of the node.
func (n *NotExpr) IsNullable() bool {
	return true
}

// InitialNames returns names of nodes with which an expression can begin.
func (n *NotExpr) InitialNames() map[string]struct{} {
	return make(map[string]struct{})
}

// ZeroOrOneExpr is an expression that can be matched zero or one time.
type ZeroOrOneExpr struct {
	p    Pos
	Expr Expression
}

var _ Expression = (*ZeroOrOneExpr)(nil)

// NewZeroOrOneExpr creates a new zero or one expression at the specified
// position.
func NewZeroOrOneExpr(p Pos) *ZeroOrOneExpr {
	return &ZeroOrOneExpr{p: p}
}

// Pos returns the starting position of the node.
func (z *ZeroOrOneExpr) Pos() Pos { return z.p }

// String returns the textual representation of a node.
func (z *ZeroOrOneExpr) String() string {
	return fmt.Sprintf("%s: %T{Expr: %v}", z.p, z, z.Expr)
}

// NullableVisit recursively determines whether an object is nullable.
func (z *ZeroOrOneExpr) NullableVisit(rules map[string]*Rule) bool {
	return true
}

// IsNullable returns the nullable attribute of the node.
func (z *ZeroOrOneExpr) IsNullable() bool {
	return true
}

// InitialNames returns names of nodes with which an expression can begin.
func (z *ZeroOrOneExpr) InitialNames() map[string]struct{} {
	return z.Expr.InitialNames()
}

// ZeroOrMoreExpr is an expression that can be matched zero or more times.
type ZeroOrMoreExpr struct {
	p    Pos
	Expr Expression
}

var _ Expression = (*ZeroOrMoreExpr)(nil)

// NewZeroOrMoreExpr creates a new zero or more expression at the specified
// position.
func NewZeroOrMoreExpr(p Pos) *ZeroOrMoreExpr {
	return &ZeroOrMoreExpr{p: p}
}

// Pos returns the starting position of the node.
func (z *ZeroOrMoreExpr) Pos() Pos { return z.p }

// String returns the textual representation of a node.
func (z *ZeroOrMoreExpr) String() string {
	return fmt.Sprintf("%s: %T{Expr: %v}", z.p, z, z.Expr)
}

// NullableVisit recursively determines whether an object is nullable.
func (z *ZeroOrMoreExpr) NullableVisit(rules map[string]*Rule) bool {
	return true
}

// IsNullable returns the nullable attribute of the node.
func (z *ZeroOrMoreExpr) IsNullable() bool {
	return true
}

// InitialNames returns names of nodes with which an expression can begin.
func (z *ZeroOrMoreExpr) InitialNames() map[string]struct{} {
	return z.Expr.InitialNames()
}

// OneOrMoreExpr is an expression that can be matched one or more times.
type OneOrMoreExpr struct {
	p    Pos
	Expr Expression
}

var _ Expression = (*OneOrMoreExpr)(nil)

// NewOneOrMoreExpr creates a new one or more expression at the specified
// position.
func NewOneOrMoreExpr(p Pos) *OneOrMoreExpr {
	return &OneOrMoreExpr{p: p}
}

// Pos returns the starting position of the node.
func (o *OneOrMoreExpr) Pos() Pos { return o.p }

// String returns the textual representation of a node.
func (o *OneOrMoreExpr) String() string {
	return fmt.Sprintf("%s: %T{Expr: %v}", o.p, o, o.Expr)
}

// NullableVisit recursively determines whether an object is nullable.
func (o *OneOrMoreExpr) NullableVisit(rules map[string]*Rule) bool {
	return false
}

// IsNullable returns the nullable attribute of the node.
func (o *OneOrMoreExpr) IsNullable() bool {
	return false
}

// InitialNames returns names of nodes with which an expression can begin.
func (o *OneOrMoreExpr) InitialNames() map[string]struct{} {
	return o.Expr.InitialNames()
}

// RuleRefExpr is an expression that references a rule by name.
type RuleRefExpr struct {
	p    Pos
	Name *Identifier

	Nullable bool
}

var _ Expression = (*RuleRefExpr)(nil)

// NewRuleRefExpr creates a new rule reference expression at the specified
// position.
func NewRuleRefExpr(p Pos) *RuleRefExpr {
	return &RuleRefExpr{p: p}
}

// Pos returns the starting position of the node.
func (r *RuleRefExpr) Pos() Pos { return r.p }

// String returns the textual representation of a node.
func (r *RuleRefExpr) String() string {
	return fmt.Sprintf("%s: %T{Name: %v}", r.p, r, r.Name)
}

// NullableVisit recursively determines whether an object is nullable.
func (r *RuleRefExpr) NullableVisit(rules map[string]*Rule) bool {
	item, ok := rules[r.Name.Val]
	if !ok {
		// Token or unknown; never empty.
		r.Nullable = false
		return false
	}
	r.Nullable = item.NullableVisit(rules)
	return r.Nullable
}

// IsNullable returns the nullable attribute of the node.
func (r *RuleRefExpr) IsNullable() bool {
	return r.Nullable
}

// InitialNames returns names of nodes with which an expression can begin.
func (r *RuleRefExpr) InitialNames() map[string]struct{} {
	return map[string]struct{}{r.Name.Val: {}}
}

// StateCodeExpr is an expression which can modify the internal state of the parser.
type StateCodeExpr struct {
	p      Pos
	Code   *CodeBlock
	FuncIx int
}

var _ Expression = (*StateCodeExpr)(nil)

// NewStateCodeExpr creates a new state (#) code expression at the specified
// position.
func NewStateCodeExpr(p Pos) *StateCodeExpr {
	return &StateCodeExpr{p: p}
}

// Pos returns the starting position of the node.
func (s *StateCodeExpr) Pos() Pos { return s.p }

// String returns the textual representation of a node.
func (s *StateCodeExpr) String() string {
	return fmt.Sprintf("%s: %T{Code: %v}", s.p, s, s.Code)
}

// NullableVisit recursively determines whether an object is nullable.
func (s *StateCodeExpr) NullableVisit(rules map[string]*Rule) bool {
	return true
}

// IsNullable returns the nullable attribute of the node.
func (s *StateCodeExpr) IsNullable() bool {
	return true
}

// InitialNames returns names of nodes with which an expression can begin.
func (s *StateCodeExpr) InitialNames() map[string]struct{} {
	return make(map[string]struct{})
}

// AndCodeExpr is a zero-length matcher that is considered a match if the
// code block returns true.
type AndCodeExpr struct {
	p      Pos
	Code   *CodeBlock
	FuncIx int
}

var _ Expression = (*AndCodeExpr)(nil)

// NewAndCodeExpr creates a new and (&) code expression at the specified
// position.
func NewAndCodeExpr(p Pos) *AndCodeExpr {
	return &AndCodeExpr{p: p}
}

// Pos returns the starting position of the node.
func (a *AndCodeExpr) Pos() Pos { return a.p }

// String returns the textual representation of a node.
func (a *AndCodeExpr) String() string {
	return fmt.Sprintf("%s: %T{Code: %v}", a.p, a, a.Code)
}

// NullableVisit recursively determines whether an object is nullable.
func (a *AndCodeExpr) NullableVisit(rules map[string]*Rule) bool {
	return true
}

// IsNullable returns the nullable attribute of the node.
func (a *AndCodeExpr) IsNullable() bool {
	return true
}

// InitialNames returns names of nodes with which an expression can begin.
func (a *AndCodeExpr) InitialNames() map[string]struct{} {
	return make(map[string]struct{})
}

// NotCodeExpr is a zero-length matcher that is considered a match if the
// code block returns false.
type NotCodeExpr struct {
	p      Pos
	Code   *CodeBlock
	FuncIx int
}

var _ Expression = (*NotCodeExpr)(nil)

// NewNotCodeExpr creates a new not (!) code expression at the specified
// position.
func NewNotCodeExpr(p Pos) *NotCodeExpr {
	return &NotCodeExpr{p: p}
}

// Pos returns the starting position of the node.
func (n *NotCodeExpr) Pos() Pos { return n.p }

// String returns the textual representation of a node.
func (n *NotCodeExpr) String() string {
	return fmt.Sprintf("%s: %T{Code: %v}", n.p, n, n.Code)
}

// NullableVisit recursively determines whether an object is nullable.
func (n *NotCodeExpr) NullableVisit(rules map[string]*Rule) bool {
	return true
}

// IsNullable returns the nullable attribute of the node.
func (n *NotCodeExpr) IsNullable() bool {
	return true
}

// InitialNames returns names of nodes with which an expression can begin.
func (n *NotCodeExpr) InitialNames() map[string]struct{} {
	return make(map[string]struct{})
}

// LitMatcher is a string literal matcher. The value to match may be a
// double-quoted string, a single-quoted single character, or a back-tick
// quoted raw string.
type LitMatcher struct {
	posValue   // can be str, rstr or char
	IgnoreCase bool
}

var _ Expression = (*LitMatcher)(nil)

// NewLitMatcher creates a new literal matcher at the specified position and
// with the specified value.
func NewLitMatcher(p Pos, v string) *LitMatcher {
	return &LitMatcher{posValue: posValue{p: p, Val: v}}
}

// Pos returns the starting position of the node.
func (l *LitMatcher) Pos() Pos { return l.p }

// String returns the textual representation of a node.
func (l *LitMatcher) String() string {
	return fmt.Sprintf("%s: %T{Val: %q, IgnoreCase: %t}", l.p, l, l.Val, l.IgnoreCase)
}

// NullableVisit recursively determines whether an object is nullable.
func (l *LitMatcher) NullableVisit(rules map[string]*Rule) bool {
	return l.IsNullable()
}

// IsNullable returns the nullable attribute of the node.
func (l *LitMatcher) IsNullable() bool {
	// The string token '' is considered empty.
	return len(l.Val) == 0
}

// InitialNames returns names of nodes with which an expression can begin.
func (l *LitMatcher) InitialNames() map[string]struct{} {
	return make(map[string]struct{})
}

// CharClassMatcher is a character class matcher. The value to match must
// be one of the specified characters, in a range of characters, or in the
// Unicode classes of characters.
type CharClassMatcher struct {
	posValue
	IgnoreCase     bool
	Inverted       bool
	Chars          []rune
	Ranges         []rune // pairs of low/high range
	UnicodeClasses []string
}

var _ Expression = (*CharClassMatcher)(nil)

// NewCharClassMatcher creates a new character class matcher at the specified
// position and with the specified raw value. It parses the raw value into
// the list of characters, ranges and Unicode classes.
func NewCharClassMatcher(p Pos, raw string) *CharClassMatcher {
	c := &CharClassMatcher{posValue: posValue{p: p, Val: raw}}
	c.parse()
	return c
}

func (c *CharClassMatcher) parse() {
	raw := c.Val
	c.IgnoreCase = strings.HasSuffix(raw, "i")
	if c.IgnoreCase {
		raw = raw[:len(raw)-1]
	}

	// "unquote" the character classes
	raw = raw[1 : len(raw)-1]
	if len(raw) == 0 {
		return
	}

	c.Inverted = raw[0] == '^'
	if c.Inverted {
		raw = raw[1:]
		if len(raw) == 0 {
			return
		}
	}

	// content of char class is necessarily valid, so escapes are correct
	r := strings.NewReader(raw)
	var chars []rune
	var buf bytes.Buffer
outer:
	for {
		rn, _, err := r.ReadRune()
		if err != nil {
			break outer
		}

		consumeN := 0
		switch rn {
		case '\\':
			rn, _, _ := r.ReadRune()
			switch rn {
			case ']':
				chars = append(chars, rn)
				continue

			case 'p':
				rn, _, _ := r.ReadRune()
				if rn == '{' {
					buf.Reset()
					for {
						rn, _, _ := r.ReadRune()
						if rn == '}' {
							break
						}
						buf.WriteRune(rn)
					}
					c.UnicodeClasses = append(c.UnicodeClasses, buf.String())
				} else {
					c.UnicodeClasses = append(c.UnicodeClasses, string(rn))
				}
				continue

			case 'x':
				consumeN = 2
			case 'u':
				consumeN = 4
			case 'U':
				consumeN = 8
			case '0', '1', '2', '3', '4', '5', '6', '7':
				consumeN = 2
			}

			buf.Reset()
			buf.WriteRune(rn)
			for i := 0; i < consumeN; i++ {
				rn, _, _ := r.ReadRune()
				buf.WriteRune(rn)
			}
			rn, _, _, _ = strconv.UnquoteChar("\\"+buf.String(), 0)
			chars = append(chars, rn)

		default:
			chars = append(chars, rn)
		}
	}

	// extract ranges and chars
	inRange, wasRange := false, false
	for i, r := range chars {
		if inRange {
			c.Ranges = append(c.Ranges, r)
			inRange = false
			wasRange = true
			continue
		}

		if r == '-' && !wasRange && len(c.Chars) > 0 && i < len(chars)-1 {
			inRange = true
			wasRange = false
			// start of range is the last Char added
			c.Ranges = append(c.Ranges, c.Chars[len(c.Chars)-1])
			c.Chars = c.Chars[:len(c.Chars)-1]
			continue
		}
		wasRange = false
		c.Chars = append(c.Chars, r)
	}
}

// Pos returns the starting position of the node.
func (c *CharClassMatcher) Pos() Pos { return c.p }

// String returns the textual representation of a node.
func (c *CharClassMatcher) String() string {
	return fmt.Sprintf("%s: %T{Val: %q, IgnoreCase: %t, Inverted: %t}",
		c.p, c, c.Val, c.IgnoreCase, c.Inverted)
}

// NullableVisit recursively determines whether an object is nullable.
func (c *CharClassMatcher) NullableVisit(rules map[string]*Rule) bool {
	return c.IsNullable()
}

// IsNullable returns the nullable attribute of the node.
func (c *CharClassMatcher) IsNullable() bool {
	return len(c.Chars) == 0 && len(c.Ranges) == 0 && len(c.UnicodeClasses) == 0
}

// InitialNames returns names of nodes with which an expression can begin.
func (c *CharClassMatcher) InitialNames() map[string]struct{} {
	return make(map[string]struct{})
}

// AnyMatcher is a matcher that matches any character except end-of-file.
type AnyMatcher struct {
	posValue
}

var _ Expression = (*AnyMatcher)(nil)

// NewAnyMatcher creates a new any matcher at the specified position. The
// value is provided for completeness' sake, but it is always the dot.
func NewAnyMatcher(p Pos, v string) *AnyMatcher {
	return &AnyMatcher{posValue{p, v}}
}

// Pos returns the starting position of the node.
func (a *AnyMatcher) Pos() Pos { return a.p }

// String returns the textual representation of a node.
func (a *AnyMatcher) String() string {
	return fmt.Sprintf("%s: %T{Val: %q}", a.p, a, a.Val)
}

// NullableVisit recursively determines whether an object is nullable.
func (a *AnyMatcher) NullableVisit(rules map[string]*Rule) bool {
	return false
}

// IsNullable returns the nullable attribute of the node.
func (a *AnyMatcher) IsNullable() bool {
	return false
}

// InitialNames returns names of nodes with which an expression can begin.
func (a *AnyMatcher) InitialNames() map[string]struct{} {
	return make(map[string]struct{})
}

// CodeBlock represents a code block.
type CodeBlock struct {
	posValue
}

var _ Expression = (*CodeBlock)(nil)

// NewCodeBlock creates a new code block at the specified position and with
// the specified value. The value includes the outer braces.
func NewCodeBlock(p Pos, code string) *CodeBlock {
	return &CodeBlock{posValue{p, code}}
}

// Pos returns the starting position of the node.
func (c *CodeBlock) Pos() Pos { return c.p }

// String returns the textual representation of a node.
func (c *CodeBlock) String() string {
	return fmt.Sprintf("%s: %T{Val: %q}", c.p, c, c.Val)
}

// NullableVisit recursively determines whether an object is nullable.
func (c *CodeBlock) NullableVisit(rules map[string]*Rule) bool {
	panic("NullableVisit should not be called on the CodeBlock")
}

// IsNullable returns the nullable attribute of the node.
func (c *CodeBlock) IsNullable() bool {
	panic("IsNullable should not be called on the CodeBlock")
}

// InitialNames returns names of nodes with which an expression can begin.
func (c *CodeBlock) InitialNames() map[string]struct{} {
	panic("InitialNames should not be called on the CodeBlock")
}

// Identifier represents an identifier.
type Identifier struct {
	posValue
}

var _ Expression = (*Identifier)(nil)

// NewIdentifier creates a new identifier at the specified position and
// with the specified name.
func NewIdentifier(p Pos, name string) *Identifier {
	return &Identifier{posValue{p: p, Val: name}}
}

// Pos returns the starting position of the node.
func (i *Identifier) Pos() Pos { return i.p }

// String returns the textual representation of a node.
func (i *Identifier) String() string {
	return fmt.Sprintf("%s: %T{Val: %q}", i.p, i, i.Val)
}

// NullableVisit recursively determines whether an object is nullable.
func (i *Identifier) NullableVisit(rules map[string]*Rule) bool {
	panic("NullableVisit should not be called on the Identifier")
}

// IsNullable returns the nullable attribute of the node.
func (i *Identifier) IsNullable() bool {
	panic("IsNullable should not be called on the Identifier")
}

// InitialNames returns names of nodes with which an expression can begin.
func (i *Identifier) InitialNames() map[string]struct{} {
	panic("InitialNames should not be called on the Identifier")
}

// StringLit represents a string literal.
type StringLit struct {
	posValue
}

var _ Expression = (*StringLit)(nil)

// NewStringLit creates a new string literal at the specified position and
// with the specified value.
func NewStringLit(p Pos, val string) *StringLit {
	return &StringLit{posValue{p: p, Val: val}}
}

// Pos returns the starting position of the node.
func (s *StringLit) Pos() Pos { return s.p }

// String returns the textual representation of a node.
func (s *StringLit) String() string {
	return fmt.Sprintf("%s: %T{Val: %q}", s.p, s, s.Val)
}

// NullableVisit recursively determines whether an object is nullable.
func (s *StringLit) NullableVisit(rules map[string]*Rule) bool {
	panic("NullableVisit should not be called on the StringLit")
}

// IsNullable returns the nullable attribute of the node.
func (s *StringLit) IsNullable() bool {
	panic("IsNullable should not be called on the StringLit")
}

// InitialNames returns names of nodes with which an expression can begin.
func (s *StringLit) InitialNames() map[string]struct{} {
	panic("InitialNames should not be called on the StringLit")
}

type posValue struct {
	p   Pos
	Val string
}
