From 6fd95bd486446068b280a5f3b13660a1ee025633 Mon Sep 17 00:00:00 2001 From: Shautvast Date: Fri, 25 Oct 2024 16:43:57 +0200 Subject: [PATCH] C++23, rusty error handling, renamed output repr of tokens --- compile_commands.json | 53 +----------------------- makefile | 2 +- src/error.hpp | 31 +++++++++++++++ src/lox.cpp | 21 ++++++---- src/parser.cpp | 93 ++++++++++++++++++++++++++++--------------- src/parser.hpp | 29 +++++++------- src/scanner.cpp | 24 +++++------ src/scanner.hpp | 7 ++-- src/tokens.cpp | 14 +++---- 9 files changed, 144 insertions(+), 130 deletions(-) create mode 100644 src/error.hpp diff --git a/compile_commands.json b/compile_commands.json index 91b8f36..0dbb570 100644 --- a/compile_commands.json +++ b/compile_commands.json @@ -3,7 +3,7 @@ "arguments": [ "/usr/bin/clang++", "-c", - "-std=c++17", + "-std=c++23", "-Wall", "-Wextra", "-pedantic", @@ -15,56 +15,5 @@ "directory": "/Users/Shautvast/dev/clox", "file": "/Users/Shautvast/dev/clox/src/lox.cpp", "output": "/Users/Shautvast/dev/clox/target/lox.cpp.o" - }, - { - "arguments": [ - "/usr/bin/clang++", - "-c", - "-std=c++17", - "-Wall", - "-Wextra", - "-pedantic", - "-Werror", - "-o", - "target/parser.cpp.o", - "src/parser.cpp" - ], - "directory": "/Users/Shautvast/dev/clox", - "file": "/Users/Shautvast/dev/clox/src/parser.cpp", - "output": "/Users/Shautvast/dev/clox/target/parser.cpp.o" - }, - { - "arguments": [ - "/usr/bin/clang++", - "-c", - "-std=c++17", - "-Wall", - "-Wextra", - "-pedantic", - "-Werror", - "-o", - "target/scanner.cpp.o", - "src/scanner.cpp" - ], - "directory": "/Users/Shautvast/dev/clox", - "file": "/Users/Shautvast/dev/clox/src/scanner.cpp", - "output": "/Users/Shautvast/dev/clox/target/scanner.cpp.o" - }, - { - "arguments": [ - "/usr/bin/clang++", - "-c", - "-std=c++17", - "-Wall", - "-Wextra", - "-pedantic", - "-Werror", - "-o", - "target/tokens.cpp.o", - "src/tokens.cpp" - ], - "directory": "/Users/Shautvast/dev/clox", - "file": "/Users/Shautvast/dev/clox/src/tokens.cpp", - "output": "/Users/Shautvast/dev/clox/target/tokens.cpp.o" } ] diff --git a/makefile b/makefile index a306f6d..bbe2515 100644 --- a/makefile +++ b/makefile @@ -1,6 +1,6 @@ TARGET := ./target SRC := ./src -CC := clang++ -c -std=c++17 -Wall -Wextra -pedantic -Werror +CC := clang++ -c -std=c++23 -Wall -Wextra -pedantic -Werror SRCS := $(shell find $(SRC) -name '*.c') OBJS := $(SRCS:%=$(TARGET)/%.o) diff --git a/src/error.hpp b/src/error.hpp new file mode 100644 index 0000000..c045ddc --- /dev/null +++ b/src/error.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +using namespace std; + +class Error { + +public: + string message; + Error(string _message) : message(_message){}; +}; + +class Void {}; + +template using Result = std::variant; + +template bool is_err(Result r) { + return std::holds_alternative(r); +} +template bool is_ok(Result r) { + return std::holds_alternative(r); +} + +template R Ok(Result r) { return std::get(r); } +/// enables rewrapping errors in a new Result type +template Error Err(Result r) { return std::get(r); } +template string err_msg(Result r) { + return std::get(r).message; +} diff --git a/src/lox.cpp b/src/lox.cpp index cef5239..c94ee35 100644 --- a/src/lox.cpp +++ b/src/lox.cpp @@ -12,7 +12,7 @@ using namespace std; void print_tokens(vector *list); int run_file(string file); void run_prompt(void); -ScanResult run(string source); +Result> run(string source); int main(int argc, char *argv[]) { if (argc > 2) { @@ -36,7 +36,7 @@ int run_file(string filename) { exit(1); } - ScanResult scan_result = run(content); + Result> scan_result = run(content); return EXIT_SUCCESS; } @@ -49,17 +49,22 @@ void run_prompt(void) { getline(cin, line); - ScanResult scan_result = run(line.substr(0, line.length())); + auto scan_result = run(line.substr(0, line.length())); // print_tokens(&scan_result.token_list); - if (!scan_result.had_error) { - Expression *e = (new Parser())->parse(scan_result.token_list); - cout << e->as_string(); - cout << "\n"; + if (is_ok(scan_result)) { + auto expression = (new Parser())->parse(get>(scan_result)); + if (is_ok(expression)) { + cout << Ok(expression)->as_string() << "\n"; + } else { + cout << err_msg(expression) << "\n"; + } + } else { + cout << err_msg(scan_result) << "\n"; } } } -ScanResult run(string source) { +Result> run(string source) { Scanner *scanner = new Scanner(source); return scanner->scan_tokens(); } diff --git a/src/parser.cpp b/src/parser.cpp index b548edb..38df427 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,4 +1,5 @@ #include "parser.hpp" + #include #include @@ -61,7 +62,7 @@ string Literal::as_string() { } // class Parser -Expression *Parser::parse(vector tokenlist) { +Result Parser::parse(vector tokenlist) { tokens = tokenlist; current_token = 0; return expression(); @@ -101,25 +102,25 @@ bool Parser::match(int count, ...) { return false; }; -Token *Parser::consume(Token::Type typ, string message) { +Result Parser::consume(Token::Type typ, string message) { if (check(typ)) { return advance(); } - throw error(peek(), message); + return error(peek(), message); } -runtime_error Parser::error(Token token, string message) { +Error Parser::error(Token token, string message) { std::cout << token.as_string() << " " << message; - return runtime_error(message); // TODO no exceptions + return Error(message); // TODO no exceptions } -Expression *Parser::primary() { +Result Parser::primary() { if (match(1, Token::Type::FALSE)) return new Literal(false); if (match(1, Token::Type::TRUE)) return new Literal(true); if (match(1, Token::Type::NIL)) - return new Literal(new Void()); + return new Literal(new NilType()); if (match(1, Token::Type::NUMBER)) { return new Literal(stod(previous()->literal)); } @@ -127,62 +128,90 @@ Expression *Parser::primary() { return new Literal(previous()->literal); } if (match(1, Token::Type::LEFT_PAREN)) { - Expression *expr = expression(); - consume(Token::Type::RIGHT_PAREN, "Expect ')'."); - return new Grouping(expr); + auto expr = expression(); + Result r = consume(Token::Type::RIGHT_PAREN, "Expect ')'."); + if (is_err(r)) { + return Err(r); + } + return new Grouping(Ok(expr)); } - throw runtime_error("Expected an expression"); + return Error("Expected an expression"); } -Expression *Parser::unary() { +Result Parser::unary() { if (match(2, Token::BANG, Token::Type::MINUS)) { Token *op = previous(); - Expression *right = unary(); - return new Unary(op, right); + Result right = unary(); + if (is_ok(right)) { + return new Unary(op, Ok(right)); + } } return primary(); } -Expression *Parser::expression() { return equality(); } +Result Parser::expression() { return equality(); } -Expression *Parser::factor() { - Expression *expr = unary(); +Result Parser::factor() { + Result expr = unary(); + if (is_err(expr)) { + return expr; + } while (match(2, Token::Type::SLASH, Token::Type::STAR)) { Token *op = previous(); - Expression *right = unary(); - expr = new Binary(expr, op, right); + auto right = unary(); + if (is_err(right)) { + return right; + } + expr = new Binary(Ok(expr), op, Ok(right)); } return expr; } -Expression *Parser::term() { - Expression *expr = factor(); +Result Parser::term() { + auto expr = factor(); + if (is_err(expr)) { + return expr; + } while (match(2, Token::Type::MINUS, Token::Type::PLUS)) { Token *op = previous(); - Expression *right = factor(); - expr = new Binary(expr, op, right); + auto right = factor(); + if (is_err(right)) { + return right; + } + expr = new Binary(Ok(expr), op, Ok(right)); } return expr; } -Expression *Parser::equality(void) { - Expression *expr = comparison(); - +Result Parser::equality(void) { + auto expr = comparison(); + if (is_err(expr)) { + return expr; + } while (match(2, Token::Type::BANG_EQUAL, Token::Type::BANG_EQUAL)) { Token *op = previous(); - Expression *right = comparison(); - return new Binary(expr, op, right); + auto right = comparison(); + if (is_err(right)) { + return right; + } + return new Binary(Ok(expr), op, Ok(right)); } return expr; } -Expression *Parser::comparison(void) { - Expression *expr = term(); +Result Parser::comparison(void) { + auto expr = term(); + if (is_err(expr)) { + return expr; + } while (match(4, Token::Type::GREATER, Token::Type::GREATER_EQUAL, Token::Type::LESS, Token::Type::LESS_EQUAL)) { Token *op = previous(); - Expression *right = term(); - expr = new Binary(expr, op, right); + auto right = term(); + if (is_err(right)) { + return right; + } + expr = new Binary(Ok(expr), op, Ok(right)); } return expr; } diff --git a/src/parser.hpp b/src/parser.hpp index 2bd4fd2..362fc68 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -1,5 +1,6 @@ #pragma once +#include "error.hpp" #include "tokens.hpp" #include #include @@ -53,7 +54,7 @@ public: }; /// empty class that is the type of the Nil value -class Void {}; +class NilType {}; /// encapsulates a value: numeric, string etc class Literal : public Expression { @@ -66,16 +67,16 @@ public: double_t numeric; bool boolean; string str; - Void dummy; + NilType dummy; Value(double_t _numeric) : numeric(_numeric) {} Value(bool _boolean) : boolean(_boolean) {} Value(string _str) : str(_str) {} - Value(Void v) : dummy(v) {} + Value(NilType v) : dummy(v) {} ~Value() {} } value; - Literal(Void v) : valuetype(ValueType::Nil), value(v){}; + Literal(NilType v) : valuetype(ValueType::Nil), value(v){}; Literal(double_t _numeric) : valuetype(ValueType::Numeric), value(_numeric){}; Literal(string _str) : valuetype(ValueType::String), value(_str){}; Literal(bool _boolean) : valuetype(ValueType::Boolean), value(_boolean){}; @@ -106,26 +107,26 @@ class Parser { /// checks if the current token is of the specified type and /// moves the token forward if so, otherwise throws an exception with /// the specified message - Token *consume(Token::Type typ, string message); + Result consume(Token::Type typ, string message); /// throws an exception for the specified token with the specified message - runtime_error error(Token token, string message); + Error error(Token token, string message); /// tries to parse the token as a primary value (string, number etc) - Expression *primary(); + Result primary(); /// tries to parse the tokens as a unary expression - Expression *unary(); + Result unary(); /// tries to parse the tokens - Expression *expression(); + Result expression(); /// tries to parse the tokens as a multiplication or division - Expression *factor(); + Result factor(); /// tries to parse the tokens as an addition or subtraction - Expression *term(); + Result term(); /// tries to parse the tokens as an equality (`a == b` / `a!= b`) - Expression *equality(void); + Result equality(); /// tries to parse the tokens as a comparison (`a > b` / `a >= b` / `a < b` / /// `a <= b` ) - Expression *comparison(void); + Result comparison(); public: /// public method for parsing expressions - Expression *parse(vector tokenlist); + Result parse(vector tokenlist); }; diff --git a/src/scanner.cpp b/src/scanner.cpp index 005d5f4..75beaeb 100644 --- a/src/scanner.cpp +++ b/src/scanner.cpp @@ -1,4 +1,5 @@ #include "scanner.hpp" +#include "error.hpp" #include "tokens.hpp" #include #include @@ -23,17 +24,15 @@ Scanner::Scanner(string _source) : had_error(false), current_pos(0), start(0), current_line(1), source(_source), token_list(vector()) {} -ScanResult Scanner::scan_tokens() { +Result> Scanner::scan_tokens() { while (current_pos < source.length()) { start = current_pos; - scan_token(); + Result r = scan_token(); + if (is_err(r)) { + return Err(r); + } } - - ScanResult scan_result; - scan_result.token_list = token_list; - scan_result.had_error = had_error; - - return scan_result; + return token_list; } void Scanner::add_token(Token::Type type) { @@ -51,7 +50,7 @@ char Scanner::advance() { return c; } -void Scanner::scan_token() { +Result Scanner::scan_token() { char c = advance(); switch (c) { @@ -119,10 +118,11 @@ void Scanner::scan_token() { } else if (is_alpha(c)) { identifier(); } else { - error("Unexpected character."); + return Error{"Unexpected character."}; } break; } + return Void{}; } void Scanner::identifier() { @@ -159,7 +159,7 @@ void Scanner::scan_string() { } if (is_at_end()) { - error("Unterminated string."); + report("Unterminated string."); return; } @@ -202,7 +202,7 @@ bool Scanner::is_alphanumeric(char c) { return is_alpha(c) || is_digit(c); } bool Scanner::is_at_end(void) { return current_pos >= source.length(); } -void Scanner::error(string message) { report("", message); } +void Scanner::report(string message) { report("", message); } void Scanner::report(string where, std::string message) { cout << "*[Line " << current_line << "] Error " << where << " : " << message diff --git a/src/scanner.hpp b/src/scanner.hpp index 0d44171..296548b 100644 --- a/src/scanner.hpp +++ b/src/scanner.hpp @@ -1,5 +1,6 @@ #pragma once +#include "error.hpp" #include "tokens.hpp" #include #include @@ -23,11 +24,11 @@ private: public: Scanner(string s); - ScanResult scan_tokens(); + Result> scan_tokens(); + Result scan_token(); void add_token(Token::Type type); void add_token(Token::Type type, string literal); char advance(); - void scan_token(); void identifier(); void number(); bool is_digit(char c); @@ -38,6 +39,6 @@ public: bool is_alpha(char c); bool is_alphanumeric(char c); bool is_at_end(void); - void error(string message); + void report(string message); void report(string where, string message); }; diff --git a/src/tokens.cpp b/src/tokens.cpp index 2d1df34..9756f79 100644 --- a/src/tokens.cpp +++ b/src/tokens.cpp @@ -7,14 +7,12 @@ Token::Token(Token::Type _tokentype, string _lexeme, string _literal, int _line) string token_name(Token::Type tokentype) { static const std::string tokens[] = { - "END_OF_FILE", "LEFT_PAREN", "RIGHT_PAREN", "LEFT_BRACE", "RIGHT_BRACE", - "COMMA", "DOT", "MINUS", "PLUS", "SEMICOLON", - "SLASH", "STAR", "BANG", "BANG_EQUAL", "EQUAL", - "EQUAL_EQUAL", "GREATER", "GREATER_EQUAL", "LESS", "LESS_EQUAL", - "IDENTIFIER", "STRING", "NUMBER", "AND", "CLASS", - "ELSE", "FALSE", "FUN", "FOR", "IF", - "NIL", "OR", "PRINT", "RETURN", "SUPER", - "THIS", "TRUE", "VAR", "WHILE"}; + "EOF", "(", ")", "{", "}", ",", "*", + "-", "+", ";", "/", "*", "!", "!=", + "=", "==", ">", ">=", "<", "<=", "IDENTIFIER", + "string", "number", "and", "class", "else", "false", "fun", + "for", "if", "Nil", "or", "print", "return", "super", + "this", "true", "var", "while"}; return tokens[(int)tokentype]; }