296 lines
6.9 KiB
C++
296 lines
6.9 KiB
C++
#include <compiler.h>
|
|
|
|
global_variable constexpr u8 left_bracket = '[';
|
|
global_variable constexpr u8 right_bracket = ']';
|
|
global_variable constexpr u8 left_brace = '{';
|
|
global_variable constexpr u8 right_brace = '}';
|
|
global_variable constexpr u8 left_parenthesis = '(';
|
|
global_variable constexpr u8 right_parenthesis = ')';
|
|
|
|
fn bool is_space(u8 ch)
|
|
{
|
|
return ((ch == ' ') | (ch == '\n')) | ((ch == '\t') | (ch == '\r'));
|
|
}
|
|
|
|
fn bool is_lower(u8 ch)
|
|
{
|
|
return ((ch >= 'a') & (ch <= 'z'));
|
|
}
|
|
|
|
fn bool is_upper(u8 ch)
|
|
{
|
|
return ((ch >= 'A') & (ch <= 'Z'));
|
|
}
|
|
|
|
fn bool is_decimal(u8 ch)
|
|
{
|
|
return ((ch >= '0') & (ch <= '9'));
|
|
}
|
|
|
|
fn bool is_identifier_start(u8 ch)
|
|
{
|
|
return (is_lower(ch) | is_upper(ch)) | (ch == '_');
|
|
}
|
|
|
|
fn bool is_identifier(u8 ch)
|
|
{
|
|
return is_identifier_start(ch) | is_decimal(ch);
|
|
}
|
|
|
|
fn u32 get_line(Module& module)
|
|
{
|
|
auto line = module.line_offset + 1;
|
|
assert(line < ~(u32)0);
|
|
return (u32)line;
|
|
}
|
|
|
|
fn u32 get_column(Module& module)
|
|
{
|
|
auto column = module.offset - module.line_character_offset + 1;
|
|
assert(column < ~(u32)0);
|
|
return (u32)column;
|
|
}
|
|
|
|
fn bool consume_character_if_match(Module& module, u8 expected_ch)
|
|
{
|
|
bool is_ch = false;
|
|
auto i = module.offset;
|
|
if (i < module.content.length)
|
|
{
|
|
auto ch = module.content[i];
|
|
is_ch = expected_ch == ch;
|
|
module.offset = i + 1;
|
|
}
|
|
|
|
return is_ch;
|
|
}
|
|
|
|
fn void expect_character(Module& module, u8 expected_ch)
|
|
{
|
|
if (!consume_character_if_match(module, expected_ch))
|
|
{
|
|
report_error();
|
|
}
|
|
}
|
|
|
|
fn void skip_space(Module& module)
|
|
{
|
|
while (1)
|
|
{
|
|
auto iteration_offset = module.offset;
|
|
|
|
while (module.offset < module.content.length)
|
|
{
|
|
auto ch = module.content[module.offset];
|
|
if (!is_space(ch))
|
|
{
|
|
break;
|
|
}
|
|
|
|
module.line_offset += ch == '\n';
|
|
module.line_character_offset = ch == '\n' ? module.offset : module.line_character_offset;
|
|
module.offset += 1;
|
|
}
|
|
|
|
if (module.offset + 1 < module.content.length)
|
|
{
|
|
auto i = module.offset;
|
|
auto first_ch = module.content[i];
|
|
auto second_ch = module.content[i + 1];
|
|
auto is_comment = first_ch == '/' && second_ch == '/';
|
|
|
|
if (is_comment)
|
|
{
|
|
while (module.offset < module.content.length)
|
|
{
|
|
auto ch = module.content[module.offset];
|
|
if (ch == '\n')
|
|
{
|
|
break;
|
|
}
|
|
module.offset += 1;
|
|
}
|
|
|
|
if (module.offset < module.content.length)
|
|
{
|
|
module.line_offset += 1;
|
|
module.line_character_offset = module.offset;
|
|
module.offset += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (module.offset - iteration_offset == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn String parse_identifier(Module& module)
|
|
{
|
|
auto start = module.offset;
|
|
|
|
if (is_identifier_start(module.content[start]))
|
|
{
|
|
module.offset = start + 1;
|
|
|
|
while (module.offset < module.content.length)
|
|
{
|
|
auto i = module.offset;
|
|
if (is_identifier(module.content[i]))
|
|
{
|
|
module.offset = i + 1;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto end = module.offset;
|
|
if (end - start == 0)
|
|
{
|
|
report_error();
|
|
}
|
|
|
|
return module.content(start, end);
|
|
}
|
|
|
|
fn Type* parse_type(Module& module)
|
|
{
|
|
trap_raw();
|
|
}
|
|
|
|
void parse(Module& module)
|
|
{
|
|
while (1)
|
|
{
|
|
skip_space(module);
|
|
|
|
if (module.offset == module.content.length)
|
|
{
|
|
break;
|
|
}
|
|
|
|
bool is_export = false;
|
|
bool is_extern = false;
|
|
|
|
auto global_line = get_line(module);
|
|
auto global_column = get_column(module);
|
|
|
|
if (consume_character_if_match(module, left_bracket))
|
|
{
|
|
while (module.offset < module.content.length)
|
|
{
|
|
auto global_keyword_string = parse_identifier(module);
|
|
String global_keyword_strings[] = {
|
|
str("export"),
|
|
str("extern"),
|
|
};
|
|
enum class GlobalKeyword
|
|
{
|
|
export_keyword,
|
|
extern_keyword,
|
|
};
|
|
|
|
u32 i;
|
|
for (i = 0; i < array_length(global_keyword_strings); i += 1)
|
|
{
|
|
String keyword = global_keyword_strings[i];
|
|
if (keyword.equal(global_keyword_string))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == array_length(global_keyword_strings))
|
|
{
|
|
report_error();
|
|
}
|
|
|
|
auto global_keyword = (GlobalKeyword)i;
|
|
switch (global_keyword)
|
|
{
|
|
case GlobalKeyword::export_keyword:
|
|
{
|
|
is_export = true;
|
|
} break;
|
|
case GlobalKeyword::extern_keyword:
|
|
{
|
|
is_extern = true;
|
|
} break;
|
|
}
|
|
|
|
if (consume_character_if_match(module, right_bracket))
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
report_error();
|
|
}
|
|
}
|
|
|
|
skip_space(module);
|
|
}
|
|
|
|
auto global_name = parse_identifier(module);
|
|
|
|
Global* last_global = module.first_global;
|
|
while (last_global)
|
|
{
|
|
if (global_name.equal(last_global->variable.name))
|
|
{
|
|
report_error();
|
|
}
|
|
|
|
if (!last_global->next)
|
|
{
|
|
break;
|
|
}
|
|
|
|
last_global = last_global->next;
|
|
}
|
|
|
|
Type* type_it = module.first_type;
|
|
Type* forward_declaration = 0;
|
|
while (type_it)
|
|
{
|
|
if (global_name.equal(type_it->name))
|
|
{
|
|
if (type_it->id == TypeId::forward_declaration)
|
|
{
|
|
forward_declaration = type_it;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
report_error();
|
|
}
|
|
}
|
|
|
|
if (!type_it->next)
|
|
{
|
|
break;
|
|
}
|
|
|
|
type_it = type_it->next;
|
|
}
|
|
|
|
Type* global_type = 0;
|
|
|
|
if (consume_character_if_match(module, ':'))
|
|
{
|
|
skip_space(module);
|
|
|
|
global_type = parse_type(module);
|
|
|
|
skip_space(module);
|
|
}
|
|
|
|
trap_raw();
|
|
}
|
|
}
|