...
1
2
3
4
5 package main
6
7 import (
8 "bytes"
9 "flag"
10 "fmt"
11 "go/scanner"
12 "go/token"
13 "io"
14 "os"
15 "path/filepath"
16
17 "golang.org/x/exp/ebnf"
18 )
19
20 var fset = token.NewFileSet()
21 var start = flag.String("start", "Start", "name of start production")
22
23 func usage() {
24 fmt.Fprintf(os.Stderr, "usage: go tool ebnflint [flags] [filename]\n")
25 flag.PrintDefaults()
26 os.Exit(1)
27 }
28
29
30 var (
31 open = []byte(`<pre class="ebnf">`)
32 close = []byte(`</pre>`)
33 )
34
35 func report(err error) {
36 scanner.PrintError(os.Stderr, err)
37 os.Exit(1)
38 }
39
40 func extractEBNF(src []byte) []byte {
41 var buf bytes.Buffer
42
43 for {
44
45 i := bytes.Index(src, open)
46 if i < 0 {
47 break
48 }
49 i += len(open)
50
51
52
53 for _, ch := range src[0:i] {
54 if ch == '\n' {
55 buf.WriteByte('\n')
56 }
57 }
58
59
60 j := bytes.Index(src[i:], close)
61 if j < 0 {
62 j = len(src) - i
63 }
64 j += i
65
66
67 buf.Write(src[i:j])
68
69
70 src = src[j:]
71 }
72
73 return buf.Bytes()
74 }
75
76 func main() {
77 flag.Parse()
78
79 var (
80 name string
81 r io.Reader
82 )
83 switch flag.NArg() {
84 case 0:
85 name, r = "<stdin>", os.Stdin
86 case 1:
87 name = flag.Arg(0)
88 default:
89 usage()
90 }
91
92 if err := verify(name, *start, r); err != nil {
93 report(err)
94 }
95 }
96
97 func verify(name, start string, r io.Reader) error {
98 if r == nil {
99 f, err := os.Open(name)
100 if err != nil {
101 return err
102 }
103 defer f.Close()
104 r = f
105 }
106
107 src, err := io.ReadAll(r)
108 if err != nil {
109 return err
110 }
111
112 if filepath.Ext(name) == ".html" || bytes.Index(src, open) >= 0 {
113 src = extractEBNF(src)
114 }
115
116 grammar, err := ebnf.Parse(name, bytes.NewBuffer(src))
117 if err != nil {
118 return err
119 }
120
121 return ebnf.Verify(grammar, start)
122 }
123
View as plain text