LangStudies/App/EoI/Scripts/Eva.gd
2022-07-24 09:50:38 -04:00

275 lines
5.6 KiB
GDScript

extends Object
# ---------------------------------------------------------- UTILITIES
var EvalOut
func check( condition : bool, message : String):
assert(condition, message)
if ! condition:
EvalOut.text = "Eva - Error: " + message
func throw( message ):
assert(false, message)
EvalOut.text = "Eva - Error: " + message
# ---------------------------------------------------------- UTILITIES END
class_name Eva
# ---------------------------------------------------------- GLOBALS
const Parser = preload("Parser.gd")
const NType = Parser.NType
const EvaEnv = preload("EvaEnv.gd")
var Env : EvaEnv
var Parent
# ---------------------------------------------------------- GLOBALS END
func get_class():
return "Eva"
func _init(parent, evalOut):
EvalOut = evalOut
Env = EvaEnv.new(EvalOut)
Parent = parent
if Parent == null:
Env.setup_Globals()
func eval( ast ):
match ast.type():
NType.program :
var index = 1;
while index < ast.num_args():
eval( ast.arg(index) )
index += 1
var result = eval( ast.arg(index) )
if result != null:
if typeof(result) == TYPE_OBJECT && result.get_class() == "ASTNode":
return JSON.print(result.to_SExpression())
return String( result )
else:
return null
NType.block :
return eval_Block( ast )
NType.conditional :
var condition = eval( ast.arg(1) )
if condition:
# consequent
return eval( ast.arg(2) )
# Alternate
if ast.num_args() > 2:
return eval( ast.arg(3))
NType.expr_While :
var result
while eval( ast.arg(1) ):
result = eval( ast.arg(2) )
return result
NType.fn_User :
var symbol = ast.arg(1)
var fnDef = \
[
ast.arg(2), # Parameters
ast.arg(3), # Body
self # Closure (Environment capture)
]
Env.define(symbol, fnDef)
return Env.lookup(symbol)
NType.fn_Lambda:
var fnDef = \
[
ast.arg(1), # Parameters
ast.arg(2), # Body
self # Closure (Environment capture)
]
return fnDef
NType.fn_IIL:
var params = ast.arg(1).arg(1)
var body = ast.arg(1).arg(2)
var fnEva = get_script().new( self, EvalOut )
if params.type() != NType.empty:
var index = 1
while index <= params.num_args():
var paramVal = eval( ast.arg(index + 1) )
fnEva.Env.define(params.arg(index), paramVal )
index += 1
var result
var index = 1;
while index <= body.num_args() :
result = fnEva.eval( body.arg( index ) )
index += 1
return result
NType.identifier :
return eval_Lookup( ast )
NType.op_Assign :
return eval_Assign( ast )
NType.op_Fn:
return eval_Func( ast )
NType.op_Greater:
return eval( ast.arg(1) ) > eval( ast.arg(2) )
NType.op_Lesser:
return eval( ast.arg(1) ) < eval( ast.arg(2) )
NType.op_GreaterEqual:
return eval( ast.arg(1) ) >= eval( ast.arg(2) )
NType.op_LesserEqual:
return eval( ast.arg(1) ) <= eval( ast.arg(2) )
NType.fn_Print :
return eval_Print( ast )
NType.variable :
var symbol = ast.arg(1)
var value
if ast.num_args() == 2:
value = eval( ast.arg(2) )
Env.define(symbol, value)
return Env.lookup(symbol)
if ast.is_Number() :
return float( ast.arg(1) )
elif ast.is_String() :
return ast.string()
return eval_Numeric( ast )
func eval_Block( ast ):
var eva_Block = get_script().new( self, EvalOut )
var result
var index = 1;
while index <= ast.num_args() :
result = eva_Block.eval( ast.arg(index) )
index += 1
return result
func eval_Lookup( ast ) :
var identifier = ast.arg(1)
if Parent != null && !Env.has( identifier):
return Parent.eval_Lookup( ast )
return Env.lookup( identifier )
func eval_Assign( ast, oriEva = null ) :
var symbol = ast.arg(1)
if Parent != null && !Env.has( symbol):
return Parent.eval_Assign( ast, self )
var value
if oriEva != null :
value = oriEva.eval( ast.arg(2) )
else :
value = eval( ast.arg(2) )
return Env.set( symbol, value )
func eval_Func( ast ):
var fn = eval_Lookup( ast )
var params = fn[0]
var body = fn[1]
var fnEva = get_script().new( fn[2], EvalOut )
if params.type() != NType.empty:
var index = 1
while index <= params.num_args():
var paramVal = eval( ast.arg(index + 1) )
fnEva.Env.define(params.arg(index), paramVal )
index += 1
var result
var index = 1;
while index <= body.num_args() :
result = fnEva.eval( body.arg( index ) )
index += 1
return result
func eval_Numeric( ast ):
if ast.type() == NType.op_Add:
var result = 0.0; var index = 1
while index <= ast.num_args():
result += eval( ast.arg(index) )
index += 1
return result
if ast.type() == NType.op_Sub:
if ast.num_args() == 2:
return -eval( ast.arg(1) )
var result = 0.0; var index = 1
while index <= ast.num_args():
result -= eval( ast.arg(index) )
index += 1
return result
if ast.type() == NType.op_Mult:
var result = 1.0; var index = 1
while index <= ast.num_args():
result *= eval( ast.arg(index) )
index += 1
return result
if ast.type() == NType.op_Div:
var result = 1.0; var index = 1
while index <= ast.num_args():
result /= eval( ast.arg(index) )
result += 1
return result
var msgT = "eval - Unimplemented: {ast}"
var msg = msgT.format({"ast" : JSON.print(ast.to_SExpression(), "\t") })
throw(msg)
func eval_Print( ast ):
EvalOut.text += "\n" + String( eval( ast.arg(1) ) )
return null
func get_EnvSnapshot():
var \
snapshot = EvaEnv.new(EvalOut)
snapshot.Records = Env.Records.duplicate(true)
if Parent != null:
snapshot[Parent] = Parent.get_EnvSnapshot()
return snapshot.to_Dictionary()