summaryrefslogtreecommitdiff
path: root/parser
diff options
context:
space:
mode:
Diffstat (limited to 'parser')
-rw-r--r--parser/README.md5
-rw-r--r--parser/compiler.go2
-rw-r--r--parser/interpreter_test.go48
-rw-r--r--parser/parse.go56
4 files changed, 87 insertions, 24 deletions
diff --git a/parser/README.md b/parser/README.md
index 9aec179..e5e2d53 100644
--- a/parser/README.md
+++ b/parser/README.md
@@ -63,9 +63,10 @@ Go language support:
- [x] for statement
- [ ] switch statement
- [x] break statement
-- [ ] continue statement
+- [x] continue statement
- [ ] fallthrough statement
-- [ ] goto statement
+- [x] goto statement
+- [x] label statement
- [ ] select statement
- [x] binary operators
- [ ] unary operators
diff --git a/parser/compiler.go b/parser/compiler.go
index 87eec8d..c538a94 100644
--- a/parser/compiler.go
+++ b/parser/compiler.go
@@ -177,7 +177,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
case lang.JumpFalse:
label = t.Str[10:]
}
- s, _, ok := c.getSym(label, "")
+ s, ok := c.symbols[label]
if !ok {
return fmt.Errorf("label not found: %q", label)
}
diff --git a/parser/interpreter_test.go b/parser/interpreter_test.go
index 7d676d4..efaaedb 100644
--- a/parser/interpreter_test.go
+++ b/parser/interpreter_test.go
@@ -19,24 +19,28 @@ func init() {
GoScanner = scanner.NewScanner(golang.GoSpec)
}
+func gen(test etest) func(*testing.T) {
+ return func(t *testing.T) {
+ interp := parser.NewInterpreter(GoScanner)
+ errStr := ""
+ r, e := interp.Eval(test.src)
+ t.Log(r, e)
+ if e != nil {
+ errStr = e.Error()
+ }
+ if errStr != test.err {
+ t.Errorf("got error %#v, want error %#v", errStr, test.err)
+ }
+ if res := fmt.Sprintf("%v", r); test.err == "" && res != test.res {
+ t.Errorf("got %#v, want %#v", res, test.res)
+ }
+ }
+}
+
func run(t *testing.T, tests []etest) {
for _, test := range tests {
test := test
- t.Run("", func(t *testing.T) {
- interp := parser.NewInterpreter(GoScanner)
- errStr := ""
- r, e := interp.Eval(test.src)
- t.Log(r, e)
- if e != nil {
- errStr = e.Error()
- }
- if errStr != test.err {
- t.Errorf("got error %#v, want error %#v", errStr, test.err)
- }
- if res := fmt.Sprintf("%v", r); test.err == "" && res != test.res {
- t.Errorf("got %#v, want %#v", res, test.res)
- }
- })
+ t.Run("", gen(test))
}
}
@@ -82,5 +86,19 @@ func TestFor(t *testing.T) {
{src: "func f() int {a := 0; for i := 0; i < 3; i = i+1 {a = a+i}; return a}; f()", res: "3"},
{src: "a := 0; for {a = a+1; if a == 3 {break}}; a", res: "3"},
{src: "func f() int {a := 0; for {a = a+1; if a == 3 {break}}; return a}; f()", res: "3"},
+ {src: "func f() int {a := 0; for {a = a+1; if a < 3 {continue}; break}; return a}; f()", res: "3"},
})
}
+
+func TestGoto(t *testing.T) {
+ gen(etest{src: `
+func f(a int) int {
+ a = a+1
+ goto end
+ a = a+1
+end:
+ return a
+}
+f(3)`,
+ res: "4"})(t)
+}
diff --git a/parser/parse.go b/parser/parse.go
index c51a73b..bad9417 100644
--- a/parser/parse.go
+++ b/parser/parse.go
@@ -18,8 +18,10 @@ type Parser struct {
scope string
fname string
- labelCount map[string]int
- breakLabel string
+ funcScope string
+ labelCount map[string]int
+ breakLabel string
+ continueLabel string
}
func (p *Parser) Scan(s string, endSemi bool) (Tokens, error) {
@@ -76,7 +78,6 @@ func (p *Parser) Parse(src string) (out Tokens, err error) {
if endstmt == -1 {
return out, scanner.ErrBlock
}
-
// Skip over simple init statements for some tokens (if, for, ...)
if lang.HasInit[in[0].Id] {
for in[endstmt-1].Id != lang.BraceBlock {
@@ -105,14 +106,23 @@ func (p *Parser) ParseStmt(in Tokens) (out Tokens, err error) {
switch t := in[0]; t.Id {
case lang.Break:
return p.ParseBreak(in)
+ case lang.Continue:
+ return p.ParseContinue(in)
case lang.For:
return p.ParseFor(in)
case lang.Func:
return p.ParseFunc(in)
+ case lang.Goto:
+ return p.ParseGoto(in)
case lang.If:
return p.ParseIf(in)
case lang.Return:
return p.ParseReturn(in)
+ case lang.Ident:
+ if len(in) == 2 && in[1].Id == lang.Colon {
+ return p.ParseLabel(in)
+ }
+ fallthrough
default:
return p.ParseExpr(in)
}
@@ -127,6 +137,7 @@ func (p *Parser) ParseBreak(in Tokens) (out Tokens, err error) {
if in[1].Id != lang.Ident {
return nil, fmt.Errorf("invalid break statement")
}
+ // TODO: check validity of user provided label
label = in[1].Str
default:
return nil, fmt.Errorf("invalid break statement")
@@ -135,6 +146,32 @@ func (p *Parser) ParseBreak(in Tokens) (out Tokens, err error) {
return out, err
}
+func (p *Parser) ParseContinue(in Tokens) (out Tokens, err error) {
+ var label string
+ switch len(in) {
+ case 1:
+ label = p.continueLabel
+ case 2:
+ if in[1].Id != lang.Ident {
+ return nil, fmt.Errorf("invalid continue statement")
+ }
+ // TODO: check validity of user provided label
+ label = in[1].Str
+ default:
+ return nil, fmt.Errorf("invalid continue statement")
+ }
+ out = Tokens{{Id: lang.Goto, Str: "goto " + label}}
+ return out, err
+}
+
+func (p *Parser) ParseGoto(in Tokens) (out Tokens, err error) {
+ if len(in) != 2 || in[1].Id != lang.Ident {
+ return nil, fmt.Errorf("invalid goto statement")
+ }
+ // TODO: check validity of user provided label
+ return Tokens{{Id: lang.Goto, Str: "goto " + p.funcScope + "/" + in[1].Str}}, nil
+}
+
func (p *Parser) ParseFor(in Tokens) (out Tokens, err error) {
// TODO: detect invalid code.
fc := strconv.Itoa(p.labelCount[p.scope])
@@ -150,10 +187,10 @@ func (p *Parser) ParseFor(in Tokens) (out Tokens, err error) {
return nil, fmt.Errorf("invalild for statement")
}
p.pushScope("for" + fc)
- breakLabel := p.breakLabel
- p.breakLabel = p.scope + "e"
+ breakLabel, continueLabel := p.breakLabel, p.continueLabel
+ p.breakLabel, p.continueLabel = p.scope+"e", p.scope+"b"
defer func() {
- p.breakLabel = breakLabel
+ p.breakLabel, p.continueLabel = breakLabel, continueLabel
p.popScope()
}()
if len(init) > 0 {
@@ -195,15 +232,18 @@ func (p *Parser) ParseFunc(in Tokens) (out Tokens, err error) {
ofname := p.fname
p.fname = fname
ofunc := p.function
+ funcScope := p.funcScope
s, _, ok := p.getSym(fname, p.scope)
if !ok {
s = &symbol{}
p.symbols[p.scope+fname] = s
}
p.pushScope(fname)
+ p.funcScope = p.scope
defer func() {
p.fname = ofname // TODO remove if favor of function.
p.function = ofunc
+ p.funcScope = funcScope
p.popScope()
}()
@@ -291,6 +331,10 @@ func (p *Parser) ParseIf(in Tokens) (out Tokens, err error) {
return out, err
}
+func (p *Parser) ParseLabel(in Tokens) (out Tokens, err error) {
+ return Tokens{{Id: lang.Label, Str: p.funcScope + "/" + in[0].Str}}, nil
+}
+
func (p *Parser) ParseReturn(in Tokens) (out Tokens, err error) {
if len(in) > 1 {
if out, err = p.ParseExpr(in[1:]); err != nil {