1336 lines
40 KiB
Plaintext
1336 lines
40 KiB
Plaintext
#import "Flat_Pool";
|
|
|
|
/**
|
|
* TODO:
|
|
* if parsing
|
|
* for/while loop parsing
|
|
**/
|
|
|
|
////////////////////////////
|
|
//@nb - Parse_state state
|
|
Parse_State :: struct {
|
|
current : *Token;
|
|
previous : *Token;
|
|
|
|
current_token_index : int;
|
|
|
|
result : *Compile_Result;
|
|
}
|
|
|
|
////////////////////////////
|
|
//@nb - Result and error handling
|
|
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_MINUSEQUALS] = .{null, binary, .PREC_COMPARISON};
|
|
rules[Token_Kind.TOKEN_PLUSEQUALS] = .{null, binary, .PREC_COMPARISON};
|
|
rules[Token_Kind.TOKEN_DIVEQUALS] = .{null, binary, .PREC_COMPARISON};
|
|
rules[Token_Kind.TOKEN_TIMESEQUALS] = .{null, binary, .PREC_COMPARISON};
|
|
rules[Token_Kind.TOKEN_MODEQUALS] = .{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;
|
|
}
|
|
|
|
////////////////////////////
|
|
//@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.result.file.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;
|
|
|
|
snap := snapshot_state(parse_state);
|
|
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);
|
|
|
|
rewind_to_snapshot(parse_state, snap);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
else_if_without_if :: (state : *Parse_State) {
|
|
builder : String_Builder;
|
|
init_string_builder(*builder,, temp);
|
|
|
|
append(*builder, "'else if' without 'if'\n");
|
|
|
|
token := state.previous;
|
|
|
|
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);
|
|
white(*builder);
|
|
|
|
final_message := builder_to_string(*builder);
|
|
record_error(state, token, final_message, false);
|
|
}
|
|
|
|
else_without_if :: (state : *Parse_State) {
|
|
builder : String_Builder;
|
|
init_string_builder(*builder,, temp);
|
|
|
|
append(*builder, "'else' without 'if'\n");
|
|
|
|
token := state.previous;
|
|
|
|
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);
|
|
white(*builder);
|
|
|
|
final_message := builder_to_string(*builder);
|
|
record_error(state, token, final_message, false);
|
|
}
|
|
|
|
unable_to_parse_statement :: (state : *Parse_State, token : Token, message : string = "") {
|
|
builder : String_Builder;
|
|
init_string_builder(*builder,, temp);
|
|
|
|
print_to_builder(*builder, "Unable to parse statement here. %\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);
|
|
}
|
|
|
|
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.result.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 {
|
|
if parse_state.current_token_index >= parse_state.result.tokens.tokens.count {
|
|
break;
|
|
}
|
|
parse_state.current = *parse_state.result.tokens.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;
|
|
}
|
|
|
|
check_any :: (parse_state : *Parse_State, kinds : ..Token_Kind) -> bool {
|
|
for kind : kinds {
|
|
if check(parse_state, kind) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//nb - Checks if the next token is of a certain kind
|
|
check_next :: (parse_state : *Parse_State, kind : Token_Kind) -> bool {
|
|
return parse_state.result.tokens.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_to_sync_point(parse_state);
|
|
|
|
unexpected_token(parse_state, token, message);
|
|
|
|
if parse_state.current.kind == .TOKEN_EOF {
|
|
return;
|
|
}
|
|
}
|
|
|
|
////////////////////////////
|
|
//@nb - Expression parsing
|
|
get_rule :: (kind : Token_Kind) -> *Parse_Rule {
|
|
return *parse_rules[kind];
|
|
}
|
|
|
|
precedence :: (parse_state : *Parse_State, precedence : Precedence, message : string = "") -> *AST_Node {
|
|
prev := parse_state.previous;
|
|
advance(parse_state);
|
|
|
|
prefix_rule := get_rule(parse_state.previous.kind).prefix;
|
|
if prefix_rule == null {
|
|
tok_s : string;
|
|
tok_s.data = prev.source;
|
|
tok_s.count = prev.length;
|
|
if message {
|
|
expected_expression(parse_state, prev, tprint("Expected expression after '%'. %", tok_s, message));
|
|
} else {
|
|
expected_expression(parse_state, prev, tprint("Expected expression after '%'.", tok_s));
|
|
}
|
|
|
|
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 {
|
|
tok_s : string;
|
|
tok_s.data = parse_state.previous.source;
|
|
tok_s.count = parse_state.previous.length;
|
|
expected_expression(parse_state, parse_state.current, tprint("Reached end of file. Expected expression after '%'.", tok_s));
|
|
// @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_TIMESEQUALS; #through;
|
|
case .TOKEN_DIVEQUALS; #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.result.tokens.tokens[parse_state.current_token_index - 3];
|
|
left_bracket := parse_state.result.tokens.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;
|
|
} else if state.current.ident_value == "if" {
|
|
if_directive := make_node(state, .If_Directive);
|
|
|
|
source_location : Source_Range;
|
|
// source_location.begin = state.previous;
|
|
|
|
advance(state);
|
|
|
|
cond := expression(state);
|
|
add_child(if_directive, cond);
|
|
|
|
if_body := block(state);
|
|
add_child(if_directive, if_body);
|
|
|
|
if_directive.source_location = source_location;
|
|
|
|
return if_directive;
|
|
}
|
|
|
|
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_any(parse_state, .TOKEN_ASSIGN, .TOKEN_MINUSEQUALS, .TOKEN_PLUSEQUALS, .TOKEN_DIVEQUALS, .TOKEN_MODEQUALS, .TOKEN_TIMESEQUALS) {
|
|
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;
|
|
}
|
|
variable := make_node(parse_state, .Variable);
|
|
variable.name = identifier.ident_value;
|
|
|
|
if check(parse_state, .TOKEN_DOT) {
|
|
advance(parse_state);
|
|
dot(parse_state, variable);
|
|
}
|
|
|
|
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, message : string = "") -> *AST_Node {
|
|
expression := precedence(parse_state, .PREC_ASSIGNMENT, message);
|
|
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.result.file.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;
|
|
|
|
error_before := parse_state.result.had_error;
|
|
parse_state.result.had_error = false;
|
|
|
|
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.");
|
|
|
|
if parse_state.result.had_error {
|
|
break;
|
|
}
|
|
}
|
|
|
|
parse_state.result.had_error = error_before || parse_state.result.had_error;
|
|
|
|
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_cond := expression(parse_state, "Expected if condition.");
|
|
add_child(node, if_cond);
|
|
source_location.end = parse_state.previous;
|
|
advance_to_sync_point(parse_state);
|
|
|
|
if_body := block(parse_state);
|
|
add_child(node, if_body);
|
|
|
|
if match(parse_state, .TOKEN_ELSE) {
|
|
else_node := else_statement(parse_state);
|
|
add_child(node, else_node);
|
|
}
|
|
|
|
node.source_location = source_location;
|
|
|
|
return node;
|
|
} else if match(parse_state, .TOKEN_ELSE) {
|
|
if check(parse_state, .TOKEN_IF) {
|
|
else_if_without_if(parse_state);
|
|
advance_to_sync_point(parse_state);
|
|
if check(parse_state, .TOKEN_LEFTBRACE) {
|
|
return block(parse_state);
|
|
}
|
|
return error_node(parse_state, "'else if' without 'if'.");
|
|
} else {
|
|
else_without_if(parse_state);
|
|
advance_to_sync_point(parse_state);
|
|
if check(parse_state, .TOKEN_LEFTBRACE) {
|
|
return block(parse_state);
|
|
}
|
|
|
|
return error_node(parse_state, "'else' without 'if'.");
|
|
}
|
|
} else if match(parse_state, .TOKEN_FOR) {
|
|
if check(parse_state, .TOKEN_IDENTIFIER) {
|
|
node := make_node(parse_state, .For);
|
|
|
|
source_location : Source_Range;
|
|
source_location.begin = parse_state.previous;
|
|
|
|
loop_iterator := parse_state.current;
|
|
node.token = loop_iterator;
|
|
advance(parse_state);
|
|
consume(parse_state, .TOKEN_COLON, "Expect ':' after for loop iterator.");
|
|
|
|
snap := snapshot_state(parse_state);
|
|
|
|
begin_iter := expression(parse_state, "Expected beginning of iterator.");
|
|
if begin_iter.kind == .Error {
|
|
unable_to_parse_statement(parse_state, source_location.begin);
|
|
rewind_to_snapshot(parse_state, snap);
|
|
if parse_state.current.kind == .TOKEN_LEFTBRACE {
|
|
block(parse_state);
|
|
}
|
|
return error_node(parse_state, "'for' without well-formed iterator expression.");
|
|
}
|
|
add_child(node, begin_iter);
|
|
|
|
consume(parse_state, .TOKEN_DOTDOT, "Expect '..' after for loop iter left hand side.");
|
|
|
|
snap = snapshot_state(parse_state);
|
|
end_iter := expression(parse_state, "Expected end of iterator.");
|
|
if end_iter.kind == .Error {
|
|
unable_to_parse_statement(parse_state, source_location.begin);
|
|
rewind_to_snapshot(parse_state, snap);
|
|
|
|
if parse_state.current.kind == .TOKEN_LEFTBRACE {
|
|
|
|
block(parse_state);
|
|
}
|
|
return error_node(parse_state, "'for' without well-formed iterator expression.");
|
|
}
|
|
add_child(node, end_iter);
|
|
|
|
if check(parse_state, .TOKEN_LEFTBRACE) {
|
|
for_body := block(parse_state);
|
|
add_child(node, for_body);
|
|
} else {
|
|
unable_to_parse_statement(parse_state, source_location.begin, "'for' currently expects a brace-enclosed block as a body.");
|
|
return error_node(parse_state, "'for' currently expects a brace-enclosed block as a body.");
|
|
}
|
|
|
|
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;
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
parse_state : Parse_State;
|
|
result.nodes.allocator = result.allocator;
|
|
array_reserve(*result.nodes, 4096);
|
|
parse_state.current_token_index = 0;
|
|
parse_state.result = 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#load "AST.jai";
|