Support statements

This commit is contained in:
Night Kaly 2025-01-06 03:53:15 +00:00
parent 3cc1b4b0d4
commit 9818c21097
Signed by: night0721
SSH key fingerprint: SHA256:B/hgVwUoBpx5vdNsXl9w8XwZljA9766uk6T4ubZp5HM
10 changed files with 405 additions and 271 deletions

View file

@ -4,24 +4,28 @@
#include "lexer.h" #include "lexer.h"
/* /*
expression literal expression literal | unary | binary | grouping ;
| unary literal NUMBER | STRING | "true" | "false" | "nil" ;
| binary grouping "(" expression ")" ;
| grouping ; unary ( "-" | "!" ) expression ;
literal NUMBER | STRING | "true" | "false" | "nil" ; binary expression operator expression ;
grouping "(" expression ")" ; operator "==" | "!=" | "<" | "<=" | ">" | ">=" | "+" | "-" | "*" | "/" ;
unary ( "-" | "!" ) expression ;
binary expression operator expression ;
operator "==" | "!=" | "<" | "<=" | ">" | ">="
| "+" | "-" | "*" | "/" ;
*/ */
typedef enum { typedef enum {
BINARY, EXPR_ASSIGN,
UNARY, EXPR_BINARY,
LITERAL, EXPR_CALL,
GROUPING, EXPR_GET,
} expr_type; EXPR_GROUPING,
EXPR_LITERAL,
EXPR_LOGICAL,
EXPR_SET,
EXPR_SUPER,
EXPR_THIS,
EXPR_UNARY,
EXPR_VARIABLE,
} expr_type_t;
typedef enum { typedef enum {
VAL_BOOL, VAL_BOOL,
@ -40,29 +44,62 @@ typedef struct {
} value_t; } value_t;
typedef struct expr_t { typedef struct expr_t {
expr_type type; expr_type_t type;
int line; int line;
union { union {
struct { struct {
token_t binary_op; token_t name;
struct expr_t *value;
} assign;
struct {
token_t operator;
struct expr_t *left; struct expr_t *left;
struct expr_t *right; struct expr_t *right;
} binary; } binary;
struct { struct {
token_t unary_op; struct expr_t *callee;
struct expr_t *right; token_t paren;
} unary; struct expr_t **arguments;
} call;
struct {
struct expr_t *object;
token_t name;
} get;
struct {
struct expr_t *expression;
} grouping;
struct { struct {
value_t value; value_t value;
} literal; } literal;
struct { struct {
struct expr_t *expression; token_t operator;
} grouping; struct expr_t *left;
struct expr_t *right;
} logical;
struct {
struct expr_t *object;
token_t name;
struct expr_t *value;
} set;
struct {
token_t keyword;
token_t method;
} super;
struct {
token_t keyword;
} this;
struct {
token_t operator;
struct expr_t *right;
} unary;
struct {
token_t name;
} variable;
} as; } as;
} expr_t; } expr_t;
expr_t *create_binary_expr(token_t *binary_op, expr_t *left, expr_t *right); expr_t *create_binary_expr(token_t *operator, expr_t *left, expr_t *right);
expr_t *create_unary_expr(token_t *unary_op, expr_t *right); expr_t *create_unary_expr(token_t *operator, expr_t *right);
expr_t *create_literal_expr(token_t *token); expr_t *create_literal_expr(token_t *token);
expr_t *create_grouping_expr(expr_t *expression); expr_t *create_grouping_expr(expr_t *expression);
void print_ast(expr_t *expr); void print_ast(expr_t *expr);

View file

@ -2,8 +2,10 @@
#define INTERPRETER_H #define INTERPRETER_H
#include "ast.h" #include "ast.h"
#include "stmt.h"
value_t evaluate(expr_t *expr); value_t evaluate(expr_t *expr);
void print_value(value_t *value); void print_value(value_t *value);
void print_statements(stmt_array_t *array);
#endif #endif

View file

@ -5,23 +5,23 @@
typedef enum { typedef enum {
// Single-character tokens // Single-character tokens
LEFT_PAREN, RIGHT_PAREN, LEFT_BRACE, RIGHT_BRACE, TOKEN_LEFT_PAREN, TOKEN_RIGHT_PAREN, TOKEN_LEFT_BRACE, TOKEN_RIGHT_BRACE,
COMMA, DOT, MINUS, PLUS, SEMICOLON, SLASH, STAR, TOKEN_COMMA, TOKEN_DOT, TOKEN_MINUS, TOKEN_PLUS, TOKEN_SEMICOLON,
TOKEN_SLASH, TOKEN_STAR,
// One or two character tokens // One or two character tokens
BANG, BANG_EQUAL, TOKEN_BANG, TOKEN_BANG_EQUAL, TOKEN_EQUAL, TOKEN_EQUAL_EQUAL, TOKEN_GREATER,
EQUAL, EQUAL_EQUAL, TOKEN_GREATER_EQUAL, TOKEN_LESS, TOKEN_LESS_EQUAL,
GREATER, GREATER_EQUAL,
LESS, LESS_EQUAL,
// Literals // Literals
IDENTIFIER, STRING, NUMBER, TOKEN_IDENTIFIER, TOKEN_STRING, TOKEN_NUMBER,
// Keywords // Keywords
AND, CLASS, ELSE, FALSE, FUN, FOR, IF, NIL, OR, TOKEN_AND, TOKEN_CLASS, TOKEN_ELSE, TOKEN_FALSE, TOKEN_FUN, TOKEN_FOR,
PRINT, RETURN, SUPER, THIS, TRUE, VAR, WHILE, TOKEN_IF, TOKEN_NIL, TOKEN_OR, TOKEN_PRINT, TOKEN_RETURN, TOKEN_SUPER,
TOKEN_THIS, TOKEN_TRUE, TOKEN_VAR, TOKEN_WHILE,
END_OF_FILE TOKEN_EOF
} token_type_t; } token_type_t;
typedef struct { typedef struct {

View file

@ -7,16 +7,17 @@ equality → comparison ( ( "!=" | "==" ) comparison )* ;
comparison term ( ( ">" | ">=" | "<" | "<=" ) term )* ; comparison term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
term factor ( ( "-" | "+" ) factor )* ; term factor ( ( "-" | "+" ) factor )* ;
factor unary ( ( "/" | "*" ) unary )* ; factor unary ( ( "/" | "*" ) unary )* ;
unary ( "!" | "-" ) unary unary ( "!" | "-" ) unary | primary ;
| primary ; primary NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" ;
primary NUMBER | STRING | "true" | "false" | "nil"
| "(" expression ")" ;
*/ */
#include "ast.h" #include "ast.h"
#include "lexer.h" #include "lexer.h"
#include "stmt.h"
expr_t *parse(token_t *tks); stmt_array_t *parse(token_t *tks);
expr_t *parse_expr(token_t *tks);
void free_expr(expr_t *expr); void free_expr(expr_t *expr);
void free_statements(stmt_array_t *array);
#endif #endif

View file

@ -2,7 +2,10 @@
#define STMT_H #define STMT_H
#include "ast.h" #include "ast.h"
#include "scanner.h" #include "lexer.h"
#define DEFAULT_STMTS_SIZE 512
typedef enum { typedef enum {
STMT_BLOCK, STMT_BLOCK,
STMT_CLASS, STMT_CLASS,
@ -13,53 +16,54 @@ typedef enum {
STMT_VAR, STMT_VAR,
STMT_WHILE, STMT_WHILE,
STMT_COUNT, STMT_COUNT,
} StmtType; } stmt_type_t;
typedef struct Stmt Stmt;
struct Stmt{ typedef struct stmt_t {
StmtType type; stmt_type_t type;
union { union {
struct { struct {
Stmt **statements; struct stmt_t **statements;
} block; } block;
struct { struct {
Token name; token_t name;
Token superclass; token_t superclass;
Stmt **methods; struct stmt_t **methods;
} class; } class;
struct { struct {
Expr *expression; expr_t *expression;
} expr; } expr;
struct { struct {
Token name; token_t name;
TokenArray params; array_t *params;
Stmt **body; struct stmt_t **body;
} function; } function;
struct { struct {
Expr *condition; expr_t *condition;
Stmt *thenBranch; struct stmt_t *thenBranch;
Stmt *elseBranch; struct stmt_t *elseBranch;
} _if; } _if;
struct { struct {
Expr *expression; expr_t *expression;
} print; } print;
struct { struct {
Token keyword; token_t keyword;
Expr *value; expr_t *value;
} _return; } _return;
struct { struct {
Token name; token_t name;
Expr *initializer; expr_t *initializer;
} variable; } variable;
struct { struct {
Expr *condition; expr_t *condition;
Stmt *body; struct stmt_t *body;
} _while; } _while;
} as; } as;
}; } stmt_t;
typedef struct { typedef struct {
Stmt *statements; struct stmt_t *statements;
int count; int length;
int capacity; int capacity;
bool hadError; } stmt_array_t;
} StmtArray;
#endif #endif

View file

@ -5,29 +5,29 @@
#include "ast.h" #include "ast.h"
#include "lexer.h" #include "lexer.h"
expr_t *create_binary_expr(token_t *binary_op, expr_t *left, expr_t *right) expr_t *create_binary_expr(token_t *operator, expr_t *left, expr_t *right)
{ {
expr_t *expr = malloc(sizeof(expr_t)); expr_t *expr = malloc(sizeof(expr_t));
expr->type = BINARY; expr->type = EXPR_BINARY;
expr->line = binary_op->line; expr->line = operator->line;
expr->as.binary.left = left; expr->as.binary.left = left;
expr->as.binary.right = right; expr->as.binary.right = right;
expr->as.binary.binary_op.type = binary_op->type; expr->as.binary.operator.type = operator->type;
char *bin_op_val = strdup(binary_op->value); char *bin_op_val = strdup(operator->value);
expr->as.binary.binary_op.value = bin_op_val; expr->as.binary.operator.value = bin_op_val;
expr->as.binary.binary_op.line = binary_op->line; expr->as.binary.operator.line = operator->line;
return expr; return expr;
} }
expr_t *create_unary_expr(token_t *unary_op, expr_t *right) expr_t *create_unary_expr(token_t *operator, expr_t *right)
{ {
expr_t *expr = malloc(sizeof(expr_t)); expr_t *expr = malloc(sizeof(expr_t));
expr->type = UNARY; expr->type = EXPR_UNARY;
expr->line = unary_op->line; expr->line = operator->line;
expr->as.unary.unary_op.type = unary_op->type; expr->as.unary.operator.type = operator->type;
char *u_op_val = strdup(unary_op->value); char *u_op_val = strdup(operator->value);
expr->as.unary.unary_op.value = u_op_val; expr->as.unary.operator.value = u_op_val;
expr->as.unary.unary_op.line = unary_op->line; expr->as.unary.operator.line = operator->line;
expr->as.unary.right = right; expr->as.unary.right = right;
return expr; return expr;
} }
@ -35,28 +35,28 @@ expr_t *create_unary_expr(token_t *unary_op, expr_t *right)
expr_t *create_literal_expr(token_t *token) expr_t *create_literal_expr(token_t *token)
{ {
expr_t *expr = malloc(sizeof(expr_t)); expr_t *expr = malloc(sizeof(expr_t));
expr->type = LITERAL; expr->type = EXPR_LITERAL;
expr->line = token->line; expr->line = token->line;
switch (token->type) { switch (token->type) {
case NUMBER: case TOKEN_NUMBER:
expr->as.literal.value.type = VAL_NUMBER; expr->as.literal.value.type = VAL_NUMBER;
double num; double num;
sscanf(token->value, "%lf", &num); sscanf(token->value, "%lf", &num);
expr->as.literal.value.as.number = num; expr->as.literal.value.as.number = num;
break; break;
case NIL: case TOKEN_NIL:
expr->as.literal.value.type = VAL_NIL; expr->as.literal.value.type = VAL_NIL;
expr->as.literal.value.as.number = 0; expr->as.literal.value.as.number = 0;
break; break;
case TRUE: case TOKEN_TRUE:
case FALSE: case TOKEN_FALSE:
expr->as.literal.value.type = VAL_BOOL; expr->as.literal.value.type = VAL_BOOL;
expr->as.literal.value.as.boolean = token->type == TRUE; expr->as.literal.value.as.boolean = token->type == TOKEN_TRUE;
break; break;
case STRING: case TOKEN_STRING:
expr->as.literal.value.type = VAL_STRING; expr->as.literal.value.type = VAL_STRING;
char *tkvalue = strdup(token->value); char *tkvalue = strdup(token->value);
expr->as.literal.value.as.string = tkvalue; expr->as.literal.value.as.string = tkvalue;
@ -74,7 +74,7 @@ expr_t *create_grouping_expr(expr_t *expression)
return NULL; return NULL;
} }
expr_t *expr = malloc(sizeof(expr_t)); expr_t *expr = malloc(sizeof(expr_t));
expr->type = GROUPING; expr->type = EXPR_GROUPING;
expr->line = expression->line; expr->line = expression->line;
expr->as.grouping.expression = expression; expr->as.grouping.expression = expression;
return expr; return expr;
@ -84,7 +84,7 @@ void print_ast(expr_t *expr)
{ {
if (!expr) if (!expr)
return; return;
if (expr->type == LITERAL) { if (expr->type == EXPR_LITERAL) {
switch (expr->as.literal.value.type) { switch (expr->as.literal.value.type) {
case VAL_BOOL: case VAL_BOOL:
printf("%s", expr->as.literal.value.as.boolean ? "true" : "false"); printf("%s", expr->as.literal.value.as.boolean ? "true" : "false");
@ -107,17 +107,17 @@ void print_ast(expr_t *expr)
printf("%s", expr->as.literal.value.as.string); printf("%s", expr->as.literal.value.as.string);
break; break;
} }
} else if (expr->type == BINARY) { } else if (expr->type == EXPR_BINARY) {
printf("(%s ", expr->as.binary.binary_op.value); printf("(%s ", expr->as.binary.operator.value);
print_ast(expr->as.binary.left); print_ast(expr->as.binary.left);
printf(" "); printf(" ");
print_ast(expr->as.binary.right); print_ast(expr->as.binary.right);
printf(")"); printf(")");
} else if (expr->type == UNARY) { } else if (expr->type == EXPR_UNARY) {
printf("(%s ", expr->as.unary.unary_op.value); printf("(%s ", expr->as.unary.operator.value);
print_ast(expr->as.unary.right); print_ast(expr->as.unary.right);
printf(")"); printf(")");
} else if (expr->type == GROUPING) { } else if (expr->type == EXPR_GROUPING) {
printf("(group "); printf("(group ");
print_ast(expr->as.grouping.expression); print_ast(expr->as.grouping.expression);
printf(")"); printf(")");

View file

@ -31,7 +31,7 @@ void runtime_error(const char *message, int line)
value_t visit_binary(expr_t *expr) value_t visit_binary(expr_t *expr)
{ {
token_type_t op_type = expr->as.binary.binary_op.type; token_type_t op_type = expr->as.binary.operator.type;
value_t right = evaluate(expr->as.binary.right); value_t right = evaluate(expr->as.binary.right);
value_t left = evaluate(expr->as.binary.left); value_t left = evaluate(expr->as.binary.left);
@ -39,19 +39,19 @@ value_t visit_binary(expr_t *expr)
if (left.type == VAL_NUMBER && right.type == VAL_NUMBER) { if (left.type == VAL_NUMBER && right.type == VAL_NUMBER) {
value_t result = {.type = VAL_NUMBER}; value_t result = {.type = VAL_NUMBER};
switch (op_type) { switch (op_type) {
case PLUS: case TOKEN_PLUS:
result.as.number = left.as.number + right.as.number; result.as.number = left.as.number + right.as.number;
return result; return result;
case MINUS: case TOKEN_MINUS:
result.as.number = left.as.number - right.as.number; result.as.number = left.as.number - right.as.number;
return result; return result;
case STAR: case TOKEN_STAR:
result.as.number = left.as.number * right.as.number; result.as.number = left.as.number * right.as.number;
return result; return result;
case SLASH: case TOKEN_SLASH:
if (right.as.number == 0) { if (right.as.number == 0) {
runtime_error("Division by zero.", expr->line); runtime_error("Division by zero.", expr->line);
} }
@ -63,7 +63,7 @@ value_t visit_binary(expr_t *expr)
} }
// Comparison // Comparison
if (op_type == EQUAL_EQUAL || op_type == BANG_EQUAL) { if (op_type == TOKEN_EQUAL_EQUAL || op_type == TOKEN_BANG_EQUAL) {
int is_equal; int is_equal;
if (left.type != right.type) { if (left.type != right.type) {
is_equal = 0; is_equal = 0;
@ -91,7 +91,7 @@ value_t visit_binary(expr_t *expr)
} }
} }
value_t result = {.type = VAL_BOOL}; value_t result = {.type = VAL_BOOL};
result.as.boolean = op_type == EQUAL_EQUAL ? is_equal : !is_equal; result.as.boolean = op_type == TOKEN_EQUAL_EQUAL ? is_equal : !is_equal;
return result; return result;
} }
@ -99,19 +99,19 @@ value_t visit_binary(expr_t *expr)
if (left.type == VAL_NUMBER && right.type == VAL_NUMBER) { if (left.type == VAL_NUMBER && right.type == VAL_NUMBER) {
value_t result = {.type = VAL_BOOL }; value_t result = {.type = VAL_BOOL };
switch (op_type) { switch (op_type) {
case GREATER: case TOKEN_GREATER:
result.as.boolean= left.as.number > right.as.number; result.as.boolean= left.as.number > right.as.number;
return result; return result;
case GREATER_EQUAL: case TOKEN_GREATER_EQUAL:
result.as.boolean = left.as.number >= right.as.number; result.as.boolean = left.as.number >= right.as.number;
return result; return result;
case LESS: case TOKEN_LESS:
result.as.boolean = left.as.number < right.as.number; result.as.boolean = left.as.number < right.as.number;
return result; return result;
case LESS_EQUAL: case TOKEN_LESS_EQUAL:
result.as.boolean = left.as.number <= right.as.number; result.as.boolean = left.as.number <= right.as.number;
return result; return result;
@ -121,7 +121,7 @@ value_t visit_binary(expr_t *expr)
// String concatenation // String concatenation
if (left.type == VAL_STRING && right.type == VAL_STRING) { if (left.type == VAL_STRING && right.type == VAL_STRING) {
if (op_type == PLUS) { if (op_type == TOKEN_PLUS) {
value_t result = {.type = VAL_STRING}; value_t result = {.type = VAL_STRING};
size_t left_len = strlen(left.as.string); size_t left_len = strlen(left.as.string);
size_t right_len = strlen(right.as.string); size_t right_len = strlen(right.as.string);
@ -133,7 +133,8 @@ value_t visit_binary(expr_t *expr)
} }
// String/number comparisons // String/number comparisons
if ((left.type == VAL_STRING && right.type == VAL_NUMBER) || (left.type == VAL_NUMBER && right.type == VAL_STRING)) { if ((left.type == VAL_STRING && right.type == VAL_NUMBER) ||
(left.type == VAL_NUMBER && right.type == VAL_STRING)) {
runtime_error("Operands must be numbers.", expr->line); runtime_error("Operands must be numbers.", expr->line);
} }
@ -168,14 +169,14 @@ value_t visit_unary(expr_t *expr)
{ {
value_t operand = evaluate(expr->as.unary.right); value_t operand = evaluate(expr->as.unary.right);
if (expr->as.unary.unary_op.type == MINUS) { if (expr->as.unary.operator.type == TOKEN_MINUS) {
if (operand.type == VAL_NUMBER) { if (operand.type == VAL_NUMBER) {
value_t result = {.type = VAL_NUMBER, .as.number = -operand.as.number}; value_t result = {.type = VAL_NUMBER, .as.number = -operand.as.number};
return result; return result;
} else { } else {
runtime_error("Operand must be a number.", expr->line); runtime_error("Operand must be a number.", expr->line);
} }
} else if (expr->as.unary.unary_op.type == BANG) { } else if (expr->as.unary.operator.type == TOKEN_BANG) {
value_t result = {.type = VAL_BOOL, .as.boolean = !is_truthy(&operand)}; value_t result = {.type = VAL_BOOL, .as.boolean = !is_truthy(&operand)};
return result; return result;
} }
@ -190,16 +191,16 @@ value_t evaluate(expr_t *expr)
return nil_value; return nil_value;
} }
switch (expr->type) { switch (expr->type) {
case LITERAL: case EXPR_LITERAL:
return visit_literal(expr); return visit_literal(expr);
case BINARY: case EXPR_BINARY:
return visit_binary(expr); return visit_binary(expr);
case UNARY: case EXPR_UNARY:
return visit_unary(expr); return visit_unary(expr);
case GROUPING: case EXPR_GROUPING:
return visit_grouping(expr); return visit_grouping(expr);
default: default:
errno = 65; exit(65);
break; break;
} }
} }
@ -231,3 +232,20 @@ void print_value(value_t *value)
break; break;
} }
} }
void print_statement(stmt_t stmt)
{
if (stmt.type == STMT_PRINT) {
value_t obj = evaluate(stmt.as.print.expression);
print_value(&obj);
/* } else if (stmt.type == STMT_EXPR) { */
/* value_t obj = evaluate(stmt.as.expr.expression); */
/* print_value(&obj); */
}
}
void print_statements(stmt_array_t *array)
{
for (int i = 0; i < array->length; i++) {
print_statement(array->statements[i]);
}
}

View file

@ -7,35 +7,35 @@
#include "lexer.h" #include "lexer.h"
const keyword_map reserved_keywords[] = { const keyword_map reserved_keywords[] = {
{"and", AND}, {"class", CLASS}, {"else", ELSE}, {"and", TOKEN_AND}, {"class", TOKEN_CLASS}, {"else", TOKEN_ELSE},
{"false", FALSE}, {"fun", FUN}, {"for", FOR}, {"false", TOKEN_FALSE}, {"fun", TOKEN_FUN}, {"for", TOKEN_FOR},
{"if", IF}, {"nil", NIL}, {"or", OR}, {"if", TOKEN_IF}, {"nil", TOKEN_NIL}, {"or", TOKEN_OR},
{"print", PRINT}, {"return", RETURN}, {"print", TOKEN_PRINT}, {"return", TOKEN_RETURN},
{"super", SUPER}, {"this", THIS}, {"true", TRUE}, {"super", TOKEN_SUPER}, {"this", TOKEN_THIS}, {"true", TOKEN_TRUE},
{"var", VAR}, {"while", WHILE} {"var", TOKEN_VAR}, {"while", TOKEN_WHILE}
}; };
const keyword_map regular_tokens[] = { const keyword_map regular_tokens[] = {
{"LEFT_PAREN", LEFT_PAREN}, {"RIGHT_PAREN", RIGHT_PAREN}, {"LEFT_PAREN", TOKEN_LEFT_PAREN}, {"RIGHT_PAREN", TOKEN_RIGHT_PAREN},
{"LEFT_BRACE", LEFT_BRACE}, {"RIGHT_BRACE", RIGHT_BRACE}, {"LEFT_BRACE", TOKEN_LEFT_BRACE}, {"RIGHT_BRACE", TOKEN_RIGHT_BRACE},
{"COMMA", COMMA}, {"DOT", DOT}, {"MINUS", MINUS}, {"COMMA", TOKEN_COMMA}, {"DOT", TOKEN_DOT}, {"MINUS", TOKEN_MINUS},
{"PLUS", PLUS}, {"SEMICOLON", SEMICOLON}, {"SLASH", SLASH}, {"PLUS", TOKEN_PLUS}, {"SEMICOLON", TOKEN_SEMICOLON}, {"SLASH", TOKEN_SLASH},
{"STAR", STAR}, {"BANG", BANG}, {"BANG_EQUAL", BANG_EQUAL}, {"STAR", TOKEN_STAR}, {"BANG", TOKEN_BANG}, {"BANG_EQUAL", TOKEN_BANG_EQUAL},
{"EQUAL", EQUAL}, {"EQUAL_EQUAL", EQUAL_EQUAL}, {"GREATER", GREATER}, {"EQUAL", TOKEN_EQUAL}, {"EQUAL_EQUAL", TOKEN_EQUAL_EQUAL}, {"GREATER", TOKEN_GREATER},
{"GREATER_EQUAL", GREATER_EQUAL}, {"LESS", LESS}, {"LESS_EQUAL", LESS_EQUAL}, {"GREATER_EQUAL", TOKEN_GREATER_EQUAL}, {"LESS", TOKEN_LESS}, {"LESS_EQUAL", TOKEN_LESS_EQUAL},
{"IDENTIFIER", IDENTIFIER}, {"STRING", STRING}, {"NUMBER", NUMBER}, {"IDENTIFIER", TOKEN_IDENTIFIER}, {"STRING", TOKEN_STRING}, {"NUMBER", TOKEN_NUMBER},
{"AND", AND}, {"CLASS", CLASS}, {"ELSE", ELSE}, {"FALSE", FALSE}, {"AND", TOKEN_AND}, {"CLASS", TOKEN_CLASS}, {"ELSE", TOKEN_ELSE}, {"FALSE", TOKEN_FALSE},
{"FUN", FUN}, {"FOR", FOR}, {"IF", IF}, {"NIL", NIL}, {"FUN", TOKEN_FUN}, {"FOR", TOKEN_FOR}, {"IF", TOKEN_IF}, {"NIL", TOKEN_NIL},
{"OR", OR}, {"PRINT", PRINT}, {"RETURN", RETURN}, {"OR", TOKEN_OR}, {"PRINT", TOKEN_PRINT}, {"RETURN", TOKEN_RETURN},
{"SUPER", SUPER}, {"THIS", THIS}, {"TRUE", TRUE}, {"SUPER", TOKEN_SUPER}, {"THIS", TOKEN_THIS}, {"TRUE", TOKEN_TRUE},
{"VAR", VAR}, {"WHILE", WHILE}, {"END_OF_FILE", END_OF_FILE} {"VAR", TOKEN_VAR}, {"WHILE", TOKEN_WHILE}, {"END_OF_FILE", TOKEN_EOF}
}; };
char *read_source(const char *filename) char *read_source(const char *filename)
{ {
FILE *file = fopen(filename, "r"); FILE *file = fopen(filename, "r");
if (file == NULL) { if (file == NULL) {
fprintf(stderr, "rd: Error reading file: %s\n", filename); fprintf(stderr, "Error reading file: %s\n", filename);
return NULL; return NULL;
} }
@ -45,14 +45,14 @@ char *read_source(const char *filename)
char *source = malloc(file_size + 1); char *source = malloc(file_size + 1);
if (source == NULL) { if (source == NULL) {
fprintf(stderr, "rd: Error allocating memory\n"); fprintf(stderr, "Memory allocation failed\n");
fclose(file); fclose(file);
return NULL; return NULL;
} }
size_t bytes_read = fread(source, 1, file_size, file); size_t bytes_read = fread(source, 1, file_size, file);
if (bytes_read < file_size) { if (bytes_read < file_size) {
fprintf(stderr, "rd: Error reading file contents\n"); fprintf(stderr, "Error reading file contents\n");
free(source); free(source);
fclose(file); fclose(file);
return NULL; return NULL;
@ -105,11 +105,11 @@ char *type_str(token_type_t type)
void print_tokens(token_t *tokens) void print_tokens(token_t *tokens)
{ {
for (int i = 0; tokens[i].type != END_OF_FILE; i++) { for (int i = 0; tokens[i].type != TOKEN_EOF; i++) {
token_t token = tokens[i]; token_t token = tokens[i];
if (token.type == STRING) { if (token.type == TOKEN_STRING) {
printf("STRING \"%s\" %s\n", token.value, token.value); printf("STRING \"%s\" %s\n", token.value, token.value);
} else if (token.type == NUMBER) { } else if (token.type == TOKEN_NUMBER) {
double value = strtod(token.value, NULL); double value = strtod(token.value, NULL);
if (value == (int) value) { if (value == (int) value) {
printf("NUMBER %s %d.0\n", token.value, (int) value); printf("NUMBER %s %d.0\n", token.value, (int) value);
@ -142,74 +142,71 @@ array_t *tokenize(char *filename)
tokens->capacity = DEFAULT_TOKENS_SIZE; tokens->capacity = DEFAULT_TOKENS_SIZE;
char *source = read_source(filename); char *source = read_source(filename);
if (!source) {
return NULL;
}
int line = 1; int line = 1;
size_t source_len = strlen(source); size_t source_len = strlen(source);
if (source_len > 0) { if (source_len > 0) {
for (int i = 0; i < source_len; i++) { for (int i = 0; i < source_len; i++) {
switch (source[i]) { switch (source[i]) {
case '(': case '(':
token_add(tokens, token_gen(LEFT_PAREN, "(", line)); token_add(tokens, token_gen(TOKEN_LEFT_PAREN, "(", line));
break; break;
case ')': case ')':
token_add(tokens, token_gen(RIGHT_PAREN, ")", line)); token_add(tokens, token_gen(TOKEN_RIGHT_PAREN, ")", line));
break; break;
case '{': case '{':
token_add(tokens, token_gen(LEFT_BRACE, "{", line)); token_add(tokens, token_gen(TOKEN_LEFT_BRACE, "{", line));
break; break;
case '}': case '}':
token_add(tokens, token_gen(RIGHT_BRACE, "}", line)); token_add(tokens, token_gen(TOKEN_RIGHT_BRACE, "}", line));
break; break;
case '*': case '*':
token_add(tokens, token_gen(STAR, "*", line)); token_add(tokens, token_gen(TOKEN_STAR, "*", line));
break; break;
case '.': case '.':
token_add(tokens, token_gen(DOT, ".", line)); token_add(tokens, token_gen(TOKEN_DOT, ".", line));
break; break;
case ',': case ',':
token_add(tokens, token_gen(COMMA, ",", line)); token_add(tokens, token_gen(TOKEN_COMMA, ",", line));
break; break;
case '+': case '+':
token_add(tokens, token_gen(PLUS, "+", line)); token_add(tokens, token_gen(TOKEN_PLUS, "+", line));
break; break;
case '-': case '-':
token_add(tokens, token_gen(MINUS, "-", line)); token_add(tokens, token_gen(TOKEN_MINUS, "-", line));
break; break;
case ';': case ';':
token_add(tokens, token_gen(SEMICOLON, ";", line)); token_add(tokens, token_gen(TOKEN_SEMICOLON, ";", line));
break; break;
case '=': case '=':
if (source[i + 1] == '=') { if (source[i + 1] == '=') {
token_add(tokens, token_gen(EQUAL_EQUAL, "==", line)); token_add(tokens, token_gen(TOKEN_EQUAL_EQUAL, "==", line));
i++; i++;
} else { } else {
token_add(tokens, token_gen(EQUAL, "=", line)); token_add(tokens, token_gen(TOKEN_EQUAL, "=", line));
} }
break; break;
case '!': case '!':
if (source[i + 1] == '=') { if (source[i + 1] == '=') {
token_add(tokens, token_gen(BANG_EQUAL, "!=", line)); token_add(tokens, token_gen(TOKEN_BANG_EQUAL, "!=", line));
i++; i++;
} else { } else {
token_add(tokens, token_gen(BANG, "!", line)); token_add(tokens, token_gen(TOKEN_BANG, "!", line));
} }
break; break;
case '>': case '>':
if (source[i + 1] == '=') { if (source[i + 1] == '=') {
token_add(tokens, token_gen(GREATER_EQUAL, ">=", line)); token_add(tokens, token_gen(TOKEN_GREATER_EQUAL, ">=", line));
i++; i++;
} else { } else {
token_add(tokens, token_gen(GREATER, ">", line)); token_add(tokens, token_gen(TOKEN_GREATER, ">", line));
} }
break; break;
case '<': case '<':
if (source[i + 1] == '=') { if (source[i + 1] == '=') {
token_add(tokens, token_gen(LESS_EQUAL, "<=", line)); token_add(tokens, token_gen(TOKEN_LESS_EQUAL, "<=", line));
i++; i++;
} else { } else {
token_add(tokens, token_gen(LESS, "<", line)); token_add(tokens, token_gen(TOKEN_LESS, "<", line));
} }
break; break;
case '/': case '/':
@ -220,7 +217,7 @@ array_t *tokenize(char *filename)
} }
i--; i--;
} else { } else {
token_add(tokens, token_gen(SLASH, "/", line)); token_add(tokens, token_gen(TOKEN_SLASH, "/", line));
} }
break; break;
case ' ': case ' ':
@ -246,7 +243,7 @@ array_t *tokenize(char *filename)
char str[len + 1]; char str[len + 1];
strncpy(str, &source[str_start], len); strncpy(str, &source[str_start], len);
str[len] = 0; str[len] = 0;
token_add(tokens, token_gen(STRING, str, line)); token_add(tokens, token_gen(TOKEN_STRING, str, line));
} }
break; break;
@ -277,7 +274,7 @@ array_t *tokenize(char *filename)
} }
} }
if (!found) { if (!found) {
token_add(tokens, token_gen(IDENTIFIER, id, line)); token_add(tokens, token_gen(TOKEN_IDENTIFIER, id, line));
} }
i--; i--;
break; break;
@ -296,7 +293,7 @@ array_t *tokenize(char *filename)
char integer[len + 1]; char integer[len + 1];
strncpy(integer, iend, len); strncpy(integer, iend, len);
integer[len] = 0; integer[len] = 0;
token_add(tokens, token_gen(NUMBER, integer, line)); token_add(tokens, token_gen(TOKEN_NUMBER, integer, line));
i--; i--;
} else { } else {
@ -306,7 +303,7 @@ array_t *tokenize(char *filename)
} }
} }
} }
token_add(tokens, token_gen(END_OF_FILE, "EOF", line)); token_add(tokens, token_gen(TOKEN_EOF, "EOF", line));
free(source); free(source);
return tokens; return tokens;
} }

View file

@ -8,12 +8,11 @@
int current = 0; int current = 0;
token_t *tokens; token_t *tokens;
expr_t *expression(void); expr_t *expression(void);
void error(token_t *token, char *message) void error(token_t *token, char *message)
{ {
if (token->type == END_OF_FILE) { if (token->type == TOKEN_EOF) {
fprintf(stderr, "[line %d] at end: %s\n", token->line, message); fprintf(stderr, "[line %d] at end: %s\n", token->line, message);
} else { } else {
fprintf(stderr, "[line %d] at '%s': %s\n", token->line, token->value, message); fprintf(stderr, "[line %d] at '%s': %s\n", token->line, token->value, message);
@ -26,25 +25,25 @@ void free_expr(expr_t *expr)
if (!expr) if (!expr)
return; return;
switch (expr->type) { switch (expr->type) {
case BINARY: case EXPR_BINARY:
free(expr->as.binary.binary_op.value); free(expr->as.binary.operator.value);
free_expr(expr->as.binary.left); free_expr(expr->as.binary.left);
free_expr(expr->as.binary.right); free_expr(expr->as.binary.right);
free(expr); free(expr);
break; break;
case GROUPING: case EXPR_GROUPING:
free_expr(expr->as.grouping.expression); free_expr(expr->as.grouping.expression);
free(expr); free(expr);
break; break;
case UNARY: case EXPR_UNARY:
free(expr->as.unary.unary_op.value); free(expr->as.unary.operator.value);
free_expr(expr->as.unary.right); free_expr(expr->as.unary.right);
free(expr); free(expr);
break; break;
case LITERAL: case EXPR_LITERAL:
if (expr->as.literal.value.type == VAL_STRING) { if (expr->as.literal.value.type == VAL_STRING) {
free(expr->as.literal.value.as.string); free(expr->as.literal.value.as.string);
} }
@ -56,24 +55,14 @@ void free_expr(expr_t *expr)
} }
} }
expr_t *parse(token_t *tks)
{
tokens = tks;
if (errno == 65) {
return NULL;
} else {
return expression();
}
}
token_t *peek(void) token_t *peek(void)
{ {
return &tokens[current]; return &tokens[current];
} }
int isAtEnd(void) int end(void)
{ {
return tokens[current].type == END_OF_FILE; return tokens[current].type == TOKEN_EOF;
} }
token_t *previous(void) token_t *previous(void)
@ -83,11 +72,10 @@ token_t *previous(void)
void advance(void) void advance(void)
{ {
if (!isAtEnd()) if (!end())
current++; current++;
} }
int check(token_type_t type) int check(token_type_t type)
{ {
if (tokens[current].type == type) { if (tokens[current].type == type) {
@ -106,12 +94,13 @@ void consume(token_type_t type, char *message) {
expr_t *primary(void) expr_t *primary(void)
{ {
if (check(FALSE) || check(TRUE) || check(NIL) || check(NUMBER) || check(STRING)) { if (check(TOKEN_FALSE) || check(TOKEN_TRUE) || check(TOKEN_NIL) ||
check(TOKEN_NUMBER) || check(TOKEN_STRING)) {
return create_literal_expr(previous()); return create_literal_expr(previous());
} }
if (check(LEFT_PAREN)) { if (check(TOKEN_LEFT_PAREN)) {
expr_t *expr = expression(); expr_t *expr = expression();
consume(RIGHT_PAREN, "Expect ')' after expression."); consume(TOKEN_RIGHT_PAREN, "Expect ')' after expression.");
return create_grouping_expr(expr); return create_grouping_expr(expr);
} }
error(peek(), "Expect expression."); error(peek(), "Expect expression.");
@ -120,7 +109,7 @@ expr_t *primary(void)
expr_t *unary(void) expr_t *unary(void)
{ {
if (check(BANG) || check(MINUS)) { if (check(TOKEN_BANG) || check(TOKEN_MINUS)) {
token_t *operator = previous(); token_t *operator = previous();
expr_t *right = unary(); expr_t *right = unary();
return create_unary_expr(operator, right); return create_unary_expr(operator, right);
@ -133,7 +122,7 @@ expr_t *factor(void)
{ {
expr_t *expr = unary(); expr_t *expr = unary();
while (check(SLASH) || check(STAR)) { while (check(TOKEN_SLASH) || check(TOKEN_STAR)) {
token_t *operator = previous(); token_t *operator = previous();
expr_t *right = unary(); expr_t *right = unary();
expr = create_binary_expr(operator, expr, right); expr = create_binary_expr(operator, expr, right);
@ -146,7 +135,7 @@ expr_t *term(void)
{ {
expr_t *expr = factor(); expr_t *expr = factor();
while (check(MINUS) || check(PLUS)) { while (check(TOKEN_MINUS) || check(TOKEN_PLUS)) {
token_t *operator = previous(); token_t *operator = previous();
expr_t *right = factor(); expr_t *right = factor();
expr = create_binary_expr(operator, expr, right); expr = create_binary_expr(operator, expr, right);
@ -159,7 +148,8 @@ expr_t *comparison(void)
{ {
expr_t *expr = term(); expr_t *expr = term();
while (check(GREATER) || check(GREATER_EQUAL) || check(LESS) || check(LESS_EQUAL)) { while (check(TOKEN_GREATER) || check(TOKEN_GREATER_EQUAL) || check(TOKEN_LESS)
|| check(TOKEN_LESS_EQUAL)) {
token_t *operator = previous(); token_t *operator = previous();
expr_t *right = term(); expr_t *right = term();
expr = create_binary_expr(operator, expr, right); expr = create_binary_expr(operator, expr, right);
@ -172,7 +162,7 @@ expr_t *equality(void)
{ {
expr_t *expr = comparison(); expr_t *expr = comparison();
while (check(BANG_EQUAL) || check(EQUAL_EQUAL)) { while (check(TOKEN_BANG_EQUAL) || check(TOKEN_EQUAL_EQUAL)) {
token_t *operator = previous(); token_t *operator = previous();
expr_t *right = comparison(); expr_t *right = comparison();
expr = create_binary_expr(operator, expr, right); expr = create_binary_expr(operator, expr, right);
@ -186,22 +176,99 @@ expr_t *expression(void)
return equality(); return equality();
} }
stmt_t print_stmt(void)
{
expr_t *value = expression();
consume(TOKEN_SEMICOLON, "Expect ; after value.");
return (stmt_t) {
.type = STMT_PRINT,
.as.print.expression = value,
};
}
stmt_t expression_stmt(void)
{
expr_t *expr = expression();
consume(TOKEN_SEMICOLON, "Expect ; after expression.");
return (stmt_t) {
.type = STMT_EXPR,
.as.expr.expression = expr,
};
}
stmt_t statement(void)
{
if (check(TOKEN_PRINT))
return print_stmt();
return expression_stmt();
}
void stmt_add(stmt_array_t *array, stmt_t stmt)
{
if (array->length == array->capacity) {
array->capacity *= 2;
array->statements = realloc(array->statements, array->capacity * sizeof(stmt_t));
}
array->statements[array->length++] = stmt;
}
void free_statements(stmt_array_t *array)
{
for (int i = 0; i < array->length; i++) {
if (array->statements[i].type == STMT_PRINT) {
free_expr(array->statements[i].as.print.expression);
}
if (array->statements[i].type == STMT_EXPR) {
free_expr(array->statements[i].as.expr.expression);
}
}
free(array->statements);
free(array);
}
stmt_array_t *parse(token_t *tks)
{
tokens = tks;
if (errno == 65) {
return NULL;
} else {
stmt_array_t *statements = malloc(sizeof(stmt_array_t));
statements->statements = malloc(DEFAULT_STMTS_SIZE * sizeof(stmt_t));
statements->length = 0;
statements->capacity = DEFAULT_STMTS_SIZE;
while (!end()) {
stmt_add(statements, statement());
}
return statements;
}
}
expr_t *parse_expr(token_t *tks)
{
tokens = tks;
if (errno == 65) {
return NULL;
} else {
return expression();
}
}
void synchronize(void) void synchronize(void)
{ {
advance(); advance();
while (!isAtEnd()) { while (!end()) {
if (previous()->type == SEMICOLON) return; if (previous()->type == TOKEN_SEMICOLON) return;
switch (peek()->type) { switch (peek()->type) {
case CLASS: case TOKEN_CLASS:
case FUN: case TOKEN_FUN:
case VAR: case TOKEN_VAR:
case FOR: case TOKEN_FOR:
case IF: case TOKEN_IF:
case WHILE: case TOKEN_WHILE:
case PRINT: case TOKEN_PRINT:
case RETURN: case TOKEN_RETURN:
return; return;
default: default:
return; return;

View file

@ -11,21 +11,21 @@
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
if (argc < 3) { if (argc < 3) {
fprintf(stderr, "Usage: rd tokenize|parse|evaluate <filename>\n"); fprintf(stderr, "Usage: rd tokenize|parse|evaluate|run <filename>\n");
return 1; return 1;
} }
const char *command = argv[1]; const char *command = argv[1];
if (!strcmp(command, "tokenize")) {
array_t *array = tokenize(argv[2]); array_t *array = tokenize(argv[2]);
if (array) { if (!array) {
return 1;
}
if (!strcmp(command, "tokenize")) {
print_tokens(array->tokens); print_tokens(array->tokens);
free_array(array); free_array(array);
}
} else if (!strcmp(command, "parse")) { } else if (!strcmp(command, "parse")) {
array_t *array = tokenize(argv[2]); expr_t *expr = parse_expr(array->tokens);
expr_t *expr = parse(array->tokens);
if (errno != 65) { if (errno != 65) {
print_ast(expr); print_ast(expr);
printf("\n"); printf("\n");
@ -33,10 +33,18 @@ int main(int argc, char **argv)
free_array(array); free_array(array);
free_expr(expr); free_expr(expr);
} else if (!strcmp(command, "evaluate")) { } else if (!strcmp(command, "evaluate")) {
array_t *array = tokenize(argv[2]); expr_t *expr = parse_expr(array->tokens);
expr_t *expr = parse(array->tokens);
value_t val = evaluate(expr); value_t val = evaluate(expr);
print_value(&val); print_value(&val);
free_array(array);
free_expr(expr);
} else if (!strcmp(command, "run")) {
stmt_array_t *stmts = parse(array->tokens);
if (errno != 65) {
print_statements(stmts);
free_array(array);
free_statements(stmts);
}
} else { } else {
fprintf(stderr, "Unknown command: %s\n", command); fprintf(stderr, "Unknown command: %s\n", command);
return 1; return 1;