summaryrefslogtreecommitdiff
path: root/parser/parse.go
diff options
context:
space:
mode:
Diffstat (limited to 'parser/parse.go')
-rw-r--r--parser/parse.go112
1 files changed, 106 insertions, 6 deletions
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
+}