C++23, rusty error handling, renamed output repr of tokens

This commit is contained in:
Shautvast 2024-10-25 16:43:57 +02:00
parent 32e76fb704
commit 6fd95bd486
9 changed files with 144 additions and 130 deletions

View file

@ -3,7 +3,7 @@
"arguments": [ "arguments": [
"/usr/bin/clang++", "/usr/bin/clang++",
"-c", "-c",
"-std=c++17", "-std=c++23",
"-Wall", "-Wall",
"-Wextra", "-Wextra",
"-pedantic", "-pedantic",
@ -15,56 +15,5 @@
"directory": "/Users/Shautvast/dev/clox", "directory": "/Users/Shautvast/dev/clox",
"file": "/Users/Shautvast/dev/clox/src/lox.cpp", "file": "/Users/Shautvast/dev/clox/src/lox.cpp",
"output": "/Users/Shautvast/dev/clox/target/lox.cpp.o" "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"
} }
] ]

View file

@ -1,6 +1,6 @@
TARGET := ./target TARGET := ./target
SRC := ./src 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') SRCS := $(shell find $(SRC) -name '*.c')
OBJS := $(SRCS:%=$(TARGET)/%.o) OBJS := $(SRCS:%=$(TARGET)/%.o)

31
src/error.hpp Normal file
View file

@ -0,0 +1,31 @@
#pragma once
#include <string>
#include <variant>
using namespace std;
class Error {
public:
string message;
Error(string _message) : message(_message){};
};
class Void {};
template <typename R> using Result = std::variant<R, Error>;
template <typename R> bool is_err(Result<R> r) {
return std::holds_alternative<Error>(r);
}
template <typename R> bool is_ok(Result<R> r) {
return std::holds_alternative<R>(r);
}
template <typename R> R Ok(Result<R> r) { return std::get<R>(r); }
/// enables rewrapping errors in a new Result type
template <typename R> Error Err(Result<R> r) { return std::get<Error>(r); }
template <typename R> string err_msg(Result<R> r) {
return std::get<Error>(r).message;
}

View file

@ -12,7 +12,7 @@ using namespace std;
void print_tokens(vector<Token> *list); void print_tokens(vector<Token> *list);
int run_file(string file); int run_file(string file);
void run_prompt(void); void run_prompt(void);
ScanResult run(string source); Result<vector<Token>> run(string source);
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
if (argc > 2) { if (argc > 2) {
@ -36,7 +36,7 @@ int run_file(string filename) {
exit(1); exit(1);
} }
ScanResult scan_result = run(content); Result<vector<Token>> scan_result = run(content);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
@ -49,17 +49,22 @@ void run_prompt(void) {
getline(cin, line); 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); // print_tokens(&scan_result.token_list);
if (!scan_result.had_error) { if (is_ok(scan_result)) {
Expression *e = (new Parser())->parse(scan_result.token_list); auto expression = (new Parser())->parse(get<vector<Token>>(scan_result));
cout << e->as_string(); if (is_ok(expression)) {
cout << "\n"; cout << Ok(expression)->as_string() << "\n";
} else {
cout << err_msg(expression) << "\n";
}
} else {
cout << err_msg(scan_result) << "\n";
} }
} }
} }
ScanResult run(string source) { Result<vector<Token>> run(string source) {
Scanner *scanner = new Scanner(source); Scanner *scanner = new Scanner(source);
return scanner->scan_tokens(); return scanner->scan_tokens();
} }

View file

@ -1,4 +1,5 @@
#include "parser.hpp" #include "parser.hpp"
#include <iostream> #include <iostream>
#include <memory> #include <memory>
@ -61,7 +62,7 @@ string Literal::as_string() {
} }
// class Parser // class Parser
Expression *Parser::parse(vector<Token> tokenlist) { Result<Expression *> Parser::parse(vector<Token> tokenlist) {
tokens = tokenlist; tokens = tokenlist;
current_token = 0; current_token = 0;
return expression(); return expression();
@ -101,25 +102,25 @@ bool Parser::match(int count, ...) {
return false; return false;
}; };
Token *Parser::consume(Token::Type typ, string message) { Result<Token *> Parser::consume(Token::Type typ, string message) {
if (check(typ)) { if (check(typ)) {
return advance(); 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; std::cout << token.as_string() << " " << message;
return runtime_error(message); // TODO no exceptions return Error(message); // TODO no exceptions
} }
Expression *Parser::primary() { Result<Expression *> Parser::primary() {
if (match(1, Token::Type::FALSE)) if (match(1, Token::Type::FALSE))
return new Literal(false); return new Literal(false);
if (match(1, Token::Type::TRUE)) if (match(1, Token::Type::TRUE))
return new Literal(true); return new Literal(true);
if (match(1, Token::Type::NIL)) if (match(1, Token::Type::NIL))
return new Literal(new Void()); return new Literal(new NilType());
if (match(1, Token::Type::NUMBER)) { if (match(1, Token::Type::NUMBER)) {
return new Literal(stod(previous()->literal)); return new Literal(stod(previous()->literal));
} }
@ -127,62 +128,90 @@ Expression *Parser::primary() {
return new Literal(previous()->literal); return new Literal(previous()->literal);
} }
if (match(1, Token::Type::LEFT_PAREN)) { if (match(1, Token::Type::LEFT_PAREN)) {
Expression *expr = expression(); auto expr = expression();
consume(Token::Type::RIGHT_PAREN, "Expect ')'."); Result<Token *> r = consume(Token::Type::RIGHT_PAREN, "Expect ')'.");
return new Grouping(expr); 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<Expression *> Parser::unary() {
if (match(2, Token::BANG, Token::Type::MINUS)) { if (match(2, Token::BANG, Token::Type::MINUS)) {
Token *op = previous(); Token *op = previous();
Expression *right = unary(); Result<Expression *> right = unary();
return new Unary(op, right); if (is_ok(right)) {
return new Unary(op, Ok(right));
}
} }
return primary(); return primary();
} }
Expression *Parser::expression() { return equality(); } Result<Expression *> Parser::expression() { return equality(); }
Expression *Parser::factor() { Result<Expression *> Parser::factor() {
Expression *expr = unary(); Result<Expression *> expr = unary();
if (is_err(expr)) {
return expr;
}
while (match(2, Token::Type::SLASH, Token::Type::STAR)) { while (match(2, Token::Type::SLASH, Token::Type::STAR)) {
Token *op = previous(); Token *op = previous();
Expression *right = unary(); auto right = unary();
expr = new Binary(expr, op, right); if (is_err(right)) {
return right;
}
expr = new Binary(Ok(expr), op, Ok(right));
} }
return expr; return expr;
} }
Expression *Parser::term() { Result<Expression *> Parser::term() {
Expression *expr = factor(); auto expr = factor();
if (is_err(expr)) {
return expr;
}
while (match(2, Token::Type::MINUS, Token::Type::PLUS)) { while (match(2, Token::Type::MINUS, Token::Type::PLUS)) {
Token *op = previous(); Token *op = previous();
Expression *right = factor(); auto right = factor();
expr = new Binary(expr, op, right); if (is_err(right)) {
return right;
}
expr = new Binary(Ok(expr), op, Ok(right));
} }
return expr; return expr;
} }
Expression *Parser::equality(void) { Result<Expression *> Parser::equality(void) {
Expression *expr = comparison(); auto expr = comparison();
if (is_err(expr)) {
return expr;
}
while (match(2, Token::Type::BANG_EQUAL, Token::Type::BANG_EQUAL)) { while (match(2, Token::Type::BANG_EQUAL, Token::Type::BANG_EQUAL)) {
Token *op = previous(); Token *op = previous();
Expression *right = comparison(); auto right = comparison();
return new Binary(expr, op, right); if (is_err(right)) {
return right;
}
return new Binary(Ok(expr), op, Ok(right));
} }
return expr; return expr;
} }
Expression *Parser::comparison(void) { Result<Expression *> Parser::comparison(void) {
Expression *expr = term(); auto expr = term();
if (is_err(expr)) {
return expr;
}
while (match(4, Token::Type::GREATER, Token::Type::GREATER_EQUAL, while (match(4, Token::Type::GREATER, Token::Type::GREATER_EQUAL,
Token::Type::LESS, Token::Type::LESS_EQUAL)) { Token::Type::LESS, Token::Type::LESS_EQUAL)) {
Token *op = previous(); Token *op = previous();
Expression *right = term(); auto right = term();
expr = new Binary(expr, op, right); if (is_err(right)) {
return right;
}
expr = new Binary(Ok(expr), op, Ok(right));
} }
return expr; return expr;
} }

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "error.hpp"
#include "tokens.hpp" #include "tokens.hpp"
#include <memory> #include <memory>
#include <vector> #include <vector>
@ -53,7 +54,7 @@ public:
}; };
/// empty class that is the type of the Nil value /// empty class that is the type of the Nil value
class Void {}; class NilType {};
/// encapsulates a value: numeric, string etc /// encapsulates a value: numeric, string etc
class Literal : public Expression { class Literal : public Expression {
@ -66,16 +67,16 @@ public:
double_t numeric; double_t numeric;
bool boolean; bool boolean;
string str; string str;
Void dummy; NilType dummy;
Value(double_t _numeric) : numeric(_numeric) {} Value(double_t _numeric) : numeric(_numeric) {}
Value(bool _boolean) : boolean(_boolean) {} Value(bool _boolean) : boolean(_boolean) {}
Value(string _str) : str(_str) {} Value(string _str) : str(_str) {}
Value(Void v) : dummy(v) {} Value(NilType v) : dummy(v) {}
~Value() {} ~Value() {}
} 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(double_t _numeric) : valuetype(ValueType::Numeric), value(_numeric){};
Literal(string _str) : valuetype(ValueType::String), value(_str){}; Literal(string _str) : valuetype(ValueType::String), value(_str){};
Literal(bool _boolean) : valuetype(ValueType::Boolean), value(_boolean){}; 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 /// checks if the current token is of the specified type and
/// moves the token forward if so, otherwise throws an exception with /// moves the token forward if so, otherwise throws an exception with
/// the specified message /// the specified message
Token *consume(Token::Type typ, string message); Result<Token *> consume(Token::Type typ, string message);
/// throws an exception for the specified token with the specified 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) /// tries to parse the token as a primary value (string, number etc)
Expression *primary(); Result<Expression *> primary();
/// tries to parse the tokens as a unary expression /// tries to parse the tokens as a unary expression
Expression *unary(); Result<Expression *> unary();
/// tries to parse the tokens /// tries to parse the tokens
Expression *expression(); Result<Expression *> expression();
/// tries to parse the tokens as a multiplication or division /// tries to parse the tokens as a multiplication or division
Expression *factor(); Result<Expression *> factor();
/// tries to parse the tokens as an addition or subtraction /// tries to parse the tokens as an addition or subtraction
Expression *term(); Result<Expression *> term();
/// tries to parse the tokens as an equality (`a == b` / `a!= b`) /// tries to parse the tokens as an equality (`a == b` / `a!= b`)
Expression *equality(void); Result<Expression *> equality();
/// tries to parse the tokens as a comparison (`a > b` / `a >= b` / `a < b` / /// tries to parse the tokens as a comparison (`a > b` / `a >= b` / `a < b` /
/// `a <= b` ) /// `a <= b` )
Expression *comparison(void); Result<Expression *> comparison();
public: public:
/// public method for parsing expressions /// public method for parsing expressions
Expression *parse(vector<Token> tokenlist); Result<Expression *> parse(vector<Token> tokenlist);
}; };

View file

@ -1,4 +1,5 @@
#include "scanner.hpp" #include "scanner.hpp"
#include "error.hpp"
#include "tokens.hpp" #include "tokens.hpp"
#include <cstdbool> #include <cstdbool>
#include <iostream> #include <iostream>
@ -23,17 +24,15 @@ Scanner::Scanner(string _source)
: had_error(false), current_pos(0), start(0), current_line(1), : had_error(false), current_pos(0), start(0), current_line(1),
source(_source), token_list(vector<Token>()) {} source(_source), token_list(vector<Token>()) {}
ScanResult Scanner::scan_tokens() { Result<vector<Token>> Scanner::scan_tokens() {
while (current_pos < source.length()) { while (current_pos < source.length()) {
start = current_pos; start = current_pos;
scan_token(); Result<Void> r = scan_token();
if (is_err(r)) {
return Err(r);
}
} }
return token_list;
ScanResult scan_result;
scan_result.token_list = token_list;
scan_result.had_error = had_error;
return scan_result;
} }
void Scanner::add_token(Token::Type type) { void Scanner::add_token(Token::Type type) {
@ -51,7 +50,7 @@ char Scanner::advance() {
return c; return c;
} }
void Scanner::scan_token() { Result<Void> Scanner::scan_token() {
char c = advance(); char c = advance();
switch (c) { switch (c) {
@ -119,10 +118,11 @@ void Scanner::scan_token() {
} else if (is_alpha(c)) { } else if (is_alpha(c)) {
identifier(); identifier();
} else { } else {
error("Unexpected character."); return Error{"Unexpected character."};
} }
break; break;
} }
return Void{};
} }
void Scanner::identifier() { void Scanner::identifier() {
@ -159,7 +159,7 @@ void Scanner::scan_string() {
} }
if (is_at_end()) { if (is_at_end()) {
error("Unterminated string."); report("Unterminated string.");
return; 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(); } 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) { void Scanner::report(string where, std::string message) {
cout << "*[Line " << current_line << "] Error " << where << " : " << message cout << "*[Line " << current_line << "] Error " << where << " : " << message

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "error.hpp"
#include "tokens.hpp" #include "tokens.hpp"
#include <cstdbool> #include <cstdbool>
#include <string> #include <string>
@ -23,11 +24,11 @@ private:
public: public:
Scanner(string s); Scanner(string s);
ScanResult scan_tokens(); Result<vector<Token>> scan_tokens();
Result<Void> scan_token();
void add_token(Token::Type type); void add_token(Token::Type type);
void add_token(Token::Type type, string literal); void add_token(Token::Type type, string literal);
char advance(); char advance();
void scan_token();
void identifier(); void identifier();
void number(); void number();
bool is_digit(char c); bool is_digit(char c);
@ -38,6 +39,6 @@ public:
bool is_alpha(char c); bool is_alpha(char c);
bool is_alphanumeric(char c); bool is_alphanumeric(char c);
bool is_at_end(void); bool is_at_end(void);
void error(string message); void report(string message);
void report(string where, string message); void report(string where, string message);
}; };

View file

@ -7,14 +7,12 @@ Token::Token(Token::Type _tokentype, string _lexeme, string _literal, int _line)
string token_name(Token::Type tokentype) { string token_name(Token::Type tokentype) {
static const std::string tokens[] = { static const std::string tokens[] = {
"END_OF_FILE", "LEFT_PAREN", "RIGHT_PAREN", "LEFT_BRACE", "RIGHT_BRACE", "EOF", "(", ")", "{", "}", ",", "*",
"COMMA", "DOT", "MINUS", "PLUS", "SEMICOLON", "-", "+", ";", "/", "*", "!", "!=",
"SLASH", "STAR", "BANG", "BANG_EQUAL", "EQUAL", "=", "==", ">", ">=", "<", "<=", "IDENTIFIER",
"EQUAL_EQUAL", "GREATER", "GREATER_EQUAL", "LESS", "LESS_EQUAL", "string", "number", "and", "class", "else", "false", "fun",
"IDENTIFIER", "STRING", "NUMBER", "AND", "CLASS", "for", "if", "Nil", "or", "print", "return", "super",
"ELSE", "FALSE", "FUN", "FOR", "IF", "this", "true", "var", "while"};
"NIL", "OR", "PRINT", "RETURN", "SUPER",
"THIS", "TRUE", "VAR", "WHILE"};
return tokens[(int)tokentype]; return tokens[(int)tokentype];
} }