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,65 +4,102 @@
#include "lexer.h"
/*
expression literal
| unary
| binary
| grouping ;
literal NUMBER | STRING | "true" | "false" | "nil" ;
grouping "(" expression ")" ;
unary ( "-" | "!" ) expression ;
binary expression operator expression ;
operator "==" | "!=" | "<" | "<=" | ">" | ">="
| "+" | "-" | "*" | "/" ;
expression literal | unary | binary | grouping ;
literal NUMBER | STRING | "true" | "false" | "nil" ;
grouping "(" expression ")" ;
unary ( "-" | "!" ) expression ;
binary expression operator expression ;
operator "==" | "!=" | "<" | "<=" | ">" | ">=" | "+" | "-" | "*" | "/" ;
*/
typedef enum {
BINARY,
UNARY,
LITERAL,
GROUPING,
} expr_type;
EXPR_ASSIGN,
EXPR_BINARY,
EXPR_CALL,
EXPR_GET,
EXPR_GROUPING,
EXPR_LITERAL,
EXPR_LOGICAL,
EXPR_SET,
EXPR_SUPER,
EXPR_THIS,
EXPR_UNARY,
EXPR_VARIABLE,
} expr_type_t;
typedef enum {
VAL_BOOL,
VAL_NIL,
VAL_NUMBER,
VAL_STRING,
VAL_BOOL,
VAL_NIL,
VAL_NUMBER,
VAL_STRING,
} value_type_t;
typedef struct {
value_type_t type;
union {
int boolean;
double number;
char *string;
} as;
value_type_t type;
union {
int boolean;
double number;
char *string;
} as;
} value_t;
typedef struct expr_t {
expr_type type;
int line;
union {
struct {
token_t binary_op;
struct expr_t *left;
struct expr_t *right;
} binary;
struct {
token_t unary_op;
struct expr_t *right;
} unary;
struct {
value_t value;
} literal;
expr_type_t type;
int line;
union {
struct {
token_t name;
struct expr_t *value;
} assign;
struct {
token_t operator;
struct expr_t *left;
struct expr_t *right;
} binary;
struct {
struct expr_t *callee;
token_t paren;
struct expr_t **arguments;
} call;
struct {
struct expr_t *object;
token_t name;
} get;
struct {
struct expr_t *expression;
} grouping;
} as;
struct {
value_t value;
} literal;
struct {
token_t operator;
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;
} expr_t;
expr_t *create_binary_expr(token_t *binary_op, expr_t *left, expr_t *right);
expr_t *create_unary_expr(token_t *unary_op, expr_t *right);
expr_t *create_binary_expr(token_t *operator, expr_t *left, 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_grouping_expr(expr_t *expression);
void print_ast(expr_t *expr);

View file

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

View file

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

View file

@ -2,21 +2,22 @@
#define PARSER_H
/*
expression equality ;
equality comparison ( ( "!=" | "==" ) comparison )* ;
comparison term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
term factor ( ( "-" | "+" ) factor )* ;
factor unary ( ( "/" | "*" ) unary )* ;
unary ( "!" | "-" ) unary
| primary ;
primary NUMBER | STRING | "true" | "false" | "nil"
| "(" expression ")" ;
expression equality ;
equality comparison ( ( "!=" | "==" ) comparison )* ;
comparison term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
term factor ( ( "-" | "+" ) factor )* ;
factor unary ( ( "/" | "*" ) unary )* ;
unary ( "!" | "-" ) unary | primary ;
primary NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" ;
*/
#include "ast.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_statements(stmt_array_t *array);
#endif

View file

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

View file

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

View file

@ -31,7 +31,7 @@ void runtime_error(const char *message, int line)
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 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) {
value_t result = {.type = VAL_NUMBER};
switch (op_type) {
case PLUS:
case TOKEN_PLUS:
result.as.number = left.as.number + right.as.number;
return result;
case MINUS:
case TOKEN_MINUS:
result.as.number = left.as.number - right.as.number;
return result;
case STAR:
case TOKEN_STAR:
result.as.number = left.as.number * right.as.number;
return result;
case SLASH:
case TOKEN_SLASH:
if (right.as.number == 0) {
runtime_error("Division by zero.", expr->line);
}
@ -63,7 +63,7 @@ value_t visit_binary(expr_t *expr)
}
// Comparison
if (op_type == EQUAL_EQUAL || op_type == BANG_EQUAL) {
if (op_type == TOKEN_EQUAL_EQUAL || op_type == TOKEN_BANG_EQUAL) {
int is_equal;
if (left.type != right.type) {
is_equal = 0;
@ -91,7 +91,7 @@ value_t visit_binary(expr_t *expr)
}
}
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;
}
@ -99,19 +99,19 @@ value_t visit_binary(expr_t *expr)
if (left.type == VAL_NUMBER && right.type == VAL_NUMBER) {
value_t result = {.type = VAL_BOOL };
switch (op_type) {
case GREATER:
case TOKEN_GREATER:
result.as.boolean= left.as.number > right.as.number;
return result;
case GREATER_EQUAL:
case TOKEN_GREATER_EQUAL:
result.as.boolean = left.as.number >= right.as.number;
return result;
case LESS:
case TOKEN_LESS:
result.as.boolean = left.as.number < right.as.number;
return result;
case LESS_EQUAL:
case TOKEN_LESS_EQUAL:
result.as.boolean = left.as.number <= right.as.number;
return result;
@ -121,7 +121,7 @@ value_t visit_binary(expr_t *expr)
// String concatenation
if (left.type == VAL_STRING && right.type == VAL_STRING) {
if (op_type == PLUS) {
if (op_type == TOKEN_PLUS) {
value_t result = {.type = VAL_STRING};
size_t left_len = strlen(left.as.string);
size_t right_len = strlen(right.as.string);
@ -133,7 +133,8 @@ value_t visit_binary(expr_t *expr)
}
// 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);
}
@ -168,14 +169,14 @@ value_t visit_unary(expr_t *expr)
{
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) {
value_t result = {.type = VAL_NUMBER, .as.number = -operand.as.number};
return result;
} else {
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)};
return result;
}
@ -190,16 +191,16 @@ value_t evaluate(expr_t *expr)
return nil_value;
}
switch (expr->type) {
case LITERAL:
case EXPR_LITERAL:
return visit_literal(expr);
case BINARY:
case EXPR_BINARY:
return visit_binary(expr);
case UNARY:
case EXPR_UNARY:
return visit_unary(expr);
case GROUPING:
case EXPR_GROUPING:
return visit_grouping(expr);
default:
errno = 65;
exit(65);
break;
}
}
@ -231,3 +232,20 @@ void print_value(value_t *value)
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"
const keyword_map reserved_keywords[] = {
{"and", AND}, {"class", CLASS}, {"else", ELSE},
{"false", FALSE}, {"fun", FUN}, {"for", FOR},
{"if", IF}, {"nil", NIL}, {"or", OR},
{"print", PRINT}, {"return", RETURN},
{"super", SUPER}, {"this", THIS}, {"true", TRUE},
{"var", VAR}, {"while", WHILE}
{"and", TOKEN_AND}, {"class", TOKEN_CLASS}, {"else", TOKEN_ELSE},
{"false", TOKEN_FALSE}, {"fun", TOKEN_FUN}, {"for", TOKEN_FOR},
{"if", TOKEN_IF}, {"nil", TOKEN_NIL}, {"or", TOKEN_OR},
{"print", TOKEN_PRINT}, {"return", TOKEN_RETURN},
{"super", TOKEN_SUPER}, {"this", TOKEN_THIS}, {"true", TOKEN_TRUE},
{"var", TOKEN_VAR}, {"while", TOKEN_WHILE}
};
const keyword_map regular_tokens[] = {
{"LEFT_PAREN", LEFT_PAREN}, {"RIGHT_PAREN", RIGHT_PAREN},
{"LEFT_BRACE", LEFT_BRACE}, {"RIGHT_BRACE", RIGHT_BRACE},
{"COMMA", COMMA}, {"DOT", DOT}, {"MINUS", MINUS},
{"PLUS", PLUS}, {"SEMICOLON", SEMICOLON}, {"SLASH", SLASH},
{"STAR", STAR}, {"BANG", BANG}, {"BANG_EQUAL", BANG_EQUAL},
{"EQUAL", EQUAL}, {"EQUAL_EQUAL", EQUAL_EQUAL}, {"GREATER", GREATER},
{"GREATER_EQUAL", GREATER_EQUAL}, {"LESS", LESS}, {"LESS_EQUAL", LESS_EQUAL},
{"IDENTIFIER", IDENTIFIER}, {"STRING", STRING}, {"NUMBER", NUMBER},
{"AND", AND}, {"CLASS", CLASS}, {"ELSE", ELSE}, {"FALSE", FALSE},
{"FUN", FUN}, {"FOR", FOR}, {"IF", IF}, {"NIL", NIL},
{"OR", OR}, {"PRINT", PRINT}, {"RETURN", RETURN},
{"SUPER", SUPER}, {"THIS", THIS}, {"TRUE", TRUE},
{"VAR", VAR}, {"WHILE", WHILE}, {"END_OF_FILE", END_OF_FILE}
{"LEFT_PAREN", TOKEN_LEFT_PAREN}, {"RIGHT_PAREN", TOKEN_RIGHT_PAREN},
{"LEFT_BRACE", TOKEN_LEFT_BRACE}, {"RIGHT_BRACE", TOKEN_RIGHT_BRACE},
{"COMMA", TOKEN_COMMA}, {"DOT", TOKEN_DOT}, {"MINUS", TOKEN_MINUS},
{"PLUS", TOKEN_PLUS}, {"SEMICOLON", TOKEN_SEMICOLON}, {"SLASH", TOKEN_SLASH},
{"STAR", TOKEN_STAR}, {"BANG", TOKEN_BANG}, {"BANG_EQUAL", TOKEN_BANG_EQUAL},
{"EQUAL", TOKEN_EQUAL}, {"EQUAL_EQUAL", TOKEN_EQUAL_EQUAL}, {"GREATER", TOKEN_GREATER},
{"GREATER_EQUAL", TOKEN_GREATER_EQUAL}, {"LESS", TOKEN_LESS}, {"LESS_EQUAL", TOKEN_LESS_EQUAL},
{"IDENTIFIER", TOKEN_IDENTIFIER}, {"STRING", TOKEN_STRING}, {"NUMBER", TOKEN_NUMBER},
{"AND", TOKEN_AND}, {"CLASS", TOKEN_CLASS}, {"ELSE", TOKEN_ELSE}, {"FALSE", TOKEN_FALSE},
{"FUN", TOKEN_FUN}, {"FOR", TOKEN_FOR}, {"IF", TOKEN_IF}, {"NIL", TOKEN_NIL},
{"OR", TOKEN_OR}, {"PRINT", TOKEN_PRINT}, {"RETURN", TOKEN_RETURN},
{"SUPER", TOKEN_SUPER}, {"THIS", TOKEN_THIS}, {"TRUE", TOKEN_TRUE},
{"VAR", TOKEN_VAR}, {"WHILE", TOKEN_WHILE}, {"END_OF_FILE", TOKEN_EOF}
};
char *read_source(const char *filename)
{
FILE *file = fopen(filename, "r");
if (file == NULL) {
fprintf(stderr, "rd: Error reading file: %s\n", filename);
fprintf(stderr, "Error reading file: %s\n", filename);
return NULL;
}
@ -45,14 +45,14 @@ char *read_source(const char *filename)
char *source = malloc(file_size + 1);
if (source == NULL) {
fprintf(stderr, "rd: Error allocating memory\n");
fprintf(stderr, "Memory allocation failed\n");
fclose(file);
return NULL;
}
size_t bytes_read = fread(source, 1, file_size, file);
if (bytes_read < file_size) {
fprintf(stderr, "rd: Error reading file contents\n");
fprintf(stderr, "Error reading file contents\n");
free(source);
fclose(file);
return NULL;
@ -105,11 +105,11 @@ char *type_str(token_type_t type)
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];
if (token.type == STRING) {
if (token.type == TOKEN_STRING) {
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);
if (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;
char *source = read_source(filename);
if (!source) {
return NULL;
}
int line = 1;
size_t source_len = strlen(source);
if (source_len > 0) {
for (int i = 0; i < source_len; i++) {
switch (source[i]) {
case '(':
token_add(tokens, token_gen(LEFT_PAREN, "(", line));
token_add(tokens, token_gen(TOKEN_LEFT_PAREN, "(", line));
break;
case ')':
token_add(tokens, token_gen(RIGHT_PAREN, ")", line));
token_add(tokens, token_gen(TOKEN_RIGHT_PAREN, ")", line));
break;
case '{':
token_add(tokens, token_gen(LEFT_BRACE, "{", line));
token_add(tokens, token_gen(TOKEN_LEFT_BRACE, "{", line));
break;
case '}':
token_add(tokens, token_gen(RIGHT_BRACE, "}", line));
token_add(tokens, token_gen(TOKEN_RIGHT_BRACE, "}", line));
break;
case '*':
token_add(tokens, token_gen(STAR, "*", line));
token_add(tokens, token_gen(TOKEN_STAR, "*", line));
break;
case '.':
token_add(tokens, token_gen(DOT, ".", line));
token_add(tokens, token_gen(TOKEN_DOT, ".", line));
break;
case ',':
token_add(tokens, token_gen(COMMA, ",", line));
token_add(tokens, token_gen(TOKEN_COMMA, ",", line));
break;
case '+':
token_add(tokens, token_gen(PLUS, "+", line));
token_add(tokens, token_gen(TOKEN_PLUS, "+", line));
break;
case '-':
token_add(tokens, token_gen(MINUS, "-", line));
token_add(tokens, token_gen(TOKEN_MINUS, "-", line));
break;
case ';':
token_add(tokens, token_gen(SEMICOLON, ";", line));
token_add(tokens, token_gen(TOKEN_SEMICOLON, ";", line));
break;
case '=':
if (source[i + 1] == '=') {
token_add(tokens, token_gen(EQUAL_EQUAL, "==", line));
token_add(tokens, token_gen(TOKEN_EQUAL_EQUAL, "==", line));
i++;
} else {
token_add(tokens, token_gen(EQUAL, "=", line));
token_add(tokens, token_gen(TOKEN_EQUAL, "=", line));
}
break;
case '!':
if (source[i + 1] == '=') {
token_add(tokens, token_gen(BANG_EQUAL, "!=", line));
token_add(tokens, token_gen(TOKEN_BANG_EQUAL, "!=", line));
i++;
} else {
token_add(tokens, token_gen(BANG, "!", line));
token_add(tokens, token_gen(TOKEN_BANG, "!", line));
}
break;
case '>':
if (source[i + 1] == '=') {
token_add(tokens, token_gen(GREATER_EQUAL, ">=", line));
token_add(tokens, token_gen(TOKEN_GREATER_EQUAL, ">=", line));
i++;
} else {
token_add(tokens, token_gen(GREATER, ">", line));
token_add(tokens, token_gen(TOKEN_GREATER, ">", line));
}
break;
case '<':
if (source[i + 1] == '=') {
token_add(tokens, token_gen(LESS_EQUAL, "<=", line));
token_add(tokens, token_gen(TOKEN_LESS_EQUAL, "<=", line));
i++;
} else {
token_add(tokens, token_gen(LESS, "<", line));
token_add(tokens, token_gen(TOKEN_LESS, "<", line));
}
break;
case '/':
@ -220,7 +217,7 @@ array_t *tokenize(char *filename)
}
i--;
} else {
token_add(tokens, token_gen(SLASH, "/", line));
token_add(tokens, token_gen(TOKEN_SLASH, "/", line));
}
break;
case ' ':
@ -246,7 +243,7 @@ array_t *tokenize(char *filename)
char str[len + 1];
strncpy(str, &source[str_start], len);
str[len] = 0;
token_add(tokens, token_gen(STRING, str, line));
token_add(tokens, token_gen(TOKEN_STRING, str, line));
}
break;
@ -277,7 +274,7 @@ array_t *tokenize(char *filename)
}
}
if (!found) {
token_add(tokens, token_gen(IDENTIFIER, id, line));
token_add(tokens, token_gen(TOKEN_IDENTIFIER, id, line));
}
i--;
break;
@ -296,7 +293,7 @@ array_t *tokenize(char *filename)
char integer[len + 1];
strncpy(integer, iend, len);
integer[len] = 0;
token_add(tokens, token_gen(NUMBER, integer, line));
token_add(tokens, token_gen(TOKEN_NUMBER, integer, line));
i--;
} 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);
return tokens;
}

View file

@ -8,12 +8,11 @@
int current = 0;
token_t *tokens;
expr_t *expression(void);
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);
} else {
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)
return;
switch (expr->type) {
case BINARY:
free(expr->as.binary.binary_op.value);
case EXPR_BINARY:
free(expr->as.binary.operator.value);
free_expr(expr->as.binary.left);
free_expr(expr->as.binary.right);
free(expr);
break;
case GROUPING:
case EXPR_GROUPING:
free_expr(expr->as.grouping.expression);
free(expr);
break;
case UNARY:
free(expr->as.unary.unary_op.value);
case EXPR_UNARY:
free(expr->as.unary.operator.value);
free_expr(expr->as.unary.right);
free(expr);
break;
case LITERAL:
case EXPR_LITERAL:
if (expr->as.literal.value.type == VAL_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)
{
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)
@ -83,11 +72,10 @@ token_t *previous(void)
void advance(void)
{
if (!isAtEnd())
if (!end())
current++;
}
int check(token_type_t type)
{
if (tokens[current].type == type) {
@ -106,12 +94,13 @@ void consume(token_type_t type, char *message) {
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());
}
if (check(LEFT_PAREN)) {
if (check(TOKEN_LEFT_PAREN)) {
expr_t *expr = expression();
consume(RIGHT_PAREN, "Expect ')' after expression.");
consume(TOKEN_RIGHT_PAREN, "Expect ')' after expression.");
return create_grouping_expr(expr);
}
error(peek(), "Expect expression.");
@ -120,7 +109,7 @@ expr_t *primary(void)
expr_t *unary(void)
{
if (check(BANG) || check(MINUS)) {
if (check(TOKEN_BANG) || check(TOKEN_MINUS)) {
token_t *operator = previous();
expr_t *right = unary();
return create_unary_expr(operator, right);
@ -133,7 +122,7 @@ expr_t *factor(void)
{
expr_t *expr = unary();
while (check(SLASH) || check(STAR)) {
while (check(TOKEN_SLASH) || check(TOKEN_STAR)) {
token_t *operator = previous();
expr_t *right = unary();
expr = create_binary_expr(operator, expr, right);
@ -146,7 +135,7 @@ expr_t *term(void)
{
expr_t *expr = factor();
while (check(MINUS) || check(PLUS)) {
while (check(TOKEN_MINUS) || check(TOKEN_PLUS)) {
token_t *operator = previous();
expr_t *right = factor();
expr = create_binary_expr(operator, expr, right);
@ -159,7 +148,8 @@ expr_t *comparison(void)
{
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();
expr_t *right = term();
expr = create_binary_expr(operator, expr, right);
@ -172,7 +162,7 @@ expr_t *equality(void)
{
expr_t *expr = comparison();
while (check(BANG_EQUAL) || check(EQUAL_EQUAL)) {
while (check(TOKEN_BANG_EQUAL) || check(TOKEN_EQUAL_EQUAL)) {
token_t *operator = previous();
expr_t *right = comparison();
expr = create_binary_expr(operator, expr, right);
@ -186,22 +176,99 @@ expr_t *expression(void)
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)
{
advance();
while (!isAtEnd()) {
if (previous()->type == SEMICOLON) return;
while (!end()) {
if (previous()->type == TOKEN_SEMICOLON) return;
switch (peek()->type) {
case CLASS:
case FUN:
case VAR:
case FOR:
case IF:
case WHILE:
case PRINT:
case RETURN:
case TOKEN_CLASS:
case TOKEN_FUN:
case TOKEN_VAR:
case TOKEN_FOR:
case TOKEN_IF:
case TOKEN_WHILE:
case TOKEN_PRINT:
case TOKEN_RETURN:
return;
default:
return;

View file

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