diff options
| author | Marc Vertes <mvertes@free.fr> | 2023-10-14 13:07:24 +0200 |
|---|---|---|
| committer | Marc Vertes <mvertes@free.fr> | 2023-10-14 13:07:24 +0200 |
| commit | e7814f9d8c1d062852f5c9a652a1c4eb3335cf04 (patch) | |
| tree | 2af0a5d3fa6cddd95c2b4644d1220895e34dbb96 /parser | |
| parent | a032cfd881facc8990445fc856de90638c3476bf (diff) | |
parser: implement label, goto and continue statements
Diffstat (limited to 'parser')
| -rw-r--r-- | parser/README.md | 5 | ||||
| -rw-r--r-- | parser/compiler.go | 2 | ||||
| -rw-r--r-- | parser/interpreter_test.go | 48 | ||||
| -rw-r--r-- | parser/parse.go | 56 |
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 { |
