forked from Mirror/wren
Local variables.
This commit is contained in:
@ -1,2 +1,3 @@
|
|||||||
123.ok
|
var a = 123
|
||||||
123.yar
|
var b = 345
|
||||||
|
a
|
||||||
|
|||||||
115
src/compiler.c
115
src/compiler.c
@ -76,6 +76,9 @@ typedef struct
|
|||||||
Block* block;
|
Block* block;
|
||||||
int numCodes;
|
int numCodes;
|
||||||
|
|
||||||
|
// Symbol table for declared local variables in the current block.
|
||||||
|
SymbolTable locals;
|
||||||
|
|
||||||
// Non-zero if a compile error has occurred.
|
// Non-zero if a compile error has occurred.
|
||||||
int hasError;
|
int hasError;
|
||||||
} Compiler;
|
} Compiler;
|
||||||
@ -105,6 +108,7 @@ static char peekChar(Compiler* compiler);
|
|||||||
static void makeToken(Compiler* compiler, TokenType type);
|
static void makeToken(Compiler* compiler, TokenType type);
|
||||||
|
|
||||||
// Utility:
|
// Utility:
|
||||||
|
static void emit(Compiler* compiler, Code code);
|
||||||
static void error(Compiler* compiler, const char* format, ...);
|
static void error(Compiler* compiler, const char* format, ...);
|
||||||
|
|
||||||
Block* compile(VM* vm, const char* source, size_t sourceLength)
|
Block* compile(VM* vm, const char* source, size_t sourceLength)
|
||||||
@ -118,7 +122,14 @@ Block* compile(VM* vm, const char* source, size_t sourceLength)
|
|||||||
compiler.tokenStart = 0;
|
compiler.tokenStart = 0;
|
||||||
compiler.currentChar = 0;
|
compiler.currentChar = 0;
|
||||||
|
|
||||||
// TODO(bob): Zero-init current token.
|
initSymbolTable(&compiler.locals);
|
||||||
|
|
||||||
|
// Zero-init the current token. This will get copied to previous when
|
||||||
|
// advance() is called below.
|
||||||
|
compiler.current.type = TOKEN_EOF;
|
||||||
|
compiler.current.start = 0;
|
||||||
|
compiler.current.end = 0;
|
||||||
|
|
||||||
// Read the first token.
|
// Read the first token.
|
||||||
advance(&compiler);
|
advance(&compiler);
|
||||||
|
|
||||||
@ -132,37 +143,47 @@ Block* compile(VM* vm, const char* source, size_t sourceLength)
|
|||||||
|
|
||||||
compiler.numCodes = 0;
|
compiler.numCodes = 0;
|
||||||
|
|
||||||
do
|
for (;;)
|
||||||
{
|
{
|
||||||
statement(&compiler);
|
statement(&compiler);
|
||||||
// TODO(bob): Discard previous value.
|
|
||||||
} while (!match(&compiler, TOKEN_EOF));
|
|
||||||
|
|
||||||
compiler.block->bytecode[compiler.numCodes++] = CODE_END;
|
if (match(&compiler, TOKEN_EOF)) break;
|
||||||
|
|
||||||
|
// Discard the result of the previous expression.
|
||||||
|
emit(&compiler, CODE_POP);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(&compiler, CODE_END);
|
||||||
|
|
||||||
|
compiler.block->numLocals = compiler.locals.count;
|
||||||
|
|
||||||
return compiler.hasError ? NULL : compiler.block;
|
return compiler.hasError ? NULL : compiler.block;
|
||||||
}
|
}
|
||||||
|
|
||||||
void statement(Compiler* compiler)
|
void statement(Compiler* compiler)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
if (match(compiler, TOKEN_VAR))
|
if (match(compiler, TOKEN_VAR))
|
||||||
{
|
{
|
||||||
Token* name = consume(compiler, TOKEN_NAME);
|
consume(compiler, TOKEN_NAME);
|
||||||
Node* initializer = NULL;
|
int local = addSymbol(&compiler->locals,
|
||||||
if (match(compiler, TOKEN_EQ))
|
compiler->source + compiler->previous.start,
|
||||||
{
|
compiler->previous.end - compiler->previous.start);
|
||||||
initializer = expression(parser);
|
|
||||||
}
|
|
||||||
if (peek(parser) != TOKEN_OUTDENT) consume(compiler, TOKEN_LINE);
|
|
||||||
|
|
||||||
NodeVar* node = malloc(sizeof(NodeVar));
|
if (local == -1)
|
||||||
node->node.type = NODE_VAR;
|
{
|
||||||
node->name = name;
|
error(compiler, "Local variable is already defined.");
|
||||||
node->initializer = initializer;
|
}
|
||||||
return (Node*)node;
|
|
||||||
|
// TODO(bob): Allow uninitialized vars?
|
||||||
|
consume(compiler, TOKEN_EQ);
|
||||||
|
|
||||||
|
// Compile the initializer.
|
||||||
|
expression(compiler);
|
||||||
|
|
||||||
|
emit(compiler, CODE_STORE_LOCAL);
|
||||||
|
emit(compiler, local);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
// Statement expression.
|
// Statement expression.
|
||||||
expression(compiler);
|
expression(compiler);
|
||||||
@ -186,23 +207,38 @@ void call(Compiler* compiler)
|
|||||||
if (match(compiler, TOKEN_DOT))
|
if (match(compiler, TOKEN_DOT))
|
||||||
{
|
{
|
||||||
consume(compiler, TOKEN_NAME);
|
consume(compiler, TOKEN_NAME);
|
||||||
int symbol = getSymbol(compiler->vm,
|
int symbol = ensureSymbol(&compiler->vm->symbols,
|
||||||
compiler->source + compiler->previous.start,
|
compiler->source + compiler->previous.start,
|
||||||
compiler->previous.end - compiler->previous.start);
|
compiler->previous.end - compiler->previous.start);
|
||||||
printf("symbol %d\n", symbol);
|
|
||||||
|
|
||||||
|
|
||||||
// Compile the method call.
|
// Compile the method call.
|
||||||
compiler->block->bytecode[compiler->numCodes++] = CODE_CALL;
|
emit(compiler, CODE_CALL);
|
||||||
compiler->block->bytecode[compiler->numCodes++] = symbol;
|
emit(compiler, symbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void primary(Compiler* compiler)
|
void primary(Compiler* compiler)
|
||||||
{
|
{
|
||||||
|
if (match(compiler, TOKEN_NAME))
|
||||||
|
{
|
||||||
|
int local = findSymbol(&compiler->locals,
|
||||||
|
compiler->source + compiler->previous.start,
|
||||||
|
compiler->previous.end - compiler->previous.start);
|
||||||
|
if (local == -1)
|
||||||
|
{
|
||||||
|
// TODO(bob): Look for globals or names in outer scopes.
|
||||||
|
error(compiler, "Unknown variable.");
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(compiler, CODE_LOAD_LOCAL);
|
||||||
|
emit(compiler, local);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (match(compiler, TOKEN_NUMBER))
|
if (match(compiler, TOKEN_NUMBER))
|
||||||
{
|
{
|
||||||
number(compiler, &compiler->previous);
|
number(compiler, &compiler->previous);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,18 +255,14 @@ void number(Compiler* compiler, Token* token)
|
|||||||
|
|
||||||
// Define a constant for the literal.
|
// Define a constant for the literal.
|
||||||
// TODO(bob): See if constant with same value already exists.
|
// TODO(bob): See if constant with same value already exists.
|
||||||
Value constant = malloc(sizeof(Obj));
|
|
||||||
constant->type = OBJ_INT;
|
|
||||||
constant->flags = 0;
|
|
||||||
|
|
||||||
// TODO(bob): Handle truncation!
|
// TODO(bob): Handle truncation!
|
||||||
constant->value = (int)value;
|
Value constant = makeNum((int)value);
|
||||||
|
|
||||||
compiler->block->constants[compiler->block->numConstants++] = constant;
|
compiler->block->constants[compiler->block->numConstants++] = constant;
|
||||||
|
|
||||||
// Compile the code to load the constant.
|
// Compile the code to load the constant.
|
||||||
compiler->block->bytecode[compiler->numCodes++] = CODE_CONSTANT;
|
emit(compiler, CODE_CONSTANT);
|
||||||
compiler->block->bytecode[compiler->numCodes++] = compiler->block->numConstants - 1;
|
emit(compiler, compiler->block->numConstants - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
TokenType peek(Compiler* compiler)
|
TokenType peek(Compiler* compiler)
|
||||||
@ -286,7 +318,17 @@ void readNextToken(Compiler* compiler)
|
|||||||
case '/': makeToken(compiler, TOKEN_SLASH); return;
|
case '/': makeToken(compiler, TOKEN_SLASH); return;
|
||||||
case '%': makeToken(compiler, TOKEN_PERCENT); return;
|
case '%': makeToken(compiler, TOKEN_PERCENT); return;
|
||||||
case '+': makeToken(compiler, TOKEN_PLUS); return;
|
case '+': makeToken(compiler, TOKEN_PLUS); return;
|
||||||
case '-': makeToken(compiler, TOKEN_MINUS); return;
|
case '-':
|
||||||
|
if (isDigit(peekChar(compiler)))
|
||||||
|
{
|
||||||
|
readNumber(compiler);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
makeToken(compiler, TOKEN_MINUS);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
case '|': makeToken(compiler, TOKEN_PIPE); return;
|
case '|': makeToken(compiler, TOKEN_PIPE); return;
|
||||||
case '&': makeToken(compiler, TOKEN_AMP); return;
|
case '&': makeToken(compiler, TOKEN_AMP); return;
|
||||||
case '=':
|
case '=':
|
||||||
@ -434,6 +476,11 @@ void makeToken(Compiler* compiler, TokenType type)
|
|||||||
compiler->current.end = compiler->currentChar;
|
compiler->current.end = compiler->currentChar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void emit(Compiler* compiler, Code code)
|
||||||
|
{
|
||||||
|
compiler->block->bytecode[compiler->numCodes++] = code;
|
||||||
|
}
|
||||||
|
|
||||||
void error(Compiler* compiler, const char* format, ...)
|
void error(Compiler* compiler, const char* format, ...)
|
||||||
{
|
{
|
||||||
compiler->hasError = 1;
|
compiler->hasError = 1;
|
||||||
|
|||||||
176
src/vm.c
176
src/vm.c
@ -4,68 +4,194 @@
|
|||||||
|
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// Index of the current (really next-to-be-executed) instruction in the
|
||||||
|
// block's bytecode.
|
||||||
|
int ip;
|
||||||
|
|
||||||
|
// The block being executed.
|
||||||
|
Block* block;
|
||||||
|
|
||||||
|
// Index of the stack slot that contains the first local for this block.
|
||||||
|
int locals;
|
||||||
|
} CallFrame;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
Value stack[STACK_SIZE];
|
Value stack[STACK_SIZE];
|
||||||
int stackSize;
|
int stackSize;
|
||||||
|
|
||||||
|
CallFrame frames[MAX_CALL_FRAMES];
|
||||||
|
int numFrames;
|
||||||
} Fiber;
|
} Fiber;
|
||||||
|
|
||||||
|
static void callBlock(Fiber* fiber, Block* block, int locals);
|
||||||
|
static void push(Fiber* fiber, Value value);
|
||||||
static Value pop(Fiber* fiber);
|
static Value pop(Fiber* fiber);
|
||||||
|
static Value primitiveNumAbs(Value number);
|
||||||
|
|
||||||
VM* newVM()
|
VM* newVM()
|
||||||
{
|
{
|
||||||
VM* vm = malloc(sizeof(VM));
|
VM* vm = malloc(sizeof(VM));
|
||||||
vm->numSymbols = 0;
|
initSymbolTable(&vm->symbols);
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_SYMBOLS; i++)
|
||||||
|
{
|
||||||
|
vm->numClass.methods[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
vm->numClass.methods[ensureSymbol(&vm->symbols, "abs", 3)] = primitiveNumAbs;
|
||||||
|
|
||||||
return vm;
|
return vm;
|
||||||
}
|
}
|
||||||
|
|
||||||
void freeVM(VM* vm)
|
void freeVM(VM* vm)
|
||||||
{
|
{
|
||||||
|
clearSymbolTable(&vm->symbols);
|
||||||
free(vm);
|
free(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
int getSymbol(VM* vm, const char* name, size_t length)
|
Value makeNum(int number)
|
||||||
|
{
|
||||||
|
Value value = malloc(sizeof(Obj));
|
||||||
|
value->type = OBJ_INT;
|
||||||
|
value->flags = 0;
|
||||||
|
value->value = number;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void initSymbolTable(SymbolTable* symbols)
|
||||||
|
{
|
||||||
|
symbols->count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearSymbolTable(SymbolTable* symbols)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < symbols->count; i++)
|
||||||
|
{
|
||||||
|
free(symbols->names[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int addSymbolUnchecked(SymbolTable* symbols, const char* name, size_t length)
|
||||||
|
{
|
||||||
|
symbols->names[symbols->count] = malloc(length + 1);
|
||||||
|
strncpy(symbols->names[symbols->count], name, length);
|
||||||
|
symbols->names[symbols->count][length] = '\0';
|
||||||
|
|
||||||
|
return symbols->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int addSymbol(SymbolTable* symbols, const char* name, size_t length)
|
||||||
|
{
|
||||||
|
// If already present, return an error.
|
||||||
|
if (findSymbol(symbols, name, length) != -1) return -1;
|
||||||
|
|
||||||
|
return addSymbolUnchecked(symbols, name, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ensureSymbol(SymbolTable* symbols, const char* name, size_t length)
|
||||||
|
{
|
||||||
|
// See if the symbol is already defined.
|
||||||
|
int existing = findSymbol(symbols, name, length);
|
||||||
|
if (existing != -1) return existing;
|
||||||
|
|
||||||
|
// New symbol, so add it.
|
||||||
|
return addSymbolUnchecked(symbols, name, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
int findSymbol(SymbolTable* symbols, const char* name, size_t length)
|
||||||
{
|
{
|
||||||
// See if the symbol is already defined.
|
// See if the symbol is already defined.
|
||||||
// TODO(bob): O(n). Do something better.
|
// TODO(bob): O(n). Do something better.
|
||||||
for (int i = 0; i < vm->numSymbols; i++)
|
for (int i = 0; i < symbols->count; i++)
|
||||||
{
|
{
|
||||||
if (strncmp(vm->symbols[i], name, length) == 0) return i;
|
if (strlen(symbols->names[i]) == length &&
|
||||||
|
strncmp(symbols->names[i], name, length) == 0) return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
// New symbol, so add it.
|
return -1;
|
||||||
vm->symbols[vm->numSymbols] = malloc(length);
|
|
||||||
strncpy(vm->symbols[vm->numSymbols], name, length);
|
|
||||||
return vm->numSymbols++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Value interpret(VM* vm, Block* block)
|
Value interpret(VM* vm, Block* block)
|
||||||
{
|
{
|
||||||
Fiber fiber;
|
Fiber fiber;
|
||||||
fiber.stackSize = 0;
|
fiber.stackSize = 0;
|
||||||
|
fiber.numFrames = 0;
|
||||||
|
|
||||||
|
callBlock(&fiber, block, 0);
|
||||||
|
|
||||||
int ip = 0;
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
switch (block->bytecode[ip++])
|
CallFrame* frame = &fiber.frames[fiber.numFrames - 1];
|
||||||
|
|
||||||
|
switch (frame->block->bytecode[frame->ip++])
|
||||||
{
|
{
|
||||||
case CODE_CONSTANT:
|
case CODE_CONSTANT:
|
||||||
{
|
{
|
||||||
Value value = block->constants[block->bytecode[ip++]];
|
int constant = frame->block->bytecode[frame->ip++];
|
||||||
|
Value value = frame->block->constants[constant];
|
||||||
fiber.stack[fiber.stackSize++] = value;
|
fiber.stack[fiber.stackSize++] = value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case CODE_LOAD_LOCAL:
|
||||||
|
{
|
||||||
|
int local = frame->block->bytecode[frame->ip++];
|
||||||
|
push(&fiber, fiber.stack[frame->locals + local]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CODE_STORE_LOCAL:
|
||||||
|
{
|
||||||
|
int local = frame->block->bytecode[frame->ip++];
|
||||||
|
fiber.stack[frame->locals + local] = pop(&fiber);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CODE_POP:
|
||||||
|
pop(&fiber);
|
||||||
|
break;
|
||||||
|
|
||||||
case CODE_CALL:
|
case CODE_CALL:
|
||||||
{
|
{
|
||||||
int symbol = block->bytecode[ip++];
|
Value receiver = pop(&fiber);
|
||||||
printf("call %d\n", symbol);
|
// TODO(bob): Arguments.
|
||||||
|
|
||||||
|
int symbol = frame->block->bytecode[frame->ip++];
|
||||||
|
|
||||||
|
// TODO(bob): Support classes for other object types.
|
||||||
|
Class* classObj = &vm->numClass;
|
||||||
|
|
||||||
|
Primitive primitive = classObj->methods[symbol];
|
||||||
|
if (primitive)
|
||||||
|
{
|
||||||
|
Value result = primitive(receiver);
|
||||||
|
push(&fiber, result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO(bob): Should return nil or suspend fiber or something.
|
||||||
|
printf("No method.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case CODE_END:
|
case CODE_END:
|
||||||
return pop(&fiber);
|
{
|
||||||
|
Value result = pop(&fiber);
|
||||||
|
fiber.numFrames--;
|
||||||
|
|
||||||
|
// If we are returning from the top-level block, just return the value.
|
||||||
|
if (fiber.numFrames == 0) return result;
|
||||||
|
|
||||||
|
// Store the result of the block in the first slot, which is where the
|
||||||
|
// caller expects it.
|
||||||
|
fiber.stack[frame->locals] = result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,7 +206,29 @@ void printValue(Value value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void callBlock(Fiber* fiber, Block* block, int locals)
|
||||||
|
{
|
||||||
|
fiber->frames[fiber->numFrames].block = block;
|
||||||
|
fiber->frames[fiber->numFrames].ip = 0;
|
||||||
|
fiber->frames[fiber->numFrames].locals = locals;
|
||||||
|
fiber->numFrames++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(Fiber* fiber, Value value)
|
||||||
|
{
|
||||||
|
// TODO(bob): Check for stack overflow.
|
||||||
|
fiber->stack[fiber->stackSize++] = value;
|
||||||
|
}
|
||||||
|
|
||||||
Value pop(Fiber* fiber)
|
Value pop(Fiber* fiber)
|
||||||
{
|
{
|
||||||
return fiber->stack[--fiber->stackSize];
|
return fiber->stack[--fiber->stackSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Value primitiveNumAbs(Value number)
|
||||||
|
{
|
||||||
|
int value = number->value;
|
||||||
|
if (value < 0) value = -value;
|
||||||
|
|
||||||
|
return makeNum(value);
|
||||||
|
}
|
||||||
|
|||||||
51
src/vm.h
51
src/vm.h
@ -1,8 +1,10 @@
|
|||||||
#ifndef wren_vm_h
|
#ifndef wren_vm_h
|
||||||
#define wren_vm_h
|
#define wren_vm_h
|
||||||
|
|
||||||
// TODO(bob): Make this externally controllable.
|
// TODO(bob): Make these externally controllable.
|
||||||
#define STACK_SIZE 1024
|
#define STACK_SIZE 1024
|
||||||
|
#define MAX_CALL_FRAMES 256
|
||||||
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
OBJ_INT
|
OBJ_INT
|
||||||
@ -31,6 +33,15 @@ typedef enum
|
|||||||
CODE_CONSTANT,
|
CODE_CONSTANT,
|
||||||
// Load the constant at index [arg].
|
// Load the constant at index [arg].
|
||||||
|
|
||||||
|
CODE_POP,
|
||||||
|
// Pop and discard the top of stack.
|
||||||
|
|
||||||
|
CODE_LOAD_LOCAL,
|
||||||
|
// Pushes the value in local slot [arg].
|
||||||
|
|
||||||
|
CODE_STORE_LOCAL,
|
||||||
|
// Pops and stores the value in local slot [arg].
|
||||||
|
|
||||||
CODE_CALL,
|
CODE_CALL,
|
||||||
// Invoke the method with symbol [arg].
|
// Invoke the method with symbol [arg].
|
||||||
|
|
||||||
@ -44,22 +55,54 @@ typedef struct
|
|||||||
unsigned char* bytecode;
|
unsigned char* bytecode;
|
||||||
Value* constants;
|
Value* constants;
|
||||||
int numConstants;
|
int numConstants;
|
||||||
|
int numLocals;
|
||||||
} Block;
|
} Block;
|
||||||
|
|
||||||
#define MAX_SYMBOLS 256
|
#define MAX_SYMBOLS 256
|
||||||
|
|
||||||
|
typedef Value (*Primitive)(Value receiver);
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
// TODO(bob): Make this dynamically sized.
|
// TODO(bob): Make this dynamically sized.
|
||||||
char* symbols[MAX_SYMBOLS];
|
char* names[MAX_SYMBOLS];
|
||||||
int numSymbols;
|
int count;
|
||||||
|
} SymbolTable;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// TODO(bob): Hack. Probably don't want to use this much space.
|
||||||
|
Primitive methods[MAX_SYMBOLS];
|
||||||
|
} Class;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
SymbolTable symbols;
|
||||||
|
Class numClass;
|
||||||
} VM;
|
} VM;
|
||||||
|
|
||||||
VM* newVM();
|
VM* newVM();
|
||||||
void freeVM(VM* vm);
|
void freeVM(VM* vm);
|
||||||
|
|
||||||
int getSymbol(VM* vm, const char* name, size_t length);
|
Value makeNum(int number);
|
||||||
|
|
||||||
|
// Initializes the symbol table.
|
||||||
|
void initSymbolTable(SymbolTable* symbols);
|
||||||
|
|
||||||
|
// Frees all dynamically allocated memory used by the symbol table, but not the
|
||||||
|
// SymbolTable itself.
|
||||||
|
void clearSymbolTable(SymbolTable* symbols);
|
||||||
|
|
||||||
|
// Adds name to the symbol table. Returns the index of it in the table. Returns
|
||||||
|
// -1 if the symbol is already present.
|
||||||
|
int addSymbol(SymbolTable* symbols, const char* name, size_t length);
|
||||||
|
|
||||||
|
// Adds name to the symbol table. Returns the index of it in the table. Will
|
||||||
|
// use an existing symbol if already present.
|
||||||
|
int ensureSymbol(SymbolTable* symbols, const char* name, size_t length);
|
||||||
|
|
||||||
|
// Looks up name in the symbol table. Returns its index if found or -1 if not.
|
||||||
|
int findSymbol(SymbolTable* symbols, const char* name, size_t length);
|
||||||
|
|
||||||
Value interpret(VM* vm, Block* block);
|
Value interpret(VM* vm, Block* block);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user