new eval.go
This commit is contained in:
parent
89d6a1013b
commit
2a2e5b4527
15 changed files with 382 additions and 1215 deletions
150
ast/eval.go
150
ast/eval.go
|
|
@ -17,88 +17,96 @@
|
|||
|
||||
package ast
|
||||
|
||||
import "gitlab.com/whom/shs/log"
|
||||
import (
|
||||
"gitlab.com/whom/shs/log"
|
||||
)
|
||||
|
||||
var CallExecutablesFromUndefFuncCalls = false
|
||||
var CallExecutableToken = "l"
|
||||
/* determines whether or not to execute a system call
|
||||
* when a function cannot be found in the functable
|
||||
* (use case: shell)
|
||||
* ExecFunc determines the name of the system call function to fetch
|
||||
*/
|
||||
var ExecWhenFuncUndef = false
|
||||
var ExecFunc = "l"
|
||||
|
||||
func (t *Token) Eval(funcs FuncTable, vars VarTable) (*Token, bool) {
|
||||
if t == nil {
|
||||
return nil, false
|
||||
/* Runs through an AST of tokens
|
||||
* Evaluates the Tokens to determine simplest form
|
||||
* Returns simplest form
|
||||
*
|
||||
* canFunc determines whether a symbol could be a function to call
|
||||
* (true when first elem of a list)
|
||||
*/
|
||||
func (in *Token) Eval(funcs FuncTable, vars VarTable) *Token {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var reduce func(*Token) *Token
|
||||
reduce = func(t_ *Token) *Token {
|
||||
var unwrap bool
|
||||
var res *Token
|
||||
|
||||
if t_.Next != nil {
|
||||
t_.Next = reduce(t_.Next)
|
||||
}
|
||||
switch in.Tag {
|
||||
case BOOL, NUMBER, STRING:
|
||||
res = in
|
||||
|
||||
switch (t_.Tag) {
|
||||
case SYMBOL:
|
||||
maybeToken := GetVar(t_.Inner.(string), vars)
|
||||
if maybeToken != nil {
|
||||
tok, _ := maybeToken.Eval(funcs, vars)
|
||||
tok.Next = t_.Next
|
||||
return tok
|
||||
}
|
||||
case SYMBOL:
|
||||
res = GetVar(in.Value(), vars)
|
||||
if res == nil {
|
||||
res = in
|
||||
|
||||
case LIST:
|
||||
t_.Inner, unwrap = t_.Inner.(*Token).Eval(funcs, vars)
|
||||
if unwrap {
|
||||
next := t_.Next
|
||||
t_ = t_.Inner.(*Token)
|
||||
if t_ == nil {
|
||||
log.Log(log.DEBUG, "nil Inner on list unwrap", "eval")
|
||||
return nil
|
||||
}
|
||||
|
||||
i := &t_
|
||||
for (*i).Next != nil {
|
||||
i = &((*i).Next)
|
||||
}
|
||||
|
||||
(*i).Next = next
|
||||
}
|
||||
}
|
||||
|
||||
return t_
|
||||
}
|
||||
|
||||
ret := reduce(t)
|
||||
if ret == nil {
|
||||
log.Log(log.INFO, "reduce returned nil", "eval")
|
||||
return nil, false
|
||||
}
|
||||
|
||||
//if symbol in front of a list, could be a function call
|
||||
if ret.Tag == SYMBOL {
|
||||
f := GetFunction(ret.Inner.(string), funcs)
|
||||
if f == nil {
|
||||
if CallExecutablesFromUndefFuncCalls {
|
||||
f = GetFunction(CallExecutableToken, funcs)
|
||||
if f == nil {
|
||||
log.Log(log.DEBUG, "Symbol " + ret.Inner.(string) +
|
||||
" has no definition in function table. Additionally " +
|
||||
"the configured LoadExecutableToken is also not defined",
|
||||
"eval")
|
||||
return ret, false
|
||||
}
|
||||
|
||||
// see the use of CallFunction below
|
||||
ret = &Token{Next: ret}
|
||||
|
||||
} else {
|
||||
log.Log(log.DEBUG,
|
||||
"could not find definition for symbol " + ret.Inner.(string),
|
||||
if GetFunction(in.Value(), funcs) == nil {
|
||||
log.Log(log.ERR,
|
||||
"undefined symbol: "+in.Value(),
|
||||
"eval")
|
||||
return ret, false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
res.Next = in.Next
|
||||
|
||||
|
||||
case LIST:
|
||||
inner := in.Expand()
|
||||
if inner == nil {
|
||||
res = in
|
||||
break
|
||||
}
|
||||
|
||||
if inner.Tag != SYMBOL {
|
||||
in.Direct(inner.Eval(funcs, vars))
|
||||
res = in
|
||||
break
|
||||
}
|
||||
|
||||
makeHead := false
|
||||
funct := GetFunction(inner.Value(), funcs)
|
||||
if funct == nil {
|
||||
if ExecWhenFuncUndef {
|
||||
funct = GetFunction(ExecFunc, funcs)
|
||||
makeHead = true
|
||||
}
|
||||
}
|
||||
|
||||
return (*f).CallFunction(ret.Next, vars, funcs), true
|
||||
if funct != nil {
|
||||
if makeHead {
|
||||
inner = &Token{inner: inner}
|
||||
}
|
||||
res = funct.CallFunction(inner.Next, vars, funcs).Eval(funcs, vars)
|
||||
res.Append(in.Next)
|
||||
|
||||
} else {
|
||||
log.Log(log.ERR,
|
||||
"undefined function "+inner.Value()+" called",
|
||||
"eval")
|
||||
return nil
|
||||
}
|
||||
|
||||
default:
|
||||
log.Log(log.ERR,
|
||||
"Eval hit unknown token type!",
|
||||
"eval")
|
||||
return nil
|
||||
}
|
||||
|
||||
return ret, false
|
||||
if res.Next != nil {
|
||||
res.Next = res.Next.Eval(funcs, vars)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue