Compare commits

...

10 commits

Author SHA1 Message Date
Shautvast
1c092acd40 interpreter working nicely 2024-12-11 21:36:36 +01:00
Shautvast
5df54044f8 back to C 2024-12-11 14:16:44 +01:00
Shautvast
8c7e93f09b add some const 2024-10-27 16:06:08 +01:00
Shautvast
47b33bf276 no using global namespace 2024-10-27 15:48:58 +01:00
Shautvast
e3c729550e remove std namespace from header files 2024-10-27 15:45:15 +01:00
Shautvast
ea734dd234 get rid of unions2 2024-10-25 17:38:06 +02:00
Shautvast
a5d77019f3 get rid of unions 2024-10-25 17:35:19 +02:00
Shautvast
6fd95bd486 C++23, rusty error handling, renamed output repr of tokens 2024-10-25 16:43:57 +02:00
Shautvast
32e76fb704 bugfix 2024-10-22 16:29:39 +02:00
Shautvast
8d3d1e8787 use smart pointers 2024-10-22 12:58:27 +02:00
25 changed files with 1330 additions and 846 deletions

8
.idea/.gitignore generated vendored
View file

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

9
.idea/clox.iml generated
View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/misc.xml generated
View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="24" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

8
.idea/modules.xml generated
View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/clox.iml" filepath="$PROJECT_DIR$/.idea/clox.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated
View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View file

@ -1,70 +0,0 @@
[
{
"arguments": [
"/usr/bin/clang++",
"-c",
"-std=c++17",
"-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"
},
{
"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,23 +1,30 @@
TARGET := ./target TARGET := ./target
SRC := ./src SRC := ./src
CC := clang++ -c -std=c++17 -Wall -Wextra -pedantic -Werror CC := clang -c -std=c17
# -Wall -Wextra -pedantic -Werror
SRCS := $(shell find $(SRC) -name '*.c') SRCS := $(shell find $(SRC) -name '*.c')
OBJS := $(SRCS:%=$(TARGET)/%.o) 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 $(TARGET)/lox: $(TARGET)/lox.c.o $(TARGET)/interpreter.c.o $(TARGET)/parser.c.o $(TARGET)/scanner.c.o $(TARGET)/tokens.c.o $(TARGET)/utils.c.o
clang++ $(TARGET)/lox.cpp.o -L$(TARGET) -lscanner.cpp.o -ltokens.cpp.o -lparser.cpp.o -o $(TARGET)/lox clang $(TARGET)/lox.c.o -L$(TARGET) -linterpreter.c.o -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 $@ $(CC) $< -o $@
$(TARGET)/scanner.cpp.o: $(SRC)/scanner.cpp $(TARGET)/tokens.c.o: $(SRC)/tokens.c
$(CC) $< -o $@ $(CC) $< -o $@
$(TARGET)/parser.cpp.o: $(SRC)/parser.cpp $(TARGET)/scanner.c.o: $(SRC)/scanner.c
$(CC) $< -o $@ $(CC) $< -o $@
$(TARGET)/lox.cpp.o: $(SRC)/lox.cpp $(TARGET) $(TARGET)/parser.c.o: $(SRC)/parser.c
$(CC) $< -o $@
$(TARGET)/interpreter.c.o: $(SRC)/interpreter.c
$(CC) $< -o $@
$(TARGET)/lox.c.o: $(SRC)/lox.c $(TARGET)
$(CC) $< -o $@ $(CC) $< -o $@
$(TARGET): $(TARGET):

248
src/interpreter.c Normal file
View file

@ -0,0 +1,248 @@
#include "interpreter.h"
#include <string.h>
Value *accept(Expression *expr);
void checkNumeric(Value *left, Value *right);
Value *isEqual(Value *left, Value *right);
bool streq(char *left, char *right);
Value *visitBinary(Expression *expr);
Value *visitUnary(Expression *unary);
Value *visitLiteral(Expression *literal);
Value *visitGroup(Expression *literal);
Value *visitVariable(Expression *var);
Value *visitVariableStmt(Expression *varStmt);
Value *visitAssignStmt(Expression *varStmt);
Value *visitPrintStmt(Expression *printStatement);
Value *visitBlock(Expression *block);
static VarMap *current;
Value *accept(Expression *expr) {
// printf("accept %s\n", expr->type);
char *type = expr->type;
if (streq(type, "BinaryExpr")) {
return visitBinary(expr);
}
if (streq(type, "UnaryExpr")) {
return visitUnary(expr);
}
if (streq(type, "Literal")) {
return visitLiteral(expr);
}
if (streq(type, "Group")) {
return visitGroup(expr);
}
if (streq(type, "PrintStmt")) {
return visitPrintStmt(expr);
}
if (streq(type, "VariableStmt")) {
return visitVariableStmt(expr);
}
if (streq(type, "AssignStmt")) {
return visitAssignStmt(expr);
}
if (streq(type, "Variable")) {
return visitVariable(expr);
}
if (streq(type, "ExprStmt")) {
return accept(expr->left);
}
if (streq(type, "Block")) {
return visitBlock(expr);
}
return NULL;
}
void execute(Expression *statement) { accept(statement); }
void interpret(VarMap *environment, ExpressionList *statements) {
current = environment;
for (int i = 0; i < statements->size; i++) {
execute(exprlist_get(statements, i));
}
}
Value *visitVariable(Expression *var) { return var_get(current, var->name); }
Value *visitVariableStmt(Expression *var) {
Value *value = accept(var->left);
if (var_isdefined(current, var->name)) {
printf("%s is already defined\n", var->name);
return NULL;
}
var_add(current, var->name, value);
return NULL;
}
Value *visitBlock(Expression *blockStmt) {
VarMap *previous = current;
current = newVarMap(previous);
for (int i = 0; i < blockStmt->block->size; i++) {
Expression *e = exprlist_get(blockStmt->block, i);
execute(e);
}
current = previous;
return NULL;
}
Value *visitGroup(Expression *group) { return accept(group->left); }
Value *visitPrintStmt(Expression *printStatement) {
Value *value = accept(printStatement->left);
if (value == NULL) {
return NULL;
}
printf("%s\n", value_string(value));
return NULL;
}
Value *visitAssignStmt(Expression *var) {
Value *value = accept(var->left);
bool result = var_set(current, var->name, value);
if (!result) {
printf("%s is not defined", var->name);
}
return NULL;
}
Value *visitUnary(Expression *unary) {
Value *right = accept(unary->right);
switch (unary->operator->type) {
case MINUS:
return newNumber(-right->value.number);
case BANG:
return newBoolean(!right->value.boolean);
default:
return NULL;
};
}
Value *visitLiteral(Expression *literal) { return literal->value; }
Value *visitBinary(Expression *expr) {
Value *left = accept(expr->left);
Value *right = accept(expr->right);
switch (expr->operator->type) {
case MINUS:
checkNumeric(left, right);
return newNumber(left->value.number - right->value.number);
case PLUS:
checkNumeric(left, right);
return newNumber(left->value.number + right->value.number);
case SLASH:
checkNumeric(left, right);
return newNumber(left->value.number / right->value.number);
case STAR:
checkNumeric(left, right);
return newNumber(left->value.number * right->value.number);
case GREATER:
checkNumeric(left, right);
return newBoolean(left->value.number > right->value.number);
case GREATER_EQUAL:
checkNumeric(left, right);
return newBoolean(left->value.number >= right->value.number);
case LESS:
checkNumeric(left, right);
return newBoolean(left->value.number < right->value.number);
case LESS_EQUAL:
checkNumeric(left, right);
return newBoolean(left->value.number <= right->value.number);
case BANG_EQUAL:
return isEqual(left, right);
case EQUAL_EQUAL:
return isEqual(left, right);
default:
return NULL;
}
}
void checkNumeric(Value *left, Value *right) {
if (left->type != NUMBERTYPE || right->type != NUMBERTYPE) {
printf("operands should be numeric");
exit(-1);
}
}
VarMap *newVarMap(VarMap *enclosing) {
VarMap *map = malloc(sizeof(VarMap));
if (map == NULL) {
printf("Can not allocate memory for VarMap");
exit(1);
}
map->size = 0;
map->enclosing = enclosing;
return map;
}
bool var_isdefined(VarMap *map, const char *key) {
for (int i = 0; i < map->size; i++) {
if (strcmp(map->entries[i].key, key) == 0) {
return true;
}
}
if (map->enclosing != NULL) {
return var_isdefined(map->enclosing, key);
}
return false;
}
void var_add(VarMap *map, const char *key, Value *value) {
if (map->size == MAX_MAP_SIZE) {
printf("Map is full!\n");
return;
}
strcpy(map->entries[map->size].key, key);
map->entries[map->size].value = value;
map->size += 1;
}
bool var_set(VarMap *map, char *key, Value *value) {
for (int i = 0; i < map->size; i++) {
if (strcmp(map->entries[i].key, key) == 0) {
map->entries[i].value = value; // Return the value
return true;
}
}
return false;
}
Value *var_get(VarMap *map, const char *key) {
for (int i = 0; i < map->size; i++) {
if (strcmp(map->entries[i].key, key) == 0) {
return map->entries[i].value; // Return the value
}
}
if (map->enclosing != NULL) {
return var_get(map->enclosing, key);
}
printf("%s is not defined\n", key);
return NULL; // Key not found
}
Value *isEqual(Value *left, Value *right) {
if (left->type != right->type) {
return newBoolean(false);
}
switch (left->type) {
case STRINGTYPE:
return newBoolean(strcmp(left->value.string, right->value.string) == 0);
case NUMBERTYPE:
return newBoolean(left->value.number == right->value.number);
case BOOLEANTYPE:
return newBoolean(left->value.boolean == right->value.boolean);
case EXPR: // MUST NOT HAPPEN :(=)
return NULL;
}
}
bool streq(char *left, char *right) { return strcmp(left, right) == 0; }

28
src/interpreter.h Normal file
View file

@ -0,0 +1,28 @@
#ifndef INTERPRETER_H
#define INTERPRETER_H
#include "parser.h"
#define MAX_MAP_SIZE 1000
typedef struct VarMap VarMap;
VarMap *newVarMap(VarMap *enclosing);
void interpret(VarMap *environment, ExpressionList *statements);
typedef struct {
char key[50]; // Array of strings for keys
Value *value; // Integer values associated with keys
} MapEntry;
struct VarMap {
VarMap *enclosing;
MapEntry entries[MAX_MAP_SIZE]; // Array of key-value pairs
int size; // Current size of the map
};
bool var_isdefined(VarMap *map, const char *key);
void var_add(VarMap *map, const char *key, Value *value);
Value *var_get(VarMap *map, const char *key);
bool var_set(VarMap *map, char *key, Value *value);
#endif

90
src/lox.c Normal file
View file

@ -0,0 +1,90 @@
#include "interpreter.h"
#include "parser.h"
#include "scanner.h"
#include "utils.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int run_file(char *file);
void run_prompt(void);
void run(char *source);
static VarMap *environment;
int main(int argc, char *argv[]) {
environment = newVarMap(NULL);
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);
interpret(environment, list);
}

View file

@ -1,74 +0,0 @@
#include "parser.hpp"
#include "scanner.hpp"
#include "tokens.hpp"
#include <cstdbool>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void print_tokens(vector<Token> *list);
int run_file(string file);
void run_prompt(void);
ScanResult 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);
}
ScanResult scan_result = run(content);
return EXIT_SUCCESS;
}
void run_prompt(void) {
string line;
for (;;) {
cout << ">";
getline(cin, line);
ScanResult 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";
}
}
}
ScanResult run(string source) {
Scanner *scanner = new Scanner(source);
return scanner->scan_tokens();
}
void print_tokens(vector<Token> *list) {
for (vector<Token>::iterator token = list->begin(); token != list->end();
++token) {
cout << token->as_string() << "(" << token->literal << "), ";
}
cout << "\n";
}

437
src/parser.c Normal file
View file

@ -0,0 +1,437 @@
#include "parser.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
bool is_at_end(void);
Token *peek(void);
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 *parse_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 = newExpressionList();
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 = NULL;
if (match1(EQUAL)) {
initializer = expression();
}
consume(SEMICOLON, "Expected semicolon");
Expression *variableStatement = newExpression("VariableStmt");
variableStatement->name = name->lexeme;
variableStatement->left = initializer;
return variableStatement;
}
Expression *statement(void) {
if (match1(PRINT)) {
return printStatement();
}
if (match1(LEFT_BRACE)) {
Expression *block = newExpression("Block");
ExpressionList *block_statements = newExpressionList();
while (!check(RIGHT_BRACE) && !is_at_end()) {
exprlist_add(block_statements, declaration());
}
advance();
block->block = block_statements;
return block;
}
return expressionStatement();
}
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->left = 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->left = 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->value = newBoolean(false);
return r;
}
if (match1(TRUE)) {
r->value = newBoolean(true);
return r;
}
if (match1(NIL)) {
r->name = "nil";
r->value = NULL;
return r;
}
if (check(NUMBER)) {
advance();
r->value = newNumber(strtod(previous()->literal, NULL));
return r;
}
if (check(STRING)) {
advance();
r->value = newString(previous()->literal);
return r;
}
if (match1(IDENTIFIER)) {
Expression *var = newExpression("Variable");
var->name = previous()->lexeme;
return var;
}
if (match1(LEFT_PAREN)) {
Expression *expr = expression();
Expression *group = newExpression("Group");
consume(RIGHT_PAREN, "Expect ')' after expression.");
group->left = expr;
return group;
}
Expression *error = newExpression("Error");
return error;
}
Token *consume(TokenType type, char *message) {
// printf("%s==%s\n", token_name(type), token_name(peek()->type));
if (check(type)) {
return advance();
}
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); }
ExpressionList *newExpressionList() {
ExpressionList *list = malloc(sizeof(ExpressionList));
if (list == NULL) {
printf("Cannot allocate memory for ExpressionList");
exit(1);
}
list->expressions = malloc(sizeof(Expression) * 32);
if (list->expressions == NULL) {
printf("Cannot allocate memory for ExpressionList");
exit(1);
}
list->size = 0;
list->capacity = 32;
return list;
}
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;
}
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, "nil") == 0 && expr->value == NULL) {
printf(", value: NULL");
}
}
if (expr->value != NULL) {
printf(", value: %s", value_string(expr->value));
}
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;
e->block = NULL;
return e;
}
Value *newString(char *string) {
Value *value = newValue();
value->type = STRINGTYPE;
value->value.string = string;
return value;
}
Value *newBoolean(bool boolean) {
Value *value = newValue();
value->type = BOOLEANTYPE;
value->value.boolean = boolean;
return value;
}
Value *newNumber(double number) {
Value *value = newValue();
value->type = NUMBERTYPE;
value->value.number = number;
return value;
}
Value *newValue(void) {
Value *value = malloc(sizeof(Value));
if (value == NULL) {
printf("can't allocate memory for Value");
exit(1);
}
return value;
}
char *d_to_s(double d) {
char *str = (char *)malloc(50);
if (str == NULL) {
puts("cannot allocate memory for string");
exit(1);
}
snprintf(str, sizeof(str), "%lf", d); //
return str;
}
const char *value_string(Value *v) {
switch (v->type) {
case STRINGTYPE:
return v->value.string;
case BOOLEANTYPE:
return v->value.boolean ? "true" : "false";
case NUMBERTYPE:
return d_to_s(v->value.number);
case EXPR:
return v->value.expr->type;
}
}

View file

@ -1,193 +0,0 @@
#include "parser.hpp"
using namespace std;
Expression::~Expression() {}
// class Binary
ExprType Binary::get_type() { return ExprType::Binary; }
string Binary::as_string() {
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() {
delete left;
delete right;
delete op;
}
// class Grouping
ExprType Grouping::get_type() { return ExprType::Grouping; }
string Grouping::as_string() { return "(" + expr->as_string() + ")"; }
Grouping::Grouping(Expression *_expr) : expr(_expr){};
Grouping::~Grouping() { delete expr; }
// class Unary
ExprType Unary::get_type() { return ExprType::Unary; }
string Unary::as_string() {
return token_name(op->tokentype) + right->as_string();
}
Unary::Unary(Token *_operator, Expression *_right)
: op(_operator), right(_right){};
Unary::~Unary() {
delete right;
delete op;
}
// class Literal
string Literal::as_string() {
string text;
switch (valuetype) {
case String:
text = "\"" + value.str + "\"";
break;
case Numeric:
text = to_string(value.numeric);
break;
case Boolean:
text = value.boolean ? "True" : "False";
break;
case Nil:
text = "NULL";
break;
}
return text;
}
// class Parser
Expression *Parser::parse(vector<Token> 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;
};
Token *Parser::consume(Token::Type typ, string message) {
if (check(typ)) {
return advance();
}
throw error(peek(), message);
}
runtime_error Parser::error(Token token, string message) {
cout << token.as_string() << " " << message;
return runtime_error(message); // TODO no exceptions
}
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());
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)) {
Expression *e = expression();
consume(Token::Type::RIGHT_PAREN, "Expect ')'.");
return new Grouping(e);
}
throw runtime_error("Expected an expression");
}
Expression *Parser::unary() {
if (match(2, Token::BANG, Token::Type::MINUS)) {
Token *op = previous();
Expression *right = unary();
return new Unary(op, right);
}
return primary();
}
Expression *Parser::expression() { return equality(); }
Expression *Parser::factor() {
Expression *expr = unary();
while (match(2, Token::Type::SLASH, Token::Type::STAR)) {
Token *op = previous();
Expression *right = unary();
expr = new Binary(expr, op, right);
}
return expr;
}
Expression *Parser::term() {
Expression *expr = factor();
while (match(2, Token::Type::MINUS, Token::Type::PLUS)) {
Token *op = previous();
Expression *right = unary();
expr = new Binary(expr, op, right);
}
return expr;
}
Expression *Parser::equality(void) {
Expression *expr = comparison();
while (match(2, Token::Type::BANG_EQUAL, Token::Type::BANG_EQUAL)) {
Token *op = previous();
Expression *right = comparison();
return new Binary(expr, op, right);
}
return expr;
}
Expression *Parser::comparison(void) {
Expression *expr = term();
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);
}
return expr;
}

56
src/parser.h Normal file
View file

@ -0,0 +1,56 @@
#ifndef PARSER_H
#define PARSER_H
#include "tokens.h"
typedef struct Expression Expression;
typedef struct ExpressionList ExpressionList;
typedef union ValueHolder {
double number;
char *string;
bool boolean;
Expression *expr;
} ValueHolder;
typedef enum { NUMBERTYPE, STRINGTYPE, BOOLEANTYPE, EXPR } Type;
typedef struct Value {
Type type;
ValueHolder value;
} Value;
struct Expression {
char *type;
Expression *left;
Expression *right;
Token *operator;
char *name;
Value *value;
ExpressionList *block;
};
typedef struct ExpressionList {
Expression **expressions;
int size;
int capacity;
} ExpressionList;
const char *value_string(Value *v);
Value *newValue(void);
Value *newString(char *string);
Value *newNumber(double number);
Value *newBoolean(bool boolean);
ExpressionList *parse(TokenList *tokens);
ExpressionList *newExpressionList();
// 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);
#endif

View file

@ -1,130 +0,0 @@
#pragma once
#include "tokens.hpp"
#include <vector>
enum class ExprType { Binary, Grouping, Unary, Literal, None };
using namespace std;
/// Base class for expressions
class Expression {
public:
virtual ExprType get_type() = 0; // abstract, getter for tyoe
virtual string as_string() = 0; // abstract, string rep for debugging
virtual ~Expression(); // destructor
}; // namespace stdclass Expression
/// An expression with two operands
class Binary : public Expression {
Expression *left;
Token *op;
Expression *right;
public:
ExprType get_type() override;
string as_string() override;
Binary(Expression *_left, Token *_operator, Expression *_right);
~Binary();
};
/// An expression between parentheses
class Grouping : public Expression {
Expression *expr;
public:
ExprType get_type() override;
string as_string() override;
Grouping(Expression *_expr);
~Grouping();
};
/// An expression with one operand (operator is `-` or `!`)
class Unary : public Expression {
Token *op;
Expression *right;
public:
ExprType get_type() override;
string as_string() override;
Unary(Token *_operator, Expression *_right);
~Unary();
};
/// empty class that is the type of the Nil value
class Void {};
/// encapsulates a value: numeric, string etc
class Literal : public Expression {
public:
enum ValueType { String, Numeric, Boolean, Nil } valuetype;
ExprType get_type() override { return ExprType::Literal; }
union Value {
double_t numeric;
bool boolean;
string str;
Void dummy;
Value(double_t _numeric) : numeric(_numeric) {}
Value(bool _boolean) : boolean(_boolean) {}
Value(string _str) : str(_str) {}
Value(Void v) : dummy(v) {}
~Value() {}
} value;
Literal(Void 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){};
string as_string() override;
};
class Parser {
vector<Expression> expressions;
vector<Token> 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
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);
/// tries to parse the token as a primary value (string, number etc)
Expression *primary();
/// tries to parse the tokens as a unary expression
Expression *unary();
/// tries to parse the tokens
Expression *expression();
/// tries to parse the tokens as a multiplication or division
Expression *factor();
/// tries to parse the tokens as an addition or subtraction
Expression *term();
/// tries to parse the tokens as an equality (`a == b` / `a!= b`)
Expression *equality(void);
/// tries to parse the tokens as a comparison (`a > b` / `a >= b` / `a < b` /
/// `a <= b` )
Expression *comparison(void);
public:
/// public method for parsing expressions
Expression *parse(vector<Token> tokenlist);
};

243
src/scanner.c Normal file
View file

@ -0,0 +1,243 @@
#include "scanner.h"
#include "tokens.h"
#include "utils.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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(STAR);
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;
}

View file

@ -1,211 +0,0 @@
#include "scanner.hpp"
#include "tokens.hpp"
#include <cstdbool>
#include <iostream>
#include <map>
#include <string>
#include <vector>
using namespace std;
static const map<string, Token::Type> keywords = {
{"and", Token::Type::AND}, {"class", Token::Type::CLASS},
{"else", Token::Type::ELSE}, {"false", Token::Type::FALSE},
{"for", Token::Type::FOR}, {"fun", Token::Type::FUN},
{"if", Token::Type::IF}, {"nil", Token::Type::NIL},
{"or", Token::Type::OR}, {"print", Token::Type::PRINT},
{"return", Token::Type::RETURN}, {"super", Token::Type::SUPER},
{"this", Token::Type::THIS}, {"true", Token::Type::TRUE},
{"var", Token::Type::VAR}, {"while", Token::Type::WHILE},
};
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() {
while (current_pos < source.length()) {
start = current_pos;
scan_token();
}
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) {
Token token = Token(type, "", "", current_line);
token_list.push_back(token);
}
void Scanner::add_token(Token::Type type, string literal) {
Token token = Token(type, literal, literal, current_line);
token_list.push_back(token);
}
char Scanner::advance() {
char c = source.at(current_pos++);
return c;
}
void Scanner::scan_token() {
char c = advance();
switch (c) {
case '(':
add_token(Token::Type::LEFT_PAREN);
break;
case ')':
add_token(Token::Type::RIGHT_PAREN);
break;
case '{':
add_token(Token::Type::LEFT_BRACE);
break;
case '}':
add_token(Token::Type::RIGHT_BRACE);
break;
case ',':
add_token(Token::Type::COMMA);
break;
case '.':
add_token(Token::Type::DOT);
break;
case '+':
add_token(Token::Type::PLUS);
break;
case '-':
add_token(Token::Type::MINUS);
break;
case '!':
add_token(match('=') ? Token::Type::BANG_EQUAL : Token::Type::BANG);
break;
case '=':
add_token(match('=') ? Token::Type::EQUAL_EQUAL : Token::Type::EQUAL);
break;
case '>':
add_token(match('=') ? Token::Type::GREATER_EQUAL : Token::Type::GREATER);
break;
case '<':
add_token(match('=') ? Token::Type::LESS_EQUAL : Token::Type::LESS);
break;
case '*':
add_token(Token::Type::STAR);
break;
case '/':
if (match('/')) {
while (peek() != '\n' && !is_at_end()) {
advance();
}
} else {
add_token(Token::Type::SLASH);
}
break;
case ' ':
case '\t':
case '\r':
break;
case '\n':
current_line += 1;
break;
case '"':
scan_string();
break;
default:
if (is_digit(c)) {
number();
} else if (is_alpha(c)) {
identifier();
} else {
error("Unexpected character.");
}
break;
}
}
void Scanner::identifier() {
while (is_alphanumeric(peek())) {
advance();
}
string text = source.substr(start, current_pos - start);
auto it = keywords.find(text);
if (it != keywords.end()) {
add_token(it->second, text);
} else {
add_token(Token::Type::IDENTIFIER, text);
}
}
void Scanner::number() {
while (is_digit(peek()))
advance();
if (peek() == '.' && is_digit((peek_next())))
advance();
while (is_digit(peek()))
advance();
add_token(Token::Type::NUMBER, source.substr(start, current_pos - start));
}
bool Scanner::is_digit(char c) { return c >= '0' && c <= '9'; }
void Scanner::scan_string() {
while (peek() != '"' && !is_at_end()) {
if (peek() == '\n')
current_line += 1;
advance();
}
if (is_at_end()) {
error("Unterminated string.");
return;
}
advance();
string s = source.substr(start + 1, current_pos - start - 2);
add_token(Token::Type::STRING, s);
}
bool Scanner::match(char expected) {
if (is_at_end()) {
return false;
}
if (expected != source[current_pos]) {
return false;
}
current_pos += 1;
return true;
}
char Scanner::peek_next() {
if (current_pos + 1 >= source.length()) {
return '\0';
}
return source[current_pos + 1];
}
char Scanner::peek() {
if (is_at_end()) {
return '\0';
}
return source[current_pos];
}
bool Scanner::is_alpha(char c) {
return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_');
}
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 where, std::string message) {
cout << "*[Line " << current_line << "] Error " << where << " : " << message
<< "\n";
had_error = true;
}

45
src/scanner.h Normal file
View file

@ -0,0 +1,45 @@
#ifndef SCANNER_H
#define SCANNER_H
#include "tokens.h"
#include <stdbool.h>
#include <string.h>
typedef struct {
bool had_error;
TokenList token_list;
} ScanResult;
ScanResult scan_tokens(char *source);
typedef struct {
const char *key;
const TokenType value;
} Item;
static const Item keywords[] = {
{"and", AND}, {"class", CLASS}, {"else", ELSE}, {"false", FALSE},
{"for", FOR}, {"fun", FUN}, {"if", IF}, {"nil", NIL},
{"or", OR}, {"print", PRINT}, {"return", RETURN}, {"super", SUPER},
{"this", THIS}, {"true", TRUE}, {"var", VAR}, {"while", WHILE}};
inline static const TokenType *get_keyword_token(char *key) {
int low = 0;
int high = sizeof(keywords) / sizeof(Item);
while (low < high) {
int mid = (low + high) / 2;
int c = strcmp(keywords[mid].key, key);
if (c == 0) {
return &keywords[mid].value;
}
if (c < 0) {
low = mid + 1;
} else {
high = mid;
}
}
return NULL;
}
#endif

View file

@ -1,43 +0,0 @@
#pragma once
#include "tokens.hpp"
#include <cstdbool>
#include <string>
#include <vector>
using namespace std;
typedef struct {
bool had_error;
vector<Token> token_list;
} ScanResult;
class Scanner {
private:
bool had_error;
size_t current_pos;
int start;
int current_line;
string source;
vector<Token> token_list;
public:
Scanner(string s);
ScanResult scan_tokens();
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);
void scan_string();
bool match(char expected);
char peek_next();
char peek();
bool is_alpha(char c);
bool is_alphanumeric(char c);
bool is_at_end(void);
void error(string message);
void report(string where, string message);
};

52
src/tokens.c Normal file
View file

@ -0,0 +1,52 @@
#include "tokens.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
Token *newToken(void) {
Token *token = malloc(sizeof(Token));
if (token == NULL) {
printf("can't allocate memory for Token");
exit(1);
}
return token;
}
void tokenlist_init(TokenList *list) {
list->tokens = malloc(sizeof(Token) * 32);
if (list->tokens == NULL) {
printf("Cannot allocate memory for TokenList");
exit(1);
}
list->size = 0;
list->capacity = 32;
}
void tokenlist_add(TokenList *list, Token *value) {
if (list->size >= list->capacity) {
list->capacity *= 2;
list->tokens = realloc(list->tokens, sizeof(Token) * list->capacity);
}
list->tokens[list->size] = value;
list->size += 1;
}
Token *tokenlist_get(TokenList *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->tokens[index];
}
Token *tokenlist_last(TokenList *list) { return list->tokens[list->size - 1]; }
void tokenlist_print(TokenList *tokenlist) {
for (int i = 0; i < tokenlist->size; i++) {
Token *token = tokenlist_get(tokenlist, i);
printf("%s(%s)", token_name(token->type), token->lexeme);
}
printf("\n");
}
void tokenlist_free(TokenList *list) { free(list->tokens); }

View file

@ -1,21 +0,0 @@
#include "tokens.hpp"
using namespace std;
Token::Token(Token::Type _tokentype, string _lexeme, string _literal, int _line)
: lexeme(_lexeme), literal(_literal), line(_line), tokentype(_tokentype) {}
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"};
return tokens[(int)tokentype];
}
std::string Token::as_string() { return token_name(tokentype); }

90
src/tokens.h Normal file
View file

@ -0,0 +1,90 @@
#ifndef TOKENS_H
#define TOKENS_H
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
typedef enum {
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,
END_OF_FILE,
ERROR
} TokenType;
static inline const char *token_name(TokenType type) {
static const char *tokens[] = {
"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", "END_OF_FILE", "ERROR"};
return tokens[type];
}
typedef struct Token {
TokenType type;
char *lexeme;
char *literal;
int line;
} Token;
typedef struct TokenList {
Token **tokens;
int size;
int capacity;
} TokenList;
Token *newToken(void);
void tokenlist_init(TokenList *list);
void tokenlist_add(TokenList *list, Token *value);
Token *tokenlist_get(TokenList *list, int index);
void tokenlist_print(TokenList *tokenlist);
void tokenlist_free(TokenList *list);
Token *tokenlist_last(TokenList *list);
#endif

View file

@ -1,60 +0,0 @@
#pragma once
#include <string>
using namespace std;
class Token {
public:
string lexeme;
string literal;
int line;
enum Type {
END_OF_FILE = 0,
LEFT_PAREN = 1,
RIGHT_PAREN = 2,
LEFT_BRACE = 3,
RIGHT_BRACE = 4,
COMMA = 5,
DOT = 6,
MINUS = 7,
PLUS = 8,
SEMICOLON = 9,
SLASH = 10,
STAR = 11,
BANG = 12,
BANG_EQUAL = 13,
EQUAL = 14,
EQUAL_EQUAL = 15,
GREATER = 16,
GREATER_EQUAL = 17,
LESS = 18,
LESS_EQUAL = 19,
IDENTIFIER = 20,
STRING = 21,
NUMBER = 22,
AND = 23,
CLASS = 24,
ELSE = 25,
FALSE = 26,
FUN = 27,
FOR = 28,
IF = 29,
NIL = 30,
OR = 31,
PRINT = 32,
RETURN = 33,
SUPER = 34,
THIS = 35,
TRUE = 36,
VAR = 37,
WHILE = 38,
} tokentype;
string as_string();
Token(Token::Type _tokentype, std::string _lexeme, std::string _literal,
int line);
};
string token_name(Token::Type tokentype);

21
src/utils.c Normal file
View file

@ -0,0 +1,21 @@
#include "utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *substring(char *string, int position, int length) {
char *ptr = malloc(length + 1);
if (ptr == NULL) {
printf("out of memory");
exit(EXIT_FAILURE);
}
int c;
for (c = 0; c < length; c += 1) {
*(ptr + c) = *(string + position - 1);
string += sizeof(char);
}
*(ptr + c) = '\0';
return ptr;
}

6
src/utils.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef UTILS_H
#define UTILS_H
char *substring(char *string, int position, int length);
#endif