diff options
| author | Marc Vertes <mvertes@free.fr> | 2025-11-26 15:42:21 +0100 |
|---|---|---|
| committer | Marc Vertes <mvertes@free.fr> | 2025-11-26 15:42:21 +0100 |
| commit | aed20c1c453e50f716c454c0bd7e4995a0f5d898 (patch) | |
| tree | 91af798989300451113e5b865a20fb2eae777938 | |
| parent | 31054164870b413db797572b8e3d5a00c41d328e (diff) | |
Chore: improve tracing of code emits
| -rw-r--r-- | main.go | 18 | ||||
| -rw-r--r-- | parser/compiler.go | 89 | ||||
| -rw-r--r-- | parser/interpreter_test.go | 2 | ||||
| -rw-r--r-- | parser/symbol.go | 2 | ||||
| -rw-r--r-- | parser/symkind_string.go | 30 | ||||
| -rw-r--r-- | scanner/scan.go | 3 | ||||
| -rw-r--r-- | vm/op_string.go | 5 | ||||
| -rw-r--r-- | vm/vm.go | 5 |
8 files changed, 109 insertions, 45 deletions
@@ -9,6 +9,7 @@ import ( "log" "os" "reflect" + "strings" "github.com/mvertes/parscan/lang/golang" "github.com/mvertes/parscan/parser" @@ -62,12 +63,14 @@ func repl(interp Interpreter, in io.Reader) (err error) { } func run(arg []string) (err error) { + var str string rflag := flag.NewFlagSet("run", flag.ContinueOnError) rflag.Usage = func() { fmt.Println("Usage: parscan run [options] [path] [args]") - // fmt.Println("Options:") - // rflag.PrintDefaults() + fmt.Println("Options:") + rflag.PrintDefaults() } + rflag.StringVar(&str, "e", "", "string to eval") if err = rflag.Parse(arg); err != nil { return err } @@ -75,12 +78,19 @@ func run(arg []string) (err error) { interp := parser.NewInterpreter(scanner.NewScanner(golang.GoSpec)) - in := os.Stdin + var in io.Reader + if str != "" { + in = strings.NewReader(str) + } else { + in = os.Stdin + } if len(args) > 0 { if in, err = os.Open(arg[0]); err != nil { return err } - defer in.Close() + if i2, ok := in.(io.ReadCloser); ok { + defer i2.Close() + } } if isatty(in) { diff --git a/parser/compiler.go b/parser/compiler.go index e821c90..501fb25 100644 --- a/parser/compiler.go +++ b/parser/compiler.go @@ -4,7 +4,9 @@ import ( "fmt" "log" "os" + "path" "reflect" + "runtime" "strconv" "github.com/mvertes/parscan/lang" @@ -45,8 +47,10 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { fixList := Tokens{} // list of tokens to fix after we gathered all necessary information stack := []*symbol{} // for symbolic evaluation, type checking, etc - emit := func(pos int, op vm.Op, arg ...int) { - c.Code = append(c.Code, vm.Instruction{Pos: vm.Pos(pos), Op: op, Arg: arg}) + emit := func(t scanner.Token, op vm.Op, arg ...int) { + _, file, line, _ := runtime.Caller(1) + fmt.Fprintf(os.Stderr, "%s:%d: %v emit %v %v\n", path.Base(file), line, t, op, arg) + c.Code = append(c.Code, vm.Instruction{Pos: vm.Pos(t.Pos), Op: op, Arg: arg}) } push := func(s *symbol) { stack = append(stack, s) } pop := func() *symbol { l := len(stack) - 1; s := stack[l]; stack = stack[:l]; return s } @@ -59,7 +63,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { return err } push(&symbol{kind: symConst, value: vm.ValueOf(n), typ: vm.TypeOf(0)}) - emit(t.Pos, vm.Push, n) + emit(t, vm.Push, n) case lang.String: s := t.Block() @@ -71,49 +75,49 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { c.strings[s] = i } push(&symbol{kind: symConst, value: v}) - emit(t.Pos, vm.Dup, i) + emit(t, vm.Dup, i) case lang.Add: push(&symbol{typ: arithmeticOpType(pop(), pop())}) - emit(t.Pos, vm.Add) + emit(t, vm.Add) case lang.Mul: push(&symbol{typ: arithmeticOpType(pop(), pop())}) - emit(t.Pos, vm.Mul) + emit(t, vm.Mul) case lang.Sub: push(&symbol{typ: arithmeticOpType(pop(), pop())}) - emit(t.Pos, vm.Sub) + emit(t, vm.Sub) case lang.Minus: - emit(t.Pos, vm.Push, 0) - emit(t.Pos, vm.Sub) + emit(t, vm.Push, 0) + emit(t, vm.Sub) case lang.Not: - emit(t.Pos, vm.Not) + emit(t, vm.Not) case lang.Plus: // Unary '+' is idempotent. Nothing to do. case lang.Addr: push(&symbol{typ: vm.PointerTo(pop().typ)}) - emit(t.Pos, vm.Addr) + emit(t, vm.Addr) case lang.Deref: push(&symbol{typ: pop().typ.Elem()}) - emit(t.Pos, vm.Deref) + emit(t, vm.Deref) case lang.Index: push(&symbol{typ: pop().typ.Elem()}) - emit(t.Pos, vm.Index) + emit(t, vm.Index) case lang.Greater: push(&symbol{typ: booleanOpType(pop(), pop())}) - emit(t.Pos, vm.Greater) + emit(t, vm.Greater) case lang.Less: push(&symbol{typ: booleanOpType(pop(), pop())}) - emit(t.Pos, vm.Lower) + emit(t, vm.Lower) case lang.Call: s := pop() @@ -123,7 +127,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { for i := 0; i < typ.Rtype.NumOut(); i++ { push(&symbol{typ: typ.Out(i)}) } - emit(t.Pos, vm.Call) + emit(t, vm.Call) break } push(s) @@ -135,13 +139,29 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { for i := 0; i < rtyp.NumOut(); i++ { push(&symbol{typ: &vm.Type{Rtype: rtyp.Out(i)}}) } - emit(t.Pos, vm.CallX, t.Beg) + emit(t, vm.CallX, t.Beg) case lang.Composite: log.Println("COMPOSITE") + /* + d := pop() + switch d.typ.Rtype.Kind() { + case reflect.Struct: + // nf := d.typ.Rtype.NumField() + // emit(t.Pos, vm.New, d.index, c.typeSym(d.typ).index) + emit(t, vm.Field, 0) + emit(t, vm.Vassign) + emit(t, vm.Fdup, 2) + emit(t, vm.Field, 1) + emit(t, vm.Vassign) + emit(t, vm.Pop, 1) + // emit(t, vm.Fdup, 2) + // Assume an element list with no keys, one per struct field in order + } + */ case lang.Grow: - emit(t.Pos, vm.Grow, t.Beg) + emit(t, vm.Grow, t.Beg) case lang.Define: // TODO: support assignment to local, composite objects. @@ -155,12 +175,12 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { v := vm.NewValue(typ) c.addSym(l, st.Str, v, symVar, typ, false) c.Data = append(c.Data, v) - emit(st.Pos, vm.Assign, l) + emit(t, vm.Assign, l) case lang.Assign: st := tokens[i-1] if st.Tok == lang.Period || st.Tok == lang.Index { - emit(t.Pos, vm.Vassign) + emit(t, vm.Vassign) break } s, ok := c.symbols[st.Str] @@ -178,25 +198,25 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { } if s.local { if !s.used { - emit(st.Pos, vm.New, s.index, c.typeSym(s.typ).index) + emit(st, vm.New, s.index, c.typeSym(s.typ).index) s.used = true } - emit(st.Pos, vm.Fassign, s.index) + emit(st, vm.Fassign, s.index) break } if s.index == unsetAddr { s.index = len(c.Data) c.Data = append(c.Data, s.value) } - emit(st.Pos, vm.Assign, s.index) + emit(st, vm.Assign, s.index) case lang.Equal: push(&symbol{typ: booleanOpType(pop(), pop())}) - emit(t.Pos, vm.Equal) + emit(t, vm.Equal) case lang.EqualSet: push(&symbol{typ: booleanOpType(pop(), pop())}) - emit(t.Pos, vm.EqualSet) + emit(t, vm.EqualSet) case lang.Ident: if i < len(tokens)-1 { @@ -214,13 +234,14 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { break } if s.local { - emit(t.Pos, vm.Fdup, s.index) + emit(t, vm.Fdup, s.index) } else { if s.index == unsetAddr { s.index = len(c.Data) c.Data = append(c.Data, s.value) } - emit(t.Pos, vm.Dup, s.index) + log.Println(t, ": emit(", t.Pos, vm.Dup, s.index, ")") + emit(t, vm.Dup, s.index) } case lang.Label: @@ -248,7 +269,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { } else { i = int(s.value.Data.Int()) - len(c.Code) } - emit(t.Pos, vm.JumpFalse, i) + emit(t, vm.JumpFalse, i) case lang.JumpSetFalse: var i int @@ -259,7 +280,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { } else { i = int(s.value.Data.Int()) - len(c.Code) } - emit(t.Pos, vm.JumpSetFalse, i) + emit(t, vm.JumpSetFalse, i) case lang.JumpSetTrue: var i int @@ -270,7 +291,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { } else { i = int(s.value.Data.Int()) - len(c.Code) } - emit(t.Pos, vm.JumpSetTrue, i) + emit(t, vm.JumpSetTrue, i) case lang.Goto: var i int @@ -280,7 +301,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { } else { i = int(s.value.Data.Int()) - len(c.Code) } - emit(t.Pos, vm.Jump, i) + emit(t, vm.Jump, i) case lang.Period: s := pop() @@ -306,17 +327,17 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { sym = c.symbols[name] } push(sym) - emit(t.Pos, vm.Dup, l) + emit(t, vm.Dup, l) default: if f, ok := s.typ.Rtype.FieldByName(t.Str[1:]); ok { - emit(t.Pos, vm.Field, f.Index...) + emit(t, vm.Field, f.Index...) break } return fmt.Errorf("field or method not found: %s", t.Str[1:]) } case lang.Return: - emit(t.Pos, vm.Return, t.Beg, t.End) + emit(t, vm.Return, t.Beg, t.End) default: return fmt.Errorf("Codegen: unsupported token %v", t) diff --git a/parser/interpreter_test.go b/parser/interpreter_test.go index 4daab55..bbe0e2c 100644 --- a/parser/interpreter_test.go +++ b/parser/interpreter_test.go @@ -262,6 +262,6 @@ 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{N int; S string}; t := T{2, "foo"}; t`, res: `{2, "foo"}`}, + // {src: `type T struct{N int; S string}; t := T{2, "foo"}`, res: `{2 foo}`}, }) } diff --git a/parser/symbol.go b/parser/symbol.go index a491073..606752c 100644 --- a/parser/symbol.go +++ b/parser/symbol.go @@ -20,6 +20,8 @@ const ( symPkg // a Go package ) +//go:generate stringer -type=symKind + const unsetAddr = -65535 type symbol struct { diff --git a/parser/symkind_string.go b/parser/symkind_string.go new file mode 100644 index 0000000..a22c994 --- /dev/null +++ b/parser/symkind_string.go @@ -0,0 +1,30 @@ +// Code generated by "stringer -type=symKind"; DO NOT EDIT. + +package parser + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[symValue-0] + _ = x[symType-1] + _ = x[symLabel-2] + _ = x[symConst-3] + _ = x[symVar-4] + _ = x[symFunc-5] + _ = x[symPkg-6] +} + +const _symKind_name = "symValuesymTypesymLabelsymConstsymVarsymFuncsymPkg" + +var _symKind_index = [...]uint8{0, 8, 15, 23, 31, 37, 44, 50} + +func (i symKind) String() string { + idx := int(i) - 0 + if i < 0 || idx >= len(_symKind_index)-1 { + return "symKind(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _symKind_name[_symKind_index[idx]:_symKind_index[idx+1]] +} diff --git a/scanner/scan.go b/scanner/scan.go index bf9914b..b5e9962 100644 --- a/scanner/scan.go +++ b/scanner/scan.go @@ -68,18 +68,15 @@ func NewScanner(spec *lang.Spec) *Scanner { // Build a regular expression to match all string start delimiters at once. var sb strings.Builder sb.WriteString("(") - // re := "(" for s, p := range sc.BlockProp { if p&lang.CharStr == 0 { continue } // TODO: sort keys in decreasing length order. for _, b := range []byte(s) { - // re += fmt.Sprintf("\\x%02x", b) sb.WriteString(fmt.Sprintf("\\x%02x", b)) } sb.WriteString("|") - // re += "|" } re := strings.TrimSuffix(sb.String(), "|") + ")$" sc.sdre = regexp.MustCompile(re) diff --git a/vm/op_string.go b/vm/op_string.go index ef3f3ee..ac5b557 100644 --- a/vm/op_string.go +++ b/vm/op_string.go @@ -42,11 +42,12 @@ func _() { _ = x[Return-31] _ = x[Sub-32] _ = x[Subi-33] + _ = x[Swap-34] } -const _Op_name = "NopAddAddrAssignFassignVassignCallCalliCallXDerefDupFdupEqualEqualSetExitFieldGreaterGrowIndexJumpJumpTrueJumpFalseJumpSetTrueJumpSetFalseLowerLoweriMulNewNotPopPushReturnSubSubi" +const _Op_name = "NopAddAddrAssignFassignVassignCallCalliCallXDerefDupFdupEqualEqualSetExitFieldGreaterGrowIndexJumpJumpTrueJumpFalseJumpSetTrueJumpSetFalseLowerLoweriMulNewNotPopPushReturnSubSubiSwap" -var _Op_index = [...]uint8{0, 3, 6, 10, 16, 23, 30, 34, 39, 44, 49, 52, 56, 61, 69, 73, 78, 85, 89, 94, 98, 106, 115, 126, 138, 143, 149, 152, 155, 158, 161, 165, 171, 174, 178} +var _Op_index = [...]uint8{0, 3, 6, 10, 16, 23, 30, 34, 39, 44, 49, 52, 56, 61, 69, 73, 78, 85, 89, 94, 98, 106, 115, 126, 138, 143, 149, 152, 155, 158, 161, 165, 171, 174, 178, 182} func (i Op) String() string { idx := int(i) - 0 @@ -55,6 +55,7 @@ const ( Return // [r1 .. ri] -- ; exit frame: sp = fp, fp = pop Sub // n1 n2 -- diff ; diff = n1 - n2 Subi // n1 -- diff ; diff = n1 - $1 + Swap // -- ) // Instruction represents a virtual machine bytecode instruction. @@ -65,7 +66,7 @@ type Instruction struct { } func (i Instruction) String() (s string) { - s = i.Op.String() + s = fmt.Sprintf("%4d: %v", i.Pos, i.Op) var sb strings.Builder for _, a := range i.Arg { sb.WriteString(fmt.Sprintf(" %v", a)) @@ -226,6 +227,8 @@ func (m *Machine) Run() (err error) { mem = mem[:sp-1] case Subi: mem[sp-1] = ValueOf(int(mem[sp-1].Data.Int()) - c.Arg[0]) + case Swap: + mem[sp-2], mem[sp-1] = mem[sp-1], mem[sp-2] case Index: mem[sp-2].Data = mem[sp-1].Data.Index(int(mem[sp-2].Data.Int())) mem = mem[:sp-1] |
