Files
Ink-Shader-Language/Parsing.jai
2025-01-15 07:15:31 +01:00

1182 lines
36 KiB
Plaintext

#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) {
/*
*/
builder : String_Builder;
init_string_builder(*builder,, temp);
print_to_builder(*builder, "%\n\n", message);
location : Source_Range;
location.begin = token;
location.begin.index -= location.begin.column;
location.begin.source -= location.begin.column;
location.begin.length += location.begin.column;
location.begin.column = 0;
location.main_token = token;
location.end = token;
// advance(state);
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);
}
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 || parse_state.current.kind == .TOKEN_EOF {
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;
}
token := parse_state.previous;
advance(parse_state);
unexpected_token(parse_state, token, 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_PLUSEQUALS; #through;
case .TOKEN_MINUSEQUALS; #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 {
if !check(parse_state, .TOKEN_ASSIGN) {
internal_error_message(*parse_state.result.messages, "Unimplemented error message.", parse_state.path);
return node;
}
// missing_type_specifier(parse_state, identifier_token, "Expected type specifier after field name.");
}
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 if match(parse_state, .TOKEN_IF) {
node := make_node(parse_state, .If);
source_location : Source_Range;
source_location.begin = parse_state.previous;
if_expression := expression(parse_state);
add_child(node, if_expression);
// consume(parse_state, .TOKEN_LEFTBRACE, "Expect '{' after if-condition.");
if_body := block(parse_state);
if if_body.children.count > 0 {
add_child(node, if_body);
}
if match(parse_state, .TOKEN_ELSE) {
else_node := else_statement(parse_state);
add_child(node, else_node);
}
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.");
}
else_statement :: (parse_state : *Parse_State) -> *AST_Node {
if check(parse_state, .TOKEN_IF) {
return statement(parse_state);
}
return block(parse_state);
}
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);
result.had_error |= parse_state.result.had_error;
}
}
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";