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 = "ExpressionParenthesisStart", ExpressionPEnd = "ExpressionParenthesisEnd", expr_SqBStart = "ExpressionSquareBracketStart", expr_SqBEnd = "ExpressionSquareBracketEnd", # 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", MemberDelimiter = "MemberDelimiter", # 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", New = "New", Class = "Class", Extends = "Extends", This = "This", Super = "Super" } const TokenSpec = \ { # Comments TokenType.CommentLine : "^\\/\\/.*", TokenType.CommentMultiLine : "^\\/\\*[\\s\\S]*?\\*\\/", # Formatting TokenType.Whitespace : "^\\s+", # Expressions TokenType.ExpressionPStart : "^\\(", TokenType.ExpressionPEnd : "^\\)", TokenType.expr_SqBStart : "^\\[", TokenType.expr_SqBEnd : "^\\]", # 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 : "^,", TokenType.MemberDelimiter : "^\\.", # 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.New : "^\\bnew\\b", TokenType.Class : "^\\bclass\\b", TokenType.Extends : "^\\bextends\\b", TokenType.This : "^\\bthis\\b", TokenType.Super : "^\\bsuper\\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 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", MemberExpression = "MemberExpression", CallExpression = "CallExpression", ClassDeclaration = "ClassDeclaration", ClassExtension = "ClassExtension", ThisExpression = "ThisExpression", SuperExpression = "SuperExpression", NewExpression = "NewExpression" } 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 # | ClassDeclaration # ; 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() TokenType.Class: return parse_ClassDeclaration() 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() eat(TokenType.StatementEnd) var \ node = SyntaxNode.new() node.Type = SyntaxNodeType.ReturnStatement node.Value = argument return node # > Statement # ClassDeclaration # : class Identifier OptClassExtends BLockStatement # ; func parse_ClassDeclaration(): eat(TokenType.Class) var identifier = parse_Identifier() var superClass = null if NextToken.Type == TokenType.Extends : superClass = parse_ClassExtension() var body = parse_BlockStatement() var \ node = SyntaxNode.new() node.Type = SyntaxNodeType.ClassDeclaration node.Value = [ identifier, superClass, body ] return node # > ClassDeclaration # SuperClass # : extends Identifier # ; func parse_ClassExtension(): eat(TokenType.Extends) return parse_Identifier() # > 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) # : MemberExpression # ; func parse_ResolvedSymbol(): return parse_FunctionExpression() # > ResolvedSymbol # FunctionExpression # : Super # | MemberExpression # | CallExpression # ; func parse_FunctionExpression(): if NextToken.Type == TokenType.Super : return parse_CallExpression(parse_SuperExpression()) var member = parse_MemberExpression() if NextToken.Type == TokenType.ExpressionPStart : return parse_CallExpression(member) return member # > FunctionExpression # Super # : super # ; func parse_SuperExpression(): eat(TokenType.Super) var \ node = SyntaxNode.new() node.Type = SyntaxNodeType.SuperExpression return node # > ResolvedSymbol # MemberExpression # : PrimaryExpression # | MemberExpression . Identifier # | MemberExpression [ Expression ] # ; func parse_MemberExpression(): var expression = parse_PrimaryExpression() while NextToken.Type == TokenType.MemberDelimiter || NextToken.Type == TokenType.expr_SqBStart : if NextToken.Type == TokenType.MemberDelimiter : eat(TokenType.MemberDelimiter) var member = parse_Identifier() var \ node = SyntaxNode.new() node.Type = SyntaxNodeType.MemberExpression node.Value = [ false, expression, member ] expression = node if NextToken.Type == TokenType.expr_SqBStart : eat(TokenType.expr_SqBStart) var sbExpression = parse_Expression() eat(TokenType.expr_SqBEnd) var \ node = SyntaxNode.new() node.Type = SyntaxNodeType.MemberExpression node.Value = [ true, expression, sbExpression ] expression = node return expression # > FunctionExpression # CallExpression # : Callee Arguments # ; # # Callee # : MemberExpression # | CallExpression # ; func parse_CallExpression(callee): var \ callExpression = SyntaxNode.new() callExpression.Type = SyntaxNodeType.CallExpression callExpression.Value = \ [ callee, parse_Arguments() ] if NextToken.Type == TokenType.ExpressionPStart : callExpression = parse_CallExpression(callExpression) return callExpression # > CallExpression # Arugments # : ( OptArgumentList ) # ; func parse_Arguments(): eat(TokenType.ExpressionPStart) var argumentList = null if NextToken.Type != TokenType.ExpressionPEnd : argumentList = parse_ArgumentList() eat(TokenType.ExpressionPEnd) return argumentList # > Arguments # ArgumentList # : AssignmentExpression # | ArgumentList , AssignmentExpression # ; func parse_ArgumentList(): var argumentList = [ parse_AssignmentExpression() ] while NextToken.Type == TokenType.CommaDelimiter : eat(TokenType.CommaDelimiter) argumentList.append( parse_AssignmentExpression() ) return argumentList # > MemberExpression # PrimaryExpression # : Literal # | ParenthesizedExpression # | Identifier # | ThisExpression # | NewExpression # ; 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) TokenType.This : return parse_ThisExpression() TokenType.New : return parse_NewExpression() 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 # > PrimaryExpression # ThisExpression # : this # ; func parse_ThisExpression(): eat(TokenType.This) var \ node = SyntaxNode.new() node.Type = SyntaxNodeType.ThisExpression return node # > PrimaryExpression # NewExpression # : new MemberExpression Arugments -> new MyNamespace.MyClass(1, 2); # ; func parse_NewExpression(): eat(TokenType.New) var memberExp = parse_MemberExpression() var args = parse_Arguments() var \ node = SyntaxNode.new() node.Type = SyntaxNodeType.NewExpression node.Value = [ memberExp, args ] return node; # > 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" # }, # MemberExpressions = \ # { # Name = "Member Expressions", # File = "13.MemberExpressions.uf" # }, Classes = \ { Name = "Classes", File = "14.Classes.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])