]> Cypherpunks repositories - gostls13.git/commitdiff
ebnf: follow EBNF for EBNF faithfully
authorRobert Griesemer <gri@golang.org>
Wed, 8 Jun 2011 16:10:30 +0000 (09:10 -0700)
committerRobert Griesemer <gri@golang.org>
Wed, 8 Jun 2011 16:10:30 +0000 (09:10 -0700)
Apply analogous changes in godoc/spec.go.

Fixes #1829.

R=nmessenger, r
CC=golang-dev
https://golang.org/cl/4528127

src/cmd/godoc/spec.go
src/pkg/ebnf/ebnf.go
src/pkg/ebnf/ebnf_test.go
src/pkg/ebnf/parser.go

index d863ca0d844811ec443e993588ca03ce5be9a324..444e36e086731e8dd4cdf31cf65da3e2092fca74 100644 (file)
@@ -129,6 +129,9 @@ func (p *ebnfParser) parseTerm() bool {
 
 
 func (p *ebnfParser) parseSequence() {
+       if !p.parseTerm() {
+               p.errorExpected(p.pos, "term")
+       }
        for p.parseTerm() {
        }
 }
@@ -148,7 +151,9 @@ func (p *ebnfParser) parseExpression() {
 func (p *ebnfParser) parseProduction() {
        p.parseIdentifier(true)
        p.expect(token.ASSIGN)
-       p.parseExpression()
+       if p.tok != token.PERIOD {
+               p.parseExpression()
+       }
        p.expect(token.PERIOD)
 }
 
index 386bfce50421fc701608a97c3af9465b75df3ce8..661afdd35cf8a41b058abe956ff630a503c9c356 100644 (file)
@@ -82,6 +82,12 @@ type (
                Body   Expression // {body}
        }
 
+       // A Bad node stands for pieces of source code that lead to a parse error.
+       Bad struct {
+               TokPos token.Pos
+               Error  string // parser error message
+       }
+
        // A Production node represents an EBNF production.
        Production struct {
                Name *Name
@@ -103,6 +109,7 @@ func (x *Range) Pos() token.Pos      { return x.Begin.Pos() }
 func (x *Group) Pos() token.Pos      { return x.Lparen }
 func (x *Option) Pos() token.Pos     { return x.Lbrack }
 func (x *Repetition) Pos() token.Pos { return x.Lbrace }
+func (x *Bad) Pos() token.Pos        { return x.TokPos }
 func (x *Production) Pos() token.Pos { return x.Name.Pos() }
 
 
index 2055f872ac27d0b9bf289ea4b57d2dc6f4096552..30301748d22729788a352ad710d19a906c79473b 100644 (file)
@@ -14,7 +14,7 @@ import (
 var fset = token.NewFileSet()
 
 
-var grammars = []string{
+var goodGrammars = []string{
        `Program = .`,
 
        `Program = foo .
@@ -38,7 +38,19 @@ var grammars = []string{
 }
 
 
-func check(t *testing.T, filename string, src []byte) {
+var badGrammars = []string{
+       `Program = | .`,
+       `Program = | b .`,
+       `Program = a … b .`,
+       `Program = "a" … .`,
+       `Program = … "b" .`,
+       `Program = () .`,
+       `Program = [] .`,
+       `Program = {} .`,
+}
+
+
+func checkGood(t *testing.T, filename string, src []byte) {
        grammar, err := Parse(fset, filename, src)
        if err != nil {
                t.Errorf("Parse(%s) failed: %v", src, err)
@@ -49,9 +61,20 @@ func check(t *testing.T, filename string, src []byte) {
 }
 
 
+func checkBad(t *testing.T, filename string, src []byte) {
+       _, err := Parse(fset, filename, src)
+       if err == nil {
+               t.Errorf("Parse(%s) should have failed", src)
+       }
+}
+
+
 func TestGrammars(t *testing.T) {
-       for _, src := range grammars {
-               check(t, "", []byte(src))
+       for _, src := range goodGrammars {
+               checkGood(t, "", []byte(src))
+       }
+       for _, src := range badGrammars {
+               checkBad(t, "", []byte(src))
        }
 }
 
@@ -67,6 +90,6 @@ func TestFiles(t *testing.T) {
                if err != nil {
                        t.Fatal(err)
                }
-               check(t, filename, src)
+               checkGood(t, filename, src)
        }
 }
index 166412f990eea39bd3da07522fce340b02172155..ede4f7073a825cbf1ebd61d877519e95591209ae 100644 (file)
@@ -85,6 +85,7 @@ func (p *parser) parseToken() *Token {
 }
 
 
+// ParseTerm returns nil if no term was found.
 func (p *parser) parseTerm() (x Expression) {
        pos := p.pos
 
@@ -131,7 +132,8 @@ func (p *parser) parseSequence() Expression {
        // no need for a sequence if list.Len() < 2
        switch len(list) {
        case 0:
-               return nil
+               p.errorExpected(p.pos, "term")
+               return &Bad{p.pos, "term expected"}
        case 1:
                return list[0]
        }
@@ -144,20 +146,16 @@ func (p *parser) parseExpression() Expression {
        var list Alternative
 
        for {
-               if x := p.parseSequence(); x != nil {
-                       list = append(list, x)
-               }
+               list = append(list, p.parseSequence())
                if p.tok != token.OR {
                        break
                }
                p.next()
        }
+       // len(list) > 0
 
        // no need for an Alternative node if list.Len() < 2
-       switch len(list) {
-       case 0:
-               return nil
-       case 1:
+       if len(list) == 1 {
                return list[0]
        }
 
@@ -168,7 +166,10 @@ func (p *parser) parseExpression() Expression {
 func (p *parser) parseProduction() *Production {
        name := p.parseIdentifier()
        p.expect(token.ASSIGN)
-       expr := p.parseExpression()
+       var expr Expression
+       if p.tok != token.PERIOD {
+               expr = p.parseExpression()
+       }
        p.expect(token.PERIOD)
        return &Production{name, expr}
 }