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) } func (p *process_t) push_context(node *Cnode_t) { p.ctx = &context_t{count: 0, parent_ctx: p.ctx, parent_node: node} } func (p *process_t) pop_context() (node *Cnode_t) { var i int node = p.ctx.parent_node // 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 } return } /* stmt text text bracket stmt: text text stmt: text TEXT */ func (interp *Interp) eval_atom_node(node *Cnode_t) (*string, error) { var ( p process_t v *string inode *Cnode_t err error ) p.interp = interp for node != nil { p.push_context(node) p.push_string_value("") // placeholder for return value for inode = node.child; inode != nil; inode = inode.next { switch inode.code { case CNODE_BRACKET: //p.push_context(inode) //p.push_string_value("") //node = case CNODE_TEXT: //fmt.Printf("XXXXXXXXXXXXXXXXXXXx[%s]\n", string(inode.token)) err = p.push_string_value(string(inode.token)) if err != nil { goto oops } } } fmt.Printf("CALLING\n") err = p.call() if err != nil { goto oops } if p.ctx.parent_ctx != nil { p.pop_context() } node = node.next } 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 } func (interp *Interp) eval_atom_node_old(node *Cnode_t) (*string, error) { 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 } 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) return interp.eval_atom_node((*Cnode_t)(unsafe.Pointer(ptr)).child) } 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 }