Files
Курнат Андрей 2315f25754 Initial QSfera import
2026-06-07 10:20:04 +03:00

1132 lines
30 KiB
Go

// 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
}