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": [
"/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"
}
]

View file

@ -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)

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);
int run_file(string file);
void run_prompt(void);
ScanResult run(string source);
Result<vector<Token>> 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<vector<Token>> 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<vector<Token>>(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<vector<Token>> run(string source) {
Scanner *scanner = new Scanner(source);
return scanner->scan_tokens();
}

View file

@ -1,4 +1,5 @@
#include "parser.hpp"
#include <iostream>
#include <memory>
@ -61,7 +62,7 @@ string Literal::as_string() {
}
// class Parser
Expression *Parser::parse(vector<Token> tokenlist) {
Result<Expression *> Parser::parse(vector<Token> 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<Token *> 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<Expression *> 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<Token *> 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<Expression *> Parser::unary() {
if (match(2, Token::BANG, Token::Type::MINUS)) {
Token *op = previous();
Expression *right = unary();
return new Unary(op, right);
Result<Expression *> right = unary();
if (is_ok(right)) {
return new Unary(op, Ok(right));
}
}
return primary();
}
Expression *Parser::expression() { return equality(); }
Result<Expression *> Parser::expression() { return equality(); }
Expression *Parser::factor() {
Expression *expr = unary();
Result<Expression *> Parser::factor() {
Result<Expression *> 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<Expression *> 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<Expression *> 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<Expression *> 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;
}

View file

@ -1,5 +1,6 @@
#pragma once
#include "error.hpp"
#include "tokens.hpp"
#include <memory>
#include <vector>
@ -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<Token *> 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<Expression *> primary();
/// tries to parse the tokens as a unary expression
Expression *unary();
Result<Expression *> unary();
/// tries to parse the tokens
Expression *expression();
Result<Expression *> expression();
/// 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
Expression *term();
Result<Expression *> term();
/// 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` /
/// `a <= b` )
Expression *comparison(void);
Result<Expression *> comparison();
public:
/// 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 "error.hpp"
#include "tokens.hpp"
#include <cstdbool>
#include <iostream>
@ -23,17 +24,15 @@ Scanner::Scanner(string _source)
: had_error(false), current_pos(0), start(0), current_line(1),
source(_source), token_list(vector<Token>()) {}
ScanResult Scanner::scan_tokens() {
Result<vector<Token>> Scanner::scan_tokens() {
while (current_pos < source.length()) {
start = current_pos;
scan_token();
Result<Void> 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<Void> 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

View file

@ -1,5 +1,6 @@
#pragma once
#include "error.hpp"
#include "tokens.hpp"
#include <cstdbool>
#include <string>
@ -23,11 +24,11 @@ private:
public:
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, 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);
};

View file

@ -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];
}