diff options
Diffstat (limited to 'parser')
| -rw-r--r-- | parser/README.md | 6 | ||||
| -rw-r--r-- | parser/compiler.go | 23 | ||||
| -rw-r--r-- | parser/interpreter_test.go | 33 | ||||
| -rw-r--r-- | parser/parse.go | 112 |
4 files changed, 161 insertions, 13 deletions
diff --git a/parser/README.md b/parser/README.md index e5e2d53..23c23b8 100644 --- a/parser/README.md +++ b/parser/README.md @@ -72,19 +72,19 @@ Go language support: - [ ] unary operators - [ ] logical operators && and || - [ ] assign operators -- [ ] operator precedence +- [x] operator precedence rules - [x] parenthesis expressions - [x] call expressions - [ ] index expressions - [ ] selector expressions -- [ ] type conversions +- [ ] type convertions - [ ] type assertions - [ ] parametric types (generic) - [ ] type parametric functions (generic) - [ ] type constraints (generic) - [ ] type checking - [ ] comment pragmas -- [ ] packages import +- [ ] package import - [ ] modules Other items: diff --git a/parser/compiler.go b/parser/compiler.go index c538a94..4791069 100644 --- a/parser/compiler.go +++ b/parser/compiler.go @@ -68,9 +68,15 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { case lang.Add: c.Emit(int64(t.Pos), vm.Add) + case lang.Mul: + c.Emit(int64(t.Pos), vm.Mul) + case lang.Sub: c.Emit(int64(t.Pos), vm.Sub) + case lang.Greater: + c.Emit(int64(t.Pos), vm.Greater) + case lang.Less: c.Emit(int64(t.Pos), vm.Lower) @@ -188,12 +194,18 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { } func (c *Compiler) PrintCode() { - labels := map[int]string{} + labels := map[int]string{} // labels indexed by code location + data := map[int]string{} // data indexed by frame location + for name, sym := range c.symbols { if sym.kind == symLabel || sym.kind == symFunc { labels[sym.value.(int)] = name } + if sym.used { + data[sym.index] = name + } } + fmt.Println("# Code:") for i, l := range c.Code { if label, ok := labels[i]; ok { @@ -205,9 +217,18 @@ func (c *Compiler) PrintCode() { if d, ok := labels[i+(int)(l[2])]; ok { extra = "// " + d } + case vm.Dup, vm.Assign, vm.Fdup, vm.Fassign: + if d, ok := data[int(l[2])]; ok { + extra = "// " + d + } } fmt.Printf("%4d %-14v %v\n", i, vm.CodeString(l), extra) } + + if label, ok := labels[len(c.Code)]; ok { + fmt.Println(label + ":") + } + fmt.Println("# End code") } type entry struct { diff --git a/parser/interpreter_test.go b/parser/interpreter_test.go index efaaedb..a49eb96 100644 --- a/parser/interpreter_test.go +++ b/parser/interpreter_test.go @@ -55,6 +55,10 @@ func TestExpr(t *testing.T) { {src: "(6+(1+2)+3)+5", res: "17"}, {src: "(6+(1+2+3)+5", err: "1:1: block not terminated"}, {src: "a := 2; a = 3; a", res: "3"}, + {src: "2 * 3 + 1 == 7", res: "true"}, + {src: "7 == 2 * 3 + 1", res: "true"}, + {src: "1 + 3 * 2 == 2 * 3 + 1", res: "true"}, + {src: "a := 1 + 3 * 2 == 2 * 3 + 1; a", res: "true"}, }) } @@ -77,6 +81,8 @@ func TestIf(t *testing.T) { {src: "a := 0; if a == 1 { a = 2 } else { a = 1 }; a", res: "1"}, {src: "a := 0; if a == 1 { a = 2 } else if a == 0 { a = 3 } else { a = 1 }; a", res: "3"}, {src: "a := 0; if a == 1 { a = 2 } else if a == 2 { a = 3 } else { a = 1 }; a", res: "1"}, + //{src: "a := 1; if a > 0 && a < 2 { a = 3 }; a", res: "3"}, + //{src: "a := 1; if a < 0 || a < 2 { a = 3 }; a", res: "3"}, }) } @@ -91,7 +97,8 @@ func TestFor(t *testing.T) { } func TestGoto(t *testing.T) { - gen(etest{src: ` + run(t, []etest{{ + src: ` func f(a int) int { a = a+1 goto end @@ -99,6 +106,26 @@ func f(a int) int { end: return a } -f(3)`, - res: "4"})(t) +f(3)`, res: "4"}, + }) +} + +/* +func TestSwitch(t *testing.T) { + run(t, []etest{{ + src: ` +func f(a int) int { + switch a { + default: + a = 0 + case 1,2: + a = a+1 + case 3: + a = a+2 + } + return a +} +f(3)`, res: "5"}, + }) } +*/ diff --git a/parser/parse.go b/parser/parse.go index bad9417..757468c 100644 --- a/parser/parse.go +++ b/parser/parse.go @@ -1,6 +1,7 @@ package parser import ( + "errors" "fmt" "log" "strconv" @@ -66,6 +67,17 @@ func (toks Tokens) Split(id lang.TokenId) (result []Tokens) { } } +func (toks Tokens) SplitStart(id lang.TokenId) (result []Tokens) { + for { + i := toks[1:].Index(id) + if i < 0 { + return append(result, toks) + } + result = append(result, toks[:i]) + toks = toks[i+1:] + } +} + func (p *Parser) Parse(src string) (out Tokens, err error) { log.Printf("Parse src: %#v\n", src) in, err := p.Scan(src, true) @@ -118,6 +130,8 @@ func (p *Parser) ParseStmt(in Tokens) (out Tokens, err error) { return p.ParseIf(in) case lang.Return: return p.ParseReturn(in) + case lang.Switch: + return p.ParseSwitch(in) case lang.Ident: if len(in) == 2 && in[1].Id == lang.Colon { return p.ParseLabel(in) @@ -186,8 +200,8 @@ func (p *Parser) ParseFor(in Tokens) (out Tokens, err error) { default: return nil, fmt.Errorf("invalild for statement") } - p.pushScope("for" + fc) breakLabel, continueLabel := p.breakLabel, p.continueLabel + p.pushScope("for" + fc) p.breakLabel, p.continueLabel = p.scope+"e", p.scope+"b" defer func() { p.breakLabel, p.continueLabel = breakLabel, continueLabel @@ -235,7 +249,7 @@ func (p *Parser) ParseFunc(in Tokens) (out Tokens, err error) { funcScope := p.funcScope s, _, ok := p.getSym(fname, p.scope) if !ok { - s = &symbol{} + s = &symbol{used: true} p.symbols[p.scope+fname] = s } p.pushScope(fname) @@ -331,6 +345,81 @@ func (p *Parser) ParseIf(in Tokens) (out Tokens, err error) { return out, err } +func (p *Parser) ParseSwitch(in Tokens) (out Tokens, err error) { + var init, cond, clauses Tokens + initcond := in[1 : len(in)-1] + if ii := initcond.Index(lang.Semicolon); ii < 0 { + cond = initcond + } else { + init = initcond[:ii] + cond = initcond[ii+1:] + } + label := "switch" + strconv.Itoa(p.labelCount[p.scope]) + p.labelCount[p.scope]++ + breakLabel := p.breakLabel + p.pushScope(label) + p.breakLabel = p.scope + defer func() { + p.breakLabel = breakLabel + p.popScope() + }() + log.Println("### ic:", initcond, "#init:", init, "#cond:", cond) + if len(init) > 0 { + if init, err = p.ParseStmt(init); err != nil { + return nil, err + } + out = init + } + if len(cond) > 0 { + if cond, err = p.ParseExpr(cond); err != nil { + return nil, err + } + } else { + cond = Tokens{{Id: lang.Ident, Str: "true"}} + } + out = append(out, cond...) + // Split switch body into case clauses. + clauses, err = p.Scan(in[len(in)-1].Block(), true) + log.Println("## clauses:", clauses) + sc := clauses.SplitStart(lang.Case) + // Make sure that the default clause is the last. + lsc := len(sc) - 1 + for i, cl := range sc { + if cl[1].Id == lang.Colon && i != lsc { + sc[i], sc[lsc] = sc[lsc], sc[i] + break + } + } + // Process each clause. + for i, cl := range sc { + co, err := p.ParseCaseClause(cl, i) + if err != nil { + return nil, err + } + out = append(out, co...) + } + return out, err +} + +func (p *Parser) ParseCaseClause(in Tokens, index int) (out Tokens, err error) { + var initcond, init, cond, body Tokens + tl := in.Split(lang.Colon) + if len(tl) != 2 { + return nil, errors.New("invalid case clause") + } + initcond, body = tl[0][1:], tl[1] + if ii := initcond.Index(lang.Semicolon); ii < 0 { + cond = initcond + } else { + init = initcond[:ii] + cond = initcond[ii+1:] + } + lcond := cond.Split(lang.Comma) + log.Println("# ParseCaseClause:", init, "cond:", cond, len(lcond)) + _ = body + 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 } @@ -376,7 +465,7 @@ func (p *Parser) ParseExpr(in Tokens) (out Tokens, err error) { case lang.Int, lang.String: out = append(out, t) vl++ - case lang.Define, lang.Add, lang.Sub, lang.Assign, lang.Equal, lang.Less: + case lang.Define, lang.Add, lang.Sub, lang.Assign, lang.Equal, lang.Greater, lang.Less, lang.Mul: // TODO: handle operator precedence to swap operators / operands if necessary if vl < 2 { ops = append(ops, t) @@ -407,9 +496,16 @@ func (p *Parser) ParseExpr(in Tokens) (out Tokens, err error) { } ops = append(ops, scanner.Token{Str: "call", Id: lang.Call, Pos: t.Pos}) } - if ol := len(ops); ol > 0 && vl > ol { - op := ops[ol-1] - ops = ops[:ol-1] + if lops, lout := len(ops), len(out); lops > 0 && vl > lops { + op := ops[lops-1] + ops = ops[:lops-1] + // Reorder tokens according to operator precedence rules. + if p.precedence(out[lout-2]) > p.precedence(op) { + op, out[lout-1], out[lout-2] = out[lout-2], op, out[lout-1] + if p.precedence(out[lout-3]) > p.precedence(out[lout-1]) { + out[lout-1], out[lout-2], out[lout-3] = out[lout-3], out[lout-1], out[lout-2] + } + } out = append(out, op) vl-- } @@ -484,3 +580,7 @@ func (p *Parser) popScope() { } p.scope = p.scope[:j] } + +func (p *Parser) precedence(t scanner.Token) int { + return p.TokenProps[t.Str].Precedence +} |
