EOI: Lecture 10 & 11 complete.

This commit is contained in:
Edward R. Gonzalez 2022-07-24 07:54:17 -04:00
parent ba53395fc7
commit bd6e39ecdd
6 changed files with 259 additions and 57 deletions

View File

@ -42,17 +42,27 @@ margin_bottom = -2.0
[node name="Eva_Interpret_Btn" type="Button" parent="VBox"] [node name="Eva_Interpret_Btn" type="Button" parent="VBox"]
margin_right = 203.0 margin_right = 203.0
margin_bottom = 30.0 margin_bottom = 27.0
rect_pivot_offset = Vector2( -123, -302 ) rect_pivot_offset = Vector2( -123, -302 )
size_flags_vertical = 3 size_flags_vertical = 3
size_flags_stretch_ratio = 0.08 size_flags_stretch_ratio = 0.08
theme = ExtResource( 1 ) theme = ExtResource( 1 )
text = "Eva: Interpret" text = "Eva: Interpret"
[node name="ClearOutput_Btn" type="Button" parent="VBox"] [node name="Eva_ResetEnv_Btn" type="Button" parent="VBox"]
margin_top = 34.0 margin_top = 31.0
margin_right = 203.0 margin_right = 203.0
margin_bottom = 64.0 margin_bottom = 59.0
rect_pivot_offset = Vector2( -123, -302 )
size_flags_vertical = 3
size_flags_stretch_ratio = 0.08
theme = ExtResource( 1 )
text = "Eva: Reset Enviornment"
[node name="ClearOutput_Btn" type="Button" parent="VBox"]
margin_top = 63.0
margin_right = 203.0
margin_bottom = 91.0
rect_pivot_offset = Vector2( -123, -302 ) rect_pivot_offset = Vector2( -123, -302 )
size_flags_vertical = 3 size_flags_vertical = 3
size_flags_stretch_ratio = 0.08 size_flags_stretch_ratio = 0.08
@ -61,14 +71,14 @@ text = "Clear Output"
[node name="Separator" type="HSeparator" parent="VBox"] [node name="Separator" type="HSeparator" parent="VBox"]
modulate = Color( 0.145098, 0.145098, 0.164706, 0 ) modulate = Color( 0.145098, 0.145098, 0.164706, 0 )
margin_top = 68.0 margin_top = 95.0
margin_right = 203.0 margin_right = 203.0
margin_bottom = 443.0 margin_bottom = 445.0
size_flags_vertical = 15 size_flags_vertical = 15
theme = ExtResource( 5 ) theme = ExtResource( 5 )
[node name="Back_Btn" type="Button" parent="VBox"] [node name="Back_Btn" type="Button" parent="VBox"]
margin_top = 447.0 margin_top = 449.0
margin_right = 203.0 margin_right = 203.0
margin_bottom = 478.0 margin_bottom = 478.0
rect_pivot_offset = Vector2( -123, -302 ) rect_pivot_offset = Vector2( -123, -302 )

View File

@ -13,18 +13,18 @@ var Eva : SEva
# UX -------------------------------------------------------- # UX --------------------------------------------------------
onready var Editor = get_node("Editor_TEdit") onready var Editor = get_node("Editor_TEdit")
onready var Output = get_node("Output_TEdit") onready var Output = get_node("Output_TEdit")
onready var Debug = get_node("Debug_TEdit") onready var Debug = get_node("Debug_TEdit")
onready var Eva_Btn = get_node("VBox/Eva_Interpret_Btn") onready var Eva_Btn = get_node("VBox/Eva_Interpret_Btn")
onready var Clear_Btn = get_node("VBox/ClearOutput_Btn") onready var Eva_Reset_Btn = get_node("VBox/Eva_ResetEnv_Btn")
onready var Back_Btn = get_node("VBox/Back_Btn") onready var Clear_Btn = get_node("VBox/ClearOutput_Btn")
onready var Back_Btn = get_node("VBox/Back_Btn")
func evaBtn_pressed(): func evaBtn_pressed():
Lexer = SLexer.new(Editor.text, Output) Lexer = SLexer.new(Editor.text, Output)
Parser = SParser.new(Lexer, Output) Parser = SParser.new(Lexer, Output)
Eva = SEva.new(null, Output)
var ast = Parser.parse() var ast = Parser.parse()
var result = Eva.eval(ast) var result = Eva.eval(ast)
@ -34,6 +34,10 @@ func evaBtn_pressed():
Debug.text = JSON.print( Eva.get_EnvSnapshot(), "\t" ) Debug.text = JSON.print( Eva.get_EnvSnapshot(), "\t" )
func evaResetBtn_pressed():
Eva = SEva.new(null, Output)
Debug.text = JSON.print( Eva.get_EnvSnapshot(), "\t" )
func clearBtn_pressed(): func clearBtn_pressed():
Output.text = "" Output.text = ""
@ -42,6 +46,10 @@ func backBtn_pressed():
func _ready(): func _ready():
Eva = SEva.new(null, Output)
Debug.text = JSON.print( Eva.get_EnvSnapshot(), "\t" )
Eva_Btn.connect("pressed", self, "evaBtn_pressed") Eva_Btn.connect("pressed", self, "evaBtn_pressed")
Eva_Reset_Btn.connect("pressed", self, "evaResetBtn_pressed")
Clear_Btn.connect("pressed", self, "clearBtn_pressed") Clear_Btn.connect("pressed", self, "clearBtn_pressed")
Back_Btn.connect("pressed", self, "backBtn_pressed") Back_Btn.connect("pressed", self, "backBtn_pressed")

View File

@ -25,11 +25,17 @@ var Env : EvaEnv
var Parent var Parent
# ---------------------------------------------------------- GLOBALS END # ---------------------------------------------------------- GLOBALS END
func get_class():
return "Eva"
func _init(parent, evalOut): func _init(parent, evalOut):
EvalOut = evalOut EvalOut = evalOut
Env = EvaEnv.new(EvalOut) Env = EvaEnv.new(EvalOut)
Parent = parent Parent = parent
if Parent == null:
Env.setup_Globals()
func eval( ast ): func eval( ast ):
match ast.type(): match ast.type():
NType.program : NType.program :
@ -47,7 +53,7 @@ func eval( ast ):
NType.block : NType.block :
return eval_Block( ast ) return eval_Block( ast )
NType.conditional: NType.conditional :
var condition = eval( ast.arg(1) ) var condition = eval( ast.arg(1) )
if condition: if condition:
@ -58,7 +64,7 @@ func eval( ast ):
if ast.num_args() > 2: if ast.num_args() > 2:
return eval( ast.arg(3)) return eval( ast.arg(3))
NType.expr_While: NType.expr_While :
var result var result
while eval( ast.arg(1) ): while eval( ast.arg(1) ):
@ -66,32 +72,33 @@ func eval( ast ):
return result 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.identifier : NType.identifier :
var identifier = ast.arg(1) return eval_Lookup( ast )
if Parent != null && !Env.has( identifier):
return Parent.Env.lookup( identifier )
return Env.lookup( identifier )
NType.op_Assign : NType.op_Assign :
var symbol = ast.arg(1) return eval_Assign( ast )
var value = eval( ast.arg(2) )
if Parent != null && !Env.has( symbol): NType.op_Fn:
return Parent.Env.set( symbol, value ) return eval_Func( ast )
return Env.set( symbol, value )
NType.op_Greater: NType.op_Greater:
return eval( ast.arg(1) ) > eval( ast.arg(2) ) return eval( ast.arg(1) ) > eval( ast.arg(2) )
NType.op_Lesser: NType.op_Lesser:
return eval( ast.arg(1) ) < eval( ast.arg(2) ) return eval( ast.arg(1) ) < eval( ast.arg(2) )
NType.op_GreaterEqual: NType.op_GreaterEqual:
return eval( ast.arg(1) ) >= eval( ast.arg(2) ) return eval( ast.arg(1) ) >= eval( ast.arg(2) )
NType.op_LesserEqual: NType.op_LesserEqual:
return eval( ast.arg(1) ) <= eval( ast.arg(2) ) return eval( ast.arg(1) ) <= eval( ast.arg(2) )
@ -100,10 +107,14 @@ func eval( ast ):
NType.variable : NType.variable :
var symbol = ast.arg(1) var symbol = ast.arg(1)
var value = eval( ast.arg(2) ) var value
Env.define_Var(symbol, value) if ast.num_args() == 2:
return value value = eval( ast.arg(2) )
Env.define(symbol, value)
return Env.lookup(symbol)
if ast.is_Number() : if ast.is_Number() :
return float( ast.arg(1) ) return float( ast.arg(1) )
@ -125,6 +136,44 @@ func eval_Block( ast ):
return result 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
return fnEva.eval( body )
func eval_Numeric( ast ): func eval_Numeric( ast ):
if ast.type() == NType.op_Add: if ast.type() == NType.op_Add:
var result = 0.0; var index = 1 var result = 0.0; var index = 1
@ -136,6 +185,9 @@ func eval_Numeric( ast ):
return result return result
if ast.type() == NType.op_Sub: if ast.type() == NType.op_Sub:
if ast.num_args() == 2:
return -eval( ast.arg(1) )
var result = 0.0; var index = 1 var result = 0.0; var index = 1
while index <= ast.num_args(): while index <= ast.num_args():
@ -171,9 +223,11 @@ func eval_Print( ast ):
return null return null
func get_EnvSnapshot(): func get_EnvSnapshot():
var snapshot = Env.Records.duplicate(true) var \
snapshot = EvaEnv.new(EvalOut)
snapshot.Records = Env.Records.duplicate(true)
if Parent != null: if Parent != null:
snapshot[Parent] = Parent.Env.Records.duplicate(true) snapshot[Parent] = Parent.get_EnvSnapshot()
return snapshot return snapshot.to_Dictionary()

View File

@ -19,15 +19,17 @@ class_name EvaEnv
var Records : Dictionary var Records : Dictionary
func _init(errorOut): func define(symbol : String, value) :
ErrorOut = errorOut
func define_Var(symbol : String, value) :
Records[symbol] = value Records[symbol] = value
func has(symbol : String) : func has(symbol : String) :
return Records.has(symbol) return Records.has(symbol)
func lookup(symbol : String) :
check(Records.has(symbol), String("Symbol not found in environment records"))
return Records[symbol]
func set(symbol : String, value) : func set(symbol : String, value) :
check(Records.has(symbol), String("Symbol not found in environment records")) check(Records.has(symbol), String("Symbol not found in environment records"))
@ -35,7 +37,83 @@ func set(symbol : String, value) :
return Records[symbol] return Records[symbol]
func lookup(symbol : String) : func setup_Globals():
check(Records.has(symbol), String("Symbol not found in environment records")) Records["null"] = null
Records["true"] = true
Records["false"] = false
return Records[symbol]
func _init(errorOut):
ErrorOut = errorOut
# Serialization ----------------------------------------------------
var SEva
func array_Serialize(array, fn_objSerializer) :
var result = []
for entry in array :
if typeof(entry) == TYPE_ARRAY :
result.append( array_Serialize( entry, fn_objSerializer ))
elif typeof(entry) == TYPE_OBJECT :
if entry.get_class() == "Eva":
result.append(entry)
else:
fn_objSerializer.set_instance(entry)
result.append( fn_objSerializer.call_func() )
else :
result.append( entry )
return result
func to_SExpression():
var expression = []
for key in Records.keys() :
var entry = [key]
var Value = Records[key]
if typeof( Value ) == TYPE_ARRAY :
var \
to_SExpression_Fn = FuncRef.new()
to_SExpression_Fn.set_function("to_SExpression")
var array = array_Serialize( Value, to_SExpression_Fn )
entry.append(array)
elif typeof( Value ) == TYPE_OBJECT :
entry.append( Value.to_SExpression() )
else :
entry.append(Value)
expression.append(entry)
return expression
func to_Dictionary():
var result = {}
for key in Records.keys() :
var Value = Records[key]
if typeof(Value) == TYPE_ARRAY :
var \
to_SExpression_Fn = FuncRef.new()
to_SExpression_Fn.set_function("to_SExpression")
var array = array_Serialize( Value, to_SExpression_Fn )
result[key] = array
elif typeof(Value) == TYPE_OBJECT :
result[key] = Value.to_SExpression()
else :
result[key] = Value
return result
# Serialization END -------------------------------------------------

View File

@ -24,12 +24,13 @@ const TType : Dictionary = \
cmt_SL = "Comment Single-Line", cmt_SL = "Comment Single-Line",
cmt_ML = "Comment Multi-Line", cmt_ML = "Comment Multi-Line",
def_Block = "Expression Block Start",
def_Start = "Expression Start", def_Start = "Expression Start",
def_End = "Expression End", def_End = "Expression End",
def_Block = "Expression Block Start",
def_Cond = "Expression Conditional", def_Cond = "Expression Conditional",
def_While = "Expression While", def_While = "Expression While",
def_Var = "Variable Declaration", def_Var = "Variable Declaration",
def_Func = "Function Declaration",
literal_Number = "Literal: Number", literal_Number = "Literal: Number",
literal_String = "Literal: String", literal_String = "Literal: String",
@ -50,12 +51,13 @@ const Spec : Dictionary = \
TType.fmt_S : "start whitespace.repeat(1-).lazy", TType.fmt_S : "start whitespace.repeat(1-).lazy",
TType.def_Block : "start \"begin\"",
TType.def_Start : "start \\(", TType.def_Start : "start \\(",
TType.def_End : "start \\)", TType.def_End : "start \\)",
TType.def_Block : "start \"begin\"",
TType.def_Cond : "start \"if\"", TType.def_Cond : "start \"if\"",
TType.def_While : "start \"while\"", TType.def_While : "start \"while\"",
TType.def_Var : "start \"var\"", TType.def_Var : "start \"var\"",
TType.def_Func : "start \"def\"",
TType.literal_Number : \ TType.literal_Number : \
"""start """start

View File

@ -21,6 +21,8 @@ const NType = \
{ {
program = "Program", program = "Program",
empty = "Empty",
block = "Scope Block", block = "Scope Block",
conditional = "Conditional", conditional = "Conditional",
@ -30,6 +32,7 @@ const NType = \
literal_String = "Literal: String", literal_String = "Literal: String",
op_Assign = "Assignment", op_Assign = "Assignment",
op_Fn = "Function Call",
op_Add = "+", op_Add = "+",
op_Sub = "-", op_Sub = "-",
@ -42,9 +45,11 @@ const NType = \
op_LesserEqual = "<=", op_LesserEqual = "<=",
fn_Print = "Print", fn_Print = "Print",
fn_User = "User Function",
fn_Params = "Function Parameters",
identifier = "Identifier", identifier = "Identifier",
variable = "Variable" variable = "Variable"
} }
class ASTNode: class ASTNode:
@ -163,6 +168,8 @@ func parse_Expression():
node = parse_While() node = parse_While()
TType.def_Var: TType.def_Var:
node = parse_Variable() node = parse_Variable()
TType.def_Func:
node = parse_fn_User()
TType.fn_Print: TType.fn_Print:
node = parse_fn_Print() node = parse_fn_Print()
TType.op_Assgin: TType.op_Assgin:
@ -171,7 +178,8 @@ func parse_Expression():
node = parse_op_Numeric() node = parse_op_Numeric()
TType.op_Relational: TType.op_Relational:
node = parse_op_Relational() node = parse_op_Relational()
TType.identifier:
node = parse_op_Fn()
var arg = 1 var arg = 1
while NextToken.Type != TType.def_End: while NextToken.Type != TType.def_End:
@ -184,6 +192,10 @@ func parse_Expression():
eat(TType.def_End) eat(TType.def_End)
if node == null:
node = ASTNode.new()
node.set_Type(NType.empty)
return node return node
func parse_Block(): func parse_Block():
@ -222,11 +234,40 @@ func parse_Variable():
node.add_TokenValue( NextToken ) node.add_TokenValue( NextToken )
eat(TType.identifier) eat(TType.identifier)
if NextToken.Type == TType.def_Start : return node
node.add_Expr( parse_Expression() )
else : func parse_fn_User():
node.add_Expr( parse_Literal() ) var \
node = ASTNode.new()
node.set_Type(NType.fn_User)
eat(TType.def_Func)
check( NextToken.Type == TType.identifier,
String("Parser - parse_op_Assign: NextToken should have been identifier, Type: {type} Value: {value}") \
.format({"type" : NextToken.Type, "value" : NextToken.Value })
)
node.add_TokenValue( NextToken )
eat(TType.identifier)
# Parameters
var \
pNode = ASTNode.new()
pNode.set_Type(NType.fn_Params)
eat(TType.def_Start)
while NextToken.Type != TType.def_End:
check( NextToken.Type == TType.identifier,
String("Parser - parse_op_Assign: NextToken should have been identifier, Type: {type} Value: {value}") \
.format({"type" : NextToken.Type, "value" : NextToken.Value })
)
pNode.add_TokenValue(NextToken)
eat(TType.identifier)
eat(TType.def_End)
node.add_Expr( pNode )
return node return node
@ -306,6 +347,15 @@ func parse_op_Relational():
return node return node
func parse_op_Fn():
var \
node = ASTNode.new()
node.set_Type(NType.op_Fn)
node.add_TokenValue( NextToken )
eat(TType.identifier)
return node
func parse_Literal(): func parse_Literal():
var node = ASTNode.new() var node = ASTNode.new()