compiler: implement switch statement support

This commit is contained in:
Evgenii Stratonikov 2020-01-28 15:47:56 +03:00
parent 0ce0ecb95e
commit 28571bd3dc
2 changed files with 187 additions and 5 deletions

View file

@ -337,6 +337,44 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
c.setLabel(lElseEnd)
return nil
case *ast.SwitchStmt:
// fallthrough is not supported
ast.Walk(c, n.Tag)
eqOpcode := c.getEqualityOpcode(n.Tag)
switchEnd := c.newLabel()
for i := range n.Body.List {
lEnd := c.newLabel()
lStart := c.newLabel()
cc := n.Body.List[i].(*ast.CaseClause)
if l := len(cc.List); l != 0 { // if not `default`
for j := range cc.List {
emitOpcode(c.prog.BinWriter, opcode.DUP)
ast.Walk(c, cc.List[j])
emitOpcode(c.prog.BinWriter, eqOpcode)
if j == l-1 {
emitJmp(c.prog.BinWriter, opcode.JMPIFNOT, int16(lEnd))
} else {
emitJmp(c.prog.BinWriter, opcode.JMPIF, int16(lStart))
}
}
}
c.setLabel(lStart)
for _, stmt := range cc.Body {
ast.Walk(c, stmt)
}
emitJmp(c.prog.BinWriter, opcode.JMP, int16(switchEnd))
c.setLabel(lEnd)
}
c.setLabel(switchEnd)
emitOpcode(c.prog.BinWriter, opcode.DROP)
return nil
case *ast.BasicLit:
c.emitLoadConst(c.typeInfo.Types[n])
return nil
@ -431,11 +469,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
}
case n.Op == token.EQL:
// VM has separate opcodes for number and string equality
if isStringType(c.typeInfo.Types[n.X].Type) {
emitOpcode(c.prog.BinWriter, opcode.EQUAL)
} else {
emitOpcode(c.prog.BinWriter, opcode.NUMEQUAL)
}
op := c.getEqualityOpcode(n.X)
emitOpcode(c.prog.BinWriter, op)
case n.Op == token.NEQ:
// VM has separate opcodes for number and string equality
if isStringType(c.typeInfo.Types[n.X].Type) {
@ -657,6 +692,15 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
return c
}
func (c *codegen) getEqualityOpcode(expr ast.Expr) opcode.Opcode {
t, ok := c.typeInfo.Types[expr].Type.Underlying().(*types.Basic)
if ok && t.Info()&types.IsNumeric != 0 {
return opcode.NUMEQUAL
}
return opcode.EQUAL
}
// getByteArray returns byte array value from constant expr.
// Only literals are supported.
func (c *codegen) getByteArray(expr ast.Expr) []byte {