diff --git a/env.go b/env.go index e1de459..b50ef27 100644 --- a/env.go +++ b/env.go @@ -68,6 +68,16 @@ func (e *environment) delblockscope(mod *module) error { return nil } +func (e *environment) setstruct(mod *module, name string, s *structdef) error { + m, err := e.findmodule(mod) + if err != nil { + return err + } + + m.setstruct(name, s) + return nil +} + func (e *environment) setobj(mod *module, name string, o *obj) error { m, err := e.findmodule(mod) if err != nil { @@ -78,6 +88,15 @@ func (e *environment) setobj(mod *module, name string, o *obj) error { return nil } +func (e *environment) getstruct(mod *module, name string) (*structdef, bool) { + m, err := e.findmodule(mod) + if err != nil { + return nil, false + } + + return m.getstruct(name) +} + func (e *environment) getobj(mod *module, name string) (*obj, bool) { m, err := e.findmodule(mod) if err != nil { diff --git a/mod.go b/mod.go index 0dfb41a..25a0532 100644 --- a/mod.go +++ b/mod.go @@ -171,6 +171,15 @@ func (m *module) setobj(name string, o *obj) { m.globscope.setobj(name, o) } +func (m *module) setstruct(name string, s *structdef) { + if m.funcscopes.Len() != 0 { + m.funcscopes.Back().Value.(*scope).setstruct(name, s) + return + } + + m.globscope.setstruct(name, s) +} + func (m *module) getobj(name string) (*obj, bool) { if m.funcscopes.Len() != 0 { o, ok := m.funcscopes.Back().Value.(*scope).getobj(name) @@ -184,6 +193,19 @@ func (m *module) getobj(name string) (*obj, bool) { return m.globscope.getobj(name) } +func (m *module) getstruct(name string) (*structdef, bool) { + if m.funcscopes.Len() != 0 { + s, ok := m.funcscopes.Back().Value.(*scope).getstruct(name) + if ok { + return s, true + } + + return m.globscope.getglobstruct(name) + } + + return m.globscope.getstruct(name) +} + func modtofile(modname string) string { return modname + ".sb" } diff --git a/node.go b/node.go index 2393b10..c470705 100644 --- a/node.go +++ b/node.go @@ -407,6 +407,31 @@ func (n *ndDict) String() string { return sb.String() } +type ndStruct struct { + tok *token + name node + vars []node + fns []node +} + +func (n *ndStruct) token() *token { return n.tok } +func (n *ndStruct) String() string { + sb := strings.Builder{} + sb.WriteString("struct ") + sb.WriteString(n.name.String()) + sb.WriteString("{\n") + for i := range n.vars { + sb.WriteString(" " + n.vars[i].String() + "\n") + } + sb.WriteString("---\n") + for i := range n.fns { + sb.WriteString(" " + n.fns[i].String() + "\n") + } + sb.WriteString("}") + + return sb.String() +} + type ndContinue struct { tok *token } diff --git a/parse.go b/parse.go index 56aaa92..469117d 100644 --- a/parse.go +++ b/parse.go @@ -105,6 +105,10 @@ func (p *parser) stmt() node { return p.def() } + if p.iscur(tkStruct) { + return p._struct() + } + if p.iscur(tkReturn) { return p._return() } @@ -294,6 +298,47 @@ func (p *parser) def() node { return n } +// struct = "struct" ident "{" ident-list? def-list? "}" +func (p *parser) _struct() node { + p.skipnewline() + n := &ndStruct{tok: p.cur} + p.must(tkStruct) + n.name = p.ident() + p.must(tkLBrace) + p.skipnewline() + + if p.iscur(tkRBrace) { + return n + } + + // read variables + for { + if p.iscur(tkDef) { + break + } + + if p.iscur(tkRBrace) { + break + } + + n.vars = append(n.vars, p.ident()) + p.skipnewline() + } + + // read functions + for { + if p.iscur(tkRBrace) { + break + } + + n.fns = append(n.fns, p.def()) + p.skipnewline() + } + + p.must(tkRBrace) + return n +} + // return = "return" expr func (p *parser) _return() node { p.skipnewline() diff --git a/process.go b/process.go index 60b936f..bd39a43 100644 --- a/process.go +++ b/process.go @@ -34,6 +34,9 @@ func process(mod *module, nd node) (procResult, shibaErr) { case *ndCondLoop: return procCondLoop(mod, n) + case *ndStruct: + return procStruct(mod, n) + case *ndFunDef: return procFunDef(mod, n) @@ -467,6 +470,31 @@ func procCondLoop(mod *module, n *ndCondLoop) (procResult, shibaErr) { return nil, nil } +func procStruct(mod *module, n *ndStruct) (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 + sd := &structdef{name: name} + + for _, v := range n.vars { + if _, ok := v.(*ndIdent); !ok { + return nil, &errSimple{msg: fmt.Sprintf("invalid variable name %s in struct %s", v, name), l: n.token().loc} + } + sd.vars = append(sd.vars, v.(*ndIdent).ident) + } + + for _, fn := range n.fns { + if _, err := process(mod, fn); err != nil { + return nil, err + } + } + + env.setstruct(mod, name, sd) + return nil, nil +} + func procFunDef(mod *module, n *ndFunDef) (procResult, shibaErr) { params := []string{} for _, p := range n.params { diff --git a/scope.go b/scope.go index 19bbc08..0de96d2 100644 --- a/scope.go +++ b/scope.go @@ -4,12 +4,14 @@ import "container/list" type scope struct { objs map[string]*obj + structdefs map[string]*structdef blockscopes *list.List } func newscope() *scope { return &scope{ objs: map[string]*obj{}, + structdefs: map[string]*structdef{}, blockscopes: list.New(), } } @@ -22,6 +24,23 @@ func (s *scope) delblockscope() { s.blockscopes.Remove(s.blockscopes.Back()) } +func (s *scope) setstruct(name string, sd *structdef) { + if s.blockscopes.Len() == 0 { + s.structdefs[name] = sd + return + } + + for e := s.blockscopes.Back(); e != nil; e = e.Prev() { + bs := e.Value.(*blockscope) + if _, ok := bs.structdefs[name]; ok { + bs.structdefs[name] = sd + return + } + } + + s.blockscopes.Back().Value.(*blockscope).structdefs[name] = sd +} + func (s *scope) setobj(name string, o *obj) { if s.blockscopes.Len() == 0 { s.objs[name] = o @@ -39,6 +58,18 @@ func (s *scope) setobj(name string, o *obj) { s.blockscopes.Back().Value.(*blockscope).objs[name] = o } +func (s *scope) getstruct(name string) (*structdef, bool) { + for e := s.blockscopes.Back(); e != nil; e = e.Prev() { + bs := e.Value.(*blockscope) + if sd, ok := bs.structdefs[name]; ok { + return sd, ok + } + } + + sd, ok := s.structdefs[name] + return sd, ok +} + func (s *scope) getobj(name string) (*obj, bool) { for e := s.blockscopes.Back(); e != nil; e = e.Prev() { bs := e.Value.(*blockscope) @@ -51,6 +82,11 @@ func (s *scope) getobj(name string) (*obj, bool) { return o, ok } +func (s *scope) getglobstruct(name string) (*structdef, bool) { + sd, ok := s.structdefs[name] + return sd, ok +} + func (s *scope) getglobobj(name string) (*obj, bool) { o, ok := s.objs[name] return o, ok @@ -58,8 +94,12 @@ func (s *scope) getglobobj(name string) (*obj, bool) { type blockscope struct { objs map[string]*obj + structdefs map[string]*structdef } func newblockscope() *blockscope { - return &blockscope{objs: map[string]*obj{}} + return &blockscope{ + objs: map[string]*obj{}, + structdefs: map[string]*structdef{}, + } } diff --git a/struct.go b/struct.go new file mode 100644 index 0000000..8665d83 --- /dev/null +++ b/struct.go @@ -0,0 +1,7 @@ +package main + +type structdef struct { + name string + vars []string + defs []*obj +} diff --git a/tests/struct.sb b/tests/struct.sb new file mode 100644 index 0000000..c1b24f4 --- /dev/null +++ b/tests/struct.sb @@ -0,0 +1,22 @@ +import assert + +as = assert.assert + +struct person{ + name + age + + def birthday() { + age += 1 + } + + def setname(n) { + name = n + } + + def getname() { + return name + } +} + +print("struct test succeeded") diff --git a/tokenize.go b/tokenize.go index f40da11..eb04847 100644 --- a/tokenize.go +++ b/tokenize.go @@ -63,6 +63,7 @@ const ( tkBreak // break tkReturn // return tkImport // import + tkStruct // struct tkIdent tkStr @@ -88,6 +89,7 @@ var keywords = []*strToTktype{ {"break", tkBreak}, {"return", tkReturn}, {"import", tkImport}, + {"struct", tkStruct}, } var punctuators = []*strToTktype{