summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Vertes <mvertes@free.fr>2025-11-26 15:42:21 +0100
committerMarc Vertes <mvertes@free.fr>2025-11-26 15:42:21 +0100
commitaed20c1c453e50f716c454c0bd7e4995a0f5d898 (patch)
tree91af798989300451113e5b865a20fb2eae777938
parent31054164870b413db797572b8e3d5a00c41d328e (diff)
Chore: improve tracing of code emits
-rw-r--r--main.go18
-rw-r--r--parser/compiler.go89
-rw-r--r--parser/interpreter_test.go2
-rw-r--r--parser/symbol.go2
-rw-r--r--parser/symkind_string.go30
-rw-r--r--scanner/scan.go3
-rw-r--r--vm/op_string.go5
-rw-r--r--vm/vm.go5
8 files changed, 109 insertions, 45 deletions
diff --git a/main.go b/main.go
index 232e122..1293bd6 100644
--- a/main.go
+++ b/main.go
@@ -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
diff --git a/vm/vm.go b/vm/vm.go
index 6c9f3e1..f981415 100644
--- a/vm/vm.go
+++ b/vm/vm.go
@@ -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]