367 lines
6.5 KiB
Go
367 lines
6.5 KiB
Go
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
|
|
}
|
|
|
|
type process_t struct {
|
|
interp *Interp
|
|
stack [16]unsafe.Pointer // value stack - TODO: change size
|
|
sp int
|
|
ctx *context_t
|
|
}
|
|
|
|
type call_frame_t struct {
|
|
vars *Var
|
|
parent *call_frame_t
|
|
}
|
|
|
|
type cnode_code_t int
|
|
|
|
const (
|
|
CNODE_INIT cnode_code_t = iota
|
|
CNODE_BRACKET
|
|
CNODE_BRACE
|
|
CNODE_DQUOTE
|
|
CNODE_VAR
|
|
CNODE_TEXT
|
|
CNODE_SEP
|
|
CNODE_EOL
|
|
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_atom_node(node.child)
|
|
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_SEP:
|
|
fmt.Printf(" ")
|
|
|
|
case CNODE_EOL:
|
|
fmt.Printf("\n")
|
|
|
|
case CNODE_JOIN:
|
|
// do nothing
|
|
//fmt.Printf("<M>")
|
|
|
|
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)
|
|
}
|