From 058958729dacfba4ac2b94be346563a7d636aab1 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 23 Jan 2020 17:06:15 +0300 Subject: [PATCH 1/2] compiler: support map literals --- pkg/compiler/codegen.go | 15 +++++++++++++ pkg/compiler/map_test.go | 46 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 pkg/compiler/map_test.go diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 063bfc922..87ad57291 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -357,6 +357,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { typ = c.typeInfo.ObjectOf(t).Type().Underlying() case *ast.SelectorExpr: typ = c.typeInfo.ObjectOf(t.Sel).Type().Underlying() + case *ast.MapType: + typ = c.typeInfo.TypeOf(t) default: ln := len(n.Elts) // ByteArrays needs a different approach than normal arrays. @@ -375,6 +377,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { switch typ.(type) { case *types.Struct: c.convertStruct(n) + case *types.Map: + c.convertMap(n) } return nil @@ -700,6 +704,17 @@ func (c *codegen) convertByteArray(lit *ast.CompositeLit) { emitBytes(c.prog.BinWriter, buf) } +func (c *codegen) convertMap(lit *ast.CompositeLit) { + emitOpcode(c.prog.BinWriter, opcode.NEWMAP) + for i := range lit.Elts { + elem := lit.Elts[i].(*ast.KeyValueExpr) + emitOpcode(c.prog.BinWriter, opcode.DUP) + ast.Walk(c, elem.Key) + ast.Walk(c, elem.Value) + emitOpcode(c.prog.BinWriter, opcode.SETITEM) + } +} + func (c *codegen) convertStruct(lit *ast.CompositeLit) { // Create a new structScope to initialize and store // the positions of its variables. diff --git a/pkg/compiler/map_test.go b/pkg/compiler/map_test.go new file mode 100644 index 000000000..0ae70841e --- /dev/null +++ b/pkg/compiler/map_test.go @@ -0,0 +1,46 @@ +package compiler_test + +import ( + "math/big" + "testing" +) + +var mapTestCases = []testCase{ + { + "map composite literal", + ` + package foo + func Main() int { + t := map[int]int{ + 1: 6, + 2: 9, + } + + age := t[2] + return age + } + `, + big.NewInt(9), + }, + { + "nested map", + ` + package foo + func Main() int { + t := map[int]map[int]int{ + 1: map[int]int{2: 5, 3: 1}, + 2: nil, + 5: map[int]int{3: 4, 7: 2}, + } + + x := t[5][3] + return x + } + `, + big.NewInt(4), + }, +} + +func TestMaps(t *testing.T) { + runTestCases(t, mapTestCases) +} From 4fd766fe096db2ece92aade041fefedeefa41e45 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 23 Jan 2020 17:28:35 +0300 Subject: [PATCH 2/2] compiler: allow usage of string literals in index expressions --- pkg/compiler/codegen.go | 53 ++++++++++++++++++++++++---------------- pkg/compiler/map_test.go | 16 ++++++++++++ 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 87ad57291..1d5e144eb 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -61,30 +61,34 @@ func (c *codegen) emitLoadConst(t types.TypeAndValue) { } switch typ := t.Type.Underlying().(type) { case *types.Basic: - switch typ.Kind() { - case types.Int, types.UntypedInt, types.Uint: - val, _ := constant.Int64Val(t.Value) - emitInt(c.prog.BinWriter, val) - case types.String, types.UntypedString: - val := constant.StringVal(t.Value) - emitString(c.prog.BinWriter, val) - case types.Bool, types.UntypedBool: - val := constant.BoolVal(t.Value) - emitBool(c.prog.BinWriter, val) - case types.Byte: - val, _ := constant.Int64Val(t.Value) - b := byte(val) - emitBytes(c.prog.BinWriter, []byte{b}) - default: - c.prog.Err = fmt.Errorf("compiler doesn't know how to convert this basic type: %v", t) - return - } + c.convertBasicType(t, typ) default: c.prog.Err = fmt.Errorf("compiler doesn't know how to convert this constant: %v", t) return } } +func (c *codegen) convertBasicType(t types.TypeAndValue, typ *types.Basic) { + switch typ.Kind() { + case types.Int, types.UntypedInt, types.Uint: + val, _ := constant.Int64Val(t.Value) + emitInt(c.prog.BinWriter, val) + case types.String, types.UntypedString: + val := constant.StringVal(t.Value) + emitString(c.prog.BinWriter, val) + case types.Bool, types.UntypedBool: + val := constant.BoolVal(t.Value) + emitBool(c.prog.BinWriter, val) + case types.Byte: + val, _ := constant.Int64Val(t.Value) + b := byte(val) + emitBytes(c.prog.BinWriter, []byte{b}) + default: + c.prog.Err = fmt.Errorf("compiler doesn't know how to convert this basic type: %v", t) + return + } +} + func (c *codegen) emitLoadLocal(name string) { pos := c.scope.loadLocal(name) if pos < 0 { @@ -574,12 +578,19 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { switch n.Index.(type) { case *ast.BasicLit: t := c.typeInfo.Types[n.Index] - val, _ := constant.Int64Val(t.Value) - c.emitLoadField(int(val)) + switch typ := t.Type.Underlying().(type) { + case *types.Basic: + c.convertBasicType(t, typ) + default: + c.prog.Err = fmt.Errorf("compiler can't use following type as an index: %T", typ) + return nil + } default: ast.Walk(c, n.Index) - emitOpcode(c.prog.BinWriter, opcode.PICKITEM) // just pickitem here } + + emitOpcode(c.prog.BinWriter, opcode.PICKITEM) // just pickitem here + return nil case *ast.ForStmt: diff --git a/pkg/compiler/map_test.go b/pkg/compiler/map_test.go index 0ae70841e..be7453507 100644 --- a/pkg/compiler/map_test.go +++ b/pkg/compiler/map_test.go @@ -39,6 +39,22 @@ var mapTestCases = []testCase{ `, big.NewInt(4), }, + { + "map with string index", + ` + package foo + func Main() string { + t := map[string]string{ + "name": "Valera", + "age": "33", + } + + name := t["name"] + return name + } + `, + []byte("Valera"), + }, } func TestMaps(t *testing.T) {