compiler: refactor convertToken func

Move `getEqualityOpcode` into `convertToken`.
`convertToken` does not need codegen.
This commit is contained in:
Evgenii Stratonikov 2020-08-23 12:39:58 +03:00
parent ae88c77a8a
commit fd7af77895
3 changed files with 79 additions and 69 deletions

View file

@ -446,7 +446,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
// RHS can contain exactly one expression, thus there is no need to iterate. // RHS can contain exactly one expression, thus there is no need to iterate.
ast.Walk(c, n.Lhs[0]) ast.Walk(c, n.Lhs[0])
ast.Walk(c, n.Rhs[0]) ast.Walk(c, n.Rhs[0])
c.convertToken(n.Tok) c.emitToken(n.Tok, c.typeOf(n.Rhs[0]))
} }
for i := 0; i < len(n.Lhs); i++ { for i := 0; i < len(n.Lhs); i++ {
switch t := n.Lhs[i].(type) { switch t := n.Lhs[i].(type) {
@ -581,7 +581,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
case *ast.SwitchStmt: case *ast.SwitchStmt:
ast.Walk(c, n.Tag) ast.Walk(c, n.Tag)
eqOpcode := c.getEqualityOpcode(n.Tag) eqOpcode, _ := convertToken(token.EQL, c.typeOf(n.Tag))
switchEnd, label := c.generateLabel(labelEnd) switchEnd, label := c.generateLabel(labelEnd)
lastSwitch := c.currentSwitch lastSwitch := c.currentSwitch
@ -735,28 +735,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
default: default:
ast.Walk(c, n.X) ast.Walk(c, n.X)
ast.Walk(c, n.Y) ast.Walk(c, n.Y)
switch { c.emitToken(n.Op, c.typeOf(n.X))
case n.Op == token.ADD:
// VM has separate opcodes for number and string concatenation
if isString(tinfo.Type) {
emit.Opcode(c.prog.BinWriter, opcode.CAT)
} else {
emit.Opcode(c.prog.BinWriter, opcode.ADD)
}
case n.Op == token.EQL:
// VM has separate opcodes for number and string equality
op := c.getEqualityOpcode(n.X)
emit.Opcode(c.prog.BinWriter, op)
case n.Op == token.NEQ:
// VM has separate opcodes for number and string equality
if isString(c.typeOf(n.X)) {
emit.Opcode(c.prog.BinWriter, opcode.NOTEQUAL)
} else {
emit.Opcode(c.prog.BinWriter, opcode.NUMNOTEQUAL)
}
default:
c.convertToken(n.Op)
}
return nil return nil
} }
@ -925,7 +904,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
case *ast.IncDecStmt: case *ast.IncDecStmt:
ast.Walk(c, n.X) ast.Walk(c, n.X)
c.convertToken(n.Tok) c.emitToken(n.Tok, c.typeOf(n.X))
// For now only identifiers are supported for (post) for stmts. // For now only identifiers are supported for (post) for stmts.
// for i := 0; i < 10; i++ {} // for i := 0; i < 10; i++ {}
@ -1205,15 +1184,6 @@ func (c *codegen) getLabelOffset(typ labelOffsetType, name string) uint16 {
return c.labels[labelWithType{name: name, typ: typ}] return c.labels[labelWithType{name: name, typ: typ}]
} }
func (c *codegen) getEqualityOpcode(expr ast.Expr) opcode.Opcode {
t, ok := c.typeOf(expr).Underlying().(*types.Basic)
if ok && t.Info()&types.IsNumeric != 0 {
return opcode.NUMEQUAL
}
return opcode.EQUAL
}
// getByteArray returns byte array value from constant expr. // getByteArray returns byte array value from constant expr.
// Only literals are supported. // Only literals are supported.
func (c *codegen) getByteArray(expr ast.Expr) []byte { func (c *codegen) getByteArray(expr ast.Expr) []byte {
@ -1459,59 +1429,79 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit, ptr bool) {
} }
} }
func (c *codegen) convertToken(tok token.Token) { func (c *codegen) emitToken(tok token.Token, typ types.Type) {
op, err := convertToken(tok, typ)
if err != nil {
c.prog.Err = err
return
}
emit.Opcode(c.prog.BinWriter, op)
}
func convertToken(tok token.Token, typ types.Type) (opcode.Opcode, error) {
switch tok { switch tok {
case token.ADD_ASSIGN: case token.ADD_ASSIGN:
emit.Opcode(c.prog.BinWriter, opcode.ADD) return opcode.ADD, nil
case token.SUB_ASSIGN: case token.SUB_ASSIGN:
emit.Opcode(c.prog.BinWriter, opcode.SUB) return opcode.SUB, nil
case token.MUL_ASSIGN: case token.MUL_ASSIGN:
emit.Opcode(c.prog.BinWriter, opcode.MUL) return opcode.MUL, nil
case token.QUO_ASSIGN: case token.QUO_ASSIGN:
emit.Opcode(c.prog.BinWriter, opcode.DIV) return opcode.DIV, nil
case token.REM_ASSIGN: case token.REM_ASSIGN:
emit.Opcode(c.prog.BinWriter, opcode.MOD) return opcode.MOD, nil
case token.ADD: case token.ADD:
emit.Opcode(c.prog.BinWriter, opcode.ADD) // VM has separate opcodes for number and string concatenation
if isString(typ) {
return opcode.CAT, nil
}
return opcode.ADD, nil
case token.SUB: case token.SUB:
emit.Opcode(c.prog.BinWriter, opcode.SUB) return opcode.SUB, nil
case token.MUL: case token.MUL:
emit.Opcode(c.prog.BinWriter, opcode.MUL) return opcode.MUL, nil
case token.QUO: case token.QUO:
emit.Opcode(c.prog.BinWriter, opcode.DIV) return opcode.DIV, nil
case token.REM: case token.REM:
emit.Opcode(c.prog.BinWriter, opcode.MOD) return opcode.MOD, nil
case token.LSS: case token.LSS:
emit.Opcode(c.prog.BinWriter, opcode.LT) return opcode.LT, nil
case token.LEQ: case token.LEQ:
emit.Opcode(c.prog.BinWriter, opcode.LTE) return opcode.LTE, nil
case token.GTR: case token.GTR:
emit.Opcode(c.prog.BinWriter, opcode.GT) return opcode.GT, nil
case token.GEQ: case token.GEQ:
emit.Opcode(c.prog.BinWriter, opcode.GTE) return opcode.GTE, nil
case token.EQL: case token.EQL:
emit.Opcode(c.prog.BinWriter, opcode.NUMEQUAL) // VM has separate opcodes for number and string equality
if isNumber(typ) {
return opcode.NUMEQUAL, nil
}
return opcode.EQUAL, nil
case token.NEQ: case token.NEQ:
emit.Opcode(c.prog.BinWriter, opcode.NUMNOTEQUAL) // VM has separate opcodes for number and string equality
if isNumber(typ) {
return opcode.NUMNOTEQUAL, nil
}
return opcode.NOTEQUAL, nil
case token.DEC: case token.DEC:
emit.Opcode(c.prog.BinWriter, opcode.DEC) return opcode.DEC, nil
case token.INC: case token.INC:
emit.Opcode(c.prog.BinWriter, opcode.INC) return opcode.INC, nil
case token.NOT: case token.NOT:
emit.Opcode(c.prog.BinWriter, opcode.NOT) return opcode.NOT, nil
case token.AND: case token.AND:
emit.Opcode(c.prog.BinWriter, opcode.AND) return opcode.AND, nil
case token.OR: case token.OR:
emit.Opcode(c.prog.BinWriter, opcode.OR) return opcode.OR, nil
case token.SHL: case token.SHL:
emit.Opcode(c.prog.BinWriter, opcode.SHL) return opcode.SHL, nil
case token.SHR: case token.SHR:
emit.Opcode(c.prog.BinWriter, opcode.SHR) return opcode.SHR, nil
case token.XOR: case token.XOR:
emit.Opcode(c.prog.BinWriter, opcode.XOR) return opcode.XOR, nil
default: default:
c.prog.Err = fmt.Errorf("compiler could not convert token: %s", tok) return 0, fmt.Errorf("compiler could not convert token: %s", tok)
return
} }
} }

View file

@ -2,9 +2,9 @@ package compiler
import ( import (
"go/token" "go/token"
"go/types"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -14,59 +14,74 @@ func TestConvertToken(t *testing.T) {
name string name string
token token.Token token token.Token
opcode opcode.Opcode opcode opcode.Opcode
typ types.Type
} }
testCases := []testCase{ testCases := []testCase{
{"ADD", {"ADD (string)",
token.ADD, token.ADD,
opcode.ADD, opcode.ADD,
types.Typ[types.Int],
},
{"ADD (number)",
token.ADD,
opcode.CAT,
types.Typ[types.String],
}, },
{"SUB", {"SUB",
token.SUB, token.SUB,
opcode.SUB, opcode.SUB,
nil,
}, },
{"MUL", {"MUL",
token.MUL, token.MUL,
opcode.MUL, opcode.MUL,
nil,
}, },
{"QUO", {"QUO",
token.QUO, token.QUO,
opcode.DIV, opcode.DIV,
nil,
}, },
{"REM", {"REM",
token.REM, token.REM,
opcode.MOD, opcode.MOD,
nil,
}, },
{"ADD_ASSIGN", {"ADD_ASSIGN",
token.ADD_ASSIGN, token.ADD_ASSIGN,
opcode.ADD, opcode.ADD,
nil,
}, },
{"SUB_ASSIGN", {"SUB_ASSIGN",
token.SUB_ASSIGN, token.SUB_ASSIGN,
opcode.SUB, opcode.SUB,
nil,
}, },
{"MUL_ASSIGN", {"MUL_ASSIGN",
token.MUL_ASSIGN, token.MUL_ASSIGN,
opcode.MUL, opcode.MUL,
nil,
}, },
{"QUO_ASSIGN", {"QUO_ASSIGN",
token.QUO_ASSIGN, token.QUO_ASSIGN,
opcode.DIV, opcode.DIV,
nil,
}, },
{"REM_ASSIGN", {"REM_ASSIGN",
token.REM_ASSIGN, token.REM_ASSIGN,
opcode.MOD, opcode.MOD,
nil,
}, },
} }
for _, tcase := range testCases { for _, tcase := range testCases {
t.Run(tcase.name, func(t *testing.T) { eval(t, tcase.token, tcase.opcode) }) t.Run(tcase.name, func(t *testing.T) { eval(t, tcase.token, tcase.opcode, tcase.typ) })
} }
} }
func eval(t *testing.T, token token.Token, opcode opcode.Opcode) { func eval(t *testing.T, token token.Token, opcode opcode.Opcode, typ types.Type) {
codegen := &codegen{prog: io.NewBufBinWriter()} op, err := convertToken(token, typ)
codegen.convertToken(token) assert.NoError(t, err)
readOpcode := codegen.prog.Bytes() assert.Equal(t, opcode, op)
assert.Equal(t, []byte{byte(opcode)}, readOpcode)
} }

View file

@ -31,6 +31,11 @@ func isByte(typ types.Type) bool {
return isBasicTypeOfKind(typ, types.Uint8, types.Int8) return isBasicTypeOfKind(typ, types.Uint8, types.Int8)
} }
func isNumber(typ types.Type) bool {
t, ok := typ.Underlying().(*types.Basic)
return ok && t.Info()&types.IsNumeric != 0
}
func isString(typ types.Type) bool { func isString(typ types.Type) bool {
return isBasicTypeOfKind(typ, types.String) return isBasicTypeOfKind(typ, types.String)
} }