Skip to content

Commit

Permalink
Added support for char literals.
Browse files Browse the repository at this point in the history
  • Loading branch information
Goubermouche committed Jan 20, 2024
1 parent a6b2015 commit f0a8ff2
Show file tree
Hide file tree
Showing 28 changed files with 138 additions and 19 deletions.
1 change: 1 addition & 0 deletions source/abstract_syntax_tree/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ namespace sigma {
case OPERATOR_MODULO: return "OPERATOR_MODULO";

case NUMERICAL_LITERAL: return "NUMERICAL_LITERAL";
case CHARACTER_LITERAL: return "CHARACTER_LITERAL";
case STRING_LITERAL: return "STRING_LITERAL";
case BOOL_LITERAL: return "BOOL_LITERAL";

Expand Down
1 change: 1 addition & 0 deletions source/abstract_syntax_tree/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ namespace sigma {
OPERATOR_MODULO,

NUMERICAL_LITERAL,
CHARACTER_LITERAL,
STRING_LITERAL,
BOOL_LITERAL
};
Expand Down
2 changes: 2 additions & 0 deletions source/compiler/compiler/diagnostics.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ namespace sigma {

// tokenizer (2000 - 2999)
INVALID_STRING_TERMINATOR = 2000,
INVALID_CHAR_TERMINATOR,
NUMERICAL_LITERAL_FP_WITHOUT_DOT,
NUMERICAL_LITERAL_UNSIGNED_WITH_DOT,
NUMERICAL_LITERAL_MORE_THAN_ONE_DOT,
Expand Down Expand Up @@ -85,6 +86,7 @@ namespace sigma {

// tokenizer
{ code::INVALID_STRING_TERMINATOR, "invalid string literal terminator detected" },
{ code::INVALID_CHAR_TERMINATOR, "invalid char literal terminator detected" },
{ code::NUMERICAL_LITERAL_FP_WITHOUT_DOT, "numerical floating-point literal without '.' character detected" },
{ code::NUMERICAL_LITERAL_UNSIGNED_WITH_DOT, "unsigned numerical literal with '.' character detected" },
{ code::NUMERICAL_LITERAL_MORE_THAN_ONE_DOT, "numerical literal with more than one '.' character detected" },
Expand Down
1 change: 1 addition & 0 deletions source/compiler/compiler/type_system/semantic_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ namespace sigma {
case data_type::U8: return I8_TYPE;
case data_type::I16:
case data_type::U16: return I16_TYPE;
case data_type::CHAR:
case data_type::I32:
case data_type::U32: return I32_TYPE;
case data_type::I64:
Expand Down
7 changes: 2 additions & 5 deletions source/compiler/test/main.s
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,10 @@
// - check for memory oversteps in the parser
// - add namespaces to error messages, whenever applicable (ie. x::y::test)
// - namespace directives should probably be a part of the function signature?
// - handle literal overflow in the typechecker, instead of the IR translator

i32 main() {
u64 size = 20;
i32* value = malloc(size);
printf("%d\n", value);
void x;
char c = 'x';

printf("test %c\n", c);
ret 0;
}
14 changes: 13 additions & 1 deletion source/ir_translator/ir_translator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ namespace sigma {

// literals
case node_type::NUMERICAL_LITERAL: return translate_numerical_literal(ast_node);
case node_type::CHARACTER_LITERAL: return translate_character_literal(ast_node);
case node_type::STRING_LITERAL: return translate_string_literal(ast_node);
case node_type::BOOL_LITERAL: return translate_bool_literal(ast_node);
default: PANIC("irgen for node '{}' is not implemented", ast_node->type.to_string());
Expand Down Expand Up @@ -169,6 +170,12 @@ namespace sigma {
return literal_to_ir(numerical_literal_node->get<ast_literal>());
}

auto ir_translator::translate_character_literal(handle<node> character_literal_node) const -> handle<ir::node> {
const std::string& value = m_context.strings.get(character_literal_node->get<ast_literal>().value_key);
ASSERT(value.size() == 1, "invalid char literal length");
return m_context.builder.create_signed_integer(value[0], 32);
}

auto ir_translator::translate_string_literal(handle<node> string_literal_node) const -> handle<ir::node> {
const std::string& value = m_context.strings.get(string_literal_node->get<ast_literal>().value_key);
return m_context.builder.create_string(value);
Expand Down Expand Up @@ -238,10 +245,15 @@ namespace sigma {
NOT_IMPLEMENTED();
}

bool overflow = false;
bool overflow; // ignored

switch (literal.type.base_type) {
case data_type::I8: return m_context.builder.create_signed_integer(utility::detail::from_string<i32>(value, overflow), 8);
case data_type::I16: return m_context.builder.create_signed_integer(utility::detail::from_string<i32>(value, overflow), 16);
case data_type::I32: return m_context.builder.create_signed_integer(utility::detail::from_string<i32>(value, overflow), 32);
case data_type::I64: return m_context.builder.create_signed_integer(utility::detail::from_string<i32>(value, overflow), 64);
case data_type::U8: return m_context.builder.create_unsigned_integer(utility::detail::from_string<u32>(value, overflow), 8);
case data_type::U16: return m_context.builder.create_unsigned_integer(utility::detail::from_string<u32>(value, overflow), 16);
case data_type::U32: return m_context.builder.create_unsigned_integer(utility::detail::from_string<u32>(value, overflow), 32);
case data_type::U64: return m_context.builder.create_unsigned_integer(utility::detail::from_string<u64>(value, overflow), 64);
default: NOT_IMPLEMENTED();
Expand Down
1 change: 1 addition & 0 deletions source/ir_translator/ir_translator.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ namespace sigma {
void translate_branch(handle<node> branch_node, handle<ir::node> exit_control);

auto translate_numerical_literal(handle<node> numerical_literal_node) const->handle<ir::node>;
auto translate_character_literal(handle<node> character_literal_node) const->handle<ir::node>;
auto translate_string_literal(handle<node> string_literal_node) const->handle<ir::node>;
auto translate_bool_literal(handle<node> bool_literal_node) const->handle<ir::node>;

Expand Down
1 change: 1 addition & 0 deletions source/parser/data_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ namespace sigma {
case token_type::U64: return U64;
case token_type::BOOL: return BOOL;
case token_type::VOID: return VOID;
case token_type::CHAR: return CHAR;
default: PANIC("undefined token -> type conversion for token '{}'", token.to_string());
}

Expand Down
17 changes: 17 additions & 0 deletions source/parser/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@ namespace sigma {
switch (m_tokens.get_current_token()) {
case token_type::IDENTIFIER: return parse_identifier_statement();
case token_type::STRING_LITERAL: return parse_string_literal();
case token_type::CHARACTER_LITERAL: return parse_character_literal();
case token_type::MINUS_SIGN: return parse_negative_expression();
case token_type::BOOL_LITERAL_TRUE:
case token_type::BOOL_LITERAL_FALSE: return parse_bool_literal();
Expand Down Expand Up @@ -556,6 +557,22 @@ namespace sigma {
return literal_node;
}

auto parser::parse_character_literal() const -> utility::result<handle<node>> {
EXPECT_CURRENT_TOKEN(token_type::CHARACTER_LITERAL);
const handle<token_location> location = m_tokens.get_current_token_location();

// create the string node
const handle<node> char_node = create_node<ast_literal>(node_type::CHARACTER_LITERAL, 0);

// initialize the literal
auto& literal = char_node->get<ast_literal>();
literal.value_key = m_tokens.get_current().symbol_key;
literal.location = location;
literal.type = { data_type::CHAR, 0 }; // char

return char_node;
}

auto parser::parse_string_literal() const -> utility::result<handle<node>> {
EXPECT_CURRENT_TOKEN(token_type::STRING_LITERAL);
const handle<token_location> location = m_tokens.get_current_token_location();
Expand Down
1 change: 1 addition & 0 deletions source/parser/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ namespace sigma {

// literals
auto parse_numerical_literal() const-> utility::result<handle<node>>;
auto parse_character_literal() const->utility::result<handle<node>>;
auto parse_string_literal() const-> utility::result<handle<node>>;
auto parse_bool_literal() const -> utility::result<handle<node>>;

Expand Down
20 changes: 11 additions & 9 deletions source/tests/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,25 @@

using namespace utility::types;

#define STOUD_FILE "STDOUT.txt"
#define STERR_FILE "STDERR.txt"
#define STDOUT_FILE "STDOUT.txt"
#define STDERR_FILE "STDERR.txt"

bool run_test(const filepath& path, const filepath& compiler_path) {
const std::string command = std::format("{} compile {} -e none > {} 2> {}", compiler_path, path, STOUD_FILE, STERR_FILE);
const std::string command = std::format("{} compile {} -e none > {} 2> {}", compiler_path, path, STDOUT_FILE, STDERR_FILE);
const filepath& pretty_path = path.get_parent_path().get_filename() / path.get_filename();

const i32 return_code = utility::shell::execute(command);

if(return_code == 0) {
utility::console::print("{:<30} OK\n", path.get_filename().to_string());
utility::console::print("{:<40} OK\n", pretty_path.to_string());
return false;
}

utility::console::printerr("{:<30} ERROR\n", path.get_filename().to_string());
utility::console::printerr("{:<40} ERROR\n", pretty_path.to_string());

const auto file_result = utility::fs::file<std::string>::load(STERR_FILE);
const auto file_result = utility::fs::file<std::string>::load(STDERR_FILE);
if(file_result.has_error()) {
throw std::runtime_error(std::format("cannot open file {}", STERR_FILE).c_str());
throw std::runtime_error(std::format("cannot open file {}", STDERR_FILE).c_str());
}

utility::console::printerr("'{}'\n", file_result.get_value());
Expand Down Expand Up @@ -57,8 +59,8 @@ i32 run_all_tests(const parametric::parameters& params) {

// cleanup
try {
utility::fs::remove(STOUD_FILE);
utility::fs::remove(STERR_FILE);
utility::fs::remove(STDOUT_FILE);
utility::fs::remove(STDERR_FILE);
} catch(const std::exception& exception) {
utility::console::printerr("error: {}\n", exception.what());
encountered_error = true;
Expand Down
3 changes: 3 additions & 0 deletions source/tokenizer/token.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace sigma {
case token_type::U64:
case token_type::BOOL:
case token_type::VOID:
case token_type::CHAR:
return true;
default:
return false;
Expand Down Expand Up @@ -67,6 +68,7 @@ namespace sigma {

case token_type::BOOL: return "BOOL";
case token_type::VOID: return "VOID";
case token_type::CHAR: return "CHAR";

// control flow
case token_type::RET: return "RET";
Expand All @@ -84,6 +86,7 @@ namespace sigma {
case token_type::HEXADECIMAL_LITERAL: return "HEXADECIMAL_LITERAL";
case token_type::BINARY_LITERAL: return "BINARY_LITERAL";
case token_type::STRING_LITERAL: return "STRING_LITERAL";
case token_type::CHARACTER_LITERAL: return "CHARACTER_LITERAL";
case token_type::BOOL_LITERAL_TRUE: return "BOOL_LITERAL_TRUE";
case token_type::BOOL_LITERAL_FALSE: return "BOOL_LITERAL_FALSE";

Expand Down
2 changes: 2 additions & 0 deletions source/tokenizer/token.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ namespace sigma {

BOOL,
VOID,
CHAR,

// control flow
RET, // ret
Expand All @@ -58,6 +59,7 @@ namespace sigma {
HEXADECIMAL_LITERAL, // 0x / 0X
BINARY_LITERAL, // 0b / 0B
STRING_LITERAL, // "text"
CHARACTER_LITERAL, // 'x'
BOOL_LITERAL_TRUE, // true
BOOL_LITERAL_FALSE, // false

Expand Down
20 changes: 19 additions & 1 deletion source/tokenizer/tokenizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ namespace sigma {

// single quote characters are interpreted as character literals
if(m_last_character == '\'') {
NOT_IMPLEMENTED();
return get_char_literal_token();
}

// double quote characters are interpreted as string literals
Expand Down Expand Up @@ -255,6 +255,24 @@ namespace sigma {
};
}

auto tokenizer::get_char_literal_token() -> utility::result<token_info> {
get_next_char(); // read the character after the opening quote
m_current_section = get_escaped_character();
get_next_char(); // read the closing quote

if(m_last_character != '\'') {
return error::emit(error::code::INVALID_CHAR_TERMINATOR);
}

get_next_char();

return token_info{
.tok = { token_type::CHARACTER_LITERAL },
.location = m_context.allocator.emplace<token_location>(m_token_start_location),
.symbol_key = m_context.strings.insert(m_current_section)
};
}

auto tokenizer::get_special_token() -> utility::result<token_info> {
m_current_section = m_last_character;

Expand Down
5 changes: 4 additions & 1 deletion source/tokenizer/tokenizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace sigma {
[[nodiscard]] auto get_alphabetical_token() -> utility::result<token_info>;
[[nodiscard]] auto get_numerical_token() -> utility::result<token_info>;
[[nodiscard]] auto get_string_literal_token() -> utility::result<token_info>;
[[nodiscard]] auto get_char_literal_token() -> utility::result<token_info>;
[[nodiscard]] auto get_special_token() -> utility::result<token_info>;

void consume_spaces();
Expand Down Expand Up @@ -52,9 +53,11 @@ namespace sigma {
{ "u32", token_type::U32 },
{ "u64", token_type::U64 },
{ "bool", token_type::BOOL },
{ "void", token_type::VOID },
{ "char", token_type::CHAR },

{ "true", token_type::BOOL_LITERAL_TRUE },
{ "false", token_type::BOOL_LITERAL_FALSE },
{ "void", token_type::VOID },
};

const std::unordered_map<std::string, token_type> m_special_tokens = {
Expand Down
7 changes: 7 additions & 0 deletions source/type_checker/type_checker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ namespace sigma {

// literals
{ node_type::NUMERICAL_LITERAL, &type_checker::type_check_numerical_literal },
{ node_type::CHARACTER_LITERAL, &type_checker::type_check_character_literal },
{ node_type::STRING_LITERAL, &type_checker::type_check_string_literal },
{ node_type::BOOL_LITERAL, &type_checker::type_check_bool_literal },
};
Expand Down Expand Up @@ -269,6 +270,12 @@ namespace sigma {
return literal.type;
}

auto type_checker::type_check_character_literal(handle<node> literal_node, data_type expected) -> utility::result<data_type> {
auto& literal = literal_node->get<ast_literal>();
apply_expected_data_type(literal.type, expected);
return literal.type;
}

auto type_checker::type_check_string_literal(handle<node> literal_node, data_type expected) -> utility::result<data_type> {
auto& literal = literal_node->get<ast_literal>();
apply_expected_data_type(literal.type, expected);
Expand Down
1 change: 1 addition & 0 deletions source/type_checker/type_checker.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ namespace sigma {
auto type_check_variable_assignment(handle<node> assignment_node, data_type expected)->utility::result<data_type>;

auto type_check_numerical_literal(handle<node> literal_node, data_type expected) -> utility::result<data_type>;
auto type_check_character_literal(handle<node> literal_node, data_type expected) -> utility::result<data_type>;
auto type_check_string_literal(handle<node> literal_node, data_type expected) -> utility::result<data_type>;
auto type_check_bool_literal(handle<node> literal_node, data_type expected) -> utility::result<data_type>;

Expand Down
4 changes: 4 additions & 0 deletions source/utility/filesystem/filepath.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ namespace utility::types {
auto get_filename() const -> filepath {
return m_path.filename();
}

auto operator/(const filepath& other) {
return m_path / other.m_path;
}
private:
path_type m_path;
};
Expand Down
4 changes: 4 additions & 0 deletions tests/literals/char.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
i32 main() {
char c = 'x';
ret 0;
}
5 changes: 5 additions & 0 deletions tests/literals/i16.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
i32 main() {
i32 min = -32768;
i32 max = 32767;
ret 0;
}
5 changes: 3 additions & 2 deletions tests/literals/i32.s
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
i32 main() {
i32 value = 1000;
i32 min = -2147483648;
i32 max = 2147483647;
ret 0;
}
}
5 changes: 5 additions & 0 deletions tests/literals/i64.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
i32 main() {
i32 min = -9223372036854775808;
i32 max = 9223372036854775807;
ret 0;
}
5 changes: 5 additions & 0 deletions tests/literals/i8.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
i32 main() {
i32 min = -128;
i32 max = 127;
ret 0;
}
5 changes: 5 additions & 0 deletions tests/literals/string.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
i32 main() {
char* str1 = "test";
char* str2 = "\n\n\n";
ret 0;
}
5 changes: 5 additions & 0 deletions tests/literals/u16.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
i32 main() {
i32 min = 0;
i32 max = 65535;
ret 0;
}
5 changes: 5 additions & 0 deletions tests/literals/u32.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
i32 main() {
i32 min = 0;
i32 max = 4294967295;
ret 0;
}
5 changes: 5 additions & 0 deletions tests/literals/u64.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
i32 main() {
i32 min = 0;
i32 max = 18446744073709551615;
ret 0;
}
5 changes: 5 additions & 0 deletions tests/literals/u8.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
i32 main() {
i32 min = 0;
i32 max = 255;
ret 0;
}

0 comments on commit f0a8ff2

Please sign in to comment.