From bd6e39ecdd7273fa12b251cc3232ee09764dc737 Mon Sep 17 00:00:00 2001 From: Ed94 Date: Sun, 24 Jul 2022 07:54:17 -0400 Subject: [PATCH] EOI: Lecture 10 & 11 complete. --- App/EoI/EoI_Viewer.tscn | 24 +++++--- App/EoI/Scripts/EoI_Viewer.gd | 26 ++++++--- App/EoI/Scripts/Eva.gd | 102 ++++++++++++++++++++++++++-------- App/EoI/Scripts/EvaEnv.gd | 94 ++++++++++++++++++++++++++++--- App/EoI/Scripts/Lexer.gd | 6 +- App/EoI/Scripts/Parser.gd | 64 ++++++++++++++++++--- 6 files changed, 259 insertions(+), 57 deletions(-) diff --git a/App/EoI/EoI_Viewer.tscn b/App/EoI/EoI_Viewer.tscn index c9750af..2eebf97 100644 --- a/App/EoI/EoI_Viewer.tscn +++ b/App/EoI/EoI_Viewer.tscn @@ -42,17 +42,27 @@ margin_bottom = -2.0 [node name="Eva_Interpret_Btn" type="Button" parent="VBox"] margin_right = 203.0 -margin_bottom = 30.0 +margin_bottom = 27.0 rect_pivot_offset = Vector2( -123, -302 ) size_flags_vertical = 3 size_flags_stretch_ratio = 0.08 theme = ExtResource( 1 ) text = "Eva: Interpret" -[node name="ClearOutput_Btn" type="Button" parent="VBox"] -margin_top = 34.0 +[node name="Eva_ResetEnv_Btn" type="Button" parent="VBox"] +margin_top = 31.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 ) size_flags_vertical = 3 size_flags_stretch_ratio = 0.08 @@ -61,14 +71,14 @@ text = "Clear Output" [node name="Separator" type="HSeparator" parent="VBox"] modulate = Color( 0.145098, 0.145098, 0.164706, 0 ) -margin_top = 68.0 +margin_top = 95.0 margin_right = 203.0 -margin_bottom = 443.0 +margin_bottom = 445.0 size_flags_vertical = 15 theme = ExtResource( 5 ) [node name="Back_Btn" type="Button" parent="VBox"] -margin_top = 447.0 +margin_top = 449.0 margin_right = 203.0 margin_bottom = 478.0 rect_pivot_offset = Vector2( -123, -302 ) diff --git a/App/EoI/Scripts/EoI_Viewer.gd b/App/EoI/Scripts/EoI_Viewer.gd index d5b54d4..941bd84 100644 --- a/App/EoI/Scripts/EoI_Viewer.gd +++ b/App/EoI/Scripts/EoI_Viewer.gd @@ -13,19 +13,19 @@ var Eva : SEva # UX -------------------------------------------------------- -onready var Editor = get_node("Editor_TEdit") -onready var Output = get_node("Output_TEdit") -onready var Debug = get_node("Debug_TEdit") -onready var Eva_Btn = get_node("VBox/Eva_Interpret_Btn") -onready var Clear_Btn = get_node("VBox/ClearOutput_Btn") -onready var Back_Btn = get_node("VBox/Back_Btn") +onready var Editor = get_node("Editor_TEdit") +onready var Output = get_node("Output_TEdit") +onready var Debug = get_node("Debug_TEdit") +onready var Eva_Btn = get_node("VBox/Eva_Interpret_Btn") +onready var Eva_Reset_Btn = get_node("VBox/Eva_ResetEnv_Btn") +onready var Clear_Btn = get_node("VBox/ClearOutput_Btn") +onready var Back_Btn = get_node("VBox/Back_Btn") func evaBtn_pressed(): Lexer = SLexer.new(Editor.text, Output) Parser = SParser.new(Lexer, Output) - Eva = SEva.new(null, Output) - + var ast = Parser.parse() var result = Eva.eval(ast) @@ -34,14 +34,22 @@ func evaBtn_pressed(): 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(): Output.text = "" func backBtn_pressed(): queue_free() - + func _ready(): + Eva = SEva.new(null, Output) + Debug.text = JSON.print( Eva.get_EnvSnapshot(), "\t" ) + Eva_Btn.connect("pressed", self, "evaBtn_pressed") + Eva_Reset_Btn.connect("pressed", self, "evaResetBtn_pressed") Clear_Btn.connect("pressed", self, "clearBtn_pressed") Back_Btn.connect("pressed", self, "backBtn_pressed") diff --git a/App/EoI/Scripts/Eva.gd b/App/EoI/Scripts/Eva.gd index 0a32c89..9e381bb 100644 --- a/App/EoI/Scripts/Eva.gd +++ b/App/EoI/Scripts/Eva.gd @@ -25,10 +25,16 @@ 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(): @@ -47,7 +53,7 @@ func eval( ast ): NType.block : return eval_Block( ast ) - NType.conditional: + NType.conditional : var condition = eval( ast.arg(1) ) if condition: @@ -58,40 +64,41 @@ func eval( ast ): if ast.num_args() > 2: return eval( ast.arg(3)) - NType.expr_While: + 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.identifier : - var identifier = ast.arg(1) - - if Parent != null && !Env.has( identifier): - return Parent.Env.lookup( identifier ) - - return Env.lookup( identifier ) + return eval_Lookup( ast ) NType.op_Assign : - var symbol = ast.arg(1) - var value = eval( ast.arg(2) ) + return eval_Assign( ast ) - if Parent != null && !Env.has( symbol): - return Parent.Env.set( symbol, value ) - - return Env.set( symbol, value ) + 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) ) @@ -100,10 +107,14 @@ func eval( ast ): NType.variable : var symbol = ast.arg(1) - var value = eval( ast.arg(2) ) - - Env.define_Var(symbol, value) - return value + 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) ) @@ -125,6 +136,44 @@ func eval_Block( ast ): 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 ): if ast.type() == NType.op_Add: var result = 0.0; var index = 1 @@ -136,6 +185,9 @@ func eval_Numeric( ast ): 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(): @@ -171,9 +223,11 @@ func eval_Print( ast ): return null func get_EnvSnapshot(): - var snapshot = Env.Records.duplicate(true) + var \ + snapshot = EvaEnv.new(EvalOut) + snapshot.Records = Env.Records.duplicate(true) if Parent != null: - snapshot[Parent] = Parent.Env.Records.duplicate(true) + snapshot[Parent] = Parent.get_EnvSnapshot() - return snapshot + return snapshot.to_Dictionary() diff --git a/App/EoI/Scripts/EvaEnv.gd b/App/EoI/Scripts/EvaEnv.gd index 71a2ee7..95f04e1 100644 --- a/App/EoI/Scripts/EvaEnv.gd +++ b/App/EoI/Scripts/EvaEnv.gd @@ -18,16 +18,18 @@ class_name EvaEnv var Records : Dictionary - -func _init(errorOut): - ErrorOut = errorOut - -func define_Var(symbol : String, value) : + +func define(symbol : String, value) : Records[symbol] = value func has(symbol : String) : 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) : check(Records.has(symbol), String("Symbol not found in environment records")) @@ -35,7 +37,83 @@ func set(symbol : String, value) : return Records[symbol] -func lookup(symbol : String) : - check(Records.has(symbol), String("Symbol not found in environment records")) +func setup_Globals(): + Records["null"] = null + Records["true"] = true + Records["false"] = false + + +func _init(errorOut): + ErrorOut = errorOut + + +# Serialization ---------------------------------------------------- +var SEva - return Records[symbol] +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 ------------------------------------------------- diff --git a/App/EoI/Scripts/Lexer.gd b/App/EoI/Scripts/Lexer.gd index bb2e3d4..a5e9a8c 100644 --- a/App/EoI/Scripts/Lexer.gd +++ b/App/EoI/Scripts/Lexer.gd @@ -24,12 +24,13 @@ const TType : Dictionary = \ cmt_SL = "Comment Single-Line", cmt_ML = "Comment Multi-Line", - def_Block = "Expression Block Start", def_Start = "Expression Start", def_End = "Expression End", + def_Block = "Expression Block Start", def_Cond = "Expression Conditional", def_While = "Expression While", def_Var = "Variable Declaration", + def_Func = "Function Declaration", literal_Number = "Literal: Number", literal_String = "Literal: String", @@ -50,12 +51,13 @@ const Spec : Dictionary = \ TType.fmt_S : "start whitespace.repeat(1-).lazy", - TType.def_Block : "start \"begin\"", TType.def_Start : "start \\(", TType.def_End : "start \\)", + TType.def_Block : "start \"begin\"", TType.def_Cond : "start \"if\"", TType.def_While : "start \"while\"", TType.def_Var : "start \"var\"", + TType.def_Func : "start \"def\"", TType.literal_Number : \ """start diff --git a/App/EoI/Scripts/Parser.gd b/App/EoI/Scripts/Parser.gd index dbc328d..25a6208 100644 --- a/App/EoI/Scripts/Parser.gd +++ b/App/EoI/Scripts/Parser.gd @@ -21,6 +21,8 @@ const NType = \ { program = "Program", + empty = "Empty", + block = "Scope Block", conditional = "Conditional", @@ -30,6 +32,7 @@ const NType = \ literal_String = "Literal: String", op_Assign = "Assignment", + op_Fn = "Function Call", op_Add = "+", op_Sub = "-", @@ -42,9 +45,11 @@ const NType = \ op_LesserEqual = "<=", fn_Print = "Print", + fn_User = "User Function", + fn_Params = "Function Parameters", identifier = "Identifier", - variable = "Variable" + variable = "Variable" } class ASTNode: @@ -163,6 +168,8 @@ func parse_Expression(): node = parse_While() TType.def_Var: node = parse_Variable() + TType.def_Func: + node = parse_fn_User() TType.fn_Print: node = parse_fn_Print() TType.op_Assgin: @@ -171,7 +178,8 @@ func parse_Expression(): node = parse_op_Numeric() TType.op_Relational: node = parse_op_Relational() - + TType.identifier: + node = parse_op_Fn() var arg = 1 while NextToken.Type != TType.def_End: @@ -184,6 +192,10 @@ func parse_Expression(): eat(TType.def_End) + if node == null: + node = ASTNode.new() + node.set_Type(NType.empty) + return node func parse_Block(): @@ -221,12 +233,41 @@ func parse_Variable(): node.add_TokenValue( NextToken ) eat(TType.identifier) - - if NextToken.Type == TType.def_Start : - node.add_Expr( parse_Expression() ) - else : - node.add_Expr( parse_Literal() ) + return node + +func parse_fn_User(): + 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 @@ -306,6 +347,15 @@ func parse_op_Relational(): 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(): var node = ASTNode.new()