summaryrefslogtreecommitdiff
path: root/parser
diff options
context:
space:
mode:
Diffstat (limited to 'parser')
-rw-r--r--parser/README.md6
-rw-r--r--parser/compiler.go23
-rw-r--r--parser/interpreter_test.go33
-rw-r--r--parser/parse.go112
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
+}