#import "Flat_Pool"; /** * TODO: * if parsing * for/while loop parsing **/ //////////////////////////// //@nb - Parse_state state Parse_State :: struct { current : *Token; previous : *Token; tokens : [..]Token; current_token_index : int; node_allocator : Allocator; node_arena : Arena; child_allocator : Allocator; child_arena : Arena; had_error : bool; path : string; result : Parse_Result; } //////////////////////////// //@nb - Result and error handling Parse_Result :: struct { root : *AST_Node; nodes : [..]AST_Node; had_error : bool; messages : [..]Compiler_Message; } Parse_Error_Kind :: enum { Parse_Error_Type_Missing; Parse_Error_Expected_Expression; Parse_Error_Empty_Block; Parse_Error_Unexpected_Token; } //////////////////////////// //@nb - Parsing helper types Separator_Type :: enum { Comma; Semicolon; } Entry_Point_Type :: enum { None; Vertex; Pixel; } //////////////////////////// //@nb - Expression parsing Precedence :: enum { PREC_NONE; PREC_ASSIGNMENT; // = PREC_OR; // || PREC_AND; // && PREC_BITWISE; // | & ^ PREC_EQUALITY; // == != PREC_COMPARISON; // < > <= >= PREC_TERM; // + - PREC_FACTOR; // * / PREC_UNARY; // ! - PREC_CALL; // . () PREC_PRIMARY; } Parse_Fn :: #type (parse_state: *Parse_State, left : *AST_Node) -> *AST_Node; Parse_Rule :: struct { prefix : Parse_Fn; infix : Parse_Fn; precedence : Precedence; } parse_rules :: #run -> [(cast(int)Token_Kind.TOKEN_ERROR) + 1]Parse_Rule { rules : [(cast(int)Token_Kind.TOKEN_ERROR) + 1]Parse_Rule; rules[Token_Kind.TOKEN_LEFTPAREN] = .{grouping, call, .PREC_CALL}; rules[Token_Kind.TOKEN_RIGHTPAREN] = .{null, null, .PREC_NONE}; rules[Token_Kind.TOKEN_LEFTBRACE] = .{null, null, .PREC_NONE}; rules[Token_Kind.TOKEN_RIGHTBRACE] = .{null, null, .PREC_NONE}; rules[Token_Kind.TOKEN_LEFTBRACKET] = .{null, array_access, .PREC_CALL}; rules[Token_Kind.TOKEN_RIGHTBRACKET] = .{null, null, .PREC_NONE}; rules[Token_Kind.TOKEN_COMMA] = .{null, null, .PREC_NONE}; rules[Token_Kind.TOKEN_DOT] = .{null, dot, .PREC_CALL}; rules[Token_Kind.TOKEN_PROPERTIES] = .{named_variable, null, .PREC_CALL}; rules[Token_Kind.TOKEN_MINUS] = .{unary, binary, .PREC_TERM}; rules[Token_Kind.TOKEN_PLUS] = .{null, binary, .PREC_TERM}; rules[Token_Kind.TOKEN_SEMICOLON] = .{null, null, .PREC_NONE}; rules[Token_Kind.TOKEN_SLASH] = .{null, binary, .PREC_FACTOR}; rules[Token_Kind.TOKEN_STAR] = .{null, binary, .PREC_FACTOR}; rules[Token_Kind.TOKEN_ISNOTEQUAL] = .{null, binary, .PREC_COMPARISON}; rules[Token_Kind.TOKEN_ASSIGN] = .{null, binary, .PREC_COMPARISON}; rules[Token_Kind.TOKEN_ISEQUAL] = .{null, binary, .PREC_EQUALITY}; rules[Token_Kind.TOKEN_GREATER] = .{null, binary, .PREC_COMPARISON}; rules[Token_Kind.TOKEN_GREATEREQUALS] = .{null, binary, .PREC_COMPARISON}; rules[Token_Kind.TOKEN_LESS] = .{null, binary, .PREC_COMPARISON}; rules[Token_Kind.TOKEN_LESSEQUALS] = .{null, binary, .PREC_COMPARISON}; rules[Token_Kind.TOKEN_IDENTIFIER] = .{named_variable, null, .PREC_NONE}; rules[Token_Kind.TOKEN_INTLITERAL] = .{integer, null, .PREC_NONE}; rules[Token_Kind.TOKEN_FLOATLITERAL] = .{floating, null, .PREC_NONE}; rules[Token_Kind.TOKEN_ELSE] = .{null, null, .PREC_NONE}; rules[Token_Kind.TOKEN_FALSE] = .{null, null, .PREC_NONE}; rules[Token_Kind.TOKEN_FOR] = .{null, null, .PREC_NONE}; rules[Token_Kind.TOKEN_IF] = .{null, null, .PREC_NONE}; rules[Token_Kind.TOKEN_LOGICALOR] = .{null, binary, .PREC_OR}; rules[Token_Kind.TOKEN_LOGICALAND] = .{null, binary, .PREC_AND}; rules[Token_Kind.TOKEN_RETURN] = .{null, null, .PREC_NONE}; rules[Token_Kind.TOKEN_TRUE] = .{null, null, .PREC_NONE}; rules[Token_Kind.TOKEN_WHILE] = .{null, null, .PREC_NONE}; rules[Token_Kind.TOKEN_ERROR] = .{null, null, .PREC_NONE}; rules[Token_Kind.TOKEN_EOF] = .{null, null, .PREC_NONE}; return rules; } init_parse_state :: (parse_state : *Parse_State, tokens : [..]Token, path : string) { parse_state.tokens = tokens; parse_state.path = path; parse_state.node_allocator = make_arena(*parse_state.node_arena); parse_state.child_allocator = make_arena(*parse_state.child_arena); parse_state.result.nodes.allocator = parse_state.node_allocator; array_reserve(*parse_state.result.nodes, 4096); parse_state.current_token_index = 0; } //////////////////////////// //@nb - Error handling functions //nb - Record an error and report it immediately to the user. record_error :: (parse_state : *Parse_State, token : Token, message : string, report_source_location : bool = true) { error : Compiler_Message; error.message_kind = .Error; error.message = message; error.path = parse_state.path; source_location : Source_Range; source_location.begin = token; source_location.begin.column = 0; source_location.begin.source = source_location.begin.source - source_location.begin.column; source_location.main_token = token; advance_to_sync_point(parse_state); error.report_source_location = report_source_location; source_location.end = parse_state.current; array_add(*error.source_locations, source_location); parse_state.result.had_error = true; array_add(*parse_state.result.messages, error); } generate_source_location_from_token :: (state : *Parse_State, token : Token) -> Source_Range { location : Source_Range; begin : Token = token; begin.index -= begin.column; begin.length += begin.column; begin.source -= begin.column; begin.column = 0; location.begin = begin; location.main_token = token; snapshot := snapshot_state(state); advance_to_sync_point(state); location.end = state.current; rewind_to_snapshot(state, snapshot); return location; } unexpected_token :: (state : *Parse_State, token : Token, message : string) { record_error(state, token, message); } expected_expression :: (state : *Parse_State, token : Token, message : string) { builder : String_Builder; init_string_builder(*builder,, temp); print_to_builder(*builder, "%\n", message); location : Source_Range = generate_source_location_from_token(state, token); indent(*builder, 1); cyan(*builder); print_to_builder(*builder, "%\n", print_from_source_location(location)); indent(*builder, 1); print_token_pointer(*builder, token); final_message := builder_to_string(*builder); record_error(state, token, final_message, false); } missing_type_specifier :: (state : *Parse_State, token : Token, message : string) { builder : String_Builder; init_string_builder(*builder,, temp); print_to_builder(*builder, "%\n", message); location := generate_source_location_from_token(state, token); indent(*builder, 1); cyan(*builder); print_to_builder(*builder, "%\n", print_from_source_location(location)); indent(*builder, 1); loc := location.begin; increment := location.begin.length + 2; loc.source += increment; loc.index += increment; loc.column += increment; print_token_pointer(*builder, loc); final_message := builder_to_string(*builder); record_error(state, token, final_message, false); } empty_block :: (state : *Parse_State, token : Token, message : string) { builder : String_Builder; init_string_builder(*builder,, temp); print_to_builder(*builder, "%\n", message); location := generate_source_location_from_token(state, token); indent(*builder, 1); cyan(*builder); print_to_builder(*builder, "%\n", print_from_source_location(location)); indent(*builder, 1); loc := location.begin; // increment := location.begin.length + 2; // loc.source += increment; // loc.index += increment; // loc.column += increment; print_token_pointer(*builder, loc); final_message := builder_to_string(*builder); record_error(state, token, final_message, false); } error_node :: (parse_state : *Parse_State, message : string) -> *AST_Node { node := make_node(parse_state, .Error); node.name = copy_string(message); return node; } //nb - Advance to the next sync point. // A sync point is the next token that ends a statement or starts/ends a block. advance_to_sync_point :: (parse_state : *Parse_State) { while true { if parse_state.current.kind == .TOKEN_SEMICOLON || parse_state.current.kind == .TOKEN_RIGHTBRACE || parse_state.current.kind == .TOKEN_LEFTBRACE{ break; } advance(parse_state); } } //////////////////////////// //////////////////////////// //@nb - Base parsing functions make_node :: (parse_state : *Parse_State, kind : AST_Kind) -> *AST_Node { node : AST_Node; node.kind = kind; node.children.allocator = parse_state.child_allocator; array_add(*parse_state.result.nodes, node); return *parse_state.result.nodes[parse_state.result.nodes.count - 1]; } add_child :: (node : *AST_Node, child : *AST_Node) { child.parent = node; array_add(*node.children, child); } Sync_Snapshot :: struct { current : *Token; previous : *Token; current_token_index : int; } snapshot_state :: (parse_state : *Parse_State) -> Sync_Snapshot { snapshot : Sync_Snapshot; snapshot.current = parse_state.current; snapshot.previous = parse_state.previous; snapshot.current_token_index = parse_state.current_token_index; return snapshot; } rewind_to_snapshot :: (parse_state : *Parse_State, snapshot : Sync_Snapshot) { parse_state.current = snapshot.current; parse_state.previous = snapshot.previous; parse_state.current_token_index = snapshot.current_token_index; } advance :: (parse_state : *Parse_State) { parse_state.previous = parse_state.current; while true { parse_state.current = *parse_state.tokens[parse_state.current_token_index]; parse_state.current_token_index += 1; if parse_state.current.kind != .TOKEN_ERROR break; err := tprint("unknown token \x1b[1;37m'%'\x1b[0m", parse_state.current.string_value); unexpected_token(parse_state, parse_state.current, err); return; } } //nb - Checks if the current token is of a certain kind and advances if it is match :: (parse_state : *Parse_State, kind : Token_Kind) -> bool { if !check(parse_state, kind) return false; advance(parse_state); return true; } //nb - Checks if the current token is of a certain kind check :: (parse_state : *Parse_State, kind : Token_Kind) -> bool { return parse_state.current.kind == kind; } //nb - Checks if the next token is of a certain kind check_next :: (parse_state : *Parse_State, kind : Token_Kind) -> bool { return parse_state.tokens[parse_state.current_token_index].kind == kind; } //nb - Consume a token if consume :: (parse_state : *Parse_State, kind : Token_Kind, message : string) { if parse_state.current.kind == kind { advance(parse_state); return; } advance(parse_state); unexpected_token(parse_state, parse_state.current, message); consume(parse_state, kind, message); } //////////////////////////// //@nb - Expression parsing get_rule :: (kind : Token_Kind) -> *Parse_Rule { return *parse_rules[kind]; } precedence :: (parse_state : *Parse_State, precedence : Precedence) -> *AST_Node { advance(parse_state); prefix_rule := get_rule(parse_state.previous.kind).prefix; if prefix_rule == null { expected_expression(parse_state, parse_state.current, "Expected expression."); // @Incomplete: Add error node here? return error_node(parse_state, "Expected expression."); } left := prefix_rule(parse_state, null); while precedence <= get_rule(parse_state.current.kind).precedence { advance(parse_state); if parse_state.current.kind == .TOKEN_EOF { expected_expression(parse_state, parse_state.current, "Reached end of file. Expected expression."); // @Incomplete: Add error node here? return null; } infix_rule := get_rule(parse_state.previous.kind).infix; left = infix_rule(parse_state, left); } return left; } named_variable :: (parse_state : *Parse_State, left : *AST_Node) -> *AST_Node { if check(parse_state, .TOKEN_LEFTPAREN) { return call(parse_state, left); } variable := make_node(parse_state, .Variable); variable.source_location = generate_source_location_from_token(parse_state, parse_state.previous); variable.name = parse_state.previous.ident_value; return variable; } binary :: (parse_state : *Parse_State, left : *AST_Node) -> *AST_Node { op := parse_state.previous.*; rule := get_rule(op.kind); source_location := generate_source_location_from_token(parse_state, op); // source_location : Source_Range; // source_location.begin = left.source_location.begin; binary_expression := make_node(parse_state, .Binary); add_child(binary_expression, left); add_child(binary_expression, precedence(parse_state, rule.precedence + 1)); if op.kind == { case .TOKEN_PLUS; #through; case .TOKEN_MINUS; #through; case .TOKEN_STAR; #through; case .TOKEN_SLASH; #through; case .TOKEN_ISEQUAL; #through; case .TOKEN_ASSIGN; #through; case .TOKEN_ISNOTEQUAL; #through; case .TOKEN_LOGICALOR; #through; case .TOKEN_LOGICALAND; #through; case .TOKEN_LESS; #through; case .TOKEN_LESSEQUALS; #through; case .TOKEN_GREATER; #through; case .TOKEN_GREATEREQUALS; { binary_expression.token = op; } } // source_location.end = parse_state.previous; binary_expression.source_location = source_location; return binary_expression; } array_access :: (parse_state : *Parse_State, left : *AST_Node) -> *AST_Node { identifier := parse_state.tokens[parse_state.current_token_index - 3]; left_bracket := parse_state.tokens[parse_state.current_token_index - 2]; array_access := make_node(parse_state, .Unary); array_access.token = left_bracket; array_index := expression(parse_state); add_child(array_access, array_index); add_child(left, array_access); consume(parse_state, .TOKEN_RIGHTBRACKET, "Expected ']' after array index."); source_location : Source_Range; source_location.begin = left.source_location.begin; if check(parse_state, .TOKEN_ASSIGN) { advance(parse_state); node := make_node(parse_state, .Binary); node.token = parse_state.previous; add_child(node, left); add_child(node, expression(parse_state)); return node; } source_location.end = parse_state.previous; left.source_location = source_location; return left; } unary :: (parse_state : *Parse_State, left : *AST_Node) -> *AST_Node { op := parse_state.previous.*; rule := get_rule(op.kind); unary_expression := make_node(parse_state, .Unary); add_child(unary_expression, precedence(parse_state, rule.precedence + 1)); if op.kind == { case .TOKEN_MINUS; { unary_expression.token = op; } case .TOKEN_LEFTBRACKET; { unary_expression.token = op; consume(parse_state, .TOKEN_RIGHTBRACKET, "Expect ']' after array access."); } } return unary_expression; } grouping :: (parse_state : *Parse_State, left : *AST_Node) -> *AST_Node { grouping := expression(parse_state); consume(parse_state, .TOKEN_RIGHTPAREN, "Expect ')' after group expression."); return grouping; } directive :: (state : *Parse_State) -> *AST_Node { if state.current.ident_value == "foreign" { advance(state); identifier_token := state.current; advance(state); consume(state, .TOKEN_DOUBLECOLON, "Expect '::' after function name."); func := function_declaration(state, identifier_token, .None, false, false); func.foreign_declaration = true; return func; } return null; } call :: (parse_state : *Parse_State, left : *AST_Node) -> *AST_Node { call := make_node(parse_state, .Call); source_location := generate_source_location_from_token(parse_state, parse_state.previous); // source_location : Source_Range; // source_location.begin = parse_state.previous; // source_location.main_token = parse_state.previous; prev := parse_state.previous; call.name = prev.ident_value; advance(parse_state); arg_list := argument_list(parse_state); if arg_list { add_child(call, arg_list); } snapshot := snapshot_state(parse_state); advance_to_sync_point(parse_state); source_location.end = parse_state.current; rewind_to_snapshot(parse_state, snapshot); call.source_location = source_location; return call; } dot :: (parse_state : *Parse_State, left : *AST_Node) -> *AST_Node { consume(parse_state, .TOKEN_IDENTIFIER, "Expect property name after '.'."); identifier := parse_state.previous; source_location : Source_Range; source_location.begin = left.source_location.begin; if check(parse_state, .TOKEN_ASSIGN) { advance(parse_state); variable := make_node(parse_state, .Variable); variable.source_location = generate_source_location_from_token(parse_state, identifier); variable.name = identifier.ident_value; add_child(left, variable); node := make_node(parse_state, .Binary); node.token = parse_state.previous; add_child(node, left); add_child(node, expression(parse_state)); return node; } else if check(parse_state, .TOKEN_DOT) { // @Incomplete(nb): Another level of access } variable := make_node(parse_state, .Variable); variable.name = identifier.ident_value; add_child(left, variable); source_location.end = parse_state.previous; variable.source_location = source_location; return left; } integer :: (parse_state : *Parse_State, left : *AST_Node) -> *AST_Node { value := parse_state.previous.integer_value; node := make_node(parse_state, .Integer); node.source_location.begin = parse_state.previous; node.source_location.end = parse_state.previous; node.source_location.main_token = parse_state.previous; node.integer_value = value; return node; } floating :: (parse_state : *Parse_State, left : *AST_Node) -> *AST_Node { value := parse_state.previous.float_value; node := make_node(parse_state, .Float); node.source_location.begin = parse_state.previous; node.source_location.end = parse_state.previous; node.source_location.main_token = parse_state.previous; node.float_value = value; return node; } expression :: (parse_state : *Parse_State) -> *AST_Node { expression := precedence(parse_state, .PREC_ASSIGNMENT); return expression; } //////////////////////////// //@nb - Statement parsing functions field_assignment :: (parse_state : *Parse_State, identifier_token : *Token) -> *AST_Node { node : *AST_Node = make_node(parse_state, .Field); identifier := identifier_token.*; source_location : Source_Range; source_location.begin = identifier; source_location.main_token = identifier; node.name = identifier.string_value; add_child(node, expression(parse_state)); source_location.end = parse_state.previous; node.source_location = source_location; return node; } //nb - Non-const field declarations field_declaration :: (parse_state : *Parse_State, identifier_token : *Token) -> *AST_Node { node : *AST_Node = make_node(parse_state, .Field); identifier := identifier_token.*; source_location : Source_Range; source_location.begin = identifier; source_location.main_token = identifier; node.name = identifier.string_value; consume(parse_state, .TOKEN_COLON, "Expected ':' after field name for declarations."); if check(parse_state, .TOKEN_IDENTIFIER) { type_identifier := parse_state.current; node.token = type_identifier; advance(parse_state); } else if check(parse_state, .TOKEN_LEFTBRACKET) { advance(parse_state); array_size_expression := expression(parse_state); add_child(node, array_size_expression); consume(parse_state, .TOKEN_RIGHTBRACKET, "Expected closing ']' in array declaration."); consume(parse_state, .TOKEN_DOT, "Expected '.' before array type."); type_identifier := parse_state.current; node.token = type_identifier; advance(parse_state); node.array_field = true; } else { missing_type_specifier(parse_state, identifier_token, "Expected type specifier after field name."); return node; } if check(parse_state, .TOKEN_AT) { while check(parse_state, .TOKEN_AT) { advance(parse_state); // @Incomplete(niels): this is a mapping if check(parse_state, .TOKEN_IDENTIFIER) { array_add(*node.hint_tokens, parse_state.current); advance(parse_state); } else if check(parse_state, .TOKEN_HINT) { array_add(*node.hint_tokens, parse_state.current); advance(parse_state); } else if check(parse_state, .TOKEN_OPTIONAL) { array_add(*node.hint_tokens, parse_state.current); advance(parse_state); } } } else if match(parse_state, .TOKEN_ASSIGN) { add_child(node, expression(parse_state)); } source_location.end = parse_state.previous; node.source_location = source_location; return node; } argument_list :: (parse_state : *Parse_State) -> *AST_Node { node : *AST_Node; source_location : Source_Range; source_location.begin = parse_state.previous; source_location.begin.index -= source_location.begin.column; source_location.begin.source -= source_location.begin.column; source_location.begin.length += source_location.begin.column; source_location.begin.column = 0; source_location.main_token = parse_state.current; while !check(parse_state, .TOKEN_RIGHTPAREN) { arg := expression(parse_state); if !node { node = make_node(parse_state, .ArgList); } add_child(node, arg); if check(parse_state, .TOKEN_RIGHTPAREN) break; consume(parse_state, .TOKEN_COMMA, "Expect ',' after function argument."); } consume(parse_state, .TOKEN_RIGHTPAREN, "Expect ')' after function call."); if node { snapshot := snapshot_state(parse_state); advance_to_sync_point(parse_state); source_location.end = parse_state.current; rewind_to_snapshot(parse_state, snapshot); node.source_location = source_location; } return node; } expression_statement :: (parse_state : *Parse_State) -> *AST_Node { node := make_node(parse_state, .Expression_Statement); source_location : Source_Range; source_location.begin = parse_state.current; expr := expression(parse_state); add_child(node, expr); consume(parse_state, .TOKEN_SEMICOLON, "Expect ';' after expression."); while parse_state.current.kind == .TOKEN_SEMICOLON { advance(parse_state); } source_location.end = parse_state.previous; node.source_location = source_location; return node; } statement :: (parse_state : *Parse_State) -> *AST_Node { if match(parse_state, .TOKEN_RETURN) { node := make_node(parse_state, .Return); source_location : Source_Range; source_location.begin = parse_state.previous; return_expression := expression(parse_state); consume(parse_state, .TOKEN_SEMICOLON, "Expect ';' after return statement."); while parse_state.current.kind == .TOKEN_SEMICOLON { advance(parse_state); } if return_expression { add_child(node, return_expression); } source_location.end = parse_state.previous; node.source_location = source_location; return node; } else { return expression_statement(parse_state); } return error_node(parse_state, "Couldn't parse statement."); } block :: (parse_state : *Parse_State) -> *AST_Node { node : *AST_Node = make_node(parse_state, .Block); array_reserve(*node.children, 1024); source_location : Source_Range; consume(parse_state, .TOKEN_LEFTBRACE, "Expect '{' at start of block."); source_location.begin = parse_state.previous; while !check(parse_state, .TOKEN_RIGHTBRACE) && !check(parse_state, .TOKEN_EOF) { decl := declaration(parse_state); if decl { add_child(node, decl); } } consume(parse_state, .TOKEN_RIGHTBRACE, "Expect '}' after block."); source_location.end = parse_state.previous; node.source_location = source_location; return node; } field_list :: (parse_state : *Parse_State, separator : Separator_Type, require_field_names := true) -> *AST_Node { node : *AST_Node = make_node(parse_state, .FieldList); array_reserve(*node.children, 16); source_location : Source_Range; source_location.begin = parse_state.previous; source_location.main_token = parse_state.current; while check(parse_state, .TOKEN_IDENTIFIER) { field : *AST_Node; identifier := parse_state.current; advance(parse_state); if require_field_names || check(parse_state, .TOKEN_COLON) { field = field_declaration(parse_state, identifier); } else { field = make_node(parse_state, .Unnamed_Field); source_location : Source_Range; source_location.begin = identifier; source_location.main_token = identifier; field.name = identifier.ident_value; field.token = identifier; } add_child(node, field); if check(parse_state, .TOKEN_RIGHTPAREN) { source_location.end = parse_state.current; node.source_location = source_location; return node; } if separator == { case .Comma; { consume(parse_state, .TOKEN_COMMA, "Expect ',' after field declaration."); } case .Semicolon; { consume(parse_state, .TOKEN_SEMICOLON, "Expect ';' after field declaration."); } } } return node; } function_declaration :: (parse_state : *Parse_State, identifier_token : *Token, entry_point_kind : Entry_Point_Type, expect_body : bool = true, require_field_names : bool = true) -> *AST_Node { node : *AST_Node; source_location : Source_Range; source_location = generate_source_location_from_token(parse_state, identifier_token); function_name_token := identifier_token; consume(parse_state, .TOKEN_LEFTPAREN, "Expect argument list after '::' in function declaration."); function_args := field_list(parse_state, .Comma, require_field_names); consume(parse_state, .TOKEN_RIGHTPAREN, "Expect right ')' after function argument list."); return_type_token : Token; hint_token : Token; if match(parse_state, .TOKEN_ARROW) { return_type_token = parse_state.current; advance(parse_state); if check(parse_state, .TOKEN_AT) { advance(parse_state); hint_token = parse_state.current; advance(parse_state); } } node = make_node(parse_state, .Function); add_child(node, function_args); name := function_name_token.ident_value; if entry_point_kind == { case .Vertex; { node.vertex_entry_point = true; name = sprint("vs_%", function_name_token.ident_value); } case .Pixel; { node.pixel_entry_point = true; name = sprint("ps_%", function_name_token.ident_value); } } node.name = name; node.token = return_type_token; array_add(*node.hint_tokens, hint_token); if expect_body { function_body := block(parse_state); if function_body.children.count > 0 { add_child(node, function_body); } } else { consume(parse_state, .TOKEN_SEMICOLON, "Expect ';' after a function with no body."); } node.source_location = source_location; return node; } instance_block :: (parse_state : *Parse_State) -> *AST_Node { node : *AST_Node; source_location : Source_Range; source_location.begin = parse_state.current; consume(parse_state, .TOKEN_LEFTBRACE, "Expect '{' after 'instance' keyword"); properties := field_list(parse_state, .Semicolon); node = make_node(parse_state, .Instance); add_child(node, properties); consume(parse_state, .TOKEN_RIGHTBRACE, "Expect '}' after instance block"); source_location.end = parse_state.previous; node.source_location = source_location; return node; } meta_block :: (parse_state : *Parse_State) -> *AST_Node { node : *AST_Node; source_location : Source_Range; source_location.begin = parse_state.current; consume(parse_state, .TOKEN_LEFTBRACE, "Expect '{' after 'meta' keyword"); properties := field_list(parse_state, .Semicolon); node = make_node(parse_state, .Meta); add_child(node, properties); consume(parse_state, .TOKEN_RIGHTBRACE, "Expect '}' after meta block"); source_location.end = parse_state.previous; node.source_location = source_location; return node; } property_block :: (parse_state : *Parse_State, identifier_token : *Token = null) -> *AST_Node { node : *AST_Node; source_location : Source_Range; source_location.begin = parse_state.current; consume(parse_state, .TOKEN_LEFTBRACE, "Expect '{' after 'property' keyword"); properties := field_list(parse_state, .Semicolon); node = make_node(parse_state, .Properties); if identifier_token { node.name = identifier_token.ident_value; } add_child(node, properties); consume(parse_state, .TOKEN_RIGHTBRACE, "Expect '}' after 'property' keyword"); source_location.end = parse_state.previous; node.source_location = source_location; return node; } constant_buffer :: (parse_state : *Parse_State, identifier_token : *Token = null) -> *AST_Node { node : *AST_Node; source_location : Source_Range; source_location.begin = parse_state.current; consume(parse_state, .TOKEN_LEFTBRACE, "Expect '{' after 'constant_buffer' keyword"); buffer := field_list(parse_state, .Semicolon); node = make_node(parse_state, .CBuffer); if identifier_token { node.name = identifier_token.ident_value; } add_child(node, buffer); consume(parse_state, .TOKEN_RIGHTBRACE, "Expect '}' after 'constant_buffer' block"); source_location.end = parse_state.previous; node.source_location = source_location; return node; } struct_declaration :: (parse_state : *Parse_State, identifier_token : *Token) -> *AST_Node { source_location := generate_source_location_from_token(parse_state, identifier_token); consume(parse_state, .TOKEN_LEFTBRACE, "Expect '{' before struct declaration."); fields := field_list(parse_state, .Semicolon); node := make_node(parse_state, .Struct); node.name = identifier_token.ident_value; add_child(node, fields); consume(parse_state, .TOKEN_RIGHTBRACE, "Expect '}' after struct declaration."); source_location.end = parse_state.previous; node.source_location = source_location; return node; } access :: (parse_state : *Parse_State, identifier_token : *Token) -> *AST_Node { err_node := error_node(parse_state, tprint("Accessors not yet implemented. Token: %.", identifier_token.ident_value)); advance(parse_state); return err_node; } const_declaration :: (parse_state : *Parse_State, identifier_token : *Token) -> *AST_Node { if match(parse_state, .TOKEN_STRUCT) { return struct_declaration(parse_state, identifier_token); } else if check(parse_state, .TOKEN_LEFTPAREN) { return function_declaration(parse_state, identifier_token, .None); } else if match(parse_state, .TOKEN_PROPERTIES) { return property_block(parse_state, identifier_token); } else if match(parse_state, .TOKEN_CONSTANT_BUFFER) { return constant_buffer(parse_state, identifier_token); } return error_node(parse_state, tprint("Couldn't parse constant declaration at token %\n", parse_state.current.*)); } declaration :: (parse_state : *Parse_State) -> *AST_Node { decl_node : *AST_Node; if match(parse_state, .TOKEN_PROPERTIES) { decl_node = property_block(parse_state); } else if match(parse_state, .TOKEN_INSTANCE) { decl_node = instance_block(parse_state); } else if match(parse_state, .TOKEN_META) { decl_node = meta_block(parse_state); } else if match(parse_state, .TOKEN_VERTEX) { vertex_token := parse_state.previous; identifier := parse_state.current; advance(parse_state); consume(parse_state, .TOKEN_DOUBLECOLON, "Expect '::' after vertex entry point declaration."); decl_node = function_declaration(parse_state, identifier, .Vertex); } else if match(parse_state, .TOKEN_PIXEL) { pixel_token := parse_state.previous; identifier := parse_state.current; advance(parse_state); consume(parse_state, .TOKEN_DOUBLECOLON, "Expect '::' after pixel entry point declaration."); decl_node = function_declaration(parse_state, identifier, .Pixel); } else if check(parse_state, .TOKEN_LEFTPAREN) { decl_node = call(parse_state, null); } else if check(parse_state, .TOKEN_DIRECTIVE) { decl_node = directive(parse_state); } else if check(parse_state, .TOKEN_IDENTIFIER) { identifier := parse_state.current; if check_next(parse_state, .TOKEN_DOUBLECOLON) { advance(parse_state); advance(parse_state); decl_node = const_declaration(parse_state, identifier); } else if check_next(parse_state, .TOKEN_LEFTPAREN) { decl_node = statement(parse_state); } else if check_next(parse_state, .TOKEN_COLON) { advance(parse_state); decl_node = field_declaration(parse_state, identifier); consume(parse_state, .TOKEN_SEMICOLON, "Expect ';' after a field declaration."); } else if check_next(parse_state, .TOKEN_ASSIGN) { decl_node = expression_statement(parse_state); } } else if check(parse_state, .TOKEN_OUT) || check(parse_state, .TOKEN_IN) { error := error_node(parse_state, tprint("Expected a declaration or function call. '%' not allowed as a declaration name.", parse_state.current.kind)); advance(parse_state); decl_node = error; } if !decl_node { decl_node = statement(parse_state); } while parse_state.current.kind == .TOKEN_SEMICOLON { advance(parse_state); } return decl_node; } parse :: (result : *Compile_Result) { if result.had_error { return; } for *file : result.files { parse_state : Parse_State; init_parse_state(*parse_state, file.tokens.tokens, file.file.path); advance(*parse_state); if !match(*parse_state, .TOKEN_EOF) { parse_state.result.root = make_node(*parse_state, .Program); array_reserve(*parse_state.result.root.children, 1024); program := parse_state.result.root; while !check(*parse_state, .TOKEN_EOF) { decl := declaration(*parse_state); if decl { add_child(program, decl); } } } //@Incomplete(nb): will this straight copy just work? // Might need to rething how we do this. file.ast_root = parse_state.result.root; file.ast_nodes = parse_state.result.nodes; copy_messages(parse_state.result.messages, *result.messages); } } parse :: (parse_state : *Parse_State) -> Parse_Result { advance(parse_state); if !match(parse_state, .TOKEN_EOF) { parse_state.result.root = make_node(parse_state, .Program); array_reserve(*parse_state.result.root.children, 1024); program := parse_state.result.root; while !check(parse_state, .TOKEN_EOF) { decl := declaration(parse_state); if decl { add_child(program, decl); } } } return parse_state.result; } #load "AST.jai";