| // 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. | |
| // A library for EBNF grammars. The input is text ([]byte) satisfying | |
| // the following grammar (represented itself in EBNF): | |
| // | |
| // Production = name "=" Expression "." . | |
| // Expression = Alternative { "|" Alternative } . | |
| // Alternative = Term { Term } . | |
| // Term = name | token [ "..." token ] | Group | Option | Repetition . | |
| // Group = "(" Expression ")" . | |
| // Option = "[" Expression "]" . | |
| // Repetition = "{" Expression "}" . | |
| // | |
| // A name is a Go identifier, a token is a Go string, and comments | |
| // and white space follow the same rules as for the Go language. | |
| // Production names starting with an uppercase Unicode letter denote | |
| // non-terminal productions (i.e., productions which allow white-space | |
| // and comments between tokens); all other production names denote | |
| // lexical productions. | |
| // | |
| package ebnf | |
| import ( | |
| "container/vector" | |
| "go/scanner" | |
| "go/token" | |
| "os" | |
| "unicode" | |
| "utf8" | |
| ) | |
| // ---------------------------------------------------------------------------- | |
| // Internal representation | |
| type ( | |
| // An Expression node represents a production expression. | |
| Expression interface { | |
| // Pos is the position of the first character of the syntactic construct | |
| Pos() token.Position | |
| } | |
| // An Alternative node represents a non-empty list of alternative expressions. | |
| Alternative []Expression // x | y | z | |
| // A Sequence node represents a non-empty list of sequential expressions. | |
| Sequence []Expression // x y z | |
| // A Name node represents a production name. | |
| Name struct { | |
| token.Position | |
| String string | |
| } | |
| // A Token node represents a literal. | |
| Token struct { | |
| token.Position | |
| String string | |
| } | |
| // A List node represents a range of characters. | |
| Range struct { | |
| Begin, End *Token // begin ... end | |
| } | |
| // A Group node represents a grouped expression. | |
| Group struct { | |
| token.Position | |
| Body Expression // (body) | |
| } | |
| // An Option node represents an optional expression. | |
| Option struct { | |
| token.Position | |
| Body Expression // [body] | |
| } | |
| // A Repetition node represents a repeated expression. | |
| Repetition struct { | |
| token.Position | |
| Body Expression // {body} | |
| } | |
| // A Production node represents an EBNF production. | |
| Production struct { | |
| Name *Name | |
| Expr Expression | |
| } | |
| // A Grammar is a set of EBNF productions. The map | |
| // is indexed by production name. | |
| // | |
| Grammar map[string]*Production | |
| ) | |
| func (x Alternative) Pos() token.Position { | |
| return x[0].Pos() // the parser always generates non-empty Alternative | |
| } | |
| func (x Sequence) Pos() token.Position { | |
| return x[0].Pos() // the parser always generates non-empty Sequences | |
| } | |
| func (x Range) Pos() token.Position { return x.Begin.Pos() } | |
| func (p *Production) Pos() token.Position { return p.Name.Pos() } | |
| // ---------------------------------------------------------------------------- | |
| // Grammar verification | |
| func isLexical(name string) bool { | |
| ch, _ := utf8.DecodeRuneInString(name) | |
| return !unicode.IsUpper(ch) | |
| } | |
| type verifier struct { | |
| scanner.ErrorVector | |
| worklist vector.Vector | |
| reached Grammar // set of productions reached from (and including) the root production | |
| grammar Grammar | |
| } | |
| func (v *verifier) push(prod *Production) { | |
| name := prod.Name.String | |
| if _, found := v.reached[name]; !found { | |
| v.worklist.Push(prod) | |
| v.reached[name] = prod | |
| } | |
| } | |
| func (v *verifier) verifyChar(x *Token) int { | |
| s := x.String | |
| if utf8.RuneCountInString(s) != 1 { | |
| v.Error(x.Pos(), "single char expected, found "+s) | |
| return 0 | |
| } | |
| ch, _ := utf8.DecodeRuneInString(s) | |
| return ch | |
| } | |
| func (v *verifier) verifyExpr(expr Expression, lexical bool) { | |
| switch x := expr.(type) { | |
| case nil: | |
| // empty expression | |
| case Alternative: | |
| for _, e := range x { | |
| v.verifyExpr(e, lexical) | |
| } | |
| case Sequence: | |
| for _, e := range x { | |
| v.verifyExpr(e, lexical) | |
| } | |
| case *Name: | |
| // a production with this name must exist; | |
| // add it to the worklist if not yet processed | |
| if prod, found := v.grammar[x.String]; found { | |
| v.push(prod) | |
| } else { | |
| v.Error(x.Pos(), "missing production "+x.String) | |
| } | |
| // within a lexical production references | |
| // to non-lexical productions are invalid | |
| if lexical && !isLexical(x.String) { | |
| v.Error(x.Pos(), "reference to non-lexical production "+x.String) | |
| } | |
| case *Token: | |
| // nothing to do for now | |
| case *Range: | |
| i := v.verifyChar(x.Begin) | |
| j := v.verifyChar(x.End) | |
| if i >= j { | |
| v.Error(x.Pos(), "decreasing character range") | |
| } | |
| case *Group: | |
| v.verifyExpr(x.Body, lexical) | |
| case *Option: | |
| v.verifyExpr(x.Body, lexical) | |
| case *Repetition: | |
| v.verifyExpr(x.Body, lexical) | |
| default: | |
| panic("unreachable") | |
| } | |
| } | |
| func (v *verifier) verify(grammar Grammar, start string) { | |
| // find root production | |
| root, found := grammar[start] | |
| if !found { | |
| var noPos token.Position | |
| v.Error(noPos, "no start production "+start) | |
| return | |
| } | |
| // initialize verifier | |
| v.ErrorVector.Reset() | |
| v.worklist.Resize(0, 0) | |
| v.reached = make(Grammar) | |
| v.grammar = grammar | |
| // work through the worklist | |
| v.push(root) | |
| for v.worklist.Len() > 0 { | |
| prod := v.worklist.Pop().(*Production) | |
| v.verifyExpr(prod.Expr, isLexical(prod.Name.String)) | |
| } | |
| // check if all productions were reached | |
| if len(v.reached) < len(v.grammar) { | |
| for name, prod := range v.grammar { | |
| if _, found := v.reached[name]; !found { | |
| v.Error(prod.Pos(), name+" is unreachable") | |
| } | |
| } | |
| } | |
| } | |
| // Verify checks that: | |
| // - all productions used are defined | |
| // - all productions defined are used when beginning at start | |
| // - lexical productions refer only to other lexical productions | |
| // | |
| func Verify(grammar Grammar, start string) os.Error { | |
| var v verifier | |
| v.verify(grammar, start) | |
| return v.GetError(scanner.Sorted) | |
| } |