Compare commits

...

2 Commits

Author SHA1 Message Date
zongor 881c6c7740 remove doc 2025-08-03 16:03:50 -04:00
zongor c348ea3fdd add initial compiler 2025-08-03 16:03:25 -04:00
11 changed files with 579 additions and 297 deletions

4
.gitignore vendored
View File

@ -104,4 +104,6 @@ dkms.conf
zre
zre.wasm
memory_dump.bin
src/build/
src/build/
.gdb_history
.vscode

View File

@ -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);

305
src/compiler.c Normal file
View File

@ -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;
}

10
src/compiler.h Normal file
View File

@ -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

View File

@ -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.
*/

View File

@ -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.");
}

View File

@ -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

View File

@ -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 {

View File

@ -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;

View File

@ -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());

View File

@ -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);
}