├── Olive-bci ├── test.olv ├── olive ├── asset │ ├── exit.png │ ├── icon.png │ ├── repl.png │ └── persistence.png ├── Makefile ├── debug.h ├── stack.h ├── compiler.h ├── common.h ├── control.h ├── stack.c ├── memory.h ├── table.h ├── control.c ├── dynamic_array.h ├── vm.h ├── scanner.h ├── value.h ├── chunk.h ├── object.h ├── chunk.c ├── main.c ├── value.c ├── object.c ├── table.c ├── memory.c ├── debug.c ├── scanner.c ├── vm.c └── compiler.c ├── LICENSE └── README.md /Olive-bci/test.olv: -------------------------------------------------------------------------------- 1 | print 10; 2 | -------------------------------------------------------------------------------- /Olive-bci/olive: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wldfngrs/Olive/HEAD/Olive-bci/olive -------------------------------------------------------------------------------- /Olive-bci/asset/exit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wldfngrs/Olive/HEAD/Olive-bci/asset/exit.png -------------------------------------------------------------------------------- /Olive-bci/asset/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wldfngrs/Olive/HEAD/Olive-bci/asset/icon.png -------------------------------------------------------------------------------- /Olive-bci/asset/repl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wldfngrs/Olive/HEAD/Olive-bci/asset/repl.png -------------------------------------------------------------------------------- /Olive-bci/asset/persistence.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wldfngrs/Olive/HEAD/Olive-bci/asset/persistence.png -------------------------------------------------------------------------------- /Olive-bci/Makefile: -------------------------------------------------------------------------------- 1 | C_SOURCES = ${wildcard *.c} 2 | HEADERS = ${wildcard *.h} 3 | 4 | olive: ${C_SOURCES} ${HEADERS} 5 | gcc -g -o olive main.c chunk.c memory.c debug.c value.c vm.c stack.c compiler.c scanner.c object.c table.c control.c 6 | -------------------------------------------------------------------------------- /Olive-bci/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef olive_debug_h 2 | #define olive_debug_h 3 | 4 | #include "chunk.h" 5 | 6 | void disassembleChunk(Chunk* chunk, const char* name); 7 | int disassembleInstruction(Chunk* chunk, int offset); 8 | void resetDebugInfo(); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /Olive-bci/stack.h: -------------------------------------------------------------------------------- 1 | #ifndef stack_olive_h 2 | #define stack_olive_h 3 | 4 | #include "value.h" 5 | 6 | typedef struct { 7 | int count; 8 | int capacity; 9 | Value* stack; 10 | } Stack; 11 | 12 | void initStack(Stack* stack); 13 | void freeStack(Stack* stack); 14 | void growStack(Stack* stack); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /Olive-bci/compiler.h: -------------------------------------------------------------------------------- 1 | #ifndef olive_compiler_h 2 | #define olive_compiler_h 3 | 4 | #include "vm.h" 5 | #include "object.h" 6 | 7 | ObjFunction* compile(const char* source, size_t len, bool REPLmode, bool withinREPL); 8 | ObjFunction* compileREPL(const char* source, size_t len, bool REPLmode, bool withinREPL); 9 | void markCompilerRoots(); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /Olive-bci/common.h: -------------------------------------------------------------------------------- 1 | #ifndef olive_common_h 2 | #define olive_common_h 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define DEBUG_PRINT_CODE 10 | //#define DEBUG_TRACE_EXECUTION 11 | 12 | //#define DEBUG_STRESS_GC 13 | //#define DEBUG_LOG_GC 14 | 15 | #define SCOPE_COUNT 1000 // Increase to 32 bits if too little over time. 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /Olive-bci/control.h: -------------------------------------------------------------------------------- 1 | #ifndef break_exit_h 2 | #define break_exit_h 3 | 4 | 5 | struct controlFlow { 6 | struct controlFlow* prev; 7 | int count; 8 | int capacity; 9 | int cpCapacity; 10 | int cpCount; 11 | int* exits; 12 | int* continuePoint; 13 | }; 14 | 15 | typedef struct controlFlow controlFlow; 16 | 17 | void initControlFlow(controlFlow* control); 18 | void freeControlFlow(controlFlow* control); 19 | void growControlFlow(controlFlow* control); 20 | void growCpControlFlow(controlFlow* control); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /Olive-bci/stack.c: -------------------------------------------------------------------------------- 1 | #include "stack.h" 2 | #include "memory.h" 3 | 4 | void initStack(Stack* stack) { 5 | stack->count = 0; 6 | stack->capacity = 0; 7 | stack->stack = NULL; 8 | growStack(stack); 9 | } 10 | 11 | void growStack(Stack* stack) { 12 | int oldCapacity = stack->capacity; 13 | stack->capacity = GROW_STACK_CAPACITY(oldCapacity); 14 | stack->stack = GROW_ARRAY(Value, stack->stack, oldCapacity, stack->capacity); 15 | } 16 | 17 | void freeStack(Stack* stack) { 18 | FREE_ARRAY(Value, stack->stack, stack->capacity); 19 | } 20 | -------------------------------------------------------------------------------- /Olive-bci/memory.h: -------------------------------------------------------------------------------- 1 | #ifndef olive_memory_h 2 | #define olive_memory_h 3 | 4 | #include "common.h" 5 | #include "object.h" 6 | 7 | #define ALLOCATE(type, count)\ 8 | (type*)reallocate(NULL, 0, sizeof(type) * (count)) 9 | 10 | #define FREE(type, pointer) reallocate(pointer, sizeof(type), 0) 11 | 12 | #define GROW_CAPACITY(capacity) \ 13 | ((capacity) < 8 ? 8 : (capacity)*2) 14 | 15 | #define GROW_STACK_CAPACITY(capacity) \ 16 | ((capacity) < 256 ? 256 : (256)*2) 17 | 18 | #define GROW_ARRAY(type, pointer, oldCount, newCount) \ 19 | (type*)reallocate(pointer, sizeof(type)*(oldCount), sizeof(type)*(newCount)) 20 | 21 | #define FREE_ARRAY(type, pointer, oldCount) \ 22 | reallocate(pointer, sizeof(type)*(oldCount), 0) 23 | 24 | void* reallocate(void* pointer, size_t oldSize, size_t newSize); 25 | void markObject(Obj* object); 26 | void markValue(Value value); 27 | void collectGarbage(); 28 | void freeObjects(); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /Olive-bci/table.h: -------------------------------------------------------------------------------- 1 | #ifndef olive_table_h 2 | #define olive_table_h 3 | 4 | #include "common.h" 5 | #include "value.h" 6 | 7 | #define NULL_KEY ((Key){VAL_NULL, {.number = 0}}) 8 | #define OBJ_KEY(object) ((Key){VAL_OBJ, {.obj = object}}) 9 | 10 | typedef struct { 11 | ValueType type; 12 | union { 13 | bool boolean; 14 | double number; 15 | ObjString* obj; 16 | } as; 17 | bool constant; 18 | } Key; 19 | 20 | typedef struct { 21 | Key key; 22 | Value value; 23 | } Entry; 24 | 25 | typedef struct { 26 | int count; 27 | int capacity; 28 | Entry* entries; 29 | } Table; 30 | 31 | void initTable(Table* table); 32 | void freeTable(Table* table); 33 | bool tableGet(Table* table, Key* key, Value* value); 34 | bool tableSet(Table* table, Key* key, Value value); 35 | bool tableSetGlobal(Table* table, Key* key, Value value); 36 | bool tableDelete(Table* table, Key* key); 37 | void tableAddAll(Table* from, Table* to); 38 | ObjString* tableFindString(Table* table, const char* chars, int length, uint32_t hash); 39 | void tableRemoveWhite(Table* table); 40 | void markTable(Table* table); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 wldfngrs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Olive-bci/control.c: -------------------------------------------------------------------------------- 1 | #include "control.h" 2 | #include "memory.h" 3 | 4 | void initControlFlow(controlFlow* control) { 5 | control->count = 0; 6 | control->capacity = 0; 7 | control->exits = NULL; 8 | control->cpCount = 0; 9 | control->cpCapacity = 0; 10 | control->continuePoint = NULL; 11 | control->prev = NULL; 12 | growControlFlow(control); 13 | growCpControlFlow(control); 14 | } 15 | 16 | void growControlFlow(controlFlow* control) { 17 | int oldCapacity = control->capacity; 18 | control->capacity = GROW_STACK_CAPACITY(oldCapacity); 19 | control->exits = GROW_ARRAY(int, control->exits, oldCapacity, control->capacity); 20 | } 21 | 22 | void growCpControlFlow(controlFlow* control) { 23 | int oldCapacityCp = control->cpCapacity; 24 | control->cpCapacity = GROW_STACK_CAPACITY(oldCapacityCp); 25 | control->continuePoint = GROW_ARRAY(int, control->continuePoint, oldCapacityCp, control->cpCapacity); 26 | } 27 | 28 | void freeControlFlow(controlFlow* control) { 29 | FREE_ARRAY(int, control->exits, control->capacity); 30 | FREE_ARRAY(int, control->continuePoint, control->cpCapacity); 31 | control->count = 0; 32 | control->cpCount = 0; 33 | } 34 | -------------------------------------------------------------------------------- /Olive-bci/dynamic_array.h: -------------------------------------------------------------------------------- 1 | #include "memory.h" 2 | 3 | struct dynamic_array { 4 | int count; 5 | int capacity; 6 | char* array; 7 | }; 8 | 9 | void growDynamicArray(struct dynamic_array* da) { 10 | int oldCapacity = da->capacity; 11 | da->capacity = GROW_STACK_CAPACITY(oldCapacity); // large growth capacity is so string intern resolution happens infrequently. 12 | da->array = GROW_ARRAY(char, da->array, oldCapacity, da->capacity); 13 | } 14 | 15 | void appendDynamicArray(struct dynamic_array* da, char* in) { 16 | size_t len = strlen(in); 17 | if (da->count + len > da->capacity) { 18 | char* prev = da->array; 19 | growDynamicArray(da); 20 | char* cur = da->array; 21 | resolveStringInterns(cur - prev); 22 | } 23 | memcpy(da->array + da->count, in, len); 24 | da->count += len; 25 | } 26 | 27 | void freeDynamicArray(struct dynamic_array* da) { 28 | FREE_ARRAY(int, da->array, da->capacity); 29 | da->count = 0; 30 | da->capacity = 0; 31 | da->array = NULL; 32 | } 33 | 34 | void initDynamicArray(struct dynamic_array* da) { 35 | da->count = 0; 36 | da->capacity = 0; 37 | da->array = NULL; 38 | growDynamicArray(da); 39 | } 40 | 41 | 42 | typedef struct dynamic_array DA; 43 | -------------------------------------------------------------------------------- /Olive-bci/vm.h: -------------------------------------------------------------------------------- 1 | #ifndef olive_vm_h 2 | #define olive_vm_h 3 | 4 | #include "chunk.h" 5 | #include "stack.h" 6 | #include "object.h" 7 | #include "table.h" 8 | 9 | #define FRAMES_MAX 64 10 | 11 | #define STACK_MAX 256 12 | #define NATIVE_ID_MAX 10 13 | 14 | typedef struct { 15 | ObjClosure* closure; 16 | uint8_t* ip; 17 | Value* slots; 18 | } CallFrame; 19 | 20 | typedef struct { 21 | //Chunk* chunk; 22 | //uint8_t* ip; 23 | CallFrame frames[FRAMES_MAX]; 24 | int frameCount; 25 | 26 | Stack stack; 27 | Value* stackTop; 28 | Table globals; 29 | Table strings; 30 | ObjString* initString; 31 | Table globalConstantIndex; // probably find a better name 32 | int nativeIdentifierCount; 33 | const char* nativeIdentifiers[NATIVE_ID_MAX]; 34 | ObjUpvalue* openUpvalues; 35 | Obj* objects; 36 | 37 | size_t bytesAllocated; 38 | size_t nextGC; 39 | 40 | int grayCount; 41 | int grayCapacity; 42 | Obj** grayStack; 43 | } VM; 44 | 45 | 46 | typedef enum { 47 | INTERPRET_OK, 48 | INTERPRET_COMPILE_ERROR, 49 | INTERPRET_RUNTIME_ERROR 50 | } InterpretResult; 51 | 52 | extern VM vm; 53 | 54 | void initVM(); 55 | void freeVM(bool REPLmode); 56 | InterpretResult interpret(const char* source, size_t len, bool REPLmode, bool* withinREPL); 57 | void push(Value value); 58 | Value pop(uint8_t popCount); 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /Olive-bci/scanner.h: -------------------------------------------------------------------------------- 1 | #ifndef olive_scanner_h 2 | #define olive_scanner_h 3 | 4 | typedef enum { 5 | // Single-character tokens. 6 | TOKEN_LEFT_PAREN, TOKEN_RIGHT_PAREN, 7 | TOKEN_LEFT_BRACE, TOKEN_RIGHT_BRACE, 8 | TOKEN_COMMA, TOKEN_DOT, TOKEN_MINUS, TOKEN_PLUS, 9 | TOKEN_SEMICOLON, TOKEN_SLASH, TOKEN_MOD, TOKEN_STAR, TOKEN_QUESTION_MARK, TOKEN_COLON, 10 | TOKEN_PERCENT, 11 | // One or two character tokens. 12 | TOKEN_BANG, TOKEN_BANG_EQUAL, 13 | TOKEN_EQUAL, TOKEN_EQUAL_EQUAL, 14 | TOKEN_GREATER, TOKEN_GREATER_EQUAL, 15 | TOKEN_LESS, TOKEN_LESS_EQUAL, 16 | // Literals. 17 | TOKEN_IDENTIFIER, TOKEN_STRING, TOKEN_NUMBER, 18 | // Keywords. // NIL TO NULL, SUPER TO BASE, FUN TO DEF 19 | TOKEN_AND, TOKEN_CLASS, TOKEN_ELSE, TOKEN_FALSE, 20 | TOKEN_FOR, TOKEN_DEF, TOKEN_IF, TOKEN_NULL, TOKEN_OR, 21 | TOKEN_PRINT, TOKEN_RETURN, TOKEN_BASE, TOKEN_THIS, 22 | TOKEN_TRUE, TOKEN_VAR, TOKEN_WHILE, TOKEN_SWITCH, TOKEN_SWITCHCASE, TOKEN_SWITCHDEFAULT, TOKEN_BREAK, TOKEN_CONTINUE, TOKEN_DELATTR, 23 | TOKEN_ERROR, TOKEN_CONST, TOKEN_NEWLINE, TOKEN_NL, 24 | TOKEN_EOF, 25 | // Placeholder tokens for string interpolation 26 | TOKEN_INTERPOLATION, TOKEN_CONCAT, 27 | } TokenType; 28 | 29 | typedef struct { 30 | TokenType type; 31 | const char* start; 32 | int length; 33 | int line; 34 | } Token; 35 | 36 | void initScanner(const char* source, size_t len); 37 | Token scanToken(); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /Olive-bci/value.h: -------------------------------------------------------------------------------- 1 | #ifndef olive_value_h 2 | #define olive_value_h 3 | 4 | #include "common.h" 5 | 6 | typedef struct Obj Obj; 7 | typedef struct ObjString ObjString; 8 | 9 | typedef enum { 10 | VAL_BOOL, 11 | VAL_NULL, 12 | VAL_NUMBER, 13 | VAL_OBJ, 14 | VAL_NL, 15 | } ValueType; 16 | 17 | typedef struct { 18 | ValueType type; 19 | union { 20 | bool boolean; 21 | double number; 22 | Obj* obj; 23 | } as; 24 | bool isConst; 25 | } Value; 26 | 27 | #define IS_BOOL(value) ((value).type == VAL_BOOL) 28 | #define IS_NULL(value) ((value).type == VAL_NULL) 29 | #define IS_NUMBER(value) ((value).type == VAL_NUMBER) 30 | #define IS_OBJ(value) ((value).type == VAL_OBJ) 31 | #define IS_NL(value) ((value).type == VAL_NL) 32 | 33 | #define AS_OBJ(value) ((value).as.obj) 34 | #define AS_BOOL(value) ((value).as.boolean) 35 | #define AS_NUMBER(value) ((value).as.number) 36 | 37 | #define BOOL_VAL(value) ((Value){VAL_BOOL, {.boolean = value}}) 38 | #define NULL_VAL ((Value){VAL_NULL, {.number = 0}}) 39 | #define NUMBER_VAL(value) ((Value){VAL_NUMBER, {.number = value}}) 40 | #define OBJ_VAL(object) ((Value){VAL_OBJ, {.obj = (Obj*)object}}) 41 | #define NL_VAL ((Value){VAL_NL}) 42 | 43 | typedef struct { 44 | int capacity; 45 | int count; 46 | Value* values; 47 | } ValueArray; 48 | 49 | bool valuesEqual(Value a, Value b); 50 | bool valuesNotEqual(Value a, Value b); 51 | bool valuesGreater(Value a, Value b); 52 | bool valuesGreaterEqual(Value a, Value b); 53 | bool valuesLess(Value a, Value b); 54 | bool valuesLessEqual(Value a, Value b); 55 | Value valuesConditional(Value a, Value b, Value conditional); 56 | void initValueArray(ValueArray* array); 57 | void writeValueArray(ValueArray* array, Value value); 58 | void freeValueArray(ValueArray* array); 59 | void printValue(Value value); 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /Olive-bci/chunk.h: -------------------------------------------------------------------------------- 1 | #ifndef olive_chunk_h 2 | #define olive_chunk_h 3 | 4 | #include "common.h" 5 | #include "value.h" 6 | 7 | /* Line info cleared by clearLineInfo() function at parsing error. */ 8 | static int currentLine = 0; 9 | static int operationsPerLine = 0; 10 | static int indx = 0; 11 | static int temp; 12 | 13 | /* Bytecode instructions-set. */ 14 | typedef enum { 15 | OP_CONSTANT_LONG, 16 | OP_CONSTANT, 17 | OP_NULL, 18 | OP_TRUE, 19 | OP_FALSE, 20 | OP_POP, 21 | OP_POPN, 22 | OP_GET_LOCAL, 23 | OP_SET_LOCAL, 24 | OP_GET_GLOBAL, 25 | OP_GET_UPVALUE, 26 | OP_SET_UPVALUE, 27 | OP_GET_PROPERTY, 28 | OP_SET_PROPERTY, 29 | OP_GET_BASE, 30 | OP_DELATTR, 31 | OP_DEFINE_GLOBAL, 32 | OP_SET_GLOBAL, 33 | OP_EQUAL, 34 | OP_SWITCH_EQUAL, 35 | OP_NOT_EQUAL, 36 | OP_GREATER, 37 | OP_GREATER_EQUAL, 38 | OP_LESS, 39 | OP_LESS_EQUAL, 40 | OP_TERNARY, 41 | OP_ADD, 42 | OP_SUBTRACT, 43 | OP_MULTIPLY, 44 | OP_DIVIDE, 45 | OP_MOD, 46 | OP_PERCENT, 47 | OP_NOT, 48 | OP_NEGATE, 49 | OP_PRINT, 50 | OP_JUMP, 51 | OP_JUMP_IF_FALSE, 52 | OP_LOOP, 53 | OP_CALL, 54 | OP_CLOSURE, 55 | OP_CLOSE_UPVALUE, 56 | OP_BREAK, 57 | //OP_JUMP_IF_BREAK, 58 | OP_FALLTHROUGH, 59 | OP_CONTINUE, 60 | OP_RETURN, 61 | OP_CLASS, 62 | OP_INHERIT, 63 | OP_INVOKE, 64 | OP_BASE_INVOKE, 65 | OP_METHOD, 66 | } OpCode; 67 | 68 | /* A Chunk type to hold the bytecode instructions. A dynamic array with the ValueArray included in it's definition. */ 69 | typedef struct { 70 | int count; 71 | int capacity; 72 | uint8_t* code; 73 | int* lineArr; 74 | int* codeArr; 75 | ValueArray* constants; 76 | } Chunk; 77 | 78 | void initChunk(Chunk* chunk, ValueArray* constants); 79 | void freeChunk(Chunk* chunk); 80 | void freeChunkButNotValueArray(Chunk* chunk); 81 | void writeChunk(Chunk* chunk, uint8_t bytes, int line); 82 | int addConstant(Chunk* chunk, Value value, bool constness); 83 | void writeConstant(Chunk* chunk, Value value, int line); 84 | int getLine(Chunk* chunk, int instructionIndex); 85 | void clearLineInfo(); 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /Olive-bci/object.h: -------------------------------------------------------------------------------- 1 | #ifndef olive_object_h 2 | #define olive_object_h 3 | 4 | #include "common.h" 5 | #include "chunk.h" 6 | #include "value.h" 7 | #include "table.h" 8 | 9 | #define OBJ_TYPE(value) (AS_OBJ(value)->type) 10 | 11 | #define IS_BOUND_METHOD(value) isObjType(value, OBJ_BOUND_METHOD) 12 | #define IS_CLASS(value) isObjType(value, OBJ_CLASS) 13 | #define IS_CLOSURE(value) isObjType(value, OBJ_CLOSURE) 14 | #define IS_FUNCTION(value) isObjType(value, OBJ_FUNCTION) 15 | #define IS_INSTANCE(value) isObjType(value, OBJ_INSTANCE) 16 | #define IS_NATIVE(value) isObjType(value, OBJ_NATIVE) 17 | #define IS_STRING(value) isObjType(value, OBJ_STRING) 18 | 19 | #define AS_BOUND_METHOD(value) ((ObjBoundMethod*)AS_OBJ(value)) 20 | #define AS_CLASS(value) ((ObjClass*)AS_OBJ(value)) 21 | #define AS_CLOSURE(value) ((ObjClosure*)AS_OBJ(value)) 22 | #define AS_FUNCTION(value) ((ObjFunction*)AS_OBJ(value)) 23 | #define AS_INSTANCE(value) ((ObjInstance*)AS_OBJ(value)) 24 | #define AS_NATIVE(value) (((ObjNative*)AS_OBJ(value))->function) 25 | #define AS_STRING(value) ((ObjString*)AS_OBJ(value)) 26 | #define AS_CSTRING(value) (((ObjString*)AS_OBJ(value))->chars) 27 | 28 | typedef enum { 29 | OBJ_BOUND_METHOD, 30 | OBJ_CLASS, 31 | OBJ_CLOSURE, 32 | OBJ_FUNCTION, 33 | OBJ_INSTANCE, 34 | OBJ_NATIVE, 35 | OBJ_STRING, 36 | OBJ_UPVALUE, 37 | } ObjType; 38 | 39 | struct Obj { 40 | ObjType type; 41 | bool isMarked; 42 | struct Obj* next; 43 | }; 44 | 45 | typedef struct { 46 | Obj obj; 47 | int arity; 48 | int upvalueCount; 49 | Chunk chunk; 50 | ObjString* name; 51 | } ObjFunction; 52 | 53 | typedef Value (*NativeFunction)(int argCount, Value* args); 54 | 55 | typedef struct { 56 | Obj obj; 57 | NativeFunction function; 58 | } ObjNative; 59 | 60 | struct ObjString { 61 | Obj obj; 62 | int length; 63 | bool ownString; 64 | const char* chars; 65 | uint32_t hash; 66 | }; 67 | 68 | typedef struct ObjUpvalue { 69 | Obj obj; 70 | Value* location; 71 | Value closed; 72 | struct ObjUpvalue* next; 73 | } ObjUpvalue; 74 | 75 | typedef struct { 76 | Obj obj; 77 | ObjFunction* function; 78 | ObjUpvalue** upvalues; 79 | int upvalueCount; 80 | } ObjClosure; 81 | 82 | typedef struct { 83 | Obj obj; 84 | ObjString* name; 85 | Value initCall; 86 | Table methods; 87 | } ObjClass; 88 | 89 | typedef struct { 90 | Obj obj; 91 | ObjClass* c; 92 | Table fields; 93 | } ObjInstance; 94 | 95 | typedef struct { 96 | Obj obj; 97 | Value reciever; 98 | ObjClosure* method; 99 | } ObjBoundMethod; 100 | 101 | ObjBoundMethod* newBoundMethod(Value reciever, ObjClosure* method); 102 | ObjClass* newClass(ObjString* name); 103 | ObjClosure* newClosure(ObjFunction* function); 104 | ObjFunction* newFunction(ValueArray* constants); 105 | ObjInstance* newInstance(ObjClass* c); 106 | ObjNative* newNative(NativeFunction function); 107 | ObjString* takeString(const char* chars, int length); 108 | ObjString* allocateString(bool ownString, const char* chars, int length); 109 | void resolveStringInterns(size_t offset); 110 | ObjUpvalue* newUpvalue(Value* slot); 111 | void printObject(Value value); 112 | 113 | static inline bool isObjType(Value value, ObjType type){ 114 | return IS_OBJ(value) && AS_OBJ(value)->type == type; 115 | } 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /Olive-bci/chunk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "chunk.h" 5 | #include "memory.h" 6 | #include "vm.h" 7 | 8 | /* Clear debug line info. If the debug flags are uncommented in 'common.h' this function helps to clear any existing 9 | line info in the case of a parsing error. */ 10 | void clearLineInfo() { 11 | currentLine = 0; 12 | operationsPerLine = 0; 13 | indx = 0; 14 | } 15 | 16 | /* Initialize a chunk to hold bytecode. */ 17 | void initChunk(Chunk* chunk, ValueArray* constants) { 18 | chunk->count = 0; 19 | chunk->capacity = 0; 20 | chunk->code = NULL; 21 | chunk->lineArr = NULL; 22 | chunk->codeArr = NULL; 23 | //initValueArray(&chunk->constants); 24 | chunk->constants = constants; 25 | } 26 | 27 | /* Free chunk containing bytecode. */ 28 | void freeChunk(Chunk* chunk) { 29 | FREE_ARRAY(uint8_t, chunk->code, chunk->capacity); 30 | FREE_ARRAY(int, chunk->codeArr, chunk->capacity); 31 | FREE_ARRAY(int, chunk->lineArr, chunk->capacity); 32 | freeValueArray(chunk->constants); 33 | initChunk(chunk, chunk->constants); 34 | } 35 | 36 | /* For REPL persistence. Free's the chunk with an error but preserves the stored value array. That way values declared before the error are preserved. */ 37 | void freeChunkButNotValueArray(Chunk* chunk) { 38 | FREE_ARRAY(uint8_t, chunk->code, chunk->capacity); 39 | FREE_ARRAY(int, chunk->codeArr, chunk->capacity); 40 | FREE_ARRAY(int, chunk->lineArr, chunk->capacity); 41 | chunk->count = 0; 42 | chunk->capacity = 0; 43 | chunk->code = NULL; 44 | chunk->lineArr = NULL; 45 | chunk->codeArr = NULL; 46 | } 47 | 48 | /* Write a byte to a chunk. */ 49 | void writeChunk(Chunk* chunk, uint8_t byte, int line) { 50 | if(chunk->capacity < chunk->count + 1) { 51 | int oldCapacity = chunk->capacity; 52 | chunk->capacity = GROW_CAPACITY(oldCapacity); 53 | chunk->code = GROW_ARRAY(uint8_t, chunk->code, oldCapacity, chunk->capacity); 54 | chunk->lineArr = GROW_ARRAY(int, chunk->lineArr, oldCapacity, chunk->capacity); 55 | chunk->codeArr = GROW_ARRAY(int, chunk->codeArr, oldCapacity, chunk->capacity); 56 | } 57 | 58 | if (line != currentLine) { 59 | currentLine = line; 60 | operationsPerLine = 1; 61 | chunk->lineArr[indx] = line; 62 | temp = indx; 63 | chunk->codeArr[temp] = operationsPerLine; 64 | indx++; 65 | } else { 66 | chunk->codeArr[temp] = ++operationsPerLine; 67 | } 68 | 69 | chunk->code[chunk->count] = byte; 70 | chunk->count++; 71 | } 72 | 73 | /* Add a 'Value' constant to the constant array 'ValueArray'. */ 74 | int addConstant(Chunk* chunk, Value value, bool isConst) { 75 | value.isConst = isConst; 76 | push(value); 77 | writeValueArray(chunk->constants, value); 78 | pop(1); 79 | return chunk->constants->count - 1; 80 | } 81 | 82 | /* Combination of the addConstant() and writeChunk() functions. */ 83 | void writeConstant(Chunk* chunk, Value value, int line) { 84 | int constantIndex = addConstant(chunk, value, false); 85 | 86 | if (constantIndex < 256) { 87 | writeChunk(chunk, OP_CONSTANT, line); 88 | writeChunk(chunk, (uint8_t)constantIndex, line); 89 | } else { 90 | writeChunk(chunk, OP_CONSTANT_LONG, line); 91 | writeChunk(chunk, (uint8_t)(constantIndex & 0xff), line); 92 | writeChunk(chunk, (uint8_t)((constantIndex >> 8) & 0xff), line); 93 | writeChunk(chunk, (uint8_t)((constantIndex >> 16) & 0xff), line); 94 | } 95 | } 96 | 97 | /* Returns the current line of execution for debug and error handling. */ 98 | int getLine(Chunk* chunk, int instructionIndex) { 99 | int sum = 0; 100 | int i; 101 | for (i = 0; sum <= instructionIndex; i++ ) { 102 | sum += chunk->codeArr[i]; 103 | } 104 | 105 | return chunk->lineArr[--i]; 106 | } 107 | -------------------------------------------------------------------------------- /Olive-bci/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "common.h" 7 | #include "vm.h" 8 | #include "dynamic_array.h" 9 | 10 | char* welcome_text = { 11 | " 888\n" 12 | " .d88b. 888 .d888 888 888 .d88b.\n" 13 | "888 888888 888 888 888d8P Y8b\n" 14 | "888 888888 888 888 .d8 88888888\n" 15 | "888 888888 888 888 .d8 8b.\n" 16 | " \"Y88P\" \"Y88P\"\"Y88P\" \"Y8Y\" \"Y8888P\"\n" 17 | }; 18 | 19 | char* version_text1 = {"Olive Interpreter v0.0.1"}; 20 | char* version_text2 = {"\nCopyright(C) 2023 wldfngrs, https://github.com/wldfngrs/Olive\nType \"exit\" or \"quit\" to quit the REPL session."}; 21 | 22 | 23 | bool REPLmode = false; 24 | bool withinREPL = false; 25 | int currentLength = 0; 26 | int prevLength = 0; 27 | 28 | static void printGCCVersionDateAndTime() { 29 | FILE* date = popen("date", "r"); 30 | char* line = NULL; 31 | size_t len = 0; 32 | getline(&line, &len, date); 33 | line[strlen(line) - 1] = '\0'; 34 | printf("\e[1;32m%s", version_text1); 35 | printf(" (%s) ", line); 36 | free(line); 37 | 38 | FILE* gcc = popen("gcc --version | awk '{print $4;exit}'", "r"); 39 | len = 0; 40 | getline(&line, &len, gcc); 41 | line[strlen(line) - 1] = '\0'; 42 | printf(" [GCC %s] ", line); 43 | printf("%s\n\n\e[0m", version_text2); 44 | free(line); 45 | pclose(date); 46 | pclose(gcc); 47 | } 48 | 49 | static bool quit(char* line) { 50 | if ((strcmp(line, "exit\n") * strcmp(line, "quit\n")) == 0) { 51 | return true; 52 | } 53 | 54 | return false; 55 | } 56 | 57 | // modify hardcoded line limit 58 | static void repl() { 59 | REPLmode = true; 60 | printf("%s\n", welcome_text); 61 | printGCCVersionDateAndTime(); 62 | char line[1024]; 63 | DA da_line; 64 | initDynamicArray(&da_line); 65 | for(;;) { 66 | printf("> "); 67 | 68 | if (!fgets(line, sizeof(line), stdin)) { 69 | printf("\n"); 70 | break; 71 | } 72 | 73 | appendDynamicArray(&da_line, line); 74 | currentLength = da_line.count; 75 | 76 | if (quit(line)) { 77 | withinREPL = false; 78 | printf("Exiting Olive...\n\n"); 79 | break; 80 | } 81 | 82 | interpret(da_line.array + prevLength, currentLength - prevLength, REPLmode, &withinREPL); 83 | prevLength = currentLength; 84 | } 85 | 86 | freeDynamicArray(&da_line); 87 | } 88 | 89 | static int checkExtension(const char* path) { 90 | char* extension = strrchr(path, '.'); 91 | if (extension == NULL) { 92 | return -1; 93 | } 94 | 95 | if (memcmp(extension, ".olv", 4) == 0) { 96 | return 0; 97 | } else { 98 | return -1; 99 | } 100 | } 101 | 102 | static char* readFile(const char* path) { 103 | if (checkExtension(path) == -1){ 104 | fprintf(stderr, "\e[1;31mWrong file type. File must be a .olv file\n\e[0m"); 105 | exit(70); 106 | } 107 | 108 | FILE* file = fopen(path, "rb"); 109 | if (file == NULL){ 110 | fprintf(stderr, "\e[1;31mFailed to open file \"%s\".\n\e[0m", path); 111 | exit(74); 112 | } 113 | 114 | fseek(file, 0L, SEEK_END); 115 | size_t fileSize = ftell(file); 116 | rewind(file); 117 | 118 | char* buffer = (char*)malloc(fileSize + 1); 119 | if (buffer == NULL) { 120 | fprintf(stderr, "\e[1;31mNot enough memory to read \"%s\".\n\e[0m", path); 121 | exit(74); 122 | } 123 | size_t bytesRead = fread(buffer, sizeof(char), fileSize, file); 124 | if(bytesRead < fileSize) { 125 | fprintf(stderr, "\e[1;31mCould not read file \"%s\".\n\e[0m", path); 126 | exit(74); 127 | } 128 | buffer[bytesRead] = '\0'; 129 | 130 | fclose(file); 131 | return buffer; 132 | } 133 | 134 | static void runFile(const char* path) { 135 | REPLmode = false; 136 | char* source = readFile(path); 137 | size_t len = strlen(source); 138 | 139 | if (source[len] == '\0') { 140 | len--; 141 | } 142 | 143 | InterpretResult result = interpret(source, len, REPLmode, &withinREPL); 144 | free(source); 145 | 146 | if (result == INTERPRET_COMPILE_ERROR) exit(65); 147 | if (result == INTERPRET_RUNTIME_ERROR) exit(70); 148 | } 149 | 150 | int main(int argc, const char* argv[]) { 151 | initVM(); 152 | 153 | if (argc == 1) { 154 | repl(); 155 | } else if (argc == 2) { 156 | runFile(argv[1]); 157 | } else { 158 | fprintf(stderr, "Usage: olive [path]\n"); 159 | exit(64); 160 | } 161 | freeVM(REPLmode); 162 | return 0; 163 | } 164 | -------------------------------------------------------------------------------- /Olive-bci/value.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "object.h" 5 | #include "memory.h" 6 | #include "value.h" 7 | 8 | void initValueArray(ValueArray* array) { 9 | array->values = NULL; 10 | array->capacity = 0; 11 | array->count = 0; 12 | } 13 | 14 | void writeValueArray(ValueArray* array, Value value) { 15 | if (array->capacity < array->count + 1) { 16 | int oldCapacity = array->capacity; 17 | array->capacity = GROW_CAPACITY(oldCapacity); 18 | array->values = GROW_ARRAY(Value, array->values, oldCapacity, array->capacity); 19 | } 20 | 21 | array->values[array->count] = value; 22 | array->count++; 23 | } 24 | 25 | void freeValueArray(ValueArray* array) { 26 | FREE_ARRAY(Value, array->values, array->capacity); 27 | initValueArray(array); 28 | } 29 | 30 | void printValue(Value value) { 31 | switch(value.type) { 32 | case VAL_BOOL: 33 | printf(AS_BOOL(value) ? "true" : "false"); 34 | break; 35 | case VAL_NULL: printf("null"); break; 36 | case VAL_NUMBER: printf("%g", AS_NUMBER(value)); break; 37 | case VAL_OBJ: printObject(value); break; 38 | case VAL_NL: printf("\\n"); break; 39 | } 40 | } 41 | 42 | bool valuesEqual(Value a, Value b) { 43 | if (a.type != b.type) return false; 44 | 45 | switch(a.type) { 46 | case VAL_BOOL: return AS_BOOL(a) == AS_BOOL(b); 47 | case VAL_NULL: return true; 48 | case VAL_NUMBER: return AS_NUMBER(a) == AS_NUMBER(b); 49 | case VAL_OBJ: { 50 | /*ObjString* aString = AS_STRING(a); 51 | ObjString* bString = AS_STRING(b); 52 | return aString->length == bString->length && memcmp(aString->chars, bString->chars, aString->length) == 0;*/ 53 | return AS_OBJ(a) == AS_OBJ(b); 54 | } 55 | default: 56 | return false; 57 | } 58 | } 59 | 60 | bool valuesNotEqual(Value a, Value b) { 61 | if (a.type != b.type) return false; 62 | 63 | switch(a.type) { 64 | case VAL_BOOL: return AS_BOOL(a) != AS_BOOL(b); 65 | case VAL_NULL: return false; 66 | case VAL_NUMBER: return AS_NUMBER(a) != AS_NUMBER(b); 67 | case VAL_OBJ: { 68 | /*ObjString* aString = AS_STRING(a); 69 | ObjString* bString = AS_STRING(b); 70 | return aString->length != bString->length && memcmp(aString->chars, bString->chars, aString->length) != 0;*/ 71 | return AS_OBJ(a) == AS_OBJ(b); 72 | } 73 | default: 74 | return false; 75 | } 76 | } 77 | 78 | bool valuesGreater(Value a, Value b) { 79 | if (a.type != b.type) return false; 80 | 81 | switch(a.type) { 82 | case VAL_BOOL: return AS_BOOL(a) > AS_BOOL(b); 83 | case VAL_NULL: return false; 84 | case VAL_NUMBER: return AS_NUMBER(a) > AS_NUMBER(b); 85 | case VAL_OBJ: { 86 | ObjString* aString = AS_STRING(a); 87 | ObjString* bString = AS_STRING(b); 88 | return aString->length > bString->length && memcmp(aString->chars, bString->chars, aString->length) > 0; 89 | } 90 | default: 91 | return false; 92 | } 93 | } 94 | 95 | bool valuesGreaterEqual(Value a, Value b) { 96 | if (a.type != b.type) return false; 97 | 98 | switch(a.type) { 99 | case VAL_BOOL: return AS_BOOL(a) >= AS_BOOL(b); 100 | case VAL_NULL: return false; 101 | case VAL_NUMBER: return AS_NUMBER(a) >= AS_NUMBER(b); 102 | case VAL_OBJ: { 103 | ObjString* aString = AS_STRING(a); 104 | ObjString* bString = AS_STRING(b); 105 | return aString->length >= bString->length && memcmp(aString->chars, bString->chars, aString->length) >= 0; 106 | } 107 | default: 108 | return false; 109 | } 110 | } 111 | 112 | bool valuesLess(Value a, Value b) { 113 | if (a.type != b.type) return false; 114 | 115 | switch(a.type) { 116 | case VAL_BOOL: return AS_BOOL(a) < AS_BOOL(b); 117 | case VAL_NULL: return false; 118 | case VAL_NUMBER: return AS_NUMBER(a) < AS_NUMBER(b); 119 | case VAL_OBJ: { 120 | ObjString* aString = AS_STRING(a); 121 | ObjString* bString = AS_STRING(b); 122 | return aString->length < bString->length && memcmp(aString->chars, bString->chars, aString->length) < 0; 123 | } 124 | default: 125 | return false; 126 | } 127 | } 128 | 129 | bool valuesLessEqual(Value a, Value b) { 130 | if (a.type != b.type) return false; 131 | 132 | switch(a.type) { 133 | case VAL_BOOL: return AS_BOOL(a) <= AS_BOOL(b); 134 | case VAL_NULL: return false; 135 | case VAL_NUMBER: return AS_NUMBER(a) <= AS_NUMBER(b); 136 | case VAL_OBJ: { 137 | ObjString* aString = AS_STRING(a); 138 | ObjString* bString = AS_STRING(b); 139 | return aString->length <= bString->length && memcmp(aString->chars, bString->chars, aString->length) <= 0; 140 | } 141 | default: 142 | return false; 143 | } 144 | } 145 | 146 | Value valuesConditional(Value a, Value b, Value conditional) { 147 | switch(AS_BOOL(conditional)) { 148 | case true: 149 | return a; 150 | case false: 151 | return b; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /Olive-bci/object.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "memory.h" 5 | #include "object.h" 6 | #include "value.h" 7 | #include "vm.h" 8 | 9 | #define ALLOCATE_OBJ(type, objectType) \ 10 | (type*)allocateObject(sizeof(type), objectType) 11 | 12 | static Obj* allocateObject(size_t size, ObjType type) { 13 | Obj* object = (Obj*)reallocate(NULL, 0, size); 14 | object->type = type; 15 | object->isMarked = false; 16 | 17 | object->next = vm.objects; 18 | vm.objects = object; 19 | return object; 20 | } 21 | 22 | ObjBoundMethod* newBoundMethod(Value reciever, ObjClosure* method) { 23 | ObjBoundMethod* bound = ALLOCATE_OBJ(ObjBoundMethod, OBJ_BOUND_METHOD); 24 | bound->reciever = reciever; 25 | bound->method = method; 26 | return bound; 27 | } 28 | 29 | ObjClass* newClass(ObjString* name) { 30 | ObjClass* c = ALLOCATE_OBJ(ObjClass, OBJ_CLASS); 31 | initTable(&c->methods); 32 | c->name = name; 33 | c->initCall = NULL_VAL; 34 | return c; 35 | } 36 | 37 | ObjClosure* newClosure(ObjFunction* function) { 38 | ObjUpvalue** upvalues = ALLOCATE(ObjUpvalue*, function->upvalueCount); 39 | 40 | for (int i = 0; i < function->upvalueCount; i++) { 41 | upvalues[i] = NULL; 42 | } 43 | 44 | ObjClosure* closure = ALLOCATE_OBJ(ObjClosure, OBJ_CLOSURE); 45 | closure->function = function; 46 | closure->upvalues = upvalues; 47 | closure->upvalueCount = function->upvalueCount; 48 | return closure; 49 | } 50 | 51 | ObjFunction* newFunction(ValueArray* constants) { 52 | ObjFunction* function = ALLOCATE_OBJ(ObjFunction, OBJ_FUNCTION); 53 | 54 | function->arity = 0; 55 | function->upvalueCount = 0; 56 | function->name = NULL; 57 | initChunk(&function->chunk, constants); 58 | return function; 59 | } 60 | 61 | ObjInstance* newInstance(ObjClass* c) { 62 | ObjInstance* instance = ALLOCATE_OBJ(ObjInstance, OBJ_INSTANCE); 63 | instance->c = c; 64 | initTable(&instance->fields); 65 | return instance; 66 | } 67 | 68 | ObjNative* newNative(NativeFunction function) { 69 | ObjNative* native = ALLOCATE_OBJ(ObjNative, OBJ_NATIVE); 70 | native->function = function; 71 | return native; 72 | } 73 | 74 | static uint32_t hashString(const char* key, int length) { 75 | uint32_t hash = 2166136261u; 76 | 77 | for (int i = 0; i < length; i++) { 78 | hash ^= key[i]; 79 | hash *= 16777619; 80 | } 81 | 82 | return hash; 83 | } 84 | 85 | // does not own their copy of character array. Points back to source string but doesn't free source string 86 | ObjString* allocateString(bool ownString, const char* chars, int length) { 87 | // Check whether string is stored in string table already 88 | uint32_t hash = hashString(chars, length); 89 | ObjString* interned = tableFindString(&vm.strings, chars, length, hash); 90 | if (interned != NULL) { // if string is stored already in string table 91 | if (ownString) { 92 | FREE_ARRAY(char, (char*)chars, length + 1); 93 | } 94 | return interned; 95 | } 96 | 97 | // ObjString points directly into the source code. 98 | ObjString* string = (ObjString*)allocateObject(sizeof(ObjString), OBJ_STRING); 99 | string->length = length; 100 | string->chars = chars; 101 | string->ownString = ownString; 102 | string->hash = hash; 103 | 104 | push(OBJ_VAL(string)); 105 | tableSet(&vm.strings, &OBJ_KEY(string), NULL_VAL); 106 | pop(1); 107 | 108 | return string; 109 | } 110 | 111 | void resolveStringInterns(size_t offset) { 112 | Obj* object = vm.objects; 113 | 114 | /* Recall that Obj linked list of VM objects links backwards. i.e vm.objects points to the most recently added object and not the oldest added object. This function therfore starts checking from the most recently added object (and resolves it's chars if it's an OBJ_STRING) and stops right at the OBJ_NATIVE object. The OBJ_NATIVE object is one of the first objects to be added to the VM and preceeding it are the native functions and the init() constructor object. Those objects contain interned strings but do not need to be resolved as their references are not contained in the reallocated buffer */ 115 | 116 | while (object->type != OBJ_NATIVE) { 117 | if (object->type == OBJ_STRING) { 118 | ObjString* string = (ObjString*)object; 119 | /* point to the location of chars in the newly reallocated buffer*/ 120 | string->chars += offset; 121 | } 122 | object = object->next; 123 | } 124 | } 125 | 126 | ObjUpvalue* newUpvalue(Value* slot) { 127 | ObjUpvalue* upvalue = ALLOCATE_OBJ(ObjUpvalue, OBJ_UPVALUE); 128 | upvalue->closed = NULL_VAL; 129 | upvalue->location = slot; 130 | upvalue->next = NULL; 131 | return upvalue; 132 | } 133 | 134 | // own their copy of the character array. Points back to heap allocated string and frees string. 135 | ObjString* takeString(const char* chars, int length) { 136 | return allocateString(true, chars, length); 137 | } 138 | 139 | static void printFunction(ObjFunction* function) { 140 | if (function->name == NULL) { 141 | printf("