start from scratch with own code
This commit is contained in:
		
							parent
							
								
									ae665d89ed
								
							
						
					
					
						commit
						52c33178d3
					
				
							
								
								
									
										40
									
								
								src/chunk.c
								
								
								
								
							
							
						
						
									
										40
									
								
								src/chunk.c
								
								
								
								
							| 
						 | 
				
			
			@ -1,40 +0,0 @@
 | 
			
		|||
#include "chunk.h"
 | 
			
		||||
#include "memory.h"
 | 
			
		||||
#include "vm.h"
 | 
			
		||||
 | 
			
		||||
void initChunk(Chunk* chunk) {
 | 
			
		||||
  chunk->count = 0;
 | 
			
		||||
  chunk->capacity = 0;
 | 
			
		||||
  chunk->lines = NULL;
 | 
			
		||||
  chunk->code = NULL;
 | 
			
		||||
  initValueArray(&chunk->constants);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void writeChunk(Chunk* chunk, uint8_t byte, int line) {
 | 
			
		||||
  if (chunk->capacity < chunk->count + 1) {
 | 
			
		||||
    int oldCapacity = chunk->capacity;
 | 
			
		||||
    chunk->capacity = GROW_CAPACITY(oldCapacity);
 | 
			
		||||
    chunk->code = GROW_ARRAY(uint8_t, chunk->code,
 | 
			
		||||
        oldCapacity, chunk->capacity);
 | 
			
		||||
    chunk->lines = GROW_ARRAY(int, chunk->lines,
 | 
			
		||||
        oldCapacity, chunk->capacity);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  chunk->code[chunk->count] = byte;
 | 
			
		||||
  chunk->lines[chunk->count] = line;
 | 
			
		||||
  chunk->count++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void freeChunk(Chunk* chunk) {
 | 
			
		||||
  FREE_ARRAY(uint8_t, chunk->code, chunk->capacity);
 | 
			
		||||
  freeValueArray(&chunk->constants);
 | 
			
		||||
  initChunk(chunk);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int addConstant(Chunk* chunk, Value value) {
 | 
			
		||||
  push(value);
 | 
			
		||||
  writeValueArray(&chunk->constants, value);
 | 
			
		||||
  pop();
 | 
			
		||||
  return chunk->constants.count - 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										57
									
								
								src/chunk.h
								
								
								
								
							
							
						
						
									
										57
									
								
								src/chunk.h
								
								
								
								
							| 
						 | 
				
			
			@ -1,57 +0,0 @@
 | 
			
		|||
#ifndef zlc_chunk_h
 | 
			
		||||
#define zlc_chunk_h
 | 
			
		||||
 | 
			
		||||
#include "common.h"
 | 
			
		||||
#include "value.h"
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
  OP_CONSTANT,
 | 
			
		||||
  OP_NIL,
 | 
			
		||||
  OP_TRUE,
 | 
			
		||||
  OP_FALSE,
 | 
			
		||||
  OP_POP,
 | 
			
		||||
  OP_GET_LOCAL,
 | 
			
		||||
  OP_GET_GLOBAL,
 | 
			
		||||
  OP_DEFINE_GLOBAL,
 | 
			
		||||
  OP_SET_LOCAL,
 | 
			
		||||
  OP_SET_GLOBAL,
 | 
			
		||||
  OP_GET_UPVALUE,
 | 
			
		||||
  OP_SET_UPVALUE,
 | 
			
		||||
  OP_GET_PROPERTY,
 | 
			
		||||
  OP_SET_PROPERTY,
 | 
			
		||||
  OP_EQUAL,
 | 
			
		||||
  OP_GREATER,
 | 
			
		||||
  OP_LESS,
 | 
			
		||||
  OP_ADD,
 | 
			
		||||
  OP_SUBTRACT,
 | 
			
		||||
  OP_MULTIPLY,
 | 
			
		||||
  OP_DIVIDE,
 | 
			
		||||
  OP_NOT,
 | 
			
		||||
  OP_NEGATE,
 | 
			
		||||
  OP_PRINT,
 | 
			
		||||
  OP_JUMP,
 | 
			
		||||
  OP_JUMP_IF_FALSE,
 | 
			
		||||
  OP_LOOP,
 | 
			
		||||
  OP_CALL,
 | 
			
		||||
  OP_INVOKE,
 | 
			
		||||
  OP_CLOSURE,
 | 
			
		||||
  OP_CLOSE_UPVALUE,
 | 
			
		||||
  OP_RETURN,
 | 
			
		||||
  OP_TYPE,
 | 
			
		||||
  OP_METHOD
 | 
			
		||||
} OpCode;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  int count;
 | 
			
		||||
  int capacity;
 | 
			
		||||
  uint8_t *code;
 | 
			
		||||
  int *lines;
 | 
			
		||||
  ValueArray constants;
 | 
			
		||||
} Chunk;
 | 
			
		||||
 | 
			
		||||
void initChunk(Chunk *chunk);
 | 
			
		||||
void freeChunk(Chunk *chunk);
 | 
			
		||||
void writeChunk(Chunk *chunk, uint8_t byte, int line);
 | 
			
		||||
int addConstant(Chunk *chunk, Value value);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										16
									
								
								src/common.h
								
								
								
								
							
							
						
						
									
										16
									
								
								src/common.h
								
								
								
								
							| 
						 | 
				
			
			@ -1,16 +0,0 @@
 | 
			
		|||
#ifndef zlc_common_h
 | 
			
		||||
#define zlc_common_h
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
/* #define DEBUG_TRACE_EXECUTION */
 | 
			
		||||
/* #define DEBUG_PRINT_CODE */
 | 
			
		||||
 | 
			
		||||
/* #define DEBUG_STRESS_GC */
 | 
			
		||||
/* #define DEBUG_LOG_GC */
 | 
			
		||||
 | 
			
		||||
#define UINT8_COUNT (UINT8_MAX + 1)
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										917
									
								
								src/compiler.c
								
								
								
								
							
							
						
						
									
										917
									
								
								src/compiler.c
								
								
								
								
							| 
						 | 
				
			
			@ -1,917 +0,0 @@
 | 
			
		|||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "common.h"
 | 
			
		||||
#include "compiler.h"
 | 
			
		||||
#include "memory.h"
 | 
			
		||||
#include "scanner.h"
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_PRINT_CODE
 | 
			
		||||
#include "debug.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
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,      // ! -
 | 
			
		||||
  PREC_CALL,       // . ()
 | 
			
		||||
  PREC_PRIMARY
 | 
			
		||||
} Precedence;
 | 
			
		||||
 | 
			
		||||
typedef void (*ParseFn)(bool canAssign);
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  ParseFn prefix;
 | 
			
		||||
  ParseFn infix;
 | 
			
		||||
  Precedence precedence;
 | 
			
		||||
} ParseRule;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  Token name;
 | 
			
		||||
  int depth;
 | 
			
		||||
  bool isCaptured;
 | 
			
		||||
} Local;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  uint8_t index;
 | 
			
		||||
  bool isLocal;
 | 
			
		||||
} Upvalue;
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
  TYPE_FUNCTION,
 | 
			
		||||
  TYPE_INITIALIZER,
 | 
			
		||||
  TYPE_METHOD,
 | 
			
		||||
  TYPE_SCRIPT
 | 
			
		||||
} FunctionType;
 | 
			
		||||
 | 
			
		||||
typedef struct Compiler {
 | 
			
		||||
  struct Compiler *enclosing;
 | 
			
		||||
  ObjFunction *function;
 | 
			
		||||
  FunctionType type;
 | 
			
		||||
  Local locals[UINT8_COUNT];
 | 
			
		||||
  int localCount;
 | 
			
		||||
  Upvalue upvalues[UINT8_COUNT];
 | 
			
		||||
  int scopeDepth;
 | 
			
		||||
} Compiler;
 | 
			
		||||
 | 
			
		||||
typedef struct TypeCompiler {
 | 
			
		||||
  struct TypeCompiler *enclosing;
 | 
			
		||||
} TypeCompiler;
 | 
			
		||||
 | 
			
		||||
Parser parser;
 | 
			
		||||
Compiler *current = NULL;
 | 
			
		||||
TypeCompiler *currentType = NULL;
 | 
			
		||||
 | 
			
		||||
static Chunk *currentChunk() { return ¤t->function->chunk; }
 | 
			
		||||
 | 
			
		||||
static 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) {
 | 
			
		||||
    // Nothing.
 | 
			
		||||
  } else {
 | 
			
		||||
    fprintf(stderr, " at '%.*s'", token->length, token->start);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fprintf(stderr, ": %s\n", message);
 | 
			
		||||
  parser.hadError = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void error(const char *message) { errorAt(&parser.previous, message); }
 | 
			
		||||
 | 
			
		||||
static void errorAtCurrent(const char *message) {
 | 
			
		||||
  errorAt(&parser.current, message);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void advance() {
 | 
			
		||||
  parser.previous = parser.current;
 | 
			
		||||
 | 
			
		||||
  for (;;) {
 | 
			
		||||
    parser.current = scanToken();
 | 
			
		||||
    if (parser.current.type != TOKEN_ERROR)
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    errorAtCurrent(parser.current.start);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void consume(TokenType type, const char *message) {
 | 
			
		||||
  if (parser.current.type == type) {
 | 
			
		||||
    advance();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  errorAtCurrent(message);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool check(TokenType type) { return parser.current.type == type; }
 | 
			
		||||
 | 
			
		||||
static bool match(TokenType type) {
 | 
			
		||||
  if (!check(type))
 | 
			
		||||
    return false;
 | 
			
		||||
  advance();
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void emitByte(uint8_t byte) {
 | 
			
		||||
  writeChunk(currentChunk(), byte, parser.previous.line);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void emitBytes(uint8_t byte1, uint8_t byte2) {
 | 
			
		||||
  emitByte(byte1);
 | 
			
		||||
  emitByte(byte2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void emitLoop(int loopStart) {
 | 
			
		||||
  emitByte(OP_LOOP);
 | 
			
		||||
 | 
			
		||||
  int offset = currentChunk()->count - loopStart + 2;
 | 
			
		||||
  if (offset > UINT16_MAX)
 | 
			
		||||
    error("Loop body too large.");
 | 
			
		||||
 | 
			
		||||
  emitByte((offset >> 8) & 0xff);
 | 
			
		||||
  emitByte(offset & 0xff);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int emitJump(uint8_t instruction) {
 | 
			
		||||
  emitByte(instruction);
 | 
			
		||||
  emitByte(0xff);
 | 
			
		||||
  emitByte(0xff);
 | 
			
		||||
  return currentChunk()->count - 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void emitReturn() {
 | 
			
		||||
  if (current->type == TYPE_INITIALIZER) {
 | 
			
		||||
    emitBytes(OP_GET_LOCAL, 0);
 | 
			
		||||
  } else {
 | 
			
		||||
    emitByte(OP_NIL);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  emitByte(OP_RETURN);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint8_t makeConstant(Value value) {
 | 
			
		||||
  int constant = addConstant(currentChunk(), value);
 | 
			
		||||
  if (constant > UINT8_MAX) {
 | 
			
		||||
    error("Too many constants in one chunk.");
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (uint8_t)constant;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void emitConstant(Value value) {
 | 
			
		||||
  emitBytes(OP_CONSTANT, makeConstant(value));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void patchJump(int offset) {
 | 
			
		||||
  // -2 to adjust for the bytecode for the jump offset itself.
 | 
			
		||||
  int jump = currentChunk()->count - offset - 2;
 | 
			
		||||
 | 
			
		||||
  if (jump > UINT16_MAX) {
 | 
			
		||||
    error("Too much code to jump over.");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  currentChunk()->code[offset] = (jump >> 8) & 0xff;
 | 
			
		||||
  currentChunk()->code[offset + 1] = jump & 0xff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void initCompiler(Compiler *compiler, FunctionType type) {
 | 
			
		||||
  compiler->enclosing = current;
 | 
			
		||||
  compiler->function = NULL;
 | 
			
		||||
  compiler->type = type;
 | 
			
		||||
  compiler->localCount = 0;
 | 
			
		||||
  compiler->scopeDepth = 0;
 | 
			
		||||
  compiler->function = newFunction();
 | 
			
		||||
  current = compiler;
 | 
			
		||||
  if (type != TYPE_SCRIPT) {
 | 
			
		||||
    current->function->name =
 | 
			
		||||
        copyString(parser.previous.start, parser.previous.length);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Local *local = ¤t->locals[current->localCount++];
 | 
			
		||||
  local->depth = 0;
 | 
			
		||||
  local->isCaptured = false;
 | 
			
		||||
  if (type != TYPE_FUNCTION) {
 | 
			
		||||
    local->name.start = "this";
 | 
			
		||||
    local->name.length = 4;
 | 
			
		||||
  } else {
 | 
			
		||||
    local->name.start = "";
 | 
			
		||||
    local->name.length = 0;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ObjFunction *endCompiler() {
 | 
			
		||||
  emitReturn();
 | 
			
		||||
  ObjFunction *function = current->function;
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_PRINT_CODE
 | 
			
		||||
  if (!parser.hadError) {
 | 
			
		||||
    disassembleChunk(currentChunk(), function->name != NULL
 | 
			
		||||
                                         ? function->name->chars
 | 
			
		||||
                                         : "<script>");
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  current = current->enclosing;
 | 
			
		||||
  return function;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void beginScope() { current->scopeDepth++; }
 | 
			
		||||
 | 
			
		||||
static void endScope() {
 | 
			
		||||
  current->scopeDepth--;
 | 
			
		||||
 | 
			
		||||
  while (current->localCount > 0 &&
 | 
			
		||||
         current->locals[current->localCount - 1].depth > current->scopeDepth) {
 | 
			
		||||
    if (current->locals[current->localCount - 1].isCaptured) {
 | 
			
		||||
      emitByte(OP_CLOSE_UPVALUE);
 | 
			
		||||
    } else {
 | 
			
		||||
      emitByte(OP_POP);
 | 
			
		||||
    }
 | 
			
		||||
    current->localCount--;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void expression();
 | 
			
		||||
static void statement();
 | 
			
		||||
static void declaration();
 | 
			
		||||
static ParseRule *getRule(TokenType type);
 | 
			
		||||
static void parsePrecedence(Precedence precedence);
 | 
			
		||||
static void namedVariable(Token name, bool canAssign);
 | 
			
		||||
 | 
			
		||||
static void binary(bool canAssign) {
 | 
			
		||||
  TokenType operatorType = parser.previous.type;
 | 
			
		||||
  ParseRule *rule = getRule(operatorType);
 | 
			
		||||
  parsePrecedence((Precedence)(rule->precedence + 1));
 | 
			
		||||
 | 
			
		||||
  switch (operatorType) {
 | 
			
		||||
  case TOKEN_BANG_EQUAL:
 | 
			
		||||
    emitBytes(OP_EQUAL, OP_NOT);
 | 
			
		||||
    break;
 | 
			
		||||
  case TOKEN_EQUAL_EQUAL:
 | 
			
		||||
    emitByte(OP_EQUAL);
 | 
			
		||||
    break;
 | 
			
		||||
  case TOKEN_GREATER:
 | 
			
		||||
    emitByte(OP_GREATER);
 | 
			
		||||
    break;
 | 
			
		||||
  case TOKEN_GREATER_EQUAL:
 | 
			
		||||
    emitBytes(OP_LESS, OP_NOT);
 | 
			
		||||
    break;
 | 
			
		||||
  case TOKEN_LESS:
 | 
			
		||||
    emitByte(OP_LESS);
 | 
			
		||||
    break;
 | 
			
		||||
  case TOKEN_LESS_EQUAL:
 | 
			
		||||
    emitBytes(OP_GREATER, OP_NOT);
 | 
			
		||||
    break;
 | 
			
		||||
  case TOKEN_PLUS:
 | 
			
		||||
    emitByte(OP_ADD);
 | 
			
		||||
    break;
 | 
			
		||||
  case TOKEN_MINUS:
 | 
			
		||||
    emitByte(OP_SUBTRACT);
 | 
			
		||||
    break;
 | 
			
		||||
  case TOKEN_STAR:
 | 
			
		||||
    emitByte(OP_MULTIPLY);
 | 
			
		||||
    break;
 | 
			
		||||
  case TOKEN_SLASH:
 | 
			
		||||
    emitByte(OP_DIVIDE);
 | 
			
		||||
    break;
 | 
			
		||||
  default:
 | 
			
		||||
    return; // Unreachable.
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint8_t argumentList() {
 | 
			
		||||
  uint8_t argCount = 0;
 | 
			
		||||
  if (!check(TOKEN_RIGHT_PAREN)) {
 | 
			
		||||
    do {
 | 
			
		||||
      expression();
 | 
			
		||||
      if (argCount == 255) {
 | 
			
		||||
        error("Can't have more than 255 arguments.");
 | 
			
		||||
      }
 | 
			
		||||
      argCount++;
 | 
			
		||||
    } while (match(TOKEN_COMMA));
 | 
			
		||||
  }
 | 
			
		||||
  consume(TOKEN_RIGHT_PAREN, "Expect ')' after arguments.");
 | 
			
		||||
  return argCount;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void call(bool canAssign) {
 | 
			
		||||
  uint8_t argCount = argumentList();
 | 
			
		||||
  emitBytes(OP_CALL, argCount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void literal(bool canAssign) {
 | 
			
		||||
  switch (parser.previous.type) {
 | 
			
		||||
  case TOKEN_FALSE:
 | 
			
		||||
    emitByte(OP_FALSE);
 | 
			
		||||
    break;
 | 
			
		||||
  case TOKEN_NIL:
 | 
			
		||||
    emitByte(OP_NIL);
 | 
			
		||||
    break;
 | 
			
		||||
  case TOKEN_TRUE:
 | 
			
		||||
    emitByte(OP_TRUE);
 | 
			
		||||
    break;
 | 
			
		||||
  default:
 | 
			
		||||
    return; // Unreachable.
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void expression() { parsePrecedence(PREC_ASSIGNMENT); }
 | 
			
		||||
 | 
			
		||||
static void block() {
 | 
			
		||||
  while (!check(TOKEN_RIGHT_BRACE) && !check(TOKEN_EOF)) {
 | 
			
		||||
    declaration();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  consume(TOKEN_RIGHT_BRACE, "Expect '}' after block.");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint8_t identifierConstant(Token *name) {
 | 
			
		||||
  return makeConstant(OBJ_VAL(copyString(name->start, name->length)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool identifiersEqual(Token *a, Token *b) {
 | 
			
		||||
  if (a->length != b->length)
 | 
			
		||||
    return false;
 | 
			
		||||
  return memcmp(a->start, b->start, a->length) == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void dot(bool canAssign) {
 | 
			
		||||
  consume(TOKEN_IDENTIFIER, "Expect property name after '.'.");
 | 
			
		||||
  uint8_t name = identifierConstant(&parser.previous);
 | 
			
		||||
 | 
			
		||||
  if (canAssign && match(TOKEN_EQUAL)) {
 | 
			
		||||
    expression();
 | 
			
		||||
    emitBytes(OP_SET_PROPERTY, name);
 | 
			
		||||
  } else if (match(TOKEN_LEFT_PAREN)) {
 | 
			
		||||
    uint8_t argCount = argumentList();
 | 
			
		||||
    emitBytes(OP_INVOKE, name);
 | 
			
		||||
    emitByte(argCount);
 | 
			
		||||
  } else {
 | 
			
		||||
    emitBytes(OP_GET_PROPERTY, name);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int resolveLocal(Compiler *compiler, Token *name) {
 | 
			
		||||
  for (int i = compiler->localCount - 1; i >= 0; i--) {
 | 
			
		||||
    Local *local = &compiler->locals[i];
 | 
			
		||||
    if (identifiersEqual(name, &local->name)) {
 | 
			
		||||
      if (local->depth == -1) {
 | 
			
		||||
        error("Can't read local variable in its own initializer.");
 | 
			
		||||
      }
 | 
			
		||||
      return i;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int addUpvalue(Compiler *compiler, uint8_t index, bool isLocal) {
 | 
			
		||||
  int upvalueCount = compiler->function->upvalueCount;
 | 
			
		||||
  for (int i = 0; i < upvalueCount; i++) {
 | 
			
		||||
    Upvalue *upvalue = &compiler->upvalues[i];
 | 
			
		||||
    if (upvalue->index == index && upvalue->isLocal == isLocal) {
 | 
			
		||||
      return i;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (upvalueCount == UINT8_COUNT) {
 | 
			
		||||
    error("Too many closure variables in function.");
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  compiler->upvalues[upvalueCount].isLocal = isLocal;
 | 
			
		||||
  compiler->upvalues[upvalueCount].index = index;
 | 
			
		||||
  return compiler->function->upvalueCount++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int resolveUpvalue(Compiler *compiler, Token *name) {
 | 
			
		||||
  if (compiler->enclosing == NULL)
 | 
			
		||||
    return -1;
 | 
			
		||||
 | 
			
		||||
  int local = resolveLocal(compiler->enclosing, name);
 | 
			
		||||
  if (local != -1) {
 | 
			
		||||
    compiler->enclosing->locals[local].isCaptured = true;
 | 
			
		||||
    return addUpvalue(compiler, (uint8_t)local, true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int upvalue = resolveUpvalue(compiler->enclosing, name);
 | 
			
		||||
  if (upvalue != -1) {
 | 
			
		||||
    return addUpvalue(compiler, (uint8_t)upvalue, false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void addLocal(Token name) {
 | 
			
		||||
  if (current->localCount == UINT8_COUNT) {
 | 
			
		||||
    error("Too many local variables in function.");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Local *local = ¤t->locals[current->localCount++];
 | 
			
		||||
  local->name = name;
 | 
			
		||||
  local->depth = -1;
 | 
			
		||||
  local->isCaptured = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void declareVariable() {
 | 
			
		||||
  if (current->scopeDepth == 0)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  Token *name = &parser.previous;
 | 
			
		||||
  for (int i = current->localCount - 1; i >= 0; i--) {
 | 
			
		||||
    Local *local = ¤t->locals[i];
 | 
			
		||||
    if (local->depth != -1 && local->depth < current->scopeDepth) {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (identifiersEqual(name, &local->name)) {
 | 
			
		||||
      error("Already a variable with this name in this scope.");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  addLocal(*name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint8_t parseVariable(const char *errorMessage) {
 | 
			
		||||
  consume(TOKEN_IDENTIFIER, errorMessage);
 | 
			
		||||
 | 
			
		||||
  declareVariable();
 | 
			
		||||
  if (current->scopeDepth > 0)
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  return identifierConstant(&parser.previous);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void markInitialized() {
 | 
			
		||||
  if (current->scopeDepth == 0)
 | 
			
		||||
    return;
 | 
			
		||||
  current->locals[current->localCount - 1].depth = current->scopeDepth;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void defineVariable(uint8_t global) {
 | 
			
		||||
  if (current->scopeDepth > 0) {
 | 
			
		||||
    markInitialized();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  emitBytes(OP_DEFINE_GLOBAL, global);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void function(FunctionType type) {
 | 
			
		||||
  Compiler compiler;
 | 
			
		||||
  initCompiler(&compiler, type);
 | 
			
		||||
  beginScope();
 | 
			
		||||
 | 
			
		||||
  consume(TOKEN_LEFT_PAREN, "Expect '(' after function name.");
 | 
			
		||||
  if (!check(TOKEN_RIGHT_PAREN)) {
 | 
			
		||||
    do {
 | 
			
		||||
      current->function->arity++;
 | 
			
		||||
      if (current->function->arity > 255) {
 | 
			
		||||
        errorAtCurrent("Can't have more than 255 parameters.");
 | 
			
		||||
      }
 | 
			
		||||
      uint8_t constant = parseVariable("Expect parameter name.");
 | 
			
		||||
      defineVariable(constant);
 | 
			
		||||
    } while (match(TOKEN_COMMA));
 | 
			
		||||
  }
 | 
			
		||||
  consume(TOKEN_RIGHT_PAREN, "Expect ')' after parameters.");
 | 
			
		||||
  consume(TOKEN_LEFT_BRACE, "Expect '{' before function body.");
 | 
			
		||||
  block();
 | 
			
		||||
 | 
			
		||||
  ObjFunction *function = endCompiler();
 | 
			
		||||
  emitBytes(OP_CLOSURE, makeConstant(OBJ_VAL(function)));
 | 
			
		||||
 | 
			
		||||
  for (int i = 0; i < function->upvalueCount; i++) {
 | 
			
		||||
    emitByte(compiler.upvalues[i].isLocal ? 1 : 0);
 | 
			
		||||
    emitByte(compiler.upvalues[i].index);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void method() {
 | 
			
		||||
  consume(TOKEN_IDENTIFIER, "Expect method name.");
 | 
			
		||||
  uint8_t constant = identifierConstant(&parser.previous);
 | 
			
		||||
 | 
			
		||||
  FunctionType type = TYPE_METHOD;
 | 
			
		||||
  if (parser.previous.length == 4 &&
 | 
			
		||||
      memcmp(parser.previous.start, "init", 4) == 0) {
 | 
			
		||||
    type = TYPE_INITIALIZER;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function(type);
 | 
			
		||||
  emitBytes(OP_METHOD, constant);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void typeDeclaration() {
 | 
			
		||||
  consume(TOKEN_IDENTIFIER, "Expect type name.");
 | 
			
		||||
  Token typeName = parser.previous;
 | 
			
		||||
  uint8_t nameConstant = identifierConstant(&parser.previous);
 | 
			
		||||
  declareVariable();
 | 
			
		||||
 | 
			
		||||
  emitBytes(OP_TYPE, nameConstant);
 | 
			
		||||
  defineVariable(nameConstant);
 | 
			
		||||
 | 
			
		||||
  TypeCompiler typeCompiler;
 | 
			
		||||
  typeCompiler.enclosing = currentType;
 | 
			
		||||
  currentType = &typeCompiler;
 | 
			
		||||
 | 
			
		||||
  namedVariable(typeName, false);
 | 
			
		||||
  consume(TOKEN_LEFT_BRACE, "Expect '{' before type body.");
 | 
			
		||||
  while (!check(TOKEN_RIGHT_BRACE) && !check(TOKEN_EOF)) {
 | 
			
		||||
    method();
 | 
			
		||||
  }
 | 
			
		||||
  consume(TOKEN_RIGHT_BRACE, "Expect '}' after type body.");
 | 
			
		||||
  emitByte(OP_POP);
 | 
			
		||||
 | 
			
		||||
  currentType = currentType->enclosing;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void funDeclaration() {
 | 
			
		||||
  uint8_t global = parseVariable("Expect function name.");
 | 
			
		||||
  markInitialized();
 | 
			
		||||
  function(TYPE_FUNCTION);
 | 
			
		||||
  defineVariable(global);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void and_(bool canAssign) {
 | 
			
		||||
  int endJump = emitJump(OP_JUMP_IF_FALSE);
 | 
			
		||||
 | 
			
		||||
  emitByte(OP_POP);
 | 
			
		||||
  parsePrecedence(PREC_AND);
 | 
			
		||||
 | 
			
		||||
  patchJump(endJump);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void varDeclaration() {
 | 
			
		||||
  uint8_t global = parseVariable("Expect variable name.");
 | 
			
		||||
 | 
			
		||||
  if (match(TOKEN_EQUAL)) {
 | 
			
		||||
    expression();
 | 
			
		||||
  } else {
 | 
			
		||||
    emitByte(OP_NIL);
 | 
			
		||||
  }
 | 
			
		||||
  consume(TOKEN_SEMICOLON, "Expect ';' after variable declaration.");
 | 
			
		||||
 | 
			
		||||
  defineVariable(global);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void expressionStatement() {
 | 
			
		||||
  expression();
 | 
			
		||||
  consume(TOKEN_SEMICOLON, "Expect ';' after expression.");
 | 
			
		||||
  emitByte(OP_POP);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void forStatement() {
 | 
			
		||||
  beginScope();
 | 
			
		||||
  consume(TOKEN_LEFT_PAREN, "Expect '(' after 'for'.");
 | 
			
		||||
  if (match(TOKEN_SEMICOLON)) {
 | 
			
		||||
    // No initializer.
 | 
			
		||||
  } else if (match(TOKEN_LET)) {
 | 
			
		||||
    varDeclaration();
 | 
			
		||||
  } else {
 | 
			
		||||
    expressionStatement();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int loopStart = currentChunk()->count;
 | 
			
		||||
  int exitJump = -1;
 | 
			
		||||
  if (!match(TOKEN_SEMICOLON)) {
 | 
			
		||||
    expression();
 | 
			
		||||
    consume(TOKEN_SEMICOLON, "Expect ';' after loop condition.");
 | 
			
		||||
 | 
			
		||||
    // Jump out of the loop if the condition is false.
 | 
			
		||||
    exitJump = emitJump(OP_JUMP_IF_FALSE);
 | 
			
		||||
    emitByte(OP_POP); // Condition.
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!match(TOKEN_RIGHT_PAREN)) {
 | 
			
		||||
    int bodyJump = emitJump(OP_JUMP);
 | 
			
		||||
    int incrementStart = currentChunk()->count;
 | 
			
		||||
    expression();
 | 
			
		||||
    emitByte(OP_POP);
 | 
			
		||||
    consume(TOKEN_RIGHT_PAREN, "Expect ')' after for clauses.");
 | 
			
		||||
 | 
			
		||||
    emitLoop(loopStart);
 | 
			
		||||
    loopStart = incrementStart;
 | 
			
		||||
    patchJump(bodyJump);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  statement();
 | 
			
		||||
  emitLoop(loopStart);
 | 
			
		||||
 | 
			
		||||
  if (exitJump != -1) {
 | 
			
		||||
    patchJump(exitJump);
 | 
			
		||||
    emitByte(OP_POP); // Condition.
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  endScope();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ifStatement() {
 | 
			
		||||
  consume(TOKEN_LEFT_PAREN, "Expect '(' after 'if'.");
 | 
			
		||||
  expression();
 | 
			
		||||
  consume(TOKEN_RIGHT_PAREN, "Expect ')' after condition.");
 | 
			
		||||
 | 
			
		||||
  int thenJump = emitJump(OP_JUMP_IF_FALSE);
 | 
			
		||||
  emitByte(OP_POP);
 | 
			
		||||
  statement();
 | 
			
		||||
 | 
			
		||||
  int elseJump = emitJump(OP_JUMP);
 | 
			
		||||
 | 
			
		||||
  patchJump(thenJump);
 | 
			
		||||
  emitByte(OP_POP);
 | 
			
		||||
 | 
			
		||||
  if (match(TOKEN_ELSE))
 | 
			
		||||
    statement();
 | 
			
		||||
  patchJump(elseJump);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void printStatement() {
 | 
			
		||||
  expression();
 | 
			
		||||
  consume(TOKEN_SEMICOLON, "Expect ';' after value.");
 | 
			
		||||
  emitByte(OP_PRINT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void returnStatement() {
 | 
			
		||||
  if (current->type == TYPE_SCRIPT) {
 | 
			
		||||
    error("Can't return from top-level code.");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (match(TOKEN_SEMICOLON)) {
 | 
			
		||||
    emitReturn();
 | 
			
		||||
  } else {
 | 
			
		||||
    if (current->type == TYPE_INITIALIZER) {
 | 
			
		||||
      error("Can't return a value from an initializer.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    expression();
 | 
			
		||||
    consume(TOKEN_SEMICOLON, "Expect ';' after return value.");
 | 
			
		||||
    emitByte(OP_RETURN);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void whileStatement() {
 | 
			
		||||
  int loopStart = currentChunk()->count;
 | 
			
		||||
  consume(TOKEN_LEFT_PAREN, "Expect '(' after 'while'.");
 | 
			
		||||
  expression();
 | 
			
		||||
  consume(TOKEN_RIGHT_PAREN, "Expect ')' after condition.");
 | 
			
		||||
 | 
			
		||||
  int exitJump = emitJump(OP_JUMP_IF_FALSE);
 | 
			
		||||
  emitByte(OP_POP);
 | 
			
		||||
  statement();
 | 
			
		||||
  emitLoop(loopStart);
 | 
			
		||||
 | 
			
		||||
  patchJump(exitJump);
 | 
			
		||||
  emitByte(OP_POP);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void synchronize() {
 | 
			
		||||
  parser.panicMode = false;
 | 
			
		||||
 | 
			
		||||
  while (parser.current.type != TOKEN_EOF) {
 | 
			
		||||
    if (parser.previous.type == TOKEN_SEMICOLON)
 | 
			
		||||
      return;
 | 
			
		||||
    switch (parser.current.type) {
 | 
			
		||||
    case TOKEN_TYPE:
 | 
			
		||||
    case TOKEN_FN:
 | 
			
		||||
    case TOKEN_LET:
 | 
			
		||||
    case TOKEN_FOR:
 | 
			
		||||
    case TOKEN_IF:
 | 
			
		||||
    case TOKEN_WHILE:
 | 
			
		||||
    case TOKEN_PRINT:
 | 
			
		||||
    case TOKEN_RETURN:
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
    default:; // Do nothing.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    advance();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void declaration() {
 | 
			
		||||
  if (match(TOKEN_TYPE)) {
 | 
			
		||||
    typeDeclaration();
 | 
			
		||||
  } else if (match(TOKEN_FN)) {
 | 
			
		||||
    funDeclaration();
 | 
			
		||||
  } else if (match(TOKEN_LET)) {
 | 
			
		||||
    varDeclaration();
 | 
			
		||||
  } else {
 | 
			
		||||
    statement();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (parser.panicMode)
 | 
			
		||||
    synchronize();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void statement() {
 | 
			
		||||
  if (match(TOKEN_PRINT)) {
 | 
			
		||||
    printStatement();
 | 
			
		||||
  } else if (match(TOKEN_FOR)) {
 | 
			
		||||
    forStatement();
 | 
			
		||||
  } else if (match(TOKEN_IF)) {
 | 
			
		||||
    ifStatement();
 | 
			
		||||
  } else if (match(TOKEN_RETURN)) {
 | 
			
		||||
    returnStatement();
 | 
			
		||||
  } else if (match(TOKEN_WHILE)) {
 | 
			
		||||
    whileStatement();
 | 
			
		||||
  } else if (match(TOKEN_LEFT_BRACE)) {
 | 
			
		||||
    beginScope();
 | 
			
		||||
    block();
 | 
			
		||||
    endScope();
 | 
			
		||||
  } else {
 | 
			
		||||
    expressionStatement();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void grouping(bool canAssign) {
 | 
			
		||||
  expression();
 | 
			
		||||
  consume(TOKEN_RIGHT_PAREN, "Expect ')' after expression.");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void number(bool canAssign) {
 | 
			
		||||
  double value = strtod(parser.previous.start, NULL);
 | 
			
		||||
  emitConstant(NUMBER_VAL(value));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void or_(bool canAssign) {
 | 
			
		||||
  int elseJump = emitJump(OP_JUMP_IF_FALSE);
 | 
			
		||||
  int endJump = emitJump(OP_JUMP);
 | 
			
		||||
 | 
			
		||||
  patchJump(elseJump);
 | 
			
		||||
  emitByte(OP_POP);
 | 
			
		||||
 | 
			
		||||
  parsePrecedence(PREC_OR);
 | 
			
		||||
  patchJump(endJump);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void string(bool canAssign) {
 | 
			
		||||
  emitConstant(OBJ_VAL(
 | 
			
		||||
      copyString(parser.previous.start + 1, parser.previous.length - 2)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void namedVariable(Token name, bool canAssign) {
 | 
			
		||||
  uint8_t getOp, setOp;
 | 
			
		||||
  int arg = resolveLocal(current, &name);
 | 
			
		||||
  if (arg != -1) {
 | 
			
		||||
    getOp = OP_GET_LOCAL;
 | 
			
		||||
    setOp = OP_SET_LOCAL;
 | 
			
		||||
  } else if ((arg = resolveUpvalue(current, &name)) != -1) {
 | 
			
		||||
    getOp = OP_GET_UPVALUE;
 | 
			
		||||
    setOp = OP_SET_UPVALUE;
 | 
			
		||||
  } else {
 | 
			
		||||
    arg = identifierConstant(&name);
 | 
			
		||||
    getOp = OP_GET_GLOBAL;
 | 
			
		||||
    setOp = OP_SET_GLOBAL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (canAssign && match(TOKEN_EQUAL)) {
 | 
			
		||||
    expression();
 | 
			
		||||
    emitBytes(setOp, (uint8_t)arg);
 | 
			
		||||
  } else {
 | 
			
		||||
    emitBytes(getOp, (uint8_t)arg);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void variable(bool canAssign) {
 | 
			
		||||
  namedVariable(parser.previous, canAssign);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void this_(bool canAssign) {
 | 
			
		||||
  if (currentType == NULL) {
 | 
			
		||||
    error("Can't use 'this' outside of a type.");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  variable(false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void unary(bool canAssign) {
 | 
			
		||||
  TokenType operatorType = parser.previous.type;
 | 
			
		||||
 | 
			
		||||
  // Compile the operand.
 | 
			
		||||
  parsePrecedence(PREC_UNARY);
 | 
			
		||||
 | 
			
		||||
  // Emit the operator instruction.
 | 
			
		||||
  switch (operatorType) {
 | 
			
		||||
  case TOKEN_MINUS:
 | 
			
		||||
    emitByte(OP_NEGATE);
 | 
			
		||||
    break;
 | 
			
		||||
  case TOKEN_BANG:
 | 
			
		||||
    emitByte(OP_NOT);
 | 
			
		||||
    break;
 | 
			
		||||
  default:
 | 
			
		||||
    return; // Unreachable.
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ParseRule rules[] = {
 | 
			
		||||
    [TOKEN_LEFT_PAREN] = {grouping, call, PREC_CALL},
 | 
			
		||||
    [TOKEN_RIGHT_PAREN] = {NULL, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_LEFT_BRACE] = {NULL, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_RIGHT_BRACE] = {NULL, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_COMMA] = {NULL, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_DOT] = {NULL, dot, PREC_CALL},
 | 
			
		||||
    [TOKEN_MINUS] = {unary, 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] = {unary, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_BANG_EQUAL] = {NULL, binary, PREC_EQUALITY},
 | 
			
		||||
    [TOKEN_EQUAL] = {NULL, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_EQUAL_EQUAL] = {NULL, binary, PREC_EQUALITY},
 | 
			
		||||
    [TOKEN_GREATER] = {NULL, binary, PREC_COMPARISON},
 | 
			
		||||
    [TOKEN_GREATER_EQUAL] = {NULL, binary, PREC_COMPARISON},
 | 
			
		||||
    [TOKEN_LESS] = {NULL, binary, PREC_COMPARISON},
 | 
			
		||||
    [TOKEN_LESS_EQUAL] = {NULL, binary, PREC_COMPARISON},
 | 
			
		||||
    [TOKEN_IDENTIFIER] = {variable, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_STRING] = {string, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_NUMBER] = {number, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_AND] = {NULL, and_, PREC_AND},
 | 
			
		||||
    [TOKEN_TYPE] = {NULL, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_ELSE] = {NULL, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_FALSE] = {literal, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_FOR] = {NULL, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_FN] = {NULL, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_IF] = {NULL, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_NIL] = {literal, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_OR] = {NULL, or_, PREC_OR},
 | 
			
		||||
    [TOKEN_PRINT] = {NULL, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_RETURN] = {NULL, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_SUPER] = {NULL, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_THIS] = {this_, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_TRUE] = {literal, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_LET] = {NULL, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_WHILE] = {NULL, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_ERROR] = {NULL, NULL, PREC_NONE},
 | 
			
		||||
    [TOKEN_EOF] = {NULL, NULL, PREC_NONE},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void parsePrecedence(Precedence precedence) {
 | 
			
		||||
  advance();
 | 
			
		||||
  ParseFn prefixRule = getRule(parser.previous.type)->prefix;
 | 
			
		||||
  if (prefixRule == NULL) {
 | 
			
		||||
    error("Expect expression.");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool canAssign = precedence <= PREC_ASSIGNMENT;
 | 
			
		||||
  prefixRule(canAssign);
 | 
			
		||||
 | 
			
		||||
  while (precedence <= getRule(parser.current.type)->precedence) {
 | 
			
		||||
    advance();
 | 
			
		||||
    ParseFn infixRule = getRule(parser.previous.type)->infix;
 | 
			
		||||
    infixRule(canAssign);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (canAssign && match(TOKEN_EQUAL)) {
 | 
			
		||||
    error("Invalid assignment target.");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ParseRule *getRule(TokenType type) { return &rules[type]; }
 | 
			
		||||
 | 
			
		||||
ObjFunction *compile(const char *source) {
 | 
			
		||||
  initScanner(source);
 | 
			
		||||
  Compiler compiler;
 | 
			
		||||
  initCompiler(&compiler, TYPE_SCRIPT);
 | 
			
		||||
 | 
			
		||||
  parser.hadError = false;
 | 
			
		||||
  parser.panicMode = false;
 | 
			
		||||
 | 
			
		||||
  advance();
 | 
			
		||||
 | 
			
		||||
  while (!match(TOKEN_EOF)) {
 | 
			
		||||
    declaration();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ObjFunction *function = endCompiler();
 | 
			
		||||
  return parser.hadError ? NULL : function;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void markCompilerRoots() {
 | 
			
		||||
  Compiler *compiler = current;
 | 
			
		||||
  while (compiler != NULL) {
 | 
			
		||||
    markObject((Obj *)compiler->function);
 | 
			
		||||
    compiler = compiler->enclosing;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,11 +0,0 @@
 | 
			
		|||
#ifndef zlc_compiler_h
 | 
			
		||||
#define zlc_compiler_h
 | 
			
		||||
 | 
			
		||||
#include "vm.h"
 | 
			
		||||
#include "object.h"
 | 
			
		||||
 | 
			
		||||
ObjFunction* compile(const char* source);
 | 
			
		||||
void markCompilerRoots();
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										148
									
								
								src/debug.c
								
								
								
								
							
							
						
						
									
										148
									
								
								src/debug.c
								
								
								
								
							| 
						 | 
				
			
			@ -1,148 +0,0 @@
 | 
			
		|||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#include "debug.h"
 | 
			
		||||
#include "object.h"
 | 
			
		||||
#include "value.h"
 | 
			
		||||
 | 
			
		||||
void disassembleChunk(Chunk *chunk, const char *name) {
 | 
			
		||||
  printf("== %s ==\n", name);
 | 
			
		||||
 | 
			
		||||
  for (int offset = 0; offset < chunk->count;) {
 | 
			
		||||
    offset = disassembleInstruction(chunk, offset);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int constantInstruction(const char *name, Chunk *chunk, int offset) {
 | 
			
		||||
  uint8_t constant = chunk->code[offset + 1];
 | 
			
		||||
  printf("%-16s %4d '", name, constant);
 | 
			
		||||
  printValue(chunk->constants.values[constant]);
 | 
			
		||||
  printf("'\n");
 | 
			
		||||
  return offset + 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int invokeInstruction(const char *name, Chunk *chunk, int offset) {
 | 
			
		||||
  uint8_t constant = chunk->code[offset + 1];
 | 
			
		||||
  uint8_t argCount = chunk->code[offset + 2];
 | 
			
		||||
  printf("%-16s (%d args) %4d '", name, argCount, constant);
 | 
			
		||||
  printValue(chunk->constants.values[constant]);
 | 
			
		||||
  printf("'\n");
 | 
			
		||||
  return offset + 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int simpleInstruction(const char *name, int offset) {
 | 
			
		||||
  printf("%s\n", name);
 | 
			
		||||
  return offset + 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int byteInstruction(const char *name, Chunk *chunk, int offset) {
 | 
			
		||||
  uint8_t slot = chunk->code[offset + 1];
 | 
			
		||||
  printf("%-16s %4d\n", name, slot);
 | 
			
		||||
  return offset + 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int jumpInstruction(const char *name, int sign, Chunk *chunk,
 | 
			
		||||
                           int offset) {
 | 
			
		||||
  uint16_t jump = (uint16_t)(chunk->code[offset + 1] << 8);
 | 
			
		||||
  jump |= chunk->code[offset + 2];
 | 
			
		||||
  printf("%-16s %4d -> %d\n", name, offset, offset + 3 + sign * jump);
 | 
			
		||||
  return offset + 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int disassembleInstruction(Chunk *chunk, int offset) {
 | 
			
		||||
  printf("%04d ", offset);
 | 
			
		||||
  if (offset > 0 && chunk->lines[offset] == chunk->lines[offset - 1]) {
 | 
			
		||||
    printf("   | ");
 | 
			
		||||
  } else {
 | 
			
		||||
    printf("%4d ", chunk->lines[offset]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  uint8_t instruction = chunk->code[offset];
 | 
			
		||||
  switch (instruction) {
 | 
			
		||||
  case OP_CONSTANT:
 | 
			
		||||
    return constantInstruction("OP_CONSTANT", chunk, offset);
 | 
			
		||||
  case OP_NIL:
 | 
			
		||||
    return simpleInstruction("OP_NIL", offset);
 | 
			
		||||
  case OP_TRUE:
 | 
			
		||||
    return simpleInstruction("OP_TRUE", offset);
 | 
			
		||||
  case OP_FALSE:
 | 
			
		||||
    return simpleInstruction("OP_FALSE", offset);
 | 
			
		||||
  case OP_SET_GLOBAL:
 | 
			
		||||
    return constantInstruction("OP_SET_GLOBAL", chunk, offset);
 | 
			
		||||
  case OP_EQUAL:
 | 
			
		||||
    return simpleInstruction("OP_EQUAL", offset);
 | 
			
		||||
  case OP_GET_PROPERTY:
 | 
			
		||||
    return constantInstruction("OP_GET_PROPERTY", chunk, offset);
 | 
			
		||||
  case OP_SET_PROPERTY:
 | 
			
		||||
    return constantInstruction("OP_SET_PROPERTY", chunk, offset);
 | 
			
		||||
  case OP_GET_UPVALUE:
 | 
			
		||||
    return byteInstruction("OP_GET_UPVALUE", chunk, offset);
 | 
			
		||||
  case OP_SET_UPVALUE:
 | 
			
		||||
    return byteInstruction("OP_SET_UPVALUE", chunk, offset);
 | 
			
		||||
  case OP_POP:
 | 
			
		||||
    return simpleInstruction("OP_POP", offset);
 | 
			
		||||
  case OP_GET_LOCAL:
 | 
			
		||||
    return byteInstruction("OP_GET_LOCAL", chunk, offset);
 | 
			
		||||
  case OP_SET_LOCAL:
 | 
			
		||||
    return byteInstruction("OP_SET_LOCAL", chunk, offset);
 | 
			
		||||
  case OP_GET_GLOBAL:
 | 
			
		||||
    return constantInstruction("OP_GET_GLOBAL", chunk, offset);
 | 
			
		||||
  case OP_DEFINE_GLOBAL:
 | 
			
		||||
    return constantInstruction("OP_DEFINE_GLOBAL", chunk, offset);
 | 
			
		||||
  case OP_GREATER:
 | 
			
		||||
    return simpleInstruction("OP_GREATER", offset);
 | 
			
		||||
  case OP_LESS:
 | 
			
		||||
    return simpleInstruction("OP_LESS", offset);
 | 
			
		||||
  case OP_ADD:
 | 
			
		||||
    return simpleInstruction("OP_ADD", offset);
 | 
			
		||||
  case OP_SUBTRACT:
 | 
			
		||||
    return simpleInstruction("OP_SUBTRACT", offset);
 | 
			
		||||
  case OP_MULTIPLY:
 | 
			
		||||
    return simpleInstruction("OP_MULTIPLY", offset);
 | 
			
		||||
  case OP_DIVIDE:
 | 
			
		||||
    return simpleInstruction("OP_DIVIDE", offset);
 | 
			
		||||
  case OP_NOT:
 | 
			
		||||
    return simpleInstruction("OP_NOT", offset);
 | 
			
		||||
  case OP_NEGATE:
 | 
			
		||||
    return simpleInstruction("OP_NEGATE", offset);
 | 
			
		||||
  case OP_PRINT:
 | 
			
		||||
    return simpleInstruction("OP_PRINT", offset);
 | 
			
		||||
  case OP_JUMP:
 | 
			
		||||
    return jumpInstruction("OP_JUMP", 1, chunk, offset);
 | 
			
		||||
  case OP_JUMP_IF_FALSE:
 | 
			
		||||
    return jumpInstruction("OP_JUMP_IF_FALSE", 1, chunk, offset);
 | 
			
		||||
  case OP_LOOP:
 | 
			
		||||
    return jumpInstruction("OP_LOOP", -1, chunk, offset);
 | 
			
		||||
  case OP_CALL:
 | 
			
		||||
    return byteInstruction("OP_CALL", chunk, offset);
 | 
			
		||||
  case OP_INVOKE:
 | 
			
		||||
    return invokeInstruction("OP_INVOKE", chunk, offset);
 | 
			
		||||
  case OP_CLOSURE: {
 | 
			
		||||
    offset++;
 | 
			
		||||
    uint8_t constant = chunk->code[offset++];
 | 
			
		||||
    printf("%-16s %4d ", "OP_CLOSURE", constant);
 | 
			
		||||
    printValue(chunk->constants.values[constant]);
 | 
			
		||||
    printf("\n");
 | 
			
		||||
 | 
			
		||||
    ObjFunction *function = AS_FUNCTION(chunk->constants.values[constant]);
 | 
			
		||||
    for (int j = 0; j < function->upvalueCount; j++) {
 | 
			
		||||
      int isLocal = chunk->code[offset++];
 | 
			
		||||
      int index = chunk->code[offset++];
 | 
			
		||||
      printf("%04d      |                     %s %d\n", offset - 2,
 | 
			
		||||
             isLocal ? "local" : "upvalue", index);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return offset;
 | 
			
		||||
  }
 | 
			
		||||
  case OP_CLOSE_UPVALUE:
 | 
			
		||||
    return simpleInstruction("OP_CLOSE_UPVALUE", offset);
 | 
			
		||||
  case OP_RETURN:
 | 
			
		||||
    return simpleInstruction("OP_RETURN", offset);
 | 
			
		||||
  case OP_TYPE:
 | 
			
		||||
    return constantInstruction("OP_TYPE", chunk, offset);
 | 
			
		||||
  case OP_METHOD:
 | 
			
		||||
    return constantInstruction("OP_METHOD", chunk, offset);
 | 
			
		||||
  default:
 | 
			
		||||
    printf("Unknown opcode %d\n", instruction);
 | 
			
		||||
    return offset + 1;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,9 +0,0 @@
 | 
			
		|||
#ifndef zlc_debug_h
 | 
			
		||||
#define zlc_debug_h
 | 
			
		||||
 | 
			
		||||
#include "chunk.h"
 | 
			
		||||
 | 
			
		||||
void disassembleChunk(Chunk* chunk, const char* name);
 | 
			
		||||
int disassembleInstruction(Chunk* chunk, int offset);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										77
									
								
								src/main.c
								
								
								
								
							
							
						
						
									
										77
									
								
								src/main.c
								
								
								
								
							| 
						 | 
				
			
			@ -1,77 +0,0 @@
 | 
			
		|||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "common.h"
 | 
			
		||||
#include "chunk.h"
 | 
			
		||||
#include "debug.h"
 | 
			
		||||
#include "vm.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void repl() {
 | 
			
		||||
  char line[1024];
 | 
			
		||||
  for (;;) {
 | 
			
		||||
    printf("> ");
 | 
			
		||||
 | 
			
		||||
    if (!fgets(line, sizeof(line), stdin)) {
 | 
			
		||||
      printf("\n");
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    interpret(line);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char* readFile(const char* path) {
 | 
			
		||||
  FILE* file = fopen(path, "rb");
 | 
			
		||||
  if (file == NULL) {
 | 
			
		||||
    fprintf(stderr, "Could not open file \"%s\".\n", path);
 | 
			
		||||
    exit(74);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fseek(file, 0L, SEEK_END);
 | 
			
		||||
  size_t fileSize = ftell(file);
 | 
			
		||||
  rewind(file);
 | 
			
		||||
 | 
			
		||||
  char* buffer = (char*)malloc(fileSize + 1);
 | 
			
		||||
  if (buffer == NULL) {
 | 
			
		||||
    fprintf(stderr, "Not enough memory to read \"%s\".\n", path);
 | 
			
		||||
    exit(74);
 | 
			
		||||
  }
 | 
			
		||||
  size_t bytesRead = fread(buffer, sizeof(char), fileSize, file);
 | 
			
		||||
  if (bytesRead < fileSize) {
 | 
			
		||||
    fprintf(stderr, "Could not read file \"%s\".\n", path);
 | 
			
		||||
    exit(74);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  buffer[bytesRead] = '\0';
 | 
			
		||||
 | 
			
		||||
  fclose(file);
 | 
			
		||||
  return buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void runFile(const char* path) {
 | 
			
		||||
  char* source = readFile(path);
 | 
			
		||||
  InterpretResult result = interpret(source);
 | 
			
		||||
  free(source); 
 | 
			
		||||
 | 
			
		||||
  if (result == INTERPRET_COMPILE_ERROR) exit(65);
 | 
			
		||||
  if (result == INTERPRET_RUNTIME_ERROR) exit(70);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, const char* argv[]) {
 | 
			
		||||
  initVM();
 | 
			
		||||
 | 
			
		||||
  if (argc == 1) {
 | 
			
		||||
    repl();
 | 
			
		||||
  } else if (argc == 2) {
 | 
			
		||||
    runFile(argv[1]);
 | 
			
		||||
  } else {
 | 
			
		||||
    fprintf(stderr, "Usage: zlc [path]\n");
 | 
			
		||||
    exit(64);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  freeVM();
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										247
									
								
								src/memory.c
								
								
								
								
							
							
						
						
									
										247
									
								
								src/memory.c
								
								
								
								
							| 
						 | 
				
			
			@ -1,247 +0,0 @@
 | 
			
		|||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
#include "compiler.h"
 | 
			
		||||
#include "memory.h"
 | 
			
		||||
#include "vm.h"
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_LOG_GC
 | 
			
		||||
#include "debug.h"
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define GC_HEAP_GROW_FACTOR 2
 | 
			
		||||
 | 
			
		||||
void *reallocate(void *pointer, size_t oldSize, size_t newSize) {
 | 
			
		||||
  vm.bytesAllocated += newSize - oldSize;
 | 
			
		||||
  if (newSize > oldSize) {
 | 
			
		||||
#ifdef DEBUG_STRESS_GC
 | 
			
		||||
    collectGarbage();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (vm.bytesAllocated > vm.nextGC) {
 | 
			
		||||
      collectGarbage();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (newSize == 0) {
 | 
			
		||||
    free(pointer);
 | 
			
		||||
    return NULL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void *result = realloc(pointer, newSize);
 | 
			
		||||
  if (result == NULL)
 | 
			
		||||
    exit(1);
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void markObject(Obj *object) {
 | 
			
		||||
  if (object == NULL)
 | 
			
		||||
    return;
 | 
			
		||||
  if (object->isMarked)
 | 
			
		||||
    return;
 | 
			
		||||
#ifdef DEBUG_LOG_GC
 | 
			
		||||
  printf("%p mark ", (void *)object);
 | 
			
		||||
  printValue(OBJ_VAL(object));
 | 
			
		||||
  printf("\n");
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  object->isMarked = true;
 | 
			
		||||
 | 
			
		||||
  if (vm.grayCapacity < vm.grayCount + 1) {
 | 
			
		||||
    vm.grayCapacity = GROW_CAPACITY(vm.grayCapacity);
 | 
			
		||||
    vm.grayStack =
 | 
			
		||||
        (Obj **)realloc(vm.grayStack, sizeof(Obj *) * vm.grayCapacity);
 | 
			
		||||
 | 
			
		||||
    if (vm.grayStack == NULL)
 | 
			
		||||
      exit(1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  vm.grayStack[vm.grayCount++] = object;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void markValue(Value value) {
 | 
			
		||||
  if (IS_OBJ(value))
 | 
			
		||||
    markObject(AS_OBJ(value));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void markArray(ValueArray *array) {
 | 
			
		||||
  for (int i = 0; i < array->count; i++) {
 | 
			
		||||
    markValue(array->values[i]);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void blackenObject(Obj *object) {
 | 
			
		||||
#ifdef DEBUG_LOG_GC
 | 
			
		||||
  printf("%p blacken ", (void *)object);
 | 
			
		||||
  printValue(OBJ_VAL(object));
 | 
			
		||||
  printf("\n");
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  switch (object->type) {
 | 
			
		||||
  case OBJ_BOUND_METHOD: {
 | 
			
		||||
    ObjBoundMethod *bound = (ObjBoundMethod *)object;
 | 
			
		||||
    markValue(bound->receiver);
 | 
			
		||||
    markObject((Obj *)bound->method);
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case OBJ_TYPE: {
 | 
			
		||||
    TypeObj *t = (TypeObj *)object;
 | 
			
		||||
    markObject((Obj *)t->name);
 | 
			
		||||
    markTable(&t->methods);
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case OBJ_CLOSURE: {
 | 
			
		||||
    ObjClosure *closure = (ObjClosure *)object;
 | 
			
		||||
    markObject((Obj *)closure->function);
 | 
			
		||||
    for (int i = 0; i < closure->upvalueCount; i++) {
 | 
			
		||||
      markObject((Obj *)closure->upvalues[i]);
 | 
			
		||||
    }
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case OBJ_FUNCTION: {
 | 
			
		||||
    ObjFunction *function = (ObjFunction *)object;
 | 
			
		||||
    markObject((Obj *)function->name);
 | 
			
		||||
    markArray(&function->chunk.constants);
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case OBJ_INSTANCE: {
 | 
			
		||||
    ObjInstance *instance = (ObjInstance *)object;
 | 
			
		||||
    markObject((Obj *)instance->t);
 | 
			
		||||
    markTable(&instance->fields);
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case OBJ_UPVALUE:
 | 
			
		||||
    markValue(((ObjUpvalue *)object)->closed);
 | 
			
		||||
    break;
 | 
			
		||||
  case OBJ_NATIVE:
 | 
			
		||||
  case OBJ_STRING:
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void freeObject(Obj *object) {
 | 
			
		||||
  switch (object->type) {
 | 
			
		||||
  case OBJ_BOUND_METHOD:
 | 
			
		||||
    FREE(ObjBoundMethod, object);
 | 
			
		||||
    break;
 | 
			
		||||
  case OBJ_TYPE: {
 | 
			
		||||
    TypeObj *t = (TypeObj *)object;
 | 
			
		||||
    freeTable(&t->methods);
 | 
			
		||||
    FREE(TypeObj, object);
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case OBJ_CLOSURE: {
 | 
			
		||||
    ObjClosure *closure = (ObjClosure *)object;
 | 
			
		||||
    FREE_ARRAY(ObjUpvalue *, closure->upvalues, closure->upvalueCount);
 | 
			
		||||
    FREE(ObjClosure, object);
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case OBJ_FUNCTION: {
 | 
			
		||||
    ObjFunction *function = (ObjFunction *)object;
 | 
			
		||||
    freeChunk(&function->chunk);
 | 
			
		||||
    FREE(ObjFunction, object);
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case OBJ_INSTANCE: {
 | 
			
		||||
    ObjInstance *instance = (ObjInstance *)object;
 | 
			
		||||
    freeTable(&instance->fields);
 | 
			
		||||
    FREE(ObjInstance, object);
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case OBJ_NATIVE:
 | 
			
		||||
    FREE(ObjNative, object);
 | 
			
		||||
    break;
 | 
			
		||||
  case OBJ_STRING: {
 | 
			
		||||
    ObjString *string = (ObjString *)object;
 | 
			
		||||
    FREE_ARRAY(char, string->chars, string->length + 1);
 | 
			
		||||
    FREE(ObjString, object);
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case OBJ_UPVALUE: {
 | 
			
		||||
    FREE(ObjUpvalue, object);
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void freeObjects() {
 | 
			
		||||
  Obj *object = vm.objects;
 | 
			
		||||
  while (object != NULL) {
 | 
			
		||||
#ifdef DEBUG_LOG_GC
 | 
			
		||||
    printf("%p free type %d\n", (void *)object, object->type);
 | 
			
		||||
#endif
 | 
			
		||||
    Obj *next = object->next;
 | 
			
		||||
    freeObject(object);
 | 
			
		||||
    object = next;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  free(vm.grayStack);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void markRoots() {
 | 
			
		||||
  for (Value *slot = vm.stack; slot < vm.stackTop; slot++) {
 | 
			
		||||
    markValue(*slot);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (int i = 0; i < vm.frameCount; i++) {
 | 
			
		||||
    markObject((Obj *)vm.frames[i].closure);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (ObjUpvalue *upvalue = vm.openUpvalues; upvalue != NULL;
 | 
			
		||||
       upvalue = upvalue->next) {
 | 
			
		||||
    markObject((Obj *)upvalue);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  markTable(&vm.globals);
 | 
			
		||||
  markCompilerRoots();
 | 
			
		||||
  markObject((Obj *)vm.initString);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void traceReferences() {
 | 
			
		||||
  while (vm.grayCount > 0) {
 | 
			
		||||
    Obj *object = vm.grayStack[--vm.grayCount];
 | 
			
		||||
    blackenObject(object);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void sweep() {
 | 
			
		||||
  Obj *previous = NULL;
 | 
			
		||||
  Obj *object = vm.objects;
 | 
			
		||||
  while (object != NULL) {
 | 
			
		||||
    if (object->isMarked) {
 | 
			
		||||
      object->isMarked = false;
 | 
			
		||||
      previous = object;
 | 
			
		||||
      object = object->next;
 | 
			
		||||
    } else {
 | 
			
		||||
      Obj *unreached = object;
 | 
			
		||||
      object = object->next;
 | 
			
		||||
      if (previous != NULL) {
 | 
			
		||||
        previous->next = object;
 | 
			
		||||
      } else {
 | 
			
		||||
        vm.objects = object;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      freeObject(unreached);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void collectGarbage() {
 | 
			
		||||
#ifdef DEBUG_LOG_GC
 | 
			
		||||
  printf("-- gc begin\n");
 | 
			
		||||
  size_t before = vm.bytesAllocated;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  markRoots();
 | 
			
		||||
  traceReferences();
 | 
			
		||||
  tableRemoveWhite(&vm.strings);
 | 
			
		||||
  sweep();
 | 
			
		||||
 | 
			
		||||
  vm.nextGC = vm.bytesAllocated * GC_HEAP_GROW_FACTOR;
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_LOG_GC
 | 
			
		||||
  printf("-- gc end\n");
 | 
			
		||||
  printf("   collected %zu bytes (from %zu to %zu) next at %zu\n",
 | 
			
		||||
         before - vm.bytesAllocated, before, vm.bytesAllocated, vm.nextGC);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								src/memory.h
								
								
								
								
							
							
						
						
									
										28
									
								
								src/memory.h
								
								
								
								
							| 
						 | 
				
			
			@ -1,28 +0,0 @@
 | 
			
		|||
#ifndef zlc_memory_h
 | 
			
		||||
#define zlc_memory_h
 | 
			
		||||
 | 
			
		||||
#include "common.h"
 | 
			
		||||
#include "object.h"
 | 
			
		||||
 | 
			
		||||
#define ALLOCATE(type, count) \
 | 
			
		||||
    (type*)reallocate(NULL, 0, sizeof(type) * (count))
 | 
			
		||||
 | 
			
		||||
#define FREE(type, pointer) reallocate(pointer, sizeof(type), 0)
 | 
			
		||||
 | 
			
		||||
#define GROW_CAPACITY(capacity) \
 | 
			
		||||
    ((capacity) < 8 ? 8 : (capacity) * 2)
 | 
			
		||||
 | 
			
		||||
#define GROW_ARRAY(type, pointer, oldCount, newCount) \
 | 
			
		||||
    (type*)reallocate(pointer, sizeof(type) * (oldCount), \
 | 
			
		||||
        sizeof(type) * (newCount))
 | 
			
		||||
 | 
			
		||||
#define FREE_ARRAY(type, pointer, oldCount) \
 | 
			
		||||
    reallocate(pointer, sizeof(type) * (oldCount), 0)
 | 
			
		||||
 | 
			
		||||
void* reallocate(void* pointer, size_t oldSize, size_t newSize);
 | 
			
		||||
void markObject(Obj* object);
 | 
			
		||||
void markValue(Value value);
 | 
			
		||||
void collectGarbage();
 | 
			
		||||
void freeObjects();
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										163
									
								
								src/object.c
								
								
								
								
							
							
						
						
									
										163
									
								
								src/object.c
								
								
								
								
							| 
						 | 
				
			
			@ -1,163 +0,0 @@
 | 
			
		|||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "memory.h"
 | 
			
		||||
#include "object.h"
 | 
			
		||||
#include "table.h"
 | 
			
		||||
#include "value.h"
 | 
			
		||||
#include "vm.h"
 | 
			
		||||
 | 
			
		||||
#define ALLOCATE_OBJ(type, objectType)                                         \
 | 
			
		||||
  (type *)allocateObject(sizeof(type), objectType)
 | 
			
		||||
 | 
			
		||||
static Obj *allocateObject(size_t size, ObjType type) {
 | 
			
		||||
  Obj *object = (Obj *)reallocate(NULL, 0, size);
 | 
			
		||||
  object->type = type;
 | 
			
		||||
  object->isMarked = false;
 | 
			
		||||
  object->next = vm.objects;
 | 
			
		||||
  vm.objects = object;
 | 
			
		||||
 | 
			
		||||
#ifdef DEBUG_LOG_GC
 | 
			
		||||
  printf("%p allocate %zu for %d\n", (void *)object, size, type);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  return object;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ObjBoundMethod *newBoundMethod(Value receiver, ObjClosure *method) {
 | 
			
		||||
  ObjBoundMethod *bound = ALLOCATE_OBJ(ObjBoundMethod, OBJ_BOUND_METHOD);
 | 
			
		||||
  bound->receiver = receiver;
 | 
			
		||||
  bound->method = method;
 | 
			
		||||
  return bound;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TypeObj *newType(ObjString *name) {
 | 
			
		||||
  TypeObj *t = ALLOCATE_OBJ(TypeObj, OBJ_TYPE);
 | 
			
		||||
  t->name = name;
 | 
			
		||||
  initTable(&t->methods);
 | 
			
		||||
  return t;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ObjClosure *newClosure(ObjFunction *function) {
 | 
			
		||||
  ObjUpvalue **upvalues = ALLOCATE(ObjUpvalue *, function->upvalueCount);
 | 
			
		||||
  for (int i = 0; i < function->upvalueCount; i++) {
 | 
			
		||||
    upvalues[i] = NULL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ObjClosure *closure = ALLOCATE_OBJ(ObjClosure, OBJ_CLOSURE);
 | 
			
		||||
  closure->function = function;
 | 
			
		||||
  closure->upvalues = upvalues;
 | 
			
		||||
  closure->upvalueCount = function->upvalueCount;
 | 
			
		||||
  return closure;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ObjFunction *newFunction() {
 | 
			
		||||
  ObjFunction *function = ALLOCATE_OBJ(ObjFunction, OBJ_FUNCTION);
 | 
			
		||||
  function->arity = 0;
 | 
			
		||||
  function->upvalueCount = 0;
 | 
			
		||||
  function->name = NULL;
 | 
			
		||||
  initChunk(&function->chunk);
 | 
			
		||||
  return function;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ObjInstance *newInstance(TypeObj *t) {
 | 
			
		||||
  ObjInstance *instance = ALLOCATE_OBJ(ObjInstance, OBJ_INSTANCE);
 | 
			
		||||
  instance->t = t;
 | 
			
		||||
  initTable(&instance->fields);
 | 
			
		||||
  return instance;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ObjNative *newNative(NativeFn function) {
 | 
			
		||||
  ObjNative *native = ALLOCATE_OBJ(ObjNative, OBJ_NATIVE);
 | 
			
		||||
  native->function = function;
 | 
			
		||||
  return native;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ObjString *allocateString(char *chars, int length, uint32_t hash) {
 | 
			
		||||
  ObjString *string = ALLOCATE_OBJ(ObjString, OBJ_STRING);
 | 
			
		||||
  string->length = length;
 | 
			
		||||
  string->chars = chars;
 | 
			
		||||
  string->hash = hash;
 | 
			
		||||
  push(OBJ_VAL(string));
 | 
			
		||||
  tableSet(&vm.strings, string, NIL_VAL);
 | 
			
		||||
  pop();
 | 
			
		||||
  return string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t hashString(const char *key, int length) {
 | 
			
		||||
  uint32_t hash = 2166136261u;
 | 
			
		||||
  for (int i = 0; i < length; i++) {
 | 
			
		||||
    hash ^= (uint8_t)key[i];
 | 
			
		||||
    hash *= 16777619;
 | 
			
		||||
  }
 | 
			
		||||
  return hash;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ObjString *takeString(char *chars, int length) {
 | 
			
		||||
  uint32_t hash = hashString(chars, length);
 | 
			
		||||
  ObjString *interned = tableFindString(&vm.strings, chars, length, hash);
 | 
			
		||||
  if (interned != NULL) {
 | 
			
		||||
    FREE_ARRAY(char, chars, length + 1);
 | 
			
		||||
    return interned;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return allocateString(chars, length, hash);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ObjString *copyString(const char *chars, int length) {
 | 
			
		||||
  uint32_t hash = hashString(chars, length);
 | 
			
		||||
  ObjString *interned = tableFindString(&vm.strings, chars, length, hash);
 | 
			
		||||
  if (interned != NULL)
 | 
			
		||||
    return interned;
 | 
			
		||||
 | 
			
		||||
  char *heapChars = ALLOCATE(char, length + 1);
 | 
			
		||||
  memcpy(heapChars, chars, length);
 | 
			
		||||
  heapChars[length] = '\0';
 | 
			
		||||
  return allocateString(heapChars, length, hash);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ObjUpvalue *newUpvalue(Value *slot) {
 | 
			
		||||
  ObjUpvalue *upvalue = ALLOCATE_OBJ(ObjUpvalue, OBJ_UPVALUE);
 | 
			
		||||
  upvalue->closed = NIL_VAL;
 | 
			
		||||
  upvalue->location = slot;
 | 
			
		||||
  upvalue->next = NULL;
 | 
			
		||||
  return upvalue;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void printFunction(ObjFunction *function) {
 | 
			
		||||
  if (function->name == NULL) {
 | 
			
		||||
    printf("<script>");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  printf("<fn %s>", function->name->chars);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void printObject(Value value) {
 | 
			
		||||
  switch (OBJ_TYPE(value)) {
 | 
			
		||||
  case OBJ_BOUND_METHOD:
 | 
			
		||||
    printFunction(AS_BOUND_METHOD(value)->method->function);
 | 
			
		||||
    break;
 | 
			
		||||
  case OBJ_TYPE:
 | 
			
		||||
    printf("%s", AS_TYPE(value)->name->chars);
 | 
			
		||||
    break;
 | 
			
		||||
  case OBJ_CLOSURE:
 | 
			
		||||
    printFunction(AS_CLOSURE(value)->function);
 | 
			
		||||
    break;
 | 
			
		||||
  case OBJ_FUNCTION:
 | 
			
		||||
    printFunction(AS_FUNCTION(value));
 | 
			
		||||
    break;
 | 
			
		||||
  case OBJ_INSTANCE:
 | 
			
		||||
    printf("%s instance", AS_INSTANCE(value)->t->name->chars);
 | 
			
		||||
    break;
 | 
			
		||||
  case OBJ_NATIVE:
 | 
			
		||||
    printf("<native fn>");
 | 
			
		||||
    break;
 | 
			
		||||
  case OBJ_STRING:
 | 
			
		||||
    printf("%s", AS_CSTRING(value));
 | 
			
		||||
    break;
 | 
			
		||||
  case OBJ_UPVALUE:
 | 
			
		||||
    printf("upvalue");
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										114
									
								
								src/object.h
								
								
								
								
							
							
						
						
									
										114
									
								
								src/object.h
								
								
								
								
							| 
						 | 
				
			
			@ -1,114 +0,0 @@
 | 
			
		|||
#ifndef zlc_object_h
 | 
			
		||||
#define zlc_object_h
 | 
			
		||||
 | 
			
		||||
#include "chunk.h"
 | 
			
		||||
#include "common.h"
 | 
			
		||||
#include "table.h"
 | 
			
		||||
#include "value.h"
 | 
			
		||||
 | 
			
		||||
#define OBJ_TYPE(value) (AS_OBJ(value)->type)
 | 
			
		||||
 | 
			
		||||
#define IS_BOUND_METHOD(value) isObjType(value, OBJ_BOUND_METHOD)
 | 
			
		||||
#define IS_TYPE(value) isObjType(value, OBJ_TYPE)
 | 
			
		||||
#define IS_CLOSURE(value) isObjType(value, OBJ_CLOSURE)
 | 
			
		||||
#define IS_FUNCTION(value) isObjType(value, OBJ_FUNCTION)
 | 
			
		||||
#define IS_INSTANCE(value) isObjType(value, OBJ_INSTANCE)
 | 
			
		||||
#define IS_NATIVE(value) isObjType(value, OBJ_NATIVE)
 | 
			
		||||
#define IS_STRING(value) isObjType(value, OBJ_STRING)
 | 
			
		||||
 | 
			
		||||
#define AS_BOUND_METHOD(value) ((ObjBoundMethod *)AS_OBJ(value))
 | 
			
		||||
#define AS_TYPE(value) ((TypeObj *)AS_OBJ(value))
 | 
			
		||||
#define AS_CLOSURE(value) ((ObjClosure *)AS_OBJ(value))
 | 
			
		||||
#define AS_FUNCTION(value) ((ObjFunction *)AS_OBJ(value))
 | 
			
		||||
#define AS_INSTANCE(value) ((ObjInstance *)AS_OBJ(value))
 | 
			
		||||
#define AS_NATIVE(value) (((ObjNative *)AS_OBJ(value))->function)
 | 
			
		||||
#define AS_STRING(value) ((ObjString *)AS_OBJ(value))
 | 
			
		||||
#define AS_CSTRING(value) (((ObjString *)AS_OBJ(value))->chars)
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
  OBJ_BOUND_METHOD,
 | 
			
		||||
  OBJ_TYPE,
 | 
			
		||||
  OBJ_CLOSURE,
 | 
			
		||||
  OBJ_FUNCTION,
 | 
			
		||||
  OBJ_INSTANCE,
 | 
			
		||||
  OBJ_NATIVE,
 | 
			
		||||
  OBJ_STRING,
 | 
			
		||||
  OBJ_UPVALUE
 | 
			
		||||
} ObjType;
 | 
			
		||||
 | 
			
		||||
struct Obj {
 | 
			
		||||
  ObjType type;
 | 
			
		||||
  bool isMarked;
 | 
			
		||||
  struct Obj *next;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  Obj obj;
 | 
			
		||||
  int arity;
 | 
			
		||||
  int upvalueCount;
 | 
			
		||||
  Chunk chunk;
 | 
			
		||||
  ObjString *name;
 | 
			
		||||
} ObjFunction;
 | 
			
		||||
 | 
			
		||||
typedef Value (*NativeFn)(int argCount, Value *args);
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  Obj obj;
 | 
			
		||||
  NativeFn function;
 | 
			
		||||
} ObjNative;
 | 
			
		||||
 | 
			
		||||
struct ObjString {
 | 
			
		||||
  Obj obj;
 | 
			
		||||
  int length;
 | 
			
		||||
  char *chars;
 | 
			
		||||
  uint32_t hash;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct ObjUpvalue {
 | 
			
		||||
  Obj obj;
 | 
			
		||||
  Value *location;
 | 
			
		||||
  Value closed;
 | 
			
		||||
  struct ObjUpvalue *next;
 | 
			
		||||
} ObjUpvalue;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  Obj obj;
 | 
			
		||||
  ObjFunction *function;
 | 
			
		||||
  ObjUpvalue **upvalues;
 | 
			
		||||
  int upvalueCount;
 | 
			
		||||
} ObjClosure;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  Obj obj;
 | 
			
		||||
  ObjString *name;
 | 
			
		||||
  Table methods;
 | 
			
		||||
} TypeObj;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  Obj obj;
 | 
			
		||||
  TypeObj *t;
 | 
			
		||||
  Table fields;
 | 
			
		||||
} ObjInstance;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  Obj obj;
 | 
			
		||||
  Value receiver;
 | 
			
		||||
  ObjClosure *method;
 | 
			
		||||
} ObjBoundMethod;
 | 
			
		||||
 | 
			
		||||
ObjBoundMethod *newBoundMethod(Value receiver, ObjClosure *method);
 | 
			
		||||
TypeObj *newType(ObjString *name);
 | 
			
		||||
ObjClosure *newClosure(ObjFunction *function);
 | 
			
		||||
ObjFunction *newFunction();
 | 
			
		||||
ObjInstance *newInstance(TypeObj *t);
 | 
			
		||||
ObjNative *newNative(NativeFn function);
 | 
			
		||||
ObjString *takeString(char *chars, int length);
 | 
			
		||||
ObjString *copyString(const char *chars, int length);
 | 
			
		||||
ObjUpvalue *newUpvalue(Value *slot);
 | 
			
		||||
void printObject(Value value);
 | 
			
		||||
 | 
			
		||||
static inline bool isObjType(Value value, ObjType type) {
 | 
			
		||||
  return IS_OBJ(value) && AS_OBJ(value)->type == type;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										261
									
								
								src/scanner.c
								
								
								
								
							
							
						
						
									
										261
									
								
								src/scanner.c
								
								
								
								
							| 
						 | 
				
			
			@ -1,261 +0,0 @@
 | 
			
		|||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "common.h"
 | 
			
		||||
#include "scanner.h"
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  const char *start;
 | 
			
		||||
  const char *current;
 | 
			
		||||
  int line;
 | 
			
		||||
} Scanner;
 | 
			
		||||
 | 
			
		||||
Scanner scanner;
 | 
			
		||||
 | 
			
		||||
void initScanner(const char *source) {
 | 
			
		||||
  scanner.start = source;
 | 
			
		||||
  scanner.current = source;
 | 
			
		||||
  scanner.line = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool isAlpha(char c) {
 | 
			
		||||
  return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool isDigit(char c) { return c >= '0' && c <= '9'; }
 | 
			
		||||
 | 
			
		||||
static bool isAtEnd() { return *scanner.current == '\0'; }
 | 
			
		||||
 | 
			
		||||
static char advance() {
 | 
			
		||||
  scanner.current++;
 | 
			
		||||
  return scanner.current[-1];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char peek() { return *scanner.current; }
 | 
			
		||||
 | 
			
		||||
static char peekNext() {
 | 
			
		||||
  if (isAtEnd())
 | 
			
		||||
    return '\0';
 | 
			
		||||
  return scanner.current[1];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool match(char expected) {
 | 
			
		||||
  if (isAtEnd())
 | 
			
		||||
    return false;
 | 
			
		||||
  if (*scanner.current != expected)
 | 
			
		||||
    return false;
 | 
			
		||||
  scanner.current++;
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Token makeToken(TokenType type) {
 | 
			
		||||
  Token token;
 | 
			
		||||
  token.type = type;
 | 
			
		||||
  token.start = scanner.start;
 | 
			
		||||
  token.length = (int)(scanner.current - scanner.start);
 | 
			
		||||
  token.line = scanner.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 = scanner.line;
 | 
			
		||||
  return token;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void skipWhitespace() {
 | 
			
		||||
  for (;;) {
 | 
			
		||||
    char c = peek();
 | 
			
		||||
    switch (c) {
 | 
			
		||||
    case ' ':
 | 
			
		||||
    case '\r':
 | 
			
		||||
    case '\t':
 | 
			
		||||
      advance();
 | 
			
		||||
      break;
 | 
			
		||||
    case '\n':
 | 
			
		||||
      scanner.line++;
 | 
			
		||||
      advance();
 | 
			
		||||
      break;
 | 
			
		||||
    case '/':
 | 
			
		||||
      if (peekNext() == '/') {
 | 
			
		||||
        // A comment goes until the end of the line.
 | 
			
		||||
        while (peek() != '\n' && !isAtEnd())
 | 
			
		||||
          advance();
 | 
			
		||||
      } else {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static TokenType checkKeyword(int start, int length, const char *rest,
 | 
			
		||||
                              TokenType type) {
 | 
			
		||||
  if (scanner.current - scanner.start == start + length &&
 | 
			
		||||
      memcmp(scanner.start + start, rest, length) == 0) {
 | 
			
		||||
    return type;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return TOKEN_IDENTIFIER;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static TokenType identifierType() {
 | 
			
		||||
  switch (scanner.start[0]) {
 | 
			
		||||
  case 'a':
 | 
			
		||||
    return checkKeyword(1, 2, "nd", TOKEN_AND);
 | 
			
		||||
  case 'b':
 | 
			
		||||
    return checkKeyword(1, 4, "reak", TOKEN_BREAK);
 | 
			
		||||
  case 'e':
 | 
			
		||||
    return checkKeyword(1, 3, "lse", TOKEN_ELSE);
 | 
			
		||||
  case 'f':
 | 
			
		||||
    if (scanner.current - scanner.start > 1) {
 | 
			
		||||
      switch (scanner.start[1]) {
 | 
			
		||||
      case 'a':
 | 
			
		||||
        return checkKeyword(2, 3, "lse", TOKEN_FALSE);
 | 
			
		||||
      case 'o':
 | 
			
		||||
        return checkKeyword(2, 1, "r", TOKEN_FOR);
 | 
			
		||||
      }
 | 
			
		||||
      return checkKeyword(1, 1, "n", TOKEN_FN);
 | 
			
		||||
    }
 | 
			
		||||
    break;
 | 
			
		||||
  case 'i':
 | 
			
		||||
    return checkKeyword(1, 1, "f", TOKEN_IF);
 | 
			
		||||
  case 'n':
 | 
			
		||||
    return checkKeyword(1, 2, "il", TOKEN_NIL);
 | 
			
		||||
  case 'o':
 | 
			
		||||
    return checkKeyword(1, 1, "r", TOKEN_OR);
 | 
			
		||||
  case 'p':
 | 
			
		||||
    return checkKeyword(1, 4, "rint", TOKEN_PRINT);
 | 
			
		||||
  case 'r':
 | 
			
		||||
    return checkKeyword(1, 5, "eturn", TOKEN_RETURN);
 | 
			
		||||
  case 's':
 | 
			
		||||
    return checkKeyword(1, 4, "uper", TOKEN_SUPER);
 | 
			
		||||
  case 't':
 | 
			
		||||
    if (scanner.current - scanner.start > 1) {
 | 
			
		||||
      switch (scanner.start[1]) {
 | 
			
		||||
      case 'h':
 | 
			
		||||
        return checkKeyword(2, 2, "is", TOKEN_THIS);
 | 
			
		||||
      case 'r':
 | 
			
		||||
        return checkKeyword(2, 2, "ue", TOKEN_TRUE);
 | 
			
		||||
      case 'y':
 | 
			
		||||
        return checkKeyword(2, 2, "pe", TOKEN_TYPE);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    break;
 | 
			
		||||
  case 'l':
 | 
			
		||||
    return checkKeyword(1, 2, "et", TOKEN_LET);
 | 
			
		||||
  case 'w':
 | 
			
		||||
    return checkKeyword(1, 4, "hile", TOKEN_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_NUMBER);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Token string() {
 | 
			
		||||
  while (peek() != '"' && !isAtEnd()) {
 | 
			
		||||
    if (peek() == '\n')
 | 
			
		||||
      scanner.line++;
 | 
			
		||||
    advance();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (isAtEnd())
 | 
			
		||||
    return errorToken("Unterminated string.");
 | 
			
		||||
 | 
			
		||||
  // The closing quote.
 | 
			
		||||
  advance();
 | 
			
		||||
  return makeToken(TOKEN_STRING);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Token scanToken() {
 | 
			
		||||
  skipWhitespace();
 | 
			
		||||
  scanner.start = scanner.current;
 | 
			
		||||
 | 
			
		||||
  if (isAtEnd())
 | 
			
		||||
    return makeToken(TOKEN_EOF);
 | 
			
		||||
 | 
			
		||||
  char c = advance();
 | 
			
		||||
  if (isAlpha(c))
 | 
			
		||||
    return identifier();
 | 
			
		||||
  if (isDigit(c))
 | 
			
		||||
    return number();
 | 
			
		||||
 | 
			
		||||
  switch (c) {
 | 
			
		||||
  case '(':
 | 
			
		||||
    return makeToken(TOKEN_LEFT_PAREN);
 | 
			
		||||
  case ')':
 | 
			
		||||
    return makeToken(TOKEN_RIGHT_PAREN);
 | 
			
		||||
  case '{':
 | 
			
		||||
    return makeToken(TOKEN_LEFT_BRACE);
 | 
			
		||||
  case '}':
 | 
			
		||||
    return makeToken(TOKEN_RIGHT_BRACE);
 | 
			
		||||
  case '[':
 | 
			
		||||
    return makeToken(TOKEN_LEFT_BRACKET);
 | 
			
		||||
  case ']':
 | 
			
		||||
    return makeToken(TOKEN_RIGHT_BRACKET);
 | 
			
		||||
  case ';':
 | 
			
		||||
    return makeToken(TOKEN_SEMICOLON);
 | 
			
		||||
  case ':':
 | 
			
		||||
    return makeToken(TOKEN_COLON);
 | 
			
		||||
  case '#':
 | 
			
		||||
    return makeToken(TOKEN_MESH);
 | 
			
		||||
  case '$':
 | 
			
		||||
    return makeToken(TOKEN_DOLLAR);
 | 
			
		||||
  case '%':
 | 
			
		||||
    return makeToken(TOKEN_PERCENT);
 | 
			
		||||
  case '&':
 | 
			
		||||
    return makeToken(TOKEN_AMPERSAND);
 | 
			
		||||
  case '@':
 | 
			
		||||
    return makeToken(TOKEN_AT);
 | 
			
		||||
  case ',':
 | 
			
		||||
    return makeToken(TOKEN_COMMA);
 | 
			
		||||
  case '.':
 | 
			
		||||
    return makeToken(TOKEN_DOT);
 | 
			
		||||
  case '-':
 | 
			
		||||
    return makeToken(TOKEN_MINUS);
 | 
			
		||||
  case '+':
 | 
			
		||||
    return makeToken(TOKEN_PLUS);
 | 
			
		||||
  case '/':
 | 
			
		||||
    return makeToken(TOKEN_SLASH);
 | 
			
		||||
  case '*':
 | 
			
		||||
    return makeToken(TOKEN_STAR);
 | 
			
		||||
  case '!':
 | 
			
		||||
    return makeToken(match('=') ? TOKEN_BANG_EQUAL : TOKEN_BANG);
 | 
			
		||||
  case '=':
 | 
			
		||||
    return makeToken(match('=') ? TOKEN_EQUAL_EQUAL : TOKEN_EQUAL);
 | 
			
		||||
  case '<':
 | 
			
		||||
    return makeToken(match('=') ? TOKEN_LESS_EQUAL : TOKEN_LESS);
 | 
			
		||||
  case '>':
 | 
			
		||||
    return makeToken(match('=') ? TOKEN_GREATER_EQUAL : TOKEN_GREATER);
 | 
			
		||||
  case '"':
 | 
			
		||||
    return string();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return errorToken("Unexpected character.");
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,71 +0,0 @@
 | 
			
		|||
#ifndef zlc_scanner_h
 | 
			
		||||
#define zlc_scanner_h
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
  // End of file
 | 
			
		||||
  TOKEN_EOF,
 | 
			
		||||
  // Single-character tokens.
 | 
			
		||||
  TOKEN_LEFT_PAREN,
 | 
			
		||||
  TOKEN_RIGHT_PAREN,
 | 
			
		||||
  TOKEN_LEFT_BRACE,
 | 
			
		||||
  TOKEN_RIGHT_BRACE,
 | 
			
		||||
  TOKEN_LEFT_BRACKET,
 | 
			
		||||
  TOKEN_RIGHT_BRACKET,
 | 
			
		||||
  TOKEN_COMMA,
 | 
			
		||||
  TOKEN_DOT,
 | 
			
		||||
  TOKEN_MINUS,
 | 
			
		||||
  TOKEN_PLUS,
 | 
			
		||||
  TOKEN_COLON,
 | 
			
		||||
  TOKEN_SEMICOLON,
 | 
			
		||||
  TOKEN_SLASH,
 | 
			
		||||
  TOKEN_STAR,
 | 
			
		||||
  TOKEN_MESH,
 | 
			
		||||
  TOKEN_DOLLAR,
 | 
			
		||||
  TOKEN_AT,
 | 
			
		||||
  TOKEN_AMPERSAND,
 | 
			
		||||
  TOKEN_PERCENT,
 | 
			
		||||
  // One or two character tokens.
 | 
			
		||||
  TOKEN_BANG,
 | 
			
		||||
  TOKEN_BANG_EQUAL,
 | 
			
		||||
  TOKEN_EQUAL,
 | 
			
		||||
  TOKEN_EQUAL_EQUAL,
 | 
			
		||||
  TOKEN_GREATER,
 | 
			
		||||
  TOKEN_GREATER_EQUAL,
 | 
			
		||||
  TOKEN_LESS,
 | 
			
		||||
  TOKEN_LESS_EQUAL,
 | 
			
		||||
  // Literals.
 | 
			
		||||
  TOKEN_IDENTIFIER,
 | 
			
		||||
  TOKEN_STRING,
 | 
			
		||||
  TOKEN_NUMBER,
 | 
			
		||||
  // Keywords.
 | 
			
		||||
  TOKEN_AND,
 | 
			
		||||
  TOKEN_TYPE,
 | 
			
		||||
  TOKEN_ELSE,
 | 
			
		||||
  TOKEN_FALSE,
 | 
			
		||||
  TOKEN_FOR,
 | 
			
		||||
  TOKEN_FN,
 | 
			
		||||
  TOKEN_IF,
 | 
			
		||||
  TOKEN_NIL,
 | 
			
		||||
  TOKEN_OR,
 | 
			
		||||
  TOKEN_PRINT,
 | 
			
		||||
  TOKEN_RETURN,
 | 
			
		||||
  TOKEN_SUPER,
 | 
			
		||||
  TOKEN_THIS,
 | 
			
		||||
  TOKEN_TRUE,
 | 
			
		||||
  TOKEN_LET,
 | 
			
		||||
  TOKEN_WHILE,
 | 
			
		||||
  TOKEN_BREAK,
 | 
			
		||||
  TOKEN_ERROR,
 | 
			
		||||
} TokenType;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  TokenType type;
 | 
			
		||||
  const char *start;
 | 
			
		||||
  int length;
 | 
			
		||||
  int line;
 | 
			
		||||
} Token;
 | 
			
		||||
 | 
			
		||||
void initScanner(const char *source);
 | 
			
		||||
Token scanToken();
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										151
									
								
								src/table.c
								
								
								
								
							
							
						
						
									
										151
									
								
								src/table.c
								
								
								
								
							| 
						 | 
				
			
			@ -1,151 +0,0 @@
 | 
			
		|||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "memory.h"
 | 
			
		||||
#include "object.h"
 | 
			
		||||
#include "table.h"
 | 
			
		||||
#include "value.h"
 | 
			
		||||
 | 
			
		||||
#define TABLE_MAX_LOAD 0.75
 | 
			
		||||
 | 
			
		||||
void initTable(Table* table) {
 | 
			
		||||
  table->count = 0;
 | 
			
		||||
  table->capacity = 0;
 | 
			
		||||
  table->entries = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void freeTable(Table* table) {
 | 
			
		||||
  FREE_ARRAY(Entry, table->entries, table->capacity);
 | 
			
		||||
  initTable(table);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Entry* findEntry(Entry* entries, int capacity,
 | 
			
		||||
                        ObjString* key) {
 | 
			
		||||
  uint32_t index = key->hash % capacity;
 | 
			
		||||
  Entry* tombstone = NULL;
 | 
			
		||||
  for (;;) {
 | 
			
		||||
    Entry* entry = &entries[index];
 | 
			
		||||
    if (entry->key == NULL) {
 | 
			
		||||
      if (IS_NIL(entry->value)) {
 | 
			
		||||
        // Empty entry.
 | 
			
		||||
        return tombstone != NULL ? tombstone : entry;
 | 
			
		||||
      } else {
 | 
			
		||||
        // We found a tombstone.
 | 
			
		||||
        if (tombstone == NULL) tombstone = entry;
 | 
			
		||||
      }
 | 
			
		||||
    } else if (entry->key == key) {
 | 
			
		||||
      // We found the key.
 | 
			
		||||
      return entry;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    index = (index + 1) % capacity;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool tableGet(Table* table, ObjString* key, Value* value) {
 | 
			
		||||
  if (table->count == 0) return false;
 | 
			
		||||
 | 
			
		||||
  Entry* entry = findEntry(table->entries, table->capacity, key);
 | 
			
		||||
  if (entry->key == NULL) return false;
 | 
			
		||||
 | 
			
		||||
  *value = entry->value;
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void adjustCapacity(Table* table, int capacity) {
 | 
			
		||||
  Entry* entries = ALLOCATE(Entry, capacity);
 | 
			
		||||
  for (int i = 0; i < capacity; i++) {
 | 
			
		||||
    entries[i].key = NULL;
 | 
			
		||||
    entries[i].value = NIL_VAL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  table->count = 0;
 | 
			
		||||
  for (int i = 0; i < table->capacity; i++) {
 | 
			
		||||
    Entry* entry = &table->entries[i];
 | 
			
		||||
    if (entry->key == NULL) continue;
 | 
			
		||||
 | 
			
		||||
    Entry* dest = findEntry(entries, capacity, entry->key);
 | 
			
		||||
    dest->key = entry->key;
 | 
			
		||||
    dest->value = entry->value;
 | 
			
		||||
    table->count++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FREE_ARRAY(Entry, table->entries, table->capacity);
 | 
			
		||||
  table->entries = entries;
 | 
			
		||||
  table->capacity = capacity;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool tableSet(Table* table, ObjString* key, Value value) {
 | 
			
		||||
  if (table->count + 1 > table->capacity * TABLE_MAX_LOAD) {
 | 
			
		||||
    int capacity = GROW_CAPACITY(table->capacity);
 | 
			
		||||
    adjustCapacity(table, capacity);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Entry* entry = findEntry(table->entries, table->capacity, key);
 | 
			
		||||
  bool isNewKey = entry->key == NULL;
 | 
			
		||||
  if (isNewKey && IS_NIL(entry->value)) table->count++;
 | 
			
		||||
 | 
			
		||||
  entry->key = key;
 | 
			
		||||
  entry->value = value;
 | 
			
		||||
  return isNewKey;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool tableDelete(Table* table, ObjString* key) {
 | 
			
		||||
  if (table->count == 0) return false;
 | 
			
		||||
 | 
			
		||||
  // Find the entry.
 | 
			
		||||
  Entry* entry = findEntry(table->entries, table->capacity, key);
 | 
			
		||||
  if (entry->key == NULL) return false;
 | 
			
		||||
 | 
			
		||||
  // Place a tombstone in the entry.
 | 
			
		||||
  entry->key = NULL;
 | 
			
		||||
  entry->value = BOOL_VAL(true);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void tableAddAll(Table* from, Table* to) {
 | 
			
		||||
  for (int i = 0; i < from->capacity; i++) {
 | 
			
		||||
    Entry* entry = &from->entries[i];
 | 
			
		||||
    if (entry->key != NULL) {
 | 
			
		||||
      tableSet(to, entry->key, entry->value);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ObjString* tableFindString(Table* table, const char* chars,
 | 
			
		||||
                           int length, uint32_t hash) {
 | 
			
		||||
  if (table->count == 0) return NULL;
 | 
			
		||||
 | 
			
		||||
  uint32_t index = hash % table->capacity;
 | 
			
		||||
  for (;;) {
 | 
			
		||||
    Entry* entry = &table->entries[index];
 | 
			
		||||
    if (entry->key == NULL) {
 | 
			
		||||
      // Stop if we find an empty non-tombstone entry.
 | 
			
		||||
      if (IS_NIL(entry->value)) return NULL;
 | 
			
		||||
    } else if (entry->key->length == length &&
 | 
			
		||||
        entry->key->hash == hash &&
 | 
			
		||||
        memcmp(entry->key->chars, chars, length) == 0) {
 | 
			
		||||
      // We found it.
 | 
			
		||||
      return entry->key;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    index = (index + 1) % table->capacity;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void tableRemoveWhite(Table* table) {
 | 
			
		||||
  for (int i = 0; i < table->capacity; i++) {
 | 
			
		||||
    Entry* entry = &table->entries[i];
 | 
			
		||||
    if (entry->key != NULL && !entry->key->obj.isMarked) {
 | 
			
		||||
      tableDelete(table, entry->key);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void markTable(Table* table) {
 | 
			
		||||
  for (int i = 0; i < table->capacity; i++) {
 | 
			
		||||
    Entry* entry = &table->entries[i];
 | 
			
		||||
    markObject((Obj*)entry->key);
 | 
			
		||||
    markValue(entry->value);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										29
									
								
								src/table.h
								
								
								
								
							
							
						
						
									
										29
									
								
								src/table.h
								
								
								
								
							| 
						 | 
				
			
			@ -1,29 +0,0 @@
 | 
			
		|||
#ifndef zlc_table_h
 | 
			
		||||
#define zlc_table_h
 | 
			
		||||
 | 
			
		||||
#include "common.h"
 | 
			
		||||
#include "value.h"
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  ObjString* key;
 | 
			
		||||
  Value value;
 | 
			
		||||
} Entry;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  int count;
 | 
			
		||||
  int capacity;
 | 
			
		||||
  Entry* entries;
 | 
			
		||||
} Table;
 | 
			
		||||
 | 
			
		||||
void initTable(Table* table);
 | 
			
		||||
void freeTable(Table* table);
 | 
			
		||||
bool tableGet(Table* table, ObjString* key, Value* value);
 | 
			
		||||
bool tableSet(Table* table, ObjString* key, Value value);
 | 
			
		||||
bool tableDelete(Table* table, ObjString* key);
 | 
			
		||||
void tableAddAll(Table* from, Table* to);
 | 
			
		||||
ObjString* tableFindString(Table* table, const char* chars,
 | 
			
		||||
                           int length, uint32_t hash);
 | 
			
		||||
void tableRemoveWhite(Table* table);
 | 
			
		||||
void markTable(Table* table);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										51
									
								
								src/value.c
								
								
								
								
							
							
						
						
									
										51
									
								
								src/value.c
								
								
								
								
							| 
						 | 
				
			
			@ -1,51 +0,0 @@
 | 
			
		|||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "object.h"
 | 
			
		||||
#include "memory.h"
 | 
			
		||||
#include "value.h"
 | 
			
		||||
 | 
			
		||||
void initValueArray(ValueArray* array) {
 | 
			
		||||
  array->values = NULL;
 | 
			
		||||
  array->capacity = 0;
 | 
			
		||||
  array->count = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void writeValueArray(ValueArray* array, Value value) {
 | 
			
		||||
  if (array->capacity < array->count + 1) {
 | 
			
		||||
    int oldCapacity = array->capacity;
 | 
			
		||||
    array->capacity = GROW_CAPACITY(oldCapacity);
 | 
			
		||||
    array->values = GROW_ARRAY(Value, array->values,
 | 
			
		||||
                               oldCapacity, array->capacity);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  array->values[array->count] = value;
 | 
			
		||||
  array->count++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void freeValueArray(ValueArray* array) {
 | 
			
		||||
  FREE_ARRAY(Value, array->values, array->capacity);
 | 
			
		||||
  initValueArray(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void printValue(Value value) {
 | 
			
		||||
  switch (value.type) {
 | 
			
		||||
    case VAL_BOOL:
 | 
			
		||||
      printf(AS_BOOL(value) ? "true" : "false");
 | 
			
		||||
      break;
 | 
			
		||||
    case VAL_NIL: printf("nil"); break;
 | 
			
		||||
    case VAL_NUMBER: printf("%g", AS_NUMBER(value)); break;
 | 
			
		||||
    case VAL_OBJ: printObject(value); break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool valuesEqual(Value a, Value b) {
 | 
			
		||||
  if (a.type != b.type) return false;
 | 
			
		||||
  switch (a.type) {
 | 
			
		||||
    case VAL_BOOL:   return AS_BOOL(a) == AS_BOOL(b);
 | 
			
		||||
    case VAL_NIL:    return true;
 | 
			
		||||
    case VAL_NUMBER: return AS_NUMBER(a) == AS_NUMBER(b);
 | 
			
		||||
    case VAL_OBJ:    return AS_OBJ(a) == AS_OBJ(b);
 | 
			
		||||
    default:         return false; // Unreachable.
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										52
									
								
								src/value.h
								
								
								
								
							
							
						
						
									
										52
									
								
								src/value.h
								
								
								
								
							| 
						 | 
				
			
			@ -1,52 +0,0 @@
 | 
			
		|||
#ifndef zlc_value_h
 | 
			
		||||
#define zlc_value_h
 | 
			
		||||
 | 
			
		||||
#include "common.h"
 | 
			
		||||
 | 
			
		||||
typedef struct Obj Obj;
 | 
			
		||||
typedef struct ObjString ObjString;
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
  VAL_BOOL,
 | 
			
		||||
  VAL_NIL, 
 | 
			
		||||
  VAL_NUMBER,
 | 
			
		||||
  VAL_OBJ,
 | 
			
		||||
} ValueType;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  ValueType type;
 | 
			
		||||
  union {
 | 
			
		||||
    bool boolean;
 | 
			
		||||
    double number;
 | 
			
		||||
    Obj* obj;
 | 
			
		||||
  } as; 
 | 
			
		||||
} Value;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define IS_BOOL(value)    ((value).type == VAL_BOOL)
 | 
			
		||||
#define IS_NIL(value)     ((value).type == VAL_NIL)
 | 
			
		||||
#define IS_NUMBER(value)  ((value).type == VAL_NUMBER)
 | 
			
		||||
#define IS_OBJ(value)     ((value).type == VAL_OBJ)
 | 
			
		||||
 | 
			
		||||
#define AS_BOOL(value)    ((value).as.boolean)
 | 
			
		||||
#define AS_NUMBER(value)  ((value).as.number)
 | 
			
		||||
#define AS_OBJ(value)     ((value).as.obj)
 | 
			
		||||
 | 
			
		||||
#define BOOL_VAL(value)   ((Value){VAL_BOOL, {.boolean = value}})
 | 
			
		||||
#define NIL_VAL           ((Value){VAL_NIL, {.number = 0}})
 | 
			
		||||
#define NUMBER_VAL(value) ((Value){VAL_NUMBER, {.number = value}})
 | 
			
		||||
#define OBJ_VAL(object)   ((Value){VAL_OBJ, {.obj = (Obj*)object}})
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  int capacity;
 | 
			
		||||
  int count;
 | 
			
		||||
  Value* values;
 | 
			
		||||
} ValueArray;
 | 
			
		||||
 | 
			
		||||
bool valuesEqual(Value a, Value b);
 | 
			
		||||
void initValueArray(ValueArray* array);
 | 
			
		||||
void writeValueArray(ValueArray* array, Value value);
 | 
			
		||||
void freeValueArray(ValueArray* array);
 | 
			
		||||
void printValue(Value value);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										516
									
								
								src/vm.c
								
								
								
								
							
							
						
						
									
										516
									
								
								src/vm.c
								
								
								
								
							| 
						 | 
				
			
			@ -1,516 +0,0 @@
 | 
			
		|||
#include "vm.h"
 | 
			
		||||
#include "common.h"
 | 
			
		||||
#include "compiler.h"
 | 
			
		||||
#include "debug.h"
 | 
			
		||||
#include "memory.h"
 | 
			
		||||
#include "object.h"
 | 
			
		||||
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
VM vm;
 | 
			
		||||
 | 
			
		||||
static Value clockNative(int argCount, Value *args) {
 | 
			
		||||
  return NUMBER_VAL((double)clock() / CLOCKS_PER_SEC);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void resetStack() {
 | 
			
		||||
  vm.stackTop = vm.stack;
 | 
			
		||||
  vm.frameCount = 0;
 | 
			
		||||
  vm.openUpvalues = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void runtimeError(const char *format, ...) {
 | 
			
		||||
  va_list args;
 | 
			
		||||
  va_start(args, format);
 | 
			
		||||
  vfprintf(stderr, format, args);
 | 
			
		||||
  va_end(args);
 | 
			
		||||
  fputs("\n", stderr);
 | 
			
		||||
 | 
			
		||||
  for (int i = vm.frameCount - 1; i >= 0; i--) {
 | 
			
		||||
    CallFrame *frame = &vm.frames[i];
 | 
			
		||||
    ObjFunction *function = frame->closure->function;
 | 
			
		||||
    size_t instruction = frame->ip - function->chunk.code - 1;
 | 
			
		||||
    fprintf(stderr, "[line %d] in ", function->chunk.lines[instruction]);
 | 
			
		||||
    if (function->name == NULL) {
 | 
			
		||||
      fprintf(stderr, "script\n");
 | 
			
		||||
    } else {
 | 
			
		||||
      fprintf(stderr, "%s()\n", function->name->chars);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  resetStack();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void defineNative(const char *name, NativeFn function) {
 | 
			
		||||
  push(OBJ_VAL(copyString(name, (int)strlen(name))));
 | 
			
		||||
  push(OBJ_VAL(newNative(function)));
 | 
			
		||||
  tableSet(&vm.globals, AS_STRING(vm.stack[0]), vm.stack[1]);
 | 
			
		||||
  pop();
 | 
			
		||||
  pop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void push(Value value) {
 | 
			
		||||
  *vm.stackTop = value;
 | 
			
		||||
  vm.stackTop++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Value pop() {
 | 
			
		||||
  vm.stackTop--;
 | 
			
		||||
  return *vm.stackTop;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Value peek(int distance) { return vm.stackTop[-1 - distance]; }
 | 
			
		||||
 | 
			
		||||
static bool call(ObjClosure *closure, int argCount) {
 | 
			
		||||
  if (argCount != closure->function->arity) {
 | 
			
		||||
    runtimeError("Expected %d arguments but got %d.", closure->function->arity,
 | 
			
		||||
                 argCount);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  if (vm.frameCount == FRAMES_MAX) {
 | 
			
		||||
    runtimeError("Stack overflow.");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  CallFrame *frame = &vm.frames[vm.frameCount++];
 | 
			
		||||
  frame->closure = closure;
 | 
			
		||||
  frame->ip = closure->function->chunk.code;
 | 
			
		||||
  frame->slots = vm.stackTop - argCount - 1;
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool callValue(Value callee, int argCount) {
 | 
			
		||||
  if (IS_OBJ(callee)) {
 | 
			
		||||
    switch (OBJ_TYPE(callee)) {
 | 
			
		||||
    case OBJ_BOUND_METHOD: {
 | 
			
		||||
      ObjBoundMethod *bound = AS_BOUND_METHOD(callee);
 | 
			
		||||
      vm.stackTop[-argCount - 1] = bound->receiver;
 | 
			
		||||
      return call(bound->method, argCount);
 | 
			
		||||
    }
 | 
			
		||||
    case OBJ_TYPE: {
 | 
			
		||||
      TypeObj *t = AS_TYPE(callee);
 | 
			
		||||
      vm.stackTop[-argCount - 1] = OBJ_VAL(newInstance(t));
 | 
			
		||||
      Value initializer;
 | 
			
		||||
      if (tableGet(&t->methods, vm.initString, &initializer)) {
 | 
			
		||||
        return call(AS_CLOSURE(initializer), argCount);
 | 
			
		||||
      } else if (argCount != 0) {
 | 
			
		||||
        runtimeError("Expected 0 arguments but got %d.", argCount);
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    case OBJ_CLOSURE:
 | 
			
		||||
      return call(AS_CLOSURE(callee), argCount);
 | 
			
		||||
    case OBJ_NATIVE: {
 | 
			
		||||
      NativeFn native = AS_NATIVE(callee);
 | 
			
		||||
      Value result = native(argCount, vm.stackTop - argCount);
 | 
			
		||||
      vm.stackTop -= argCount + 1;
 | 
			
		||||
      push(result);
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
      break; // Non-callable object type.
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  runtimeError("Can only call functions and typees.");
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool invokeFromType(TypeObj *t, ObjString *name, int argCount) {
 | 
			
		||||
  Value method;
 | 
			
		||||
  if (!tableGet(&t->methods, name, &method)) {
 | 
			
		||||
    runtimeError("Undefined property '%s'.", name->chars);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  return call(AS_CLOSURE(method), argCount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool invoke(ObjString *name, int argCount) {
 | 
			
		||||
  Value receiver = peek(argCount);
 | 
			
		||||
  if (!IS_INSTANCE(receiver)) {
 | 
			
		||||
    runtimeError("Only instances have methods.");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ObjInstance *instance = AS_INSTANCE(receiver);
 | 
			
		||||
  Value value;
 | 
			
		||||
  if (tableGet(&instance->fields, name, &value)) {
 | 
			
		||||
    vm.stackTop[-argCount - 1] = value;
 | 
			
		||||
    return callValue(value, argCount);
 | 
			
		||||
  }
 | 
			
		||||
  return invokeFromType(instance->t, name, argCount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool bindMethod(TypeObj *t, ObjString *name) {
 | 
			
		||||
  Value method;
 | 
			
		||||
  if (!tableGet(&t->methods, name, &method)) {
 | 
			
		||||
    runtimeError("Undefined property '%s'.", name->chars);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ObjBoundMethod *bound = newBoundMethod(peek(0), AS_CLOSURE(method));
 | 
			
		||||
  pop();
 | 
			
		||||
  push(OBJ_VAL(bound));
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ObjUpvalue *captureUpvalue(Value *local) {
 | 
			
		||||
  ObjUpvalue *prevUpvalue = NULL;
 | 
			
		||||
  ObjUpvalue *upvalue = vm.openUpvalues;
 | 
			
		||||
  while (upvalue != NULL && upvalue->location > local) {
 | 
			
		||||
    prevUpvalue = upvalue;
 | 
			
		||||
    upvalue = upvalue->next;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (upvalue != NULL && upvalue->location == local) {
 | 
			
		||||
    return upvalue;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ObjUpvalue *createdUpvalue = newUpvalue(local);
 | 
			
		||||
  createdUpvalue->next = upvalue;
 | 
			
		||||
 | 
			
		||||
  if (prevUpvalue == NULL) {
 | 
			
		||||
    vm.openUpvalues = createdUpvalue;
 | 
			
		||||
  } else {
 | 
			
		||||
    prevUpvalue->next = createdUpvalue;
 | 
			
		||||
  }
 | 
			
		||||
  return createdUpvalue;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void closeUpvalues(Value *last) {
 | 
			
		||||
  while (vm.openUpvalues != NULL && vm.openUpvalues->location >= last) {
 | 
			
		||||
    ObjUpvalue *upvalue = vm.openUpvalues;
 | 
			
		||||
    upvalue->closed = *upvalue->location;
 | 
			
		||||
    upvalue->location = &upvalue->closed;
 | 
			
		||||
    vm.openUpvalues = upvalue->next;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void defineMethod(ObjString *name) {
 | 
			
		||||
  Value method = peek(0);
 | 
			
		||||
  TypeObj *t = AS_TYPE(peek(1));
 | 
			
		||||
  tableSet(&t->methods, name, method);
 | 
			
		||||
  pop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool isFalsey(Value value) {
 | 
			
		||||
  return IS_NIL(value) || (IS_BOOL(value) && !AS_BOOL(value));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void concatenate() {
 | 
			
		||||
  ObjString *b = AS_STRING(peek(0));
 | 
			
		||||
  ObjString *a = AS_STRING(peek(1));
 | 
			
		||||
 | 
			
		||||
  int length = a->length + b->length;
 | 
			
		||||
  char *chars = ALLOCATE(char, length + 1);
 | 
			
		||||
  memcpy(chars, a->chars, a->length);
 | 
			
		||||
  memcpy(chars + a->length, b->chars, b->length);
 | 
			
		||||
  chars[length] = '\0';
 | 
			
		||||
 | 
			
		||||
  ObjString *result = takeString(chars, length);
 | 
			
		||||
  pop();
 | 
			
		||||
  pop();
 | 
			
		||||
  push(OBJ_VAL(result));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void initVM() {
 | 
			
		||||
  resetStack();
 | 
			
		||||
  vm.objects = NULL;
 | 
			
		||||
  vm.bytesAllocated = 0;
 | 
			
		||||
  vm.nextGC = 1024 * 1024;
 | 
			
		||||
  vm.grayCount = 0;
 | 
			
		||||
  vm.grayCapacity = 0;
 | 
			
		||||
  vm.grayStack = NULL;
 | 
			
		||||
 | 
			
		||||
  initTable(&vm.globals);
 | 
			
		||||
  initTable(&vm.strings);
 | 
			
		||||
 | 
			
		||||
  vm.initString = NULL;
 | 
			
		||||
  vm.initString = copyString("init", 4);
 | 
			
		||||
 | 
			
		||||
  defineNative("clock", clockNative);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void freeVM() {
 | 
			
		||||
  freeTable(&vm.globals);
 | 
			
		||||
  freeTable(&vm.strings);
 | 
			
		||||
  vm.initString = NULL;
 | 
			
		||||
  freeObjects();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static InterpretResult run() {
 | 
			
		||||
  CallFrame *frame = &vm.frames[vm.frameCount - 1];
 | 
			
		||||
 | 
			
		||||
#define READ_BYTE() (*frame->ip++)
 | 
			
		||||
#define READ_SHORT()                                                           \
 | 
			
		||||
  (frame->ip += 2, (uint16_t)((frame->ip[-2] << 8) | frame->ip[-1]))
 | 
			
		||||
#define READ_CONSTANT()                                                        \
 | 
			
		||||
  (frame->closure->function->chunk.constants.values[READ_BYTE()])
 | 
			
		||||
#define READ_STRING() AS_STRING(READ_CONSTANT())
 | 
			
		||||
#define BINARY_OP(valueType, op)                                               \
 | 
			
		||||
  do {                                                                         \
 | 
			
		||||
    if (!IS_NUMBER(peek(0)) || !IS_NUMBER(peek(1))) {                          \
 | 
			
		||||
      runtimeError("Operands must be numbers.");                               \
 | 
			
		||||
      return INTERPRET_RUNTIME_ERROR;                                          \
 | 
			
		||||
    }                                                                          \
 | 
			
		||||
    double b = AS_NUMBER(pop());                                               \
 | 
			
		||||
    double a = AS_NUMBER(pop());                                               \
 | 
			
		||||
    push(valueType(a op b));                                                   \
 | 
			
		||||
  } while (false)
 | 
			
		||||
 | 
			
		||||
  for (;;) {
 | 
			
		||||
#ifdef DEBUG_TRACE_EXECUTION
 | 
			
		||||
    printf("          ");
 | 
			
		||||
    for (Value *slot = vm.stack; slot < vm.stackTop; slot++) {
 | 
			
		||||
      printf("[ ");
 | 
			
		||||
      printValue(*slot);
 | 
			
		||||
      printf(" ]");
 | 
			
		||||
    }
 | 
			
		||||
    printf("\n");
 | 
			
		||||
    disassembleInstruction(
 | 
			
		||||
        &frame->closure->function->chunk,
 | 
			
		||||
        (int)(frame->ip - frame->closure->function->chunk.code));
 | 
			
		||||
#endif
 | 
			
		||||
    uint8_t instruction;
 | 
			
		||||
    switch (instruction = READ_BYTE()) {
 | 
			
		||||
    case OP_CONSTANT: {
 | 
			
		||||
      Value constant = READ_CONSTANT();
 | 
			
		||||
      push(constant);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case OP_NIL:
 | 
			
		||||
      push(NIL_VAL);
 | 
			
		||||
      break;
 | 
			
		||||
    case OP_TRUE:
 | 
			
		||||
      push(BOOL_VAL(true));
 | 
			
		||||
      break;
 | 
			
		||||
    case OP_FALSE:
 | 
			
		||||
      push(BOOL_VAL(false));
 | 
			
		||||
      break;
 | 
			
		||||
    case OP_POP:
 | 
			
		||||
      pop();
 | 
			
		||||
      break;
 | 
			
		||||
    case OP_GET_LOCAL: {
 | 
			
		||||
      uint8_t slot = READ_BYTE();
 | 
			
		||||
      push(frame->slots[slot]);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case OP_SET_LOCAL: {
 | 
			
		||||
      uint8_t slot = READ_BYTE();
 | 
			
		||||
      vm.stack[slot] = peek(0);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case OP_GET_GLOBAL: {
 | 
			
		||||
      ObjString *name = READ_STRING();
 | 
			
		||||
      Value value;
 | 
			
		||||
      if (!tableGet(&vm.globals, name, &value)) {
 | 
			
		||||
        runtimeError("Undefined variable '%s'.", name->chars);
 | 
			
		||||
        return INTERPRET_RUNTIME_ERROR;
 | 
			
		||||
      }
 | 
			
		||||
      push(value);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case OP_DEFINE_GLOBAL: {
 | 
			
		||||
      ObjString *name = READ_STRING();
 | 
			
		||||
      tableSet(&vm.globals, name, peek(0));
 | 
			
		||||
      pop();
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case OP_SET_GLOBAL: {
 | 
			
		||||
      ObjString *name = READ_STRING();
 | 
			
		||||
      if (tableSet(&vm.globals, name, peek(0))) {
 | 
			
		||||
        tableDelete(&vm.globals, name);
 | 
			
		||||
        runtimeError("Undefined variable '%s'.", name->chars);
 | 
			
		||||
        return INTERPRET_RUNTIME_ERROR;
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case OP_GET_UPVALUE: {
 | 
			
		||||
      uint8_t slot = READ_BYTE();
 | 
			
		||||
      push(*frame->closure->upvalues[slot]->location);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case OP_SET_UPVALUE: {
 | 
			
		||||
      uint8_t slot = READ_BYTE();
 | 
			
		||||
      *frame->closure->upvalues[slot]->location = peek(0);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case OP_GET_PROPERTY: {
 | 
			
		||||
      if (!IS_INSTANCE(peek(0))) {
 | 
			
		||||
        runtimeError("Only instances have properties.");
 | 
			
		||||
        return INTERPRET_RUNTIME_ERROR;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      ObjInstance *instance = AS_INSTANCE(peek(0));
 | 
			
		||||
      ObjString *name = READ_STRING();
 | 
			
		||||
 | 
			
		||||
      Value value;
 | 
			
		||||
      if (tableGet(&instance->fields, name, &value)) {
 | 
			
		||||
        pop(); // Instance.
 | 
			
		||||
        push(value);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!bindMethod(instance->t, name)) {
 | 
			
		||||
        return INTERPRET_RUNTIME_ERROR;
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case OP_SET_PROPERTY: {
 | 
			
		||||
      if (!IS_INSTANCE(peek(1))) {
 | 
			
		||||
        runtimeError("Only instances have fields.");
 | 
			
		||||
        return INTERPRET_RUNTIME_ERROR;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      ObjInstance *instance = AS_INSTANCE(peek(1));
 | 
			
		||||
      tableSet(&instance->fields, READ_STRING(), peek(0));
 | 
			
		||||
      Value value = pop();
 | 
			
		||||
      pop();
 | 
			
		||||
      push(value);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case OP_EQUAL: {
 | 
			
		||||
      Value b = pop();
 | 
			
		||||
      Value a = pop();
 | 
			
		||||
      push(BOOL_VAL(valuesEqual(a, b)));
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case OP_GREATER:
 | 
			
		||||
      BINARY_OP(BOOL_VAL, >);
 | 
			
		||||
      break;
 | 
			
		||||
    case OP_LESS:
 | 
			
		||||
      BINARY_OP(BOOL_VAL, <);
 | 
			
		||||
      break;
 | 
			
		||||
    case OP_ADD: {
 | 
			
		||||
      if (IS_STRING(peek(0)) && IS_STRING(peek(1))) {
 | 
			
		||||
        concatenate();
 | 
			
		||||
      } else if (IS_NUMBER(peek(0)) && IS_NUMBER(peek(1))) {
 | 
			
		||||
        double b = AS_NUMBER(pop());
 | 
			
		||||
        double a = AS_NUMBER(pop());
 | 
			
		||||
        push(NUMBER_VAL(a + b));
 | 
			
		||||
      } else {
 | 
			
		||||
        runtimeError("Operands must be two numbers or two strings.");
 | 
			
		||||
        return INTERPRET_RUNTIME_ERROR;
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case OP_SUBTRACT:
 | 
			
		||||
      BINARY_OP(NUMBER_VAL, -);
 | 
			
		||||
      break;
 | 
			
		||||
    case OP_MULTIPLY:
 | 
			
		||||
      BINARY_OP(NUMBER_VAL, *);
 | 
			
		||||
      break;
 | 
			
		||||
    case OP_DIVIDE:
 | 
			
		||||
      BINARY_OP(NUMBER_VAL, /);
 | 
			
		||||
      break;
 | 
			
		||||
    case OP_NOT:
 | 
			
		||||
      push(BOOL_VAL(isFalsey(pop())));
 | 
			
		||||
      break;
 | 
			
		||||
    case OP_NEGATE:
 | 
			
		||||
      if (!IS_NUMBER(peek(0))) {
 | 
			
		||||
        runtimeError("Operand must be a number.");
 | 
			
		||||
        return INTERPRET_RUNTIME_ERROR;
 | 
			
		||||
      }
 | 
			
		||||
      push(NUMBER_VAL(-AS_NUMBER(pop())));
 | 
			
		||||
      break;
 | 
			
		||||
    case OP_PRINT: {
 | 
			
		||||
      printValue(pop());
 | 
			
		||||
      printf("\n");
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case OP_JUMP: {
 | 
			
		||||
      uint16_t offset = READ_SHORT();
 | 
			
		||||
      frame->ip += offset;
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case OP_JUMP_IF_FALSE: {
 | 
			
		||||
      uint16_t offset = READ_SHORT();
 | 
			
		||||
      if (isFalsey(peek(0)))
 | 
			
		||||
        frame->ip += offset;
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case OP_LOOP: {
 | 
			
		||||
      uint16_t offset = READ_SHORT();
 | 
			
		||||
      frame->ip -= offset;
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case OP_CALL: {
 | 
			
		||||
      int argCount = READ_BYTE();
 | 
			
		||||
      if (!callValue(peek(argCount), argCount)) {
 | 
			
		||||
        return INTERPRET_RUNTIME_ERROR;
 | 
			
		||||
      }
 | 
			
		||||
      frame = &vm.frames[vm.frameCount - 1];
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case OP_INVOKE: {
 | 
			
		||||
      ObjString *method = READ_STRING();
 | 
			
		||||
      int argCount = READ_BYTE();
 | 
			
		||||
      if (!invoke(method, argCount)) {
 | 
			
		||||
        return INTERPRET_RUNTIME_ERROR;
 | 
			
		||||
      }
 | 
			
		||||
      frame = &vm.frames[vm.frameCount - 1];
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case OP_CLOSURE: {
 | 
			
		||||
      ObjFunction *function = AS_FUNCTION(READ_CONSTANT());
 | 
			
		||||
      ObjClosure *closure = newClosure(function);
 | 
			
		||||
      push(OBJ_VAL(closure));
 | 
			
		||||
      for (int i = 0; i < closure->upvalueCount; i++) {
 | 
			
		||||
        uint8_t isLocal = READ_BYTE();
 | 
			
		||||
        uint8_t index = READ_BYTE();
 | 
			
		||||
        if (isLocal) {
 | 
			
		||||
          closure->upvalues[i] = captureUpvalue(frame->slots + index);
 | 
			
		||||
        } else {
 | 
			
		||||
          closure->upvalues[i] = frame->closure->upvalues[index];
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case OP_CLOSE_UPVALUE:
 | 
			
		||||
      closeUpvalues(vm.stackTop - 1);
 | 
			
		||||
      pop();
 | 
			
		||||
      break;
 | 
			
		||||
    case OP_TYPE:
 | 
			
		||||
      push(OBJ_VAL(newType(READ_STRING())));
 | 
			
		||||
      break;
 | 
			
		||||
    case OP_METHOD:
 | 
			
		||||
      defineMethod(READ_STRING());
 | 
			
		||||
      break;
 | 
			
		||||
    case OP_RETURN:
 | 
			
		||||
      Value result = pop();
 | 
			
		||||
      closeUpvalues(frame->slots);
 | 
			
		||||
      vm.frameCount--;
 | 
			
		||||
      if (vm.frameCount == 0) {
 | 
			
		||||
        pop();
 | 
			
		||||
        return INTERPRET_OK;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      vm.stackTop = frame->slots;
 | 
			
		||||
      push(result);
 | 
			
		||||
      frame = &vm.frames[vm.frameCount - 1];
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#undef READ_BYTE
 | 
			
		||||
#undef READ_SHORT
 | 
			
		||||
#undef READ_CONSTANT
 | 
			
		||||
#undef READ_STRING
 | 
			
		||||
#undef BINARY_OP
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
InterpretResult interpret(const char *source) {
 | 
			
		||||
  ObjFunction *function = compile(source);
 | 
			
		||||
  if (function == NULL)
 | 
			
		||||
    return INTERPRET_COMPILE_ERROR;
 | 
			
		||||
 | 
			
		||||
  push(OBJ_VAL(function));
 | 
			
		||||
  ObjClosure *closure = newClosure(function);
 | 
			
		||||
  pop();
 | 
			
		||||
  push(OBJ_VAL(closure));
 | 
			
		||||
  call(closure, 0);
 | 
			
		||||
 | 
			
		||||
  return run();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										50
									
								
								src/vm.h
								
								
								
								
							
							
						
						
									
										50
									
								
								src/vm.h
								
								
								
								
							| 
						 | 
				
			
			@ -1,50 +0,0 @@
 | 
			
		|||
#ifndef zlc_vm_h
 | 
			
		||||
#define zlc_vm_h
 | 
			
		||||
 | 
			
		||||
#include "object.h"
 | 
			
		||||
#include "table.h"
 | 
			
		||||
#include "value.h"
 | 
			
		||||
 | 
			
		||||
#define FRAMES_MAX 64
 | 
			
		||||
#define STACK_MAX (FRAMES_MAX * UINT8_COUNT)
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  ObjClosure* closure;
 | 
			
		||||
  uint8_t* ip;
 | 
			
		||||
  Value* slots;
 | 
			
		||||
} CallFrame;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  CallFrame frames[FRAMES_MAX];
 | 
			
		||||
  int frameCount;
 | 
			
		||||
 | 
			
		||||
  Value stack[STACK_MAX];
 | 
			
		||||
  Value* stackTop;
 | 
			
		||||
  Table globals;
 | 
			
		||||
  Table strings;
 | 
			
		||||
  ObjString* initString;
 | 
			
		||||
  ObjUpvalue* openUpvalues;
 | 
			
		||||
 | 
			
		||||
  size_t bytesAllocated;
 | 
			
		||||
  size_t nextGC;
 | 
			
		||||
  Obj* objects;
 | 
			
		||||
  int grayCount;
 | 
			
		||||
  int grayCapacity;
 | 
			
		||||
  Obj** grayStack;
 | 
			
		||||
} VM;
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
  INTERPRET_OK,
 | 
			
		||||
  INTERPRET_COMPILE_ERROR,
 | 
			
		||||
  INTERPRET_RUNTIME_ERROR
 | 
			
		||||
} InterpretResult;
 | 
			
		||||
 | 
			
		||||
extern VM vm;
 | 
			
		||||
 | 
			
		||||
void initVM();
 | 
			
		||||
void freeVM();
 | 
			
		||||
InterpretResult interpret(const char* source);
 | 
			
		||||
void push(Value value);
 | 
			
		||||
Value pop();
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
for (let i = 0; i < 10; i = i + 1) {
 | 
			
		||||
 print i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let val = true;
 | 
			
		||||
let j = 0;
 | 
			
		||||
while (val) {
 | 
			
		||||
      j = j + 1;
 | 
			
		||||
      if (j > 9) val = false;
 | 
			
		||||
      print j;
 | 
			
		||||
}
 | 
			
		||||
print "done";
 | 
			
		||||
		Loading…
	
		Reference in New Issue