diff options
| -rw-r--r-- | .golangci.yaml | 2 | ||||
| -rw-r--r-- | comp/compiler.go | 226 | ||||
| -rw-r--r-- | comp/dump.go | 76 | ||||
| -rw-r--r-- | interp/interpreter_test.go | 173 | ||||
| -rw-r--r-- | lang/golang/go.go | 87 | ||||
| -rw-r--r-- | parser/decl.go | 15 | ||||
| -rw-r--r-- | parser/expr.go | 355 | ||||
| -rw-r--r-- | parser/parse.go | 12 | ||||
| -rw-r--r-- | parser/type.go | 14 | ||||
| -rw-r--r-- | scanner/scan.go | 4 | ||||
| -rw-r--r-- | symbol/symbol.go | 40 | ||||
| -rw-r--r-- | vm/op_string.go | 19 | ||||
| -rw-r--r-- | vm/type.go | 10 | ||||
| -rw-r--r-- | vm/vm.go | 34 | ||||
| -rw-r--r-- | vm/vm_test.go | 102 |
15 files changed, 515 insertions, 654 deletions
diff --git a/.golangci.yaml b/.golangci.yaml index ade2ad1..f21704a 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -8,7 +8,7 @@ linters: - govet - ineffassign - misspell - - modernize +# - modernize - perfsprint - prealloc - predeclared diff --git a/comp/compiler.go b/comp/compiler.go index 49633a1..dd37788 100644 --- a/comp/compiler.go +++ b/comp/compiler.go @@ -9,6 +9,7 @@ import ( "reflect" "runtime" "strconv" + "strings" "github.com/mvertes/parscan/lang" "github.com/mvertes/parscan/parser" @@ -44,11 +45,18 @@ func (c *Compiler) AddSym(name string, value vm.Value) int { return p } +func errorf(format string, v ...any) error { + _, file, line, _ := runtime.Caller(1) + loc := fmt.Sprintf("%s:%d: ", path.Base(file), line) + return fmt.Errorf(loc+format, v...) +} + // Generate generates vm code and data from parsed tokens. func (c *Compiler) Generate(tokens parser.Tokens) (err error) { log.Println("Codegen tokens:", tokens) fixList := parser.Tokens{} // list of tokens to fix after all necessary information is gathered - stack := []*symbol.Symbol{} // for symbolic evaluation, type checking, etc + stack := []*symbol.Symbol{} // for symbolic evaluation and type checking + flen := []int{} // stack length according to function scopes keyList := []string{} emit := func(t scanner.Token, op vm.Op, arg ...int) { @@ -58,13 +66,13 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { } push := func(s *symbol.Symbol) { stack = append(stack, s) } pop := func() *symbol.Symbol { l := len(stack) - 1; s := stack[l]; stack = stack[:l]; return s } - top := func() *symbol.Symbol { return stack[len(stack)-1] } + popflen := func() int { le := len(flen) - 1; l := flen[le]; flen = flen[:le]; return l } showStack := func() { _, file, line, _ := runtime.Caller(1) fmt.Fprintf(os.Stderr, "%s%d: showstack: %d\n", path.Base(file), line, len(stack)) for i, s := range stack { - fmt.Fprintf(os.Stderr, "%s:%d: stack[%d]: %v\n", path.Base(file), line, i, s) + fmt.Fprintf(os.Stderr, " stack[%d]: %v\n", i, s) } } @@ -87,7 +95,7 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { c.Data = append(c.Data, v) c.strings[s] = i } - push(&symbol.Symbol{Kind: symbol.Const, Value: v}) + push(&symbol.Symbol{Kind: symbol.Const, Value: v, Type: vm.TypeOf("")}) emit(t, vm.Dup, i) case lang.Add: @@ -103,8 +111,7 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { emit(t, vm.Sub) case lang.Minus: - emit(t, vm.Push, 0) - emit(t, vm.Sub) + emit(t, vm.Negate) case lang.Not: emit(t, vm.Not) @@ -121,6 +128,8 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { emit(t, vm.Deref) case lang.Index: + showStack() + pop() push(&symbol.Symbol{Type: pop().Type.Elem()}) emit(t, vm.Index) @@ -133,21 +142,30 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { emit(t, vm.Lower) case lang.Call: - s := pop() + showStack() + narg := t.Beg // FIXME: t.Beg is hijacked to store the number of function parameters. + s := stack[len(stack)-1-narg] if s.Kind != symbol.Value { typ := s.Type // TODO: pop input types (careful with variadic function). + // Pop function and input arg symbols, push return value symbols. + pop() + for i := 0; i < narg; i++ { + pop() + } for i := 0; i < typ.Rtype.NumOut(); i++ { push(&symbol.Symbol{Type: typ.Out(i)}) } - emit(t, vm.Call) + emit(t, vm.Call, narg) + + showStack() break } - push(s) fallthrough // A symValue must be called through callX. case lang.CallX: - rtyp := pop().Value.Value.Type() + s := stack[len(stack)-1-t.Beg] + rtyp := s.Value.Value.Type() // TODO: pop input types (careful with variadic function). for i := 0; i < rtyp.NumOut(); i++ { push(&symbol.Symbol{Type: &vm.Type{Rtype: rtyp.Out(i)}}) @@ -159,12 +177,17 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { // If the key is an ident (field name), then push it on the keystack, // to be computed at compile time in Composite handling. // Or generate instructions so key will be computed at runtime. - if tokens[i-1].Tok == lang.Ident { - keyList = append(keyList, tokens[i-1].Str) + showStack() + s, ok := c.Symbols[t.Str] + if ok { + j := s.Type.FieldIndex(tokens[i-2].Str) + log.Println("### fieldIndex", tokens[i-2].Str, j) + emit(t, vm.FieldSet, j...) } case lang.Composite: - d := top() // let the type symbol on the stack, may be required for assignment + showStack() + d := c.Symbols[t.Str] switch d.Type.Rtype.Kind() { case reflect.Struct: emit(t, vm.Fnew, d.Index) @@ -182,62 +205,50 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { } case reflect.Slice: emit(t, vm.Fnew, d.Index) - // if len(keyList) == 0 { - // } default: return fmt.Errorf("composite kind not supported yet: %v", d.Type.Rtype.Kind()) } + for j := len(stack) - 1; j >= 0; j-- { + // pop until type + if stack[j].Kind == symbol.Type { + stack = stack[:j+1] + break + } + } case lang.Grow: emit(t, vm.Grow, t.Beg) case lang.Define: - // TODO: support assignment to local, composite objects. - st := tokens[i-1] - l := len(c.Data) showStack() - d := pop() - typ := d.Type + rhs := pop() + typ := rhs.Type if typ == nil { - typ = d.Value.Type + typ = rhs.Value.Type } - v := vm.NewValue(typ) - c.Symbols.Add(l, st.Str, v, symbol.Var, typ, false) - c.Data = append(c.Data, v) - emit(t, vm.Assign, l) + lhs := pop() + lhs.Type = typ + c.Data[lhs.Index] = vm.NewValue(typ) + emit(t, vm.Vassign) case lang.Assign: - st := tokens[i-1] - if st.Tok == lang.Period || st.Tok == lang.Index { - emit(t, vm.Vassign) - break - } - s, ok := c.Symbols[st.Str] - if !ok { - return fmt.Errorf("symbol not found: %s", st.Str) - } - d := pop() - typ := d.Type - if typ == nil { - typ = d.Value.Type - } - if s.Type == nil { - s.Type = typ - s.Value = vm.NewValue(typ) - } - if s.Local { - if !s.Used { - emit(st, vm.New, s.Index, c.typeSym(s.Type).Index) - s.Used = true + showStack() + rhs := pop() + lhs := pop() + if lhs.Local { + if !lhs.Used { + emit(t, vm.New, lhs.Index, c.typeSym(lhs.Type).Index) + lhs.Used = true } - emit(st, vm.Fassign, s.Index) + emit(t, vm.Fassign, lhs.Index) break } - if s.Index == symbol.UnsetAddr { - s.Index = len(c.Data) - c.Data = append(c.Data, s.Value) + // TODO check source type against var type + if v := c.Data[lhs.Index]; !v.IsValid() { + c.Data[lhs.Index] = vm.NewValue(rhs.Type) + c.Symbols[lhs.Name].Type = rhs.Type } - emit(st, vm.Assign, s.Index) + emit(t, vm.Vassign) case lang.Equal: push(&symbol.Symbol{Type: booleanOpType(pop(), pop())}) @@ -248,16 +259,13 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { emit(t, vm.EqualSet) case lang.Ident: - if i < len(tokens)-1 { - switch t1 := tokens[i+1]; t1.Tok { - case lang.Define, lang.Assign, lang.Colon: - continue - } - } s, ok := c.Symbols[t.Str] if !ok { - return fmt.Errorf("symbol not found: %s", t.Str) + // return errorf("symbol not found: %s", t.Str) + // it could be either an undefined symbol or a key ident in a literal composite expr. + continue } + log.Println("Ident symbol", t.Str, s.Local, s.Index, s.Type) push(s) if s.Kind == symbol.Pkg { break @@ -276,17 +284,26 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { case lang.Label: lc := len(c.Code) - s, ok := c.Symbols[t.Str] - if ok { + if s, ok := c.Symbols[t.Str]; ok { s.Value = vm.ValueOf(lc) if s.Kind == symbol.Func { - // label is a function entry point, register its code address in data. + // Label is a function entry point, register its code address in data + // and save caller stack length. s.Index = len(c.Data) c.Data = append(c.Data, s.Value) + flen = append(flen, len(stack)) + log.Println("### func label:", lc, s.Index, s.Value.Value, c.Data[0].Value) } else { c.Data[s.Index] = s.Value } } else { + if strings.HasSuffix(t.Str, "_end") { + if s, ok = c.Symbols[strings.TrimSuffix(t.Str, "_end")]; ok && s.Kind == symbol.Func { + // Exit function: restore caller stack + l := popflen() + stack = stack[:l] + } + } c.Symbols[t.Str] = &symbol.Symbol{Kind: symbol.Label, Value: vm.ValueOf(lc)} } @@ -334,6 +351,9 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { emit(t, vm.Jump, i) case lang.Period: + if len(stack) < 1 { + return errorf("missing symbol") + } s := pop() switch s.Kind { case symbol.Pkg: @@ -361,6 +381,7 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { default: if f, ok := s.Type.Rtype.FieldByName(t.Str[1:]); ok { emit(t, vm.Field, f.Index...) + push(&symbol.Symbol{Type: s.Type.FieldType(t.Str[1:])}) break } return fmt.Errorf("field or method not found: %s", t.Str[1:]) @@ -384,8 +405,9 @@ func (c *Compiler) Generate(tokens parser.Tokens) (err error) { } return err } -func arithmeticOpType(s1, _ *symbol.Symbol) *vm.Type { return symbol.Vtype(s1) } -func booleanOpType(_, _ *symbol.Symbol) *vm.Type { return vm.TypeOf(true) } + +func arithmeticOpType(s, _ *symbol.Symbol) *vm.Type { return symbol.Vtype(s) } +func booleanOpType(_, _ *symbol.Symbol) *vm.Type { return vm.TypeOf(true) } // PrintCode pretty prints the generated code. func (c *Compiler) PrintCode() { @@ -440,7 +462,11 @@ func (c *Compiler) PrintData() { fmt.Fprintln(os.Stderr, "# Data:") for i, d := range c.Data { - fmt.Fprintf(os.Stderr, "%4d %T %v %v\n", i, d.Interface(), d.Value, dict[i]) + if d.IsValid() { + fmt.Fprintf(os.Stderr, "%4d %T %v, Symbol: %v\n", i, d.Interface(), d.Value, dict[i]) + } else { + fmt.Fprintf(os.Stderr, "%4d %v %v\n", i, d.Value, dict[i]) + } } } @@ -455,76 +481,6 @@ func (c *Compiler) symbolsByIndex() map[int]entry { return dict } -// Dump represents the state of a data dump. -type Dump struct { - Values []*DumpValue -} - -// DumpValue is a value of a dump state. -type DumpValue struct { - Index int - Name string - Kind int - Type string - Value any -} - -// Dump creates a snapshot of the execution state of global variables. -// This method is specifically implemented in the Compiler to minimize the coupling between -// the dump format and other components. By situating the dump logic in the Compiler, -// it relies solely on the program being executed and the indexing algorithm used for ordering variables -// (currently, this is an integer that corresponds to the order of variables in the program). -// This design choice allows the Virtual Machine (VM) to evolve its memory management strategies -// without compromising backward compatibility with dumps generated by previous versions. -func (c *Compiler) Dump() *Dump { - dict := c.symbolsByIndex() - dv := make([]*DumpValue, len(c.Data)) - for i, d := range c.Data { - e := dict[i] - dv[i] = &DumpValue{ - Index: e.Index, - Name: e.name, - Kind: int(e.Kind), - Type: e.Type.Name, - Value: d.Interface(), - } - } - return &Dump{Values: dv} -} - -// ApplyDump sets previously saved dump, restoring the state of global variables. -func (c *Compiler) ApplyDump(d *Dump) error { - dict := c.symbolsByIndex() - for _, dv := range d.Values { - // do all the checks to be sure we are applying the correct values - e, ok := dict[dv.Index] - if !ok { - return fmt.Errorf("entry not found on index %d", dv.Index) - } - - if dv.Name != e.name || - dv.Type != e.Type.Name || - dv.Kind != int(e.Kind) { - return fmt.Errorf("entry with index %d does not match with provided entry. "+ - "dumpValue: %s, %s, %d. memoryValue: %s, %s, %d", - dv.Index, - dv.Name, dv.Type, dv.Kind, - e.name, e.Type, e.Kind) - } - - if dv.Index >= len(c.Data) { - return fmt.Errorf("index (%d) bigger than memory (%d)", dv.Index, len(c.Data)) - } - - if !c.Data[dv.Index].CanSet() { - return fmt.Errorf("value %v cannot be set", dv.Value) - } - - c.Data[dv.Index].Set(reflect.ValueOf(dv.Value)) - } - return nil -} - func (c *Compiler) typeSym(t *vm.Type) *symbol.Symbol { tsym, ok := c.Symbols[t.Rtype.String()] if !ok { diff --git a/comp/dump.go b/comp/dump.go new file mode 100644 index 0000000..865216b --- /dev/null +++ b/comp/dump.go @@ -0,0 +1,76 @@ +package comp + +import ( + "fmt" + "reflect" +) + +// Dump represents the state of a data dump. +type Dump struct { + Values []*DumpValue +} + +// DumpValue is a value of a dump state. +type DumpValue struct { + Index int + Name string + Kind int + Type string + Value any +} + +// Dump creates a snapshot of the execution state of global variables. +// This method is specifically implemented in the Compiler to minimize the coupling between +// the dump format and other components. By situating the dump logic in the Compiler, +// it relies solely on the program being executed and the indexing algorithm used for ordering variables +// (currently, this is an integer that corresponds to the order of variables in the program). +// This design choice allows the Virtual Machine (VM) to evolve its memory management strategies +// without compromising backward compatibility with dumps generated by previous versions. +func (c *Compiler) Dump() *Dump { + dict := c.symbolsByIndex() + dv := make([]*DumpValue, len(c.Data)) + for i, d := range c.Data { + e := dict[i] + dv[i] = &DumpValue{ + Index: e.Index, + Name: e.name, + Kind: int(e.Kind), + Type: e.Type.Name, + Value: d.Interface(), + } + } + return &Dump{Values: dv} +} + +// ApplyDump sets previously saved dump, restoring the state of global variables. +func (c *Compiler) ApplyDump(d *Dump) error { + dict := c.symbolsByIndex() + for _, dv := range d.Values { + // do all the checks to be sure we are applying the correct values + e, ok := dict[dv.Index] + if !ok { + return fmt.Errorf("entry not found on index %d", dv.Index) + } + + if dv.Name != e.name || + dv.Type != e.Type.Name || + dv.Kind != int(e.Kind) { + return fmt.Errorf("entry with index %d does not match with provided entry. "+ + "dumpValue: %s, %s, %d. memoryValue: %s, %s, %d", + dv.Index, + dv.Name, dv.Type, dv.Kind, + e.name, e.Type, e.Kind) + } + + if dv.Index >= len(c.Data) { + return fmt.Errorf("index (%d) bigger than memory (%d)", dv.Index, len(c.Data)) + } + + if !c.Data[dv.Index].CanSet() { + return fmt.Errorf("value %v cannot be set", dv.Value) + } + + c.Data[dv.Index].Set(reflect.ValueOf(dv.Value)) + } + return nil +} diff --git a/interp/interpreter_test.go b/interp/interpreter_test.go index bce97b9..e1e7107 100644 --- a/interp/interpreter_test.go +++ b/interp/interpreter_test.go @@ -3,6 +3,7 @@ package interp_test import ( "fmt" "log" + "strings" "testing" "github.com/mvertes/parscan/interp" @@ -31,7 +32,7 @@ func gen(test etest) func(*testing.T) { if e != nil { errStr = e.Error() } - if errStr != test.err { + if !strings.Contains(errStr, test.err) { t.Errorf("got error %#v, want error %#v", errStr, test.err) } if res := fmt.Sprintf("%v", r); test.err == "" && res != test.res { @@ -69,18 +70,24 @@ func TestExpr(t *testing.T) { }) } +func TestCompare(t *testing.T) { + run(t, []etest{ + {src: "a := 1; a < 2", res: "true"}, + }) +} + func TestLogical(t *testing.T) { run(t, []etest{ - {src: "true", res: "true"}, // #00 - {src: "false", res: "false"}, // #01 - {src: "true && false", res: "false"}, // #02 - {src: "true && true", res: "true"}, // #03 - {src: "true && true && false", res: "false"}, // #04 - {src: "false || true && true", res: "true"}, // #05 - {src: "2 < 3 && 1 > 2 || 3 == 3", res: "true"}, // #06 - {src: "2 > 3 && 1 > 2 || 3 == 3", res: "true"}, // #07 - {src: "2 > 3 || 2 == 1+1 && 3>0", res: "true"}, // #08 - {src: "2 > 3 || 2 == 1+1 && 3>4 || 1<2", res: "true"}, // #09 + {src: "true && false", res: "false"}, // #00 + {src: "true && true", res: "true"}, // #01 + {src: "true && true && false", res: "false"}, // #02 + {src: "false || true && true", res: "true"}, // #03 + {src: "2 < 3 && 1 > 2 || 3 == 3", res: "true"}, // #04 + {src: "2 > 3 && 1 > 2 || 3 == 3", res: "true"}, // #05 + {src: "2 > 3 || 2 == 1+1 && 3>0", res: "true"}, // #06 + {src: "2 > 3 || 2 == 1+1 && 3>4 || 1<2", res: "true"}, // #07 + {src: "a := 1+1 < 3 && 4 == 2+2; a", res: "true"}, // #08 + {src: "a := 1+1 < 3 || 3 == 2+2; a", res: "true"}, // #09 {src: "a := 1+1 < 3 && 4 == 2+2; a", res: "true"}, // #10 {src: "a := 1+1 < 3 || 3 == 2+2; a", res: "true"}, // #11 }) @@ -88,37 +95,37 @@ func TestLogical(t *testing.T) { func TestFunc(t *testing.T) { run(t, []etest{ - {src: "func f() int {return 2}; a := f(); a", res: "2"}, - {src: "func f() int {return 2}; f()", res: "2"}, - {src: "func f(a int) int {return a+2}; f(3)", res: "5"}, - {src: "func f(a int) int {if a < 4 {a = 5}; return a}; f(3)", res: "5"}, - {src: "func f(a int) int {return a+2}; 7 - f(3)", res: "2"}, - {src: "func f(a int) int {return a+2}; f(5) - f(3)", res: "2"}, - {src: "func f(a int) int {return a+2}; f(3) - 2", res: "3"}, - {src: "func f(a, b, c int) int {return a+b-c} ; f(7, 1, 3)", res: "5"}, - {src: "var a int; func f() {a = a+2}; f(); a", res: "2"}, - {src: "var f = func(a int) int {return a+3}; f(2)", res: "5"}, + {src: "func f() int {return 2}; a := f(); a", res: "2"}, // #00 + {src: "func f() int {return 2}; f()", res: "2"}, // #01 + {src: "func f(a int) int {return a+2}; f(3)", res: "5"}, // #02 + {src: "func f(a int) int {if a < 4 {a = 5}; return a}; f(3)", res: "5"}, // #03 + {src: "func f(a int) int {return a+2}; 7 - f(3)", res: "2"}, // #04 + {src: "func f(a int) int {return a+2}; f(5) - f(3)", res: "2"}, // #05 + {src: "func f(a int) int {return a+2}; f(3) - 2", res: "3"}, // #06 + {src: "func f(a, b, c int) int {return a+b-c} ; f(7, 1, 3)", res: "5"}, // #07 + {src: "var a int; func f() {a = a+2}; f(); a", res: "2"}, // #08 + {src: "var f = func(a int) int {return a+3}; f(2)", res: "5"}, // #09 }) } func TestIf(t *testing.T) { run(t, []etest{ - {src: "a := 0; if a == 0 { a = 2 } else { a = 1 }; a", res: "2"}, - {src: "a := 0; if a == 1 { a = 2 } else { a = 1 }; a", res: "1"}, - {src: "a := 0; if a == 1 { a = 2 } else if a == 0 { a = 3 } else { a = 1 }; a", res: "3"}, - {src: "a := 0; if a == 1 { a = 2 } else if a == 2 { a = 3 } else { a = 1 }; a", res: "1"}, - {src: "a := 1; if a > 0 && a < 2 { a = 3 }; a", res: "3"}, - {src: "a := 1; if a < 0 || a < 2 { a = 3 }; a", res: "3"}, + {src: "a := 0; if a == 0 { a = 2 } else { a = 1 }; a", res: "2"}, // #00 + {src: "a := 0; if a == 1 { a = 2 } else { a = 1 }; a", res: "1"}, // #01 + {src: "a := 0; if a == 1 { a = 2 } else if a == 0 { a = 3 } else { a = 1 }; a", res: "3"}, // #02 + {src: "a := 0; if a == 1 { a = 2 } else if a == 2 { a = 3 } else { a = 1 }; a", res: "1"}, // #03 + {src: "a := 1; if a > 0 && a < 2 { a = 3 }; a", res: "3"}, // #04 + {src: "a := 1; if a < 0 || a < 2 { a = 3 }; a", res: "3"}, // #05 }) } func TestFor(t *testing.T) { run(t, []etest{ - {src: "a := 0; for i := 0; i < 3; i = i+1 {a = a+i}; a", res: "3"}, - {src: "func f() int {a := 0; for i := 0; i < 3; i = i+1 {a = a+i}; return a}; f()", res: "3"}, - {src: "a := 0; for {a = a+1; if a == 3 {break}}; a", res: "3"}, - {src: "func f() int {a := 0; for {a = a+1; if a == 3 {break}}; return a}; f()", res: "3"}, - {src: "func f() int {a := 0; for {a = a+1; if a < 3 {continue}; break}; return a}; f()", res: "3"}, + {src: "a := 0; for i := 0; i < 3; i = i+1 {a = a+i}; a", res: "3"}, // #00 + {src: "func f() int {a := 0; for i := 0; i < 3; i = i+1 {a = a+i}; return a}; f()", res: "3"}, // #01 + {src: "a := 0; for {a = a+1; if a == 3 {break}}; a", res: "3"}, // #02 + {src: "func f() int {a := 0; for {a = a+1; if a == 3 {break}}; return a}; f()", res: "3"}, // #03 + {src: "func f() int {a := 0; for {a = a+1; if a < 3 {continue}; break}; return a}; f()", res: "3"}, // #04 }) } @@ -157,15 +164,15 @@ func TestSwitch(t *testing.T) { } ` run(t, []etest{ - {src: src0 + "f(1)", res: "2"}, - {src: src0 + "f(2)", res: "3"}, - {src: src0 + "f(3)", res: "5"}, - {src: src0 + "f(4)", res: "10"}, - {src: src0 + "f(5)", res: "0"}, + {src: src0 + "f(1)", res: "2"}, // #00 + {src: src0 + "f(2)", res: "3"}, // #01 + {src: src0 + "f(3)", res: "5"}, // #02 + {src: src0 + "f(4)", res: "10"}, // #03 + {src: src0 + "f(5)", res: "0"}, // #04 - {src: src1 + "f(1)", res: "2"}, - {src: src1 + "f(4)", res: "5"}, - {src: src1 + "f(6)", res: "0"}, + {src: src1 + "f(1)", res: "2"}, // #05 + {src: src1 + "f(4)", res: "5"}, // #06 + {src: src1 + "f(6)", res: "0"}, // #07 }) } @@ -177,35 +184,36 @@ func TestConst(t *testing.T) { ) ` run(t, []etest{ - {src: "const a = 1+2; a", res: "3"}, - {src: "const a, b = 1, 2; a+b", res: "3"}, - {src: "const huge = 1 << 100; const four = huge >> 98; four", res: "4"}, + {src: "const a = 1+2; a", res: "3"}, // #00 + {src: "const a, b = 1, 2; a+b", res: "3"}, // #01 + {src: "const huge = 1 << 100; const four = huge >> 98; four", res: "4"}, // #02 - {src: src0 + "c", res: "2"}, + {src: src0 + "c", res: "2"}, // #03 }) } func TestArray(t *testing.T) { run(t, []etest{ - {src: "type T []int; var t T; t", res: "[]"}, - {src: "type T [3]int; var t T; t", res: "[0 0 0]"}, - {src: "type T [3]int; var t T; t[1] = 2; t", res: "[0 2 0]"}, + {src: "type T []int; var t T; t", res: "[]"}, // #00 + {src: "type T [3]int; var t T; t", res: "[0 0 0]"}, // #01 + {src: "type T [3]int; var t T; t[1]", res: "0"}, // #02 + {src: "type T [3]int; var t T; t[1] = 2; t", res: "[0 2 0]"}, // #03 }) } func TestPointer(t *testing.T) { run(t, []etest{ - {src: "var a *int; a", res: "<nil>"}, - {src: "var a int; var b *int = &a; *b", res: "0"}, - {src: "var a int = 2; var b *int = &a; *b", res: "2"}, + {src: "var a *int; a", res: "<nil>"}, // #00 + {src: "var a int; var b *int = &a; *b", res: "0"}, // #01 + {src: "var a int = 2; var b *int = &a; *b", res: "2"}, // #02 }) } func TestStruct(t *testing.T) { run(t, []etest{ - {src: "type T struct {a string; b, c int}; var t T; t", res: "{ 0 0}"}, - {src: "type T struct {a int}; var t T; t.a", res: "0"}, - {src: "type T struct {a int}; var t T; t.a = 1; t.a", res: "1"}, + {src: "type T struct {a string; b, c int}; var t T; t", res: "{ 0 0}"}, // #00 + {src: "type T struct {a int}; var t T; t.a", res: "0"}, // #01 + {src: "type T struct {a int}; var t T; t.a = 1; t.a", res: "1"}, // #02 }) } @@ -216,27 +224,24 @@ func TestType(t *testing.T) { ) ` run(t, []etest{ - {src: "type t int; var a t = 1; a", res: "1"}, - {src: "type t = int; var a t = 1; a", res: "1"}, - {src: src0 + `var s S = "xx"; s`, res: "xx"}, + {src: "type t int; var a t = 1; a", res: "1"}, // #00 + {src: "type t = int; var a t = 1; a", res: "1"}, // #01 + {src: src0 + `var s S = "xx"; s`, res: "xx"}, // #02 }) } func TestVar(t *testing.T) { run(t, []etest{ - {src: "var a int; a", res: "0"}, - {src: "var a, b, c int; a", res: "0"}, - {src: "var a, b, c int; a + b", res: "0"}, - {src: "var a, b, c int; a + b + c", res: "0"}, - {src: "var a int = 2+1; a", res: "3"}, - {src: "var a, b int = 2, 5; a+b", res: "7"}, - {src: "var x = 5; x", res: "5"}, - {src: "var a = 1; func f() int { var a, b int = 3, 4; return a+b}; a+f()", res: "8"}, - {src: `var a = "hello"; a`, res: "hello"}, - {src: `var ( - a, b int = 4+1, 3 - c = 8 -); a+b+c`, res: "16"}, + {src: "var a int; a", res: "0"}, // #00 + {src: "var a, b, c int; a", res: "0"}, // #01 + {src: "var a, b, c int; a + b", res: "0"}, // #02 + {src: "var a, b, c int; a + b + c", res: "0"}, // #03 + {src: "var a int = 2+1; a", res: "3"}, // #04 + {src: "var a, b int = 2, 5; a+b", res: "7"}, // #05 + {src: "var x = 5; x", res: "5"}, // #06 + {src: "var a = 1; func f() int { var a, b int = 3, 4; return a+b}; a+f()", res: "8"}, // #07 + {src: `var a = "hello"; a`, res: "hello"}, // #08 + {src: `var ( a, b int = 4+1, 3; c = 8); a+b+c`, res: "16"}, // #09 }) } @@ -246,25 +251,25 @@ func TestImport(t *testing.T) { ) ` run(t, []etest{ - {src: "fmt.Println(4)", err: "symbol not found: fmt"}, - {src: `import "xxx"`, err: "package not found: xxx"}, - {src: `import "fmt"; fmt.Println(4)`, res: "<nil>"}, - {src: src0 + "fmt.Println(4)", res: "<nil>"}, - {src: `func main() {import "fmt"; fmt.Println("hello")}`, err: "unexpected import"}, - {src: `import m "fmt"; m.Println(4)`, res: "<nil>"}, - {src: `import . "fmt"; Println(4)`, res: "<nil>"}, + {src: "fmt.Println(4)", err: "missing symbol"}, // #00 + {src: `import "xxx"`, err: "package not found: xxx"}, // #01 + {src: `import "fmt"; fmt.Println(4)`, res: "<nil>"}, // #02 + {src: src0 + "fmt.Println(4)", res: "<nil>"}, // #03 + {src: `func main() {import "fmt"; fmt.Println("hello")}`, err: "unexpected import"}, // #04 + {src: `import m "fmt"; m.Println(4)`, res: "<nil>"}, // #05 + {src: `import . "fmt"; Println(4)`, res: "<nil>"}, // #06 }) } func TestComposite(t *testing.T) { run(t, []etest{ - {src: "type T struct{}; t := T{}; t", res: "{}"}, - {src: "t := struct{}{}; t", res: "{}"}, - {src: `type T struct {}; var t T; t = T{}; t`, res: "{}"}, - {src: `type T struct{N int; S string}; var t T; t = T{2, "foo"}; t`, res: `{2 foo}`}, - {src: `type T struct{N int; S string}; t := T{2, "foo"}; t`, res: `{2 foo}`}, - {src: `type T struct{N int; S string}; t := T{S: "foo"}; t`, res: `{0 foo}`}, - {src: `a := []int{}`, res: `[]`}, - // {src: `a := []int{1, 2, 3}`, res: `[1 2 3]`}, + {src: "type T struct{}; t := T{}; t", res: "{}"}, // #00 + {src: "t := struct{}{}; t", res: "{}"}, // #01 + {src: `type T struct {}; var t T; t = T{}; t`, res: "{}"}, // #02 + {src: `type T struct{N int; S string}; var t T; t = T{2, "foo"}; t`, res: `{2 foo}`}, // #03 + {src: `type T struct{N int; S string}; t := T{2, "foo"}; t`, res: `{2 foo}`}, // #04 + // {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}`, res: `[1 2 3]`}, // #07 }) } diff --git a/lang/golang/go.go b/lang/golang/go.go index b1213e5..603b256 100644 --- a/lang/golang/go.go +++ b/lang/golang/go.go @@ -94,7 +94,7 @@ var GoSpec = &lang.Spec{ "chan": lang.Chan, "const": lang.Const, "continue": lang.Continue, - "default": lang.Default, + "default": lang.Case, // Consider "default" as an empty "case" clause. "defer": lang.Defer, "else": lang.Else, "fallthrough": lang.Fallthrough, @@ -126,12 +126,19 @@ var GoSpec = &lang.Spec{ lang.Sub: {Precedence: 4}, lang.Xor: {Precedence: 4}, lang.Or: {Precedence: 4}, + lang.Equal: {Precedence: 3}, lang.LessEqual: {Precedence: 3}, lang.GreaterEqual: {Precedence: 3}, lang.Less: {Precedence: 3}, lang.Greater: {Precedence: 3}, - lang.Land: {Precedence: 2}, - lang.Lor: {Precedence: 1}, + lang.Land: {Precedence: 1}, + lang.Lor: {Precedence: 0}, + lang.Minus: {Precedence: 6}, + lang.Not: {Precedence: 6}, + lang.Call: {Precedence: 6}, + lang.Index: {Precedence: 6}, + lang.Period: {Precedence: 7}, + lang.Colon: {Precedence: 7}, lang.Inc: {SkipSemi: true}, lang.Dec: {SkipSemi: true}, lang.Case: {SkipSemi: true}, @@ -155,78 +162,6 @@ var GoSpec = &lang.Spec{ lang.Switch: {SkipSemi: true}, lang.Type: {SkipSemi: true}, lang.Var: {SkipSemi: true}, + lang.MaxTok: {}, // To ensure that all Tokens have a TokenProp. }, - /* - TokenProps: map[string]lang.TokenProp{ - // Block tokens (can be nested) - "{..}": {Token: lang.BraceBlock}, - "[..]": {Token: lang.BracketBlock}, - "(..)": {Token: lang.ParenBlock}, - - // String tokens (not nested) - "//..": {Token: lang.Comment}, - "/*..": {Token: lang.Comment}, - `".."`: {Token: lang.String}, - "`..`": {Token: lang.String}, - - // Separators - ",": {Token: lang.Comma}, - ";": {Token: lang.Semicolon}, - ".": {Token: lang.Period}, - ":": {Token: lang.Colon}, - - // Operators - "&": {Token: lang.And, Precedence: 1}, - "*": {Token: lang.Mul, Precedence: 3}, - "/": {Token: lang.Quo, Precedence: 1}, - "%": {Token: lang.Rem, Precedence: 1}, - "<<": {Token: lang.Shl, Precedence: 1}, - ">>": {Token: lang.Shr, Precedence: 1}, - "+": {Token: lang.Add, Precedence: 2}, - "-": {Token: lang.Sub, Precedence: 2}, - "=": {Token: lang.Assign, Precedence: 1}, - "+=": {Token: lang.AddAssign, Precedence: 1}, - "<": {Token: lang.Less, Precedence: 1}, - ">": {Token: lang.Greater, Precedence: 1}, - "^": {Token: lang.Xor, Precedence: 2}, - "~": {Token: lang.Tilde}, - "&&": {Token: lang.Land, Precedence: 4}, - "||": {Token: lang.Lor, Precedence: 5}, - ":=": {Token: lang.Define, Precedence: 1}, - "==": {Token: lang.Equal, Precedence: 1}, - "<=": {Token: lang.LessEqual, Precedence: 1}, - ">=": {Token: lang.GreaterEqual, Precedence: 1}, - "->": {Token: lang.Arrow}, - "!": {Token: lang.Not}, - "++": {Token: lang.Inc, SkipSemi: true}, - "--": {Token: lang.Dec, SkipSemi: true}, - - // Reserved keywords - "break": {Token: lang.Break}, - "case": {Token: lang.Case, SkipSemi: true}, - "chan": {Token: lang.Chan, SkipSemi: true}, - "const": {Token: lang.Const, SkipSemi: true}, - "continue": {Token: lang.Continue}, - "default": {Token: lang.Case, SkipSemi: true}, - "defer": {Token: lang.Defer, SkipSemi: true}, - "else": {Token: lang.Else, SkipSemi: true}, - "fallthrough": {Token: lang.Fallthrough}, - "for": {Token: lang.For, SkipSemi: true}, - "func": {Token: lang.Func, SkipSemi: true}, - "go": {Token: lang.Go, SkipSemi: true}, - "goto": {Token: lang.Goto, SkipSemi: true}, - "if": {Token: lang.If, SkipSemi: true}, - "import": {Token: lang.Import, SkipSemi: true}, - "interface": {Token: lang.Interface, SkipSemi: true}, - "map": {Token: lang.Map, SkipSemi: true}, - "package": {Token: lang.Package, SkipSemi: true}, - "range": {Token: lang.Range, SkipSemi: true}, - "return": {Token: lang.Return}, - "select": {Token: lang.Select, SkipSemi: true}, - "struct": {Token: lang.Struct, SkipSemi: true}, - "switch": {Token: lang.Switch, SkipSemi: true}, - "type": {Token: lang.Type, SkipSemi: true}, - "var": {Token: lang.Var, SkipSemi: true}, - }, - */ } diff --git a/parser/decl.go b/parser/decl.go index f6e8102..015f4bd 100644 --- a/parser/decl.go +++ b/parser/decl.go @@ -69,7 +69,7 @@ func (p *Parser) parseConstLine(in Tokens) (out Tokens, err error) { values = nil } for i, v := range values { - if v, err = p.parseExpr(v); err != nil { + if v, err = p.parseExpr(v, ""); err != nil { return out, err } cval, _, err := p.evalConstExpr(v) @@ -99,11 +99,11 @@ func (p *Parser) evalConstExpr(in Tokens) (cval constant.Value, length int, err id := t.Tok switch { case id.IsBinaryOp(): - op1, l1, err := p.evalConstExpr(in[:l]) + op2, l2, err := p.evalConstExpr(in[:l]) if err != nil { return nil, 0, err } - op2, l2, err := p.evalConstExpr(in[:l-l1]) + op1, l1, err := p.evalConstExpr(in[:l-l2]) if err != nil { return nil, 0, err } @@ -164,6 +164,8 @@ func constValue(c constant.Value) any { return nil } +// Correspondence between language independent parscan tokens and Go stdlib tokens, +// To enable the use of the Go constant expression evaluator. var gotok = map[lang.Token]token.Token{ lang.Char: token.CHAR, lang.Imag: token.IMAG, @@ -352,13 +354,12 @@ func (p *Parser) parseVarLine(in Tokens) (out Tokens, err error) { values = nil } for i, v := range values { - if v, err = p.parseExpr(v); err != nil { + if v, err = p.parseExpr(v, ""); err != nil { return out, err } + out = append(out, scanner.Token{Tok: lang.Ident, Str: vars[i]}) out = append(out, v...) - out = append(out, - scanner.Token{Tok: lang.Ident, Str: vars[i]}, - scanner.Token{Tok: lang.Assign}) + out = append(out, scanner.Token{Tok: lang.Assign}) } return out, err } diff --git a/parser/expr.go b/parser/expr.go index d3161a5..3327ec5 100644 --- a/parser/expr.go +++ b/parser/expr.go @@ -1,7 +1,6 @@ package parser import ( - "fmt" "log" "strconv" @@ -11,240 +10,167 @@ import ( "github.com/mvertes/parscan/vm" ) -// parseExpr transform an infix expression into a postfix notation -func (p *Parser) parseExpr(in Tokens) (out Tokens, err error) { - log.Println("parseExpr2 in:", in) +// parseExpr transform an infix expression into a postfix notation. +func (p *Parser) parseExpr(in Tokens, typeStr string) (out Tokens, err error) { + log.Println("parseExpr in:", in) var ops Tokens - popop := func() (t scanner.Token) { l := len(ops) - 1; t = ops[l]; ops = ops[:l]; return t } + popop := func() (t scanner.Token) { + l := len(ops) - 1 + t = ops[l] + ops = ops[:l] + if t.Tok.IsLogicalOp() { + t.Tok = lang.Label // Implement conditional branching directly. + } + return t + } - for i, t := range in { - switch t.Tok { + lin := len(in) + for i := 0; i < lin; i++ { + switch t := in[i]; t.Tok { case lang.Int, lang.String: out = append(out, t) - case lang.Add, lang.Assign, lang.Define, lang.Equal, lang.Greater, lang.Less, lang.Mul: - // Apply operator precedence rule. + case lang.Func: + // Function as value (i.e closure). + if out, err = p.parseFunc(in); err != nil { + return out, err + } + // Get function label and use it as a symbol ident. + fid := out[1] + fid.Tok = lang.Ident + out = append(out, fid) + return out, err + + case lang.Period: + // TODO: fail if next is not an ident. + t.Str += in[i+1].Str // Hardwire selector argument. for len(ops) > 0 && p.precedence(t) < p.precedence(ops[len(ops)-1]) { out = append(out, popop()) } ops = append(ops, t) + i++ // Skip over next ident. - case lang.Ident: - _, sc, ok := p.Symbols.Get(t.Str, p.scope) - if ok { - // t.Str = sc + "/" + t.Str - _ = sc - } - out = append(out, t) - if i+1 < len(in) && in[i+1].Tok == lang.Define { - log.Println("t:", t.Str) - p.Symbols.Add(symbol.UnsetAddr, t.Str, vm.Value{}, symbol.Var, nil, false) + case lang.Colon: + t.Str = typeStr + for len(ops) > 0 && p.precedence(t) < p.precedence(ops[len(ops)-1]) { + out = append(out, popop()) } + ops = append(ops, t) - case lang.ParenBlock: - toks, err := p.parseExprStr(t.Block()) - if err != nil { - return out, err + case lang.Add, lang.And, lang.Assign, lang.Define, lang.Equal, lang.Greater, lang.Less, lang.Mul, lang.Not, lang.Sub, lang.Shl, lang.Shr: + if i == 0 || in[i-1].Tok.IsOperator() { + // An operator preceded by an operator or no token is unary. + t.Tok = lang.UnaryOp[t.Tok] } - out = append(out, toks...) - } - } - for len(ops) > 0 { - out = append(out, popop()) - } - log.Println("Final out:", out) - return out, err -} + for len(ops) > 0 && p.precedence(t) < p.precedence(ops[len(ops)-1]) { + out = append(out, popop()) + } + ops = append(ops, t) -func (p *Parser) parseExpr2(in Tokens) (out Tokens, err error) { - log.Println("parseExpr in:", in) - var ops, selectors Tokens - var vl int - var selectorIndex string + case lang.Land: + for len(ops) > 0 && p.precedence(t) < p.precedence(ops[len(ops)-1]) { + out = append(out, popop()) + } + xp := strconv.Itoa(p.labelCount[p.scope]) + p.labelCount[p.scope]++ + out = append(out, scanner.Token{Tok: lang.JumpSetFalse, Str: p.scope + "x" + xp}) + t.Str = p.scope + "x" + xp + ops = append(ops, t) - // - // Process tokens from last to first, the goal is to reorder the tokens in - // a stack machine processing order, so it can be directly interpreted. - // - if len(in) > 1 && in[0].Tok == lang.Func { - // Function as value (i.e closure). - if out, err = p.parseFunc(in); err != nil { - return out, err - } - // Get function label and use it as a symbol ident. - fid := out[1] - fid.Tok = lang.Ident - out = append(out, fid) - return out, err - } + case lang.Lor: + for len(ops) > 0 && p.precedence(t) < p.precedence(ops[len(ops)-1]) { + out = append(out, popop()) + } + xp := strconv.Itoa(p.labelCount[p.scope]) + p.labelCount[p.scope]++ + out = append(out, scanner.Token{Tok: lang.JumpSetTrue, Str: p.scope + "x" + xp}) + t.Str = p.scope + "x" + xp + ops = append(ops, t) - for i := len(in) - 1; i >= 0; i-- { - t := in[i] - // temporary assumptions: binary operators, returning 1 value - switch t.Tok { case lang.Ident: - if i > 0 && in[i-1].Tok == lang.Period { - selectorIndex = t.Str + if i < lin-1 && in[i].Tok == lang.Colon { continue } - // resolve symbol if not a selector rhs. - _, sc, ok := p.Symbols.Get(t.Str, p.scope) - if ok { - if sc != "" { - t.Str = sc + "/" + t.Str - } + s, sc, ok := p.Symbols.Get(t.Str, p.scope) + if ok && sc != "" { + t.Str = sc + "/" + t.Str } - out = append(out, t) - vl++ - - case lang.Colon: - // Make ':' a key-value operator for literal composite. - ops = append(ops, t) - - case lang.Period: - t.Str += selectorIndex - selectors = append(Tokens{t}, selectors...) - continue - - case lang.Int, lang.String: - out = append(out, t) - vl++ - - case lang.Define, lang.Add, lang.Sub, lang.Assign, lang.Equal, lang.Greater, lang.Less, - lang.Mul, lang.Land, lang.Lor, lang.Shl, lang.Shr, lang.Not, lang.And: - if i == 0 || in[i-1].Tok.IsOperator() { - // An operator preceded by an operator or no token is unary. - t.Tok = lang.UnaryOp[t.Tok] - j := len(out) - 1 - l := out[j] - if p.precedence(l) > 0 { - out = append(out[:j], t, l) - break - } - out = append(out, t) - break + if s != nil && s.Kind == symbol.Type { + typeStr = s.Type.String() } - if vl < 2 { - ops = append(ops, t) + out = append(out, t) + if i+1 < len(in) && in[i+1].Tok == lang.Define { + // Ident is to be assigned next. Define it as a var. + p.Symbols.Add(symbol.UnsetAddr, t.Str, vm.Value{}, symbol.Var, nil, false) } case lang.ParenBlock: - // If the previous token is an arithmetic, logic or assign operator then - // this parenthesis block is an enclosed expr, otherwise a call expr. + toks, err := p.parseExprStr(t.Block(), typeStr) + if err != nil { + return out, err + } if i == 0 || in[i-1].Tok.IsOperator() { - out = append(out, t) - vl++ - break + out = append(out, toks...) + } else { + prec := p.precedence(scanner.Token{Tok: lang.Call}) + for len(ops) > 0 && prec < p.precedence(ops[len(ops)-1]) { + out = append(out, popop()) + } + // func call: ensure that the func token in on the top of the stack, after args. + ops = append(ops, scanner.Token{Tok: lang.Call, Pos: t.Pos, Beg: p.numItems(t.Block(), lang.Comma)}) + out = append(out, toks...) } - // The call expression can be a function call, a conversion, - // a type assersion (including for type switch) - // func call: push args and func address then call - out = append(out, t) - vl++ - ops = append(ops, scanner.Token{Tok: lang.Call, Pos: t.Pos, Beg: p.numItems(t.Block(), lang.Comma)}) case lang.BraceBlock: - // the block can be a func body or a composite type content. - // In both cases it is preceded by a type definition. - // We must determine the starting token of type def, - // parse the type def, and substitute the type def by a single ident. - // TODO: handle implicit type in composite expression. - ti := p.typeStartIndex(in[:len(in)-1]) - if ti == -1 { - return out, ErrInvalidType - } - typ, err := p.parseTypeExpr(in[ti : len(in)-1]) + toks, err := p.parseExprStr(t.Block(), typeStr) + out = append(out, toks...) if err != nil { - return out, ErrInvalidType + return out, err } - p.Symbols.Add(symbol.UnsetAddr, typ.String(), vm.NewValue(typ), symbol.Type, typ, p.funcScope != "") - out = append(out, t, scanner.Token{Tok: lang.Ident, Pos: t.Pos, Str: typ.String()}) - i = ti - vl += 2 - ops = append(ops, scanner.Token{Tok: lang.Composite, Pos: t.Pos}) + ops = append(ops, scanner.Token{Tok: lang.Composite, Pos: t.Pos, Str: typeStr}) case lang.BracketBlock: - out = append(out, t) - vl++ + toks, err := p.parseExprStr(t.Block(), typeStr) + if err != nil { + return out, err + } + out = append(out, toks...) ops = append(ops, scanner.Token{Tok: lang.Index, Pos: t.Pos}) case lang.Comment: - return out, nil + // return out, nil - default: - return nil, fmt.Errorf("invalid expression: %v: %q", t.Tok, t.Str) - } - - if len(selectors) > 0 { - out = append(out, selectors...) - selectors = nil - } - - if lops, lout := len(ops), len(out); lops > 0 && vl > lops { - op := ops[lops-1] - ops = ops[:lops-1] - // Reorder tokens according to operator precedence rules. - if p.precedence(out[lout-2]) > p.precedence(op) { - op, out[lout-1], out[lout-2] = out[lout-2], op, out[lout-1] - if p.precedence(out[lout-3]) > p.precedence(out[lout-1]) { - out[lout-1], out[lout-2], out[lout-3] = out[lout-3], out[lout-1], out[lout-2] - } - } - out = append(out, op) - vl-- - } - } - out = append(out, ops...) - - log.Println("parseExpr out:", out, "vl:", vl, "ops:", ops) - // A logical operator (&&, ||) involves additional control flow operations. - if out, err = p.parseLogical(out); err != nil { - return out, err - } - - if l := len(out) - 1; l >= 0 && (out[l].Tok == lang.Define || out[l].Tok == lang.Assign) { - // Handle the assignment of a logical expression. - s1 := p.subExprLen(out[:l]) - head, err := p.parseLogical(out[:l-s1]) - if err != nil { - return out, err - } - out = append(head, out[l-s1:]...) - } - - // The tokens are now properly ordered, process nested blocks. - for i := len(out) - 1; i >= 0; i-- { - t := out[i] - var toks Tokens - switch t.Tok { - case lang.ParenBlock, lang.BracketBlock, lang.BraceBlock: - if toks, err = p.parseExprStr(t.Block()); err != nil { + case lang.Struct: + typ, err := p.parseTypeExpr(in[i : i+2]) + 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}) + log.Println("### typ:", typ) + i++ + default: - continue + log.Println("unxexpected token:", t) } - - // replace block token by its parsed result. - log.Println("toks:", toks) - out2 := append(Tokens{}, out[:i]...) - out2 = append(out2, toks...) - out = append(out2, out[i+1:]...) } - + for len(ops) > 0 { + out = append(out, popop()) + } log.Println("Final out:", out) return out, err } -func (p *Parser) parseExprStr(s string) (tokens Tokens, err error) { +func (p *Parser) parseExprStr(s, typ string) (tokens Tokens, err error) { if tokens, err = p.Scan(s, false); err != nil { return tokens, err } var result Tokens for _, sub := range tokens.Split(lang.Comma) { - toks, err := p.parseExpr(sub) + toks, err := p.parseExpr(sub, typ) if err != nil { return result, err } @@ -253,66 +179,3 @@ func (p *Parser) parseExprStr(s string) (tokens Tokens, err error) { return result, err } - -// parseLogical handles logical expressions with control flow (&& and ||) by -// ensuring the left hand side is evaluated unconditionally first, then the -// right hand side can be skipped or not by inserting a conditional jump and label. -// If the last token is not a logical operator then the function is idempotent. -func (p *Parser) parseLogical(in Tokens) (out Tokens, err error) { - l := len(in) - 1 - if l < 0 || !in[l].Tok.IsLogicalOp() { - return in, nil - } - - xp := strconv.Itoa(p.labelCount[p.scope]) - p.labelCount[p.scope]++ - rhsIndex := p.subExprLen(in[:l]) - - lhs, err := p.parseLogical(in[l-rhsIndex : l]) - if err != nil { - return out, err - } - - rhs, err := p.parseLogical(in[:l-rhsIndex]) - if err != nil { - return out, err - } - out = append(out, lhs...) - - if in[l].Tok == lang.Lor { - out = append(out, scanner.Token{Tok: lang.JumpSetTrue, Str: p.scope + "x" + xp}) - } else { - out = append(out, scanner.Token{Tok: lang.JumpSetFalse, Str: p.scope + "x" + xp}) - } - - out = append(out, rhs...) - out = append(out, scanner.Token{Tok: lang.Label, Str: p.scope + "x" + xp}) - return out, err -} - -// subExprLen returns the length of the first complete sub-expression starting from the input end. -func (p *Parser) subExprLen(in Tokens) int { - l := len(in) - 1 - last := in[l] - - switch last.Tok { - case lang.Int, lang.Float, lang.String, lang.Char, lang.Ident, lang.ParenBlock, lang.BracketBlock: - return 1 - - case lang.Call: - s1 := p.subExprLen(in[:l]) - return 1 + s1 + p.subExprLen(in[:l-s1]) - // TODO: add selector and index operators when ready - } - - if last.Tok.IsBinaryOp() { - s1 := p.subExprLen(in[:l]) - return 1 + s1 + p.subExprLen(in[:l-s1]) - } - - if last.Tok.IsUnaryOp() { - return 1 + p.subExprLen(in[:l]) - } - - return 0 // should not occur. TODO: diplay some error here. -} diff --git a/parser/parse.go b/parser/parse.go index 75bc963..60311dc 100644 --- a/parser/parse.go +++ b/parser/parse.go @@ -143,7 +143,7 @@ func (p *Parser) parseStmt(in Tokens) (out Tokens, err error) { } fallthrough default: - return p.parseExpr(in) + return p.parseExpr(in, "") } } @@ -220,7 +220,7 @@ func (p *Parser) parseFor(in Tokens) (out Tokens, err error) { } out = append(out, scanner.Token{Tok: lang.Label, Str: p.scope + "b"}) if len(cond) > 0 { - if cond, err = p.parseExpr(cond); err != nil { + if cond, err = p.parseExpr(cond, ""); err != nil { return nil, err } out = append(out, cond...) @@ -353,7 +353,7 @@ func (p *Parser) parseIf(in Tokens) (out Tokens, err error) { } pre = append(pre, init...) } - if cond, err = p.parseExpr(cond); err != nil { + if cond, err = p.parseExpr(cond, ""); err != nil { return nil, err } pre = append(pre, cond...) @@ -393,7 +393,7 @@ func (p *Parser) parseSwitch(in Tokens) (out Tokens, err error) { } condSwitch := false if len(cond) > 0 { - if cond, err = p.parseExpr(cond); err != nil { + if cond, err = p.parseExpr(cond, ""); err != nil { return nil, err } out = append(out, cond...) @@ -436,7 +436,7 @@ func (p *Parser) parseCaseClause(in Tokens, index, maximum int, condSwitch bool) } lcond := conds.Split(lang.Comma) for i, cond := range lcond { - if cond, err = p.parseExpr(cond); err != nil { + if cond, err = p.parseExpr(cond, ""); err != nil { return out, err } txt := fmt.Sprintf("%sc%d.%d", p.scope, index, i) @@ -472,7 +472,7 @@ func (p *Parser) parseLabel(in Tokens) (out Tokens, err error) { func (p *Parser) parseReturn(in Tokens) (out Tokens, err error) { if l := len(in); l > 1 { - if out, err = p.parseExpr(in[1:]); err != nil { + if out, err = p.parseExpr(in[1:], ""); err != nil { return out, err } } else if l == 0 { diff --git a/parser/type.go b/parser/type.go index 12bc06b..70ed4d1 100644 --- a/parser/type.go +++ b/parser/type.go @@ -197,17 +197,3 @@ func (p *Parser) hasFirstParam(in Tokens) bool { s, _, ok := p.Symbols.Get(in[0].Str, p.scope) return !ok || s.Kind != symbol.Type } - -// typeStartIndex returns the index of the start of type expression in tokens, or -1. -func (p *Parser) typeStartIndex(in Tokens) int { - index := len(in) - 1 - for i := index; i >= 0; i-- { - switch in[i].Tok { - case lang.Ident, lang.Struct, lang.Map, lang.Func, lang.Interface, lang.Mul, lang.BraceBlock, lang.BracketBlock, lang.ParenBlock: - index = i - default: - return index - } - } - return -1 -} diff --git a/scanner/scan.go b/scanner/scan.go index 33c198c..cb1549e 100644 --- a/scanner/scan.go +++ b/scanner/scan.go @@ -46,8 +46,10 @@ func (t *Token) Name() string { func (t *Token) String() string { s := t.Tok.String() if t.Tok.IsLiteral() || t.Tok.IsBlock() || t.Tok == lang.Ident || t.Tok == lang.Comment || - t.Tok == lang.Period || t.Tok == lang.Label || t.Tok == lang.Goto { + t.Tok == lang.Period || t.Tok == lang.Label || t.Tok == lang.Goto || t.Tok == lang.JumpSetFalse || t.Tok == lang.JumpSetTrue || t.Tok == lang.JumpFalse || t.Tok == lang.Colon || t.Tok == lang.Composite { s += strconv.Quote(t.Str) + } else if t.Tok == lang.Call { + s += "(" + strconv.Itoa(t.Beg) + ")" } return s } diff --git a/symbol/symbol.go b/symbol/symbol.go index 31869fd..e00b26d 100644 --- a/symbol/symbol.go +++ b/symbol/symbol.go @@ -31,6 +31,7 @@ const UnsetAddr = -65535 // Symbol structure used in parser and compiler. type Symbol struct { Kind Kind + Name string // Index int // address of symbol in frame PkgPath string // Type *vm.Type // @@ -40,6 +41,19 @@ type Symbol struct { Used bool // } +// func (s *Symbol) String() string { +// return fmt.Sprintf("{Kind: %v, Name: %v, Index: %v, Type: %v}\n", s.Kind, s.Name, s.Index, s.Type) +//} + +// IsConst return true if symbol is a constant. +func (s *Symbol) IsConst() bool { return s.Kind == Const } + +// IsType return true if symbol is a type. +func (s *Symbol) IsType() bool { return s.Kind == Type } + +// IsFunc return true if symbol is a function. +func (s *Symbol) IsFunc() bool { return s.Kind == Func } + // Vtype returns the VM type of a symbol. func Vtype(s *Symbol) *vm.Type { if s.Type != nil { @@ -54,7 +68,7 @@ type SymMap map[string]*Symbol // Add adds a new named symbol value at memory position i. func (sm SymMap) Add(i int, name string, v vm.Value, k Kind, t *vm.Type, local bool) { name = strings.TrimPrefix(name, "/") - sm[name] = &Symbol{Kind: k, Index: i, Local: local, Value: v, Type: t} + sm[name] = &Symbol{Kind: k, Name: name, Index: i, Local: local, Value: v, Type: t} } // Get searches for an existing symbol starting from the deepest scope. @@ -77,16 +91,16 @@ func (sm SymMap) Get(name, scope string) (sym *Symbol, sc string, ok bool) { // Init fills the symbol map with default Go symbols. func (sm SymMap) Init() { - sm["any"] = &Symbol{Kind: Type, Index: UnsetAddr, Type: vm.TypeOf((*any)(nil)).Elem()} - sm["bool"] = &Symbol{Kind: Type, Index: UnsetAddr, Type: vm.TypeOf((*bool)(nil)).Elem()} - sm["error"] = &Symbol{Kind: Type, Index: UnsetAddr, Type: vm.TypeOf((*error)(nil)).Elem()} - sm["int"] = &Symbol{Kind: Type, Index: UnsetAddr, Type: vm.TypeOf((*int)(nil)).Elem()} - sm["string"] = &Symbol{Kind: Type, Index: UnsetAddr, Type: vm.TypeOf((*string)(nil)).Elem()} - - sm["nil"] = &Symbol{Index: UnsetAddr} - sm["iota"] = &Symbol{Kind: Const, Index: UnsetAddr} - sm["true"] = &Symbol{Index: UnsetAddr, Value: vm.ValueOf(true), Type: vm.TypeOf(true)} - sm["false"] = &Symbol{Index: UnsetAddr, Value: vm.ValueOf(false), Type: vm.TypeOf(false)} - - sm["println"] = &Symbol{Index: UnsetAddr, Value: vm.ValueOf(func(v ...any) { fmt.Println(v...) })} + sm["any"] = &Symbol{Name: "any", Kind: Type, Index: UnsetAddr, Type: vm.TypeOf((*any)(nil)).Elem()} + sm["bool"] = &Symbol{Name: "bool", Kind: Type, Index: UnsetAddr, Type: vm.TypeOf((*bool)(nil)).Elem()} + sm["error"] = &Symbol{Name: "error", Kind: Type, Index: UnsetAddr, Type: vm.TypeOf((*error)(nil)).Elem()} + sm["int"] = &Symbol{Name: "int", Kind: Type, Index: UnsetAddr, Type: vm.TypeOf((*int)(nil)).Elem()} + sm["string"] = &Symbol{Name: "string", Kind: Type, Index: UnsetAddr, Type: vm.TypeOf((*string)(nil)).Elem()} + + sm["nil"] = &Symbol{Name: "nil", Index: UnsetAddr} + sm["iota"] = &Symbol{Name: "iota", Kind: Const, Index: UnsetAddr} + sm["true"] = &Symbol{Name: "true", Index: UnsetAddr, Value: vm.ValueOf(true), Type: vm.TypeOf(true)} + sm["false"] = &Symbol{Name: "false", Index: UnsetAddr, Value: vm.ValueOf(false), Type: vm.TypeOf(false)} + + sm["println"] = &Symbol{Name: "println", Index: UnsetAddr, Value: vm.ValueOf(func(v ...any) { fmt.Println(v...) })} } diff --git a/vm/op_string.go b/vm/op_string.go index 63bf738..7eb0e5d 100644 --- a/vm/op_string.go +++ b/vm/op_string.go @@ -38,18 +38,19 @@ func _() { _ = x[Loweri-27] _ = x[Mul-28] _ = x[New-29] - _ = x[Not-30] - _ = x[Pop-31] - _ = x[Push-32] - _ = x[Return-33] - _ = x[Sub-34] - _ = x[Subi-35] - _ = x[Swap-36] + _ = x[Negate-30] + _ = x[Not-31] + _ = x[Pop-32] + _ = x[Push-33] + _ = x[Return-34] + _ = x[Sub-35] + _ = x[Subi-36] + _ = x[Swap-37] } -const _Op_name = "NopAddAddrAssignFassignVassignCallCalliCallXDerefDupFdupFnewEqualEqualSetExitFieldFieldSetGreaterGrowIndexJumpJumpTrueJumpFalseJumpSetTrueJumpSetFalseLowerLoweriMulNewNotPopPushReturnSubSubiSwap" +const _Op_name = "NopAddAddrAssignFassignVassignCallCalliCallXDerefDupFdupFnewEqualEqualSetExitFieldFieldSetGreaterGrowIndexJumpJumpTrueJumpFalseJumpSetTrueJumpSetFalseLowerLoweriMulNewNegateNotPopPushReturnSubSubiSwap" -var _Op_index = [...]uint8{0, 3, 6, 10, 16, 23, 30, 34, 39, 44, 49, 52, 56, 60, 65, 73, 77, 82, 90, 97, 101, 106, 110, 118, 127, 138, 150, 155, 161, 164, 167, 170, 173, 177, 183, 186, 190, 194} +var _Op_index = [...]uint8{0, 3, 6, 10, 16, 23, 30, 34, 39, 44, 49, 52, 56, 60, 65, 73, 77, 82, 90, 97, 101, 106, 110, 118, 127, 138, 150, 155, 161, 164, 167, 173, 176, 179, 183, 189, 192, 196, 200} func (i Op) String() string { idx := int(i) - 0 @@ -104,3 +104,13 @@ func (t *Type) FieldIndex(name string) []int { } return nil } + +// FieldType returns the type of struct field name. +func (t *Type) FieldType(name string) *Type { + for _, f := range reflect.VisibleFields(t.Rtype) { + if f.Name == name { + return &Type{Name: f.Name, PkgPath: f.PkgPath, Rtype: f.Type} + } + } + return nil +} @@ -24,7 +24,7 @@ const ( Addr // a -- &a ; Assign // val -- ; mem[$1] = val Fassign // val -- ; mem[$1] = val - Vassign // val dest -- ; dest.Set(val) + Vassign // dest val -- ; dest.Set(val) Call // f [a1 .. ai] -- [r1 .. rj] ; r1, ... = prog[f](a1, ...) Calli // f [a1 .. ai] -- [r1 .. rj] ; r1, ... = prog[f](a1, ...) CallX // f [a1 .. ai] -- [r1 .. rj] ; r1, ... = mem[f](a1, ...) @@ -49,6 +49,7 @@ const ( Loweri // n1 -- cond ; cond = n1 < $1 Mul // n1 n2 -- prod ; prod = n1*n2 New // -- x; mem[fp+$1] = new mem[$2] + Negate // -- ; - mem[fp] Not // c -- r ; r = !c Pop // v -- Push // -- v @@ -119,10 +120,13 @@ func (m *Machine) Run() (err error) { mem[fp+c.Arg[0]-1].Set(mem[sp-1].Value) mem = mem[:sp-1] case Call: - nip := int(mem[sp-1].Int()) - mem = append(mem[:sp-1], ValueOf(ip+1), ValueOf(fp)) + // nip := int(mem[sp-1].Int()) + // mem = append(mem[:sp-1], ValueOf(ip+1), ValueOf(fp)) + nip := int(mem[sp-1-c.Arg[0]].Int()) + mem = append(mem, ValueOf(ip+1), ValueOf(fp)) ip = nip - fp = sp + 1 + // fp = sp + 1 + fp = sp + 2 continue case Calli: mem = append(mem, ValueOf(ip+1), ValueOf(fp)) @@ -132,9 +136,9 @@ func (m *Machine) Run() (err error) { case CallX: // Should be made optional. in := make([]reflect.Value, c.Arg[0]) for i := range in { - in[i] = mem[sp-2-i].Value + in[i] = mem[sp-1-i].Value } - f := mem[sp-1].Value + f := mem[sp-1-c.Arg[0]].Value mem = mem[:sp-c.Arg[0]-1] for _, v := range f.Call(in) { mem = append(mem, Value{Value: v}) @@ -142,7 +146,8 @@ func (m *Machine) Run() (err error) { case Deref: mem[sp-1].Value = mem[sp-1].Value.Elem() case Dup: - mem = append(mem, mem[c.Arg[0]]) + k := c.Arg[0] + mem = append(mem, mem[k]) case New: mem[c.Arg[0]+fp-1] = NewValue(mem[c.Arg[1]].Type) case Equal: @@ -214,13 +219,15 @@ func (m *Machine) Run() (err error) { } mem = mem[:sp-1] case Greater: - mem[sp-2] = ValueOf(mem[sp-1].Int() > mem[sp-2].Int()) + mem[sp-2] = ValueOf(mem[sp-2].Int() > mem[sp-1].Int()) mem = mem[:sp-1] case Lower: - mem[sp-2] = ValueOf(mem[sp-1].Int() < mem[sp-2].Int()) + mem[sp-2] = ValueOf(mem[sp-2].Int() < mem[sp-1].Int()) mem = mem[:sp-1] case Loweri: mem[sp-1] = ValueOf(mem[sp-1].Int() < int64(c.Arg[0])) + case Negate: + mem[sp-1] = ValueOf(-mem[sp-1].Int()) case Not: mem[sp-1] = ValueOf(!mem[sp-1].Bool()) case Pop: @@ -234,10 +241,10 @@ func (m *Machine) Run() (err error) { ip = int(mem[fp-2].Int()) ofp := fp fp = int(mem[fp-1].Int()) - mem = append(mem[:ofp-c.Arg[0]-c.Arg[1]-1], mem[sp-c.Arg[0]:]...) + mem = append(mem[:ofp-c.Arg[0]-c.Arg[1]-2], mem[sp-c.Arg[0]:]...) continue case Sub: - mem[sp-2] = ValueOf(int(mem[sp-1].Int() - mem[sp-2].Int())) + mem[sp-2] = ValueOf(int(mem[sp-2].Int() - mem[sp-1].Int())) mem = mem[:sp-1] case Subi: mem[sp-1] = ValueOf(int(mem[sp-1].Int()) - c.Arg[0]) @@ -245,10 +252,11 @@ func (m *Machine) Run() (err error) { a, b := sp-c.Arg[0]-1, sp-c.Arg[1]-1 mem[a], mem[b] = mem[b], mem[a] case Index: - mem[sp-2].Value = mem[sp-1].Index(int(mem[sp-2].Int())) + mem[sp-2].Value = mem[sp-2].Index(int(mem[sp-1].Int())) mem = mem[:sp-1] case Vassign: - mem[sp-1].Set(mem[sp-2].Value) + // mem[sp-1].Set(mem[sp-2].Value) + mem[sp-2].Set(mem[sp-1].Value) mem = mem[:sp-2] } ip++ diff --git a/vm/vm_test.go b/vm/vm_test.go index a968281..950e61e 100644 --- a/vm/vm_test.go +++ b/vm/vm_test.go @@ -69,7 +69,7 @@ var tests = []struct { {Op: Sub}, {Op: Exit}, }, - start: 0, end: 1, mem: "[1]", + start: 0, end: 1, mem: "[-1]", }, { // #02 -- A simple multiplication. code: []Instruction{ {Op: Push, Arg: []int{3}}, @@ -80,16 +80,16 @@ var tests = []struct { start: 0, end: 1, mem: "[6]", }, { // #03 -- lower. code: []Instruction{ - {Op: Push, Arg: []int{3}}, {Op: Push, Arg: []int{2}}, + {Op: Push, Arg: []int{3}}, {Op: Lower}, {Op: Exit}, }, start: 0, end: 1, mem: "[true]", }, { // #04 -- greater. code: []Instruction{ - {Op: Push, Arg: []int{2}}, {Op: Push, Arg: []int{3}}, + {Op: Push, Arg: []int{2}}, {Op: Greater}, {Op: Exit}, }, @@ -153,16 +153,18 @@ var tests = []struct { {Op: Exit}, }, start: 1, end: 3, mem: "[6 <nil>]", -}, { // #12 -- Defining and calling a function in VM. - code: []Instruction{ - {Op: Jump, Arg: []int{3}}, // 0 - {Op: Push, Arg: []int{3}}, // 1 - {Op: Return, Arg: []int{1, 1}}, // 2 - {Op: Push, Arg: []int{1}}, // 3 - {Op: Calli, Arg: []int{1}}, // 4 - {Op: Exit}, // 5 - }, - start: 0, end: 1, mem: "[3]", + /* + }, { // #12 -- Defining and calling a function in VM. + code: []Instruction{ + {Op: Jump, Arg: []int{3}}, // 0 + {Op: Push, Arg: []int{3}}, // 1 + {Op: Return, Arg: []int{1, 1}}, // 2 + {Op: Push, Arg: []int{1}}, // 3 + {Op: Calli, Arg: []int{1}}, // 4 + {Op: Exit}, // 5 + }, + start: 0, end: 1, mem: "[3]", + */ }, { // #13 -- Defining and calling a function in VM. code: []Instruction{ {Op: Jump, Arg: []int{3}}, // 0 @@ -170,7 +172,7 @@ var tests = []struct { {Op: Return, Arg: []int{1, 1}}, // 2 {Op: Push, Arg: []int{1}}, // 3 {Op: Push, Arg: []int{1}}, // 4 - {Op: Call}, // 5 + {Op: Call, Arg: []int{0}}, // 5 {Op: Exit}, // 6 }, start: 0, end: 1, mem: "[3]", @@ -183,56 +185,58 @@ var tests = []struct { {Op: Return, Arg: []int{1, 1}}, // 4 {Op: Push, Arg: []int{1}}, // 5 {Op: Push, Arg: []int{1}}, // 6 - {Op: Call}, // 7 + {Op: Call, Arg: []int{0}}, // 7 {Op: Exit}, // 8 }, - start: 0, end: 1, mem: "[3]", + start: 1, end: 2, mem: "[3]", }, { // #15 -- Fibonacci numbers, hand written. Showcase recursivity. code: []Instruction{ {Op: Jump, Arg: []int{19}}, // 0 - {Op: Push, Arg: []int{2}}, // 2 [2] {Op: Fdup, Arg: []int{-2}}, // 1 [2 i] + {Op: Push, Arg: []int{2}}, // 2 [2] {Op: Lower}, // 3 [true/false] {Op: JumpTrue, Arg: []int{13}}, // 4 [], goto 17 - {Op: Push, Arg: []int{2}}, // 5 [i 2] + {Op: Push, Arg: []int{1}}, // 5 {Op: Fdup, Arg: []int{-2}}, // 6 [i] - {Op: Sub}, // 7 [(i-2)] - {Op: Push, Arg: []int{1}}, // 8 - {Op: Call}, // 9 [fib(i-2)] - {Op: Push, Arg: []int{1}}, // 10 [(i-2) i 1] + {Op: Push, Arg: []int{2}}, // 7 [i 2] + {Op: Sub}, // 8 [(i-2)] + {Op: Call, Arg: []int{1}}, // 9 [fib(i-2)] + {Op: Push, Arg: []int{1}}, // 10 {Op: Fdup, Arg: []int{-2}}, // 11 [fib(i-2) i] - {Op: Sub}, // 12 [(i-2) (i-1)] - {Op: Push, Arg: []int{1}}, // 13 - {Op: Call}, // 14 [fib(i-2) fib(i-1)] + {Op: Push, Arg: []int{1}}, // 12 [(i-2) i 1] + {Op: Sub}, // 13 [(i-2) (i-1)] + {Op: Call, Arg: []int{1}}, // 14 [fib(i-2) fib(i-1)] {Op: Add}, // 15 [fib(i-2)+fib(i-1)] {Op: Return, Arg: []int{1, 1}}, // 16 return i {Op: Fdup, Arg: []int{-2}}, // 17 [i] {Op: Return, Arg: []int{1, 1}}, // 18 return i - {Op: Push, Arg: []int{6}}, // 19 [1] - {Op: Push, Arg: []int{1}}, // 20 - {Op: Call}, // 21 [fib(*1)] + {Op: Push, Arg: []int{1}}, // 19 + {Op: Push, Arg: []int{6}}, // 20 [1] + {Op: Call, Arg: []int{1}}, // 21 [fib(*1)] {Op: Exit}, // 22 }, start: 0, end: 1, mem: "[8]", -}, { // #16 -- Fibonacci with some immediate instructions. - code: []Instruction{ - {Op: Jump, Arg: []int{14}}, // 0 - {Op: Fdup, Arg: []int{-2}}, // 1 [i] - {Op: Loweri, Arg: []int{2}}, // 2 [true/false] - {Op: JumpTrue, Arg: []int{9}}, // 3 [], goto 12 - {Op: Fdup, Arg: []int{-2}}, // 4 [i] - {Op: Subi, Arg: []int{2}}, // 5 [(i-2)] - {Op: Calli, Arg: []int{1}}, // 6 [fib(i-2)] - {Op: Fdup, Arg: []int{-2}}, // 7 [fib(i-2) i] - {Op: Subi, Arg: []int{1}}, // 8 [(i-2) (i-1)] - {Op: Calli, Arg: []int{1}}, // 9 [fib(i-2) fib(i-1)], call 1 - {Op: Add}, // 10 [fib(i-2)+fib(i-1)] - {Op: Return, Arg: []int{1, 1}}, // 11 return i - {Op: Fdup, Arg: []int{-2}}, // 12 [i] - {Op: Return, Arg: []int{1, 1}}, // 13 return i - {Op: Push, Arg: []int{6}}, // 14 [1] - {Op: Calli, Arg: []int{1}}, // 15 [fib(*1)], call 1 - {Op: Exit}, // 16 - }, - start: 0, end: 1, mem: "[8]", + /* + }, { // #16 -- Fibonacci with some immediate instructions. + code: []Instruction{ + {Op: Jump, Arg: []int{14}}, // 0 + {Op: Fdup, Arg: []int{-2}}, // 1 [i] + {Op: Loweri, Arg: []int{2}}, // 2 [true/false] + {Op: JumpTrue, Arg: []int{9}}, // 3 [], goto 12 + {Op: Fdup, Arg: []int{-2}}, // 4 [i] + {Op: Subi, Arg: []int{2}}, // 5 [(i-2)] + {Op: Calli, Arg: []int{1}}, // 6 [fib(i-2)] + {Op: Fdup, Arg: []int{-2}}, // 7 [fib(i-2) i] + {Op: Subi, Arg: []int{1}}, // 8 [(i-2) (i-1)] + {Op: Calli, Arg: []int{1}}, // 9 [fib(i-2) fib(i-1)], call 1 + {Op: Add}, // 10 [fib(i-2)+fib(i-1)] + {Op: Return, Arg: []int{1, 1}}, // 11 return i + {Op: Fdup, Arg: []int{-2}}, // 12 [i] + {Op: Return, Arg: []int{1, 1}}, // 13 return i + {Op: Push, Arg: []int{6}}, // 14 [1] + {Op: Calli, Arg: []int{1}}, // 15 [fib(*1)], call 1 + {Op: Exit}, // 16 + }, + start: 0, end: 1, mem: "[8]", + */ }} |
