package interp import ( "fmt" "runtime" "unsafe" ) type error_t struct { msg string } func (m *error_t) Error() string { return m.msg } type Var struct { next *Var name string val string } type rcode_t int const ( R_OK rcode_t = iota R_ERR R_RETURN R_BREAK R_CONTINUE ) const NULL_RUNE rune = '\u0000' const EOF_RUNE rune = rune(^0) type context_t struct { count int parent_ctx *context_t parent_node *Cnode_t container_node *Cnode_t } type process_t struct { interp *Interp vstack [16]unsafe.Pointer // value stack - TODO: change size vsp int ctx *context_t } type call_frame_t struct { vars *Var parent *call_frame_t } type cnode_code_t int const ( CNODE_INVALID cnode_code_t = iota CNODE_INIT CNODE_BRACKET CNODE_BRACE CNODE_DQUOTE CNODE_VAR CNODE_TEXT CNODE_STMT CNODE_JOIN // merge the two top elements off the stack ) type feed_state_t int const ( FEED_TOP feed_state_t = iota FEED_INIT FEED_BRACKET // container for [] FEED_BRACE // container for {} FEED_BRACED_TEXT // uninterpreted text enclosed in {} FEED_DQUOTE // container for "" FEED_DQUOTED_TEXT // literal text inside "" FEED_DOLLAR // variable reference FEED_SEP // separator FEED_WORD // unquoted word FEED_EOL // end of line ) type feed_struct_t struct { parent *feed_struct_t level int state feed_state_t token []rune cnode_first *Cnode_t cnode_last *Cnode_t cnode_tmp_first *Cnode_t cnode_tmp_last *Cnode_t cnode_cont bool extra struct { brace_count int var_braced bool escaped bool escape_len int escape_max int escape_val int32 } } type Cnode_t struct { next *Cnode_t child *Cnode_t // for container nodes code cnode_code_t seqno int token []rune } type Interp struct { strict bool level int max_level int feed *feed_struct_t call_frame *call_frame_t result string } func NewInterp(max_level int, strict bool) (*Interp, error) { var ( interp *Interp ) interp = &Interp{ strict: strict, level: 0, max_level: max_level, } runtime.SetFinalizer(interp, func(interp *Interp) { interp.Close() }) interp.push_feed_struct(FEED_TOP) interp.push_feed_struct(FEED_INIT) return interp, nil } func (interp *Interp) Close() { for interp.feed != nil { interp.pop_feed_struct() } } /* func (interp *Interp) GetVar(name string, global bool) *Var { var ( v *Var f *CallFrame ) if global || is_clone_prefixed(name) { f = interp.call_frame for f.parent != nil { f = f.parent } v = f.vars } else { v = interp.call_frame.vars } // TODO: handle array for v != nil { // TODO: make the variable list can be hash map... if v.name == name { return v } } return nil } func (interp *Interp) SetVar(name string, val string, global bool) { var ( f *CallFrame v *Var ) f = interp.call_frame v = interp.GetVar(name, global) if v != nil { //if v->val = nil { // IS THIS POSSIBLE // //} } else { // TODO: handle aray if global || is_clone_prefixed((name)) { // TODD avand name by two charactes if clone_prefixed f = get_top_call_frame(f) } v = &Var{name: name} v.next = f.vars f.vars = v } v.val = val } func (interp *Interp) SetGlobalVar(name string, val string) { } func (interp *Interp) SetIntVar(name string, val int) { } func is_clone_prefixed(name string) bool { return len(name) > 2 && name[0] == ':' && name[1] == ':' } func get_top_call_frame(f *CallFrame) *CallFrame { for f.parent != nil { f = f.parent } return f } */ func (interp *Interp) Execute(node_head *Cnode_t) (*string, error) { var ( node *Cnode_t v *string err error ) v = new(string) // if there is no code the execute, the return value is an empty string for node = node_head; node != nil; node = node.next { if node.code != CNODE_INIT { return nil, 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 break } //v, err = interp.eval_node_child(node.child) v, err = interp.eval_node_child(node) if err != nil { return nil, err } } return v, nil } func (interp *Interp) dump_cnodes(node *Cnode_t, nl bool) { interp.__dump_cnodes(node, nl, 0) } func (interp *Interp) print_tabs(tabs int) { var i int for i = 0; i < tabs; i++ { fmt.Printf(" ") } } func (interp *Interp) __dump_cnodes(node *Cnode_t, nl bool, tabs int) { for node != nil { switch node.code { case CNODE_INIT: //fmt.Printf("[I]") interp.__dump_cnodes(node.child, false, tabs) //fmt.Printf("[E]") fmt.Printf("\n") case CNODE_STMT: //fmt.Printf("[S]") interp.print_tabs(tabs) interp.__dump_cnodes(node.child, false, tabs) //fmt.Printf("[E]") if node.next != nil { fmt.Printf("\n") } case CNODE_BRACKET: // this is a container if node.child == nil { fmt.Printf("[]") } else if node.child.next == nil { // only 1 item inside brackets fmt.Printf("[") interp.__dump_cnodes(node.child, false, 0) fmt.Printf("]") } else { fmt.Printf("[\n") interp.__dump_cnodes(node.child, false, tabs+1) fmt.Printf("\n") interp.print_tabs(tabs) fmt.Printf("]") } case CNODE_BRACE: // this is a container if node.child == nil { fmt.Printf("{}") } else if node.child.next == nil { // only 1 item inside braces fmt.Printf("{") interp.__dump_cnodes(node.child, false, 0) fmt.Printf("}") } else { fmt.Printf("{\n") interp.__dump_cnodes(node.child, false, tabs+1) fmt.Printf("\n") interp.print_tabs(tabs) fmt.Printf("}") } case CNODE_DQUOTE: // this is a container fmt.Printf("\"") interp.__dump_cnodes(node.child, false, 0) fmt.Printf("\"") case CNODE_VAR: fmt.Printf("${%s}", string(node.token)) case CNODE_TEXT: // TODO if text contains some specical characters while not // contained inside other containers, some escaping is required fmt.Printf("%s", string(node.token)) case CNODE_JOIN: // do nothing //fmt.Printf("") default: fmt.Printf("UNKNOWN") } if node.code != CNODE_STMT { if node.next != nil && node.next.next != nil && node.next.next.code == CNODE_JOIN { // do nothing } else if node.next != nil && node.next.code == CNODE_JOIN { // do nothing } else if node.next != nil { fmt.Printf(" ") } } node = node.next } //if nl { // fmt.Printf("\n") //} } func (interp *Interp) Dump(node *Cnode_t) { interp.dump_cnodes(node, true) }