File: ebnf.go

package info (click to toggle)
golang-github-alecthomas-participle-v2 2.1.4-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 920 kB
  • sloc: javascript: 1,164; sh: 41; makefile: 7
file content (176 lines) | stat: -rw-r--r-- 3,563 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// Package ebnf contains the AST and parser for parsing the form of EBNF produced by Participle.
//
// The self-referential EBNF is:
//
//	EBNF = Production* .
//	Production = <ident> "=" Expression "." .
//	Expression = Sequence ("|" Sequence)* .
//	SubExpression = "(" ("?!" | "?=")? Expression ")" .
//	Sequence = Term+ .
//	Term = "~"? (<ident> | <string> | ("<" <ident> ">") | SubExpression) ("*" | "+" | "?" | "!")? .
package ebnf

import (
	"fmt"
	"io"

	"github.com/alecthomas/participle/v2"
)

var parser = participle.MustBuild[EBNF]()

// A Node in the EBNF grammar.
type Node interface {
	sealed()
}

var _ Node = &Term{}

// Term in the EBNF grammar.
type Term struct {
	Negation bool `@("~")?`

	Name    string         `(   @Ident`
	Literal string         `  | @String`
	Token   string         `  | "<" @Ident ">"`
	Group   *SubExpression `  | @@ )`

	Repetition string `@("*" | "+" | "?" | "!")?`
}

func (t *Term) sealed() {}

func (t *Term) String() string {
	switch {
	case t.Name != "":
		return t.Name + t.Repetition
	case t.Literal != "":
		return t.Literal + t.Repetition
	case t.Token != "":
		return "<" + t.Token + ">" + t.Repetition
	case t.Group != nil:
		return t.Group.String() + t.Repetition
	default:
		panic("??")
	}
}

// LookaheadAssertion enum.
type LookaheadAssertion rune

func (l *LookaheadAssertion) sealed() {}

func (l *LookaheadAssertion) Capture(tokens []string) error { // nolint
	rn := tokens[0][0]
	switch rn {
	case '!', '=':
		*l = LookaheadAssertion(rn)

	default:
		panic(rn)
	}
	return nil
}

// Lookahead assertion enums.
const (
	LookaheadAssertionNone     LookaheadAssertion = 0
	LookaheadAssertionNegative LookaheadAssertion = '!'
	LookaheadAssertionPositive LookaheadAssertion = '='
)

var _ Node = &SubExpression{}

// SubExpression is an expression inside parentheses ( ... )
type SubExpression struct {
	Lookahead LookaheadAssertion `"(" ("?" @("!" | "="))?`
	Expr      *Expression        `@@ ")"`
}

func (s *SubExpression) sealed() {}

func (s *SubExpression) String() string {
	out := "("
	if s.Lookahead != LookaheadAssertionNone {
		out += "?" + string(s.Lookahead)
	}
	out += s.Expr.String() + ")"
	return out
}

var _ Node = &Sequence{}

// A Sequence of terms.
type Sequence struct {
	Terms []*Term `@@+`
}

func (s *Sequence) sealed() {}

func (s *Sequence) String() (out string) {
	for i, term := range s.Terms {
		if i > 0 {
			out += " "
		}
		out += term.String()
	}
	return
}

var _ Node = &Expression{}

// Expression is a set of alternatives separated by "|" in the EBNF.
type Expression struct {
	Alternatives []*Sequence `@@ ( "|" @@ )*`
}

func (e *Expression) sealed() {}

func (e *Expression) String() (out string) {
	for i, seq := range e.Alternatives {
		if i > 0 {
			out += " | "
		}
		out += seq.String()
	}
	return
}

var _ Node = &Production{}

// Production of the grammar.
type Production struct {
	Production string      `@Ident "="`
	Expression *Expression `@@ "."`
}

func (p *Production) sealed() {}

var _ Node = &EBNF{}

// EBNF itself.
type EBNF struct {
	Productions []*Production `@@*`
}

func (e *EBNF) sealed() {}

func (e *EBNF) String() (out string) {
	for i, production := range e.Productions {
		out += fmt.Sprintf("%s = %s .", production.Production, production.Expression)
		if i < len(e.Productions)-1 {
			out += "\n"
		}
	}
	return
}

// ParseString string into EBNF.
func ParseString(ebnf string) (*EBNF, error) {
	return parser.ParseString("", ebnf)
}

// Parse io.Reader into EBNF.
func Parse(r io.Reader) (*EBNF, error) {
	return parser.Parse("", r)
}