M go.mod => go.mod +4 -1
@@ 2,4 2,7 @@ module git.sr.ht/~mna/snow
go 1.13
-require golang.org/x/exp v0.0.0-20191129062945-2f5052295587
+require (
+ github.com/kylelemons/godebug v1.1.0
+ golang.org/x/exp v0.0.0-20191129062945-2f5052295587
+)
M go.sum => go.sum +2 -0
@@ 1,6 1,8 @@
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
M pkg/compiler/ast.go => pkg/compiler/ast.go +4 -4
@@ 16,7 16,7 @@ func (f *file) Pos() gotoken.Pos {
min = f.stmts[0].Pos()
}
if len(f.comments) > 0 {
- if p := f.comments[0].Pos(); p < min {
+ if p := f.comments[0].Pos(); p < min || !min.IsValid() {
min = p
}
}
@@ 29,7 29,7 @@ func (f *file) End() gotoken.Pos {
max = f.stmts[l-1].End()
}
if l := len(f.comments); l > 0 {
- if p := f.comments[l-1].End(); p > max {
+ if p := f.comments[l-1].End(); p > max || !max.IsValid() {
max = p
}
}
@@ 57,7 57,7 @@ type fnSig struct {
func (f *fnSig) Pos() gotoken.Pos { return f.lparen }
func (f *fnSig) End() gotoken.Pos {
- if f.rarrow == gotoken.NoPos {
+ if !f.rarrow.IsValid() {
return f.rparen + 1
}
return f.retTyp.End()
@@ 72,7 72,7 @@ type paramDef struct {
func (p *paramDef) Pos() gotoken.Pos { return p.name.Pos() }
func (p *paramDef) End() gotoken.Pos {
- if p.comma != gotoken.NoPos {
+ if p.comma.IsValid() {
return p.comma + 1
}
return p.typ.End()
M pkg/compiler/parser.go => pkg/compiler/parser.go +6 -4
@@ 91,10 91,12 @@ func (p *parser) advance(to map[token]bool) {
func (p *parser) parseFile() *file {
// TODO: scopes
- // stop if we had errors scanning the first token, likely not a valid source file.
- if p.errors.Len() != 0 {
- return nil
- }
+ /*
+ // stop if we had errors scanning the first token, likely not a valid source file.
+ if p.errors.Len() != 0 {
+ return nil
+ }
+ */
var stmts []stmt
for p.tok != EOF {
M pkg/compiler/parser_test.go => pkg/compiler/parser_test.go +47 -6
@@ 2,13 2,19 @@ package compiler
import (
"bytes"
- "fmt"
+ "flag"
+ goscanner "go/scanner"
gotoken "go/token"
"io/ioutil"
+ "os"
"path/filepath"
"testing"
+
+ "github.com/kylelemons/godebug/diff"
)
+var testUpdateParserTests = flag.Bool("test.update-parser-tests", false, "If set, replace expected parser test results with actual results.")
+
func TestParser(t *testing.T) {
fis, err := ioutil.ReadDir("testdata")
if err != nil {
@@ 35,17 41,52 @@ func TestParser(t *testing.T) {
p.init(fset, fi.Name(), b)
f := p.parseFile()
- if err := p.errors.Err(); err != nil {
- t.Fatal(err)
- }
- fmt.Printf(">>>>\n%#v\n", f)
+ p.errors.RemoveMultiples()
+ parseErr := p.errors.Err()
+
+ var ebuf bytes.Buffer
+ goscanner.PrintError(&ebuf, parseErr)
var buf bytes.Buffer
pp := printer{w: &buf, withPos: true, withComments: true}
if err := pp.Print(f, p.file); err != nil {
t.Fatal(err)
}
- t.Logf("\n%s\n", buf.String())
+
+ wantFile := filepath.Join("testdata", fi.Name()+".want")
+ errFile := filepath.Join("testdata", fi.Name()+".err")
+ if *testUpdateParserTests {
+ if err := ioutil.WriteFile(wantFile, buf.Bytes(), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := ioutil.WriteFile(errFile, ebuf.Bytes(), 0666); err != nil {
+ t.Fatal(err)
+ }
+ return
+ }
+
+ // validate AST output
+ want, err := ioutil.ReadFile(wantFile)
+ if err != nil && !os.IsNotExist(err) {
+ t.Fatal(err)
+ }
+ t.Logf("got:\n%s\n", buf.String())
+ t.Logf("want:\n%s\n", string(want))
+ if patch := diff.Diff(buf.String(), string(want)); patch != "" {
+ t.Errorf("diff:\n%s\n", patch)
+ }
+
+ // validate errors output
+ ewant, err := ioutil.ReadFile(errFile)
+ if err != nil && !os.IsNotExist(err) {
+ t.Fatal(err)
+ }
+ t.Logf("got error(s):\n%s\n", ebuf.String())
+ t.Logf("want error(s):\n%s\n", string(ewant))
+ if patch := diff.Diff(ebuf.String(), string(ewant)); patch != "" {
+ t.Errorf("diff error(s):\n%s\n", patch)
+ }
})
}
}
M pkg/compiler/printer.go => pkg/compiler/printer.go +10 -3
@@ 89,7 89,14 @@ func (p *printer) Visit(n node) visitor {
case *block:
p.printMsg("block", true)
case *varDef:
- p.printMsg("var", true)
+ lbl := n.kw.String()
+ if n.colon.IsValid() {
+ lbl += ":"
+ }
+ if n.assign.IsValid() {
+ lbl += "="
+ }
+ p.printMsg(lbl, true)
case *returnStmt:
p.printMsg("return", true)
case *binaryExpr:
@@ 99,9 106,9 @@ func (p *printer) Visit(n node) visitor {
case *parenExpr:
p.printMsg("paren", true)
case *basicLit:
- p.printMsg(fmt.Sprintf("%s [%q]", n.literal, n.value), true)
+ p.printMsg(fmt.Sprintf("%s [%s]", n.literal, n.value), true)
case *ident:
- p.printMsg(fmt.Sprintf("ident [%q]", n.name), true)
+ p.printMsg(fmt.Sprintf("ident [%s]", n.name), true)
case *badStmt:
p.printMsg("bad stmt", true)
case *badExpr:
M pkg/compiler/printer_test.go => pkg/compiler/printer_test.go +52 -50
@@ 6,6 6,8 @@ import (
gotoken "go/token"
"strings"
"testing"
+
+ "github.com/kylelemons/godebug/diff"
)
func TestPrinter(t *testing.T) {
@@ 109,94 111,94 @@ func TestPrinter(t *testing.T) {
wantNoPos := `
file
- var
- ident ["a"]
- ident ["int"]
- int ["1"]
+ var:=
+ ident [a]
+ ident [int]
+ int [1]
fn
- ident ["add"]
+ ident [add]
sig
param
- ident ["x"]
- ident ["int"]
+ ident [x]
+ ident [int]
param
- ident ["y"]
- ident ["int"]
- ident ["int"]
+ ident [y]
+ ident [int]
+ ident [int]
block
return
binary [+]
- ident ["x"]
- ident ["y"]
+ ident [x]
+ ident [y]
`
wantComments := `
file
comments
comment [#abc]
comment [#def]
- var
- ident ["a"]
- ident ["int"]
- int ["1"]
+ var:=
+ ident [a]
+ ident [int]
+ int [1]
fn
- ident ["add"]
+ ident [add]
sig
param
- ident ["x"]
- ident ["int"]
+ ident [x]
+ ident [int]
param
- ident ["y"]
- ident ["int"]
- ident ["int"]
+ ident [y]
+ ident [int]
+ ident [int]
block
return
binary [+]
- ident ["x"]
- ident ["y"]
+ ident [x]
+ ident [y]
`
wantShort := `
file [0:69]
- var [8:22]
- ident ["a"] [12:13]
- ident ["int"] [15:18]
- int ["1"] [21:22]
+ var:= [8:22]
+ ident [a] [12:13]
+ ident [int] [15:18]
+ int [1] [21:22]
fn [23:69]
- ident ["add"] [26:29]
+ ident [add] [26:29]
sig [30:51]
param [31:38]
- ident ["x"] [31:32]
- ident ["int"] [34:37]
+ ident [x] [31:32]
+ ident [int] [34:37]
param [39:45]
- ident ["y"] [39:40]
- ident ["int"] [42:45]
- ident ["int"] [48:51]
+ ident [y] [39:40]
+ ident [int] [42:45]
+ ident [int] [48:51]
block [52:69]
return [56:67]
binary [+] [64:67]
- ident ["x"] [64:65]
- ident ["y"] [66:67]
+ ident [x] [64:65]
+ ident [y] [66:67]
`
wantLong := `
file [test:1:1::1:70]
- var [test:1:9::1:23]
- ident ["a"] [test:1:13::1:14]
- ident ["int"] [test:1:16::1:19]
- int ["1"] [test:1:22::1:23]
+ var:= [test:1:9::1:23]
+ ident [a] [test:1:13::1:14]
+ ident [int] [test:1:16::1:19]
+ int [1] [test:1:22::1:23]
fn [test:1:24::1:70]
- ident ["add"] [test:1:27::1:30]
+ ident [add] [test:1:27::1:30]
sig [test:1:31::1:52]
param [test:1:32::1:39]
- ident ["x"] [test:1:32::1:33]
- ident ["int"] [test:1:35::1:38]
+ ident [x] [test:1:32::1:33]
+ ident [int] [test:1:35::1:38]
param [test:1:40::1:46]
- ident ["y"] [test:1:40::1:41]
- ident ["int"] [test:1:43::1:46]
- ident ["int"] [test:1:49::1:52]
+ ident [y] [test:1:40::1:41]
+ ident [int] [test:1:43::1:46]
+ ident [int] [test:1:49::1:52]
block [test:1:53::1:70]
return [test:1:57::1:68]
binary [+] [test:1:65::1:68]
- ident ["x"] [test:1:65::1:66]
- ident ["y"] [test:1:67::1:68]
+ ident [x] [test:1:65::1:66]
+ ident [y] [test:1:67::1:68]
`
cases := []struct {
@@ 225,8 227,8 @@ file [test:1:1::1:70]
want := strings.TrimSpace(c.want)
got := strings.TrimSpace(buf.String())
- if want != got {
- t.Fatalf("want:\n%s\ngot:\n%s\n", want, got)
+ if patch := diff.Diff(got, want); patch != "" {
+ t.Fatalf("diff:\n%s\n", patch)
}
})
}
A => +1 -0
A => +0 -0
A => +3 -0
@@ 0,0 1,3 @@
file [0:7]
comments [0:7]
comment [# hello] [0:7]
A => +10 -0
@@ 0,0 1,10 @@
# group a.1
# group a.2
# group a.3
# group a.4
$
# group b.1
# group b.2
A => +1 -0
@@ 0,0 1,1 @@
comments.snow:7:1: expected statement, found 'illegal'
A => +10 -0
@@ 0,0 1,10 @@
file [0:79]
comments [0:50]
comment [# group a.1] [0:11]
comment [# group a.2] [12:23]
comment [# group a.3] [26:37]
comment [# group a.4] [39:50]
comments [55:78]
comment [# group b.1] [55:66]
comment [# group b.2] [67:78]
bad stmt [52:79]
A pkg/compiler/testdata/empty.snow.err => pkg/compiler/testdata/empty.snow.err +0 -0
A pkg/compiler/testdata/empty.snow.want => pkg/compiler/testdata/empty.snow.want +1 -0
@@ 0,0 1,1 @@
+file [-:-]
A pkg/compiler/testdata/let.snow => pkg/compiler/testdata/let.snow +1 -0
@@ 0,0 1,1 @@
+let x: int
A pkg/compiler/testdata/let.snow.err => pkg/compiler/testdata/let.snow.err +0 -0
A pkg/compiler/testdata/let.snow.want => pkg/compiler/testdata/let.snow.want +4 -0
@@ 0,0 1,4 @@
+file [0:10]
+ let: [0:10]
+ ident [x] [4:5]
+ ident [int] [7:10]
A pkg/compiler/testdata/let_full.snow => pkg/compiler/testdata/let_full.snow +2 -0
@@ 0,0 1,2 @@
+let x: string = "abc"
+
A pkg/compiler/testdata/let_full.snow.err => pkg/compiler/testdata/let_full.snow.err +0 -0
A pkg/compiler/testdata/let_full.snow.want => pkg/compiler/testdata/let_full.snow.want +5 -0
@@ 0,0 1,5 @@
+file [0:21]
+ let:= [0:21]
+ ident [x] [4:5]
+ ident [string] [7:13]
+ string ["abc"] [16:21]
A pkg/compiler/testdata/let_infer.snow => pkg/compiler/testdata/let_infer.snow +1 -0
@@ 0,0 1,1 @@
+let x = "a"
A pkg/compiler/testdata/let_infer.snow.err => pkg/compiler/testdata/let_infer.snow.err +0 -0
A pkg/compiler/testdata/let_infer.snow.want => pkg/compiler/testdata/let_infer.snow.want +4 -0
@@ 0,0 1,4 @@
+file [0:11]
+ let= [0:11]
+ ident [x] [4:5]
+ string ["a"] [8:11]
A pkg/compiler/testdata/var.snow => pkg/compiler/testdata/var.snow +1 -0
@@ 0,0 1,1 @@
+var x: int
A pkg/compiler/testdata/var.snow.err => pkg/compiler/testdata/var.snow.err +0 -0
A pkg/compiler/testdata/var.snow.want => pkg/compiler/testdata/var.snow.want +4 -0
@@ 0,0 1,4 @@
+file [0:10]
+ var: [0:10]
+ ident [x] [4:5]
+ ident [int] [7:10]
A pkg/compiler/testdata/var_full.snow => pkg/compiler/testdata/var_full.snow +2 -0
@@ 0,0 1,2 @@
+var x: int = 123
+
A pkg/compiler/testdata/var_full.snow.err => pkg/compiler/testdata/var_full.snow.err +0 -0
A pkg/compiler/testdata/var_full.snow.want => pkg/compiler/testdata/var_full.snow.want +5 -0
@@ 0,0 1,5 @@
+file [0:16]
+ var:= [0:16]
+ ident [x] [4:5]
+ ident [int] [7:10]
+ int [123] [13:16]
A pkg/compiler/testdata/var_infer.snow => pkg/compiler/testdata/var_infer.snow +1 -0
@@ 0,0 1,1 @@
+var x = 10
A pkg/compiler/testdata/var_infer.snow.err => pkg/compiler/testdata/var_infer.snow.err +0 -0
A pkg/compiler/testdata/var_infer.snow.want => pkg/compiler/testdata/var_infer.snow.want +4 -0
@@ 0,0 1,4 @@
+file [0:10]
+ var= [0:10]
+ ident [x] [4:5]
+ int [10] [8:10]