From 2a01d7287851e988d1e093dbe5788d3631e770b5 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 26 Jan 2010 23:13:22 -0800 Subject: [PATCH] gc: improved syntax errors * example-based syntax errors (go.errors) * enable bison's more specific errors and translate grammar token names into tokens like ++ * test cases R=ken2, r, ken3 CC=golang-dev https://golang.org/cl/194085 --- src/cmd/gc/Makefile | 14 ++++- src/cmd/gc/bisonerrors | 124 +++++++++++++++++++++++++++++++++++++++ src/cmd/gc/go.errors | 46 +++++++++++++++ src/cmd/gc/go.y | 15 ++--- src/cmd/gc/lex.c | 84 ++++++++++++++++++++++++++ src/cmd/gc/subr.c | 38 +++++++++++- test/golden.out | 2 + test/run | 2 +- test/syntax/forvar.go | 10 ++++ test/syntax/import.go | 14 +++++ test/syntax/interface.go | 14 +++++ test/syntax/semi1.go | 14 +++++ test/syntax/semi2.go | 14 +++++ test/syntax/semi3.go | 14 +++++ test/syntax/semi4.go | 14 +++++ test/syntax/semi5.go | 13 ++++ test/syntax/semi6.go | 13 ++++ test/syntax/semi7.go | 14 +++++ test/syntax/slice.go | 9 +++ 19 files changed, 455 insertions(+), 13 deletions(-) create mode 100755 src/cmd/gc/bisonerrors create mode 100644 src/cmd/gc/go.errors create mode 100644 test/syntax/forvar.go create mode 100644 test/syntax/import.go create mode 100644 test/syntax/interface.go create mode 100644 test/syntax/semi1.go create mode 100644 test/syntax/semi2.go create mode 100644 test/syntax/semi3.go create mode 100644 test/syntax/semi4.go create mode 100644 test/syntax/semi5.go create mode 100644 test/syntax/semi6.go create mode 100644 test/syntax/semi7.go create mode 100644 test/syntax/slice.go diff --git a/src/cmd/gc/Makefile b/src/cmd/gc/Makefile index 99dbd51622f..16bfc663985 100644 --- a/src/cmd/gc/Makefile +++ b/src/cmd/gc/Makefile @@ -41,7 +41,7 @@ OFILES=\ typecheck.$O\ unsafe.$O\ walk.$O\ - y.tab.$O\ + y1.tab.$O\ $(LIB): $(OFILES) ar rsc $(LIB) $(OFILES) @@ -49,11 +49,19 @@ $(LIB): $(OFILES) $(OFILES): $(HFILES) y.tab.h: $(YFILES) - bison -y $(YFLAGS) $(YFILES) + bison -v -y $(YFLAGS) $(YFILES) y.tab.c: y.tab.h test -f y.tab.c && touch y.tab.c +y1.tab.c: y.tab.c # make yystate global, yytname mutable + cat y.tab.c | sed '/ int yystate;/d; s/int yychar;/int yychar, yystate;/; s/static const char \*const yytname/const char *yytname/' >y1.tab.c + +yerr.h: bisonerrors go.errors y.tab.h # y.tab.h rule generates y.output too + awk -f bisonerrors y.output go.errors >yerr.h + +subr.$O: yerr.h + builtin.c: builtin.c.boot cp builtin.c.boot builtin.c @@ -63,6 +71,6 @@ opnames.h: mkopnames go.h ./mkopnames go.h >opnames.h clean: - rm -f *.[568o] enam.c [568].out a.out y.tab.h y.tab.c $(LIB) mkbuiltin1 builtin.c _builtin.c opnames.h + rm -f *.[568o] enam.c [568].out a.out y.tab.h y.tab.c y1.tab.c y.output yerr.h $(LIB) mkbuiltin1 builtin.c _builtin.c opnames.h install: $(LIB) diff --git a/src/cmd/gc/bisonerrors b/src/cmd/gc/bisonerrors new file mode 100755 index 00000000000..5110f5350ce --- /dev/null +++ b/src/cmd/gc/bisonerrors @@ -0,0 +1,124 @@ +#!/usr/bin/awk -f +# Copyright 2010 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. + +# This program implements the core idea from +# +# Clinton L. Jeffery, Generating LR syntax error messages from examples, +# ACM TOPLAS 25(5) (September 2003). http://doi.acm.org/10.1145/937563.937566 +# +# It reads Bison's summary of a grammar followed by a file +# like go.errors, replacing lines beginning with % by the +# yystate and yychar that will be active when an error happens +# while parsing that line. +# +# Unlike the system described in the paper, the lines in go.errors +# give grammar symbol name lists, not actual program fragments. +# This is a little less programmer-friendly but doesn't require being +# able to run the text through lex.c. + +BEGIN{ + bison = 1 + grammar = 0 + states = 0 +} + +# In Grammar section of y.output, +# record lhs and length of rhs for each rule. +bison && /^Grammar/ { grammar = 1 } +bison && /^(Terminals|state 0)/ { grammar = 0 } +grammar && NF>0 { + if($2 != "|") { + r = $2 + sub(/:$/, "", r) + } + rulelhs[$1] = r + rulesize[$1] = NF-2 + if(rulesize[$1] == 3 && $3 $4 $5 == "/*empty*/") { + rulesize[$1] = 0 + } +} + +# In state dumps, record shift/reduce actions. +bison && /^state 0/ { grammar = 0; states = 1 } + +states && /^state / { state = $2 } +states { statetext[state] = statetext[state] $0 "\n" } + +states && / shift, and go to state/ { + n = nshift[state]++ + shift[state,n] = $7 + shifttoken[state,n] = $1 + next +} +states && / go to state/ { + n = nshift[state]++ + shift[state,n] = $5 + shifttoken[state,n] = $1 + next +} +states && / reduce using rule/ { + n = nreduce[state]++ + reduce[state,n] = $5 + reducetoken[state,n] = $1 + next +} + +# First // comment marks the beginning of the pattern file. +/^\/\// { bison = 0; grammar = 0; state = 0 } +bison { next } + +# Treat % as first field on line as introducing a pattern (token sequence). +# Run it through the LR machine and print the induced "yystate, yychar," +# at the point where the error happens. +$1 == "%" { + nstack = 0 + state = 0 + f = 2 + tok = "" + for(;;) { + if(tok == "" && f <= NF) { + tok = $f + f++ + } + found = 0 + for(j=0; j " shift[state,j] + stack[nstack++] = state + state = shift[state,j] + found = 1 + tok = "" + break + } + } + if(found) + continue + for(j=0; j=", + "LGT", ">", + "LLE", "<=", + "LLT", "<", + "LLSH", "<<", + "LRSH", ">>", + "LOROR", "||", + "LNE", "!=", +}; + +void +yytinit(void) +{ + int i, j; + extern char *yytname[]; + char *s, *t; + + for(i=0; yytname[i] != nil; i++) { + s = yytname[i]; + + // turn 'x' into x. + if(s[0] == '\'') { + t = strdup(s+1); + t[strlen(t)-1] = '\0'; + yytname[i] = t; + continue; + } + + // apply yytfix to the rest + for(j=0; jpass.out >times.out -for dir in . ken chan interface nilptr fixedbugs bugs +for dir in . ken chan interface nilptr syntax fixedbugs bugs do echo echo '==' $dir'/' diff --git a/test/syntax/forvar.go b/test/syntax/forvar.go new file mode 100644 index 00000000000..f12ce55caeb --- /dev/null +++ b/test/syntax/forvar.go @@ -0,0 +1,10 @@ +// errchk $G -e $D/$F.go + +// Copyright 2010 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. + +package main + +func main() { + for var x = 0; x < 10; x++ { // ERROR "var declaration not allowed in for initializer" diff --git a/test/syntax/import.go b/test/syntax/import.go new file mode 100644 index 00000000000..90e7df007eb --- /dev/null +++ b/test/syntax/import.go @@ -0,0 +1,14 @@ +// errchk $G -e $D/$F.go + +// Copyright 2010 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. + +package main + +import ( + "io", // ERROR "unexpected ," + "os" +) + + diff --git a/test/syntax/interface.go b/test/syntax/interface.go new file mode 100644 index 00000000000..a7f43533a2c --- /dev/null +++ b/test/syntax/interface.go @@ -0,0 +1,14 @@ +// errchk $G -e $D/$F.go + +// Copyright 2010 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. + +package main + +type T interface { + f, g () // ERROR "name list not allowed in interface type" +} + + + diff --git a/test/syntax/semi1.go b/test/syntax/semi1.go new file mode 100644 index 00000000000..c805bb0064b --- /dev/null +++ b/test/syntax/semi1.go @@ -0,0 +1,14 @@ +// errchk $G -e $D/$F.go + +// Copyright 2010 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. + +package main + +func main() { + if x; y // ERROR "unexpected ; or newline before {" + { + z + + diff --git a/test/syntax/semi2.go b/test/syntax/semi2.go new file mode 100644 index 00000000000..237fac8f3be --- /dev/null +++ b/test/syntax/semi2.go @@ -0,0 +1,14 @@ +// errchk $G -e $D/$F.go + +// Copyright 2010 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. + +package main + +func main() { + switch x; y // ERROR "unexpected ; or newline before {" + { + z + + diff --git a/test/syntax/semi3.go b/test/syntax/semi3.go new file mode 100644 index 00000000000..2dbcb598433 --- /dev/null +++ b/test/syntax/semi3.go @@ -0,0 +1,14 @@ +// errchk $G -e $D/$F.go + +// Copyright 2010 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. + +package main + +func main() { + for x; y; z // ERROR "unexpected ; or newline before {" + { + z + + diff --git a/test/syntax/semi4.go b/test/syntax/semi4.go new file mode 100644 index 00000000000..2268cf75afa --- /dev/null +++ b/test/syntax/semi4.go @@ -0,0 +1,14 @@ +// errchk $G -e $D/$F.go + +// Copyright 2010 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. + +package main + +func main() { + for x + { // ERROR "unexpected ; or newline before {" + z + + diff --git a/test/syntax/semi5.go b/test/syntax/semi5.go new file mode 100644 index 00000000000..7f907fb8f8c --- /dev/null +++ b/test/syntax/semi5.go @@ -0,0 +1,13 @@ +// errchk $G -e $D/$F.go + +// Copyright 2010 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. + +package main + +func main() +{ // ERROR "unexpected ; or newline before {" + + + diff --git a/test/syntax/semi6.go b/test/syntax/semi6.go new file mode 100644 index 00000000000..75de3e0a15c --- /dev/null +++ b/test/syntax/semi6.go @@ -0,0 +1,13 @@ +// errchk $G -e $D/$F.go + +// Copyright 2010 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. + +package main + +type T // ERROR "unexpected ; or newline in type declaration" +{ + + + diff --git a/test/syntax/semi7.go b/test/syntax/semi7.go new file mode 100644 index 00000000000..45890435755 --- /dev/null +++ b/test/syntax/semi7.go @@ -0,0 +1,14 @@ +// errchk $G -e $D/$F.go + +// Copyright 2010 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. + +package main + +func main() { + if x { } + else { } // ERROR "unexpected ; or newline before else" +} + + diff --git a/test/syntax/slice.go b/test/syntax/slice.go new file mode 100644 index 00000000000..4bc5d4d8d2a --- /dev/null +++ b/test/syntax/slice.go @@ -0,0 +1,9 @@ +// errchk $G -e $D/$F.go + +// Copyright 2010 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. + +package main + +var x = y[:z] // ERROR "missing lower bound in slice expression"