pcl/interp/eval.go

387 lines
7.8 KiB
Go
Raw Normal View History

2023-07-21 09:32:51 +00:00
package interp
import (
"fmt"
"unsafe"
)
var err_num_args *error_t = &error_t{msg: "wrong number of arguments"}
/*
value stack (p.stack)
<--- SP
ARG1
ARG0
NAME
RET
evaluation stack (p.ctx)
*/
func (p *process_t) push_cnode_value(val *Cnode_t) error {
if p.sp >= cap(p.stack) {
return fmt.Errorf("stack full")
}
p.stack[p.sp] = unsafe.Pointer(uintptr(unsafe.Pointer(val)) | 1)
p.sp++
p.ctx.count++
return nil
}
func (p *process_t) push_string_value(val string) error {
if p.sp >= cap(p.stack) {
return fmt.Errorf("stack full")
}
p.stack[p.sp] = unsafe.Pointer(&val)
p.sp++
p.ctx.count++
return nil
}
func (p *process_t) merge_top_values() error {
var new_val string
if p.sp < 2 {
return fmt.Errorf("stack corrupt")
}
new_val = *(*string)(p.stack[p.sp-2]) + *(*string)(p.stack[p.sp-1])
p.sp--
p.stack[p.sp] = nil
p.stack[p.sp-1] = unsafe.Pointer(&new_val)
p.ctx.count--
return nil
}
func (p *process_t) pop_value() unsafe.Pointer {
var v unsafe.Pointer
p.sp--
v = p.stack[p.sp]
p.stack[p.sp] = nil
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
default:
proc = proc_unknown
}
return proc(p)
}
func (p *process_t) GetCalleeName() *string {
return (*string)(p.stack[p.sp-p.ctx.count+1])
}
func (p *process_t) GetArg(idx int) unsafe.Pointer {
return (p.stack[p.sp-p.ctx.count+2+idx])
}
func (p *process_t) GetNumArgs() int {
return p.ctx.count - 2
}
func (p *process_t) Return(val string) {
p.stack[p.sp-p.ctx.count] = unsafe.Pointer(&val)
}
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() (*Cnode_t, *Cnode_t) {
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
// clean up the unused part of the stack
for i = 1; i < p.ctx.count; i++ {
p.stack[p.sp-p.ctx.count+i] = nil
}
// pop off the cleaned arguments
p.sp -= p.ctx.count - 1 // keep the return value in the stack
p.ctx = p.ctx.parent_ctx
if p.ctx != nil {
p.ctx.count++ // let the return value be the argument to the caller
}
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) eval_node_child(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
inner_node *Cnode_t
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
2023-08-03 13:34:42 +00:00
stmt_node = container_node.child
2023-07-21 09:32:51 +00:00
2023-08-03 13:34:42 +00:00
fmt.Printf("START p.sp = %d\n", p.sp)
for stmt_node != nil {
if stmt_node.code != CNODE_STMT {
panic("internal error - not statement node")
}
p.push_context(stmt_node, nil)
2023-07-21 09:32:51 +00:00
p.push_string_value("") // placeholder for return value
2023-08-03 13:34:42 +00:00
start_over:
inner_node = stmt_node.child
resume:
for inner_node != nil {
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 {
stmt_node = inner_node.child // first statement inside []
p.push_context(stmt_node, inner_node)
p.push_string_value("")
goto start_over
} else {
// no statements inside []. treat it like an empty string
p.push_string_value("")
}
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(child_node.token))
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-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
}
2023-08-03 13:34:42 +00:00
//fmt.Printf("CALLING\n")
2023-07-21 09:32:51 +00:00
err = p.call()
if err != nil {
goto oops
}
2023-08-03 13:34:42 +00:00
stmt_node, inner_node = p.pop_context()
if inner_node != nil {
inner_node = inner_node.next
goto resume
}
2023-07-21 09:32:51 +00:00
2023-08-03 13:34:42 +00:00
v = (*string)(p.pop_value()) // get the return value of the statement.
stmt_node = stmt_node.next
2023-07-21 09:32:51 +00:00
}
2023-08-03 13:34:42 +00:00
fmt.Printf("END p.sp = %d\n", p.sp)
2023-07-21 09:32:51 +00:00
return v, nil
oops:
return nil, err
}
2023-08-03 13:34:42 +00:00
/*
2023-07-21 09:32:51 +00:00
func (interp *Interp) eval_atom_node_old(node *Cnode_t) (*string, error) {
2023-07-21 09:32:51 +00:00
var (
p process_t
v *string
err error
)
p.interp = interp
p.push_context(nil)
p.push_string_value("") // placeholder for return value
for node != nil {
switch node.code {
case CNODE_BRACKET: // this is a container
if node.child != nil {
p.push_context(node)
err = p.push_string_value("") // placeholder for return value
if err != nil {
goto oops
}
node = node.child
continue
} else {
err = p.push_string_value("")
if err != nil {
goto oops
}
}
case CNODE_BRACE: // this is a container
err = p.push_cnode_value(node)
if err != nil {
goto oops
}
case CNODE_DQUOTE: // this is a container
// TODO: at the feed layer, recompose CNODE_DQUOTE item to
// successive ATOM item
// "abc $ddd [xx 11]" must be TEXT("abc ") + VAR("ddd") + BRACKET["xx 11"] without SEP in between
// this is not right.....
if node.child != nil {
// TODO: something is not right here. handle inner stuffs
//fmt.Printf("pushing [%s]\n", string(node.child.token))
err = p.push_string_value(string(node.child.token))
} else {
err = p.push_string_value("")
}
if err != nil {
goto oops
}
case CNODE_VAR:
case CNODE_TEXT:
//fmt.Printf("pushing [%s]\n", string(node.token))
err = p.push_string_value(string(node.token))
if err != nil {
goto oops
}
default:
err = fmt.Errorf("internal error - non-atom node - %d", node.code)
goto oops
}
node = node.next
check_end:
if node == nil { // reached the last node
err = p.call()
if err != nil {
goto oops
}
if p.ctx.parent_ctx != nil {
node = p.pop_context()
if node.seqno > 0 {
// merge the top 2 values (return from [] + previous argument)
// for instance, the expression `aa[qq 11]` must product a single word
// `aa` concatenated of the result of `qq 11`
err = p.merge_top_values()
if err != nil {
goto oops
}
}
node = node.next
// take `aa 11 22 [dd [bb cc]]` as an example
// after [bb cc] is called, it must call `[dd]`` followed by `aa`.
// this goto loop is to handle successive calls when a nested call is the
// last argument
goto check_end
}
}
}
v = (*string)(p.pop_value())
p.pop_context()
if p.ctx != nil {
err = fmt.Errorf("internal error - dangling process context")
goto oops
}
return v, nil
oops:
return nil, err
}
2023-08-03 13:34:42 +00:00
*/
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)
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)
return interp.eval_node_child((*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
}