interpreter working nicely

This commit is contained in:
Shautvast 2024-12-11 21:36:36 +01:00
parent 5df54044f8
commit 1c092acd40
11 changed files with 448 additions and 76 deletions

View file

@ -6,8 +6,8 @@ CC := clang -c -std=c17
SRCS := $(shell find $(SRC) -name '*.c')
OBJS := $(SRCS:%=$(TARGET)/%.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.c.o -L$(TARGET) -lparser.c.o -ltokens.c.o -lscanner.c.o -lutils.c.o -o $(TARGET)/lox
$(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)/utils.c.o: $(SRC)/utils.c
$(CC) $< -o $@
@ -21,6 +21,9 @@ $(TARGET)/scanner.c.o: $(SRC)/scanner.c
$(TARGET)/parser.c.o: $(SRC)/parser.c
$(CC) $< -o $@
$(TARGET)/interpreter.c.o: $(SRC)/interpreter.c
$(CC) $< -o $@
$(TARGET)/lox.c.o: $(SRC)/lox.c $(TARGET)
$(CC) $< -o $@

248
src/interpreter.c Normal file
View file

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

28
src/interpreter.h Normal file
View file

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

View file

@ -1,3 +1,4 @@
#include "interpreter.h"
#include "parser.h"
#include "scanner.h"
#include "utils.h"
@ -9,8 +10,11 @@
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]");
@ -80,6 +84,7 @@ void run_prompt(void) {
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);
ExpressionList *list = parse(&scan_result.token_list);
// exprlist_print(list);
interpret(environment, list);
}

View file

@ -1,5 +1,4 @@
#include "parser.h"
#include "tokens.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@ -7,8 +6,6 @@
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);
@ -22,7 +19,7 @@ Expression *var_declaration(void);
Expression *expression(void);
Expression *statement(void);
Expression *printStatement(void);
ExpressionList block(void);
ExpressionList *parse_block(void);
Expression *expressionStatement(void);
Expression *assignment(void);
Expression *equality(void);
@ -37,15 +34,14 @@ Token *consume(TokenType type, char *message);
static TokenList *tokens;
static int current;
ExpressionList parse(TokenList *tokens_to_parse) {
ExpressionList statements;
exprlist_init(&statements);
ExpressionList *parse(TokenList *tokens_to_parse) {
ExpressionList *statements = newExpressionList();
tokens = tokens_to_parse;
current = 0;
while (!is_at_end()) {
exprlist_add(&statements, declaration());
exprlist_add(statements, declaration());
}
return statements;
}
@ -60,12 +56,12 @@ Expression *declaration(void) {
Expression *var_declaration(void) {
Token *name = consume(IDENTIFIER, "Expected a variable name");
Expression *initializer;
Expression *initializer = NULL;
if (match1(EQUAL)) {
initializer = expression();
}
consume(SEMICOLON, "Expected semicolon");
Expression *variableStatement = newExpression("VariableStatement");
Expression *variableStatement = newExpression("VariableStmt");
variableStatement->name = name->lexeme;
variableStatement->left = initializer;
return variableStatement;
@ -76,24 +72,22 @@ Expression *statement(void) {
return printStatement();
}
if (match1(LEFT_BRACE)) {
ExpressionList block_contents = block();
Expression *block = newExpression("Block");
block->value = &block_contents;
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();
}
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");
@ -106,7 +100,7 @@ Expression *expressionStatement(void) {
Expression *value = expression();
consume(SEMICOLON, "Expected semicolon");
Expression *statement = newExpression("ExprStmt");
statement->value = &value;
statement->left = value;
return statement;
}
@ -120,7 +114,7 @@ Expression *assignment(void) {
if (strcmp(expr->type, "Variable") == 0) {
Expression *assign = newExpression("AssignStmt");
assign->name = expr->name;
assign->value = &value;
assign->left = value;
return assign;
}
Expression *error = newExpression("Error");
@ -201,13 +195,11 @@ Expression *unary(void) {
Expression *primary(void) {
Expression *r = newExpression("Literal");
if (match1(FALSE)) {
r->name = "boolean";
r->value = "false";
r->value = newBoolean(false);
return r;
}
if (match1(TRUE)) {
r->name = "boolean";
r->value = "true";
r->value = newBoolean(true);
return r;
}
@ -219,25 +211,24 @@ Expression *primary(void) {
if (check(NUMBER)) {
advance();
r->name = "number";
r->value = previous()->literal;
r->value = newNumber(strtod(previous()->literal, NULL));
return r;
}
if (check(STRING)) {
advance();
r->name = "string";
r->value = previous()->literal;
r->value = newString(previous()->literal);
return r;
}
if (match1(IDENTIFIER)) {
Expression *var = newExpression("Variable");
r->name = previous()->lexeme;
var->name = previous()->lexeme;
return var;
}
if (match1(LEFT_PAREN)) {
Expression *expr = expression();
Expression *group = newExpression("Group");
consume(RIGHT_PAREN, "Expect ')' after expression.");
r->left = group;
group->left = expr;
return group;
}
@ -246,10 +237,11 @@ Expression *primary(void) {
}
Token *consume(TokenType type, char *message) {
// printf("%s==%s\n", token_name(type), token_name(peek()->type));
if (check(type)) {
return advance();
}
printf("error\n");
Token *t = newToken();
t->type = ERROR;
t->lexeme = message;
@ -310,10 +302,21 @@ bool is_at_end(void) { return peek()->type == END_OF_FILE; }
Token *peek(void) { return tokenlist_get(tokens, current); }
void exprlist_init(ExpressionList *list) {
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) {
@ -322,8 +325,7 @@ void exprlist_add(ExpressionList *list, Expression *value) {
list->expressions =
realloc(list->expressions, sizeof(Expression) * list->capacity);
}
list->expressions[list->size] = value;
list->size += 1;
list->expressions[list->size++] = value;
}
Expression *exprlist_get(ExpressionList *list, int index) {
@ -359,15 +361,13 @@ void expr_print(const Expression *expr) {
}
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");
}
}
if (expr->value != NULL) {
printf(", value: %s", value_string(expr->value));
}
printf("]");
}
@ -379,5 +379,59 @@ Expression *newExpression(char *type) {
e->name = NULL;
e->operator= NULL;
e->value = NULL;
e->block = NULL;
return e;
}
Value *newString(char *string) {
Value *value = newValue();
value->type = STRINGTYPE;
value->value.string = string;
return value;
}
Value *newBoolean(bool boolean) {
Value *value = newValue();
value->type = BOOLEANTYPE;
value->value.boolean = boolean;
return value;
}
Value *newNumber(double number) {
Value *value = newValue();
value->type = NUMBERTYPE;
value->value.number = number;
return value;
}
Value *newValue(void) {
Value *value = malloc(sizeof(Value));
if (value == NULL) {
printf("can't allocate memory for Value");
exit(1);
}
return value;
}
char *d_to_s(double d) {
char *str = (char *)malloc(50);
if (str == NULL) {
puts("cannot allocate memory for string");
exit(1);
}
snprintf(str, sizeof(str), "%lf", d); //
return str;
}
const char *value_string(Value *v) {
switch (v->type) {
case STRINGTYPE:
return v->value.string;
case BOOLEANTYPE:
return v->value.boolean ? "true" : "false";
case NUMBERTYPE:
return d_to_s(v->value.number);
case EXPR:
return v->value.expr->type;
}
}

View file

@ -1,6 +1,24 @@
#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;
@ -8,7 +26,8 @@ struct Expression {
Expression *right;
Token *operator;
char *name;
void *value;
Value *value;
ExpressionList *block;
};
typedef struct ExpressionList {
@ -17,9 +36,15 @@ typedef struct ExpressionList {
int capacity;
} ExpressionList;
ExpressionList parse(TokenList *tokens);
const char *value_string(Value *v);
Value *newValue(void);
Value *newString(char *string);
Value *newNumber(double number);
Value *newBoolean(bool boolean);
void exprlist_init(ExpressionList *list);
ExpressionList *parse(TokenList *tokens);
ExpressionList *newExpressionList();
// void exprlist_init(ExpressionList *list);
void exprlist_add(ExpressionList *list, Expression *value);
@ -28,3 +53,4 @@ Expression *exprlist_get(ExpressionList *list, int index);
void exprlist_print(ExpressionList *list);
void exprlist_free(ExpressionList *list);
#endif

View file

@ -109,6 +109,9 @@ static void scan_token(void) {
case '-':
add_token(MINUS);
break;
case '*':
add_token(STAR);
break;
case '!':
add_token(match('=') ? BANG_EQUAL : BANG);
break;

View file

@ -1,11 +1,12 @@
#include "tokens.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
Token *newToken() {
Token *newToken(void) {
Token *token = malloc(sizeof(Token));
if (token == NULL) {
printf("can't allocate memory");
printf("can't allocate memory for Token");
exit(1);
}
return token;
@ -13,6 +14,10 @@ Token *newToken() {
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;
}
@ -39,12 +44,7 @@ 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("%s(%s)", token_name(token->type), token->lexeme);
}
printf("\n");
}

View file

@ -1,5 +1,8 @@
#ifndef TOKENS_H
#define TOKENS_H
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
typedef enum {
LEFT_PAREN,
@ -58,10 +61,10 @@ static inline const char *token_name(TokenType type) {
return tokens[type];
}
typedef struct {
typedef struct Token {
TokenType type;
char *lexeme;
void *literal;
char *literal;
int line;
} Token;
@ -71,7 +74,7 @@ typedef struct TokenList {
int capacity;
} TokenList;
Token *newToken();
Token *newToken(void);
void tokenlist_init(TokenList *list);

View file

@ -1,5 +1,7 @@
#include <stdlib.h>
#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);