Compare commits

...

4 Commits

4 changed files with 325 additions and 142 deletions

View File

@ -16,8 +16,9 @@ func main() {
r *bufio.Reader
c rune
node *pcl.Cnode_t
v *string
err error
v pcl.Value_t
err error
)
if len(os.Args) != 2 {
@ -70,38 +71,10 @@ func main() {
goto oops
}
if v == nil {
panic("return value mut not be nil")
if v.Kind != pcl.VALUE_STR {
panic("return value must not be string")
}
fmt.Printf("RETURN VALUE = [%s]\n", *v)
/*
err = interp.FeedRunes([]rune(`
proc inc{x} {
puts {10 20}
return [expr $x + 1]
}
\{abc 11 2\ \1011 \ 2\x65 \uBc29\uaD6cdefg\uZZ\xZZ\U0000BC29\UAD6cZZ \
[donkey 1 [expr [expr 2 + 3] + 3] ]
hello { world { }man}
"command { l a n g }"
set a [puts "1111" "22 22" "3333 [expr "123" + 2] 4444"]
abc [expr [expr 2 + "4[expr 2 * 6]"] + 9]
puts $a ${ kkk qqq }
puts "\x65\ubc29\n"
{}`))
*/
//err = interp.FeedRunes([]rune(`hello [world [1 9] 2]
//`))
/*
if err != nil {
fmt.Printf("ERROR %s\n", err)
} else {
err = interp.EndFeed()
if err != nil {
fmt.Printf("ERROR %s\n", err)
}
}*/
fmt.Printf("RETURN VALUE = [%s]\n", *(*string)(v.V))
interp.Close()
f.Close()

View File

@ -5,9 +5,10 @@ import (
"unsafe"
)
var debug bool = true
var debug bool = false
var err_num_args *error_t = &error_t{msg: "wrong number of arguments"}
var empty_strval = Value_t{Kind: VALUE_STR, V: unsafe.Pointer(new(string))}
/*
value stack (p.vstack)
@ -21,17 +22,37 @@ var err_num_args *error_t = &error_t{msg: "wrong number of arguments"}
evaluation stack (p.ctx)
*/
func (p *process_t) push_call_frame() {
var cf *call_frame_t
cf = &call_frame_t{}
if p.cframe == nil {
// let it point to the global frame located in the interp struct
cf.parent = p.interp.cframe
} else {
cf.parent = p.cframe
}
cf.vars = make(map[string]Value_t)
p.cframe = cf
}
func (p *process_t) pop_call_frame() {
if p.cframe == p.interp.cframe {
p.cframe = nil
} else {
p.cframe = p.cframe.parent
}
}
func (p *process_t) push_cnode_value(val *Cnode_t) error {
if p.vsp >= cap(p.vstack) {
return fmt.Errorf("stack full")
}
// TODO: using the last bit won't be compatible with go's GC.
// CHANGE to use inteface{} or devise a different scheme...
p.vstack[p.vsp] = unsafe.Pointer(uintptr(unsafe.Pointer(val)) | 1)
p.vstack[p.vsp] = Value_t{Kind: VALUE_CNODE, V: unsafe.Pointer(val)}
p.vsp++
p.ctx.count++
fmt.Printf("push_cnode_value = ctx.count => %d\n", p.ctx.count)
//fmt.Printf("push_cnode_value = ctx.count => %d\n", p.ctx.count)
return nil
}
@ -41,33 +62,49 @@ func (p *process_t) push_string_value(val string) error {
return fmt.Errorf("stack full")
}
p.vstack[p.vsp] = unsafe.Pointer(&val)
p.vstack[p.vsp] = Value_t{Kind: VALUE_STR, V: unsafe.Pointer(&val)}
p.vsp++
p.ctx.count++
fmt.Printf("push_string_value = ctx.count => %d\n", p.ctx.count)
//fmt.Printf("push_string_value = ctx.count => %d\n", p.ctx.count)
return nil
}
func (p *process_t) merge_top_values() error {
var new_val string
var v1, v2 Value_t
if p.vsp < 2 {
return fmt.Errorf("stack corrupt")
}
new_val = *(*string)(p.vstack[p.vsp-2]) + *(*string)(p.vstack[p.vsp-1])
v1 = p.vstack[p.vsp-2]
v2 = p.vstack[p.vsp-1]
if v1.Kind == VALUE_STR {
new_val += *(*string)(v1.V)
}
// TODO: correct this to get the original text inside{}
// or must panic here by making {} unmergable in the feeder side
if v2.Kind == VALUE_STR {
new_val += *(*string)(v2.V)
}
p.vsp--
p.vstack[p.vsp] = nil
p.vstack[p.vsp-1] = unsafe.Pointer(&new_val)
p.vstack[p.vsp].V = unsafe.Pointer(nil)
p.vstack[p.vsp-1] = Value_t{Kind: VALUE_STR, V: unsafe.Pointer(&new_val)}
p.ctx.count--
fmt.Printf("merge_top_values = ctx.count => %d\n", p.ctx.count)
if debug {
fmt.Printf("merge_top_values = ctx.count => %d\n", p.ctx.count)
}
return nil
}
func (p *process_t) pop_value() unsafe.Pointer {
var v unsafe.Pointer
func (p *process_t) pop_value() Value_t {
var v Value_t
p.vsp--
v = p.vstack[p.vsp]
p.vstack[p.vsp] = nil
p.vstack[p.vsp].V = unsafe.Pointer(nil)
return v
}
@ -78,8 +115,17 @@ func (p *process_t) call() error {
)
callee = p.GetCalleeName()
if debug {
fmt.Printf("calling..... [%s]\n", *callee)
}
// TODO: use a map
switch *callee {
case "proc":
proc = proc_proc
case "set":
proc = proc_set
case "if":
proc = proc_if
case "puts":
@ -97,10 +143,10 @@ func (p *process_t) call() error {
}
func (p *process_t) GetCalleeName() *string {
return (*string)(p.vstack[p.vsp-p.ctx.count+1])
return (*string)(p.vstack[p.vsp-p.ctx.count+1].V)
}
func (p *process_t) GetArg(idx int) unsafe.Pointer {
func (p *process_t) GetArg(idx int) Value_t {
return (p.vstack[p.vsp-p.ctx.count+2+idx])
}
@ -108,11 +154,18 @@ func (p *process_t) GetNumArgs() int {
return p.ctx.count - 2
}
func (p *process_t) Return(val string) {
p.vstack[p.vsp-p.ctx.count] = unsafe.Pointer(&val)
func (p *process_t) ReturnString(val string) {
p.vstack[p.vsp-p.ctx.count] = Value_t{Kind: VALUE_STR, V: unsafe.Pointer(&val)}
}
func (p *process_t) Return(val Value_t) {
p.vstack[p.vsp-p.ctx.count] = val
}
func (p *process_t) push_context(node *Cnode_t, container_node *Cnode_t) {
if debug {
fmt.Printf("PUSHING CONTEXT.....\n")
}
p.ctx = &context_t{count: 0, parent_ctx: p.ctx, parent_node: node, container_node: container_node}
}
@ -123,13 +176,17 @@ func (p *process_t) pop_context(clear_vstack bool) (*Cnode_t, *Cnode_t) {
container *Cnode_t
)
if debug {
fmt.Printf("POPPING CONTEXT.....is_stmt/clear_vstack[%v]\n", clear_vstack)
}
node = p.ctx.parent_node
container = p.ctx.container_node
if clear_vstack { // TODO: use the conttext type instead... may be able to use container_node.code???
// clean up the unused part of the stack
for i = 1; i < p.ctx.count; i++ {
p.vstack[p.vsp-p.ctx.count+i] = nil
p.vstack[p.vsp-p.ctx.count+i].V = unsafe.Pointer(nil)
}
// pop off the cleaned arguments
@ -137,9 +194,9 @@ func (p *process_t) pop_context(clear_vstack bool) (*Cnode_t, *Cnode_t) {
}
p.ctx = p.ctx.parent_ctx
// if p.ctx != nil {
// p.ctx.count++ // let the return value be the argument to the caller
// }
// if p.ctx != nil {
// p.ctx.count++ // let the return value be the argument to the caller
// }
return node, container
}
@ -147,13 +204,24 @@ func (p *process_t) pop_context(clear_vstack bool) (*Cnode_t, *Cnode_t) {
func (interp *Interp) dump_vstack(p *process_t) {
fmt.Printf("p.VSP => %d\n", p.vsp)
for i := 0; i < p.vsp; i++ {
x := uintptr(p.vstack[i])
if x&1 == 0 {
// string value
fmt.Printf(" %d => [%s]\n", i, *(*string)(p.vstack[i]))
} else {
// cnode value
fmt.Printf(" %d => cnode %p", i, p.vstack[i]) // TODO: strip 1 off
/*
x := uintptr(p.vstack[i])
if x&1 == 0 {
// string value
fmt.Printf(" %d => [%s]\n", i, *(*string)(p.vstack[i]))
} else {
// cnode value
fmt.Printf(" %d => cnode %p", i, p.vstack[i]) // TODO: strip 1 off
}*/
switch p.vstack[i].Kind {
case VALUE_STR:
fmt.Printf(" %d => [%s]\n", i, *(*string)(p.vstack[i].V))
case VALUE_CNODE:
fmt.Printf(" %d => ", i)
interp.dump_cnodes((*Cnode_t)(p.vstack[i].V), false)
fmt.Printf("\n")
default:
panic("internal error - unrecognized value")
}
}
}
@ -182,36 +250,38 @@ puts "hello" world
[STMT]
[TEXT|null] [TEXT|1]
*/
func (interp *Interp) eval_stmt_nodes(container_node *Cnode_t) (*string, error) {
func (interp *Interp) eval_stmt_nodes(p *process_t, container_node *Cnode_t) (Value_t, error) {
var (
p process_t
v *string
v Value_t
stmt_node *Cnode_t
upper_node *Cnode_t
inner_node *Cnode_t
is_stmt bool
err error
org_vsp int
)
v = new(string) // TODO: change new(string) to a const
v = empty_strval
p.interp = interp
upper_node = container_node
stmt_node = upper_node.child // the first statement
org_vsp = p.vsp
fmt.Printf("START p.sp = %d\n", p.vsp)
for stmt_node != nil {
if stmt_node == nil {
goto done
}
for {
start_over_0:
if stmt_node.code != CNODE_STMT {
panic("internal error - not statement node")
}
fmt.Printf("PUSHING CONTEXT.....\n")
p.push_context(stmt_node, upper_node)
p.push_string_value("") // placeholder for return value
//start_over:
inner_node = stmt_node.child
resume:
for inner_node != nil {
@ -235,7 +305,6 @@ func (interp *Interp) eval_stmt_nodes(container_node *Cnode_t) (*string, error)
case CNODE_DQUOTE:
if inner_node.child != nil {
fmt.Printf("PUSHING CONTEXT.....\n")
p.push_context(stmt_node, inner_node)
//p.push_string_value("") // no placeholder for return value is needed
inner_node = inner_node.child
@ -264,19 +333,19 @@ func (interp *Interp) eval_stmt_nodes(container_node *Cnode_t) (*string, error)
// TODO: many more types...
case CNODE_JOIN:
p.merge_top_values()
case CNODE_INIT:
panic("internal error - INIT node must not appear inside a statement")
}
inner_node = inner_node.next
}
if debug {
interp.dump_vstack(&p)
interp.dump_vstack(p)
}
//fmt.Printf("p.ctx.parent_node.code %d p.ctx.container_node.code %d CNODE_STMT %d CNODE_DQUOTE %d CNODE_BRACKET %d\n", p.ctx.parent_node.code, p.ctx.container_node.code, CNODE_STMT, CNODE_DQUOTE, CNODE_BRACKET)
if p.ctx.container_node.code == CNODE_INIT || p.ctx.container_node.code == CNODE_BRACKET {
if debug {
fmt.Printf("calling..... [%s]\n", *p.GetCalleeName())
}
if p.ctx.container_node.code == CNODE_INIT || p.ctx.container_node.code == CNODE_BRACKET || p.ctx.container_node.code == CNODE_BRACE {
err = p.call()
if err != nil {
goto oops
@ -287,7 +356,6 @@ func (interp *Interp) eval_stmt_nodes(container_node *Cnode_t) (*string, error)
is_stmt = false
}
fmt.Printf("POPPING CONTEXT.....is_stmt[%v]\n", is_stmt)
stmt_node, upper_node = p.pop_context(is_stmt)
if upper_node != container_node {
if debug {
@ -296,7 +364,7 @@ func (interp *Interp) eval_stmt_nodes(container_node *Cnode_t) (*string, error)
if upper_node.code != CNODE_BRACKET {
inner_node = upper_node.next // as if it hit the bottom of the innner for loop
p.ctx.count++; // use return value on the stack as an argument
p.ctx.count++ // use return value on the stack as an argument
goto resume
}
}
@ -317,76 +385,124 @@ func (interp *Interp) eval_stmt_nodes(container_node *Cnode_t) (*string, error)
if upper_node.code != CNODE_BRACKET {
panic("internal error - invalid cnode type in the context statck")
}
fmt.Printf("POPPING CONTEXT.....false\n")
//stmt_node, upper_node = p.pop_context(false)
inner_node = upper_node.next
fmt.Printf(">>>>>>>>>>>>>>>>>> vsp %d ctx.count %d\n", p.vsp, p.ctx.count)
p.ctx.count++; // use the result value as an argument
inner_node = upper_node.next
//fmt.Printf(">>>>>>>>>>>>>>>>>> vsp %d ctx.count %d\n", p.vsp, p.ctx.count)
p.ctx.count++ // use the result value as an argument
goto resume
}
fmt.Printf("POPVAL...\n")
v = (*string)(p.pop_value()) // get the return value of the statement.
v = p.pop_value() // get the return value of the statement.
if debug {
interp.dump_vstack(&p)
interp.dump_vstack(p)
}
break
}
fmt.Printf("POPVAL...\n")
v = (*string)(p.pop_value()) // get the return value of the statement.
v = p.pop_value() // get the return value of the statement.
if debug {
interp.dump_vstack(&p)
interp.dump_vstack(p)
}
}
done:
if debug {
interp.dump_vstack(&p)
interp.dump_vstack(p)
fmt.Printf("END p.sp = %d\n", p.vsp)
}
if p.vsp != org_vsp {
panic("internal error - stack not clean")
}
return v, nil
oops:
return nil, err
return empty_strval, err
}
func (interp *Interp) eval_arg(p *process_t, pos int) (*string, error) {
var (
ptr uintptr
)
func (interp *Interp) eval_arg(p *process_t, pos int) (Value_t, error) {
var v Value_t
ptr = uintptr(p.GetArg(pos))
if ptr&1 == 1 { // cnode
ptr &= ^uintptr(1)
//interp.dump_cnodes((*Cnode_t)(unsafe.Pointer(ptr)), true)
//return interp.eval_atom_node((*Cnode_t)(unsafe.Pointer(ptr)).child)
return interp.eval_stmt_nodes((*Cnode_t)(unsafe.Pointer(ptr)))
} else {
return (*string)(unsafe.Pointer(ptr)), nil
v = p.GetArg(pos)
switch v.Kind {
case VALUE_STR:
return v, nil
case VALUE_CNODE:
return interp.eval_stmt_nodes(p, (*Cnode_t)(v.V))
default:
panic("internal error - argument type unrecognized")
}
}
func (interp *Interp) eval_arg_literally(p *process_t, pos int) (*string, error) {
var (
ptr uintptr
//cnode *Cnode_t
)
func (interp *Interp) eval_arg_literally(p *process_t, pos int) (Value_t, error) {
/*
var (
ptr uintptr
//cnode *Cnode_t
)
ptr = uintptr(p.GetArg(pos))
if ptr&1 == 1 { // cnode
ptr &= ^uintptr(1)
//cnode = (*Cnode_t)(unsafe.Pointer(ptr))
//cnode.child i hate this portion....
return nil, fmt.Errorf("not supported - unable to evaluate {} literally")
} else {
return (*string)(unsafe.Pointer(ptr)), nil
ptr = uintptr(p.GetArg(pos))
if ptr&1 == 1 { // cnode
ptr &= ^uintptr(1)
//cnode = (*Cnode_t)(unsafe.Pointer(ptr))
//cnode.child i hate this portion....
return nil, fmt.Errorf("not supported - unable to evaluate {} literally")
} else {
return (*string)(unsafe.Pointer(ptr)), nil
}*/
var v Value_t
v = p.GetArg(pos)
switch v.Kind {
case VALUE_STR:
return v, nil
case VALUE_CNODE:
// TODO: can support this? by storing the original text?
return empty_strval, fmt.Errorf("not supported - unable to evaluate {} literally")
default:
panic("internal error - argument type unrecognized")
}
}
func (interp *Interp) EvalText(text []rune) (*string, error) {
func (interp *Interp) set_var(p *process_t, name Value_t, val Value_t) error {
if name.Kind != VALUE_STR {
return fmt.Errorf("invalid variable name")
}
// TODO: error check?
p.cframe.vars[*(*string)(name.V)] = val
return nil
}
func (interp *Interp) get_var(p *process_t, name Value_t) (Value_t, error) {
var (
v *string
key *string
val Value_t
f *call_frame_t
ok bool
)
if name.Kind != VALUE_STR {
return empty_strval, fmt.Errorf("invalid variable name")
}
key = (*string)(name.V)
for f = p.cframe; f != nil; f = f.parent {
val, ok = p.cframe.vars[*key]
if ok {
return val, nil
}
}
return empty_strval, fmt.Errorf("%s not found", *key)
}
func (interp *Interp) EvalText(text []rune) (Value_t, error) {
var (
v Value_t
node *Cnode_t
err error
)
@ -415,5 +531,5 @@ func (interp *Interp) EvalText(text []rune) (*string, error) {
return v, nil
oops:
return nil, err
return empty_strval, err
}

View File

@ -40,15 +40,28 @@ type context_t struct {
container_node *Cnode_t
}
type ValueKind int
const (
VALUE_STR ValueKind = iota
VALUE_CNODE
)
type Value_t struct {
Kind ValueKind
V unsafe.Pointer
}
type process_t struct {
interp *Interp
vstack [16]unsafe.Pointer // value stack - TODO: change size
vstack [16]Value_t // value stack - TODO: change size
vsp int
ctx *context_t
cframe *call_frame_t
}
type call_frame_t struct {
vars *Var
vars map[string]Value_t
parent *call_frame_t
}
@ -106,7 +119,6 @@ type Cnode_t struct {
next *Cnode_t
child *Cnode_t // for container nodes
code cnode_code_t
seqno int
token []rune
}
@ -115,9 +127,9 @@ type Interp struct {
level int
max_level int
feed *feed_struct_t
call_frame *call_frame_t
result string
feed *feed_struct_t
cframe *call_frame_t
result string
}
func NewInterp(max_level int, strict bool) (*Interp, error) {
@ -138,10 +150,15 @@ func NewInterp(max_level int, strict bool) (*Interp, error) {
interp.push_feed_struct(FEED_TOP)
interp.push_feed_struct(FEED_INIT)
// global cframe?
interp.cframe = &call_frame_t{}
return interp, nil
}
func (interp *Interp) Close() {
interp.cframe = nil
for interp.feed != nil {
interp.pop_feed_struct()
}
@ -227,19 +244,24 @@ func get_top_call_frame(f *CallFrame) *CallFrame {
}
*/
func (interp *Interp) Execute(node_head *Cnode_t) (*string, error) {
func (interp *Interp) Execute(node_head *Cnode_t) (Value_t, error) {
var (
node *Cnode_t
v *string
v Value_t
err error
p process_t
)
v = new(string) // if there is no code the execute, the return value is an empty string
p = process_t{interp: interp, vsp: 0}
p.push_call_frame()
v = empty_strval // if there is no code the execute, the return value is an empty string
err = nil
for node = node_head; node != nil; node = node.next {
if node.code != CNODE_INIT {
return nil, fmt.Errorf("non-init node")
return empty_strval, fmt.Errorf("non-init node")
}
if node.child == nil { // TODO: optmize the cnode tree that this check is not needed. the reader must not create an INIT node with empty
@ -247,13 +269,18 @@ func (interp *Interp) Execute(node_head *Cnode_t) (*string, error) {
}
//v, err = interp.eval_node_child(node.child)
v, err = interp.eval_stmt_nodes(node)
v, err = interp.eval_stmt_nodes(&p, node)
if err != nil {
return nil, err
goto done
}
}
return v, nil
done:
// if there is no error, p.cframe must point to the global call frame here.
for p.cframe != nil {
p.pop_call_frame()
}
return v, err
}
func (interp *Interp) dump_cnodes(node *Cnode_t, nl bool) {

View File

@ -2,6 +2,65 @@ package interp
import "fmt"
func proc_proc(p *process_t) error {
/*
p.push_call_frame()
p.set_var("aaa", 10)
p.set_var("bbb", 20)
p.eval_stmt_nodes()
p.pop_call_frame()
*/
var err error
if p.GetNumArgs() != 3 {
err = err_num_args
goto done
}
/*
// procedure name
v1, err = p.interp.eval_arg(p, 0)
if err != nil {
goto done
}
p.set_var(p, v1, v2)*/
done:
return err
}
func proc_set(p *process_t) error {
var (
v1, v2 Value_t
err error
)
if p.GetNumArgs() != 2 {
err = err_num_args
goto done
}
v1, err = p.interp.eval_arg(p, 0)
if err != nil {
goto done
}
v2, err = p.interp.eval_arg(p, 1)
if err != nil {
goto done
}
err = p.interp.set_var(p, v1, v2)
if err != nil {
goto done
}
p.Return(v2)
done:
return err
}
func proc_expr(p *process_t) error {
return nil
@ -9,7 +68,7 @@ func proc_expr(p *process_t) error {
func proc_if(p *process_t) error {
var (
v *string
v Value_t
err error
)
@ -24,14 +83,17 @@ func proc_if(p *process_t) error {
goto done
}
if *v != "" {
if v.Kind != VALUE_STR {
panic("internal error - screwed conditional value")
}
if *(*string)(v.V) != "" {
//v, err = p.interp.eval_atom_node((*Cnode_t)(p.GetArg(1)))
v, err = p.interp.eval_arg(p, 1)
if err != nil {
goto done
}
p.Return(*v)
p.Return(v)
} else {
// TODO: if elseif else
}
@ -44,7 +106,7 @@ func proc_puts(p *process_t) error {
var (
i int
nargs int
v *string
v Value_t
err error
)
@ -59,28 +121,33 @@ func proc_puts(p *process_t) error {
if err != nil {
return err
}
fmt.Printf("%s", *v)
// TODO: check if v.kind is VALUE_STR
if v.Kind != VALUE_STR {
panic("internal error... invalid evaluation resutl")
}
fmt.Printf("%s", *(*string)(v.V))
}
if nargs >= 1 {
p.Return(*v)
p.Return(v)
} else {
p.Return("hello")
p.ReturnString("hello")
}
return nil
}
func proc_false(p *process_t) error {
p.Return("false")
p.ReturnString("false")
return nil
}
func proc_true(p *process_t) error {
p.Return("true")
p.ReturnString("true")
return nil
}
func proc_null(p *process_t) error {
p.Return("")
p.ReturnString("")
return nil
}