Skip to content

Commit

Permalink
wip support struct initialization
Browse files Browse the repository at this point in the history
  • Loading branch information
hidetatz committed Sep 3, 2023
1 parent efe16d5 commit b16d491
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 12 deletions.
23 changes: 20 additions & 3 deletions node.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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
}
Expand Down
48 changes: 47 additions & 1 deletion obj.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const (
tStr
tList
tDict
tStruct
tBuiltinFunc
tGoStdModFunc
tFunc
Expand All @@ -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:
Expand All @@ -70,7 +73,7 @@ type obj struct {
dict *dict
mod *module

// builtin/func/gostdmodfunc
// builtin/func/gostdmodfunc/struct
name string

// builtin
Expand All @@ -83,6 +86,9 @@ type obj struct {
fmod *module
params []string
body []node

// struct
fields map[string]*obj
}

func (o *obj) update(x *obj) {
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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:
Expand Down
43 changes: 38 additions & 5 deletions parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -104,7 +112,7 @@ func (p *parser) stmt() node {
}

if p.iscur(tkStruct) {
return p._struct()
return p.structdef()
}

if p.iscur(tkReturn) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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? "]"
Expand Down
48 changes: 45 additions & 3 deletions process.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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}
}
Expand All @@ -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 {
Expand Down
10 changes: 10 additions & 0 deletions struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
3 changes: 3 additions & 0 deletions tests/struct.sb
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ struct person{
}
}

p = person{name: "alice", age: 3}
print(p)

print("struct test succeeded")
4 changes: 4 additions & 0 deletions token.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down

0 comments on commit b16d491

Please sign in to comment.