diff --git a/node.go b/node.go index 1dbf821..274ef23 100644 --- a/node.go +++ b/node.go @@ -407,15 +407,15 @@ func (n *ndDict) String() string { return sb.String() } -type ndStruct struct { +type ndStructDef struct { tok *token name node vars []node fns []node } -func (n *ndStruct) token() *token { return n.tok } -func (n *ndStruct) String() string { +func (n *ndStructDef) token() *token { return n.tok } +func (n *ndStructDef) String() string { sb := strings.Builder{} sb.WriteString("struct ") sb.WriteString(n.name.String()) @@ -432,6 +432,23 @@ func (n *ndStruct) String() string { return sb.String() } +type ndStructInit struct { + tok *token + name node + values node // ndDict +} + +func (n *ndStructInit) token() *token { return n.tok } +func (n *ndStructInit) String() string { + sb := strings.Builder{} + sb.WriteString(n.name.String()) + sb.WriteString(" {\n") + sb.WriteString(" " + n.values.String() + "\n") + sb.WriteString("}") + + return sb.String() +} + type ndContinue struct { tok *token } diff --git a/obj.go b/obj.go index a730d21..dc8eb61 100644 --- a/obj.go +++ b/obj.go @@ -25,6 +25,7 @@ const ( tStr tList tDict + tStruct tBuiltinFunc tGoStdModFunc tFunc @@ -47,6 +48,8 @@ func (o objtyp) String() string { return "list" case tDict: return "dict" + case tStruct: + return "struct" case tBuiltinFunc: return "builtinfunc" case tGoStdModFunc: @@ -70,7 +73,7 @@ type obj struct { dict *dict mod *module - // builtin/func/gostdmodfunc + // builtin/func/gostdmodfunc/struct name string // builtin @@ -83,6 +86,9 @@ type obj struct { fmod *module params []string body []node + + // struct + fields map[string]*obj } func (o *obj) update(x *obj) { @@ -100,6 +106,9 @@ func (o *obj) update(x *obj) { o.list = x.list case tDict: o.dict = x.dict + case tStruct: + o.name = x.name + o.fields = x.fields case tBuiltinFunc: o.name = x.name o.bfnbody = x.bfnbody @@ -135,6 +144,11 @@ func (o *obj) clone() *obj { } case tDict: cloned.dict = o.dict.clone() + case tStruct: + cloned.name = o.name + for k, v := range o.fields { + cloned.fields[k] = v.clone() + } case tBuiltinFunc: cloned.name = o.name cloned.bfnbody = o.bfnbody @@ -221,6 +235,23 @@ func (o *obj) equals(x *obj) bool { return o.name == x.name case tGoStdModFunc: return o.name == x.name + case tStruct: + if o.name != x.name { + return false + } + + for k, v := range o.fields { + v2, ok := x.fields[k] + if !ok { + return false + } + if !v.equals(v2) { + return false + } + } + + return true + default: return o.fmod == x.fmod && o.name == x.name } @@ -252,6 +283,21 @@ func (o *obj) String() string { return sb.String() case tDict: return o.dict.String() + case tStruct: + sb := strings.Builder{} + sb.WriteString(o.name) + sb.WriteString("{") + i := 0 + for k, v := range o.fields { + sb.WriteString(k + ":" + v.String()) + if i < len(o.fields)-1 { + sb.WriteString(", ") + } + i++ + } + sb.WriteString("}") + + return sb.String() case tMod: return o.mod.name case tBuiltinFunc: diff --git a/parse.go b/parse.go index 092e04c..ef6c122 100644 --- a/parse.go +++ b/parse.go @@ -20,6 +20,14 @@ func newparser(mod *module) *parser { return p } +func (p *parser) mark() int { + return p.tokenizer.mark() +} + +func (p *parser) reset(m int) { + p.tokenizer.reset(m) +} + func (p *parser) iscur(t tktype) bool { return p.cur.typ == t } @@ -104,7 +112,7 @@ func (p *parser) stmt() node { } if p.iscur(tkStruct) { - return p._struct() + return p.structdef() } if p.iscur(tkReturn) { @@ -297,9 +305,9 @@ func (p *parser) def() node { } // struct = "struct" ident "{" ident-list? def-list? "}" -func (p *parser) _struct() node { +func (p *parser) structdef() node { p.skipnewline() - n := &ndStruct{tok: p.cur} + n := &ndStructDef{tok: p.cur} p.must(tkStruct) n.name = p.ident() p.must(tkLBrace) @@ -743,7 +751,7 @@ func (p *parser) postfix() node { return n } -// primary = list | dict | "(" expr ")" | str | num | "true" | "false" | ident +// primary = list | dict | "(" expr ")" | str | num | "true" | "false" | ident | struct_init func (p *parser) primary() node { if p.iscur(tkLBracket) { return p.list() @@ -805,7 +813,32 @@ func (p *parser) primary() node { return n } - return p.ident() + i := p.ident() + if !p.iscur(tkLBrace) { + return i + } + + // struct initialize. + // ident{varname: value, ...} + // this is parsed as ident + dict + if d := p.try(p.dict); d != nil { + return &ndStructInit{tok: p.cur, name: i, values: d} + } + + return i +} + +func (p *parser) try(f func() node) (n node) { + m := p.mark() + c := p.cur + defer func() { + if r := recover(); r != nil { + p.reset(m) + p.cur = c + } + }() + n = f() + return } // list = "[" expr-list? "]" diff --git a/process.go b/process.go index bd39a43..afa1bcb 100644 --- a/process.go +++ b/process.go @@ -34,8 +34,11 @@ func process(mod *module, nd node) (procResult, shibaErr) { case *ndCondLoop: return procCondLoop(mod, n) - case *ndStruct: - return procStruct(mod, n) + case *ndStructDef: + return procStructDef(mod, n) + + case *ndStructInit: + return procStructInit(mod, n) case *ndFunDef: return procFunDef(mod, n) @@ -470,7 +473,7 @@ func procCondLoop(mod *module, n *ndCondLoop) (procResult, shibaErr) { return nil, nil } -func procStruct(mod *module, n *ndStruct) (procResult, shibaErr) { +func procStructDef(mod *module, n *ndStructDef) (procResult, shibaErr) { if _, ok := n.name.(*ndIdent); !ok { return nil, &errSimple{msg: fmt.Sprintf("invalid struct name %s", n.name), l: n.token().loc} } @@ -495,6 +498,45 @@ func procStruct(mod *module, n *ndStruct) (procResult, shibaErr) { return nil, nil } +func procStructInit(mod *module, n *ndStructInit) (procResult, shibaErr) { + if _, ok := n.name.(*ndIdent); !ok { + return nil, &errSimple{msg: fmt.Sprintf("invalid struct name %s", n.name), l: n.token().loc} + } + + name := n.name.(*ndIdent).ident + o := &obj{typ: tStruct, name: name, fields: map[string]*obj{}} + + sd, ok := env.getstruct(mod, name) + if !ok { + return nil, &errSimple{msg: fmt.Sprintf("struct %s is not defined", name), l: n.token().loc} + } + + d, ok := n.values.(*ndDict) + if !ok { + return nil, &errInternal{msg: fmt.Sprintf("dict expected in struct init but got %s", n.values), l: n.token().loc} + } + + for i := range d.keys { + if _, ok := d.keys[i].(*ndIdent); !ok { + return nil, &errSimple{msg: fmt.Sprintf("invalid field name %s in struct %s", d.keys[i], name), l: n.token().loc} + } + + k := d.keys[i].(*ndIdent).ident + if !sd.hasfield(k) { + return nil, &errSimple{msg: fmt.Sprintf("struct %s does not have field %s", name, k), l: n.token().loc} + } + + v, err := procAsObj(mod, d.vals[i]) + if err != nil { + return nil, err + } + + o.fields[k] = v + } + + return &prObj{o: o}, nil +} + func procFunDef(mod *module, n *ndFunDef) (procResult, shibaErr) { params := []string{} for _, p := range n.params { diff --git a/struct.go b/struct.go index 8665d83..2ffbc5c 100644 --- a/struct.go +++ b/struct.go @@ -5,3 +5,13 @@ type structdef struct { vars []string defs []*obj } + +func (sd *structdef) hasfield(f string) bool { + for _, v := range sd.vars { + if v == f { + return true + } + } + + return false +} diff --git a/tests/struct.sb b/tests/struct.sb index c1b24f4..cb62bd4 100644 --- a/tests/struct.sb +++ b/tests/struct.sb @@ -19,4 +19,7 @@ struct person{ } } +p = person{name: "alice", age: 3} +print(p) + print("struct test succeeded") diff --git a/token.go b/token.go index a0df28d..2542342 100644 --- a/token.go +++ b/token.go @@ -38,6 +38,10 @@ func (t tktype) String() string { for _, punct := range punctuators { if punct.t == t { + if t == tkNewLine { + return "newline" + } + return punct.s } }