summaryrefslogtreecommitdiff
path: root/src/pkg/ebnf
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/ebnf')
-rw-r--r--src/pkg/ebnf/ebnf.go11
-rw-r--r--src/pkg/ebnf/ebnf_test.go35
-rw-r--r--src/pkg/ebnf/parser.go24
3 files changed, 51 insertions, 19 deletions
diff --git a/src/pkg/ebnf/ebnf.go b/src/pkg/ebnf/ebnf.go
index 7918c4593..661afdd35 100644
--- a/src/pkg/ebnf/ebnf.go
+++ b/src/pkg/ebnf/ebnf.go
@@ -5,10 +5,10 @@
// Package ebnf is a library for EBNF grammars. The input is text ([]byte)
// satisfying the following grammar (represented itself in EBNF):
//
-// Production = name "=" Expression "." .
+// Production = name "=" [ Expression ] "." .
// Expression = Alternative { "|" Alternative } .
// Alternative = Term { Term } .
-// Term = name | token [ "..." token ] | Group | Option | Repetition .
+// Term = name | token [ "…" token ] | Group | Option | Repetition .
// Group = "(" Expression ")" .
// Option = "[" Expression "]" .
// Repetition = "{" Expression "}" .
@@ -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() }
diff --git a/src/pkg/ebnf/ebnf_test.go b/src/pkg/ebnf/ebnf_test.go
index e77cf64ad..30301748d 100644
--- a/src/pkg/ebnf/ebnf_test.go
+++ b/src/pkg/ebnf/ebnf_test.go
@@ -14,7 +14,7 @@ import (
var fset = token.NewFileSet()
-var grammars = []string{
+var goodGrammars = []string{
`Program = .`,
`Program = foo .
@@ -22,7 +22,7 @@ var grammars = []string{
`Program = "a" | "b" "c" .`,
- `Program = "a" ... "z" .`,
+ `Program = "a" … "z" .`,
`Program = Song .
Song = { Note } .
@@ -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)
}
}
diff --git a/src/pkg/ebnf/parser.go b/src/pkg/ebnf/parser.go
index 818168e11..ede4f7073 100644
--- a/src/pkg/ebnf/parser.go
+++ b/src/pkg/ebnf/parser.go
@@ -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
@@ -95,7 +96,8 @@ func (p *parser) parseTerm() (x Expression) {
case token.STRING:
tok := p.parseToken()
x = tok
- if p.tok == token.ELLIPSIS {
+ const ellipsis = "…" // U+2026, the horizontal ellipsis character
+ if p.tok == token.ILLEGAL && p.lit == ellipsis {
p.next()
x = &Range{tok, p.parseToken()}
}
@@ -130,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]
}
@@ -143,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]
}
@@ -167,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}
}
@@ -177,7 +179,7 @@ func (p *parser) parse(fset *token.FileSet, filename string, src []byte) Grammar
// initialize parser
p.fset = fset
p.ErrorVector.Reset()
- p.scanner.Init(fset.AddFile(filename, fset.Base(), len(src)), src, p, 0)
+ p.scanner.Init(fset.AddFile(filename, fset.Base(), len(src)), src, p, scanner.AllowIllegalChars)
p.next() // initializes pos, tok, lit
grammar := make(Grammar)