pcl/interp/pcl.go
2023-08-09 00:57:19 +09:00

371 lines
6.7 KiB
Go

package interp
import (
"fmt"
"runtime"
)
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 Value_t interface{}
type process_t struct {
interp *Interp
vstack [16]Value_t // value stack - TODO: change size
vsp int
ctx *context_t
cframe *call_frame_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
token []rune
}
type Interp struct {
strict bool
level int
max_level int
feed *feed_struct_t
cframe *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)
// global cframe?
interp.cframe = &call_frame_t{}
return interp, nil
}
func (interp *Interp) Close() {
interp.cframe = nil
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) (Value_t, error) {
var (
node *Cnode_t
v Value_t
err error
p process_t
)
p = process_t{interp: interp, vsp: 0}
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_stmt_nodes(&p, 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("<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)
}