1
0
mirror of https://github.com/golang/go synced 2024-10-02 12:08:32 -06:00

ebnf: follow EBNF for EBNF faithfully

Apply analogous changes in godoc/spec.go.

Fixes #1829.

R=nmessenger, r
CC=golang-dev
https://golang.org/cl/4528127
This commit is contained in:
Robert Griesemer 2011-06-08 09:10:30 -07:00
parent 21032ebebd
commit a609a6bb63
4 changed files with 51 additions and 15 deletions

View File

@ -129,6 +129,9 @@ func (p *ebnfParser) parseTerm() bool {
func (p *ebnfParser) parseSequence() { func (p *ebnfParser) parseSequence() {
if !p.parseTerm() {
p.errorExpected(p.pos, "term")
}
for p.parseTerm() { for p.parseTerm() {
} }
} }
@ -148,7 +151,9 @@ func (p *ebnfParser) parseExpression() {
func (p *ebnfParser) parseProduction() { func (p *ebnfParser) parseProduction() {
p.parseIdentifier(true) p.parseIdentifier(true)
p.expect(token.ASSIGN) p.expect(token.ASSIGN)
if p.tok != token.PERIOD {
p.parseExpression() p.parseExpression()
}
p.expect(token.PERIOD) p.expect(token.PERIOD)
} }

View File

@ -82,6 +82,12 @@ type (
Body Expression // {body} 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. // A Production node represents an EBNF production.
Production struct { Production struct {
Name *Name 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 *Group) Pos() token.Pos { return x.Lparen }
func (x *Option) Pos() token.Pos { return x.Lbrack } func (x *Option) Pos() token.Pos { return x.Lbrack }
func (x *Repetition) Pos() token.Pos { return x.Lbrace } 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() } func (x *Production) Pos() token.Pos { return x.Name.Pos() }

View File

@ -14,7 +14,7 @@ import (
var fset = token.NewFileSet() var fset = token.NewFileSet()
var grammars = []string{ var goodGrammars = []string{
`Program = .`, `Program = .`,
`Program = foo . `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) grammar, err := Parse(fset, filename, src)
if err != nil { if err != nil {
t.Errorf("Parse(%s) failed: %v", src, err) 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) { func TestGrammars(t *testing.T) {
for _, src := range grammars { for _, src := range goodGrammars {
check(t, "", []byte(src)) checkGood(t, "", []byte(src))
}
for _, src := range badGrammars {
checkBad(t, "", []byte(src))
} }
} }
@ -67,6 +90,6 @@ func TestFiles(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
check(t, filename, src) checkGood(t, filename, src)
} }
} }

View File

@ -85,6 +85,7 @@ func (p *parser) parseToken() *Token {
} }
// ParseTerm returns nil if no term was found.
func (p *parser) parseTerm() (x Expression) { func (p *parser) parseTerm() (x Expression) {
pos := p.pos pos := p.pos
@ -131,7 +132,8 @@ func (p *parser) parseSequence() Expression {
// no need for a sequence if list.Len() < 2 // no need for a sequence if list.Len() < 2
switch len(list) { switch len(list) {
case 0: case 0:
return nil p.errorExpected(p.pos, "term")
return &Bad{p.pos, "term expected"}
case 1: case 1:
return list[0] return list[0]
} }
@ -144,20 +146,16 @@ func (p *parser) parseExpression() Expression {
var list Alternative var list Alternative
for { for {
if x := p.parseSequence(); x != nil { list = append(list, p.parseSequence())
list = append(list, x)
}
if p.tok != token.OR { if p.tok != token.OR {
break break
} }
p.next() p.next()
} }
// len(list) > 0
// no need for an Alternative node if list.Len() < 2 // no need for an Alternative node if list.Len() < 2
switch len(list) { if len(list) == 1 {
case 0:
return nil
case 1:
return list[0] return list[0]
} }
@ -168,7 +166,10 @@ func (p *parser) parseExpression() Expression {
func (p *parser) parseProduction() *Production { func (p *parser) parseProduction() *Production {
name := p.parseIdentifier() name := p.parseIdentifier()
p.expect(token.ASSIGN) p.expect(token.ASSIGN)
expr := p.parseExpression() var expr Expression
if p.tok != token.PERIOD {
expr = p.parseExpression()
}
p.expect(token.PERIOD) p.expect(token.PERIOD)
return &Production{name, expr} return &Production{name, expr}
} }