Skip to content

Commit fe52bcc

Browse files
griesemermdempsky
authored andcommitted
cmd/compile/internal/syntax: rudimentary support for error reporting
If an ErrorHandler is provided with a syntax.ReadXXX function, it is invoked for each error encountered. Will need to be refined, but should enable progress with all.bash. Also: - added tests for lexical errors - fixed endless loops when encountering non-terminated strings and comments
1 parent f503295 commit fe52bcc

8 files changed

Lines changed: 147 additions & 78 deletions

File tree

src/cmd/compile/internal/syntax/dumper_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ func TestDump(t *testing.T) {
1414
t.Skip("skipping test in short mode")
1515
}
1616

17-
ast, err := ReadFile(*src, 0)
17+
ast, err := ReadFile(*src, nil, 0)
1818
if err != nil {
1919
t.Fatal(err)
2020
}

src/cmd/compile/internal/syntax/parser.go

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,38 @@ package syntax
66

77
import (
88
"fmt"
9+
"io"
910
"strings"
1011
)
1112

1213
const debug = false
1314
const trace = false
1415

15-
// ----------------------------------------------------------------------------
16-
// "Inherited" globals - TODO(gri): eliminate
17-
18-
var nerrors int
19-
20-
//go:noinline
21-
func Yyerror(format string, args ...interface{}) {
22-
fmt.Printf(format, args...)
23-
fmt.Println()
24-
nerrors++
25-
panic(7)
26-
}
27-
28-
// ----------------------------------------------------------------------------
29-
3016
type parser struct {
3117
scanner
3218

3319
fnest int // function nesting level (for error handling)
3420
xnest int // expression nesting level (for complit ambiguity resolution)
3521
indent []byte // tracing support
22+
23+
nerrors int // error count
24+
}
25+
26+
func (p *parser) init(src io.Reader, errh ErrorHandler) {
27+
p.scanner.init(src, func(pos, line int, msg string) {
28+
p.nerrors++
29+
if errh != nil {
30+
errh(pos, line, msg)
31+
return
32+
}
33+
fmt.Printf("%d: %s\n", line, msg)
34+
})
35+
36+
p.fnest = 0
37+
p.xnest = 0
38+
p.indent = nil
39+
40+
p.nerrors = 0
3641
}
3742

3843
func (p *parser) got(tok token) bool {
@@ -53,12 +58,13 @@ func (p *parser) want(tok token) {
5358
// ----------------------------------------------------------------------------
5459
// Error handling
5560

61+
// syntax_error reports a syntax error at the current line.
5662
func (p *parser) syntax_error(msg string) {
5763
if trace {
5864
defer p.trace("syntax_error (" + msg + ")")()
5965
}
6066

61-
if p.tok == _EOF && nerrors > 0 {
67+
if p.tok == _EOF && p.nerrors > 0 {
6268
return // avoid meaningless follow-up errors
6369
}
6470

@@ -72,7 +78,7 @@ func (p *parser) syntax_error(msg string) {
7278
msg = ", " + msg
7379
default:
7480
// plain error - we don't care about current token
75-
Yyerror("%d: syntax error: %s", p.line, msg)
81+
p.error("syntax error: " + msg)
7682
return
7783
}
7884

@@ -92,11 +98,12 @@ func (p *parser) syntax_error(msg string) {
9298
tok = tokstring(p.tok)
9399
}
94100

95-
Yyerror("%d: syntax error: unexpected %s%s", p.line, tok, msg)
101+
p.error("syntax error: unexpected " + tok + msg)
96102
}
97103

98104
// Like syntax_error, but reports error at given line rather than current lexer line.
99105
func (p *parser) syntax_error_at(lineno uint32, msg string) {
106+
// TODO(gri) fix this
100107
// defer func(lineno int32) {
101108
// lexlineno = lineno
102109
// }(lexlineno)
@@ -193,7 +200,7 @@ func (p *parser) file() *File {
193200
p.want(_Semi)
194201

195202
// don't bother continuing if package clause has errors
196-
if nerrors > 0 {
203+
if p.nerrors > 0 {
197204
return nil
198205
}
199206

@@ -376,12 +383,12 @@ func (p *parser) funcDecl() *FuncDecl {
376383
rcvr := p.paramList()
377384
switch len(rcvr) {
378385
case 0:
379-
Yyerror("method has no receiver")
386+
p.error("method has no receiver")
380387
return nil // TODO(gri) better solution
381388
case 1:
382389
f.Recv = rcvr[0]
383390
default:
384-
Yyerror("method has multiple receivers")
391+
p.error("method has multiple receivers")
385392
return nil // TODO(gri) better solution
386393
}
387394
}
@@ -396,13 +403,13 @@ func (p *parser) funcDecl() *FuncDecl {
396403
// if name.Sym.Name == "init" {
397404
// name = renameinit()
398405
// if params != nil || result != nil {
399-
// Yyerror("func init must have no arguments and no return values")
406+
// p.error("func init must have no arguments and no return values")
400407
// }
401408
// }
402409

403410
// if localpkg.Name == "main" && name.Name == "main" {
404411
// if params != nil || result != nil {
405-
// Yyerror("func main must have no arguments and no return values")
412+
// p.error("func main must have no arguments and no return values")
406413
// }
407414
// }
408415

@@ -412,7 +419,7 @@ func (p *parser) funcDecl() *FuncDecl {
412419

413420
// TODO(gri) deal with function properties
414421
// if noescape && body != nil {
415-
// Yyerror("can only use //go:noescape with external func implementations")
422+
// p.error("can only use //go:noescape with external func implementations")
416423
// }
417424

418425
return f
@@ -543,10 +550,10 @@ func (p *parser) callStmt() *CallStmt {
543550
case *CallExpr:
544551
s.Call = x
545552
case *ParenExpr:
546-
Yyerror("expression in %s must not be parenthesized", s.Tok)
553+
p.error(fmt.Sprintf("expression in %s must not be parenthesized", s.Tok))
547554
// already progressed, no need to advance
548555
default:
549-
Yyerror("expression in %s must be function call", s.Tok)
556+
p.error(fmt.Sprintf("expression in %s must be function call", s.Tok))
550557
// already progressed, no need to advance
551558
}
552559

@@ -728,13 +735,13 @@ loop:
728735
if p.got(_Colon) {
729736
// x[i:j:...]
730737
if t.Index[1] == nil {
731-
Yyerror("middle index required in 3-index slice")
738+
p.error("middle index required in 3-index slice")
732739
}
733740
if p.tok != _Rbrack {
734741
// x[i:j:k...
735742
t.Index[2] = p.expr()
736743
} else {
737-
Yyerror("final index required in 3-index slice")
744+
p.error("final index required in 3-index slice")
738745
}
739746
}
740747
p.want(_Rbrack)
@@ -1141,15 +1148,15 @@ func (p *parser) fieldDecl(styp *StructType) {
11411148
p.want(_Rparen)
11421149
tag := p.oliteral()
11431150
p.addField(styp, nil, typ, tag)
1144-
Yyerror("cannot parenthesize embedded type")
1151+
p.error("cannot parenthesize embedded type")
11451152

11461153
} else {
11471154
// '(' embed ')' oliteral
11481155
typ := p.qualifiedName(nil)
11491156
p.want(_Rparen)
11501157
tag := p.oliteral()
11511158
p.addField(styp, nil, typ, tag)
1152-
Yyerror("cannot parenthesize embedded type")
1159+
p.error("cannot parenthesize embedded type")
11531160
}
11541161

11551162
case _Star:
@@ -1160,7 +1167,7 @@ func (p *parser) fieldDecl(styp *StructType) {
11601167
p.want(_Rparen)
11611168
tag := p.oliteral()
11621169
p.addField(styp, nil, typ, tag)
1163-
Yyerror("cannot parenthesize embedded type")
1170+
p.error("cannot parenthesize embedded type")
11641171

11651172
} else {
11661173
// '*' embed oliteral
@@ -1227,7 +1234,7 @@ func (p *parser) methodDecl() *Field {
12271234
f.init(p)
12281235
f.Type = p.qualifiedName(nil)
12291236
p.want(_Rparen)
1230-
Yyerror("cannot parenthesize embedded type")
1237+
p.error("cannot parenthesize embedded type")
12311238
return f
12321239

12331240
default:
@@ -1294,7 +1301,7 @@ func (p *parser) dotsType() *DotsType {
12941301
p.want(_DotDotDot)
12951302
t.Elem = p.tryType()
12961303
if t.Elem == nil {
1297-
Yyerror("final argument in variadic function missing type")
1304+
p.error("final argument in variadic function missing type")
12981305
}
12991306

13001307
return t
@@ -1441,12 +1448,12 @@ func (p *parser) simpleStmt(lhs Expr, rangeOk bool) SimpleStmt {
14411448
if x, ok := rhs.(*AssertExpr); ok && x.Type == nil {
14421449
// x.(type)
14431450
// if len(rhs) > 1 {
1444-
// Yyerror("expr.(type) must be alone on ths")
1451+
// p.error("expr.(type) must be alone on ths")
14451452
// }
14461453
// if len(lhs) > 1 {
1447-
// Yyerror("argument count mismatch: %d = %d", len(lhs), 1)
1454+
// p.error("argument count mismatch: %d = %d", len(lhs), 1)
14481455
// } else if x, ok := lhs[0].(*Name); !ok {
1449-
// Yyerror("invalid variable name %s in type switch", x)
1456+
// p.error("invalid variable name %s in type switch", x)
14501457
// }
14511458
}
14521459

@@ -1557,7 +1564,7 @@ func (p *parser) header(forStmt bool) (init SimpleStmt, cond Expr, post SimpleSt
15571564
if p.tok != _Semi {
15581565
// accept potential varDecl but complain
15591566
if p.got(_Var) {
1560-
Yyerror("var declaration not allowed in initializer")
1567+
p.error("var declaration not allowed in initializer")
15611568
}
15621569
init = p.simpleStmt(nil, forStmt)
15631570
// If we have a range clause, we are done.
@@ -1597,7 +1604,7 @@ func (p *parser) header(forStmt bool) (init SimpleStmt, cond Expr, post SimpleSt
15971604
cond = p.unpackCond(name, s.Rhs)
15981605
}
15991606
default:
1600-
Yyerror("invalid condition, tag, or type switch guard")
1607+
p.error("invalid condition, tag, or type switch guard")
16011608
}
16021609

16031610
p.xnest = outer
@@ -1614,7 +1621,7 @@ func (p *parser) unpackCond(lhs *Name, x Expr) Expr {
16141621
}
16151622

16161623
if lhs != nil {
1617-
Yyerror("invalid type switch guard")
1624+
p.error("invalid type switch guard")
16181625
}
16191626

16201627
return x
@@ -1631,7 +1638,7 @@ func (p *parser) ifStmt() *IfStmt {
16311638
p.want(_If)
16321639
s.Init, s.Cond, _ = p.header(false)
16331640
if s.Cond == nil {
1634-
Yyerror("missing condition in if statement")
1641+
p.error("missing condition in if statement")
16351642
}
16361643

16371644
s.Then = p.stmtBody("if clause")

src/cmd/compile/internal/syntax/parser_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ var src = flag.String("src", "parser.go", "source file to parse")
2222
var verify = flag.Bool("verify", false, "verify idempotent printing")
2323

2424
func TestParse(t *testing.T) {
25-
_, err := ReadFile(*src, 0)
25+
_, err := ReadFile(*src, nil, 0)
2626
if err != nil {
2727
t.Fatal(err)
2828
}
@@ -48,7 +48,7 @@ func TestStdLib(t *testing.T) {
4848
if debug {
4949
fmt.Printf("parsing %s\n", filename)
5050
}
51-
ast, err := ReadFile(filename, 0)
51+
ast, err := ReadFile(filename, nil, 0)
5252
if err != nil {
5353
t.Fatal(err)
5454
}
@@ -129,7 +129,7 @@ func verifyPrint(filename string, ast1 *File) {
129129
panic(err)
130130
}
131131

132-
ast2, err := ReadBytes(buf1.Bytes(), 0)
132+
ast2, err := ReadBytes(buf1.Bytes(), nil, 0)
133133
if err != nil {
134134
panic(err)
135135
}

src/cmd/compile/internal/syntax/printer_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func TestPrint(t *testing.T) {
1515
t.Skip("skipping test in short mode")
1616
}
1717

18-
ast, err := ReadFile(*src, 0)
18+
ast, err := ReadFile(*src, nil, 0)
1919
if err != nil {
2020
t.Fatal(err)
2121
}

0 commit comments

Comments
 (0)