package interp import ( "fmt" "unsafe" ) 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) <--- SP (p.vsp) ARG1 ARG0 NAME RET 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") } 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) return nil } func (p *process_t) push_string_value(val string) error { if p.vsp >= cap(p.vstack) { return fmt.Errorf("stack full") } 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) 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") } 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].V = unsafe.Pointer(nil) p.vstack[p.vsp-1] = Value_t{Kind: VALUE_STR, V: unsafe.Pointer(&new_val)} 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() Value_t { var v Value_t p.vsp-- v = p.vstack[p.vsp] p.vstack[p.vsp].V = unsafe.Pointer(nil) return v } func (p *process_t) call() error { var ( proc func(*process_t) error callee *string ) 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": proc = proc_puts case "true": proc = proc_true case "false": proc = proc_false case "null": proc = proc_null default: proc = proc_unknown } return proc(p) } func (p *process_t) GetCalleeName() *string { return (*string)(p.vstack[p.vsp-p.ctx.count+1].V) } func (p *process_t) GetArg(idx int) Value_t { return (p.vstack[p.vsp-p.ctx.count+2+idx]) } func (p *process_t) GetNumArgs() int { return p.ctx.count - 2 } 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} } func (p *process_t) pop_context(clear_vstack bool) (*Cnode_t, *Cnode_t) { var ( i int node *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].V = unsafe.Pointer(nil) } // pop off the cleaned arguments p.vsp -= 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 // } return node, container } 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 }*/ 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") } } } /* 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] */ func (interp *Interp) eval_stmt_nodes(p *process_t, container_node *Cnode_t) (Value_t, error) { var ( v Value_t stmt_node *Cnode_t upper_node *Cnode_t inner_node *Cnode_t is_stmt bool err error org_vsp int ) v = empty_strval 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) if stmt_node == nil { goto done } for { start_over_0: if stmt_node.code != CNODE_STMT { panic("internal error - not statement node") } p.push_context(stmt_node, upper_node) p.push_string_value("") // placeholder for return value inner_node = stmt_node.child resume: for inner_node != nil { //fmt.Printf("handling %d\n", inner_node.code) switch inner_node.code { case CNODE_BRACKET: 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 } else { // no statements inside []. treat it like an empty string p.push_string_value("") } case CNODE_DQUOTE: if inner_node.child != nil { 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") } goto resume } else { // no statements inside []. treat it like an empty string p.push_string_value("") } case CNODE_BRACE: p.push_cnode_value(inner_node) case CNODE_TEXT: //fmt.Printf("XXXXXXXXXXXXXXXXXXXx[%s]\n", string(inner_node.token)) err = p.push_string_value(string(inner_node.token)) if err != nil { goto oops } // 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) } //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 || p.ctx.container_node.code == CNODE_BRACE { err = p.call() if err != nil { goto oops } is_stmt = true } else { is_stmt = false } 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 } } //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") } 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 } v = p.pop_value() // get the return value of the statement. if debug { interp.dump_vstack(p) } break } v = p.pop_value() // get the return value of the statement. if debug { interp.dump_vstack(p) } } done: if debug { 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 empty_strval, err } func (interp *Interp) eval_arg(p *process_t, pos int) (Value_t, error) { var v Value_t 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) (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 }*/ 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) 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 ( 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 ) 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 empty_strval, err }