| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package main |
| |
| import ( |
| "bytes" |
| "ebnf" |
| "flag" |
| "fmt" |
| "go/scanner" |
| "go/token" |
| "io/ioutil" |
| "os" |
| "path" |
| ) |
| |
| |
| var fset = token.NewFileSet() |
| var start = flag.String("start", "Start", "name of start production") |
| |
| |
| func usage() { |
| fmt.Fprintf(os.Stderr, "usage: ebnflint [flags] [filename]\n") |
| flag.PrintDefaults() |
| os.Exit(1) |
| } |
| |
| |
| // Markers around EBNF sections in .html files |
| var ( |
| open = []byte(`<pre class="ebnf">`) |
| close = []byte(`</pre>`) |
| ) |
| |
| |
| func extractEBNF(src []byte) []byte { |
| var buf bytes.Buffer |
| |
| for { |
| // i = beginning of EBNF text |
| i := bytes.Index(src, open) |
| if i < 0 { |
| break // no EBNF found - we are done |
| } |
| i += len(open) |
| |
| // write as many newlines as found in the excluded text |
| // to maintain correct line numbers in error messages |
| for _, ch := range src[0:i] { |
| if ch == '\n' { |
| buf.WriteByte('\n') |
| } |
| } |
| |
| // j = end of EBNF text (or end of source) |
| j := bytes.Index(src[i:], close) // close marker |
| if j < 0 { |
| j = len(src) - i |
| } |
| j += i |
| |
| // copy EBNF text |
| buf.Write(src[i:j]) |
| |
| // advance |
| src = src[j:] |
| } |
| |
| return buf.Bytes() |
| } |
| |
| |
| func main() { |
| flag.Parse() |
| |
| var filename string |
| switch flag.NArg() { |
| case 0: |
| filename = "/dev/stdin" |
| case 1: |
| filename = flag.Arg(0) |
| default: |
| usage() |
| } |
| |
| src, err := ioutil.ReadFile(filename) |
| if err != nil { |
| scanner.PrintError(os.Stderr, err) |
| os.Exit(1) |
| } |
| |
| if path.Ext(filename) == ".html" { |
| src = extractEBNF(src) |
| } |
| |
| grammar, err := ebnf.Parse(fset, filename, src) |
| if err != nil { |
| scanner.PrintError(os.Stderr, err) |
| os.Exit(1) |
| } |
| |
| if err = ebnf.Verify(fset, grammar, *start); err != nil { |
| scanner.PrintError(os.Stderr, err) |
| os.Exit(1) |
| } |
| } |