From 5e3fd7a13c9788d55d456a76d59c10b5a322f7ed Mon Sep 17 00:00:00 2001 From: Ed94 Date: Tue, 12 Jul 2022 03:48:08 -0400 Subject: [PATCH] BAPFS Lecture 14 --- Builds/Tests/12.Functions.uf | 15 + Editor/Lectures/Lecture.14.gd | 1144 +++++++++++++++++++++++++++++++++ Editor/project.godot | 2 +- 3 files changed, 1160 insertions(+), 1 deletion(-) create mode 100644 Builds/Tests/12.Functions.uf create mode 100644 Editor/Lectures/Lecture.14.gd diff --git a/Builds/Tests/12.Functions.uf b/Builds/Tests/12.Functions.uf new file mode 100644 index 0000000..f622d21 --- /dev/null +++ b/Builds/Tests/12.Functions.uf @@ -0,0 +1,15 @@ +def square(value) +{ + return value * value; +} + +def empty() { return; } +def empty_2() { } + +def multi_variable(a, b) +{ + return a * b; +} + +// square(2); + diff --git a/Editor/Lectures/Lecture.14.gd b/Editor/Lectures/Lecture.14.gd new file mode 100644 index 0000000..7cc8a36 --- /dev/null +++ b/Editor/Lectures/Lecture.14.gd @@ -0,0 +1,1144 @@ +extends Node + +# This closesly follows the source provided in the lectures. +# Later on after the lectures are complete or when I deem +# Necessary there will be heavy refactors. + +const TokenType = \ +{ + Program = "Program", + + # Comments + CommentLine = "CommentLine", + CommentMultiLine = "CommentMultiLine", + + # Formatting + Whitespace = "Whitespace", + + # Expressions + ExpressionPStart = "ExpresssionParenthesisStart", + ExpressionPEnd = "ExpressionParenthesisEnd", + + # Logical + RelationalOp = "RelationalOperator", + EqualityOp = "EqualityOperator", + Logical_And = "Logical_And_Op", + Logical_Or = "Logical_Or_Op", + Logical_Not = "Logical_Not_Op", + + # Arithmetic + ComplexAssignment = "ComplexAssignment", + Assignment = "Assignment", + AdditiveOp = "AdditiveOperator", + MultiplicativeOp = "MultiplicativeOperator", + + # Conditional + Conditional_if = "if Conditional", + Conditional_else = "else Conditional", + + # Statements + StatementEnd = "StatementEnd", + StmtBlockStart = "BlockStatementStart", + StmtBlockEnd = "BlockStatementEnd", + CommaDelimiter = "CommaDelimiter", + + # Literals + Number = "Number", + String = "String", + + # Symbols + Bool_true = "Boolean True", + Bool_false = "Boolean False", + VarDeclare = "Variable Declaration", + Identifier = "Identifier", + NullValue = "Null Value", + While = "While", + Do = "Do", + For = "For", + FuncDeclare = "Function Delcaration", + Return = "Return" +} + +const TokenSpec = \ +{ + # Comments + TokenType.CommentLine : "^\\/\\/.*", + TokenType.CommentMultiLine : "^\\/\\*[\\s\\S]*?\\*\\/", + + # Formatting + TokenType.Whitespace : "^\\s+", + + # Expressions + TokenType.ExpressionPStart : "^\\(", + TokenType.ExpressionPEnd : "^\\)", + + # Logical + TokenType.RelationalOp : "^[>\\<]=?", + TokenType.EqualityOp : "^[=!]=", + TokenType.Logical_And : "^&&", + TokenType.Logical_Or : "^\\|\\|", + TokenType.Logical_Not : "^!", + + # Arithmetic + TokenType.ComplexAssignment : "^[*\\/\\+\\-]=", + TokenType.Assignment : "^=", + TokenType.AdditiveOp : "^[+\\-]", + TokenType.MultiplicativeOp : "^[*\\/]", + + # Literal + TokenType.Number : "\\d+", + TokenType.String : "^\"[^\"]*\"", + + TokenType.Conditional_if : "^\\bif\\b", + TokenType.Conditional_else : "^\\belse\\b", + + # Statements + TokenType.StatementEnd : "^;", + TokenType.StmtBlockStart : "^{", + TokenType.StmtBlockEnd : "^}", + TokenType.CommaDelimiter : "^,", + + # Symbols + TokenType.Bool_true : "^\\btrue\\b", + TokenType.Bool_false : "^\\bfalse\\b", + TokenType.VarDeclare : "^\\blet\\b", + TokenType.NullValue : "^\\bnull\\b", + TokenType.While : "^\\bwhile\\b", + TokenType.Do : "^\\bdo\\b", + TokenType.For : "^\\bfor\\b", + TokenType.FuncDeclare : "^\\bdef\\b", + TokenType.Return : "^\\breturn\\b", + TokenType.Identifier : "^\\w+" +} + +class Token: + var Type : String + var Value : String + + func to_Dictionary(): + var result = \ + { + Type = self.Type, + Value = self.Value + } + return result + +class Tokenizer: + var SrcTxt : String + var Cursor : int; + + # Sets up the tokenizer with the program source text. + func init(programSrcText): + SrcTxt = programSrcText + Cursor = 0 + + # Provides the next token in the source text. + func next_Token(): + if reached_EndOfTxt() == true : + return null + + var srcLeft = SrcTxt.substr(Cursor) + var regex = RegEx.new() + var token = Token.new() + + for type in TokenSpec : + regex.compile(TokenSpec[type]) + + var result = regex.search(srcLeft) + if result == null || result.get_start() != 0 : + continue + + # Skip Comments + if type == TokenType.CommentLine || type == TokenType.CommentMultiLine : + Cursor += result.get_string().length() + return next_Token() + + # Skip Whitespace + if type == TokenType.Whitespace : + var addVal = result.get_string().length() + Cursor += addVal + + return next_Token() + + token.Type = type + token.Value = result.get_string() + Cursor += ( result.get_string().length() ) + + return token + + var assertStrTmplt = "next_token: Source text not understood by tokenizer at Cursor pos: {value}" + var assertStr = assertStrTmplt.format({"value" : Cursor}) + assert(true != true, assertStr) + return null + + func reached_EndOfTxt(): + return Cursor >= ( SrcTxt.length() ) + +var GTokenizer = Tokenizer.new() + + + +const AST_Format = \ +{ + Dictionary = "Dictionary", + SExpression = "S-Expression" +} + +const SyntaxNodeType = \ +{ + NumericLiteral = "NumericLiteral", + StringLiteral = "StringLiteral", + ExpressionStatement = "ExpressionStatement", + BlockStatement = "BlockStatement", + EmptyStatement = "EmptyStatement", + BinaryExpression = "BinaryExpression", + Identifier = "Identifier", + AssignmentExpression = "AssignmentExpression", + VariableStatement = "VariableStatement", + VariableDeclaration = "VariableDeclaration", + ConditionalStatement = "ConditionalStatement", + BooleanLiteral = "BooleanLiteral", + NullLiteral = "NullLiteral", + LogicalExpression = "LogicalExpression", + UnaryExpression = "UnaryExpression", + WhileStatement = "WhileStatement", + DoWhileStatement = "DoWhileStatement", + ForStatement = "ForStatement", + FunctionDeclaration = "FunctionDeclaration", + ReturnStatement = "ReturnStatement" +} + +class SyntaxNode: + var Type : String + var Value # Not specifing a type implicity declares a Variant type. + + 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 : + fn_objSerializer.set_instance(entry) + result.append( fn_objSerializer.call_func() ) + else : + result.append( entry ) + + return result + + func to_SExpression(): + var expression = [ Type ] + + if typeof(Value) == TYPE_ARRAY : + var \ + to_SExpression_Fn = FuncRef.new() + to_SExpression_Fn.set_function("to_SExpression") + + var array = array_Serialize( self.Value, to_SExpression_Fn ) + + expression.append(array) + return expression + + if typeof(Value) == TYPE_OBJECT : + var result = [ Type, Value.to_SExpression() ] + return result + + expression.append(Value) + return expression + + func to_Dictionary(): + if typeof(Value) == TYPE_ARRAY : + var \ + to_Dictionary_Fn = FuncRef.new() + to_Dictionary_Fn.set_function("to_Dictionary") + + var array = array_Serialize( self.Value, to_Dictionary_Fn ) + var result = \ + { + Type = self.Type, + Value = array + } + return result + + if typeof(Value) == TYPE_OBJECT : + var result = \ + { + Type = self.Type, + Value = self.Value.to_Dictionary() + } + return result + + var result = \ + { + Type = self.Type, + Value = self.Value + } + return result + +class Parser: + var TokenizerRef : Tokenizer + var NextToken : Token + + # --------------------------------------------------------------------- HELPERS + + # Gets the next token only if the current token is the specified intended token (tokenType) + func eat(tokenType): + var currToken = self.NextToken + + assert(currToken != null, "eat: NextToken was null") + + var assertStrTmplt = "eat: Unexpected token: {value}, expected: {type}" + var assertStr = assertStrTmplt.format({"value" : currToken.Value, "type" : tokenType}) + + assert(currToken.Type == tokenType, assertStr) + + NextToken = TokenizerRef.next_Token() + + return currToken + + func is_Literal(): + return \ + NextToken.Type == TokenType.Number \ + || NextToken.Type == TokenType.String \ + || NextToken.Type == TokenType.Bool_true \ + || NextToken.Type == TokenType.Bool_false \ + || NextToken.Type == TokenType.NullValue + + # BinaryExpression + # : MultiplicativeExpression + # | AdditiveExpression + # ; + func parse_BinaryExpression(parse_fn, operatorToken): + var left = parse_fn.call_func() + + while NextToken.Type == operatorToken: + var operator = eat(operatorToken) + var right = parse_fn.call_func() + + var \ + nestedNode = SyntaxNode.new() + nestedNode.Type = SyntaxNodeType.BinaryExpression + nestedNode.Value = [] + nestedNode.Value.append(operator.Value) + nestedNode.Value.append(left) + nestedNode.Value.append(right) + + left = nestedNode; + + return left + + # LogicalExpression + # : LogicalAndExpression + # | LogicalOrExpression + # ; + func parse_LogicalExpression(parse_fn, operatorToken): + var left = parse_fn.call_func() + + while NextToken.Type == operatorToken : + var operator = eat(operatorToken).Value + var right = parse_fn.call_func() + + var \ + nestedNode = SyntaxNode.new() + nestedNode.Type = SyntaxNodeType.LogicalExpression + nestedNode.Value = [] + nestedNode.Value.append(operator) + nestedNode.Value.append(left) + nestedNode.Value.append(right) + + left = nestedNode + + return left + + # ------------------------------------------------------------------ END HELPERS + + # Parses the text program description into an AST. + func parse(TokenizerRef): + self.TokenizerRef = TokenizerRef + + NextToken = TokenizerRef.next_Token() + + return parse_Program() + + # > parse + # Program + # : StatementList + # : Literal + # ; + func parse_Program(): + var \ + node = SyntaxNode.new() + node.Type = TokenType.Program + node.Value = parse_StatementList(null) + + return node + + # > Program + # > BlockStatement + # StatementList + # : Statement + # | StatementList Statement -> Statement ... + # ; + func parse_StatementList(endToken): + var statementList = [ parse_Statement() ] + + while NextToken != null && NextToken.Type != endToken : + statementList.append( parse_Statement() ) + + return statementList + + # > StatementList + # > If_Statement + # > WhileStatement + # Statement + # : ExpressionStatement + # | BlockStatement + # | EmptyStatement + # | VariableStatement + # | If_Statement + # | IterationStatement + # | FunctionDeclaration + # | ReturnStatement + # ; + func parse_Statement(): + if NextToken == null : + return null + + match NextToken.Type : + TokenType.Conditional_if : + return parse_If_Statement() + TokenType.StatementEnd : + return parse_EmptyStatement() + TokenType.StmtBlockStart : + return parse_BlockStatement() + TokenType.VarDeclare : + return parse_VariableStatement() + TokenType.While : + return parse_IterationStatement() + TokenType.Do : + return parse_IterationStatement() + TokenType.For : + return parse_IterationStatement() + TokenType.FuncDeclare: + return parse_FunctionDeclaration() + TokenType.Return: + return parse_ReturnStatement() + + return parse_ExpressionStatement() + + # If Statement + # : if ( Expression ) Statement + # | if ( Expression ) Statement else Statement + # ; + func parse_If_Statement(): + eat(TokenType.Conditional_if) + + eat(TokenType.ExpressionPStart) + var condition = parse_Expression() + eat(TokenType.ExpressionPEnd) + + var consequent = parse_Statement() + var alternative = null + + if NextToken != null && NextToken.Type == TokenType.Conditional_else : + eat(TokenType.Conditional_else) + alternative = parse_Statement() + + var \ + node = SyntaxNode.new() + node.Type = SyntaxNodeType.ConditionalStatement + node.Value = [ condition, consequent, alternative ] + + return node + + # > Statement + # EmptyStatement + # ; + func parse_EmptyStatement(): + eat(TokenType.StatementEnd) + + var \ + node = SyntaxNode.new() + node.Type = SyntaxNodeType.EmptyStatement + + return node + + # > Statement + # BlockStatement + # : { OptStatementList } + # ; + func parse_BlockStatement(): + eat(TokenType.StmtBlockStart) + + var \ + node = SyntaxNode.new() + node.Type = SyntaxNodeType.BlockStatement + + if NextToken.Type != TokenType.StmtBlockEnd : + node.Value = parse_StatementList(TokenType.StmtBlockEnd) + else : + node.Value = [] + + eat(TokenType.StmtBlockEnd) + + return node + + # > Statement + # VariableStatement + # : VariableStatementInit StatementEnd + # ; + func parse_VariableStatement(): + var varStatement = parse_VariableStatementInit() + + eat(TokenType.StatementEnd) + + return varStatement + + # > VariableStatement + # VariableStatementInit + # : VarDeclare VariableDeclarationList + # ; + func parse_VariableStatementInit(): + eat(TokenType.VarDeclare) + + var declarations = parse_VariableDeclarationList() + + var \ + node = SyntaxNode.new() + node.Type = SyntaxNodeType.VariableStatement + node.Value = declarations + + return node + + # > Statement + # IterationStatement + # : WhileStatement + # | DoWhileStatement + # | ForStatement + # ; + func parse_IterationStatement(): + match NextToken.Type: + TokenType.While : + return parse_WhileStatement() + TokenType.Do : + return parse_DoWhileStatement() + TokenType.For : + return parse_ForStatement() + + # > IterationStatement + # WhileStatement + # : while ( Expression ) Statement + # ; + func parse_WhileStatement(): + eat(TokenType.While) + + eat(TokenType.ExpressionPStart) + var condition = parse_Expression() + eat(TokenType.ExpressionPEnd) + + var body = parse_Statement() + var \ + node = SyntaxNode.new() + node.Type = SyntaxNodeType.WhileStatement + node.Value = [ condition, body ] + + return node + + # > IterationStatement + # DoWhileStatement + # : do Statement while ( Expression ) + # ; + func parse_DoWhileStatement(): + eat(TokenType.Do) + + var body = parse_Statement() + + eat(TokenType.While) + + eat(TokenType.ExpressionPStart) + var condition = parse_Expression() + eat(TokenType.ExpressionPEnd) + + eat(TokenType.StatementEnd) + + var \ + node = SyntaxNode.new() + node.Type = SyntaxNodeType.DoWhileStatement + node.Value = [ condition, body ] + + return node + + # > IterationStatement + # ForStatement + # : for ( + # OptForStatementInit ; + # OptExpression ; + # OptExpression + # ) + # Statement + # ; + func parse_ForStatement(): + eat(TokenType.For) + + eat(TokenType.ExpressionPStart) + + var init = null + var condition = null + var update = null + + if NextToken.Type != TokenType.StatementEnd : + init = parse_ForStatementInit() + eat(TokenType.StatementEnd) + + if NextToken.Type != TokenType.StatementEnd : + condition = parse_Expression() + eat(TokenType.StatementEnd) + + if NextToken.Type != TokenType.ExpressionPEnd : + update = parse_Expression() + + eat(TokenType.ExpressionPEnd) + + var body = parse_Statement() + + var \ + node = SyntaxNode.new() + node.Type = SyntaxNodeType.ForStatement + node.Value = [ init, condition, update, body ] + + return node + + # > ForStatement + # ForStatementInit + # : VariableStatemetnInit + # | Expression + # ; + func parse_ForStatementInit(): + if NextToken.Type == TokenType.VarDeclare : + return parse_VariableStatementInit() + + return parse_Expression() + + # > Statement + # FunctionDeclaration + # : FuncDeclare ( OptFomralParameterList ) BlockStatement + # ; + func parse_FunctionDeclaration(): + + eat(TokenType.FuncDeclare) + + var name = parse_Identifier() + + eat(TokenType.ExpressionPStart) + + var params + if NextToken.Type != TokenType.ExpressionPEnd : + params = parse_FormalParameterList() + else : + params = [] + + eat(TokenType.ExpressionPEnd) + + var body = parse_BlockStatement() + + var \ + node = SyntaxNode.new() + node.Type = SyntaxNodeType.FunctionDeclaration + node.Value = [ name, params, body ] + + return node + + # > FunctionDeclaration + # FormalParameterList + # : Identifier + # | FormalParameterList , Identifier + # ; + func parse_FormalParameterList(): + var params = [ parse_Identifier() ] + + while NextToken.Type == TokenType.CommaDelimiter : + eat(TokenType.CommaDelimiter) + params.append(parse_Identifier()) + + return params + + # > Statement + # ReturnStatement + # : return OptExpression + # ; + func parse_ReturnStatement(): + eat(TokenType.Return) + + var argument = null + if NextToken.Type != TokenType.StatementEnd : + argument = parse_Expression() + + var \ + node = SyntaxNode.new() + node.Type = SyntaxNodeType.ReturnStatement + node.Value = argument + + return node + + # > Statement + # ExpressionStatement + # : Expression + # ; + func parse_ExpressionStatement(): + var expression = parse_Expression() + eat(TokenType.StatementEnd) + + var \ + node = SyntaxNode.new() + node.Type = SyntaxNodeType.ExpressionStatement + node.Value = expression + + return expression + + # > ExpressionStatement + # > If_Statement + # > WhileStatement + # > PrimaryExpression + # Expression + # : AssignmentExpression + # ; + func parse_Expression(): + return parse_AssignmentExpression() + + # > VariableStatement + # VariableDeclarationList + # : VariableDeclaration + # | VariableDelcarationList , VariableDeclaration -> VariableDelcaration , ... + func parse_VariableDeclarationList(): + var \ + declarations = [] + declarations.append(parse_VariableDeclaration()) + + while NextToken.Type == TokenType.CommaDelimiter : + eat(TokenType.CommaDelimiter) + declarations.append(parse_VariableDeclaration()) + + return declarations + + # > VariableDeclarationList + # VariableDeclaration + # : Identifier OptVariableInitalizer + # ; + func parse_VariableDeclaration(): + var identifier = parse_Identifier() + var initalizer + if NextToken.Type != TokenType.StatementEnd && NextToken.Type != TokenType.CommaDelimiter : + initalizer = parse_VariableInitializer() + else : + initalizer = null + + var \ + node = SyntaxNode.new() + node.Type = SyntaxNodeType.VariableDeclaration + node.Value = [ identifier, initalizer ] + + return node + + # > VariableDeclaration + # VariableInitializer + # : Assignment AssignmentExpression + # ; + func parse_VariableInitializer(): + eat(TokenType.Assignment) + + return parse_AssignmentExpression() + + # > Expression + # > VariableInitializer + # > AssignmentExpression + # AssignmentExpression + # : RelationalExpression + # | ResolvedSymbol AssignmentOperator AssignmetnExpression + # ; + func parse_AssignmentExpression(): + var left = parse_LogicalOrExpression() + + if NextToken.Type != TokenType.Assignment && NextToken.Type != TokenType.ComplexAssignment : + return left + + var assignmentOp; + + if NextToken.Type == TokenType.Assignment : + assignmentOp = eat(TokenType.Assignment) + elif NextToken.Type == TokenType.ComplexAssignment : + assignmentOp = eat(TokenType.ComplexAssignment) + + var \ + node = SyntaxNode.new() + node.Type = SyntaxNodeType.AssignmentExpression + node.Value = \ + [ + assignmentOp.Value, + left, + parse_AssignmentExpression() + ] + + return node + + # > VariableDeclaration + # > ParenthesizedExpression + # Identifier + # : IdentifierSymbol + # ; + func parse_Identifier(): + var name = eat(TokenType.Identifier).Value + + var \ + node = SyntaxNode.new() + node.Type = SyntaxNodeType.Identifier + node.Value = name + + return node + + # > AssignmentExpression + # Logical Or Expression + # : LogicalAndExpression Logical_Or LogicalOrExpression + # | LogicalOrExpression + # ; + func parse_LogicalOrExpression(): + var \ + parseFn = FuncRef.new() + parseFn.set_instance(self) + parseFn.set_function("pasre_LogicalAndExpression") + + return parse_LogicalExpression(parseFn, TokenType.Logical_Or) + + # > LogicaOrExpression + # Logical And Expression + # : EqualityExpression Logical_And LogicalAndExpression + # | EqualityExpression + # ; + func pasre_LogicalAndExpression(): + var \ + parseFn = FuncRef.new() + parseFn.set_instance(self) + parseFn.set_function("parse_EqualityExpression") + + return parse_LogicalExpression(parseFn, TokenType.Logical_And) + + # Equality Operators: ==, != + # + # > LogicalAndExpression + # EqualityExpression + # : RelationalExpression EqualityOp RelationalExpression + # | RelationalExpression + # ; + func parse_EqualityExpression(): + var \ + parseFn = FuncRef.new() + parseFn.set_instance(self) + parseFn.set_function("parse_RelationalExpression") + + return parse_BinaryExpression(parseFn, TokenType.EqualityOp) + + # Relational Operators: >, >=, <, <= + # + # > EqualityExpression + # Relational Expression + # : AdditiveExpression + # | AdditiveExpression RelationalOp RelationalExpression + # ; + func parse_RelationalExpression(): + var \ + parseFn = FuncRef.new() + parseFn.set_instance(self) + parseFn.set_function("parse_AdditiveExpression") + + return parse_BinaryExpression(parseFn, TokenType.RelationalOp) + + # > RelationalExpression + # AdditiveExpression + # : MultiplicativeExpression + # | AdditiveExpression AdditiveOp MultiplicativeExpression -> MultiplicativeExpression AdditiveOp ... Literal + # ; + func parse_AdditiveExpression(): + var \ + parseFn = FuncRef.new() + parseFn.set_instance(self) + parseFn.set_function("parse_MultiplicativeExpression") + + return parse_BinaryExpression(parseFn, TokenType.AdditiveOp) + + # > AdditiveExpression + # MultiplicativeExpression + # : UnaryExpressioon + # : MultiplicativeExpression MultiplicativeOp UnaryExpression -> UnaryExpression MultiplicativeOp ... Literal + # ; + func parse_MultiplicativeExpression(): + var \ + parseFn = FuncRef.new() + parseFn.set_instance(self) + parseFn.set_function("parse_UnaryExpression") + + return parse_BinaryExpression(parseFn, TokenType.MultiplicativeOp) + + # > MultiplicativeExpression + # > UnaryExpression + # UnaryExpression + # : ResolvedSymbol + # | AdditiveOp UnaryExpression + # | Logical_Not UnaryExpression + # ; + func parse_UnaryExpression(): + var operator + match NextToken.Type: + TokenType.AdditiveOp: + operator = eat(TokenType.AdditiveOp).Value + TokenType.Logical_Not: + operator = eat(TokenType.Logical_Not).Value + + if operator == null : + return parse_ResolvedSymbol() + + var \ + node = SyntaxNode.new() + node.Type = SyntaxNodeType.UnaryExpression + node.Value = [ operator, parse_UnaryExpression() ] + + return node; + + # > UnaryExpression + # > PrimaryExpression + # ResolvedSymbol (LeftHandExpression) + # : PrimaryExpression + # ; + func parse_ResolvedSymbol(): + return parse_PrimaryExpression() + # var resolvedSymbol = parse_Identifier() + + # if resolvedSymbol.Type == SyntaxNodeType.Identifier : + # return resolvedSymbol + + # var assertStrTmplt = "parse_ResolvedSymbol: Unexpected symbol: {value}" + # var assertStr = assertStrTmplt.format({"value" : resolvedSymbol.Type}) + # assert(true != true, assertStr) + + # > ResolvedSymbol + # PrimaryExpression + # : Literal + # | ParenthesizedExpression + # ; + func parse_PrimaryExpression(): + if is_Literal(): + return parse_Literal() + + match NextToken.Type: + TokenType.ExpressionPStart: + return parse_ParenthesizedExpression() + TokenType.Identifier: + var identifier = parse_Identifier() + + if identifier.Type == SyntaxNodeType.Identifier : + return identifier + + var assertStrTmplt = "parse_PrimaryExpression: (Identifier) Unexpected symbol: {value}" + var assertStr = assertStrTmplt.format({"value" : identifier.Type}) + assert(true != true, assertStr) + + return parse_ResolvedSymbol() + + # > PrimaryExpression + # Literal + # : NumericLiteral + # | StringLiteral + # | BooleanLiteral + # | NullLiteral + # ; + func parse_Literal(): + match NextToken.Type : + TokenType.Number: + return parse_NumericLiteral() + TokenType.String: + return parse_StringLiteral() + TokenType.Bool_true: + return parse_BooleanLiteral(TokenType.Bool_true) + TokenType.Bool_false: + return parse_BooleanLiteral(TokenType.Bool_false) + TokenType.NullValue: + return parse_NullLiteral() + + assert(false, "parse_Literal: Was not able to detect valid literal type from NextToken") + + # > PrimaryExpression + # ParenthesizedExpression + # : ( Expression ) + # ; + func parse_ParenthesizedExpression(): + eat(TokenType.ExpressionPStart) + + var expression = parse_Expression() + + eat(TokenType.ExpressionPEnd) + + return expression + + # > Literal + # NumericLiteral + # : Number + # ; + func parse_NumericLiteral(): + var Token = eat(TokenType.Number) + var \ + node = SyntaxNode.new() + node.Type = SyntaxNodeType.NumericLiteral + node.Value = int( Token.Value ) + + return node + + # > Literal + # StringLiteral + # : String + # ; + func parse_StringLiteral(): + var Token = eat(TokenType.String) + var \ + node = SyntaxNode.new() + node.Type = SyntaxNodeType.StringLiteral + node.Value = Token.Value.substr( 1, Token.Value.length() - 2 ) + + return node + + + # > Literal + # BooleanLiteral + # : true + # | false + # ; + func parse_BooleanLiteral(token): + eat(token) + var value + if (TokenType.Bool_true == token) : + value = true + elif (TokenType.Bool_false == token) : + value = false + + var \ + node = SyntaxNode.new() + node.Type = SyntaxNodeType.BooleanLiteral + node.Value = value + + return node + + # > Literal + # NullLiteral + # : null + # ; + func parse_NullLiteral(): + eat(TokenType.NullValue) + + var \ + node = SyntaxNode.new() + node.Type = SyntaxNodeType.NullLiteral + node.Value = null + + return node + +var GParser = Parser.new() + + + +onready var TextOut = GScene.get_node("TextOutput") + +func tout(text): + TextOut.insert_text_at_cursor(text) + +const Tests = \ +{ + MultiStatement = \ + { + Name = "Multi-Statement", + File = "1.Multi-Statement.uf" + }, + BlockStatement = \ + { + Name = "Block Statement", + File = "2.BlockStatement.uf" + }, + BinaryExpression = \ + { + Name = "Binary Expression", + File = "3.BinaryExpression.uf" + }, + Assignment = \ + { + Name = "Assignment", + File = "4.Assignment.uf" + }, + VaraibleDeclaration = \ + { + Name = "Variable Declaration", + File = "5.VariableDeclaration.uf" + }, + Conditionals = \ + { + Name = "Conditionals", + File = "6.Conditionals.uf" + }, + Relations = \ + { + Name = "Relations", + File = "7.Relations.uf" + }, + Equality = \ + { + Name = "Equality", + File = "8.Equality.uf" + }, + Logical = \ + { + Name = "Logical", + File = "9.Logical.uf" + }, + Unary = \ + { + Name = "Unary", + File = "10.Unary.uf" + }, + Iteration = \ + { + Name = "Iteration", + File = "11.Iteration.uf" + }, + Functions = \ + { + Name = "Functions", + File = "12.Functions.uf" + } +} + +func test(entry): + var introMessage = "Testing: {Name}\n" + var introMessageFormatted = introMessage.format({"Name" : entry.Name}) + tout(introMessageFormatted) + + var path + if Engine.editor_hint : + path = "res://../Tests/{TestName}" + else : + path = "res://../Builds/Tests/{TestName}" + var pathFormatted = path.format({"TestName" : entry.File}) + + var \ + file = File.new() + file.open(pathFormatted, File.READ) + + var programDescription = file.get_as_text() + file.close() + + GTokenizer.init(programDescription) + var ast = GParser.parse(GTokenizer) + + var json = JSON.print(ast.to_Dictionary(), '\t') + + tout(json + "\n") + tout("Passed!\n") + + +# Main Entry point. +func _ready(): + for Key in Tests : + test(Tests[Key]) diff --git a/Editor/project.godot b/Editor/project.godot index a7d96ea..9b51c39 100644 --- a/Editor/project.godot +++ b/Editor/project.godot @@ -18,7 +18,7 @@ config/icon="res://Assets/Branding/RDP_Class_cover_small.png" [autoload] GScene="*res://Lectures/Lecture.tscn" -GScript="*res://Lectures/Lecture.13.gd" +GScript="*res://Lectures/Lecture.14.gd" [gui]