1132 lines
30 KiB
Go
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
|
|
}
|