Compare commits
No commits in common. "1c092acd4031f4ef9b88515252b11fff68bcae55" and "1f4228c038a7ead71b23620507fead28d0f4a635" have entirely different histories.
1c092acd40
...
1f4228c038
25 changed files with 846 additions and 1330 deletions
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# 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
Normal file
9
.idea/clox.iml
generated
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?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
Normal file
6
.idea/misc.xml
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?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
Normal file
8
.idea/modules.xml
generated
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?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
Normal file
6
.idea/vcs.xml
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
70
compile_commands.json
Normal file
70
compile_commands.json
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
[
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
21
makefile
21
makefile
|
|
@ -1,30 +1,23 @@
|
|||
TARGET := ./target
|
||||
SRC := ./src
|
||||
CC := clang -c -std=c17
|
||||
# -Wall -Wextra -pedantic -Werror
|
||||
CC := clang++ -c -std=c++17 -Wall -Wextra -pedantic -Werror
|
||||
|
||||
SRCS := $(shell find $(SRC) -name '*.c')
|
||||
OBJS := $(SRCS:%=$(TARGET)/%.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.c.o -L$(TARGET) -linterpreter.c.o -lparser.c.o -ltokens.c.o -lscanner.c.o -lutils.c.o -o $(TARGET)/lox
|
||||
$(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)/utils.c.o: $(SRC)/utils.c
|
||||
$(TARGET)/tokens.cpp.o: $(SRC)/tokens.cpp
|
||||
$(CC) $< -o $@
|
||||
|
||||
$(TARGET)/tokens.c.o: $(SRC)/tokens.c
|
||||
$(TARGET)/scanner.cpp.o: $(SRC)/scanner.cpp
|
||||
$(CC) $< -o $@
|
||||
|
||||
$(TARGET)/scanner.c.o: $(SRC)/scanner.c
|
||||
$(TARGET)/parser.cpp.o: $(SRC)/parser.cpp
|
||||
$(CC) $< -o $@
|
||||
|
||||
$(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)
|
||||
$(TARGET)/lox.cpp.o: $(SRC)/lox.cpp $(TARGET)
|
||||
$(CC) $< -o $@
|
||||
|
||||
$(TARGET):
|
||||
|
|
|
|||
|
|
@ -1,248 +0,0 @@
|
|||
#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; }
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
#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
90
src/lox.c
|
|
@ -1,90 +0,0 @@
|
|||
#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);
|
||||
}
|
||||
74
src/lox.cpp
Normal file
74
src/lox.cpp
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
#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
437
src/parser.c
|
|
@ -1,437 +0,0 @@
|
|||
#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;
|
||||
}
|
||||
}
|
||||
193
src/parser.cpp
Normal file
193
src/parser.cpp
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
#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
56
src/parser.h
|
|
@ -1,56 +0,0 @@
|
|||
#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
|
||||
130
src/parser.hpp
Normal file
130
src/parser.hpp
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
#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
243
src/scanner.c
|
|
@ -1,243 +0,0 @@
|
|||
#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;
|
||||
}
|
||||
211
src/scanner.cpp
Normal file
211
src/scanner.cpp
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
#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;
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
#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
|
||||
43
src/scanner.hpp
Normal file
43
src/scanner.hpp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#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
52
src/tokens.c
|
|
@ -1,52 +0,0 @@
|
|||
#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); }
|
||||
21
src/tokens.cpp
Normal file
21
src/tokens.cpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#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
90
src/tokens.h
|
|
@ -1,90 +0,0 @@
|
|||
#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
|
||||
60
src/tokens.hpp
Normal file
60
src/tokens.hpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#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
21
src/utils.c
|
|
@ -1,21 +0,0 @@
|
|||
#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;
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
char *substring(char *string, int position, int length);
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Reference in a new issue