diff options
| author | Marc Vertes <mvertes@free.fr> | 2026-01-09 19:10:27 +0100 |
|---|---|---|
| committer | Marc Vertes <mvertes@free.fr> | 2026-01-09 19:10:27 +0100 |
| commit | 7520aa4474ea30985cf26631c6bbdebf38484a0d (patch) | |
| tree | e1e344c2fdc04affc0095eab26a39eef1fc3ba91 | |
| parent | 6ae0a2530c9a57fc093d2159591d9cae8140d641 (diff) | |
feat: initial support for maps
| -rw-r--r-- | comp/compiler.go | 4 | ||||
| -rw-r--r-- | interp/interpreter_test.go | 8 | ||||
| -rw-r--r-- | parser/README.md | 6 | ||||
| -rw-r--r-- | parser/expr.go | 14 | ||||
| -rw-r--r-- | parser/type.go | 18 | ||||
| -rw-r--r-- | vm/op_string.go | 39 | ||||
| -rw-r--r-- | vm/type.go | 22 | ||||
| -rw-r--r-- | vm/vm.go | 11 |
8 files changed, 89 insertions, 33 deletions
diff --git a/comp/compiler.go b/comp/compiler.go index 5d55be4..47c4a88 100644 --- a/comp/compiler.go +++ b/comp/compiler.go @@ -188,6 +188,8 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { if v := ks.Value.Value; v.CanInt() { emit(t, vm.IndexSet) } + case reflect.Map: + emit(t, vm.MapSet) } case symbol.Unset: j := top().Type.FieldIndex(ks.Name) @@ -260,7 +262,7 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { if s.Type.Rtype.Kind() == reflect.Slice { emit(t, vm.Fnew, s.Index, s.SliceLen) } else { - emit(t, vm.Fnew, s.Index) + emit(t, vm.Fnew, s.Index, 1) } } else { emit(t, vm.Dup, s.Index) diff --git a/interp/interpreter_test.go b/interp/interpreter_test.go index c7c3cbc..6c92102 100644 --- a/interp/interpreter_test.go +++ b/interp/interpreter_test.go @@ -217,6 +217,12 @@ func TestStruct(t *testing.T) { }) } +func TestMap(t *testing.T) { + run(t, []etest{ + {src: `type M map[string]bool; var m M; m`, res: `map[]`}, // #00 + }) +} + func TestType(t *testing.T) { src0 := `type ( I int @@ -271,5 +277,7 @@ func TestComposite(t *testing.T) { {src: `type T struct{N int; S string}; t := T{S: "foo"}; t`, res: `{0 foo}`}, // #05 {src: `a := []int{}`, res: `[]`}, // #06 {src: `a := []int{1, 2, 3}; a`, res: `[1 2 3]`}, // #07 + {src: `m := map[string]bool{}`, res: `map[]`}, // #08 + {src: `m := map[string]bool{"hello": true}; m`, res: `map[hello:true]`}, // #09 }) } diff --git a/parser/README.md b/parser/README.md index ba2f15c..9f40e77 100644 --- a/parser/README.md +++ b/parser/README.md @@ -39,14 +39,14 @@ Go language support: - [ ] complete numeric types - [x] function types - [ ] variadic functions -- [ ] pointers +- [x] pointers - [x] structures - [ ] embedded structures - [ ] recursive structures -- [ ] literal composite objects +- [x] literal composite objects - [ ] interfaces - [x] arrays, slices -- [ ] maps +- [x] maps - [ ] deterministic maps - [ ] channel types - [ ] channel operations diff --git a/parser/expr.go b/parser/expr.go index e00113f..0a87999 100644 --- a/parser/expr.go +++ b/parser/expr.go @@ -155,12 +155,22 @@ func (p *Parser) parseExpr(in Tokens, typeStr string) (out Tokens, err error) { typeStr = typ.String() p.Symbols.Add(symbol.UnsetAddr, typ.String(), vm.NewValue(typ), symbol.Type, typ, p.funcScope != "") out = append(out, scanner.Token{Tok: lang.Ident, Pos: t.Pos, Str: typeStr}) - i++ + i++ // FIXME: number of tokens to skip should be computed from type parsing. + + case lang.Map: + typ, err := p.parseTypeExpr(in[i:]) + if err != nil { + return out, err + } + typeStr = typ.String() + p.Symbols.Add(symbol.UnsetAddr, typ.String(), vm.NewValue(typ), symbol.Type, typ, p.funcScope != "") + out = append(out, scanner.Token{Tok: lang.Ident, Pos: t.Pos, Str: typeStr}) + i += 2 // FIXME: number of tokens to skip should be computed from type parsing. case lang.Comment: default: - log.Println("unxexpected token:", t) + log.Println("unexpected token:", t) } } for len(ops) > 0 { diff --git a/parser/type.go b/parser/type.go index 70ed4d1..5e8df02 100644 --- a/parser/type.go +++ b/parser/type.go @@ -127,6 +127,24 @@ func (p *Parser) parseTypeExpr(in Tokens) (typ *vm.Type, err error) { } return vm.StructOf(fields), nil + case lang.Map: + if len(in) < 3 || in[1].Tok != lang.BracketBlock { + return nil, fmt.Errorf("%w: %s", ErrInvalidType, in[0].Str) + } + kin, err := p.Scan(in[1].Block(), false) + if err != nil { + return nil, err + } + ktyp, err := p.parseTypeExpr(kin) // Key type + if err != nil { + return nil, err + } + etyp, err := p.parseTypeExpr(in[2:]) // Element type + if err != nil { + return nil, err + } + return vm.MapOf(ktyp, etyp), nil + default: return nil, fmt.Errorf("%w: %v", ErrNotImplemented, in[0].Name()) } diff --git a/vm/op_string.go b/vm/op_string.go index a39dcfa..e5a00b6 100644 --- a/vm/op_string.go +++ b/vm/op_string.go @@ -31,28 +31,29 @@ func _() { _ = x[Grow-20] _ = x[Index-21] _ = x[IndexSet-22] - _ = x[Jump-23] - _ = x[JumpTrue-24] - _ = x[JumpFalse-25] - _ = x[JumpSetTrue-26] - _ = x[JumpSetFalse-27] - _ = x[Lower-28] - _ = x[Loweri-29] - _ = x[Mul-30] - _ = x[New-31] - _ = x[Negate-32] - _ = x[Not-33] - _ = x[Pop-34] - _ = x[Push-35] - _ = x[Return-36] - _ = x[Sub-37] - _ = x[Subi-38] - _ = x[Swap-39] + _ = x[MapSet-23] + _ = x[Jump-24] + _ = x[JumpTrue-25] + _ = x[JumpFalse-26] + _ = x[JumpSetTrue-27] + _ = x[JumpSetFalse-28] + _ = x[Lower-29] + _ = x[Loweri-30] + _ = x[Mul-31] + _ = x[New-32] + _ = x[Negate-33] + _ = x[Not-34] + _ = x[Pop-35] + _ = x[Push-36] + _ = x[Return-37] + _ = x[Sub-38] + _ = x[Subi-39] + _ = x[Swap-40] } -const _Op_name = "NopAddAddrAssignFassignVassignCallCalliCallXDerefDupFdupFnewEqualEqualSetExitFieldFieldSetFieldFsetGreaterGrowIndexIndexSetJumpJumpTrueJumpFalseJumpSetTrueJumpSetFalseLowerLoweriMulNewNegateNotPopPushReturnSubSubiSwap" +const _Op_name = "NopAddAddrAssignFassignVassignCallCalliCallXDerefDupFdupFnewEqualEqualSetExitFieldFieldSetFieldFsetGreaterGrowIndexIndexSetMapSetJumpJumpTrueJumpFalseJumpSetTrueJumpSetFalseLowerLoweriMulNewNegateNotPopPushReturnSubSubiSwap" -var _Op_index = [...]uint8{0, 3, 6, 10, 16, 23, 30, 34, 39, 44, 49, 52, 56, 60, 65, 73, 77, 82, 90, 99, 106, 110, 115, 123, 127, 135, 144, 155, 167, 172, 178, 181, 184, 190, 193, 196, 200, 206, 209, 213, 217} +var _Op_index = [...]uint8{0, 3, 6, 10, 16, 23, 30, 34, 39, 44, 49, 52, 56, 60, 65, 73, 77, 82, 90, 99, 106, 110, 115, 123, 129, 133, 141, 150, 161, 173, 178, 184, 187, 190, 196, 199, 202, 206, 212, 215, 219, 223} func (i Op) String() string { idx := int(i) - 0 @@ -38,8 +38,21 @@ type Value struct { } // NewValue returns an addressable zero value for the specified type. -func NewValue(typ *Type) Value { - if typ.Rtype.Kind() == reflect.Func { +func NewValue(typ *Type, arg ...int) Value { + switch typ.Rtype.Kind() { + case reflect.Slice: + if len(arg) == 1 { + v := reflect.New(typ.Rtype).Elem() + v.Set(reflect.MakeSlice(typ.Rtype, arg[0], arg[0])) + return Value{Type: typ, Value: v} + } + case reflect.Map: + if len(arg) == 1 { + v := reflect.New(typ.Rtype).Elem() + v.Set(reflect.MakeMapWithSize(typ.Rtype, arg[0])) + return Value{Type: typ, Value: v} + } + case reflect.Func: typ = TypeOf(0) // Function value is its index in the code segment. } return Value{Type: typ, Value: reflect.New(typ.Rtype).Elem()} @@ -71,6 +84,11 @@ func SliceOf(t *Type) *Type { return &Type{Rtype: reflect.SliceOf(t.Rtype)} } +// MapOf returns the map type with the given key and element types. +func MapOf(k, e *Type) *Type { + return &Type{Rtype: reflect.MapOf(k.Rtype, e.Rtype)} +} + // FuncOf returns the function type with the given argument and result types. func FuncOf(arg, ret []*Type, variadic bool) *Type { a := make([]reflect.Type, len(arg)) @@ -42,6 +42,7 @@ const ( Grow // -- ; sp += $1 Index // a i -- a[i] ; IndexSet // a i v -- a; a[i] = v + MapSet // a i v -- a; a[i] = v Jump // -- ; ip += $1 JumpTrue // cond -- ; if cond { ip += $1 } JumpFalse // cond -- ; if cond { ip += $1 } @@ -170,11 +171,7 @@ func (m *Machine) Run() (err error) { case Fdup: mem = append(mem, mem[c.Arg[0]+fp-1]) case Fnew: - mem = append(mem, NewValue(mem[c.Arg[0]].Type)) - if len(c.Arg) > 1 { - mem[len(mem)-1].Grow(c.Arg[1]) - mem[len(mem)-1].SetLen(c.Arg[1]) - } + mem = append(mem, NewValue(mem[c.Arg[0]].Type, c.Arg[1:]...)) case Field: fv := mem[sp-1].FieldByIndex(c.Arg) if !fv.CanSet() { @@ -268,9 +265,11 @@ func (m *Machine) Run() (err error) { mem[sp-2].Value = mem[sp-2].Index(int(mem[sp-1].Int())) mem = mem[:sp-1] case IndexSet: - log.Println("## IndexSet:", sp-3, mem[sp-3].Type) mem[sp-3].Value.Index(int(mem[sp-2].Int())).Set(mem[sp-1].Value) mem = mem[:sp-2] + case MapSet: + mem[sp-3].SetMapIndex(mem[sp-2].Value, mem[sp-1].Value) + mem = mem[:sp-2] case Vassign: mem[sp-2].Set(mem[sp-1].Value) mem = mem[:sp-2] |
