]> Cypherpunks repositories - gostls13.git/commitdiff
ebnflint command
authorRobert Griesemer <gri@golang.org>
Mon, 13 Jul 2009 17:26:58 +0000 (10:26 -0700)
committerRobert Griesemer <gri@golang.org>
Mon, 13 Jul 2009 17:26:58 +0000 (10:26 -0700)
- basic verification of EBNF grammars
- tested with (and has testcase for) go_spec.html

R=rsc
DELTA=150  (148 added, 0 deleted, 2 changed)
OCL=31481
CL=31517

src/cmd/clean.bash
src/cmd/ebnflint/Makefile [new file with mode: 0644]
src/cmd/ebnflint/ebnflint.go [new file with mode: 0644]
src/make.bash
src/run.bash

index 7431e6f1f31c0cb90531bafaa74781126e6fecc9..bba191c994a48a5f868d10b785394efed11414b6 100644 (file)
@@ -3,7 +3,7 @@
 # Use of this source code is governed by a BSD-style
 # license that can be found in the LICENSE file.
 
-for i in cc 6l 6a 6c 8l 8a 8c 8g 5l 5a 5c 5g gc 6g ar db nm acid cov gobuild godefs godoc gofmt prof gotest
+for i in cc 6l 6a 6c 8l 8a 8c 8g 5l 5a 5c 5g gc 6g ar db nm acid cov ebnflint gobuild godefs godoc gofmt prof gotest
 do
        cd $i
        make clean
diff --git a/src/cmd/ebnflint/Makefile b/src/cmd/ebnflint/Makefile
new file mode 100644 (file)
index 0000000..e223002
--- /dev/null
@@ -0,0 +1,24 @@
+# Copyright 2009 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.
+
+include $(GOROOT)/src/Make.$(GOARCH)
+
+TARG=ebnflint
+OFILES=\
+       ebnflint.$O\
+
+$(TARG): $(OFILES)
+       $(LD) -o $(TARG) $(OFILES)
+
+test: $(TARG)
+       $(TARG) -start="SourceFile" $(GOROOT)/doc/go_spec.html
+
+clean:
+       rm -f $(OFILES) $(TARG)
+
+install: $(TARG)
+       cp $(TARG) $(HOME)/bin/$(TARG)
+
+%.$O:  %.go
+       $(GC) $<
diff --git a/src/cmd/ebnflint/ebnflint.go b/src/cmd/ebnflint/ebnflint.go
new file mode 100644 (file)
index 0000000..7757085
--- /dev/null
@@ -0,0 +1,120 @@
+// Copyright 2009 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 (
+       "bytes";
+       "ebnf";
+       "flag";
+       "fmt";
+       "io";
+       "os";
+       "path";
+       "sort";
+       "strings";
+)
+
+
+var start = flag.String("start", "Start", "name of start production");
+
+
+func usage() {
+       fmt.Fprintf(os.Stderr, "usage: ebnflint [flags] [filename]\n");
+       flag.PrintDefaults();
+       os.Exit(1);
+}
+
+
+// Markers around EBNF sections in .html files
+var (
+       open = strings.Bytes(`<pre class="ebnf">`);
+       close = strings.Bytes(`</pre>`);
+)
+
+
+func extractEBNF(src []byte) []byte {
+       var buf bytes.Buffer;
+
+       for i, j, n := 0, 0, len(src); ; {
+               // i = beginning of EBNF section
+               i = bytes.Index(src[j : n], open);
+               if i < 0 {
+                       break;
+               }
+               i += j+len(open);
+
+               // write as many newlines as found in the excluded text
+               // to maintain correct line numbers in error messages 
+               for _, ch := range src[j : i] {
+                       if ch == '\n' {
+                               buf.WriteByte('\n');
+                       }
+               }
+
+               // j = end of EBNF section
+               j = bytes.Index(src[i : n], close);
+               if j < 0 {
+                       // missing closing
+                       // TODO(gri) should this be an error?
+                       j = n-i;
+               }
+               j += i;
+
+               // copy EBNF section
+               buf.Write(src[i : j]);
+       }
+
+       return buf.Data();
+}
+
+
+// TODO(gri) This is the same code for reportError as in gofmt.
+//           Should factor this out as part of some parsing framework
+//           that could also deal with reading various input sources.
+
+func reportError(filename string, err os.Error) {
+       if errors, ok := err.(ebnf.ErrorList); ok {
+               sort.Sort(errors);
+               for _, e := range errors {
+                       fmt.Fprintf(os.Stderr, "%s:%v\n", filename, e);
+               }
+       } else {
+               fmt.Fprintf(os.Stderr, "%s: %v\n", filename, err);
+       }
+       os.Exit(1);
+}
+
+
+func main() {
+       flag.Parse();
+
+       var filename string;
+       switch flag.NArg() {
+       case 0:
+               filename = "/dev/stdin";
+       case 1:
+               filename = flag.Arg(0);
+       default:
+               usage();
+       }
+
+       src, err := io.ReadFile(filename);
+       if err != nil {
+               reportError(filename, err);
+       }
+
+       if path.Ext(filename) == ".html" {
+               src = extractEBNF(src);
+       }
+
+       grammar, err := ebnf.Parse(src);
+       if err != nil {
+               reportError(filename, err);
+       }
+
+       if err = ebnf.Verify(grammar, *start); err != nil {
+               reportError(filename, err);
+       }
+}
index 00cc6b4fd3e63a3016f4b77fbc51de5ea212ec2b..6374f0b9e584852f4c6482bfef90249e01d3d537 100755 (executable)
@@ -18,7 +18,7 @@ rm -f $HOME/bin/quietgcc
 cp quietgcc.bash $HOME/bin/quietgcc
 chmod +x $HOME/bin/quietgcc
 
-for i in lib9 libbio libmach_amd64 libregexp cmd pkg cmd/gobuild cmd/godoc cmd/gofmt
+for i in lib9 libbio libmach_amd64 libregexp cmd pkg cmd/ebnflint cmd/gobuild cmd/godoc cmd/gofmt
 do
        # The ( ) here are to preserve the current directory
        # for the next round despite the cd $i below.
index f275d990ce2365755c9316c0969d42dedead1e17..23c10facf983a6d84a8ea41e8f99044c06dcc003 100755 (executable)
@@ -42,6 +42,12 @@ time make
 time make smoketest
 ) || exit $?
 
+(xcd cmd/ebnflint
+make clean
+time make
+time make test
+) || exit $?
+
 (xcd ../doc/progs
 time ./run
 ) || exit $?