Compare commits
	
		
			No commits in common. "881c6c7740f39d8654b2b95d333caf7216040fb6" and "039de2a7615134bd8ba1f2730eea2ad66947cbf2" have entirely different histories.
		
	
	
		
			881c6c7740
			...
			039de2a761
		
	
		| 
						 | 
				
			
			@ -104,6 +104,4 @@ dkms.conf
 | 
			
		|||
zre
 | 
			
		||||
zre.wasm
 | 
			
		||||
memory_dump.bin
 | 
			
		||||
src/build/
 | 
			
		||||
.gdb_history
 | 
			
		||||
.vscode
 | 
			
		||||
src/build/
 | 
			
		||||
| 
						 | 
				
			
			@ -1,15 +1,27 @@
 | 
			
		|||
#include "../../compiler.h"
 | 
			
		||||
#include "../../debug.h"
 | 
			
		||||
#include "../../vm.h"
 | 
			
		||||
#include "../../debug.h"
 | 
			
		||||
#include "../../test.h"
 | 
			
		||||
#include "../../lexer.h"
 | 
			
		||||
#include <SDL2/SDL.h>
 | 
			
		||||
 | 
			
		||||
#define MAX_SRC_SIZE 16384
 | 
			
		||||
 | 
			
		||||
void compileFile(const char *path, VM *vm) {
 | 
			
		||||
  FILE *f = fopen(path, "rb");
 | 
			
		||||
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");
 | 
			
		||||
  if (!f) {
 | 
			
		||||
    perror("fopen");
 | 
			
		||||
    exit(1);
 | 
			
		||||
    return 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static char source[MAX_SRC_SIZE + 1];
 | 
			
		||||
| 
						 | 
				
			
			@ -19,51 +31,32 @@ void compileFile(const char *path, VM *vm) {
 | 
			
		|||
  fseek(f, 0, SEEK_SET);
 | 
			
		||||
  if (len >= MAX_SRC_SIZE) {
 | 
			
		||||
    perror("source is larget than buffer");
 | 
			
		||||
    exit(1);
 | 
			
		||||
    return 1;
 | 
			
		||||
  }
 | 
			
		||||
  size_t read = fread(source, 1, len, f);
 | 
			
		||||
  source[read] = '\0';
 | 
			
		||||
  fclose(f);
 | 
			
		||||
 | 
			
		||||
  compile(source, vm);
 | 
			
		||||
}
 | 
			
		||||
  init_lexer(source);
 | 
			
		||||
 | 
			
		||||
static void repl(VM *vm) {
 | 
			
		||||
  char line[1024];
 | 
			
		||||
  for (;;) {
 | 
			
		||||
    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));
 | 
			
		||||
    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;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
  /* 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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
 | 
			
		||||
  uint32_t buffer_size = 640 * 480 * sizeof(uint32_t);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										305
									
								
								src/compiler.c
								
								
								
								
							
							
						
						
									
										305
									
								
								src/compiler.c
								
								
								
								
							| 
						 | 
				
			
			@ -1,305 +0,0 @@
 | 
			
		|||
#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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,10 +0,0 @@
 | 
			
		|||
#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,33 +1,26 @@
 | 
			
		|||
#include "debug.h"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Dumps the vm memory and code to a file.
 | 
			
		||||
 * Dumps the vm memory 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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
  FILE *file = fopen("memory_dump.bin", "wb");
 | 
			
		||||
  if (!file) {
 | 
			
		||||
    perror("Failed to open 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_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
    return EXIT_FAILURE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fclose(file);
 | 
			
		||||
  return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Print opcode.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										358
									
								
								src/lexer.c
								
								
								
								
							
							
						
						
									
										358
									
								
								src/lexer.c
								
								
								
								
							| 
						 | 
				
			
			@ -1,71 +1,33 @@
 | 
			
		|||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "common.h"
 | 
			
		||||
#include "lexer.h"
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  const char *start;
 | 
			
		||||
  const char *current;
 | 
			
		||||
  int line;
 | 
			
		||||
} Lexer;
 | 
			
		||||
 | 
			
		||||
Lexer lexer;
 | 
			
		||||
 | 
			
		||||
void initLexer(const char *source) {
 | 
			
		||||
void init_lexer(const char *source) {
 | 
			
		||||
  lexer.start = source;
 | 
			
		||||
  lexer.current = source;
 | 
			
		||||
  lexer.line = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool isAlpha(char c) {
 | 
			
		||||
  return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
 | 
			
		||||
}
 | 
			
		||||
int is_at_end() { return *lexer.current == '\0'; }
 | 
			
		||||
 | 
			
		||||
static bool isDigit(char c) { return c >= '0' && c <= '9'; }
 | 
			
		||||
char advance() { return *lexer.current++; }
 | 
			
		||||
 | 
			
		||||
static bool isAtEnd() { return *lexer.current == '\0'; }
 | 
			
		||||
char peek() { return *lexer.current; }
 | 
			
		||||
 | 
			
		||||
static char advance() {
 | 
			
		||||
  lexer.current++;
 | 
			
		||||
  return lexer.current[-1];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char peek() { return *lexer.current; }
 | 
			
		||||
 | 
			
		||||
static char peekNext() {
 | 
			
		||||
  if (isAtEnd())
 | 
			
		||||
char peek_next() {
 | 
			
		||||
  if (is_at_end())
 | 
			
		||||
    return '\0';
 | 
			
		||||
  return lexer.current[1];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool match(char expected) {
 | 
			
		||||
  if (isAtEnd())
 | 
			
		||||
    return false;
 | 
			
		||||
int match(char expected) {
 | 
			
		||||
  if (*lexer.current != expected)
 | 
			
		||||
    return false;
 | 
			
		||||
    return 0;
 | 
			
		||||
  lexer.current++;
 | 
			
		||||
  return true;
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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() {
 | 
			
		||||
void skip_whitespace() {
 | 
			
		||||
  for (;;) {
 | 
			
		||||
    char c = peek();
 | 
			
		||||
    switch (c) {
 | 
			
		||||
| 
						 | 
				
			
			@ -78,13 +40,13 @@ static void skipWhitespace() {
 | 
			
		|||
      lexer.line++;
 | 
			
		||||
      advance();
 | 
			
		||||
      break;
 | 
			
		||||
    case '/':
 | 
			
		||||
      if (peekNext() == '/') {
 | 
			
		||||
        /* A comment goes until the end of the line. */
 | 
			
		||||
        while (peek() != '\n' && !isAtEnd())
 | 
			
		||||
    case '!':
 | 
			
		||||
      if (peek_next() == '!') {
 | 
			
		||||
        while (peek() != '\n' && !is_at_end())
 | 
			
		||||
          advance();
 | 
			
		||||
      } else {
 | 
			
		||||
        return;
 | 
			
		||||
        while (peek() != '\n' && !is_at_end())
 | 
			
		||||
          advance();
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
| 
						 | 
				
			
			@ -93,150 +55,240 @@ static void skipWhitespace() {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 TOKEN_IDENTIFIER;
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Token identifier() {
 | 
			
		||||
  while (isAlpha(peek()) || isDigit(peek()))
 | 
			
		||||
    advance();
 | 
			
		||||
  return makeToken(identifierType());
 | 
			
		||||
}
 | 
			
		||||
int is_alpha(char c) { return isalpha(c) || c == '_'; }
 | 
			
		||||
 | 
			
		||||
static Token number() {
 | 
			
		||||
  while (isDigit(peek()))
 | 
			
		||||
int is_digit(char c) { return isdigit(c); }
 | 
			
		||||
 | 
			
		||||
Token number() {
 | 
			
		||||
  while (is_digit(peek()))
 | 
			
		||||
    advance();
 | 
			
		||||
 | 
			
		||||
  /*  Look for a fractional part. */
 | 
			
		||||
  if (peek() == '.' && isDigit(peekNext())) {
 | 
			
		||||
    /*  Consume the ".". */
 | 
			
		||||
  if (peek() == '.' && is_digit(peek_next())) {
 | 
			
		||||
    advance();
 | 
			
		||||
 | 
			
		||||
    while (isDigit(peek()))
 | 
			
		||||
    while (is_digit(peek()))
 | 
			
		||||
      advance();
 | 
			
		||||
 | 
			
		||||
    return makeToken(TOKEN_FLOAT_LITERAL);
 | 
			
		||||
    return make_token(TOKEN_FLOAT_LITERAL);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return makeToken(TOKEN_INT_LITERAL);
 | 
			
		||||
  return make_token(TOKEN_INT_LITERAL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Token string() {
 | 
			
		||||
  while (peek() != '"' && !isAtEnd()) {
 | 
			
		||||
Token string() {
 | 
			
		||||
  while (peek() != '"' && !is_at_end()) {
 | 
			
		||||
    if (peek() == '\n')
 | 
			
		||||
      lexer.line++;
 | 
			
		||||
    advance();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (isAtEnd())
 | 
			
		||||
    return errorToken("Unterminated string.");
 | 
			
		||||
  if (is_at_end())
 | 
			
		||||
    return error_token("Unterminated string.");
 | 
			
		||||
 | 
			
		||||
  /*  The closing quote. */
 | 
			
		||||
  advance();
 | 
			
		||||
  return makeToken(TOKEN_STRING_LITERAL);
 | 
			
		||||
  return make_token(TOKEN_STRING_LITERAL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Token nextToken() {
 | 
			
		||||
  skipWhitespace();
 | 
			
		||||
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();
 | 
			
		||||
  lexer.start = lexer.current;
 | 
			
		||||
 | 
			
		||||
  if (isAtEnd())
 | 
			
		||||
    return makeToken(TOKEN_EOF);
 | 
			
		||||
  if (is_at_end())
 | 
			
		||||
    return make_token(TOKEN_EOF);
 | 
			
		||||
 | 
			
		||||
  char c = advance();
 | 
			
		||||
  if (isAlpha(c))
 | 
			
		||||
 | 
			
		||||
  if (is_alpha(c))
 | 
			
		||||
    return identifier();
 | 
			
		||||
  if (isDigit(c))
 | 
			
		||||
  if (is_digit(c))
 | 
			
		||||
    return number();
 | 
			
		||||
 | 
			
		||||
  switch (c) {
 | 
			
		||||
  case '(':
 | 
			
		||||
    return makeToken(TOKEN_LPAREN);
 | 
			
		||||
    return make_token(TOKEN_LPAREN);
 | 
			
		||||
  case ')':
 | 
			
		||||
    return makeToken(TOKEN_RPAREN);
 | 
			
		||||
    return make_token(TOKEN_RPAREN);
 | 
			
		||||
  case '{':
 | 
			
		||||
    return makeToken(TOKEN_LBRACE);
 | 
			
		||||
    return make_token(TOKEN_LBRACE);
 | 
			
		||||
  case '}':
 | 
			
		||||
    return makeToken(TOKEN_RBRACE);
 | 
			
		||||
  case ';':
 | 
			
		||||
    return makeToken(TOKEN_SEMICOLON);
 | 
			
		||||
    return make_token(TOKEN_RBRACE);
 | 
			
		||||
  case '[':
 | 
			
		||||
    return make_token(TOKEN_LBRACKET);
 | 
			
		||||
  case ']':
 | 
			
		||||
    return make_token(TOKEN_RBRACKET);
 | 
			
		||||
  case ',':
 | 
			
		||||
    return makeToken(TOKEN_COMMA);
 | 
			
		||||
    return make_token(TOKEN_COMMA);
 | 
			
		||||
  case '.':
 | 
			
		||||
    return makeToken(TOKEN_DOT);
 | 
			
		||||
  case '-':
 | 
			
		||||
    return makeToken(TOKEN_MINUS);
 | 
			
		||||
    return make_token(TOKEN_DOT);
 | 
			
		||||
  case ':':
 | 
			
		||||
    return make_token(TOKEN_COLON);
 | 
			
		||||
  case ';':
 | 
			
		||||
    return make_token(TOKEN_SEMICOLON);
 | 
			
		||||
  case '+':
 | 
			
		||||
    return makeToken(TOKEN_PLUS);
 | 
			
		||||
  case '/':
 | 
			
		||||
    return makeToken(TOKEN_SLASH);
 | 
			
		||||
    return make_token(TOKEN_PLUS);
 | 
			
		||||
  case '-':
 | 
			
		||||
    return make_token(TOKEN_MINUS);
 | 
			
		||||
  case '*':
 | 
			
		||||
    return makeToken(TOKEN_STAR);
 | 
			
		||||
    return make_token(TOKEN_STAR);
 | 
			
		||||
  case '/':
 | 
			
		||||
    return make_token(TOKEN_SLASH);
 | 
			
		||||
  case '!':
 | 
			
		||||
    return makeToken(match('=') ? TOKEN_BANG_EQ : TOKEN_BANG);
 | 
			
		||||
    return make_token(match('=') ? TOKEN_BANG_EQ : TOKEN_BANG);
 | 
			
		||||
  case '=':
 | 
			
		||||
    return makeToken(match('=') ? TOKEN_EQ_EQ : TOKEN_EQ);
 | 
			
		||||
    return make_token(match('=') ? TOKEN_EQ_EQ : TOKEN_EQ);
 | 
			
		||||
  case '<':
 | 
			
		||||
    return makeToken(match('=') ? TOKEN_LTE : TOKEN_LT);
 | 
			
		||||
    return make_token(match('=') ? TOKEN_LTE : TOKEN_LT);
 | 
			
		||||
  case '>':
 | 
			
		||||
    return makeToken(match('=') ? TOKEN_GTE : TOKEN_GT);
 | 
			
		||||
    return make_token(match('=') ? TOKEN_GTE : TOKEN_GT);
 | 
			
		||||
  case '"':
 | 
			
		||||
    return string();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return errorToken("Unexpected character.");
 | 
			
		||||
  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";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										32
									
								
								src/lexer.h
								
								
								
								
							
							
						
						
									
										32
									
								
								src/lexer.h
								
								
								
								
							| 
						 | 
				
			
			@ -1,11 +1,14 @@
 | 
			
		|||
#ifndef zre_lexer_h
 | 
			
		||||
#define zre_lexer_h
 | 
			
		||||
#ifndef ZRL_LEXER_H
 | 
			
		||||
#define ZRL_LEXER_H
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
  TOKEN_EOF,
 | 
			
		||||
  TOKEN_IDENTIFIER,
 | 
			
		||||
  TOKEN_INT_LITERAL,
 | 
			
		||||
  TOKEN_UINT_LITERAL,
 | 
			
		||||
  TOKEN_FLOAT_LITERAL,
 | 
			
		||||
  TOKEN_STRING_LITERAL,
 | 
			
		||||
  TOKEN_TYPE_INT,
 | 
			
		||||
| 
						 | 
				
			
			@ -24,14 +27,7 @@ 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,
 | 
			
		||||
| 
						 | 
				
			
			@ -64,7 +60,19 @@ typedef struct {
 | 
			
		|||
  int line;
 | 
			
		||||
} Token;
 | 
			
		||||
 | 
			
		||||
void initLexer(const char *source);
 | 
			
		||||
Token nextToken();
 | 
			
		||||
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();
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -133,7 +133,6 @@ 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,8 +27,7 @@
 | 
			
		|||
 * 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;
 | 
			
		||||
| 
						 | 
				
			
			@ -58,21 +57,16 @@ bool step_vm(VM *vm) {
 | 
			
		|||
  switch (opcode) {
 | 
			
		||||
  case OP_HALT:
 | 
			
		||||
    return false;
 | 
			
		||||
  case OP_CALL: {
 | 
			
		||||
    uint32_t jmp = vm->code[vm->pc++].u;   /* location of function in code */
 | 
			
		||||
  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 */
 | 
			
		||||
    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->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 */
 | 
			
		||||
    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;
 | 
			
		||||
| 
						 | 
				
			
			@ -201,9 +195,6 @@ 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];
 | 
			
		||||
| 
						 | 
				
			
			@ -265,8 +256,7 @@ 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;
 | 
			
		||||
| 
						 | 
				
			
			@ -280,9 +270,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(int a, int b) int {
 | 
			
		||||
fn add(i8 a, i8 b) i8 {
 | 
			
		||||
  return a + b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int sum = add(1, 1);
 | 
			
		||||
i8 sum = add(1, 1);
 | 
			
		||||
print(sum.toS());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
fn fib(int n) int {
 | 
			
		||||
fn fib(i32 n) i32 {
 | 
			
		||||
  if (n < 2) return n;
 | 
			
		||||
  return fib(n - 2) + fib(n - 1);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue