From 37b9da32d3b911091deb254f6cba2a137c471287 Mon Sep 17 00:00:00 2001 From: Marc Vertes Date: Thu, 12 Oct 2023 10:51:58 +0200 Subject: move to a direct byte code compiler (#8) * chore: refactor to keep only the new parser and bytecode vm * scanner: remove Token.value field * scanner: remove scanner.kind field * chore: move language specification in lang package This avoid a cyclic dependency in scanner_test which can now use the golang/GoSpec language specification for Go. * clean code * scanner: export scanner fields Also parser now generate function calls, including externals. * chore: fix lint issues * parser: handle strings * wip * parser: implement support for 'if, else, else if' statements Resolving labels in the compiler still in progress. * parser: support if statements, improve compiler * improve handling of functions * improve support of local variables * scanner: trim leading and trailing spaces * fixes to make fibonacci work * parser: improve README, fix function parameters parsing --- vm0/vm.go | 157 -------------------------------------------------------------- 1 file changed, 157 deletions(-) delete mode 100644 vm0/vm.go (limited to 'vm0/vm.go') diff --git a/vm0/vm.go b/vm0/vm.go deleted file mode 100644 index 62344ed..0000000 --- a/vm0/vm.go +++ /dev/null @@ -1,157 +0,0 @@ -package vm0 - -import ( - "fmt" - "os" - "strings" - - "github.com/gnolang/parscan/parser" -) - -const debug = true - -type Interp struct { - *parser.Parser - stack []any // stack memory space - fp int // frame pointer: index of current frame in stack - sym map[string]int // symbol table, maps scoped identifiers to offsets relative to fp -} - -func New(p *parser.Parser) (i *Interp) { - i = &Interp{Parser: p, stack: []any{}, sym: map[string]int{}} - i.sym["println"] = i.push(fmt.Println) - return i -} - -func (i *Interp) Eval(src string) (res any, err error) { - n := &parser.Node{} - if n.Child, err = i.Parse(src, n); err != nil { - return - } - if debug { - n.Dot(os.Getenv("DOT"), "") - } - for _, nod := range n.Child { - if err = i.Run(nod, ""); err != nil { - break - } - } - return -} - -// Run implements a stack based virtual machine which directly walks the AST. -func (i *Interp) Run(node *parser.Node, scope string) (err error) { - stop := false - - node.Walk2(nil, 0, func(n, a *parser.Node, k int) (ok bool) { - // Node pre-order processing. - switch n.Kind { - case parser.BlockStmt: - if a != nil && a.Kind == parser.StmtIf { - // Control-flow in 'if' sub-tree - if k == 1 { - // 'if' first body branch, evaluated when condition is true. - if len(a.Child) > 2 { - return i.peek().(bool) // keep condition on stack for else branch - } - return i.pop().(bool) - } - // 'else' body branch, evaluated when condition is false. - return !i.pop().(bool) - } - case parser.DeclFunc: - i.declareFunc(n, scope) - return false - } - return true - }, func(n, a *parser.Node, k int) (ok bool) { - // Node post-order processing. - if stop { - return false - } - l := len(i.stack) - switch n.Kind { - case parser.LiteralNumber: - switch v := n.Value().(type) { - case int64: - i.push(int(v)) - case error: - err = v - return false - default: - err = fmt.Errorf("type not supported: %T\n", v) - return false - } - case parser.LiteralString: - i.push(n.Block()) - case parser.OpInferior: - i.stack[l-2] = i.stack[l-2].(int) < i.stack[l-1].(int) - i.stack = i.stack[:l-1] - case parser.OpAdd: - i.stack[l-2] = i.stack[l-2].(int) + i.stack[l-1].(int) - i.stack = i.stack[:l-1] - case parser.OpSubtract: - i.stack[l-2] = i.stack[l-2].(int) - i.stack[l-1].(int) - i.stack = i.stack[:l-1] - case parser.OpMultiply: - i.stack[l-2] = i.stack[l-2].(int) * i.stack[l-1].(int) - i.stack = i.stack[:l-1] - case parser.OpAssign, parser.OpDefine: - i.stack[i.stack[l-2].(int)] = i.stack[l-1] - i.stack = i.stack[:l-2] - case parser.StmtReturn: - stop = true - return false - case parser.ExprCall: - i.push(len(n.Child[1].Child)) // number of arguments to call - i.callFunc(n) - case parser.Ident: - name := n.Content() - v, sc, ok := i.getSym(name, scope) - fp := i.fp - if sc == "" { - fp = 0 - } - if ok { - if a.Content() == ":=" { - i.push(fp + v) // reference for assign (absolute index) - break - } - i.push(i.stack[fp+v]) // value - break - } - if a.Content() != ":=" { - fmt.Println("error: undefined:", name, "scope:", scope) - } - v = i.push(any(nil)) - i.fp // v is the address of new value, relative to frame pointer - i.sym[scope+name] = v // bind scoped name to address in symbol table - i.push(v) - } - return true - }) - return nil -} - -// getSym searches for an existing symbol starting from the deepest scope. -func (i *Interp) getSym(name, scope string) (index int, sc string, ok bool) { - for { - if index, ok = i.sym[scope+name]; ok { - return index, scope, ok - } - scope = strings.TrimSuffix(scope, "/") - j := strings.LastIndex(scope, "/") - if j == -1 { - scope = "" - break - } - if scope = scope[:j]; scope == "" { - break - } - } - index, ok = i.sym[name] - return index, scope, ok -} - -func (i *Interp) peek() any { return i.stack[len(i.stack)-1] } -func (i *Interp) push(v any) (l int) { l = len(i.stack); i.stack = append(i.stack, v); return } -func (i *Interp) pop() (v any) { l := len(i.stack) - 1; v = i.stack[l]; i.stack = i.stack[:l]; return } -- cgit v1.2.3