diff --git a/src/analysis.cpp b/src/analysis.cpp index 4efe0e6..c3ccc0d 100644 --- a/src/analysis.cpp +++ b/src/analysis.cpp @@ -214,6 +214,9 @@ AnalysisResult analyze(Node &ast, Memory &memory) { throw TypeError(ErrorType::AlreadyDeclaredIdentifier, token.pos, identifier); Type type = try_string_to_type(type_string, type_token.pos); + if (type.type == TypeType::Void) + throw TypeError(ErrorType::IncompleteType, token.pos, {}); + memory.declare(identifier, type); return {}; @@ -229,6 +232,9 @@ AnalysisResult analyze(Node &ast, Memory &memory) { throw TypeError(ErrorType::AlreadyDeclaredIdentifier, token.pos, identifier); Type type = try_string_to_type(type_string, type_token.pos); + if (type.type == TypeType::Void) + throw TypeError(ErrorType::IncompleteType, token.pos, {}); + memory.declare(identifier, type); get_cast(type, analyze(node.children[2], memory), get_node_pos(node)); @@ -295,6 +301,15 @@ AnalysisResult analyze(Node &ast, Memory &memory) { } } + for (int i=1; i < (int)prototype.size(); i++) { + if (get<0>(prototype.at(i)).type == TypeType::Void) { + InnerNode args_node = get(node.children[2]); + InnerNode declaration_node = get(args_node.children[i-1]); + CodePosition type_pos = get_node_pos(declaration_node.children[0]); + throw TypeError(ErrorType::IncompleteType, type_pos, {}); + } + } + memory.declare(function_name, type); return {}; @@ -335,6 +350,15 @@ AnalysisResult analyze(Node &ast, Memory &memory) { } } + for (int i=1; i < (int)prototype.size(); i++) { + if (get<0>(prototype.at(i)).type == TypeType::Void) { + InnerNode args_node = get(node.children[2]); + InnerNode declaration_node = get(args_node.children[i-1]); + CodePosition type_pos = get_node_pos(declaration_node.children[0]); + throw TypeError(ErrorType::IncompleteType, type_pos, {}); + } + } + memory.declare(function_name, type); Function fn = { {}, memory.make_closure() }; diff --git a/src/errors.cpp b/src/errors.cpp index 0503704..1a991fa 100644 --- a/src/errors.cpp +++ b/src/errors.cpp @@ -1,8 +1,10 @@ #include #include +#include #include using namespace std; +#include "include/utils.h" #include "include/errors.h" #include "include/colors.h" #include "include/tokenize.h" @@ -44,6 +46,7 @@ string UserError::get_message(void) const { case ErrorType::IncompatibleDefinition: return "Declaration of '"+get(this->data)+"' is incompatible with previous prototype"; case ErrorType::UnexpectedArgumentCount: return "Expected "+to_string(get(this->data))+" arguments to function call"; case ErrorType::IncompatibleReturnValue: return "Return value type does not match the function type"; + case ErrorType::IncompleteType: return "Incomplete type is not allowed"; // Runtime case ErrorType::UnknownIdentifier: return "Unknown identifier '"+get(this->data)+"'"; @@ -60,14 +63,27 @@ void print_error_position(vector history, CodePosition pos) { if (pos.column == -1 || pos.line == -1) return; - cout << endl; - string line = history[pos.line]; - - printf(BOLD "%4d " RESET , pos.line+1); - cout << line << endl; + cout << endl << BOLD + << setw(4) << setfill(' ') + << pos.line+1 << " " RESET + << history[pos.line] << endl; for (int i=0; i < pos.column + 5; i++) cout << " "; cout << BOLD RED "^--" RESET << endl; +} + +void print_error_stack_trace(vector history, const RuntimeError& error) { + cout << "\n" BOLD "Traceback" RESET " (most recent call last)" << endl; + for (StackTraceEntry e : error.trace) { + cout << BOLD << setw(8) << setfill(' ') + << to_string(get<1>(e).line+1) + ":" + to_string(get<1>(e).column+1) + << RESET BLUE + << setw(16) << setfill(' ') + << get<0>(e) << RESET << " \t" + << trim(history[get<1>(e).line]) << endl; + } + + cout << BOLD RED "Runtime Error: " RESET << error.get_message() << endl; } \ No newline at end of file diff --git a/src/include/errors.h b/src/include/errors.h index 408bc5a..4f9f2ae 100644 --- a/src/include/errors.h +++ b/src/include/errors.h @@ -45,6 +45,7 @@ enum class ErrorType { IncompatibleDefinition, // function declaration incompatible with prototype UnexpectedArgumentCount, IncompatibleReturnValue, + IncompleteType, // void x // Runtime UnknownIdentifier, @@ -115,4 +116,9 @@ public: */ void print_error_position(vector history, CodePosition pos); +/** + * Display the stack trace associated with an error +*/ +void print_error_stack_trace(vector history, const RuntimeError& error); + #endif \ No newline at end of file diff --git a/src/include/utils.h b/src/include/utils.h index 6e9a1f3..be847ed 100644 --- a/src/include/utils.h +++ b/src/include/utils.h @@ -39,4 +39,9 @@ string _debug_get_type_type_name(TypeType type); */ bool equal_types(Type type1, Type type2); +/** + * Trim a string (remove whitespaces before and after) +*/ +string trim(const string& str); + #endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index a8fc983..780ef16 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -119,12 +119,7 @@ int main(int argc, char* argv[]) { cout << BOLD RED "Type Error: " RESET << e.get_message() << endl; } catch (const RuntimeError& e) { - print_error_position(input, e.pos); - cout << BOLD RED "Runtime Error: " RESET << e.get_message() << endl; - - for (StackTraceEntry e : e.trace) { - cout << get<0>(e) << " " << (get<1>(e).line + 1) << endl; - } + print_error_stack_trace(input, e); } cout << endl; } diff --git a/src/utils.cpp b/src/utils.cpp index a169ef2..022154d 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -111,4 +111,14 @@ bool equal_types(Type type1, Type type2) { return false; break; } +} + +string trim(const string& str) { + size_t first = str.find_first_not_of(" \t\n\r"); + size_t last = str.find_last_not_of(" \t\n\r"); + + if (first == string::npos || last == string::npos) + return ""; // or handle empty string + + return str.substr(first, last - first + 1); } \ No newline at end of file diff --git a/test/functions.cpp b/test/functions.cpp index 5c42735..567af3f 100644 --- a/test/functions.cpp +++ b/test/functions.cpp @@ -98,5 +98,17 @@ int main() { true ); + _TEST_ASSERT( + _TEST_IS_EXCEPTION(execute("int f(int x, void y);"), ErrorType::IncompleteType), + "Argument void (prototype)", + true + ); + + _TEST_ASSERT( + _TEST_IS_EXCEPTION(execute("int f(int x, void y) { return x+2; };"), ErrorType::IncompleteType), + "Argument void (déclaration)", + true + ); + return 0; } \ No newline at end of file diff --git a/test/variables.cpp b/test/variables.cpp index b55ccdc..6938560 100644 --- a/test/variables.cpp +++ b/test/variables.cpp @@ -35,6 +35,12 @@ int main() { true ); + _TEST_ASSERT( + _TEST_IS_EXCEPTION(execute("void x;"), ErrorType::IncompleteType), + "Type void", + true + ); + _TEST_ASSERT( _TEST_NO_EXCEPTION(holds_alternative(execute("int x; { int x; }"))), "Identifieur déjà défini dans une autre scope",