diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 13566b8..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Editor-based HTTP Client requests
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/.idea/clox.iml b/.idea/clox.iml
deleted file mode 100644
index d6ebd48..0000000
--- a/.idea/clox.iml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index a16d8e7..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index a28bd99..0000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 35eb1dd..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/compile_commands.json b/compile_commands.json
deleted file mode 100644
index 0dbb570..0000000
--- a/compile_commands.json
+++ /dev/null
@@ -1,19 +0,0 @@
-[
- {
- "arguments": [
- "/usr/bin/clang++",
- "-c",
- "-std=c++23",
- "-Wall",
- "-Wextra",
- "-pedantic",
- "-Werror",
- "-o",
- "target/lox.cpp.o",
- "src/lox.cpp"
- ],
- "directory": "/Users/Shautvast/dev/clox",
- "file": "/Users/Shautvast/dev/clox/src/lox.cpp",
- "output": "/Users/Shautvast/dev/clox/target/lox.cpp.o"
- }
-]
diff --git a/makefile b/makefile
index bbe2515..6ccd569 100644
--- a/makefile
+++ b/makefile
@@ -1,23 +1,27 @@
TARGET := ./target
SRC := ./src
-CC := clang++ -c -std=c++23 -Wall -Wextra -pedantic -Werror
+CC := clang -c -std=c17
+#-Wall -Wextra -pedantic -Werror
SRCS := $(shell find $(SRC) -name '*.c')
OBJS := $(SRCS:%=$(TARGET)/%.o)
-$(TARGET)/lox: $(TARGET)/lox.cpp.o $(TARGET)/parser.cpp.o $(TARGET)/scanner.cpp.o $(TARGET)/tokens.cpp.o $(TARGET)/parser.cpp.o
- clang++ $(TARGET)/lox.cpp.o -L$(TARGET) -lscanner.cpp.o -ltokens.cpp.o -lparser.cpp.o -o $(TARGET)/lox
+$(TARGET)/lox: $(TARGET)/lox.c.o $(TARGET)/parser.c.o $(TARGET)/scanner.c.o $(TARGET)/tokens.c.o $(TARGET)/utils.c.o
+ clang $(TARGET)/lox.c.o -L$(TARGET) -lparser.c.o -ltokens.c.o -lscanner.c.o -lutils.c.o -o $(TARGET)/lox
-$(TARGET)/tokens.cpp.o: $(SRC)/tokens.cpp
+$(TARGET)/utils.c.o: $(SRC)/utils.c
$(CC) $< -o $@
-$(TARGET)/scanner.cpp.o: $(SRC)/scanner.cpp
+$(TARGET)/tokens.c.o: $(SRC)/tokens.c
$(CC) $< -o $@
-$(TARGET)/parser.cpp.o: $(SRC)/parser.cpp
+$(TARGET)/scanner.c.o: $(SRC)/scanner.c
$(CC) $< -o $@
-$(TARGET)/lox.cpp.o: $(SRC)/lox.cpp $(TARGET)
+$(TARGET)/parser.c.o: $(SRC)/parser.c
+ $(CC) $< -o $@
+
+$(TARGET)/lox.c.o: $(SRC)/lox.c $(TARGET)
$(CC) $< -o $@
$(TARGET):
diff --git a/src/error.hpp b/src/error.hpp
deleted file mode 100644
index 8beae48..0000000
--- a/src/error.hpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#pragma once
-
-#include
-#include
-
-class Error {
-
-public:
- std::string message;
- Error(std::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 std::string err_msg(Result r) {
- return std::get(r).message;
-}
diff --git a/src/lox.c b/src/lox.c
new file mode 100644
index 0000000..88c5b6d
--- /dev/null
+++ b/src/lox.c
@@ -0,0 +1,85 @@
+#include "parser.h"
+#include "scanner.h"
+#include "utils.h"
+#include
+#include
+#include
+#include
+
+int run_file(char *file);
+void run_prompt(void);
+void run(char *source);
+
+int main(int argc, char *argv[]) {
+ setvbuf(stdout, NULL, _IONBF, 0);
+ if (argc > 2) {
+ puts("Usage: lox [script]");
+ return EXIT_FAILURE;
+ } else if (argc == 2) {
+ return run_file(argv[1]);
+ } else {
+ run_prompt();
+ }
+ return EXIT_SUCCESS;
+}
+
+int run_file(char *filename) {
+
+ FILE *file = fopen(filename, "r");
+ if (file == NULL) {
+ printf("unable to open file '%s'\n", filename);
+ return EXIT_FAILURE;
+ }
+ char line[255];
+
+ char *content = malloc(1);
+ if (content == NULL) {
+ puts("Out of memory");
+ return EXIT_FAILURE;
+ }
+ content[0] = '\0';
+
+ while (fgets(line, sizeof(line), file)) {
+ content = realloc(content, strlen(content) + strlen(line) + 1);
+ if (content == NULL) {
+ return EXIT_FAILURE;
+ }
+ strcat(content, line);
+ }
+
+ fclose(file);
+
+ run(content);
+
+ // FREE UP
+ free(content);
+
+ // if (scan_result.had_error) {
+ // return 65;
+ // }
+
+ return EXIT_SUCCESS;
+}
+
+void run_prompt(void) {
+ char line[255];
+
+ for (;;) {
+ printf(">");
+ char *r = fgets(line, 255, stdin);
+
+ if (r == NULL) {
+ break;
+ }
+
+ int len = (int)strlen(line);
+ run(substring(line, 1, len - 1));
+ }
+}
+
+void run(char *source) {
+ ScanResult scan_result = scan_tokens(source);
+ // tokenlist_print(&scan_result.token_list);
+ ExpressionList list = parse(&scan_result.token_list);
+ exprlist_print(&list);
+}
diff --git a/src/lox.cpp b/src/lox.cpp
deleted file mode 100644
index 0e8212c..0000000
--- a/src/lox.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-#include "parser.hpp"
-#include "scanner.hpp"
-#include "tokens.hpp"
-#include
-#include
-#include
-#include
-#include
-
-using std::string, std::vector, std::ifstream, std::cin, std::cout;
-
-void print_tokens(vector *list);
-int run_file(string file);
-void run_prompt(void);
-Result> run(string source);
-
-int main(int argc, char *argv[]) {
- if (argc > 2) {
- puts("Usage: lox [script]");
- return EXIT_FAILURE;
- } else if (argc == 2) {
- return run_file(argv[1]);
- } else {
- run_prompt();
- }
- return EXIT_SUCCESS;
-}
-
-int run_file(string filename) {
- string content;
- ifstream file;
- file.open(filename);
- if (file.is_open()) {
- file >> content;
- } else {
- exit(1);
- }
-
- Result> scan_result = run(content);
-
- return EXIT_SUCCESS;
-}
-
-void run_prompt(void) {
- string line;
-
- for (;;) {
- cout << ">";
- getline(cin, line);
- auto scan_result = run(line.substr(0, line.length()));
-
- if (is_ok(scan_result)) {
- auto tokens = Ok(scan_result);
- print_tokens(&tokens);
- 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";
- }
- }
-}
-
-Result> run(string source) {
- Scanner *scanner = new Scanner(source);
- return scanner->scan_tokens();
-}
-
-void print_tokens(vector *const list) {
- for (Token token : *list) {
- cout << token.as_string() << "(" << token.literal << "), ";
- }
-
- cout << "\n";
-}
diff --git a/src/parser.c b/src/parser.c
new file mode 100644
index 0000000..1ed97e8
--- /dev/null
+++ b/src/parser.c
@@ -0,0 +1,383 @@
+#include "parser.h"
+#include "tokens.h"
+#include
+#include
+#include
+#include
+
+bool is_at_end(void);
+Token *peek(void);
+size_t expr_toString(const Expression *expr, char *output, size_t outputSize,
+ size_t offset);
+void expr_print(const Expression *expr);
+Expression *declaration(void);
+bool match1(TokenType t);
+bool match2(TokenType type1, TokenType type2);
+bool match3(TokenType type1, TokenType type2, TokenType type3);
+bool match4(TokenType type1, TokenType type2, TokenType type3, TokenType type4);
+bool check(TokenType t);
+Token *advance(void);
+Token *previous(void);
+Expression *var_declaration(void);
+Expression *expression(void);
+Expression *statement(void);
+Expression *printStatement(void);
+ExpressionList block(void);
+Expression *expressionStatement(void);
+Expression *assignment(void);
+Expression *equality(void);
+Expression *comparison(void);
+Expression *term(void);
+Expression *factor(void);
+Expression *unary(void);
+Expression *primary(void);
+Expression *newExpression(char *type);
+Token *consume(TokenType type, char *message);
+
+static TokenList *tokens;
+static int current;
+
+ExpressionList parse(TokenList *tokens_to_parse) {
+ ExpressionList statements;
+ exprlist_init(&statements);
+
+ tokens = tokens_to_parse;
+ current = 0;
+
+ while (!is_at_end()) {
+ exprlist_add(&statements, declaration());
+ }
+ return statements;
+}
+
+Expression *declaration(void) {
+ if (match1(VAR)) {
+ return var_declaration();
+ } else {
+ return statement();
+ }
+}
+
+Expression *var_declaration(void) {
+ Token *name = consume(IDENTIFIER, "Expected a variable name");
+ Expression *initializer;
+ if (match1(EQUAL)) {
+ initializer = expression();
+ }
+ consume(SEMICOLON, "Expected semicolon");
+ Expression *variableStatement = newExpression("VariableStatement");
+ variableStatement->name = name->lexeme;
+ variableStatement->left = initializer;
+ return variableStatement;
+}
+
+Expression *statement(void) {
+ if (match1(PRINT)) {
+ return printStatement();
+ }
+ if (match1(LEFT_BRACE)) {
+ ExpressionList block_contents = block();
+ Expression *block = newExpression("Block");
+ block->value = &block_contents;
+ return block;
+ }
+
+ return expressionStatement();
+}
+
+ExpressionList block(void) {
+ ExpressionList statements;
+ exprlist_init(&statements);
+ while (!check(RIGHT_BRACE) && !is_at_end()) {
+ exprlist_add(&statements, declaration());
+ }
+ return statements;
+}
+
+Expression *printStatement(void) {
+ Expression *value = expression();
+ consume(SEMICOLON, "Expected semicolon");
+ Expression *print = newExpression("PrintStmt");
+ print->left = value;
+ return print;
+}
+
+Expression *expressionStatement(void) {
+ Expression *value = expression();
+ consume(SEMICOLON, "Expected semicolon");
+ Expression *statement = newExpression("ExprStmt");
+ statement->value = &value;
+ return statement;
+}
+
+Expression *expression(void) { return assignment(); }
+
+Expression *assignment(void) {
+ Expression *expr = equality();
+ if (match1(EQUAL)) {
+ Token *equals = previous();
+ Expression *value = assignment();
+ if (strcmp(expr->type, "Variable") == 0) {
+ Expression *assign = newExpression("AssignStmt");
+ assign->name = expr->name;
+ assign->value = &value;
+ return assign;
+ }
+ Expression *error = newExpression("Error");
+ error->operator= equals;
+ return error;
+ }
+ return expr;
+}
+
+Expression *equality(void) {
+ Expression *expr = comparison();
+ while (match2(BANG_EQUAL, EQUAL_EQUAL)) {
+ Token *operator= previous();
+ Expression *right = comparison();
+ Expression *binary = newExpression("BinaryExpr");
+ binary->operator= operator;
+ binary->left = expr;
+ binary->right = right;
+ expr = binary;
+ }
+ return expr;
+}
+
+Expression *comparison(void) {
+ Expression *expr = term();
+ while (match4(GREATER, GREATER_EQUAL, LESS, LESS_EQUAL)) {
+ Token *operator= previous();
+ Expression *right = term();
+ Expression *binary = newExpression("BinaryExpr");
+ binary->operator= operator;
+ binary->left = expr;
+ binary->right = right;
+ expr = binary;
+ }
+ return expr;
+}
+
+Expression *term(void) {
+ Expression *expr = factor();
+ while (match2(MINUS, PLUS)) {
+ Token *operator= previous();
+ Expression *right = factor();
+ Expression *binary = newExpression("BinaryExpr");
+ binary->operator= operator;
+ binary->left = expr;
+ binary->right = right;
+ expr = binary;
+ }
+ return expr;
+}
+
+Expression *factor(void) {
+ Expression *expr = unary();
+ while (match2(SLASH, STAR)) {
+ Token *operator= previous();
+ Expression *right = unary();
+ Expression *binary = newExpression("BinaryExpr");
+ binary->operator= operator;
+ binary->left = expr;
+ binary->right = right;
+ expr = binary;
+ }
+ return expr;
+}
+
+Expression *unary(void) {
+ if (match2(BANG, MINUS)) {
+ Token *operator= previous();
+ Expression *right = unary();
+ Expression *unary = newExpression("Unary");
+ unary->operator= operator;
+ unary->right = right;
+ return unary;
+ }
+ return primary();
+}
+
+Expression *primary(void) {
+ Expression *r = newExpression("Literal");
+ if (match1(FALSE)) {
+ r->name = "boolean";
+ r->value = "false";
+ return r;
+ }
+ if (match1(TRUE)) {
+ r->name = "boolean";
+ r->value = "true";
+ return r;
+ }
+
+ if (match1(NIL)) {
+ r->name = "nil";
+ r->value = NULL;
+ return r;
+ }
+
+ if (check(NUMBER)) {
+ advance();
+ r->name = "number";
+ r->value = previous()->literal;
+ return r;
+ }
+ if (check(STRING)) {
+ advance();
+ r->name = "string";
+ r->value = previous()->literal;
+ return r;
+ }
+ if (match1(IDENTIFIER)) {
+ Expression *var = newExpression("Variable");
+ r->name = previous()->lexeme;
+ return var;
+ }
+ if (match1(LEFT_PAREN)) {
+ Expression *group = newExpression("Group");
+ consume(RIGHT_PAREN, "Expect ')' after expression.");
+ r->left = group;
+ return group;
+ }
+
+ Expression *error = newExpression("Error");
+ return error;
+}
+
+Token *consume(TokenType type, char *message) {
+ if (check(type)) {
+ return advance();
+ }
+ printf("error\n");
+ Token *t = newToken();
+ t->type = ERROR;
+ t->lexeme = message;
+
+ tokenlist_add(tokens, t);
+ return tokenlist_last(tokens);
+}
+
+bool match1(TokenType type) {
+ if (check(type)) {
+ advance();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool match2(TokenType type1, TokenType type2) {
+ if (check(type1) || check(type2)) {
+ advance();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool match3(TokenType type1, TokenType type2, TokenType type3) {
+ if (check(type1) || check(type2) || check(type3)) {
+ advance();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool match4(TokenType type1, TokenType type2, TokenType type3,
+ TokenType type4) {
+ if (check(type1) || check(type2) || check(type3) || check(type4)) {
+ advance();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+Token *advance(void) {
+ if (!is_at_end()) {
+ current += 1;
+ }
+ return previous();
+}
+
+Token *previous(void) { return tokenlist_get(tokens, current - 1); }
+
+bool check(TokenType type) { return peek()->type == type; }
+
+bool is_at_end(void) { return peek()->type == END_OF_FILE; }
+
+Token *peek(void) { return tokenlist_get(tokens, current); }
+
+void exprlist_init(ExpressionList *list) {
+ list->expressions = malloc(sizeof(Expression) * 32);
+ list->size = 0;
+ list->capacity = 32;
+}
+
+void exprlist_add(ExpressionList *list, Expression *value) {
+ if (list->size >= list->capacity) {
+ list->capacity *= 2;
+ list->expressions =
+ realloc(list->expressions, sizeof(Expression) * list->capacity);
+ }
+ list->expressions[list->size] = value;
+ list->size += 1;
+}
+
+Expression *exprlist_get(ExpressionList *list, int index) {
+ if (index >= list->size || index < 0) {
+ printf("Index %d out of bounds for list of size %d\n", index, list->size);
+ exit(1);
+ }
+ return list->expressions[index];
+}
+
+void exprlist_print(ExpressionList *list) {
+ for (int i = 0; i < list->size; i++) {
+ Expression *expr = exprlist_get(list, i);
+ expr_print(expr);
+ }
+ printf("\n");
+}
+
+void exprlist_free(ExpressionList *list) { free(list->expressions); }
+
+void expr_print(const Expression *expr) {
+ printf("Expr[type: %s", expr->type);
+ if (expr->left != NULL) {
+ printf(", left: ");
+ expr_print(expr->left);
+ }
+ if (expr->right != NULL) {
+ printf(", right: ");
+ expr_print(expr->right);
+ }
+ if (expr->operator!= NULL && expr->operator->lexeme != NULL) {
+ printf(", operator: %s", expr->operator->lexeme);
+ }
+ if (expr->name != NULL) {
+ printf(", name: %s", expr->name);
+ if (strcmp(expr->name, "string") == 0 ||
+ strcmp(expr->name, "number") == 0 ||
+ strcmp(expr->name, "boolean") == 0) {
+ printf(", value: %s", (char *)expr->value);
+ }
+ if (strcmp(expr->name, "nil") == 0 && expr->value == NULL) {
+ printf(", value: NULL");
+ }
+ }
+ printf("]");
+}
+
+Expression *newExpression(char *type) {
+ Expression *e = malloc(sizeof(Expression));
+ e->type = type;
+ e->left = NULL;
+ e->right = NULL;
+ e->name = NULL;
+ e->operator= NULL;
+ e->value = NULL;
+ return e;
+}
diff --git a/src/parser.cpp b/src/parser.cpp
deleted file mode 100644
index 21f1b11..0000000
--- a/src/parser.cpp
+++ /dev/null
@@ -1,212 +0,0 @@
-#include "parser.hpp"
-
-#include
-#include
-
-using std::string, std::to_string, std::vector;
-
-Expression::~Expression() = default;
-
-// class Binary
-
-string Binary::as_string() const {
- return "(" + token_name(op->tokentype) + " " + left->as_string() + " " +
- right->as_string() + ")";
-}
-
-Binary::Binary(Expression *_left, Token *_operator, Expression *_right)
- : left(_left), op(_operator), right(_right){};
-
-Binary::~Binary() = default;
-
-// class Grouping
-
-string Grouping::as_string() const { return "(" + expr->as_string() + ")"; }
-
-Grouping::Grouping(Expression *_expr) : expr(_expr){};
-
-Grouping::~Grouping() = default;
-
-// class Unary
-
-string Unary::as_string() const {
- return token_name(op->tokentype) + right->as_string();
-}
-
-Unary::Unary(Token *_operator, Expression *_right)
- : op(_operator), right(_right){};
-
-Unary::~Unary() = default;
-
-// class Literal
-string Literal::as_string() const {
- string text;
- if (holds_alternative(value)) {
- return "\"" + get(value) + "\"";
- }
- if (holds_alternative(value)) {
- return to_string(get(value));
- }
- if (holds_alternative(value)) {
- return get(value) ? "True" : "False";
- }
- if (holds_alternative(value)) {
- return "NULL";
- }
- return "unexpected";
-}
-
-// class Parser
-Result Parser::parse(vector tokenlist) {
- tokens = tokenlist;
- current_token = 0;
- return expression();
-}
-
-Token Parser::peek() { return tokens[current_token]; };
-
-bool Parser::is_at_end() { return peek().tokentype == Token::END_OF_FILE; };
-
-Token *Parser::previous() { return &tokens[current_token - 1]; };
-
-Token *Parser::advance() {
- if (!is_at_end())
- current_token += 1;
- return previous();
-}
-
-bool Parser::check(Token::Type type) {
- if (is_at_end()) {
- return false;
- }
- return peek().tokentype == type;
-}
-
-bool Parser::match(int count, ...) {
- va_list list;
- va_start(list, count);
-
- for (int i = 0; i < count; i++) {
- Token::Type ttc = va_arg(list, Token::Type);
- // cout << token_name(ttc) << "\n";
- if (check(ttc)) {
- advance();
- return true;
- }
- }
- return false;
-};
-
-Result Parser::consume(Token::Type typ, string message) {
- if (check(typ)) {
- return advance();
- }
- return error(peek(), message);
-}
-
-Error Parser::error(Token token, string message) {
- std::cout << token.as_string() << " " << message;
- return Error(message); // TODO no exceptions
-}
-
-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 NilType());
- if (match(1, Token::Type::NUMBER)) {
- return new Literal(stod(previous()->literal));
- }
- if (match(1, Token::Type::STRING)) {
- return new Literal(previous()->literal);
- }
- if (match(1, Token::Type::LEFT_PAREN)) {
- auto expr = expression();
- Result r = consume(Token::Type::RIGHT_PAREN, "Expect ')'.");
- if (is_err(r)) {
- return Err(r);
- }
- return new Grouping(Ok(expr));
- }
- return Error("Expected an expression");
-}
-
-Result Parser::unary() {
- if (match(2, Token::BANG, Token::Type::MINUS)) {
- Token *op = previous();
- Result right = unary();
- if (is_ok(right)) {
- return new Unary(op, Ok(right));
- }
- }
- return primary();
-}
-
-Result Parser::expression() { return equality(); }
-
-Result Parser::factor() {
- Result expr = unary();
- if (is_err(expr)) {
- return expr;
- }
- while (match(2, Token::Type::SLASH, Token::Type::STAR)) {
- Token *op = previous();
- auto right = unary();
- if (is_err(right)) {
- return right;
- }
- expr = new Binary(Ok(expr), op, Ok(right));
- }
- return expr;
-}
-
-Result Parser::term() {
- auto expr = factor();
- if (is_err(expr)) {
- return expr;
- }
- while (match(2, Token::Type::MINUS, Token::Type::PLUS)) {
- Token *op = previous();
- auto right = factor();
- if (is_err(right)) {
- return right;
- }
- expr = new Binary(Ok(expr), op, Ok(right));
- }
- return expr;
-}
-
-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();
- auto right = comparison();
- if (is_err(right)) {
- return right;
- }
- return new Binary(Ok(expr), op, Ok(right));
- }
- return expr;
-}
-
-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();
- auto right = term();
- if (is_err(right)) {
- return right;
- }
- expr = new Binary(Ok(expr), op, Ok(right));
- }
- return expr;
-}
diff --git a/src/parser.h b/src/parser.h
new file mode 100644
index 0000000..a6c98fe
--- /dev/null
+++ b/src/parser.h
@@ -0,0 +1,30 @@
+#include "tokens.h"
+
+typedef struct Expression Expression;
+
+struct Expression {
+ char *type;
+ Expression *left;
+ Expression *right;
+ Token *operator;
+ char *name;
+ void *value;
+};
+
+typedef struct ExpressionList {
+ Expression **expressions;
+ int size;
+ int capacity;
+} ExpressionList;
+
+ExpressionList parse(TokenList *tokens);
+
+void exprlist_init(ExpressionList *list);
+
+void exprlist_add(ExpressionList *list, Expression *value);
+
+Expression *exprlist_get(ExpressionList *list, int index);
+
+void exprlist_print(ExpressionList *list);
+
+void exprlist_free(ExpressionList *list);
diff --git a/src/parser.hpp b/src/parser.hpp
deleted file mode 100644
index a4804b6..0000000
--- a/src/parser.hpp
+++ /dev/null
@@ -1,115 +0,0 @@
-#pragma once
-
-#include "error.hpp"
-#include "tokens.hpp"
-#include
-#include
-#include
-
-enum class ExprType { Binary, Grouping, Unary, Literal, None };
-
-/// Base class for expressions
-class Expression {
-public:
- virtual std::string as_string() const = 0; // get string rep for debugging
- virtual ~Expression();
-};
-
-/// An expression with two operands
-class Binary : public Expression {
- std::unique_ptr left;
- std::unique_ptr op;
- std::unique_ptr right;
-
-public:
- std::string as_string() const override;
- Binary(Expression *_left, Token *_operator, Expression *_right);
- ~Binary();
-};
-
-/// An expression between parentheses
-class Grouping : public Expression {
- std::unique_ptr expr;
-
-public:
- std::string as_string() const override;
- Grouping(Expression *_expr);
- ~Grouping();
-};
-
-/// An expression with one operand (operator is `-` or `!`)
-class Unary : public Expression {
- std::unique_ptr op;
- std::unique_ptr right;
-
-public:
- std::string as_string() const override;
- Unary(Token *_operator, Expression *_right);
- ~Unary();
-};
-
-/// empty class that is the type of the Nil value
-class NilType {};
-typedef std::variant Value;
-
-/// encapsulates a value: numeric, string etc
-class Literal : public Expression {
-public:
- enum ValueType { String, Numeric, Boolean, Nil } valuetype;
-
- Value value;
-
- Literal(NilType v) : value(v){};
- Literal(double_t _numeric) : value(_numeric){};
- Literal(std::string _str) : value(_str){};
- Literal(bool _boolean) : value(_boolean){};
-
- std::string as_string() const override;
-};
-
-class Parser {
- std::vector expressions;
- std::vector tokens;
- int current_token;
-
- /// returns the current token without moving the pointer;
- /// pointer here meanse index into the tokenlist.
- Token peek();
- /// checks if the current token is EOF
- bool is_at_end();
- /// returns the previous token without moving the pointer
- Token *previous();
- /// increments the token pointer
- Token *advance();
- /// checks if the current token is of specified type
- bool check(Token::Type type);
- /// checks if the current token is one of the specified types
- /// count: the number of tokens to match
- /// ... varargs argument for the tokens to match
- bool match(int count, ...);
- /// 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
- Result consume(Token::Type typ, std::string message);
- /// throws an exception for the specified token with the specified message
- Error error(Token token, std::string message);
- /// tries to parse the token as a primary value (string, number etc)
- Result primary();
- /// tries to parse the tokens as a unary expression
- Result unary();
- /// tries to parse the tokens
- Result expression();
- /// tries to parse the tokens as a multiplication or division
- Result factor();
- /// tries to parse the tokens as an addition or subtraction
- Result term();
- /// tries to parse the tokens as an equality (`a == b` / `a!= b`)
- Result equality();
- /// tries to parse the tokens as a comparison (`a > b` / `a >= b` / `a < b`
- /// / `a <= b` )
- Result comparison();
-
-public:
- /// public method for parsing expressions
- Result parse(std::vector tokenlist);
-};
diff --git a/src/scanner.c b/src/scanner.c
new file mode 100644
index 0000000..70e8aef
--- /dev/null
+++ b/src/scanner.c
@@ -0,0 +1,240 @@
+#include "scanner.h"
+#include "tokens.h"
+#include "utils.h"
+#include
+#include
+#include
+#include
+
+static void scan_token(void);
+static void error(char *message, char c);
+static void report(char *where, char *message, char c);
+static bool is_at_end(void);
+static bool match(char expected);
+static char peek(void);
+static char peek_next(void);
+static void string(void);
+static bool is_digit(char c);
+static void number(void);
+static bool is_alpha(char c);
+static bool is_alphanumeric(char c);
+static void identifier(void);
+
+static bool had_error = false;
+static int current_pos = -1;
+static int start = -1;
+static int current_line = -1;
+static char *source;
+static TokenList token_list;
+
+ScanResult scan_tokens(char *src) {
+ current_pos = 0;
+ start = 0;
+ current_line = 1;
+ source = src;
+
+ tokenlist_init(&token_list);
+ int len = (int)strlen(source);
+
+ while (current_pos < len) {
+ start = current_pos;
+ scan_token();
+ }
+ Token *eof = newToken();
+ eof->type = END_OF_FILE;
+ eof->lexeme = "";
+ eof->literal = "";
+
+ tokenlist_add(&token_list, eof);
+
+ ScanResult scan_result;
+ scan_result.token_list = token_list;
+ scan_result.had_error = had_error;
+
+ // tokenlist_print(&scan_result.token_list);
+
+ return scan_result;
+}
+
+static void add_token(TokenType type) {
+ Token *token = newToken();
+ token->type = type;
+ token->lexeme = substring(source, start + 1, current_pos - start);
+ token->literal = NULL;
+ token->line = current_line;
+
+ tokenlist_add(&token_list, token);
+}
+
+static void add_token_with_literal(TokenType type, char *literal) {
+ Token *token = newToken();
+ token->type = type;
+ token->lexeme = substring(source, start + 1, current_pos - start);
+ token->literal = literal;
+ token->line = current_line;
+
+ tokenlist_add(&token_list, token);
+}
+
+static char advance(void) {
+ char c = source[current_pos++];
+ return c;
+}
+
+static void scan_token(void) {
+ char c = advance();
+
+ switch (c) {
+ case '(':
+ add_token(LEFT_PAREN);
+ break;
+ case ')':
+ add_token(RIGHT_PAREN);
+ break;
+ case '{':
+ add_token(LEFT_BRACE);
+ break;
+ case '}':
+ add_token(RIGHT_BRACE);
+ break;
+ case ',':
+ add_token(COMMA);
+ break;
+ case '.':
+ add_token(DOT);
+ break;
+ case '+':
+ add_token(PLUS);
+ break;
+ case '-':
+ add_token(MINUS);
+ break;
+ case '!':
+ add_token(match('=') ? BANG_EQUAL : BANG);
+ break;
+ case '=':
+ add_token(match('=') ? EQUAL_EQUAL : EQUAL);
+ break;
+ case '>':
+ add_token(match('=') ? GREATER_EQUAL : GREATER);
+ break;
+ case '<':
+ add_token(match('=') ? LESS_EQUAL : LESS);
+ break;
+ case '/':
+ if (match('/')) {
+ while (peek() != '\n' && !is_at_end()) {
+ advance();
+ }
+ } else {
+ add_token(SLASH);
+ }
+ break;
+ case ' ':
+ case '\t':
+ case '\r':
+ break;
+ case '\n':
+ current_line += 1;
+ break;
+ case '"':
+ string();
+ break;
+ case ';':
+ add_token(SEMICOLON);
+ break;
+ default:
+ if (is_digit(c)) {
+ number();
+ } else if (is_alpha(c)) {
+ identifier();
+ } else {
+ error("Unexpected character.", c);
+ }
+ break;
+ }
+}
+
+static void identifier(void) {
+ while (is_alphanumeric(peek())) {
+ advance();
+ }
+
+ char *text = substring(source, start + 1, current_pos - start);
+ const TokenType *tokentype = get_keyword_token(text);
+ if (tokentype == NULL) {
+ add_token(IDENTIFIER);
+ } else {
+ add_token(*tokentype);
+ }
+}
+
+static void number(void) {
+ while (is_digit(peek()))
+ advance();
+ if (peek() == '.' && is_digit((peek_next())))
+ advance();
+ while (is_digit(peek()))
+ advance();
+ add_token_with_literal(NUMBER,
+ substring(source, start + 1, current_pos - start));
+}
+
+bool is_digit(char c) { return c >= '0' && c <= '9'; }
+
+void string(void) {
+ while (peek() != '"' && !is_at_end()) {
+ if (peek() == '\n')
+ current_line += 1;
+ advance();
+ }
+
+ if (is_at_end()) {
+ error("Unterminated string.", '\0');
+ return;
+ }
+
+ advance();
+
+ char *string = substring(source, start + 2, current_pos - start - 2);
+ add_token_with_literal(STRING, string);
+}
+
+static bool match(char expected) {
+ if (is_at_end()) {
+ return false;
+ }
+ if (expected != source[current_pos]) {
+ return false;
+ }
+ current_pos += 1;
+ return true;
+}
+
+static char peek_next(void) {
+ if (current_pos + 1 >= (int)strlen(source)) {
+ return '\0';
+ }
+ return source[current_pos + 1];
+}
+
+static char peek(void) {
+ if (is_at_end()) {
+ return '\0';
+ }
+ return source[current_pos];
+}
+static bool is_alpha(char c) {
+ return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_');
+}
+
+static bool is_alphanumeric(char c) { return is_alpha(c) || is_digit(c); }
+
+static bool is_at_end(void) { return current_pos >= (int)strlen(source); }
+
+static void error(char *message, char c) { report("", message, c); }
+
+static void report(char *where, char *message, char c) {
+ printf("*[Line %i] Error %s : %s [%c]\n", current_line, where, message, c);
+ had_error = true;
+}
diff --git a/src/scanner.cpp b/src/scanner.cpp
deleted file mode 100644
index 8b91cfe..0000000
--- a/src/scanner.cpp
+++ /dev/null
@@ -1,211 +0,0 @@
-#include "scanner.hpp"
-#include "error.hpp"
-#include "tokens.hpp"
-#include
-#include
-#include