From 323d82a7f235f78d56e26677c7ba54470caea08e Mon Sep 17 00:00:00 2001 From: Marc Vertes Date: Thu, 12 Oct 2023 17:08:24 +0200 Subject: parser: implement 'for' statement --- parser/README.md | 6 +++--- parser/compiler.go | 7 +++--- parser/interpreter_test.go | 1 + parser/parse.go | 54 +++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 57 insertions(+), 11 deletions(-) (limited to 'parser') diff --git a/parser/README.md b/parser/README.md index d46608c..5dcee63 100644 --- a/parser/README.md +++ b/parser/README.md @@ -11,13 +11,13 @@ bytecode. The input of parser is a list of tokens produced by the scanner. Multiple tokens are processed at once. The minimal set to get -meaningful results (not an error or nil) is a complete statemement. +meaningful results (not an error or nil) is a complete statement. The output of parser is also a list of tokens, to be consummed by the compiler to produce bytecode. The output tokens set is identical to the bytecode instructions set except that: -- code locations may be provided as as labels instead of numerical +- code locations may be provided as labels instead of numerical values, - memory locations for constants and variables may be provided as symbol names instead of numerical values. @@ -60,7 +60,7 @@ Go language support: - [ ] recover statement - [ ] go statement - [x] if statement (including else and else if) -- [ ] for statement +- [x] for statement - [ ] switch statement - [ ] break statement - [ ] continue statement diff --git a/parser/compiler.go b/parser/compiler.go index 5b0ed81..68f0596 100644 --- a/parser/compiler.go +++ b/parser/compiler.go @@ -147,6 +147,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { ld := len(c.Data) c.Data = append(c.Data, lc) c.addSym(ld, t.Str, lc, symLabel, nil, false) + //c.symbols[t.Str] = &symbol{kind: symLabel, value: lc} } case lang.JumpFalse: @@ -157,7 +158,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { t.Beg = len(c.Code) fixList = append(fixList, t) } else { - i = s.value.(int) + i = s.value.(int) - len(c.Code) } c.Emit(int64(t.Pos), vm.JumpFalse, int64(i)) @@ -168,7 +169,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { t.Beg = len(c.Code) fixList = append(fixList, t) } else { - i = s.value.(int) + i = s.value.(int) - len(c.Code) } c.Emit(int64(t.Pos), vm.Jump, int64(i)) @@ -191,7 +192,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { } s, _, ok := c.getSym(label, "") if !ok { - return fmt.Errorf("label not found: %v", label) + return fmt.Errorf("label not found: %q", label) } c.Code[t.Beg][2] = int64(s.value.(int) - t.Beg) diff --git a/parser/interpreter_test.go b/parser/interpreter_test.go index ee927a5..124ac66 100644 --- a/parser/interpreter_test.go +++ b/parser/interpreter_test.go @@ -63,4 +63,5 @@ var goTests = []struct { 19: {src: "func f(a int) int {return a+2}; f(3) - 2", res: "3"}, 20: {src: "func f(a int, b int, c int) int {return a+b-c} ; f(7, 1, 3)", res: "5"}, 21: {src: "func f(a, b, c int) int {return a+b-c} ; f(7, 1, 3)", res: "5"}, + 22: {src: "a := 0; for i := 0; i < 3; i = i+1 {a = a+i}; a", res: "3"}, } diff --git a/parser/parse.go b/parser/parse.go index 5f0b643..901ac7d 100644 --- a/parser/parse.go +++ b/parser/parse.go @@ -102,6 +102,8 @@ func (p *Parser) ParseStmt(in Tokens) (out Tokens, err error) { return nil, nil } switch t := in[0]; t.Id { + case lang.For: + return p.ParseFor(in) case lang.Func: return p.ParseFunc(in) case lang.If: @@ -113,6 +115,53 @@ func (p *Parser) ParseStmt(in Tokens) (out Tokens, err error) { } } +func (p *Parser) ParseFor(in Tokens) (out Tokens, err error) { + // TODO: detect invalid code. + fc := strconv.Itoa(p.labelCount[p.scope+p.fname]) + prefix := p.fname + "_for" + fc + p.labelCount[p.scope+p.fname]++ + var init, cond, post, body Tokens + pre := in[1 : len(in)-1].Split(lang.Semicolon) + switch len(pre) { + case 1: + cond = pre[0] + case 3: + init, cond, post = pre[0], pre[1], pre[2] + default: + return nil, fmt.Errorf("invalild for statement") + } + p.pushScope("for" + fc) + defer p.popScope() + if len(init) > 0 { + if init, err = p.ParseStmt(init); err != nil { + return nil, err + } + out = init + } + out = append(out, scanner.Token{Id: lang.Label, Str: prefix + "b"}) + if len(cond) > 0 { + if cond, err = p.ParseExpr(cond); err != nil { + return nil, err + } + out = append(out, cond...) + out = append(out, scanner.Token{Id: lang.JumpFalse, Str: "JumpFalse " + prefix + "e"}) + } + if body, err = p.Parse(in[len(in)-1].Block()); err != nil { + return nil, err + } + out = append(out, body...) + if len(post) > 0 { + if post, err = p.ParseStmt(post); err != nil { + return nil, err + } + out = append(out, post...) + } + out = append(out, + scanner.Token{Id: lang.Goto, Str: "goto " + prefix + "b"}, + scanner.Token{Id: lang.Label, Str: prefix + "e"}) + return out, err +} + func (p *Parser) ParseFunc(in Tokens) (out Tokens, err error) { // TODO: handle anonymous functions (no function name) // TODO: handle receiver (methods) @@ -182,13 +231,11 @@ func (p *Parser) ParseIf(in Tokens) (out Tokens, err error) { if err != nil { return nil, err } - //pre := append(Tokens{{Id: lang.Label, Str: prefix + "_b" + ssc}}, blockout...) if sc > 0 { pre = append(pre, scanner.Token{Id: lang.Goto, Str: "goto " + prefix + "_e0"}) } pre = append(pre, scanner.Token{Id: lang.Label, Str: prefix + "_e" + ssc}) out = append(pre, out...) - i-- ifp := in[:i].LastIndex(lang.If) @@ -198,7 +245,6 @@ func (p *Parser) ParseIf(in Tokens) (out Tokens, err error) { ssc = strconv.Itoa(sc) continue } - pre = Tokens{} var init, cond Tokens initcond := in[ifp+1 : i+1] @@ -227,8 +273,6 @@ func (p *Parser) ParseIf(in Tokens) (out Tokens, err error) { sc++ ssc = strconv.Itoa(sc) } - log.Println("prefix:", prefix) - log.Println("if tokens:", out) return out, err } -- cgit v1.2.3