Compare commits
2 Commits
039de2a761
...
881c6c7740
Author | SHA1 | Date |
---|---|---|
|
881c6c7740 | |
|
c348ea3fdd |
|
@ -104,4 +104,6 @@ dkms.conf
|
|||
zre
|
||||
zre.wasm
|
||||
memory_dump.bin
|
||||
src/build/
|
||||
src/build/
|
||||
.gdb_history
|
||||
.vscode
|
|
@ -1,27 +1,15 @@
|
|||
#include "../../vm.h"
|
||||
#include "../../compiler.h"
|
||||
#include "../../debug.h"
|
||||
#include "../../test.h"
|
||||
#include "../../lexer.h"
|
||||
#include "../../vm.h"
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#define MAX_SRC_SIZE 16384
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
VM vm = {0};
|
||||
vm.frames_size = FRAMES_SIZE;
|
||||
vm.return_stack_size = STACK_SIZE;
|
||||
vm.stack_size = STACK_SIZE;
|
||||
vm.memory_size = MEMORY_SIZE;
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage: %s <file.zrl>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
FILE *f = fopen(argv[1], "rb");
|
||||
void compileFile(const char *path, VM *vm) {
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) {
|
||||
perror("fopen");
|
||||
return 1;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static char source[MAX_SRC_SIZE + 1];
|
||||
|
@ -31,32 +19,51 @@ int main(int argc, char **argv) {
|
|||
fseek(f, 0, SEEK_SET);
|
||||
if (len >= MAX_SRC_SIZE) {
|
||||
perror("source is larget than buffer");
|
||||
return 1;
|
||||
exit(1);
|
||||
}
|
||||
size_t read = fread(source, 1, len, f);
|
||||
source[read] = '\0';
|
||||
fclose(f);
|
||||
|
||||
init_lexer(source);
|
||||
compile(source, vm);
|
||||
}
|
||||
|
||||
static void repl(VM *vm) {
|
||||
char line[1024];
|
||||
for (;;) {
|
||||
Token token = next_token();
|
||||
printf("[%d] %-18s: '%.*s'\n", token.line, token_type_name(token.type), token.length, token.start);
|
||||
if (token.type == TOKEN_EOF) break;
|
||||
printf("> ");
|
||||
|
||||
if (!fgets(line, sizeof(line), stdin)) {
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
/* reset the code counter to 0 */
|
||||
vm->cp = 0;
|
||||
vm->sp = 0;
|
||||
vm->pc = 0;
|
||||
|
||||
compile(line, vm);
|
||||
core_dump(vm);
|
||||
while (step_vm(vm));
|
||||
}
|
||||
}
|
||||
|
||||
/* return 0; */
|
||||
int main(int argc, char **argv) {
|
||||
VM vm = {0};
|
||||
vm.frames_size = FRAMES_SIZE;
|
||||
vm.return_stack_size = STACK_SIZE;
|
||||
vm.stack_size = STACK_SIZE;
|
||||
vm.memory_size = MEMORY_SIZE;
|
||||
|
||||
|
||||
test_hello_world_compile(&vm);
|
||||
/* test_add_compile(&vm); */
|
||||
/* test_add_function_compile(&vm); */
|
||||
/* test_loop_compile(&vm); */
|
||||
/* test_recursive_function_compile(&vm); */
|
||||
|
||||
while(step_vm(&vm));
|
||||
core_dump(&vm);
|
||||
return 0;
|
||||
if (argc == 1) {
|
||||
repl(&vm);
|
||||
} else if (argc == 2) {
|
||||
compileFile(argv[1], &vm);
|
||||
return 1;
|
||||
} else {
|
||||
fprintf(stderr, "Usage: %s <file.zrl>\n", argv[0]);
|
||||
return 64;
|
||||
}
|
||||
|
||||
uint32_t buffer_size = 640 * 480 * sizeof(uint32_t);
|
||||
|
||||
|
|
|
@ -0,0 +1,305 @@
|
|||
#include "compiler.h"
|
||||
|
||||
typedef struct {
|
||||
Token current;
|
||||
Token previous;
|
||||
bool hadError;
|
||||
bool panicMode;
|
||||
} Parser;
|
||||
|
||||
typedef enum {
|
||||
PREC_NONE,
|
||||
PREC_ASSIGNMENT, /* = */
|
||||
PREC_OR, /* or */
|
||||
PREC_AND, /* and */
|
||||
PREC_EQUALITY, /* == != */
|
||||
PREC_COMPARISON, /* < > <= >= */
|
||||
PREC_TERM, /* + - */
|
||||
PREC_FACTOR, /* * / */
|
||||
PREC_UNARY, /* not */
|
||||
PREC_CALL, /* . () */
|
||||
PREC_PRIMARY
|
||||
} Precedence;
|
||||
|
||||
typedef void (*ParseFn)(VM *vm);
|
||||
|
||||
typedef struct {
|
||||
ParseFn prefix;
|
||||
ParseFn infix;
|
||||
Precedence precedence;
|
||||
} ParseRule;
|
||||
|
||||
Parser parser;
|
||||
|
||||
void errorAt(Token *token, const char *message) {
|
||||
if (parser.panicMode)
|
||||
return;
|
||||
parser.panicMode = true;
|
||||
fprintf(stderr, "[line %d] Error", token->line);
|
||||
|
||||
if (token->type == TOKEN_EOF) {
|
||||
fprintf(stderr, " at end");
|
||||
} else if (token->type == TOKEN_ERROR) {
|
||||
} else {
|
||||
fprintf(stderr, " at '%.*s'", token->length, token->start);
|
||||
}
|
||||
|
||||
fprintf(stderr, ": %s\n", message);
|
||||
parser.hadError = true;
|
||||
}
|
||||
|
||||
void error(const char *message) { errorAt(&parser.previous, message); }
|
||||
|
||||
void errorAtCurrent(const char *message) {
|
||||
errorAt(&parser.current, message);
|
||||
}
|
||||
|
||||
void advance() {
|
||||
parser.previous = parser.current;
|
||||
|
||||
for (;;) {
|
||||
parser.current = nextToken();
|
||||
if (parser.current.type != TOKEN_ERROR)
|
||||
break;
|
||||
|
||||
errorAtCurrent(parser.current.start);
|
||||
}
|
||||
}
|
||||
|
||||
void consume(TokenType type, const char *message) {
|
||||
if (parser.current.type == type) {
|
||||
advance();
|
||||
return;
|
||||
}
|
||||
|
||||
errorAtCurrent(message);
|
||||
}
|
||||
|
||||
void emitOp(VM *vm, uint8_t opcode, uint8_t dest, uint8_t src1,
|
||||
uint8_t src2) {
|
||||
vm->code[vm->cp++].u = OP(opcode, dest, src1, src2);
|
||||
}
|
||||
|
||||
void expression(VM *vm);
|
||||
ParseRule *getRule(TokenType type);
|
||||
void parsePrecedence(VM *vm, Precedence precedence);
|
||||
|
||||
void number(VM *vm) {
|
||||
if (parser.previous.type == TOKEN_INT_LITERAL) {
|
||||
char *endptr;
|
||||
int32_t value = (int32_t)strtol(parser.previous.start, &endptr, 10);
|
||||
emitOp(vm, OP_LOADI, vm->frames[vm->fp].rp++, 0, 0);
|
||||
vm->code[vm->cp++].i = value;
|
||||
return;
|
||||
} else if (parser.previous.type == TOKEN_UINT_LITERAL) {
|
||||
long value = atol(parser.previous.start);
|
||||
emitOp(vm, OP_LOADU, vm->frames[vm->fp].rp++, 0, 0);
|
||||
vm->code[vm->cp++].u = value;
|
||||
return;
|
||||
} else if (parser.previous.type == TOKEN_FLOAT_LITERAL) {
|
||||
float value = atof(parser.previous.start);
|
||||
emitOp(vm, OP_LOADF, vm->frames[vm->fp].rp++, 0, 0);
|
||||
vm->code[vm->cp++].f = value;
|
||||
return;
|
||||
}
|
||||
errorAtCurrent("Invalid number format");
|
||||
}
|
||||
|
||||
void grouping(VM *vm) {
|
||||
expression(vm);
|
||||
consume(TOKEN_RPAREN, "Expect ')' after expression.");
|
||||
}
|
||||
|
||||
void unary(VM *vm) {
|
||||
TokenType operatorType = parser.previous.type;
|
||||
|
||||
parsePrecedence(vm, PREC_UNARY);
|
||||
|
||||
switch (operatorType) {
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void binary(VM *vm) {
|
||||
TokenType operatorType = parser.previous.type;
|
||||
ParseRule *rule = getRule(operatorType);
|
||||
parsePrecedence(vm, (Precedence)(rule->precedence + 1));
|
||||
TokenType operandType = parser.previous.type;
|
||||
|
||||
switch (operatorType) {
|
||||
case TOKEN_PLUS:
|
||||
if (operandType == TOKEN_UINT_LITERAL) {
|
||||
Frame f = vm->frames[vm->fp];
|
||||
uint32_t src1 = f.rp--;
|
||||
uint32_t src2 = f.rp--;
|
||||
uint32_t dest = f.rp++;
|
||||
emitOp(vm, OP_ADD_UINT, dest, src1, src2);
|
||||
} else if (operandType == TOKEN_INT_LITERAL) {
|
||||
Frame f = vm->frames[vm->fp];
|
||||
uint32_t src1 = f.rp--;
|
||||
uint32_t src2 = f.rp--;
|
||||
uint32_t dest = f.rp++;
|
||||
emitOp(vm, OP_ADD_INT, dest, src1, src2);
|
||||
} else if (operandType == TOKEN_FLOAT_LITERAL) {
|
||||
Frame f = vm->frames[vm->fp];
|
||||
uint32_t src1 = f.rp--;
|
||||
uint32_t src2 = f.rp--;
|
||||
uint32_t dest = f.rp++;
|
||||
emitOp(vm, OP_ADD_REAL, dest, src1, src2);
|
||||
} else {
|
||||
error("not numeric");
|
||||
}
|
||||
break;
|
||||
case TOKEN_MINUS:
|
||||
if (operandType == TOKEN_UINT_LITERAL) {
|
||||
Frame f = vm->frames[vm->fp];
|
||||
uint32_t src1 = f.rp--;
|
||||
uint32_t src2 = f.rp--;
|
||||
uint32_t dest = f.rp++;
|
||||
emitOp(vm, OP_SUB_UINT, dest, src1, src2);
|
||||
} else if (operandType == TOKEN_INT_LITERAL) {
|
||||
Frame f = vm->frames[vm->fp];
|
||||
uint32_t src1 = f.rp--;
|
||||
uint32_t src2 = f.rp--;
|
||||
uint32_t dest = f.rp++;
|
||||
emitOp(vm, OP_SUB_INT, dest, src1, src2);
|
||||
} else if (operandType == TOKEN_FLOAT_LITERAL) {
|
||||
Frame f = vm->frames[vm->fp];
|
||||
uint32_t src1 = f.rp--;
|
||||
uint32_t src2 = f.rp--;
|
||||
uint32_t dest = f.rp++;
|
||||
emitOp(vm, OP_SUB_REAL, dest, src1, src2);
|
||||
} else {
|
||||
error("not numeric");
|
||||
}
|
||||
break;
|
||||
case TOKEN_STAR:
|
||||
if (operandType == TOKEN_UINT_LITERAL) {
|
||||
Frame f = vm->frames[vm->fp];
|
||||
uint32_t src1 = f.rp--;
|
||||
uint32_t src2 = f.rp--;
|
||||
uint32_t dest = f.rp++;
|
||||
emitOp(vm, OP_MUL_UINT, dest, src1, src2);
|
||||
} else if (operandType == TOKEN_INT_LITERAL) {
|
||||
Frame f = vm->frames[vm->fp];
|
||||
uint32_t src1 = f.rp--;
|
||||
uint32_t src2 = f.rp--;
|
||||
uint32_t dest = f.rp++;
|
||||
emitOp(vm, OP_MUL_INT, dest, src1, src2);
|
||||
} else if (operandType == TOKEN_FLOAT_LITERAL) {
|
||||
Frame f = vm->frames[vm->fp];
|
||||
uint32_t src1 = f.rp--;
|
||||
uint32_t src2 = f.rp--;
|
||||
uint32_t dest = f.rp++;
|
||||
emitOp(vm, OP_MUL_REAL, dest, src1, src2);
|
||||
} else {
|
||||
error("not numeric");
|
||||
}
|
||||
break;
|
||||
case TOKEN_SLASH:
|
||||
if (operandType == TOKEN_UINT_LITERAL) {
|
||||
Frame f = vm->frames[vm->fp];
|
||||
uint32_t src1 = f.rp--;
|
||||
uint32_t src2 = f.rp--;
|
||||
uint32_t dest = f.rp++;
|
||||
emitOp(vm, OP_DIV_UINT, dest, src1, src2);
|
||||
} else if (operandType == TOKEN_INT_LITERAL) {
|
||||
Frame f = vm->frames[vm->fp];
|
||||
uint32_t src1 = f.rp--;
|
||||
uint32_t src2 = f.rp--;
|
||||
uint32_t dest = f.rp++;
|
||||
emitOp(vm, OP_DIV_INT, dest, src1, src2);
|
||||
} else if (operandType == TOKEN_FLOAT_LITERAL) {
|
||||
Frame f = vm->frames[vm->fp];
|
||||
uint32_t src1 = f.rp--;
|
||||
uint32_t src2 = f.rp--;
|
||||
uint32_t dest = f.rp++;
|
||||
emitOp(vm, OP_DIV_REAL, dest, src1, src2);
|
||||
} else {
|
||||
error("not numeric");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return; /* Unreachable. */
|
||||
}
|
||||
}
|
||||
|
||||
ParseRule rules[] = {
|
||||
[TOKEN_LPAREN] = {grouping, NULL, PREC_NONE},
|
||||
[TOKEN_RPAREN] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_LBRACE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_RBRACE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_COMMA] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_DOT] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_MINUS] = {NULL, binary, PREC_TERM},
|
||||
[TOKEN_PLUS] = {NULL, binary, PREC_TERM},
|
||||
[TOKEN_SEMICOLON] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_SLASH] = {NULL, binary, PREC_FACTOR},
|
||||
[TOKEN_STAR] = {NULL, binary, PREC_FACTOR},
|
||||
[TOKEN_BANG] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_BANG_EQ] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_EQ] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_EQ_EQ] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_GT] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_GTE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_LT] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_LTE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_IDENTIFIER] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_STRING_LITERAL] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_INT_LITERAL] = {number, NULL, PREC_NONE},
|
||||
[TOKEN_UINT_LITERAL] = {number, NULL, PREC_NONE},
|
||||
[TOKEN_FLOAT_LITERAL] = {number, NULL, PREC_NONE},
|
||||
[TOKEN_KEYWORD_ELSE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_KEYWORD_FOR] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_KEYWORD_FN] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_KEYWORD_IF] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_OPERATOR_AND] = {NULL, binary, PREC_NONE},
|
||||
[TOKEN_OPERATOR_OR] = {NULL, binary, PREC_NONE},
|
||||
[TOKEN_OPERATOR_NOT] = {unary, NULL, PREC_NONE},
|
||||
[TOKEN_KEYWORD_NIL] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_KEYWORD_TRUE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_KEYWORD_FALSE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_KEYWORD_PRINT] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_KEYWORD_RETURN] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_KEYWORD_THIS] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_KEYWORD_LET] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_KEYWORD_WHILE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_ERROR] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_EOF] = {NULL, NULL, PREC_NONE},
|
||||
};
|
||||
|
||||
ParseRule *getRule(TokenType type) { return &rules[type]; }
|
||||
|
||||
void parsePrecedence(VM *vm, Precedence precedence) {
|
||||
advance();
|
||||
ParseFn prefixRule = getRule(parser.previous.type)->prefix;
|
||||
if (prefixRule == NULL) {
|
||||
error("Expect expression.");
|
||||
return;
|
||||
}
|
||||
|
||||
prefixRule(vm);
|
||||
|
||||
while (precedence <= getRule(parser.current.type)->precedence) {
|
||||
advance();
|
||||
ParseFn infixRule = getRule(parser.previous.type)->infix;
|
||||
infixRule(vm);
|
||||
}
|
||||
}
|
||||
|
||||
void expression(VM *vm) { parsePrecedence(vm, PREC_ASSIGNMENT); }
|
||||
|
||||
bool compile(const char *source, VM *vm) {
|
||||
initLexer(source);
|
||||
|
||||
parser.hadError = false;
|
||||
parser.panicMode = false;
|
||||
|
||||
advance();
|
||||
expression(vm);
|
||||
consume(TOKEN_EOF, "end of file");
|
||||
emitOp(vm, OP_HALT, 0, 0, 0);
|
||||
|
||||
return !parser.hadError;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef ZRL_COMPILER_H
|
||||
#define ZRL_COMPILER_H
|
||||
|
||||
#include "lexer.h"
|
||||
#include "opcodes.h"
|
||||
|
||||
|
||||
bool compile(const char* source, VM* vm);
|
||||
|
||||
#endif
|
37
src/debug.c
37
src/debug.c
|
@ -1,27 +1,34 @@
|
|||
#include "debug.h"
|
||||
|
||||
/**
|
||||
* Dumps the vm memory to a file.
|
||||
* Dumps the vm memory and code to a file.
|
||||
*/
|
||||
int core_dump(VM *vm) {
|
||||
FILE *file = fopen("memory_dump.bin", "wb");
|
||||
if (!file) {
|
||||
perror("Failed to open file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
FILE *file = fopen("memory_dump.bin", "wb");
|
||||
if (!file) {
|
||||
perror("Failed to open file");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
size_t code_written = fwrite(vm->code, 1, vm->code_size, file);
|
||||
if (code_written != vm->code_size) {
|
||||
fprintf(stderr, "Incomplete code write: %zu bytes written out of %u\n", code_written, vm->code_size);
|
||||
fclose(file);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
size_t memory_written = fwrite(vm->memory, 1, vm->memory_size, file);
|
||||
if (memory_written != vm->memory_size) {
|
||||
fprintf(stderr, "Incomplete memory write: %zu bytes written out of %u\n", memory_written, vm->memory_size);
|
||||
fclose(file);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
size_t written = fwrite(vm->memory, 1, vm->memory_size, file);
|
||||
if (written != vm->memory_size) {
|
||||
fprintf(stderr, "Incomplete write: %zu bytes written out of %u\n", written,
|
||||
vm->memory_size);
|
||||
fclose(file);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return EXIT_SUCCESS;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Print opcode.
|
||||
*/
|
||||
|
|
378
src/lexer.c
378
src/lexer.c
|
@ -1,33 +1,71 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "lexer.h"
|
||||
|
||||
typedef struct {
|
||||
const char *start;
|
||||
const char *current;
|
||||
int line;
|
||||
} Lexer;
|
||||
|
||||
Lexer lexer;
|
||||
|
||||
void init_lexer(const char *source) {
|
||||
void initLexer(const char *source) {
|
||||
lexer.start = source;
|
||||
lexer.current = source;
|
||||
lexer.line = 1;
|
||||
}
|
||||
|
||||
int is_at_end() { return *lexer.current == '\0'; }
|
||||
static bool isAlpha(char c) {
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
|
||||
}
|
||||
|
||||
char advance() { return *lexer.current++; }
|
||||
static bool isDigit(char c) { return c >= '0' && c <= '9'; }
|
||||
|
||||
char peek() { return *lexer.current; }
|
||||
static bool isAtEnd() { return *lexer.current == '\0'; }
|
||||
|
||||
char peek_next() {
|
||||
if (is_at_end())
|
||||
static char advance() {
|
||||
lexer.current++;
|
||||
return lexer.current[-1];
|
||||
}
|
||||
|
||||
static char peek() { return *lexer.current; }
|
||||
|
||||
static char peekNext() {
|
||||
if (isAtEnd())
|
||||
return '\0';
|
||||
return lexer.current[1];
|
||||
}
|
||||
|
||||
int match(char expected) {
|
||||
static bool match(char expected) {
|
||||
if (isAtEnd())
|
||||
return false;
|
||||
if (*lexer.current != expected)
|
||||
return 0;
|
||||
return false;
|
||||
lexer.current++;
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
void skip_whitespace() {
|
||||
static Token makeToken(TokenType type) {
|
||||
Token token;
|
||||
token.type = type;
|
||||
token.start = lexer.start;
|
||||
token.length = (int)(lexer.current - lexer.start);
|
||||
token.line = lexer.line;
|
||||
return token;
|
||||
}
|
||||
|
||||
static Token errorToken(const char *message) {
|
||||
Token token;
|
||||
token.type = TOKEN_ERROR;
|
||||
token.start = message;
|
||||
token.length = (int)strlen(message);
|
||||
token.line = lexer.line;
|
||||
return token;
|
||||
}
|
||||
|
||||
static void skipWhitespace() {
|
||||
for (;;) {
|
||||
char c = peek();
|
||||
switch (c) {
|
||||
|
@ -40,13 +78,13 @@ void skip_whitespace() {
|
|||
lexer.line++;
|
||||
advance();
|
||||
break;
|
||||
case '!':
|
||||
if (peek_next() == '!') {
|
||||
while (peek() != '\n' && !is_at_end())
|
||||
case '/':
|
||||
if (peekNext() == '/') {
|
||||
/* A comment goes until the end of the line. */
|
||||
while (peek() != '\n' && !isAtEnd())
|
||||
advance();
|
||||
} else {
|
||||
while (peek() != '\n' && !is_at_end())
|
||||
advance();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -55,240 +93,150 @@ void skip_whitespace() {
|
|||
}
|
||||
}
|
||||
|
||||
Token make_token(TokenType type) {
|
||||
Token token;
|
||||
token.type = type;
|
||||
token.start = lexer.start;
|
||||
token.length = (int)(lexer.current - lexer.start);
|
||||
token.line = lexer.line;
|
||||
return token;
|
||||
}
|
||||
|
||||
Token error_token(const char *message) {
|
||||
Token token;
|
||||
token.type = TOKEN_ERROR;
|
||||
token.start = message;
|
||||
token.length = (int)strlen(message);
|
||||
token.line = lexer.line;
|
||||
return token;
|
||||
}
|
||||
|
||||
int is_alpha(char c) { return isalpha(c) || c == '_'; }
|
||||
|
||||
int is_digit(char c) { return isdigit(c); }
|
||||
|
||||
Token number() {
|
||||
while (is_digit(peek()))
|
||||
advance();
|
||||
|
||||
if (peek() == '.' && is_digit(peek_next())) {
|
||||
advance();
|
||||
while (is_digit(peek()))
|
||||
advance();
|
||||
return make_token(TOKEN_FLOAT_LITERAL);
|
||||
static TokenType checkKeyword(int start, int length, const char *rest,
|
||||
TokenType type) {
|
||||
if (lexer.current - lexer.start == start + length &&
|
||||
memcmp(lexer.start + start, rest, length) == 0) {
|
||||
return type;
|
||||
}
|
||||
|
||||
return make_token(TOKEN_INT_LITERAL);
|
||||
return TOKEN_IDENTIFIER;
|
||||
}
|
||||
|
||||
Token string() {
|
||||
while (peek() != '"' && !is_at_end()) {
|
||||
static TokenType identifierType() {
|
||||
switch (lexer.start[0]) {
|
||||
case 'a':
|
||||
return checkKeyword(1, 2, "nd", TOKEN_OPERATOR_AND);
|
||||
case 'e':
|
||||
return checkKeyword(1, 3, "lse", TOKEN_KEYWORD_ELSE);
|
||||
case 'f':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'a':
|
||||
return checkKeyword(2, 3, "lse", TOKEN_KEYWORD_FALSE);
|
||||
case 'o':
|
||||
return checkKeyword(2, 1, "r", TOKEN_KEYWORD_FOR);
|
||||
}
|
||||
return checkKeyword(1, 1, "n", TOKEN_KEYWORD_FN);
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
return checkKeyword(1, 1, "f", TOKEN_KEYWORD_IF);
|
||||
case 'n':
|
||||
return checkKeyword(1, 2, "il", TOKEN_KEYWORD_NIL);
|
||||
case 'o':
|
||||
return checkKeyword(1, 1, "r", TOKEN_OPERATOR_OR);
|
||||
case 'p':
|
||||
return checkKeyword(1, 4, "rint", TOKEN_KEYWORD_PRINT);
|
||||
case 'r':
|
||||
return checkKeyword(1, 5, "eturn", TOKEN_KEYWORD_RETURN);
|
||||
case 't':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'h':
|
||||
return checkKeyword(2, 2, "is", TOKEN_KEYWORD_THIS);
|
||||
case 'r':
|
||||
return checkKeyword(2, 2, "ue", TOKEN_KEYWORD_TRUE);
|
||||
case 'y':
|
||||
return checkKeyword(2, 2, "pe", TOKEN_KEYWORD_TYPE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
return checkKeyword(1, 2, "et", TOKEN_KEYWORD_LET);
|
||||
case 'w':
|
||||
return checkKeyword(1, 4, "hile", TOKEN_KEYWORD_WHILE);
|
||||
}
|
||||
|
||||
return TOKEN_IDENTIFIER;
|
||||
}
|
||||
|
||||
static Token identifier() {
|
||||
while (isAlpha(peek()) || isDigit(peek()))
|
||||
advance();
|
||||
return makeToken(identifierType());
|
||||
}
|
||||
|
||||
static Token number() {
|
||||
while (isDigit(peek()))
|
||||
advance();
|
||||
|
||||
/* Look for a fractional part. */
|
||||
if (peek() == '.' && isDigit(peekNext())) {
|
||||
/* Consume the ".". */
|
||||
advance();
|
||||
|
||||
while (isDigit(peek()))
|
||||
advance();
|
||||
|
||||
return makeToken(TOKEN_FLOAT_LITERAL);
|
||||
}
|
||||
|
||||
return makeToken(TOKEN_INT_LITERAL);
|
||||
}
|
||||
|
||||
static Token string() {
|
||||
while (peek() != '"' && !isAtEnd()) {
|
||||
if (peek() == '\n')
|
||||
lexer.line++;
|
||||
advance();
|
||||
}
|
||||
|
||||
if (is_at_end())
|
||||
return error_token("Unterminated string.");
|
||||
if (isAtEnd())
|
||||
return errorToken("Unterminated string.");
|
||||
|
||||
/* The closing quote. */
|
||||
advance();
|
||||
return make_token(TOKEN_STRING_LITERAL);
|
||||
return makeToken(TOKEN_STRING_LITERAL);
|
||||
}
|
||||
|
||||
Token identifier() {
|
||||
while (is_alpha(peek()) || is_digit(peek()))
|
||||
advance();
|
||||
|
||||
int length = (int)(lexer.current - lexer.start);
|
||||
const char *text = lexer.start;
|
||||
|
||||
if (length == 4 && strncmp(text, "init", 4) == 0)
|
||||
return make_token(TOKEN_KEYWORD_INIT);
|
||||
if (length == 4 && strncmp(text, "this", 4) == 0)
|
||||
return make_token(TOKEN_KEYWORD_THIS);
|
||||
if (length == 4 && strncmp(text, "type", 4) == 0)
|
||||
return make_token(TOKEN_KEYWORD_TYPE);
|
||||
if (length == 2 && strncmp(text, "fn", 2) == 0)
|
||||
return make_token(TOKEN_KEYWORD_FN);
|
||||
if (length == 3 && strncmp(text, "let", 3) == 0)
|
||||
return make_token(TOKEN_KEYWORD_LET);
|
||||
if (length == 5 && strncmp(text, "const", 5) == 0)
|
||||
return make_token(TOKEN_KEYWORD_CONST);
|
||||
if (length == 2 && strncmp(text, "if", 2) == 0)
|
||||
return make_token(TOKEN_KEYWORD_IF);
|
||||
if (length == 4 && strncmp(text, "else", 4) == 0)
|
||||
return make_token(TOKEN_KEYWORD_ELSE);
|
||||
if (length == 5 && strncmp(text, "while", 5) == 0)
|
||||
return make_token(TOKEN_KEYWORD_WHILE);
|
||||
if (length == 3 && strncmp(text, "for", 3) == 0)
|
||||
return make_token(TOKEN_KEYWORD_FOR);
|
||||
if (length == 6 && strncmp(text, "return", 6) == 0)
|
||||
return make_token(TOKEN_KEYWORD_RETURN);
|
||||
if (length == 3 && strncmp(text, "use", 3) == 0)
|
||||
return make_token(TOKEN_KEYWORD_USE);
|
||||
if (length == 2 && strncmp(text, "is", 2) == 0)
|
||||
return make_token(TOKEN_OPERATOR_IS);
|
||||
if (length == 3 && strncmp(text, "int", 3) == 0)
|
||||
return make_token(TOKEN_TYPE_INT);
|
||||
if (length == 3 && strncmp(text, "nat", 3) == 0)
|
||||
return make_token(TOKEN_TYPE_NAT);
|
||||
if (length == 3 && strncmp(text, "str", 3) == 0)
|
||||
return make_token(TOKEN_TYPE_STR);
|
||||
if (length == 3 && strncmp(text, "real", 4) == 0)
|
||||
return make_token(TOKEN_TYPE_REAL);
|
||||
|
||||
return make_token(TOKEN_IDENTIFIER);
|
||||
}
|
||||
|
||||
Token next_token() {
|
||||
skip_whitespace();
|
||||
Token nextToken() {
|
||||
skipWhitespace();
|
||||
lexer.start = lexer.current;
|
||||
|
||||
if (is_at_end())
|
||||
return make_token(TOKEN_EOF);
|
||||
if (isAtEnd())
|
||||
return makeToken(TOKEN_EOF);
|
||||
|
||||
char c = advance();
|
||||
|
||||
if (is_alpha(c))
|
||||
if (isAlpha(c))
|
||||
return identifier();
|
||||
if (is_digit(c))
|
||||
if (isDigit(c))
|
||||
return number();
|
||||
|
||||
switch (c) {
|
||||
case '(':
|
||||
return make_token(TOKEN_LPAREN);
|
||||
return makeToken(TOKEN_LPAREN);
|
||||
case ')':
|
||||
return make_token(TOKEN_RPAREN);
|
||||
return makeToken(TOKEN_RPAREN);
|
||||
case '{':
|
||||
return make_token(TOKEN_LBRACE);
|
||||
return makeToken(TOKEN_LBRACE);
|
||||
case '}':
|
||||
return make_token(TOKEN_RBRACE);
|
||||
case '[':
|
||||
return make_token(TOKEN_LBRACKET);
|
||||
case ']':
|
||||
return make_token(TOKEN_RBRACKET);
|
||||
case ',':
|
||||
return make_token(TOKEN_COMMA);
|
||||
case '.':
|
||||
return make_token(TOKEN_DOT);
|
||||
case ':':
|
||||
return make_token(TOKEN_COLON);
|
||||
return makeToken(TOKEN_RBRACE);
|
||||
case ';':
|
||||
return make_token(TOKEN_SEMICOLON);
|
||||
case '+':
|
||||
return make_token(TOKEN_PLUS);
|
||||
return makeToken(TOKEN_SEMICOLON);
|
||||
case ',':
|
||||
return makeToken(TOKEN_COMMA);
|
||||
case '.':
|
||||
return makeToken(TOKEN_DOT);
|
||||
case '-':
|
||||
return make_token(TOKEN_MINUS);
|
||||
case '*':
|
||||
return make_token(TOKEN_STAR);
|
||||
return makeToken(TOKEN_MINUS);
|
||||
case '+':
|
||||
return makeToken(TOKEN_PLUS);
|
||||
case '/':
|
||||
return make_token(TOKEN_SLASH);
|
||||
return makeToken(TOKEN_SLASH);
|
||||
case '*':
|
||||
return makeToken(TOKEN_STAR);
|
||||
case '!':
|
||||
return make_token(match('=') ? TOKEN_BANG_EQ : TOKEN_BANG);
|
||||
return makeToken(match('=') ? TOKEN_BANG_EQ : TOKEN_BANG);
|
||||
case '=':
|
||||
return make_token(match('=') ? TOKEN_EQ_EQ : TOKEN_EQ);
|
||||
return makeToken(match('=') ? TOKEN_EQ_EQ : TOKEN_EQ);
|
||||
case '<':
|
||||
return make_token(match('=') ? TOKEN_LTE : TOKEN_LT);
|
||||
return makeToken(match('=') ? TOKEN_LTE : TOKEN_LT);
|
||||
case '>':
|
||||
return make_token(match('=') ? TOKEN_GTE : TOKEN_GT);
|
||||
return makeToken(match('=') ? TOKEN_GTE : TOKEN_GT);
|
||||
case '"':
|
||||
return string();
|
||||
}
|
||||
|
||||
return error_token("Unexpected character.");
|
||||
}
|
||||
|
||||
const char *token_type_name(TokenType type) {
|
||||
switch (type) {
|
||||
case TOKEN_IDENTIFIER:
|
||||
return "identifier";
|
||||
case TOKEN_INT_LITERAL:
|
||||
return "int literal";
|
||||
case TOKEN_FLOAT_LITERAL:
|
||||
return "real literal";
|
||||
case TOKEN_STRING_LITERAL:
|
||||
return "string literal";
|
||||
case TOKEN_TYPE_INT:
|
||||
return "int";
|
||||
case TOKEN_TYPE_REAL:
|
||||
return "real";
|
||||
case TOKEN_TYPE_STR:
|
||||
return "str";
|
||||
case TOKEN_TYPE_NAT:
|
||||
return "nat";
|
||||
case TOKEN_KEYWORD_THIS:
|
||||
return "this";
|
||||
case TOKEN_KEYWORD_TYPE:
|
||||
return "type";
|
||||
case TOKEN_KEYWORD_FN:
|
||||
return "fn";
|
||||
case TOKEN_KEYWORD_LET:
|
||||
return "let";
|
||||
case TOKEN_KEYWORD_CONST:
|
||||
return "const";
|
||||
case TOKEN_KEYWORD_IF:
|
||||
return "if";
|
||||
case TOKEN_KEYWORD_ELSE:
|
||||
return "else";
|
||||
case TOKEN_KEYWORD_WHILE:
|
||||
return "while";
|
||||
case TOKEN_KEYWORD_FOR:
|
||||
return "for";
|
||||
case TOKEN_KEYWORD_RETURN:
|
||||
return "return";
|
||||
case TOKEN_KEYWORD_INIT:
|
||||
return "init";
|
||||
case TOKEN_KEYWORD_USE:
|
||||
return "use";
|
||||
case TOKEN_OPERATOR_IS:
|
||||
return "is";
|
||||
case TOKEN_BANG:
|
||||
return "!";
|
||||
case TOKEN_EQ:
|
||||
return "=";
|
||||
case TOKEN_DOT:
|
||||
return ".";
|
||||
case TOKEN_COMMA:
|
||||
return ",";
|
||||
case TOKEN_COLON:
|
||||
return ":";
|
||||
case TOKEN_SEMICOLON:
|
||||
return ";";
|
||||
case TOKEN_PLUS:
|
||||
return "+";
|
||||
case TOKEN_MINUS:
|
||||
return "-";
|
||||
case TOKEN_STAR:
|
||||
return "*";
|
||||
case TOKEN_SLASH:
|
||||
return "/";
|
||||
case TOKEN_LPAREN:
|
||||
return "(";
|
||||
case TOKEN_RPAREN:
|
||||
return ")";
|
||||
case TOKEN_LBRACE:
|
||||
return "{";
|
||||
case TOKEN_RBRACE:
|
||||
return "}";
|
||||
case TOKEN_LBRACKET:
|
||||
return "[";
|
||||
case TOKEN_RBRACKET:
|
||||
return "]";
|
||||
case TOKEN_EOF:
|
||||
return "eof";
|
||||
case TOKEN_ERROR:
|
||||
return "error";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
return errorToken("Unexpected character.");
|
||||
}
|
||||
|
|
32
src/lexer.h
32
src/lexer.h
|
@ -1,14 +1,11 @@
|
|||
#ifndef ZRL_LEXER_H
|
||||
#define ZRL_LEXER_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#ifndef zre_lexer_h
|
||||
#define zre_lexer_h
|
||||
|
||||
typedef enum {
|
||||
TOKEN_EOF,
|
||||
TOKEN_IDENTIFIER,
|
||||
TOKEN_INT_LITERAL,
|
||||
TOKEN_UINT_LITERAL,
|
||||
TOKEN_FLOAT_LITERAL,
|
||||
TOKEN_STRING_LITERAL,
|
||||
TOKEN_TYPE_INT,
|
||||
|
@ -27,7 +24,14 @@ typedef enum {
|
|||
TOKEN_KEYWORD_USE,
|
||||
TOKEN_KEYWORD_INIT,
|
||||
TOKEN_KEYWORD_THIS,
|
||||
TOKEN_KEYWORD_PRINT,
|
||||
TOKEN_KEYWORD_NIL,
|
||||
TOKEN_KEYWORD_TRUE,
|
||||
TOKEN_KEYWORD_FALSE,
|
||||
TOKEN_OPERATOR_IS,
|
||||
TOKEN_OPERATOR_NOT,
|
||||
TOKEN_OPERATOR_AND,
|
||||
TOKEN_OPERATOR_OR,
|
||||
TOKEN_BANG,
|
||||
TOKEN_BANG_EQ,
|
||||
TOKEN_EQ,
|
||||
|
@ -60,19 +64,7 @@ typedef struct {
|
|||
int line;
|
||||
} Token;
|
||||
|
||||
typedef struct {
|
||||
const char *keyword;
|
||||
TokenType token;
|
||||
} Keyword;
|
||||
|
||||
typedef struct {
|
||||
const char *start;
|
||||
const char *current;
|
||||
int line;
|
||||
} Lexer;
|
||||
|
||||
void init_lexer(const char *source);
|
||||
const char *token_type_name(TokenType type);
|
||||
Token next_token();
|
||||
void initLexer(const char *source);
|
||||
Token nextToken();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -133,6 +133,7 @@ typedef enum {
|
|||
OP_READ_STRING, /* gets : dest = gets as str */
|
||||
OP_PRINT_STRING, /* puts : write src1 to stdout */
|
||||
OP_CMP_STRING, /* cmps : dest = (str == src2) as bool */
|
||||
OP_NOT,
|
||||
} Opcode;
|
||||
|
||||
typedef enum {
|
||||
|
|
30
src/vm.c
30
src/vm.c
|
@ -27,7 +27,8 @@
|
|||
* Embeds a string into the VM
|
||||
*/
|
||||
uint32_t str_alloc(VM *vm, const char *str, uint32_t length) {
|
||||
if (!length) length = strlen(str);
|
||||
if (!length)
|
||||
length = strlen(str);
|
||||
uint32_t str_addr = vm->mp;
|
||||
vm->memory[vm->mp++].u = length;
|
||||
uint32_t i, j = 0;
|
||||
|
@ -57,16 +58,21 @@ bool step_vm(VM *vm) {
|
|||
switch (opcode) {
|
||||
case OP_HALT:
|
||||
return false;
|
||||
case OP_CALL:; /* whats up with this semicolon? ANSI C does not allow you to create a variabel after a case, so this noop is here */
|
||||
uint32_t jmp = vm->code[vm->pc++].u; /* location of function in code */
|
||||
case OP_CALL: {
|
||||
uint32_t jmp = vm->code[vm->pc++].u; /* location of function in code */
|
||||
vm->return_stack[vm->rp++].u = vm->pc; /* set return address */
|
||||
vm->fp++; /* increment to the next free frame */
|
||||
vm->frames[vm->fp].allocated.start = vm->mp; /* set start of new memory block */
|
||||
vm->frames[vm->fp].allocated.start =
|
||||
vm->mp; /* set start of new memory block */
|
||||
vm->pc = jmp;
|
||||
return true;
|
||||
}
|
||||
case OP_RETURN:
|
||||
vm->pc = vm->return_stack[--vm->rp].u; /* set pc to return address */
|
||||
vm->mp = vm->frames[vm->fp--].allocated.start; /* reset memory pointer to start of old slice, pop the frame */
|
||||
vm->frames[vm->fp].rp = 0; /* reset register ptr */
|
||||
vm->pc = vm->return_stack[--vm->rp].u; /* set pc to return address */
|
||||
vm->mp =
|
||||
vm->frames[vm->fp--].allocated.start; /* reset memory pointer to start
|
||||
of old slice, pop the frame */
|
||||
return true;
|
||||
case OP_LOADI:
|
||||
vm->frames[vm->fp].registers[dest].i = vm->code[vm->pc++].i;
|
||||
|
@ -195,6 +201,9 @@ bool step_vm(VM *vm) {
|
|||
case OP_JLE_REAL: {
|
||||
COMPARE_AND_JUMP(float, u, <=);
|
||||
}
|
||||
case OP_NOT: {
|
||||
/* TODO implement not */
|
||||
}
|
||||
case OP_INT_TO_STRING: {
|
||||
int32_t a = (int32_t)vm->frames[vm->fp].registers[src1].i; /* get value */
|
||||
char buffer[32];
|
||||
|
@ -256,7 +265,8 @@ bool step_vm(VM *vm) {
|
|||
uint32_t addr2 = (uint32_t)vm->frames[vm->fp].registers[src2].u;
|
||||
uint32_t length1 = vm->memory[addr1 - 1].u;
|
||||
uint32_t length2 = vm->memory[addr2 - 1].u;
|
||||
uint32_t equal = 1; /* we dont have a native boolean type so we use uint32_t */
|
||||
uint32_t equal =
|
||||
1; /* we dont have a native boolean type so we use uint32_t */
|
||||
|
||||
if (length1 != length2) {
|
||||
equal = 0;
|
||||
|
@ -270,9 +280,9 @@ bool step_vm(VM *vm) {
|
|||
break;
|
||||
}
|
||||
if ((char1 & 0xFF) == '\0' && (char2 & 0xFF) == '\0') {
|
||||
equal = 1;
|
||||
break;
|
||||
}
|
||||
equal = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
vm->memory[dest].u = equal;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
fn add(i8 a, i8 b) i8 {
|
||||
fn add(int a, int b) int {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
i8 sum = add(1, 1);
|
||||
int sum = add(1, 1);
|
||||
print(sum.toS());
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
fn fib(i32 n) i32 {
|
||||
fn fib(int n) int {
|
||||
if (n < 2) return n;
|
||||
return fib(n - 2) + fib(n - 1);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue