Initial commit. My implementation of Clox, the programming language constructed from the book 'Crafting Interpreters'
This commit is contained in:
commit
d6281d2f5c
|
@ -0,0 +1,82 @@
|
||||||
|
# Detect OS
|
||||||
|
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
ISWIN := 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifdef ISWIN
|
||||||
|
CC := i686-w64-mingw32-gcc
|
||||||
|
else
|
||||||
|
CC := gcc
|
||||||
|
endif
|
||||||
|
|
||||||
|
RM ?= rm -f
|
||||||
|
RMDIR ?= rm -rf
|
||||||
|
MKDIR ?= mkdir -p
|
||||||
|
|
||||||
|
WARFLAGS := -Wall -Wextra -pedantic
|
||||||
|
|
||||||
|
CFLAGS_g := $(WARFLAGS) -std=gnu11 -O2 -msse2 -ffast-math -mfpmath=sse -DNDEBUG \
|
||||||
|
-MMD
|
||||||
|
|
||||||
|
LDFLAGS_g := -s
|
||||||
|
|
||||||
|
# object output dir
|
||||||
|
OBJDIR_g := obj_g
|
||||||
|
|
||||||
|
SRCS := $(wildcard *.c)
|
||||||
|
OBJS_g := $(SRCS:.c=.o)
|
||||||
|
|
||||||
|
# include dirs
|
||||||
|
CFLAGS_g +=
|
||||||
|
|
||||||
|
# libs
|
||||||
|
ifdef ISWIN
|
||||||
|
LDFLAGS_g +=
|
||||||
|
else
|
||||||
|
CFLAGS_g +=
|
||||||
|
LDFLAGS_g +=
|
||||||
|
endif
|
||||||
|
|
||||||
|
# binary target
|
||||||
|
ifdef ISWIN
|
||||||
|
TARG_g := clox.exe
|
||||||
|
else
|
||||||
|
TARG_g := clox
|
||||||
|
endif
|
||||||
|
|
||||||
|
game: $(TARG_g)
|
||||||
|
@echo "*** Target '$@': '$^' is up to date!"
|
||||||
|
|
||||||
|
default: game
|
||||||
|
|
||||||
|
.PHONY: default game clean
|
||||||
|
|
||||||
|
# rewrite OBJS so it outputs object files to seperate dir
|
||||||
|
|
||||||
|
OBJS_g := $(patsubst %,$(OBJDIR_g)/%,$(OBJS_g))
|
||||||
|
|
||||||
|
# include the dependency rules generated by -MMD
|
||||||
|
|
||||||
|
-include $(OBJS_g:.o=.d)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@echo "*** Removing target binary and object directory..."
|
||||||
|
@$(RM) $(TARG_g)
|
||||||
|
@$(RMDIR) $(OBJDIR_g)
|
||||||
|
@echo "*** Done."
|
||||||
|
|
||||||
|
# Compile
|
||||||
|
|
||||||
|
$(OBJDIR_g)/%.o: %.c
|
||||||
|
@$(MKDIR) $(@D)
|
||||||
|
@echo "*** Compiling '$<' ..."
|
||||||
|
@$(CC) $(CFLAGS_g) -c $< -o $@
|
||||||
|
|
||||||
|
# Link
|
||||||
|
|
||||||
|
$(TARG_g): $(OBJS_g)
|
||||||
|
@$(MKDIR) $(@D)
|
||||||
|
@echo "\n*** Linking binary target '$@' ...\n"
|
||||||
|
@$(CC) $(OBJS_g) -o $@ $(LDFLAGS_g)
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
#include "chunk.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "value.h"
|
||||||
|
|
||||||
|
void initChunk( Chunk* chunk ) {
|
||||||
|
chunk->capacity = 0;
|
||||||
|
chunk->count = 0;
|
||||||
|
chunk->bDebug = true;
|
||||||
|
chunk->code = NULL;
|
||||||
|
chunk->lines = NULL;
|
||||||
|
initValueArray( &chunk->constants );
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeChunk( Chunk* chunk ) {
|
||||||
|
FREE_ARRAY( uint8_t, chunk->code, chunk->capacity );
|
||||||
|
FREE_ARRAY( uint16_t, chunk->lines, chunk->capacity );
|
||||||
|
freeValueArray( &chunk->constants );
|
||||||
|
initChunk( chunk );
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeChunk( Chunk* chunk, uint8_t byte, uint16_t line ) {
|
||||||
|
if ( chunk->capacity < chunk->count + 1 ) {
|
||||||
|
int oldCap = chunk->capacity;
|
||||||
|
chunk->capacity = GROW_CAPACITY( oldCap );
|
||||||
|
chunk->code = GROW_ARRAY( chunk->code, uint8_t, oldCap, chunk->capacity );
|
||||||
|
if ( chunk->bDebug ) {
|
||||||
|
chunk->lines = GROW_ARRAY( chunk->lines, uint16_t, oldCap, chunk->capacity );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk->code[chunk->count] = byte;
|
||||||
|
if ( chunk->bDebug )
|
||||||
|
chunk->lines[chunk->count] = line;
|
||||||
|
chunk->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsigned 16bit write
|
||||||
|
void writeChunk16( Chunk* chunk, uint16_t word, uint16_t line ) {
|
||||||
|
writeChunk( chunk, word & 255, line );
|
||||||
|
writeChunk( chunk, word >> 8, line );
|
||||||
|
}
|
||||||
|
// unsigned 16bit read
|
||||||
|
uint16_t readChunk16( Chunk* chunk, int offset ) {
|
||||||
|
uint16_t val = chunk->code[offset];
|
||||||
|
return ((val) | (chunk->code[offset + 1] << 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t addConstant( Chunk* chunk, Value val ) {
|
||||||
|
writeValueArray( &chunk->constants, val );
|
||||||
|
return chunk->constants.count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// high level for adding and writing a chunk constant
|
||||||
|
void writeConstant( Chunk* chunk, Value val, uint16_t line ) {
|
||||||
|
uint16_t cont = addConstant( chunk, val );
|
||||||
|
// more than 256 constants, use 16bit operand
|
||||||
|
if ( cont > 255 ) {
|
||||||
|
writeChunk( chunk, OP_CONSTANT_LONG, line );
|
||||||
|
writeChunk16( chunk, cont, line );
|
||||||
|
} else {
|
||||||
|
writeChunk( chunk, OP_CONSTANT, line );
|
||||||
|
writeChunk( chunk, cont, line );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
#ifndef _CHUNK_H
|
||||||
|
#define _CHUNK_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "value.h"
|
||||||
|
|
||||||
|
// max 256 opcodes
|
||||||
|
typedef enum {
|
||||||
|
OP_CONSTANT, OP_CONSTANT_LONG,
|
||||||
|
OP_ADD, OP_SUBTRACT,
|
||||||
|
OP_MULTIPLY, OP_DIVIDE,
|
||||||
|
OP_NEGATE,
|
||||||
|
OP_RETURN,
|
||||||
|
|
||||||
|
NUM_OP, // total number of valid opcodes
|
||||||
|
} OpCode;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int count;
|
||||||
|
int capacity;
|
||||||
|
bool bDebug; // if line numbers are kept or not
|
||||||
|
uint8_t* code;
|
||||||
|
uint16_t* lines; // 16bit line numbers
|
||||||
|
ValueArray constants;
|
||||||
|
} Chunk;
|
||||||
|
|
||||||
|
void initChunk( Chunk* chunk );
|
||||||
|
void freeChunk( Chunk* chunk );
|
||||||
|
|
||||||
|
// write single byte to code memory
|
||||||
|
void writeChunk( Chunk* chunk, uint8_t byte, uint16_t line );
|
||||||
|
// u16 write / read
|
||||||
|
void writeChunk16( Chunk* chunk, uint16_t word, uint16_t line );
|
||||||
|
uint16_t readChunk16( Chunk* chunk, int offset );
|
||||||
|
|
||||||
|
// add constant val to chunk's array. return index where it was added
|
||||||
|
uint16_t addConstant( Chunk* chunk, Value val );
|
||||||
|
|
||||||
|
// high level for adding and writing chunk constants
|
||||||
|
void writeConstant( Chunk* chunk, Value val, uint16_t line );
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef _COMMON_H
|
||||||
|
#define _COMMON_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define DEBUG_TRACE_EXECUTION
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
#include "compiler.h"
|
||||||
|
//#include "scanner.h"
|
||||||
|
|
||||||
|
void compile( VM* vm, const char* src ) {
|
||||||
|
//initScanner( src );
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef _COMPILER_H
|
||||||
|
#define _COMPILER_H
|
||||||
|
|
||||||
|
#include "vm.h"
|
||||||
|
|
||||||
|
void compile( VM* vm, const char* src );
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
//#include <stdio.h>
|
||||||
|
|
||||||
|
#include "debug.h"
|
||||||
|
#include "value.h"
|
||||||
|
|
||||||
|
void disassembleChunk( Chunk* chunk, const char* name ) {
|
||||||
|
printChunkInfo( chunk, name );
|
||||||
|
for ( int i = 0; i < chunk->count; ) {
|
||||||
|
i = disassembleInstruction( chunk, i );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void printChunkInfo( Chunk* chunk, const char* name ) {
|
||||||
|
int codesz = chunk->count * sizeof(chunk->code[0]);
|
||||||
|
int codecapsz = chunk->capacity * sizeof(chunk->code[0]);
|
||||||
|
int concnt = chunk->constants.count;
|
||||||
|
int concap = chunk->constants.capacity;
|
||||||
|
int consz = concnt * sizeof(chunk->constants.values[0]);
|
||||||
|
int concapsz = concap * sizeof(chunk->constants.values[0]);
|
||||||
|
|
||||||
|
int linesz = sizeof(chunk->lines[0]) * chunk->count;
|
||||||
|
int linecapsz = sizeof(chunk->lines[0]) * chunk->capacity;
|
||||||
|
if ( !chunk->bDebug ) {
|
||||||
|
linesz = 0; linecapsz = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf( "=== Chunk \"%s\" Info ===\n=== Code size: %d(%d) bytes ===\n"
|
||||||
|
"=== Constants: %d(%d) (%d(%d) bytes) ===\n=== Debug lines size: %d(%d) bytes ===\n", name,
|
||||||
|
codesz, codecapsz, concnt, concap, consz, concapsz, linesz,linecapsz );
|
||||||
|
printf( "=== Total size: %d(%d) bytes ===\n", sizeof(Chunk)+codesz+consz+linesz, sizeof(Chunk)+codecapsz+concapsz+linecapsz );
|
||||||
|
}
|
||||||
|
|
||||||
|
static int constantInstruction( const char* name, Chunk* chunk, int offset ) {
|
||||||
|
uint16_t cons = 0;
|
||||||
|
|
||||||
|
cons = chunk->code[offset + 1];
|
||||||
|
|
||||||
|
if ( chunk->code[offset] == OP_CONSTANT_LONG ) {
|
||||||
|
name = "OP_CONSTANT_LONG";
|
||||||
|
cons = readChunk16( chunk, offset + 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
printf( "%-16s %05u '", name, cons );
|
||||||
|
Value conVal = -9001; // cons index out of range
|
||||||
|
if ( cons < chunk->constants.count )
|
||||||
|
conVal = chunk->constants.values[cons];
|
||||||
|
printValue( conVal );
|
||||||
|
printf( "'\n" );
|
||||||
|
|
||||||
|
return ((cons > 255) ? (offset + 3) : (offset + 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int simpleInstruction( const char* name, int offset ) {
|
||||||
|
printf( "%s\n", name );
|
||||||
|
return offset + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int disassembleInstruction( Chunk* chunk, int offset ) {
|
||||||
|
printf( "0x%08x ", offset );
|
||||||
|
if ( chunk->bDebug ) {
|
||||||
|
if ( offset > 0 && chunk->lines[offset] == chunk->lines[offset - 1] ) {
|
||||||
|
printf( " | " );
|
||||||
|
} else {
|
||||||
|
printf( "%04d ", chunk->lines[offset] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t instruction = chunk->code[offset];
|
||||||
|
switch ( instruction ) {
|
||||||
|
case OP_CONSTANT:
|
||||||
|
case OP_CONSTANT_LONG:
|
||||||
|
return constantInstruction( "OP_CONSTANT", chunk, 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_NEGATE:
|
||||||
|
return simpleInstruction( "OP_NEGATE", offset );
|
||||||
|
case OP_RETURN:
|
||||||
|
return simpleInstruction( "OP_RETURN", offset );
|
||||||
|
default:
|
||||||
|
printf( "UNK OP %d\n", instruction );
|
||||||
|
return offset + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef _DEBUG_H
|
||||||
|
#define _DEBUG_H
|
||||||
|
|
||||||
|
#include "chunk.h"
|
||||||
|
|
||||||
|
void printChunkInfo( Chunk* chunk, const char* name );
|
||||||
|
void disassembleChunk( Chunk* chunk, const char* name );
|
||||||
|
int disassembleInstruction( Chunk* chunk, int offset );
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
#include "common.h"
|
||||||
|
#include "chunk.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "vm.h"
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
static void repl( VM* vm ) {
|
||||||
|
char line[512];
|
||||||
|
|
||||||
|
for ( ; ; ) {
|
||||||
|
printf( "> " );
|
||||||
|
if ( !fgets( line, sizeof(line), stdin ) ) {
|
||||||
|
printf( "\n" ); break;
|
||||||
|
}
|
||||||
|
interpret( vm, line );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* readFile( const char* path ) {
|
||||||
|
FILE* file = fopen( path, "rb" );
|
||||||
|
if ( file == NULL ) {
|
||||||
|
fprintf( stderr, "Error opening file: \"%s\"\n", path );
|
||||||
|
exit( 74 );
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek( file, 0, SEEK_END );
|
||||||
|
size_t sz = ftell( file );
|
||||||
|
rewind( file );
|
||||||
|
|
||||||
|
char* src = (char*)malloc( sz + 1 );
|
||||||
|
if ( src == NULL ) {
|
||||||
|
fprintf( stderr, "Error allocating memory for file: \"%s\"\n", path );
|
||||||
|
exit( 74 );
|
||||||
|
}
|
||||||
|
size_t bread = fread( src, sizeof(char), sz, file );
|
||||||
|
if ( bread < sz ) {
|
||||||
|
fprintf( stderr, "Error reading file: \"%s\"\n", path );
|
||||||
|
exit( 74 );
|
||||||
|
}
|
||||||
|
src[bread] = '\0';
|
||||||
|
|
||||||
|
fclose( file );
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void runFile( VM* vm, const char* path ) {
|
||||||
|
char* source = readFile( path );
|
||||||
|
InterpretResult res = interpret( vm, source );
|
||||||
|
free( source );
|
||||||
|
if ( res == INTERPRET_COMPILE_ERROR ) exit( 65 );
|
||||||
|
if ( res == INTERPRET_RUNTIME_ERROR ) exit( 70 );
|
||||||
|
}
|
||||||
|
|
||||||
|
int main( int argc, char** argv ) {
|
||||||
|
(void)argc; (void)argv;
|
||||||
|
srand( time( NULL ) );
|
||||||
|
|
||||||
|
double dfp = 22.0 / 7.0;
|
||||||
|
printf( "DFP: %.64g\n", dfp );
|
||||||
|
|
||||||
|
float ffp = 22.0f / 7.0f;
|
||||||
|
printf( "FFP: %.64f\n", ffp );
|
||||||
|
|
||||||
|
VM vm;
|
||||||
|
initVM( &vm );
|
||||||
|
|
||||||
|
if ( argc == 1 ) {
|
||||||
|
repl( &vm );
|
||||||
|
} else if ( argc == 2 ) {
|
||||||
|
runFile( &vm, argv[1] );
|
||||||
|
} else {
|
||||||
|
fprintf( stderr, "Usage: clox [path]\n" );
|
||||||
|
exit( EXIT_FAILURE );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Chunk c;
|
||||||
|
initChunk( &c );
|
||||||
|
//c.bDebug = false;
|
||||||
|
|
||||||
|
writeConstant( &c, 4.2f, 1 );
|
||||||
|
writeConstant( &c, rand()/(float)RAND_MAX, 2 );
|
||||||
|
|
||||||
|
//for ( int i = 0; i < 10; ++i )
|
||||||
|
// writeChunk( &c, rand() % NUM_OP, 5 );
|
||||||
|
|
||||||
|
// const overload
|
||||||
|
for ( int i = 2; i < 6; ++i ) {
|
||||||
|
writeConstant( &c, (float)rand()/RAND_MAX, 6 );
|
||||||
|
}
|
||||||
|
|
||||||
|
writeConstant( &c, 69069, 9 );
|
||||||
|
writeChunk( &c, OP_NEGATE, 9 );
|
||||||
|
|
||||||
|
writeConstant( &c, 22, 9 );
|
||||||
|
writeChunk( &c, OP_ADD, 9 );
|
||||||
|
|
||||||
|
writeConstant( &c, 42, 9 );
|
||||||
|
writeChunk( &c, OP_DIVIDE, 9 );
|
||||||
|
|
||||||
|
writeChunk( &c, OP_RETURN, 10 );
|
||||||
|
|
||||||
|
//disassembleChunk( &c, "test" );
|
||||||
|
|
||||||
|
interpret( &vm, &c );
|
||||||
|
printChunkInfo( &c, "test" );
|
||||||
|
|
||||||
|
printf( "vm %u chunk %u valar %u\n", sizeof(VM), sizeof(Chunk), sizeof(ValueArray) );
|
||||||
|
|
||||||
|
freeChunk( &c );*/
|
||||||
|
|
||||||
|
freeVM( &vm );
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
//void* malloc( size_t sz ) {}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
#include "common.h"
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
void* reallocate( void* prev, size_t oldSz, size_t newSz ) {
|
||||||
|
(void)oldSz;
|
||||||
|
if ( newSz == 0 ) {
|
||||||
|
free( prev ); prev = NULL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return realloc( prev, newSz );
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef _MEMORY_H
|
||||||
|
#define _MEMORY_H
|
||||||
|
|
||||||
|
#define GROW_CAPACITY( cap ) \
|
||||||
|
((cap) < 8 ? 8 : (cap) * 1.5f)
|
||||||
|
|
||||||
|
#define GROW_ARRAY( prev, type, oldCnt, newCnt ) \
|
||||||
|
(type*)reallocate( prev, sizeof(type) * (oldCnt), sizeof(type) * (newCnt) )
|
||||||
|
|
||||||
|
#define FREE_ARRAY( type, ptr, oldCnt ) \
|
||||||
|
reallocate( ptr, sizeof(type) * (oldCnt), 0 )
|
||||||
|
|
||||||
|
void* reallocate( void* prev, size_t oldSz, size_t newSz );
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
#include "value.h"
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
void initValueArray( ValueArray* array ) {
|
||||||
|
array->capacity = 0;
|
||||||
|
array->count = 0;
|
||||||
|
array->values = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeValueArray( ValueArray* array, Value val ) {
|
||||||
|
if ( array->capacity < array->count + 1 ) {
|
||||||
|
int oldCap = array->capacity;
|
||||||
|
array->capacity = GROW_CAPACITY( oldCap );
|
||||||
|
array->values = GROW_ARRAY( array->values, Value, oldCap, array->capacity );
|
||||||
|
}
|
||||||
|
|
||||||
|
array->values[array->count] = val;
|
||||||
|
array->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeValueArray( ValueArray* array ) {
|
||||||
|
FREE_ARRAY( Value, array->values, array->capacity );
|
||||||
|
initValueArray( array );
|
||||||
|
}
|
||||||
|
|
||||||
|
void printValue( Value val ) {
|
||||||
|
printf( "%g", val );
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef _VALUE_H
|
||||||
|
#define _VALUE_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
typedef float Value;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int capacity;
|
||||||
|
int count;
|
||||||
|
Value* values;
|
||||||
|
} ValueArray;
|
||||||
|
|
||||||
|
void initValueArray( ValueArray* array );
|
||||||
|
void writeValueArray( ValueArray* array, Value val );
|
||||||
|
void freeValueArray( ValueArray* array );
|
||||||
|
void printValue( Value val );
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
#include "common.h"
|
||||||
|
#include "vm.h"
|
||||||
|
#include "compiler.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
static void resetStack( VM* vm ) {
|
||||||
|
vm->sp = vm->stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
void initVM( VM* vm ) {
|
||||||
|
resetStack( vm );
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeVM( VM* vm ) {
|
||||||
|
(void)vm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push( VM* vm, Value val ) {
|
||||||
|
if ( vm->sp - vm->stack >= MAX_STACK ) {
|
||||||
|
printf( "VM Error: Stack is full.\n" );
|
||||||
|
resetStack( vm );
|
||||||
|
}
|
||||||
|
*(vm->sp) = val;
|
||||||
|
vm->sp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value pop( VM* vm ) {
|
||||||
|
vm->sp--;
|
||||||
|
return *(vm->sp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static InterpretResult run( VM* vm ) {
|
||||||
|
#define READ_BYTE() (*(vm->ip++))
|
||||||
|
#define READ_CONSTANT( ix ) (vm->chunk->constants.values[ix])
|
||||||
|
#define BINARY_OP( op ) do { Value b = pop( vm ), a = pop( vm ); \
|
||||||
|
push( vm, a op b ); } while ( false )
|
||||||
|
|
||||||
|
uint8_t instruction = 0;
|
||||||
|
|
||||||
|
for ( ; ; ) {
|
||||||
|
#ifdef DEBUG_TRACE_EXECUTION
|
||||||
|
printf( "\t" );
|
||||||
|
for ( Value* v = vm->stack; v < vm->sp; v++ ) {
|
||||||
|
printf( "[ " );
|
||||||
|
printValue( *v );
|
||||||
|
printf( " ]" );
|
||||||
|
}
|
||||||
|
printf( "\n" );
|
||||||
|
disassembleInstruction( vm->chunk, (int)(vm->ip - vm->chunk->code) );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
switch ( instruction = READ_BYTE() ) {
|
||||||
|
case OP_CONSTANT:
|
||||||
|
case OP_CONSTANT_LONG: {
|
||||||
|
uint16_t cons = READ_BYTE();
|
||||||
|
if ( instruction == OP_CONSTANT_LONG )
|
||||||
|
cons |= (READ_BYTE() << 8);
|
||||||
|
Value constant = READ_CONSTANT( cons );
|
||||||
|
push( vm, constant );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case OP_ADD: BINARY_OP( + ); break;
|
||||||
|
case OP_SUBTRACT: BINARY_OP( - ); break;
|
||||||
|
case OP_MULTIPLY: BINARY_OP( * ); break;
|
||||||
|
case OP_DIVIDE: BINARY_OP( / ); break;
|
||||||
|
case OP_NEGATE: push( vm, -pop( vm ) ); break;
|
||||||
|
case OP_RETURN:
|
||||||
|
printValue( pop( vm ) );
|
||||||
|
printf( "\n" );
|
||||||
|
return INTERPRET_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef BINARY_OP
|
||||||
|
#undef READ_CONSTANT
|
||||||
|
#undef READ_BYTE
|
||||||
|
}
|
||||||
|
|
||||||
|
InterpretResult interpret( VM* vm, const char* src ) {
|
||||||
|
/*vm->chunk = chunk;
|
||||||
|
vm->ip = vm->chunk->code;
|
||||||
|
return run( vm );*/
|
||||||
|
compile( vm, src );
|
||||||
|
return INTERPRET_OK;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef _VM_H
|
||||||
|
#define _VM_H
|
||||||
|
|
||||||
|
#include "chunk.h"
|
||||||
|
|
||||||
|
#define MAX_STACK 256
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Chunk* chunk;
|
||||||
|
uint8_t* ip;
|
||||||
|
Value stack[MAX_STACK];
|
||||||
|
Value* sp;
|
||||||
|
} VM;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
INTERPRET_OK,
|
||||||
|
INTERPRET_COMPILE_ERROR,
|
||||||
|
INTERPRET_RUNTIME_ERROR
|
||||||
|
} InterpretResult;
|
||||||
|
|
||||||
|
void initVM( VM* vm );
|
||||||
|
void freeVM( VM* vm );
|
||||||
|
InterpretResult interpret( VM* vm, const char* src );
|
||||||
|
void push( VM* vm, Value val );
|
||||||
|
Value pop( VM* vm );
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue