pcl/interp/eval.go

420 lines
9.5 KiB
Go
Raw Normal View History

2023-07-21 09:32:51 +00:00
package interp
import (
"fmt"
"unsafe"
)
var debug bool = true
2023-07-21 09:32:51 +00:00
var err_num_args *error_t = &error_t{msg: "wrong number of arguments"}
/*
2023-08-03 13:34:42 +00:00
value stack (p.vstack)
2023-07-21 09:32:51 +00:00
2023-08-03 13:34:42 +00:00
<--- SP (p.vsp)
2023-07-21 09:32:51 +00:00
ARG1
ARG0
NAME
RET
evaluation stack (p.ctx)
*/
func (p *process_t) push_cnode_value(val *Cnode_t) error {
2023-08-03 13:34:42 +00:00
if p.vsp >= cap(p.vstack) {
2023-07-21 09:32:51 +00:00
return fmt.Errorf("stack full")
}
2023-08-03 13:34:42 +00:00
// 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.vsp++
2023-07-21 09:32:51 +00:00
p.ctx.count++
fmt.Printf("push_cnode_value = ctx.count => %d\n", p.ctx.count)
2023-07-21 09:32:51 +00:00
return nil
}
func (p *process_t) push_string_value(val string) error {
2023-08-03 13:34:42 +00:00
if p.vsp >= cap(p.vstack) {
2023-07-21 09:32:51 +00:00
return fmt.Errorf("stack full")
}
2023-08-03 13:34:42 +00:00
p.vstack[p.vsp] = unsafe.Pointer(&val)
p.vsp++
2023-07-21 09:32:51 +00:00
p.ctx.count++
fmt.Printf("push_string_value = ctx.count => %d\n", p.ctx.count)
2023-07-21 09:32:51 +00:00
return nil
}
func (p *process_t) merge_top_values() error {
var new_val string
2023-08-03 13:34:42 +00:00
if p.vsp < 2 {
2023-07-21 09:32:51 +00:00
return fmt.Errorf("stack corrupt")
}
2023-08-03 13:34:42 +00:00
new_val = *(*string)(p.vstack[p.vsp-2]) + *(*string)(p.vstack[p.vsp-1])
p.vsp--
p.vstack[p.vsp] = nil
p.vstack[p.vsp-1] = unsafe.Pointer(&new_val)
2023-07-21 09:32:51 +00:00
p.ctx.count--
fmt.Printf("merge_top_values = ctx.count => %d\n", p.ctx.count)
2023-07-21 09:32:51 +00:00
return nil
}
func (p *process_t) pop_value() unsafe.Pointer {
var v unsafe.Pointer
2023-08-03 13:34:42 +00:00
p.vsp--
v = p.vstack[p.vsp]
p.vstack[p.vsp] = nil
2023-07-21 09:32:51 +00:00
return v
}
func (p *process_t) call() error {
var (
proc func(*process_t) error
callee *string
)
callee = p.GetCalleeName()
// TODO: use a map
switch *callee {
case "if":
proc = proc_if
case "puts":
proc = proc_puts
2023-08-03 13:34:42 +00:00
case "true":
proc = proc_true
case "false":
proc = proc_false
case "null":
proc = proc_null
2023-07-21 09:32:51 +00:00
default:
proc = proc_unknown
}
return proc(p)
}
func (p *process_t) GetCalleeName() *string {
2023-08-03 13:34:42 +00:00
return (*string)(p.vstack[p.vsp-p.ctx.count+1])
2023-07-21 09:32:51 +00:00
}
func (p *process_t) GetArg(idx int) unsafe.Pointer {
2023-08-03 13:34:42 +00:00
return (p.vstack[p.vsp-p.ctx.count+2+idx])
2023-07-21 09:32:51 +00:00
}
func (p *process_t) GetNumArgs() int {
return p.ctx.count - 2
}
func (p *process_t) Return(val string) {
2023-08-03 13:34:42 +00:00
p.vstack[p.vsp-p.ctx.count] = unsafe.Pointer(&val)
2023-07-21 09:32:51 +00:00
}
2023-08-03 13:34:42 +00:00
func (p *process_t) push_context(node *Cnode_t, container_node *Cnode_t) {
p.ctx = &context_t{count: 0, parent_ctx: p.ctx, parent_node: node, container_node: container_node}
2023-07-21 09:32:51 +00:00
}
2023-08-03 13:34:42 +00:00
func (p *process_t) pop_context(clear_vstack bool) (*Cnode_t, *Cnode_t) {
2023-08-03 13:34:42 +00:00
var (
i int
node *Cnode_t
container *Cnode_t
)
2023-07-21 09:32:51 +00:00
node = p.ctx.parent_node
2023-08-03 13:34:42 +00:00
container = p.ctx.container_node
2023-07-21 09:32:51 +00:00
if clear_vstack { // TODO: use the conttext type instead... may be able to use container_node.code???
2023-08-03 13:34:42 +00:00
// 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
}
2023-07-21 09:32:51 +00:00
2023-08-03 13:34:42 +00:00
// pop off the cleaned arguments
p.vsp -= p.ctx.count - 1 // keep the return value in the stack
}
2023-07-21 09:32:51 +00:00
p.ctx = p.ctx.parent_ctx
// if p.ctx != nil {
// p.ctx.count++ // let the return value be the argument to the caller
// }
2023-07-21 09:32:51 +00:00
2023-08-03 13:34:42 +00:00
return node, container
}
2023-07-21 09:32:51 +00:00
2023-08-03 13:34:42 +00:00
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
}
}
}
/*
puts "hello" world
[STMT]
[TEXT|puts] [DQUOTE] [TEXT|world]
[TEXT|hello]
[puts 1 2; puts 1] 999
[STMT]
[BRACKET] [TEXT|999]
[STMT]
[TEXT|puts] [TEXT|1] [TEXT|2]
[STMT]
[TEXT|puts] [TEXT|1]
"pu[null 1]ts" 10 20
[STMT]
[DQUOTE] [TEXT|10] [TEXT|20]
[TEXT|pu] [BRACKET] [JOIN] [TEXT|ts] [JOIN]
[STMT]
[TEXT|null] [TEXT|1]
*/
2023-08-03 13:34:42 +00:00
func (interp *Interp) eval_stmt_nodes(container_node *Cnode_t) (*string, error) {
2023-07-21 09:32:51 +00:00
var (
2023-08-03 13:34:42 +00:00
p process_t
v *string
stmt_node *Cnode_t
upper_node *Cnode_t
2023-08-03 13:34:42 +00:00
inner_node *Cnode_t
is_stmt bool
2023-08-03 13:34:42 +00:00
err error
2023-07-21 09:32:51 +00:00
)
2023-08-03 13:34:42 +00:00
v = new(string) // TODO: change new(string) to a const
2023-07-21 09:32:51 +00:00
p.interp = interp
upper_node = container_node
stmt_node = upper_node.child // the first statement
2023-08-03 13:34:42 +00:00
fmt.Printf("START p.sp = %d\n", p.vsp)
2023-08-03 13:34:42 +00:00
for stmt_node != nil {
start_over_0:
2023-08-03 13:34:42 +00:00
if stmt_node.code != CNODE_STMT {
panic("internal error - not statement node")
}
fmt.Printf("PUSHING CONTEXT.....\n")
p.push_context(stmt_node, upper_node)
2023-07-21 09:32:51 +00:00
p.push_string_value("") // placeholder for return value
//start_over:
2023-08-03 13:34:42 +00:00
inner_node = stmt_node.child
resume:
for inner_node != nil {
2023-08-03 13:34:42 +00:00
//fmt.Printf("handling %d\n", inner_node.code)
2023-08-03 13:34:42 +00:00
switch inner_node.code {
2023-07-21 09:32:51 +00:00
case CNODE_BRACKET:
2023-08-03 13:34:42 +00:00
if inner_node.child != nil {
upper_node = inner_node
stmt_node = upper_node.child
if debug {
fmt.Printf("going to start over\n")
interp.dump_cnodes(stmt_node, true)
fmt.Printf("\n--\n")
}
goto start_over_0
2023-08-03 13:34:42 +00:00
} else {
// no statements inside []. treat it like an empty string
p.push_string_value("")
}
2023-07-21 09:32:51 +00:00
2023-08-03 13:34:42 +00:00
case CNODE_DQUOTE:
if inner_node.child != nil {
fmt.Printf("PUSHING CONTEXT.....\n")
2023-08-03 13:34:42 +00:00
p.push_context(stmt_node, inner_node)
//p.push_string_value("") // no placeholder for return value is needed
inner_node = inner_node.child
if debug {
fmt.Printf("going to start over\n")
interp.dump_cnodes(stmt_node, true)
fmt.Printf("\n--\n")
}
2023-08-03 13:34:42 +00:00
goto resume
} else {
// no statements inside []. treat it like an empty string
p.push_string_value("")
}
2023-08-03 13:34:42 +00:00
case CNODE_BRACE:
p.push_cnode_value(inner_node)
2023-07-21 09:32:51 +00:00
case CNODE_TEXT:
2023-08-03 13:34:42 +00:00
//fmt.Printf("XXXXXXXXXXXXXXXXXXXx[%s]\n", string(inner_node.token))
2023-08-03 13:34:42 +00:00
err = p.push_string_value(string(inner_node.token))
2023-07-21 09:32:51 +00:00
if err != nil {
goto oops
}
2023-08-03 13:34:42 +00:00
// TODO: many more types...
2023-08-03 13:34:42 +00:00
case CNODE_JOIN:
p.merge_top_values()
2023-07-21 09:32:51 +00:00
}
2023-08-03 13:34:42 +00:00
inner_node = inner_node.next
2023-07-21 09:32:51 +00:00
}
if debug {
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())
}
2023-08-03 13:34:42 +00:00
err = p.call()
if err != nil {
goto oops
}
2023-08-03 13:34:42 +00:00
is_stmt = true
2023-08-03 13:34:42 +00:00
} else {
is_stmt = false
2023-07-21 09:32:51 +00:00
}
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 {
fmt.Printf("resuming... %d upper_node.next %p\n", p.vsp, upper_node.next)
}
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
goto resume
}
2023-08-03 13:34:42 +00:00
}
2023-07-21 09:32:51 +00:00
//fmt.Printf("POPPING VALUE...\n")
/*v = (*string)(p.pop_value()) // get the return value of the statement.
if debug {
interp.dump_vstack(&p)
}*/
stmt_node = stmt_node.next
if stmt_node == nil {
// go doesn't allow jumping into a block.
// let's the put the code here
if upper_node != container_node {
// the upper node is not the top containing node passed to this function.
// the contenxt stack must not be empty in this case.
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
goto resume
}
fmt.Printf("POPVAL...\n")
v = (*string)(p.pop_value()) // get the return value of the statement.
if debug {
interp.dump_vstack(&p)
}
break
}
fmt.Printf("POPVAL...\n")
2023-08-03 13:34:42 +00:00
v = (*string)(p.pop_value()) // get the return value of the statement.
if debug {
interp.dump_vstack(&p)
}
2023-07-21 09:32:51 +00:00
}
if debug {
interp.dump_vstack(&p)
fmt.Printf("END p.sp = %d\n", p.vsp)
}
2023-07-21 09:32:51 +00:00
return v, nil
oops:
return nil, err
}
2023-07-21 09:32:51 +00:00
func (interp *Interp) eval_arg(p *process_t, pos int) (*string, error) {
var (
ptr uintptr
)
ptr = uintptr(p.GetArg(pos))
if ptr&1 == 1 { // cnode
ptr &= ^uintptr(1)
2023-08-03 13:34:42 +00:00
//interp.dump_cnodes((*Cnode_t)(unsafe.Pointer(ptr)), true)
2023-08-03 13:34:42 +00:00
//return interp.eval_atom_node((*Cnode_t)(unsafe.Pointer(ptr)).child)
2023-08-03 13:34:42 +00:00
return interp.eval_stmt_nodes((*Cnode_t)(unsafe.Pointer(ptr)))
2023-07-21 09:32:51 +00:00
} else {
return (*string)(unsafe.Pointer(ptr)), nil
}
}
func (interp *Interp) eval_arg_literally(p *process_t, pos int) (*string, 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
}
}
func (interp *Interp) EvalText(text []rune) (*string, error) {
var (
v *string
node *Cnode_t
err error
)
interp.BeginFeed() // this resets the feed stack to the initial state
err = interp.FeedRunes(text)
if err != nil {
goto oops
}
node, err = interp.EndFeed()
if err != nil {
goto oops
}
//fmt.Printf("--------------------\n")
//interp.dump_cnodes(node, true)
//fmt.Printf("--------------------\n")
v, err = interp.Execute(node)
if err != nil {
goto oops
}
return v, nil
oops:
return nil, err
}