1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package ebnf
24
25 import (
26 "go/scanner"
27 "go/token"
28 "os"
29 "unicode"
30 "utf8"
31 )
32
33
34
35
36 type (
37
38 Expression interface {
39
40 Pos() token.Pos
41 }
42
43
44 Alternative []Expression
45
46
47 Sequence []Expression
48
49
50 Name struct {
51 StringPos token.Pos
52 String string
53 }
54
55
56 Token struct {
57 StringPos token.Pos
58 String string
59 }
60
61
62 Range struct {
63 Begin, End *Token
64 }
65
66
67 Group struct {
68 Lparen token.Pos
69 Body Expression
70 }
71
72
73 Option struct {
74 Lbrack token.Pos
75 Body Expression
76 }
77
78
79 Repetition struct {
80 Lbrace token.Pos
81 Body Expression
82 }
83
84
85 Bad struct {
86 TokPos token.Pos
87 Error string
88 }
89
90
91 Production struct {
92 Name *Name
93 Expr Expression
94 }
95
96
97
98
99 Grammar map[string]*Production
100 )
101
102 func (x Alternative) Pos() token.Pos { return x[0].Pos() }
103 func (x Sequence) Pos() token.Pos { return x[0].Pos() }
104 func (x *Name) Pos() token.Pos { return x.StringPos }
105 func (x *Token) Pos() token.Pos { return x.StringPos }
106 func (x *Range) Pos() token.Pos { return x.Begin.Pos() }
107 func (x *Group) Pos() token.Pos { return x.Lparen }
108 func (x *Option) Pos() token.Pos { return x.Lbrack }
109 func (x *Repetition) Pos() token.Pos { return x.Lbrace }
110 func (x *Bad) Pos() token.Pos { return x.TokPos }
111 func (x *Production) Pos() token.Pos { return x.Name.Pos() }
112
113
114
115
116 func isLexical(name string) bool {
117 ch, _ := utf8.DecodeRuneInString(name)
118 return !unicode.IsUpper(ch)
119 }
120
121 type verifier struct {
122 fset *token.FileSet
123 scanner.ErrorVector
124 worklist []*Production
125 reached Grammar
126 grammar Grammar
127 }
128
129 func (v *verifier) error(pos token.Pos, msg string) {
130 v.Error(v.fset.Position(pos), msg)
131 }
132
133 func (v *verifier) push(prod *Production) {
134 name := prod.Name.String
135 if _, found := v.reached[name]; !found {
136 v.worklist = append(v.worklist, prod)
137 v.reached[name] = prod
138 }
139 }
140
141 func (v *verifier) verifyChar(x *Token) int {
142 s := x.String
143 if utf8.RuneCountInString(s) != 1 {
144 v.error(x.Pos(), "single char expected, found "+s)
145 return 0
146 }
147 ch, _ := utf8.DecodeRuneInString(s)
148 return ch
149 }
150
151 func (v *verifier) verifyExpr(expr Expression, lexical bool) {
152 switch x := expr.(type) {
153 case nil:
154
155 case Alternative:
156 for _, e := range x {
157 v.verifyExpr(e, lexical)
158 }
159 case Sequence:
160 for _, e := range x {
161 v.verifyExpr(e, lexical)
162 }
163 case *Name:
164
165
166 if prod, found := v.grammar[x.String]; found {
167 v.push(prod)
168 } else {
169 v.error(x.Pos(), "missing production "+x.String)
170 }
171
172
173 if lexical && !isLexical(x.String) {
174 v.error(x.Pos(), "reference to non-lexical production "+x.String)
175 }
176 case *Token:
177
178 case *Range:
179 i := v.verifyChar(x.Begin)
180 j := v.verifyChar(x.End)
181 if i >= j {
182 v.error(x.Pos(), "decreasing character range")
183 }
184 case *Group:
185 v.verifyExpr(x.Body, lexical)
186 case *Option:
187 v.verifyExpr(x.Body, lexical)
188 case *Repetition:
189 v.verifyExpr(x.Body, lexical)
190 default:
191 panic("unreachable")
192 }
193 }
194
195 func (v *verifier) verify(fset *token.FileSet, grammar Grammar, start string) {
196
197 root, found := grammar[start]
198 if !found {
199
200
201 v.error(token.NoPos, "no start production "+start)
202 return
203 }
204
205
206 v.fset = fset
207 v.ErrorVector.Reset()
208 v.worklist = v.worklist[0:0]
209 v.reached = make(Grammar)
210 v.grammar = grammar
211
212
213 v.push(root)
214 for {
215 n := len(v.worklist) - 1
216 if n < 0 {
217 break
218 }
219 prod := v.worklist[n]
220 v.worklist = v.worklist[0:n]
221 v.verifyExpr(prod.Expr, isLexical(prod.Name.String))
222 }
223
224
225 if len(v.reached) < len(v.grammar) {
226 for name, prod := range v.grammar {
227 if _, found := v.reached[name]; !found {
228 v.error(prod.Pos(), name+" is unreachable")
229 }
230 }
231 }
232 }
233
234
235
236
237
238
239
240
241 func Verify(fset *token.FileSet, grammar Grammar, start string) os.Error {
242 var v verifier
243 v.verify(fset, grammar, start)
244 return v.GetError(scanner.Sorted)
245 }