extends Object # ---------------------------------------------------------- UTILITIES var ErrorOut func check( condition : bool, message : String): assert(condition, message) if ! condition: ErrorOut.text = "Eva - Error: " + message func throw( message ): assert(false, message) ErrorOut.text = "Eva - Error: " + message # ---------------------------------------------------------- UTILITIES END class_name Parser # ---------------------------------------------------------- AST Node const NType = \ { program = "Program", block = "Scope Block", literal_Number = "Literal: Number", literal_String = "Literal: String", op_Assign = "Assignment", op_Add = "+", op_Sub = "-", op_Mult = "*", op_Div = "/", fn_Print = "Print", identifier = "Identifier", variable = "Variable" } class ASTNode: var Data : Array func add_Expr( expr ): Data.append(expr) func add_TokenValue( token ): Data.append( token.Value ) func set_Type( nType ): Data.append(nType) func arg( id ): return Data[id] func num_args(): return Data.size() - 1 func type(): return Data[0] func is_op_Numeric(): match type(): NType.op_Add: return true NType.op_Sub: return true NType.op_Mult: return true NType.op_Div: return true _: return false func is_Number(): return type() == NType.literal_Number func is_String(): return type() == NType.literal_String func string(): return arg(1).substr(1, arg(1).length() -2) # Serialization ---------------------------------------------------- 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 \ to_SExpression_Fn = FuncRef.new() to_SExpression_Fn.set_function("to_SExpression") return array_Serialize( self.Data, to_SExpression_Fn ) # Serialization END ------------------------------------------------- # ---------------------------------------------------------- AST Node END const SLexer = preload("Lexer.gd") const TType = SLexer.TType var Lexer : SLexer var NextToken : SLexer.Token # Gets the next token only if the current token is the specified intended token (tokenType) func eat(tokenType): var currToken = NextToken check(currToken != null, "Parser - eat: NextToken was null") var assertStrTmplt = "Parser - eat: Unexpected token: {value}, expected: {type}" var assertStr = assertStrTmplt.format({"value" : currToken.Value, "type" : tokenType}) check(currToken.Type == tokenType, assertStr) NextToken = Lexer.next_Token() return currToken func parse(): var \ node = ASTNode.new() node.set_Type(NType.program) while NextToken != null : if NextToken.Type == TType.def_Start: node.add_Expr( parse_Expression() ) elif NextToken.Type == TType.identifier: node.add_Expr( parse_Identifier() ) elif NextToken.is_Literal(): node.Add_Expr( parse_Literal() ) return node func parse_Expression(): eat(TType.def_Start) var node : ASTNode match NextToken.Type : TType.def_Block: node = parse_Block() TType.def_Var: node = parse_Variable() TType.fn_Print: node = parse_fn_Print() TType.op_Assgin: node = parse_op_Assign() TType.op_Numeric: node = parse_op_Numeric() var arg = 1 while NextToken.Type != TType.def_End: if NextToken.Type == TType.def_Start: node.add_Expr( parse_Expression() ) elif NextToken.Type == TType.identifier: node.add_Expr( parse_Identifier() ) else : node.add_Expr( parse_Literal() ) eat(TType.def_End) return node func parse_Block(): var \ node = ASTNode.new() node.set_Type(NType.block) eat(TType.def_Block) return node func parse_Variable(): var \ node = ASTNode.new() node.set_Type(NType.variable) eat(TType.def_Var) check( NextToken.Type == TType.identifier, String("Parser - parse_Variable: NextToken should have been identifier. TokenData - Type: {type} Value: {value}") \ .format({"type" : NextToken.Type, "value" : NextToken.Value }) ) 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_Identifier(): var \ node = ASTNode.new() node.set_Type(NType.identifier) node.add_TokenValue(NextToken) eat(TType.identifier) return node func parse_fn_Print(): var \ node = ASTNode.new() node.set_Type(NType.fn_Print) eat(TType.fn_Print) return node func parse_op_Assign(): var \ node = ASTNode.new() node.set_Type(NType.op_Assign) eat(TType.op_Assgin) 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) if NextToken.is_Literal() : node.add_Expr( parse_Literal() ) elif NextToken.Type == TType.def_Start : node.add_Expr( parse_Expression() ) return node func parse_op_Numeric(): var node = ASTNode.new() match NextToken.Value: NType.op_Add: node.set_Type(NType.op_Add) NType.op_Sub: node.set_Type(NType.op_Sub) NType.op_Mult: node.set_Type(NType.op_Mult) NType.op_Div: node.set_Type(NType.op_Div) eat(TType.op_Numeric) return node func parse_Literal(): var node = ASTNode.new() match NextToken.Type: TType.literal_Number: node.set_Type(NType.literal_Number) node.add_TokenValue(NextToken) eat(TType.literal_Number) TType.literal_String: node.set_Type(NType.literal_String) node.add_TokenValue(NextToken) eat(TType.literal_String) return node func _init(lexer, errorOut) : ErrorOut = errorOut Lexer = lexer NextToken = Lexer.next_Token()