golang.org/x/exp@v0.0.0-20240506185415-9bf2ced13842/ebnflint/ebnflint.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 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 // Markers around EBNF sections in .html files 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 // i = beginning of EBNF text 45 i := bytes.Index(src, open) 46 if i < 0 { 47 break // no EBNF found - we are done 48 } 49 i += len(open) 50 51 // write as many newlines as found in the excluded text 52 // to maintain correct line numbers in error messages 53 for _, ch := range src[0:i] { 54 if ch == '\n' { 55 buf.WriteByte('\n') 56 } 57 } 58 59 // j = end of EBNF text (or end of source) 60 j := bytes.Index(src[i:], close) // close marker 61 if j < 0 { 62 j = len(src) - i 63 } 64 j += i 65 66 // copy EBNF text 67 buf.Write(src[i:j]) 68 69 // advance 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 }