back to C
This commit is contained in:
parent
8c7e93f09b
commit
5df54044f8
24 changed files with 958 additions and 827 deletions
8
.idea/.gitignore
generated
vendored
8
.idea/.gitignore
generated
vendored
|
|
@ -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
9
.idea/clox.iml
generated
|
|
@ -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
6
.idea/misc.xml
generated
|
|
@ -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
8
.idea/modules.xml
generated
|
|
@ -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
6
.idea/vcs.xml
generated
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
[
|
|
||||||
{
|
|
||||||
"arguments": [
|
|
||||||
"/usr/bin/clang++",
|
|
||||||
"-c",
|
|
||||||
"-std=c++23",
|
|
||||||
"-Wall",
|
|
||||||
"-Wextra",
|
|
||||||
"-pedantic",
|
|
||||||
"-Werror",
|
|
||||||
"-o",
|
|
||||||
"target/lox.cpp.o",
|
|
||||||
"src/lox.cpp"
|
|
||||||
],
|
|
||||||
"directory": "/Users/Shautvast/dev/clox",
|
|
||||||
"file": "/Users/Shautvast/dev/clox/src/lox.cpp",
|
|
||||||
"output": "/Users/Shautvast/dev/clox/target/lox.cpp.o"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
18
makefile
18
makefile
|
|
@ -1,23 +1,27 @@
|
||||||
TARGET := ./target
|
TARGET := ./target
|
||||||
SRC := ./src
|
SRC := ./src
|
||||||
CC := clang++ -c -std=c++23 -Wall -Wextra -pedantic -Werror
|
CC := clang -c -std=c17
|
||||||
|
#-Wall -Wextra -pedantic -Werror
|
||||||
|
|
||||||
SRCS := $(shell find $(SRC) -name '*.c')
|
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)/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) -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)/lox.c.o: $(SRC)/lox.c $(TARGET)
|
||||||
$(CC) $< -o $@
|
$(CC) $< -o $@
|
||||||
|
|
||||||
$(TARGET):
|
$(TARGET):
|
||||||
|
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
class Error {
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::string message;
|
|
||||||
Error(std::string _message) : message(_message){};
|
|
||||||
};
|
|
||||||
|
|
||||||
class Void {};
|
|
||||||
|
|
||||||
template <typename R> using Result = std::variant<R, Error>;
|
|
||||||
|
|
||||||
template <typename R> bool is_err(Result<R> r) {
|
|
||||||
return std::holds_alternative<Error>(r);
|
|
||||||
}
|
|
||||||
template <typename R> bool is_ok(Result<R> r) {
|
|
||||||
return std::holds_alternative<R>(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename R> R Ok(Result<R> r) { return std::get<R>(r); }
|
|
||||||
/// enables rewrapping errors in a new Result type
|
|
||||||
template <typename R> Error Err(Result<R> r) { return std::get<Error>(r); }
|
|
||||||
template <typename R> std::string err_msg(Result<R> r) {
|
|
||||||
return std::get<Error>(r).message;
|
|
||||||
}
|
|
||||||
85
src/lox.c
Normal file
85
src/lox.c
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
#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);
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
setvbuf(stdout, NULL, _IONBF, 0);
|
||||||
|
if (argc > 2) {
|
||||||
|
puts("Usage: lox [script]");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
} else if (argc == 2) {
|
||||||
|
return run_file(argv[1]);
|
||||||
|
} else {
|
||||||
|
run_prompt();
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int run_file(char *filename) {
|
||||||
|
|
||||||
|
FILE *file = fopen(filename, "r");
|
||||||
|
if (file == NULL) {
|
||||||
|
printf("unable to open file '%s'\n", filename);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
char line[255];
|
||||||
|
|
||||||
|
char *content = malloc(1);
|
||||||
|
if (content == NULL) {
|
||||||
|
puts("Out of memory");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
content[0] = '\0';
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), file)) {
|
||||||
|
content = realloc(content, strlen(content) + strlen(line) + 1);
|
||||||
|
if (content == NULL) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
strcat(content, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
run(content);
|
||||||
|
|
||||||
|
// FREE UP
|
||||||
|
free(content);
|
||||||
|
|
||||||
|
// if (scan_result.had_error) {
|
||||||
|
// return 65;
|
||||||
|
// }
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_prompt(void) {
|
||||||
|
char line[255];
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
printf(">");
|
||||||
|
char *r = fgets(line, 255, stdin);
|
||||||
|
|
||||||
|
if (r == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = (int)strlen(line);
|
||||||
|
run(substring(line, 1, len - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(char *source) {
|
||||||
|
ScanResult scan_result = scan_tokens(source);
|
||||||
|
// tokenlist_print(&scan_result.token_list);
|
||||||
|
ExpressionList list = parse(&scan_result.token_list);
|
||||||
|
exprlist_print(&list);
|
||||||
|
}
|
||||||
78
src/lox.cpp
78
src/lox.cpp
|
|
@ -1,78 +0,0 @@
|
||||||
#include "parser.hpp"
|
|
||||||
#include "scanner.hpp"
|
|
||||||
#include "tokens.hpp"
|
|
||||||
#include <cstdbool>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using std::string, std::vector, std::ifstream, std::cin, std::cout;
|
|
||||||
|
|
||||||
void print_tokens(vector<Token> *list);
|
|
||||||
int run_file(string file);
|
|
||||||
void run_prompt(void);
|
|
||||||
Result<vector<Token>> run(string source);
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
if (argc > 2) {
|
|
||||||
puts("Usage: lox [script]");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
} else if (argc == 2) {
|
|
||||||
return run_file(argv[1]);
|
|
||||||
} else {
|
|
||||||
run_prompt();
|
|
||||||
}
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int run_file(string filename) {
|
|
||||||
string content;
|
|
||||||
ifstream file;
|
|
||||||
file.open(filename);
|
|
||||||
if (file.is_open()) {
|
|
||||||
file >> content;
|
|
||||||
} else {
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<vector<Token>> scan_result = run(content);
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void run_prompt(void) {
|
|
||||||
string line;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
cout << ">";
|
|
||||||
getline(cin, line);
|
|
||||||
auto scan_result = run(line.substr(0, line.length()));
|
|
||||||
|
|
||||||
if (is_ok(scan_result)) {
|
|
||||||
auto tokens = Ok(scan_result);
|
|
||||||
print_tokens(&tokens);
|
|
||||||
auto expression = (new Parser())->parse(get<vector<Token>>(scan_result));
|
|
||||||
if (is_ok(expression)) {
|
|
||||||
cout << Ok(expression)->as_string() << "\n";
|
|
||||||
} else {
|
|
||||||
cout << err_msg(expression) << "\n";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cout << err_msg(scan_result) << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<vector<Token>> run(string source) {
|
|
||||||
Scanner *scanner = new Scanner(source);
|
|
||||||
return scanner->scan_tokens();
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_tokens(vector<Token> *const list) {
|
|
||||||
for (Token token : *list) {
|
|
||||||
cout << token.as_string() << "(" << token.literal << "), ";
|
|
||||||
}
|
|
||||||
|
|
||||||
cout << "\n";
|
|
||||||
}
|
|
||||||
383
src/parser.c
Normal file
383
src/parser.c
Normal file
|
|
@ -0,0 +1,383 @@
|
||||||
|
#include "parser.h"
|
||||||
|
#include "tokens.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
bool is_at_end(void);
|
||||||
|
Token *peek(void);
|
||||||
|
size_t expr_toString(const Expression *expr, char *output, size_t outputSize,
|
||||||
|
size_t offset);
|
||||||
|
void expr_print(const Expression *expr);
|
||||||
|
Expression *declaration(void);
|
||||||
|
bool match1(TokenType t);
|
||||||
|
bool match2(TokenType type1, TokenType type2);
|
||||||
|
bool match3(TokenType type1, TokenType type2, TokenType type3);
|
||||||
|
bool match4(TokenType type1, TokenType type2, TokenType type3, TokenType type4);
|
||||||
|
bool check(TokenType t);
|
||||||
|
Token *advance(void);
|
||||||
|
Token *previous(void);
|
||||||
|
Expression *var_declaration(void);
|
||||||
|
Expression *expression(void);
|
||||||
|
Expression *statement(void);
|
||||||
|
Expression *printStatement(void);
|
||||||
|
ExpressionList block(void);
|
||||||
|
Expression *expressionStatement(void);
|
||||||
|
Expression *assignment(void);
|
||||||
|
Expression *equality(void);
|
||||||
|
Expression *comparison(void);
|
||||||
|
Expression *term(void);
|
||||||
|
Expression *factor(void);
|
||||||
|
Expression *unary(void);
|
||||||
|
Expression *primary(void);
|
||||||
|
Expression *newExpression(char *type);
|
||||||
|
Token *consume(TokenType type, char *message);
|
||||||
|
|
||||||
|
static TokenList *tokens;
|
||||||
|
static int current;
|
||||||
|
|
||||||
|
ExpressionList parse(TokenList *tokens_to_parse) {
|
||||||
|
ExpressionList statements;
|
||||||
|
exprlist_init(&statements);
|
||||||
|
|
||||||
|
tokens = tokens_to_parse;
|
||||||
|
current = 0;
|
||||||
|
|
||||||
|
while (!is_at_end()) {
|
||||||
|
exprlist_add(&statements, declaration());
|
||||||
|
}
|
||||||
|
return statements;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression *declaration(void) {
|
||||||
|
if (match1(VAR)) {
|
||||||
|
return var_declaration();
|
||||||
|
} else {
|
||||||
|
return statement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression *var_declaration(void) {
|
||||||
|
Token *name = consume(IDENTIFIER, "Expected a variable name");
|
||||||
|
Expression *initializer;
|
||||||
|
if (match1(EQUAL)) {
|
||||||
|
initializer = expression();
|
||||||
|
}
|
||||||
|
consume(SEMICOLON, "Expected semicolon");
|
||||||
|
Expression *variableStatement = newExpression("VariableStatement");
|
||||||
|
variableStatement->name = name->lexeme;
|
||||||
|
variableStatement->left = initializer;
|
||||||
|
return variableStatement;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression *statement(void) {
|
||||||
|
if (match1(PRINT)) {
|
||||||
|
return printStatement();
|
||||||
|
}
|
||||||
|
if (match1(LEFT_BRACE)) {
|
||||||
|
ExpressionList block_contents = block();
|
||||||
|
Expression *block = newExpression("Block");
|
||||||
|
block->value = &block_contents;
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
return expressionStatement();
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpressionList block(void) {
|
||||||
|
ExpressionList statements;
|
||||||
|
exprlist_init(&statements);
|
||||||
|
while (!check(RIGHT_BRACE) && !is_at_end()) {
|
||||||
|
exprlist_add(&statements, declaration());
|
||||||
|
}
|
||||||
|
return statements;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression *printStatement(void) {
|
||||||
|
Expression *value = expression();
|
||||||
|
consume(SEMICOLON, "Expected semicolon");
|
||||||
|
Expression *print = newExpression("PrintStmt");
|
||||||
|
print->left = value;
|
||||||
|
return print;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression *expressionStatement(void) {
|
||||||
|
Expression *value = expression();
|
||||||
|
consume(SEMICOLON, "Expected semicolon");
|
||||||
|
Expression *statement = newExpression("ExprStmt");
|
||||||
|
statement->value = &value;
|
||||||
|
return statement;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression *expression(void) { return assignment(); }
|
||||||
|
|
||||||
|
Expression *assignment(void) {
|
||||||
|
Expression *expr = equality();
|
||||||
|
if (match1(EQUAL)) {
|
||||||
|
Token *equals = previous();
|
||||||
|
Expression *value = assignment();
|
||||||
|
if (strcmp(expr->type, "Variable") == 0) {
|
||||||
|
Expression *assign = newExpression("AssignStmt");
|
||||||
|
assign->name = expr->name;
|
||||||
|
assign->value = &value;
|
||||||
|
return assign;
|
||||||
|
}
|
||||||
|
Expression *error = newExpression("Error");
|
||||||
|
error->operator= equals;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression *equality(void) {
|
||||||
|
Expression *expr = comparison();
|
||||||
|
while (match2(BANG_EQUAL, EQUAL_EQUAL)) {
|
||||||
|
Token *operator= previous();
|
||||||
|
Expression *right = comparison();
|
||||||
|
Expression *binary = newExpression("BinaryExpr");
|
||||||
|
binary->operator= operator;
|
||||||
|
binary->left = expr;
|
||||||
|
binary->right = right;
|
||||||
|
expr = binary;
|
||||||
|
}
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression *comparison(void) {
|
||||||
|
Expression *expr = term();
|
||||||
|
while (match4(GREATER, GREATER_EQUAL, LESS, LESS_EQUAL)) {
|
||||||
|
Token *operator= previous();
|
||||||
|
Expression *right = term();
|
||||||
|
Expression *binary = newExpression("BinaryExpr");
|
||||||
|
binary->operator= operator;
|
||||||
|
binary->left = expr;
|
||||||
|
binary->right = right;
|
||||||
|
expr = binary;
|
||||||
|
}
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression *term(void) {
|
||||||
|
Expression *expr = factor();
|
||||||
|
while (match2(MINUS, PLUS)) {
|
||||||
|
Token *operator= previous();
|
||||||
|
Expression *right = factor();
|
||||||
|
Expression *binary = newExpression("BinaryExpr");
|
||||||
|
binary->operator= operator;
|
||||||
|
binary->left = expr;
|
||||||
|
binary->right = right;
|
||||||
|
expr = binary;
|
||||||
|
}
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression *factor(void) {
|
||||||
|
Expression *expr = unary();
|
||||||
|
while (match2(SLASH, STAR)) {
|
||||||
|
Token *operator= previous();
|
||||||
|
Expression *right = unary();
|
||||||
|
Expression *binary = newExpression("BinaryExpr");
|
||||||
|
binary->operator= operator;
|
||||||
|
binary->left = expr;
|
||||||
|
binary->right = right;
|
||||||
|
expr = binary;
|
||||||
|
}
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression *unary(void) {
|
||||||
|
if (match2(BANG, MINUS)) {
|
||||||
|
Token *operator= previous();
|
||||||
|
Expression *right = unary();
|
||||||
|
Expression *unary = newExpression("Unary");
|
||||||
|
unary->operator= operator;
|
||||||
|
unary->right = right;
|
||||||
|
return unary;
|
||||||
|
}
|
||||||
|
return primary();
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression *primary(void) {
|
||||||
|
Expression *r = newExpression("Literal");
|
||||||
|
if (match1(FALSE)) {
|
||||||
|
r->name = "boolean";
|
||||||
|
r->value = "false";
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
if (match1(TRUE)) {
|
||||||
|
r->name = "boolean";
|
||||||
|
r->value = "true";
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match1(NIL)) {
|
||||||
|
r->name = "nil";
|
||||||
|
r->value = NULL;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check(NUMBER)) {
|
||||||
|
advance();
|
||||||
|
r->name = "number";
|
||||||
|
r->value = previous()->literal;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
if (check(STRING)) {
|
||||||
|
advance();
|
||||||
|
r->name = "string";
|
||||||
|
r->value = previous()->literal;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
if (match1(IDENTIFIER)) {
|
||||||
|
Expression *var = newExpression("Variable");
|
||||||
|
r->name = previous()->lexeme;
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
if (match1(LEFT_PAREN)) {
|
||||||
|
Expression *group = newExpression("Group");
|
||||||
|
consume(RIGHT_PAREN, "Expect ')' after expression.");
|
||||||
|
r->left = group;
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression *error = newExpression("Error");
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
Token *consume(TokenType type, char *message) {
|
||||||
|
if (check(type)) {
|
||||||
|
return advance();
|
||||||
|
}
|
||||||
|
printf("error\n");
|
||||||
|
Token *t = newToken();
|
||||||
|
t->type = ERROR;
|
||||||
|
t->lexeme = message;
|
||||||
|
|
||||||
|
tokenlist_add(tokens, t);
|
||||||
|
return tokenlist_last(tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool match1(TokenType type) {
|
||||||
|
if (check(type)) {
|
||||||
|
advance();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool match2(TokenType type1, TokenType type2) {
|
||||||
|
if (check(type1) || check(type2)) {
|
||||||
|
advance();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool match3(TokenType type1, TokenType type2, TokenType type3) {
|
||||||
|
if (check(type1) || check(type2) || check(type3)) {
|
||||||
|
advance();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool match4(TokenType type1, TokenType type2, TokenType type3,
|
||||||
|
TokenType type4) {
|
||||||
|
if (check(type1) || check(type2) || check(type3) || check(type4)) {
|
||||||
|
advance();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Token *advance(void) {
|
||||||
|
if (!is_at_end()) {
|
||||||
|
current += 1;
|
||||||
|
}
|
||||||
|
return previous();
|
||||||
|
}
|
||||||
|
|
||||||
|
Token *previous(void) { return tokenlist_get(tokens, current - 1); }
|
||||||
|
|
||||||
|
bool check(TokenType type) { return peek()->type == type; }
|
||||||
|
|
||||||
|
bool is_at_end(void) { return peek()->type == END_OF_FILE; }
|
||||||
|
|
||||||
|
Token *peek(void) { return tokenlist_get(tokens, current); }
|
||||||
|
|
||||||
|
void exprlist_init(ExpressionList *list) {
|
||||||
|
list->expressions = malloc(sizeof(Expression) * 32);
|
||||||
|
list->size = 0;
|
||||||
|
list->capacity = 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
void exprlist_add(ExpressionList *list, Expression *value) {
|
||||||
|
if (list->size >= list->capacity) {
|
||||||
|
list->capacity *= 2;
|
||||||
|
list->expressions =
|
||||||
|
realloc(list->expressions, sizeof(Expression) * list->capacity);
|
||||||
|
}
|
||||||
|
list->expressions[list->size] = value;
|
||||||
|
list->size += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression *exprlist_get(ExpressionList *list, int index) {
|
||||||
|
if (index >= list->size || index < 0) {
|
||||||
|
printf("Index %d out of bounds for list of size %d\n", index, list->size);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return list->expressions[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void exprlist_print(ExpressionList *list) {
|
||||||
|
for (int i = 0; i < list->size; i++) {
|
||||||
|
Expression *expr = exprlist_get(list, i);
|
||||||
|
expr_print(expr);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void exprlist_free(ExpressionList *list) { free(list->expressions); }
|
||||||
|
|
||||||
|
void expr_print(const Expression *expr) {
|
||||||
|
printf("Expr[type: %s", expr->type);
|
||||||
|
if (expr->left != NULL) {
|
||||||
|
printf(", left: ");
|
||||||
|
expr_print(expr->left);
|
||||||
|
}
|
||||||
|
if (expr->right != NULL) {
|
||||||
|
printf(", right: ");
|
||||||
|
expr_print(expr->right);
|
||||||
|
}
|
||||||
|
if (expr->operator!= NULL && expr->operator->lexeme != NULL) {
|
||||||
|
printf(", operator: %s", expr->operator->lexeme);
|
||||||
|
}
|
||||||
|
if (expr->name != NULL) {
|
||||||
|
printf(", name: %s", expr->name);
|
||||||
|
if (strcmp(expr->name, "string") == 0 ||
|
||||||
|
strcmp(expr->name, "number") == 0 ||
|
||||||
|
strcmp(expr->name, "boolean") == 0) {
|
||||||
|
printf(", value: %s", (char *)expr->value);
|
||||||
|
}
|
||||||
|
if (strcmp(expr->name, "nil") == 0 && expr->value == NULL) {
|
||||||
|
printf(", value: NULL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("]");
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression *newExpression(char *type) {
|
||||||
|
Expression *e = malloc(sizeof(Expression));
|
||||||
|
e->type = type;
|
||||||
|
e->left = NULL;
|
||||||
|
e->right = NULL;
|
||||||
|
e->name = NULL;
|
||||||
|
e->operator= NULL;
|
||||||
|
e->value = NULL;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
212
src/parser.cpp
212
src/parser.cpp
|
|
@ -1,212 +0,0 @@
|
||||||
#include "parser.hpp"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
using std::string, std::to_string, std::vector;
|
|
||||||
|
|
||||||
Expression::~Expression() = default;
|
|
||||||
|
|
||||||
// class Binary
|
|
||||||
|
|
||||||
string Binary::as_string() const {
|
|
||||||
return "(" + token_name(op->tokentype) + " " + left->as_string() + " " +
|
|
||||||
right->as_string() + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
Binary::Binary(Expression *_left, Token *_operator, Expression *_right)
|
|
||||||
: left(_left), op(_operator), right(_right){};
|
|
||||||
|
|
||||||
Binary::~Binary() = default;
|
|
||||||
|
|
||||||
// class Grouping
|
|
||||||
|
|
||||||
string Grouping::as_string() const { return "(" + expr->as_string() + ")"; }
|
|
||||||
|
|
||||||
Grouping::Grouping(Expression *_expr) : expr(_expr){};
|
|
||||||
|
|
||||||
Grouping::~Grouping() = default;
|
|
||||||
|
|
||||||
// class Unary
|
|
||||||
|
|
||||||
string Unary::as_string() const {
|
|
||||||
return token_name(op->tokentype) + right->as_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
Unary::Unary(Token *_operator, Expression *_right)
|
|
||||||
: op(_operator), right(_right){};
|
|
||||||
|
|
||||||
Unary::~Unary() = default;
|
|
||||||
|
|
||||||
// class Literal
|
|
||||||
string Literal::as_string() const {
|
|
||||||
string text;
|
|
||||||
if (holds_alternative<string>(value)) {
|
|
||||||
return "\"" + get<string>(value) + "\"";
|
|
||||||
}
|
|
||||||
if (holds_alternative<double_t>(value)) {
|
|
||||||
return to_string(get<double_t>(value));
|
|
||||||
}
|
|
||||||
if (holds_alternative<bool>(value)) {
|
|
||||||
return get<bool>(value) ? "True" : "False";
|
|
||||||
}
|
|
||||||
if (holds_alternative<NilType>(value)) {
|
|
||||||
return "NULL";
|
|
||||||
}
|
|
||||||
return "unexpected";
|
|
||||||
}
|
|
||||||
|
|
||||||
// class Parser
|
|
||||||
Result<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;
|
|
||||||
};
|
|
||||||
|
|
||||||
Result<Token *> Parser::consume(Token::Type typ, string message) {
|
|
||||||
if (check(typ)) {
|
|
||||||
return advance();
|
|
||||||
}
|
|
||||||
return error(peek(), message);
|
|
||||||
}
|
|
||||||
|
|
||||||
Error Parser::error(Token token, string message) {
|
|
||||||
std::cout << token.as_string() << " " << message;
|
|
||||||
return Error(message); // TODO no exceptions
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<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 NilType());
|
|
||||||
if (match(1, Token::Type::NUMBER)) {
|
|
||||||
return new Literal(stod(previous()->literal));
|
|
||||||
}
|
|
||||||
if (match(1, Token::Type::STRING)) {
|
|
||||||
return new Literal(previous()->literal);
|
|
||||||
}
|
|
||||||
if (match(1, Token::Type::LEFT_PAREN)) {
|
|
||||||
auto expr = expression();
|
|
||||||
Result<Token *> r = consume(Token::Type::RIGHT_PAREN, "Expect ')'.");
|
|
||||||
if (is_err(r)) {
|
|
||||||
return Err(r);
|
|
||||||
}
|
|
||||||
return new Grouping(Ok(expr));
|
|
||||||
}
|
|
||||||
return Error("Expected an expression");
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<Expression *> Parser::unary() {
|
|
||||||
if (match(2, Token::BANG, Token::Type::MINUS)) {
|
|
||||||
Token *op = previous();
|
|
||||||
Result<Expression *> right = unary();
|
|
||||||
if (is_ok(right)) {
|
|
||||||
return new Unary(op, Ok(right));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return primary();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<Expression *> Parser::expression() { return equality(); }
|
|
||||||
|
|
||||||
Result<Expression *> Parser::factor() {
|
|
||||||
Result<Expression *> expr = unary();
|
|
||||||
if (is_err(expr)) {
|
|
||||||
return expr;
|
|
||||||
}
|
|
||||||
while (match(2, Token::Type::SLASH, Token::Type::STAR)) {
|
|
||||||
Token *op = previous();
|
|
||||||
auto right = unary();
|
|
||||||
if (is_err(right)) {
|
|
||||||
return right;
|
|
||||||
}
|
|
||||||
expr = new Binary(Ok(expr), op, Ok(right));
|
|
||||||
}
|
|
||||||
return expr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<Expression *> Parser::term() {
|
|
||||||
auto expr = factor();
|
|
||||||
if (is_err(expr)) {
|
|
||||||
return expr;
|
|
||||||
}
|
|
||||||
while (match(2, Token::Type::MINUS, Token::Type::PLUS)) {
|
|
||||||
Token *op = previous();
|
|
||||||
auto right = factor();
|
|
||||||
if (is_err(right)) {
|
|
||||||
return right;
|
|
||||||
}
|
|
||||||
expr = new Binary(Ok(expr), op, Ok(right));
|
|
||||||
}
|
|
||||||
return expr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<Expression *> Parser::equality(void) {
|
|
||||||
auto expr = comparison();
|
|
||||||
if (is_err(expr)) {
|
|
||||||
return expr;
|
|
||||||
}
|
|
||||||
while (match(2, Token::Type::BANG_EQUAL, Token::Type::BANG_EQUAL)) {
|
|
||||||
Token *op = previous();
|
|
||||||
auto right = comparison();
|
|
||||||
if (is_err(right)) {
|
|
||||||
return right;
|
|
||||||
}
|
|
||||||
return new Binary(Ok(expr), op, Ok(right));
|
|
||||||
}
|
|
||||||
return expr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<Expression *> Parser::comparison(void) {
|
|
||||||
auto expr = term();
|
|
||||||
if (is_err(expr)) {
|
|
||||||
return expr;
|
|
||||||
}
|
|
||||||
while (match(4, Token::Type::GREATER, Token::Type::GREATER_EQUAL,
|
|
||||||
Token::Type::LESS, Token::Type::LESS_EQUAL)) {
|
|
||||||
Token *op = previous();
|
|
||||||
auto right = term();
|
|
||||||
if (is_err(right)) {
|
|
||||||
return right;
|
|
||||||
}
|
|
||||||
expr = new Binary(Ok(expr), op, Ok(right));
|
|
||||||
}
|
|
||||||
return expr;
|
|
||||||
}
|
|
||||||
30
src/parser.h
Normal file
30
src/parser.h
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
#include "tokens.h"
|
||||||
|
|
||||||
|
typedef struct Expression Expression;
|
||||||
|
|
||||||
|
struct Expression {
|
||||||
|
char *type;
|
||||||
|
Expression *left;
|
||||||
|
Expression *right;
|
||||||
|
Token *operator;
|
||||||
|
char *name;
|
||||||
|
void *value;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct ExpressionList {
|
||||||
|
Expression **expressions;
|
||||||
|
int size;
|
||||||
|
int capacity;
|
||||||
|
} ExpressionList;
|
||||||
|
|
||||||
|
ExpressionList parse(TokenList *tokens);
|
||||||
|
|
||||||
|
void exprlist_init(ExpressionList *list);
|
||||||
|
|
||||||
|
void exprlist_add(ExpressionList *list, Expression *value);
|
||||||
|
|
||||||
|
Expression *exprlist_get(ExpressionList *list, int index);
|
||||||
|
|
||||||
|
void exprlist_print(ExpressionList *list);
|
||||||
|
|
||||||
|
void exprlist_free(ExpressionList *list);
|
||||||
115
src/parser.hpp
115
src/parser.hpp
|
|
@ -1,115 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "error.hpp"
|
|
||||||
#include "tokens.hpp"
|
|
||||||
#include <memory>
|
|
||||||
#include <variant>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
enum class ExprType { Binary, Grouping, Unary, Literal, None };
|
|
||||||
|
|
||||||
/// Base class for expressions
|
|
||||||
class Expression {
|
|
||||||
public:
|
|
||||||
virtual std::string as_string() const = 0; // get string rep for debugging
|
|
||||||
virtual ~Expression();
|
|
||||||
};
|
|
||||||
|
|
||||||
/// An expression with two operands
|
|
||||||
class Binary : public Expression {
|
|
||||||
std::unique_ptr<Expression> left;
|
|
||||||
std::unique_ptr<Token> op;
|
|
||||||
std::unique_ptr<Expression> right;
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::string as_string() const override;
|
|
||||||
Binary(Expression *_left, Token *_operator, Expression *_right);
|
|
||||||
~Binary();
|
|
||||||
};
|
|
||||||
|
|
||||||
/// An expression between parentheses
|
|
||||||
class Grouping : public Expression {
|
|
||||||
std::unique_ptr<Expression> expr;
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::string as_string() const override;
|
|
||||||
Grouping(Expression *_expr);
|
|
||||||
~Grouping();
|
|
||||||
};
|
|
||||||
|
|
||||||
/// An expression with one operand (operator is `-` or `!`)
|
|
||||||
class Unary : public Expression {
|
|
||||||
std::unique_ptr<Token> op;
|
|
||||||
std::unique_ptr<Expression> right;
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::string as_string() const override;
|
|
||||||
Unary(Token *_operator, Expression *_right);
|
|
||||||
~Unary();
|
|
||||||
};
|
|
||||||
|
|
||||||
/// empty class that is the type of the Nil value
|
|
||||||
class NilType {};
|
|
||||||
typedef std::variant<double_t, bool, std::string, NilType> Value;
|
|
||||||
|
|
||||||
/// encapsulates a value: numeric, string etc
|
|
||||||
class Literal : public Expression {
|
|
||||||
public:
|
|
||||||
enum ValueType { String, Numeric, Boolean, Nil } valuetype;
|
|
||||||
|
|
||||||
Value value;
|
|
||||||
|
|
||||||
Literal(NilType v) : value(v){};
|
|
||||||
Literal(double_t _numeric) : value(_numeric){};
|
|
||||||
Literal(std::string _str) : value(_str){};
|
|
||||||
Literal(bool _boolean) : value(_boolean){};
|
|
||||||
|
|
||||||
std::string as_string() const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Parser {
|
|
||||||
std::vector<Expression> expressions;
|
|
||||||
std::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
|
|
||||||
Result<Token *> consume(Token::Type typ, std::string message);
|
|
||||||
/// throws an exception for the specified token with the specified message
|
|
||||||
Error error(Token token, std::string message);
|
|
||||||
/// tries to parse the token as a primary value (string, number etc)
|
|
||||||
Result<Expression *> primary();
|
|
||||||
/// tries to parse the tokens as a unary expression
|
|
||||||
Result<Expression *> unary();
|
|
||||||
/// tries to parse the tokens
|
|
||||||
Result<Expression *> expression();
|
|
||||||
/// tries to parse the tokens as a multiplication or division
|
|
||||||
Result<Expression *> factor();
|
|
||||||
/// tries to parse the tokens as an addition or subtraction
|
|
||||||
Result<Expression *> term();
|
|
||||||
/// tries to parse the tokens as an equality (`a == b` / `a!= b`)
|
|
||||||
Result<Expression *> equality();
|
|
||||||
/// tries to parse the tokens as a comparison (`a > b` / `a >= b` / `a < b`
|
|
||||||
/// / `a <= b` )
|
|
||||||
Result<Expression *> comparison();
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// public method for parsing expressions
|
|
||||||
Result<Expression *> parse(std::vector<Token> tokenlist);
|
|
||||||
};
|
|
||||||
240
src/scanner.c
Normal file
240
src/scanner.c
Normal file
|
|
@ -0,0 +1,240 @@
|
||||||
|
#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(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
211
src/scanner.cpp
|
|
@ -1,211 +0,0 @@
|
||||||
#include "scanner.hpp"
|
|
||||||
#include "error.hpp"
|
|
||||||
#include "tokens.hpp"
|
|
||||||
#include <cstdbool>
|
|
||||||
#include <iostream>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using std::string, std::map, std::vector, std::cout;
|
|
||||||
|
|
||||||
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>()) {}
|
|
||||||
|
|
||||||
Result<vector<Token>> Scanner::scan_tokens() {
|
|
||||||
while (current_pos < source.length()) {
|
|
||||||
start = current_pos;
|
|
||||||
Result<Void> r = scan_token();
|
|
||||||
if (is_err(r)) {
|
|
||||||
return Err(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return token_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<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 {
|
|
||||||
return Error{"Unexpected character."};
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return Void{};
|
|
||||||
}
|
|
||||||
|
|
||||||
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()) {
|
|
||||||
report("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::report(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
45
src/scanner.h
Normal 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
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "error.hpp"
|
|
||||||
#include "tokens.hpp"
|
|
||||||
#include <cstdbool>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
bool had_error;
|
|
||||||
std::vector<Token> token_list;
|
|
||||||
} ScanResult;
|
|
||||||
|
|
||||||
class Scanner {
|
|
||||||
private:
|
|
||||||
bool had_error;
|
|
||||||
size_t current_pos;
|
|
||||||
int start;
|
|
||||||
int current_line;
|
|
||||||
std::string source;
|
|
||||||
std::vector<Token> token_list;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Scanner(std::string s);
|
|
||||||
Result<std::vector<Token>> scan_tokens();
|
|
||||||
Result<Void> scan_token();
|
|
||||||
void add_token(Token::Type type);
|
|
||||||
void add_token(Token::Type type, std::string literal);
|
|
||||||
char advance();
|
|
||||||
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 report(std::string message);
|
|
||||||
void report(std::string where, std::string message);
|
|
||||||
};
|
|
||||||
52
src/tokens.c
Normal file
52
src/tokens.c
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
#include "tokens.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
Token *newToken() {
|
||||||
|
Token *token = malloc(sizeof(Token));
|
||||||
|
if (token == NULL) {
|
||||||
|
printf("can't allocate memory");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tokenlist_init(TokenList *list) {
|
||||||
|
list->tokens = malloc(sizeof(Token) * 32);
|
||||||
|
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);
|
||||||
|
if (token->literal != NULL) {
|
||||||
|
printf("%s(x:%s,l:%s), ", token_name(token->type), token->lexeme,
|
||||||
|
(char *)token->literal);
|
||||||
|
} else {
|
||||||
|
printf("%s(l:%s)", token_name(token->type), token->lexeme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void tokenlist_free(TokenList *list) { free(list->tokens); }
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
#include "tokens.hpp"
|
|
||||||
|
|
||||||
using std::string;
|
|
||||||
|
|
||||||
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[] = {
|
|
||||||
"EOF", "(", ")", "{", "}", ",", "*",
|
|
||||||
"-", "+", ";", "/", "*", "!", "!=",
|
|
||||||
"=", "==", ">", ">=", "<", "<=", "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() const { return token_name(tokentype); }
|
|
||||||
87
src/tokens.h
Normal file
87
src/tokens.h
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
#ifndef TOKENS_H
|
||||||
|
#define TOKENS_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 {
|
||||||
|
TokenType type;
|
||||||
|
char *lexeme;
|
||||||
|
void *literal;
|
||||||
|
int line;
|
||||||
|
} Token;
|
||||||
|
|
||||||
|
typedef struct TokenList {
|
||||||
|
Token **tokens;
|
||||||
|
int size;
|
||||||
|
int capacity;
|
||||||
|
} TokenList;
|
||||||
|
|
||||||
|
Token *newToken();
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class Token {
|
|
||||||
public:
|
|
||||||
std::string lexeme;
|
|
||||||
std::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;
|
|
||||||
|
|
||||||
std::string as_string() const;
|
|
||||||
|
|
||||||
Token(Token::Type _tokentype, std::string _lexeme, std::string _literal,
|
|
||||||
int line);
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string token_name(Token::Type tokentype);
|
|
||||||
19
src/utils.c
Normal file
19
src/utils.c
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.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
6
src/utils.h
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef UTILS_H
|
||||||
|
#define UTILS_H
|
||||||
|
|
||||||
|
char* substring(char* string, int position, int length);
|
||||||
|
|
||||||
|
#endif
|
||||||
Loading…
Add table
Reference in a new issue