├── examples ├── minima.exe ├── bug.txt ├── symbol.min ├── queue.txt ├── bellman-ford.txt ├── random.min ├── symbol │ ├── util.txt │ ├── compare.txt │ ├── eval.txt │ ├── print.txt │ ├── derive.txt │ ├── sort.txt │ ├── core.txt │ ├── match.txt │ └── simplify.txt ├── prime.txt ├── inheritance.txt ├── map.min ├── union.min ├── conways.txt └── list.min ├── src ├── include │ ├── io.h │ ├── hash.h │ ├── statements.h │ ├── scanner.h │ ├── object.h │ ├── collection.h │ ├── variable.h │ ├── error.h │ ├── garbage.h │ ├── opcodes.h │ ├── tokens.h │ ├── operators.h │ ├── debug.h │ ├── record.h │ ├── builtins.h │ ├── machine.h │ ├── globals.h │ ├── compiler.h │ ├── value.h │ └── chunk.h ├── collection.c ├── value.c ├── hash.c ├── object.c ├── variable.c ├── record.c ├── chunk.c ├── globals.c ├── io.c ├── machine.c ├── source.c ├── builtins.c ├── operators.c ├── garbage.c ├── compiler.c ├── scanner.c ├── debug.c ├── opcodes.c └── statments.c ├── Makefile ├── stl ├── random.min ├── buffer.min ├── hashset.min ├── map.min ├── union.min └── list.min ├── README.md └── LICENSE /examples/minima.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheRealMichaelWang/minima/HEAD/examples/minima.exe -------------------------------------------------------------------------------- /examples/bug.txt: -------------------------------------------------------------------------------- 1 | include "symbol.min" 2 | set x to new eq_var("x") 3 | set expr to x + 1 + x + 1 4 | set expr to goproc simplify(expr) -------------------------------------------------------------------------------- /examples/symbol.min: -------------------------------------------------------------------------------- 1 | include "symbol/core.txt" 2 | include "symbol/print.txt" 3 | include "symbol/eval.txt" 4 | include "symbol/derive.txt" 5 | include "symbol/match.txt" -------------------------------------------------------------------------------- /src/include/io.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef IO_H 4 | #define IO_H 5 | 6 | #include "value.h" 7 | #include "error.h" 8 | 9 | void print_value(struct value value, const int print_mode); 10 | 11 | #endif // !IO_H -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | define newline 2 | 3 | 4 | endef 5 | 6 | C_SOURCES := $(notdir $(wildcard src/*.c)) 7 | 8 | all: 9 | $(foreach C_SOURCE, $(C_SOURCES), gcc src/$(C_SOURCE) -o bin/$(C_SOURCE).o -c -Ofast$(newline)) 10 | gcc -o minima.exe $(wildcard bin/*.c.o) -------------------------------------------------------------------------------- /examples/queue.txt: -------------------------------------------------------------------------------- 1 | include "list.min" 2 | record queue extends list { 3 | proc enqueue(value) { 4 | goproc push_back as this.base(value) 5 | } 6 | proc dequeue { 7 | return goproc pop_front as this.base 8 | } 9 | } 10 | set i to 1000 11 | set queue to new queue 12 | while dec i { 13 | goproc enqueue as queue(i) 14 | } -------------------------------------------------------------------------------- /src/include/hash.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef HASH_H 4 | #define HASH_H 5 | 6 | #include 7 | #include "value.h" 8 | 9 | const uint64_t hash(const char* str, const uint64_t len); 10 | const uint64_t combine_hash(const uint64_t hash_a, const uint64_t hash_b); 11 | 12 | const uint64_t value_hash(struct value value); 13 | 14 | #endif // !HASH_H -------------------------------------------------------------------------------- /examples/bellman-ford.txt: -------------------------------------------------------------------------------- 1 | include "list.min" 2 | 3 | record edge { 4 | src 5 | dest 6 | weight 7 | 8 | proc init(src, dest, weight) { 9 | set this.src to src 10 | set this.dest to dest 11 | set this.weight to weight 12 | } 13 | } 14 | 15 | record node { 16 | distance 17 | edges 18 | 19 | proc init { 20 | set this.distance to 0 21 | set this.edges to new list 22 | } 23 | 24 | proc init ( -------------------------------------------------------------------------------- /src/include/statements.h: -------------------------------------------------------------------------------- 1 | #ifndef STATMENTS_H 2 | #define STATMENTS_H 3 | 4 | #include "compiler.h" 5 | #include "debug.h" 6 | 7 | const int compile_value(struct compiler* compiler, struct chunk_builder* builder, const int optimize_copy, uint64_t optimize_goto); 8 | const int compile_statement(struct compiler* compiler, struct chunk_builder* builder, struct loc_table* loc_table, const uint64_t callee, const int encapsulated, uint64_t proc_encapsulated); 9 | 10 | #endif // !STATMENTS_H -------------------------------------------------------------------------------- /examples/random.min: -------------------------------------------------------------------------------- 1 | rem Minima, written by Michael Wang 2020-21 2 | 3 | rem Purpose: Implements basic random value generation procedures 4 | 5 | rem Purpose: Generates a random value between a minimum and maximum 6 | rem Note that while the minimum is included within the output range, the maximum isn't 7 | proc random(min, max) { 8 | set range to max - min 9 | return (extern random * range) + min 10 | } 11 | 12 | rem Purpose: Generates a random value that's either true/false 13 | proc randbit { 14 | return extern random < 0.5 15 | } -------------------------------------------------------------------------------- /src/include/scanner.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef SCANNER_H 4 | #define SCANNER_H 5 | 6 | #include 7 | #include "tokens.h" 8 | 9 | struct scanner { 10 | const char* source; 11 | const char* file; 12 | uint64_t size, pos, row, col; 13 | 14 | char last_char; 15 | char last_err; 16 | }; 17 | 18 | void init_scanner(struct scanner* scanner, const char* source, const char* file); 19 | 20 | struct token scanner_read_tok(struct scanner* scanner); 21 | 22 | const int scanner_read_str(struct scanner* scanner, char* str, const int data_mode); 23 | 24 | #endif // !SCANNER_H 25 | -------------------------------------------------------------------------------- /examples/symbol/util.txt: -------------------------------------------------------------------------------- 1 | include "symbol/core.txt" 2 | 3 | rem Purpose: Checks if a value is a numerical. If so, it converts it to it's eq_num representation. 4 | proc num_check(num) { 5 | if extern typeof(num) == extern typeof(0) { 6 | return new eq_num(num) 7 | } 8 | return num 9 | } 10 | 11 | proc log(val, base) { 12 | return new eq_op_log(val, base) 13 | } 14 | 15 | proc log(val) { 16 | return new eq_op_log(val, new eq_var("e")) 17 | } 18 | 19 | proc abs(val) { 20 | return new eq_op_abs(val) 21 | } 22 | 23 | proc simplify(expr) { 24 | set simplifier to new simplifier 25 | return goproc apply as simplifier(expr) 26 | } -------------------------------------------------------------------------------- /stl/random.min: -------------------------------------------------------------------------------- 1 | rem Minima, written by Michael Wang 2020-21 2 | 3 | rem Purpose: Implements basic random value generation procedures 4 | 5 | rem Purpose: Generates a random value between a minimum and maximum 6 | rem Note that while the minimum is included within the output range, the maximum isn't 7 | proc random(min, max) { 8 | set range to max - min 9 | return (extern random * range) + min 10 | } 11 | 12 | rem Purpose: Generates a random value that's either true/false 13 | proc randbit { 14 | return extern random < 0.5 15 | } 16 | 17 | rem Purpose: Randomly selects an item from an collection/array 18 | proc select_random(array) { 19 | return array[goproc random(0, extern len(array))] 20 | } -------------------------------------------------------------------------------- /src/include/object.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef OBJECT_H 4 | #define OBJECT_H 5 | 6 | #include "collection.h" 7 | #include "record.h" 8 | 9 | struct object { 10 | union object_ptr 11 | { 12 | struct collection* collection; 13 | struct record* record; 14 | }ptr; 15 | 16 | enum obj_type 17 | { 18 | OBJ_TYPE_COL, 19 | OBJ_TYPE_REC 20 | } type; 21 | }; 22 | 23 | void init_object_col(struct object* object, struct collection* collection); 24 | void init_object_rec(struct object* object, struct record* record); 25 | void free_object(struct object* object); 26 | 27 | const int object_compare(const struct object* a, const struct object* b); 28 | struct value**object_get_children(const struct object* object, uint64_t* size); 29 | 30 | #endif // !OBJECT_H -------------------------------------------------------------------------------- /src/include/collection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef COLLECTION_H 4 | #define COLLECTION_H 5 | 6 | #include 7 | 8 | struct value; //foward declare value 9 | 10 | struct collection { 11 | struct value** inner_collection; 12 | uint64_t size; 13 | }; 14 | 15 | //initializes a new collection instance. Note that the collection's elements remain uninitialized 16 | const int init_collection(struct collection* collection, uint64_t size); 17 | 18 | //frees up a collection instances resources 19 | void free_collection(struct collection* collection); 20 | 21 | //compares a collection - returns 0 if they are the same, returns the difference otherwise 22 | const int collection_compare(const struct collection* a, const struct collection* b); 23 | 24 | #endif // !COLLECTION_H -------------------------------------------------------------------------------- /src/collection.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "include/error.h" 3 | #include "include/value.h" 4 | #include "include/collection.h" 5 | 6 | const int init_collection(struct collection* collection, uint64_t size) { 7 | collection->inner_collection = malloc(size * sizeof(struct value*)); 8 | ERROR_ALLOC_CHECK(collection->inner_collection); 9 | collection->size = size; 10 | return 1; 11 | } 12 | 13 | void free_collection(struct collection* collection) { 14 | free(collection->inner_collection); 15 | } 16 | 17 | const int collection_compare(const struct collection* a, const struct collection* b) { 18 | if (a->size != b->size) 19 | return 0; 20 | for (uint64_t i = 0; i < a->size; i++) { 21 | if (compare_value(a->inner_collection[i], b->inner_collection[i])) 22 | return 0; 23 | } 24 | return 1; 25 | } -------------------------------------------------------------------------------- /src/include/variable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "value.h" 4 | #include "garbage.h" 5 | 6 | #ifndef VARIABLE_H 7 | #define VARIABLE_H 8 | 9 | #include 10 | 11 | struct var_context { 12 | struct var_bucket 13 | { 14 | uint64_t identifier; 15 | struct value* value; 16 | int set_flag; 17 | }* buckets; 18 | 19 | uint64_t hash_limit; 20 | 21 | struct garbage_collector* garbage_collector; 22 | }; 23 | 24 | const int init_var_context(struct var_context* var_context, struct garbage_collector* garbage_collector); 25 | const int free_var_context(struct var_context* var_context); 26 | 27 | struct value*retrieve_var(struct var_context* var_context, const uint64_t id); 28 | const int emplace_var(struct var_context* var_context,const uint64_t id, struct value*value); 29 | 30 | #endif // !VARIABLE_H 31 | -------------------------------------------------------------------------------- /src/include/error.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef ERROR_H 4 | #define ERROR_H 5 | 6 | enum error { 7 | ERROR_SUCCESS, 8 | ERROR_OUT_OF_MEMORY, 9 | ERROR_INSUFFICIENT_EVALS, 10 | ERROR_INSUFFICIENT_CALLS, 11 | ERROR_UNRECOGNIZED_OPCODE, 12 | 13 | ERROR_LABEL_REDEFINE, 14 | ERROR_LABEL_UNDEFINED, 15 | ERROR_RECORD_REDEFINE, 16 | ERROR_RECORD_UNDEFINED, 17 | ERROR_PROPERTY_REDEFINE, 18 | ERROR_PROPERTY_UNDEFINED, 19 | 20 | ERROR_UNEXPECTED_TYPE, 21 | ERROR_INDEX_OUT_OF_RANGE, 22 | ERROR_STACK_OVERFLOW, 23 | ERROR_VARIABLE_UNDEFINED, 24 | 25 | ERROR_UNRECOGNIZED_TOKEN, 26 | ERROR_UNRECOGNIZED_CONTROL_SEQ, 27 | ERROR_UNEXPECTED_CHAR, 28 | ERROR_UNEXPECTED_TOKEN, 29 | ERROR_CANNOT_OPEN_FILE 30 | }; 31 | 32 | #define ERROR_ALLOC_CHECK(PTR) if(!(PTR)) { return 0; } 33 | 34 | const int error_info(enum error error); 35 | 36 | #endif // !ERROR_H 37 | -------------------------------------------------------------------------------- /examples/symbol/compare.txt: -------------------------------------------------------------------------------- 1 | include "symbol/core.txt" 2 | 3 | record eq_leaf { 4 | proc compare(obj) { 5 | if extern typeof(obj) != extern typeof(this) { 6 | return this.obj == obj 7 | } 8 | return this.obj == obj.obj 9 | } 10 | 11 | proc == (obj) { 12 | return goproc compare as this(obj) 13 | } 14 | 15 | proc != (obj) { 16 | return !goproc compare as this(obj) 17 | } 18 | } 19 | 20 | record eq_bin_op { 21 | proc compare(obj) { 22 | if extern typeof(obj) != extern typeof(this) { 23 | return false 24 | } 25 | return goproc compare as this.left(obj.left) and goproc compare as this.right(obj.right) 26 | } 27 | } 28 | 29 | record eq_uni_op { 30 | proc compare(obj) { 31 | if extern typeof(obj) != extern typeof(this) { 32 | return false 33 | } 34 | return goproc compare as this.operand(obj.operand) 35 | } 36 | } 37 | 38 | record match { 39 | proc compare(obj) { 40 | return false 41 | } 42 | } -------------------------------------------------------------------------------- /src/value.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "include/value.h" 4 | 5 | void init_obj_value(struct value* value, struct object obj) { 6 | value->gc_flag = GARBAGE_CONSTANT; 7 | value->type = VALUE_TYPE_OBJ; 8 | value->payload.object = obj; 9 | } 10 | 11 | const double compare_value(const struct value*a, const struct value*b) { 12 | if (a->type != b->type) 13 | return (double)a->type - (double)b->type; 14 | 15 | switch (a->type) 16 | { 17 | case VALUE_TYPE_NUM: 18 | return a->payload.numerical - b->payload.numerical; 19 | case VALUE_TYPE_CHAR: 20 | return (double)a->payload.character - (double)b->payload.character; 21 | case VALUE_TYPE_ID: 22 | return a->payload.identifier != b->payload.identifier; 23 | case VALUE_TYPE_OBJ: 24 | return object_compare(&a->payload.object, &b->payload.object); 25 | } 26 | return 0; 27 | } 28 | 29 | void free_value(struct value* value) { 30 | if (value->type == VALUE_TYPE_OBJ) 31 | free_object(&value->payload.object); 32 | } -------------------------------------------------------------------------------- /examples/prime.txt: -------------------------------------------------------------------------------- 1 | include "buffer.min" 2 | 3 | proc abs(n) { 4 | return n * (1 - 2 * (n < 0)) 5 | } 6 | 7 | proc is_prime_sure(n) { 8 | if n < 2 { 9 | return false 10 | } 11 | if n - (n % 1) != n { 12 | return false 13 | } 14 | 15 | set i to 2 16 | 17 | while i <= n^0.5 { 18 | if n % i == 0 { 19 | return false 20 | } 21 | inc i 22 | } 23 | return true 24 | } 25 | 26 | proc sieve(n) { 27 | set list_prime to alloc[n + 1] 28 | goproc memset(list_prime, true) 29 | set list_prime[0] to false 30 | set list_prime[1] to false 31 | set i to 2 32 | 33 | set primes_count to n - 1 34 | 35 | while i <= n^0.5 { 36 | if list_prime[i] { 37 | set j to i*i 38 | while j <= n { 39 | if list_prime[j] { 40 | dec primes_count 41 | } 42 | set list_prime[j] to false 43 | set j to j + i 44 | } 45 | } 46 | inc i 47 | } 48 | 49 | set j to 0 50 | set i to n + 1 51 | 52 | set primes to alloc[primes_count] 53 | while dec i { 54 | if list_prime[i] { 55 | set primes[inc j] to i 56 | } 57 | } 58 | 59 | return primes 60 | } -------------------------------------------------------------------------------- /examples/inheritance.txt: -------------------------------------------------------------------------------- 1 | record person { rem this is the base-class 2 | name 3 | age 4 | 5 | proc init(name, age) { 6 | set this.name to name 7 | set this.age to age 8 | } 9 | 10 | proc can_drink { 11 | return this.age > 21 12 | } 13 | 14 | proc print { 15 | extern printl("Name: ", this.name) 16 | extern printl("Age: ", this.age) 17 | } 18 | } 19 | 20 | record student extends person { rem this is the derived-class 21 | grade 22 | gpa 23 | 24 | proc init(name, age, grade, gpa) { 25 | goproc init as this.base(name, age) 26 | set this.grade to grade 27 | set this.gpa to gpa 28 | } 29 | 30 | proc is_smart { 31 | return this.gpa >= 4.0 32 | } 33 | 34 | proc print { 35 | goproc print as this.base 36 | extern printl("Grade: ", this.grade) 37 | extern printl("GPA: ", this.gpa) 38 | } 39 | } 40 | 41 | set tim to new student("Tim", 15, 9, 4.0) 42 | 43 | external printl(goproc can_drink as tim) rem Returns false - Tim is a person, so we can call can_drink 44 | external printl(goproc is_smart as tim) rem Returns true 45 | -------------------------------------------------------------------------------- /src/include/garbage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef GARBAGE_H 4 | #define GARBAGE_H 5 | 6 | #include 7 | #include "value.h" 8 | 9 | struct garbage_frame { 10 | struct value** to_collect; 11 | struct value** to_trace; 12 | uint64_t collect_values; 13 | uint64_t trace_values; 14 | }; 15 | 16 | struct garbage_collector { 17 | struct garbage_frame* frame_stack; 18 | struct value** value_stack; 19 | struct value** trace_stack; 20 | uint64_t frames, to_collect, to_trace; 21 | }; 22 | 23 | const int init_gcollect(struct garbage_collector* garbage_collector); 24 | void free_gcollect(struct garbage_collector* garbage_collector); 25 | 26 | const int gc_register_trace(struct garbage_collector* garbage_collector, const struct value* value); 27 | const int gc_register_children(struct garbage_collector* garbage_collector, struct value* head); 28 | struct value*gc_register_value(struct garbage_collector* garbage_collector, struct value value); 29 | 30 | void gc_new_frame(struct garbage_collector* garbage_collector); 31 | const int gc_collect(struct garbage_collector* garbage_collector); 32 | 33 | #endif // !GARBAGE_H 34 | -------------------------------------------------------------------------------- /src/include/opcodes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef OPCODE_H 4 | #define OPCODE_H 5 | 6 | //machine op-codes 7 | enum op_code { 8 | MACHINE_LOAD_CONST, 9 | MACHINE_LOAD_VAR, 10 | 11 | MACHINE_STORE_VAR, 12 | 13 | MACHINE_EVAL_BIN_OP, 14 | MACHINE_EVAL_UNI_OP, 15 | 16 | MACHINE_END_SKIP, 17 | 18 | MACHINE_MARK, 19 | 20 | MACHINE_GOTO, 21 | MACHINE_GOTO_AS, 22 | MACHINE_RETURN_GOTO, 23 | MACHINE_LABEL, 24 | 25 | MACHINE_COND_SKIP, 26 | MACHINE_COND_RETURN, 27 | 28 | MACHINE_FLAG, 29 | MACHINE_RESET_FLAG, 30 | MACHINE_FLAG_SKIP, 31 | 32 | MACHINE_NEW_FRAME, 33 | MACHINE_CLEAN, 34 | MACHINE_TRACE, 35 | MACHINE_POP, 36 | 37 | MACHINE_BUILD_COL, 38 | MACHINE_BUILD_PROTO, 39 | MACHINE_BUILD_RECORD, 40 | MACHINE_INHERIT_REC, 41 | 42 | MACHINE_GET_INDEX, 43 | MACHINE_SET_INDEX, 44 | MACHINE_GET_PROPERTY, 45 | MACHINE_SET_PROPERTY, 46 | 47 | MACHINE_CALL_EXTERN, 48 | MACHINE_END //last one isn't really an opcode, just an end signal 49 | }; 50 | 51 | struct machine; 52 | struct chunk; 53 | 54 | const int handle_opcode(enum op_code op, struct machine* machine, struct chunk* chunk); 55 | 56 | #endif // !OPCODE_H -------------------------------------------------------------------------------- /stl/buffer.min: -------------------------------------------------------------------------------- 1 | rem Minima, written by Michael Wang 2020-21 2 | 3 | rem Purpose: Implements basic array handling procedures 4 | 5 | rem Purpose: Creates a copied, extended array with references to the original array's items. 6 | rem Returns a new buffer. 7 | proc realloc(buffer, len) { 8 | if len < extern len(buffer) { 9 | return null 10 | } 11 | 12 | set newbuffer to alloc[len] 13 | 14 | set i to extern len(buffer) 15 | while dec i { 16 | set newbuffer[i] to buffer[i] 17 | } 18 | return newbuffer 19 | } 20 | 21 | rem Purpose: Repeatedley sets a value to an array 22 | proc memset(buffer, value) { 23 | set i to extern len(buffer) 24 | while dec i { 25 | set buffer[i] to value 26 | } 27 | } 28 | 29 | rem Purpose: Copies elements to another collection/array 30 | proc memcpy(dest, src, dest_offset, src_offset, length){ 31 | set i to length 32 | while dec i { 33 | set dest[i + dest_offset] to src[i + src_offset] 34 | } 35 | } 36 | 37 | rem Purpose: Reverses the order of the elements in an array 38 | proc reverse(buffer) { 39 | set len to extern len(buffer) 40 | set i to len / 2 41 | while dec i { 42 | set temp to ref buffer[i] 43 | set buffer[i] to ref buffer[len - i - 1] 44 | set buffer[len - i - 1] to ref temp 45 | } 46 | } -------------------------------------------------------------------------------- /src/include/tokens.h: -------------------------------------------------------------------------------- 1 | #ifndef TOKENS_H 2 | #define TOKENS_H 3 | 4 | #include "operators.h" 5 | #include "value.h" 6 | 7 | #define IS_VALUE_TOK(TOK) ((unsigned int)TOK.type < 13) 8 | #define IS_STATMENT_TOK(TOK) ((unsigned int)(TOK.type - 10) < 11) 9 | 10 | struct token { 11 | enum token_type { 12 | TOK_PRIMATIVE, //values 13 | TOK_STR, 14 | TOK_REF, 15 | TOK_IDENTIFIER, 16 | TOK_OPEN_BRACE, 17 | TOK_NEW, 18 | TOK_OPEN_PAREN, 19 | TOK_ALLOC, 20 | TOK_BINARY_OP, 21 | TOK_HASHTAG, 22 | 23 | TOK_UNARY_OP, //statments/values 24 | TOK_GOTO, 25 | TOK_EXTERN, 26 | 27 | TOK_SET, //statments 28 | TOK_IF, 29 | TOK_WHILE, 30 | TOK_DO, 31 | TOK_PROC, 32 | TOK_RECORD, 33 | TOK_RETURN, 34 | TOK_INCLUDE, 35 | 36 | TOK_ELSE, 37 | TOK_ELIF, 38 | TOK_TO, 39 | TOK_AS, 40 | TOK_EXTEND, 41 | TOK_REMARK, 42 | TOK_CLOSE_PAREN, 43 | TOK_CLOSE_BRACE, 44 | TOK_OPEN_BRACKET, 45 | TOK_CLOSE_BRACKET, 46 | TOK_COMMA, 47 | TOK_PERIOD, 48 | TOK_END, 49 | TOK_ERROR 50 | } type; 51 | 52 | union token_type_payload 53 | { 54 | enum binary_operator bin_op; 55 | enum unary_operator uni_op; 56 | 57 | struct value primative; 58 | 59 | uint64_t identifier; 60 | } payload; 61 | }; 62 | 63 | #endif // !TOKENS_H -------------------------------------------------------------------------------- /src/hash.c: -------------------------------------------------------------------------------- 1 | #include "include/object.h" 2 | #include "include/hash.h" 3 | 4 | const uint64_t hash(const char* str, const uint64_t len) { 5 | uint64_t hash = 5381; //DO NOT CHANGE THE HASH SIZE, otherwise pre-computed hashes WILL NOT WORK 6 | for (uint64_t i = 0; i < len; i++) 7 | hash = (hash << 5) + hash + str[i]; 8 | return hash; 9 | } 10 | 11 | const uint64_t combine_hash(const uint64_t hash_a, const uint64_t hash_b) { 12 | uint64_t hash = 5381; 13 | hash = (hash << 5) + hash + hash_a; 14 | hash = (hash << 5) + hash + hash_b; 15 | return hash; 16 | } 17 | 18 | const uint64_t value_hash(struct value value) { 19 | if (value.type == VALUE_TYPE_NULL) 20 | return 0; 21 | else if (value.type == VALUE_TYPE_CHAR) 22 | return combine_hash(VALUE_TYPE_CHAR, value.payload.character); 23 | else if (value.type == VALUE_TYPE_NUM) 24 | return combine_hash(VALUE_TYPE_NUM, value.payload.numerical); 25 | else { 26 | struct object object = value.payload.object; 27 | uint64_t size, hash; 28 | struct value**children = object_get_children(&object, &size); 29 | hash = value.type; 30 | for (uint_fast64_t i = 0; i < size; i++) 31 | hash = combine_hash(hash, value_hash(*children[i])); 32 | return hash; 33 | } 34 | } -------------------------------------------------------------------------------- /examples/symbol/eval.txt: -------------------------------------------------------------------------------- 1 | include "map.min" 2 | include "symbol/core.txt" 3 | 4 | record eq_var { 5 | proc eval(varmap) { 6 | return goproc find as varmap(this.obj) 7 | } 8 | } 9 | 10 | record eq_num { 11 | proc eval(varmap) { 12 | return this.obj 13 | } 14 | } 15 | 16 | record eq_op_add { 17 | proc eval(varmap) { 18 | return goproc eval as this.left(varmap) + goproc eval as this.right(varmap) 19 | } 20 | } 21 | 22 | record eq_op_subtract { 23 | proc eval(varmap) { 24 | return goproc eval as this.left(varmap) - goproc eval as this.right(varmap) 25 | } 26 | } 27 | 28 | record eq_op_multiply { 29 | proc eval(varmap) { 30 | return goproc eval as this.left(varmap) * goproc eval as this.right(varmap) 31 | } 32 | } 33 | 34 | record eq_op_divide { 35 | proc eval(varmap) { 36 | return goproc eval as this.left(varmap) / goproc eval as this.right(varmap) 37 | } 38 | } 39 | 40 | record eq_op_mod { 41 | proc eval(varmap) { 42 | return goproc eval as this.left(varmap) + goproc eval as this.right(varmap) 43 | } 44 | } 45 | 46 | record eq_op_pow { 47 | proc eval(varmap) { 48 | return goproc eval as this.left(varmap) ^ goproc eval as this.right(varmap) 49 | } 50 | } 51 | 52 | record eq_op_abs { 53 | proc eval(varmap) { 54 | return extern abs(goproc eval as this.operand(varmap)) 55 | } 56 | } -------------------------------------------------------------------------------- /src/include/operators.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef OPERATORS_H 4 | #define OPERATORS_H 5 | 6 | #include "value.h" 7 | 8 | enum binary_operator { 9 | OPERATOR_EQUALS, 10 | OPERATOR_NOT_EQUAL, 11 | OPERATOR_MORE, 12 | OPERATOR_LESS, 13 | OPERATOR_MORE_EQUAL, 14 | OPERATOR_LESS_EQUAL, 15 | OPERATOR_AND, 16 | OPERATOR_OR, 17 | OPERATOR_ADD, 18 | OPERATOR_SUBTRACT, 19 | OPERATOR_MULTIPLY, 20 | OPERATOR_DIVIDE, 21 | OPERATOR_MODULO, 22 | OPERATOR_POWER 23 | }; 24 | 25 | enum unary_operator { 26 | OPERATOR_COPY, 27 | OPERATOR_INVERT, 28 | OPERATOR_NEGATE, 29 | OPERATOR_ALLOC, 30 | OPERATOR_INCREMENT, 31 | OPERATOR_DECRIMENT 32 | }; 33 | 34 | enum op_precedence { 35 | PREC_BEGIN, 36 | PREC_LOGIC, 37 | PREC_COMP, 38 | PREC_ADD, 39 | PREC_MULTIPLY, 40 | PREC_EXP, 41 | }; 42 | 43 | static enum op_precedence op_precedence[14] = { 44 | PREC_COMP, 45 | PREC_COMP, 46 | PREC_COMP, 47 | PREC_COMP, 48 | PREC_COMP, 49 | PREC_COMP, 50 | PREC_LOGIC, 51 | PREC_LOGIC, 52 | PREC_ADD, 53 | PREC_ADD, 54 | PREC_MULTIPLY, 55 | PREC_MULTIPLY, 56 | PREC_MULTIPLY, 57 | PREC_EXP 58 | }; 59 | 60 | struct value* invoke_binary_op(enum binary_operator operator, struct value* a, struct value* b, struct machine* machine); 61 | struct value* invoke_unary_op(enum unary_operator operator, struct value* a, struct machine* machine); 62 | 63 | #endif // !OPERATORS -------------------------------------------------------------------------------- /src/include/debug.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef DEBUG_H 4 | #define DEBUG_H 5 | 6 | #include "scanner.h" 7 | #include "compiler.h" 8 | #include "chunk.h" 9 | #include "machine.h" 10 | 11 | struct loc_table { 12 | struct loc_info { 13 | uint64_t chunk_loc, src_row, src_col; 14 | const char* file; 15 | int offset_flag, keep_flag; 16 | }* locations; 17 | 18 | char** to_free; 19 | const char** file_stack; 20 | 21 | uint64_t loc_entries, alloced_locs, files, alloced_files, current_file, global_offset; 22 | int finilazed_flag; 23 | }; 24 | 25 | const int init_loc_table(struct loc_table* loc_table, char* initial_file); 26 | void free_loc_table(struct loc_table* loc_table); 27 | 28 | const int loc_table_include(struct loc_table* loc_table, char* file); 29 | void loc_table_uninclude(struct loc_table* loc_table); 30 | 31 | const int loc_table_insert(struct loc_table* loc_table, struct compiler* compiler, struct chunk_builder* builder); 32 | void loc_table_finalize(struct loc_table* loc_table, struct compiler* compiler, int keep); 33 | void loc_table_dispose(struct loc_table* loc_table); 34 | void loc_table_print(struct loc_table* loc_table, const uint64_t chunk_loc); 35 | 36 | void debug_print_scanner(struct scanner scanner); 37 | void debug_print_dump(struct chunk chunk); 38 | void debug_print_trace(struct machine* machine, struct loc_table* table, uint64_t last_pos); 39 | 40 | #endif // !DEBUG_H -------------------------------------------------------------------------------- /stl/hashset.min: -------------------------------------------------------------------------------- 1 | rem Minima, written by Michael Wang 2020-21 2 | 3 | rem Purpose: Implements a basic hash-set 4 | 5 | include "list.min" 6 | 7 | record hashset { 8 | keys 9 | 10 | proc init(capacity) { 11 | set this.keys to alloc[capacity] 12 | } 13 | 14 | proc init { 15 | goproc init as this(100) 16 | } 17 | 18 | proc rehash(new_capacity) { 19 | set old_cap to extern len(this.keys) 20 | 21 | set i to old_cap 22 | set found_keys to new list 23 | while dec i { 24 | if this.keys[i] { 25 | goproc push_back as found_keys(this.keys[i]) 26 | } 27 | } 28 | 29 | set this.keys to alloc[new_capacity] 30 | while found_keys.size { 31 | goproc insert_key as this(goproc pop_front as found_keys) 32 | } 33 | } 34 | 35 | proc insert_key(key) { 36 | set cap to extern len(this.keys) 37 | set i to key % cap 38 | 39 | while i < cap { 40 | if !this.keys[i] { 41 | set this.keys[i] to key 42 | return null 43 | } 44 | inc i 45 | } 46 | 47 | goproc rehash as this(2 * cap) 48 | goproc insert_key as this(key) 49 | } 50 | 51 | proc insert(object) { 52 | goproc insert_key as this(extern hash(object)) 53 | } 54 | 55 | proc find(object) { 56 | set hash to extern hash(object) 57 | set cap to extern len(this.keys) 58 | set i to hash % cap 59 | 60 | while i < cap { 61 | if this.keys[i] == hash { 62 | return true 63 | } 64 | inc i 65 | } 66 | return false 67 | } 68 | } 69 | 70 | -------------------------------------------------------------------------------- /src/include/record.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef RECORD_H 4 | #define RECORD_H 5 | 6 | #include 7 | 8 | #define RECORD_BASE_PROPERTY 6385072256 9 | #define RECORD_INIT_PROC 6385337657 10 | #define RECORD_THIS 6385726429 11 | #define RECORD_OVERLOAD_ORDER 7572763920046527 12 | 13 | #define BINARY_OVERLOAD 2187 14 | #define UNARY_OVERLOAD 3187 15 | 16 | struct value; 17 | struct machine; 18 | 19 | struct record_prototype { 20 | uint64_t identifier; 21 | uint_fast8_t* property_map; 22 | uint_fast8_t size; 23 | struct record_prototype* base_prototype; 24 | }; 25 | 26 | struct record { 27 | struct record_prototype* prototype; 28 | struct value** properties; 29 | }; 30 | 31 | void init_record_prototype(struct record_prototype* prototype, uint64_t identifier); 32 | void free_record_prototype(struct record_prototype* prototype); 33 | 34 | const int record_append_property(struct record_prototype* prototype, uint64_t property); 35 | const int record_inherit(struct record_prototype* child, struct record_prototype* parent); 36 | 37 | const int init_record(struct record* record, struct record_prototype* prototype, struct machine* machine); 38 | void free_record(struct record* record); 39 | 40 | struct value* record_get_property(struct record* record, const uint64_t property); 41 | const int record_set_property(struct record* record, const uint64_t property, const struct value* value); 42 | 43 | #endif // !RECORD_H -------------------------------------------------------------------------------- /src/include/builtins.h: -------------------------------------------------------------------------------- 1 | #ifndef BUILTINS_H 2 | #define BUILTINS_H 3 | 4 | #include 5 | #include "value.h" 6 | 7 | struct machine; 8 | 9 | //API to facilitate the declaration of built-in procedures which can be called using the "extern" keyword within the Minima language 10 | #define DECL_BUILT_IN(METHOD_NAME) struct value (METHOD_NAME)(struct value** argv, uint32_t argc, struct machine* machine) 11 | 12 | //prints values 13 | DECL_BUILT_IN(builtin_print); 14 | 15 | //calls builtin_print and prints a newline afterwards 16 | DECL_BUILT_IN(builtin_print_line); 17 | 18 | //runs a system command 19 | DECL_BUILT_IN(builtin_system_cmd); 20 | 21 | //generates a random number from 0-1 22 | DECL_BUILT_IN(builtin_random); 23 | 24 | //gets user input 25 | DECL_BUILT_IN(builtin_get_input); 26 | 27 | //gets the length of an array 28 | DECL_BUILT_IN(builtin_get_length); 29 | 30 | //gets the hash of a value 31 | DECL_BUILT_IN(builtin_get_hash); 32 | 33 | //gets a str as a numerical 34 | DECL_BUILT_IN(builtin_to_num); 35 | 36 | //gets the numerical as a str 37 | DECL_BUILT_IN(builtin_to_str); 38 | 39 | //gets the type hash of an object 40 | DECL_BUILT_IN(builtin_get_type); 41 | 42 | DECL_BUILT_IN(builtin_implements); 43 | 44 | DECL_BUILT_IN(builtin_abs); 45 | DECL_BUILT_IN(builtin_max); 46 | DECL_BUILT_IN(builtin_min); 47 | DECL_BUILT_IN(builtin_ceil); 48 | DECL_BUILT_IN(builtin_floor); 49 | 50 | #endif // !BUILTINS_H -------------------------------------------------------------------------------- /src/include/machine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef MACHINE_H 4 | #define MACHINE_H 5 | 6 | #include 7 | #include "value.h" 8 | #include "garbage.h" 9 | #include "variable.h" 10 | #include "globals.h" 11 | #include "error.h" 12 | #include "chunk.h" 13 | 14 | #define MACHINE_MAX_POSITIONS 20000 15 | #define MACHINE_MAX_EVALS 100000 16 | #define MACHINE_MAX_CALLS 10000 17 | 18 | struct machine { 19 | uint64_t* position_stack; 20 | uint8_t* position_flags; 21 | 22 | struct value* constant_stack; 23 | struct value** evaluation_stack; 24 | 25 | struct var_context* var_stack; 26 | 27 | uint64_t positions, call_size; 28 | uint16_t evals, constants; 29 | 30 | int std_flag; 31 | enum error last_err; 32 | 33 | struct global_cache global_cache; 34 | struct garbage_collector garbage_collector; 35 | }; 36 | 37 | const int init_machine(struct machine* machine); 38 | void free_machine(struct machine* machine); 39 | 40 | void machine_reset(struct machine* machine); 41 | 42 | struct value* machine_pop_eval(struct machine* machine); 43 | struct value* machine_push_eval(struct machine* machine, struct value* value); 44 | struct value* machine_push_const(struct machine* machine, struct value const_value, int push_obj_children); 45 | 46 | const int machine_condition_check(struct machine* machine); 47 | 48 | const enum error machine_execute(struct machine* machine, struct chunk* chunk); 49 | 50 | #endif // !MACHINE_H 51 | -------------------------------------------------------------------------------- /src/include/globals.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef LABEL_H 4 | #define LABEL_H 5 | 6 | #include 7 | #include "builtins.h" 8 | 9 | struct global_cache { 10 | struct cache_bucket { 11 | uint64_t id; 12 | 13 | enum cache_type { 14 | CACHE_TYPE_POS, 15 | CACHE_TYPE_BUILTIN, 16 | CACHE_TYPE_PROTO, 17 | CACHE_TYPE_PROC 18 | }type; 19 | 20 | union payload 21 | { 22 | uint64_t pos; 23 | struct record_prototype* prototype; 24 | DECL_BUILT_IN(*delegate); 25 | }payload; 26 | 27 | struct cache_bucket* next; 28 | }** buckets; 29 | }; 30 | 31 | void init_global_cache(struct global_cache* global_cache); 32 | void free_global_cache(struct global_cache* global_cache); 33 | 34 | const int cache_insert_label(struct global_cache* global_cache, uint64_t id, uint64_t pos); 35 | uint64_t cache_retrieve_pos(struct global_cache* global_cache, uint64_t id); 36 | 37 | struct record_prototype* cache_insert_prototype(struct global_cache* global_cache, uint64_t id); 38 | const int cache_init_record(struct global_cache* global_cache, uint64_t proto_id, struct record* record, struct machine* machine); 39 | const int cache_merge_proto(struct global_cache* global_cache, uint64_t child, uint64_t parent); 40 | 41 | const int cache_declare_builtin(struct global_cache* global_cache, uint64_t id, DECL_BUILT_IN(*delegate)); 42 | struct value cache_invoke_builtin(struct global_cache* global_cache, uint64_t id, struct value** argv, uint32_t argc, struct machine* machine); 43 | 44 | #endif // !LABEL_H -------------------------------------------------------------------------------- /src/object.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "include/value.h" 4 | #include "include/object.h" 5 | 6 | void init_object_col(struct object* object, struct collection* collection) { 7 | object->type = OBJ_TYPE_COL; 8 | object->ptr.collection = collection; 9 | } 10 | 11 | void init_object_rec(struct object* object, struct record* record) { 12 | object->type = OBJ_TYPE_REC; 13 | object->ptr.record = record; 14 | } 15 | 16 | void free_object(struct object* object) { 17 | switch (object->type) 18 | { 19 | case OBJ_TYPE_COL: 20 | free_collection(object->ptr.collection); 21 | free(object->ptr.collection); 22 | break; 23 | case OBJ_TYPE_REC: 24 | free_record(object->ptr.record); 25 | free(object->ptr.record); 26 | break; 27 | } 28 | } 29 | 30 | const int object_compare(const struct object* a, const struct object* b) { 31 | if (a->type != b->type) 32 | return 1; 33 | 34 | switch (a->type) 35 | { 36 | case OBJ_TYPE_COL: 37 | return collection_compare(a->ptr.collection, b->ptr.collection) != 1; 38 | case OBJ_TYPE_REC: 39 | return a->ptr.record != b->ptr.record; 40 | } 41 | 42 | return 1; 43 | } 44 | 45 | struct value**object_get_children(const struct object* object, uint64_t* size) { 46 | switch (object->type) 47 | { 48 | case OBJ_TYPE_COL: 49 | *size = object->ptr.collection->size; 50 | return object->ptr.collection->inner_collection; 51 | case OBJ_TYPE_REC: 52 | *size = object->ptr.record->prototype->size; 53 | return object->ptr.record->properties; 54 | } 55 | return NULL; 56 | } -------------------------------------------------------------------------------- /src/include/compiler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef COMPILER_H 4 | #define COMPILER_H 5 | 6 | #include 7 | #include "error.h" 8 | #include "hash.h" 9 | #include "scanner.h" 10 | #include "machine.h" 11 | #include "chunk.h" 12 | #include "debug.h" 13 | 14 | struct compiler 15 | { 16 | struct scanner scanner; 17 | struct token last_tok; 18 | enum error last_err; 19 | 20 | struct chunk_builder data_builder, code_builder; 21 | struct chunk result; 22 | struct machine* machine; 23 | 24 | uint64_t imported_file_hashes[255]; 25 | uint_fast8_t imported_files; 26 | 27 | const char* include_dir; 28 | size_t include_dir_len; 29 | }; 30 | 31 | //initializes a compiler instance 32 | void init_compiler(struct compiler* compiler, struct machine* machine, const char* include_dir, const char* source, const char* file); 33 | 34 | //compiles a program, and stores the output in "chunk builder" 35 | const int compile(struct compiler* compiler, struct loc_table* loc_table, const int repl_mode, const int link_output, uint64_t prev_offset); 36 | 37 | //reads a token as a compiler 38 | struct token compiler_read_tok(struct compiler* compiler); 39 | 40 | //compiles an expression using shunting-yard 41 | const int compile_expression(struct compiler* compiler, struct chunk_builder* builder, enum op_precedence min_prec, const int optimize_copy, uint64_t optimize_goto); 42 | 43 | //compiles a block of code 44 | const int compile_body(struct compiler* compiler, struct chunk_builder* builder, struct loc_table* loc_table, uint64_t callee, uint64_t proc_encapsulated); 45 | 46 | #endif // !COMPILER_H -------------------------------------------------------------------------------- /src/include/value.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef VALUE_H 4 | #define VALUE_H 5 | 6 | #include "object.h" 7 | 8 | #define IS_COLLECTION(VALUE) ((VALUE).type == VALUE_TYPE_OBJ && (VALUE).payload.object.type == OBJ_TYPE_COL) 9 | #define IS_RECORD(VALUE) ((VALUE).type == VALUE_TYPE_OBJ && (VALUE).payload.object.type == OBJ_TYPE_REC) 10 | 11 | #define NUM_VALUE(NUM) (struct value) { .gc_flag = GARBAGE_CONSTANT, .payload.numerical = NUM, .type = VALUE_TYPE_NUM} 12 | #define CHAR_VALUE(CHAR) (struct value) { .gc_flag = GARBAGE_CONSTANT, .payload.character = CHAR, .type = VALUE_TYPE_CHAR} 13 | #define ID_VALUE(IDENTIFIER) (struct value) {.gc_flag = GARBAGE_CONSTANT, .payload.identifier = IDENTIFIER, .type = VALUE_TYPE_ID} 14 | 15 | struct value { 16 | enum garbage_flag { 17 | GARBAGE_CONSTANT, 18 | GARBAGE_COLLECT, 19 | GARBAGE_KEEP 20 | } gc_flag; 21 | 22 | union value_payload 23 | { 24 | double numerical; 25 | char character; 26 | uint64_t identifier; 27 | struct object object; 28 | } payload; 29 | 30 | enum value_type { 31 | VALUE_TYPE_NULL, 32 | VALUE_TYPE_NUM, 33 | VALUE_TYPE_CHAR, 34 | VALUE_TYPE_ID, 35 | VALUE_TYPE_OBJ, 36 | } type; 37 | }; 38 | 39 | static const struct value const_value_null = { GARBAGE_CONSTANT, 0, VALUE_TYPE_NULL }; 40 | static const struct value const_value_true = { GARBAGE_CONSTANT, 1, VALUE_TYPE_NUM }; 41 | static const struct value const_value_false = { GARBAGE_CONSTANT, 0, VALUE_TYPE_NUM }; 42 | 43 | void init_obj_value(struct value* value, struct object obj); 44 | 45 | const double compare_value(const struct value*a, const struct value*b); 46 | 47 | void free_value(struct value* value); 48 | 49 | #endif // !VALUE_H 50 | -------------------------------------------------------------------------------- /stl/map.min: -------------------------------------------------------------------------------- 1 | rem Minima, written by Michael Wang 2020-21 2 | 3 | rem Purpose: Implements a basic hash-map 4 | 5 | include "list.min" 6 | 7 | record map_pair { 8 | key 9 | value 10 | 11 | proc init(key, value) { 12 | set this.key to key 13 | set this.value to value 14 | } 15 | } 16 | 17 | record map { 18 | blocks 19 | 20 | proc init(capacity) { 21 | set this.blocks to alloc[capacity] 22 | } 23 | 24 | proc init { 25 | goproc init as this(100) 26 | } 27 | 28 | proc rehash(new_capacity) { 29 | set old_cap to extern len(this.blocks) 30 | 31 | set i to old_cap 32 | set found to new list 33 | while dec i { 34 | if this.blocks[i] { 35 | goproc push_back as found(this.blocks[i]) 36 | } 37 | } 38 | 39 | set this.blocks to alloc[new_capacity] 40 | while found.size { 41 | set block to goproc pop_front as found 42 | goproc insert_block as this(block.key, block.value) 43 | } 44 | } 45 | 46 | proc insert_block(key, value) { 47 | set cap to extern len(this.blocks) 48 | set i to key % cap 49 | 50 | while i < cap { 51 | if !this.blocks[i] { 52 | set this.blocks[i] to new map_pair(key, value) 53 | return 54 | } 55 | inc i 56 | } 57 | 58 | goproc rehash as this(2 * cap) 59 | goproc insert_block as this(key, value) 60 | } 61 | 62 | proc insert(key, value) { 63 | goproc insert_block as this(extern hash(key), value) 64 | } 65 | 66 | proc find(key) { 67 | set hash to extern hash(key) 68 | set cap to extern len(this.blocks) 69 | set i to hash % cap 70 | 71 | while i < cap { 72 | if this.blocks[i] { 73 | if this.blocks[i].key == hash { 74 | return this.blocks[i].value 75 | } 76 | } 77 | inc i 78 | } 79 | return null 80 | } 81 | } -------------------------------------------------------------------------------- /examples/map.min: -------------------------------------------------------------------------------- 1 | rem Minima, written by Michael Wang 2020-21 2 | 3 | rem Purpose: Implements a basic hash-map 4 | 5 | include "list.min" 6 | 7 | record map_pair { 8 | key 9 | value 10 | 11 | proc init(key, value) { 12 | set this.key to key 13 | set this.value to value 14 | } 15 | } 16 | 17 | record map { 18 | blocks 19 | 20 | proc init(capacity) { 21 | set this.blocks to alloc[capacity] 22 | } 23 | 24 | proc init { 25 | goproc init as this(100) 26 | } 27 | 28 | proc rehash(new_capacity) { 29 | set old_cap to extern len(this.blocks) 30 | 31 | set i to old_cap 32 | set found to new list 33 | while dec i { 34 | if this.blocks[i] { 35 | goproc push_back as found(this.blocks[i]) 36 | } 37 | } 38 | 39 | set this.blocks to alloc[new_capacity] 40 | while found.size { 41 | set block to goproc pop_front as found 42 | goproc insert_block as this(block.key, block.value) 43 | } 44 | } 45 | 46 | proc insert_block(key, value) { 47 | set cap to extern len(this.blocks) 48 | set i to key % cap 49 | 50 | while i < cap { 51 | if !this.blocks[i] { 52 | set this.blocks[i] to new map_pair(key, value) 53 | return 54 | } 55 | inc i 56 | } 57 | 58 | goproc rehash as this(2 * cap) 59 | goproc insert_block as this(key, value) 60 | } 61 | 62 | proc insert(key, value) { 63 | goproc insert_block as this(extern hash(key), value) 64 | } 65 | 66 | proc find(key) { 67 | set hash to extern hash(key) 68 | set cap to extern len(this.blocks) 69 | set i to hash % cap 70 | 71 | while i < cap { 72 | if this.blocks[i] { 73 | if this.blocks[i].key == hash { 74 | return this.blocks[i].value 75 | } 76 | } 77 | inc i 78 | } 79 | return null 80 | } 81 | } -------------------------------------------------------------------------------- /src/include/chunk.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef CHUNK_H 4 | #define CHUNK_H 5 | 6 | #include 7 | #include "value.h" 8 | #include "opcodes.h" 9 | #include "operators.h" 10 | 11 | struct chunk_builder { 12 | uint8_t* code_buf; 13 | uint64_t size; 14 | uint64_t alloc_size; 15 | }; 16 | 17 | struct chunk { 18 | enum op_code last_code; 19 | uint8_t* code; 20 | uint64_t size; 21 | uint64_t pos; 22 | }; 23 | 24 | void init_chunk(struct chunk* chunk, uint8_t* code, const uint64_t size); 25 | void free_chunk(struct chunk* chunk); 26 | 27 | const int init_chunk_builder(struct chunk_builder* builder); 28 | struct chunk build_chunk(struct chunk_builder* builder); 29 | 30 | const int chunk_write_ulong(struct chunk_builder* chunk_builder, const uint64_t ulong); 31 | const int chunk_write_value(struct chunk_builder* chunk_builder, struct value value); 32 | const int chunk_write_chunk(struct chunk_builder* dest, struct chunk src, const int free_chunk); 33 | const int chunk_write_opcode(struct chunk_builder* chunk_builder, enum op_code opcode); 34 | const int chunk_write_bin_op(struct chunk_builder* chunk_builder, enum binary_operator bin_op); 35 | const int chunk_write_uni_op(struct chunk_builder* chunk_builder, enum unary_operator uni_op); 36 | 37 | const void* chunk_write_size(struct chunk_builder* chunk_builder, const void* ptr, const uint64_t size); 38 | 39 | const uint64_t chunk_read_ulong(struct chunk* chunk); 40 | const struct value chunk_read_value(struct chunk* chunk); 41 | const enum op_code chunk_read_opcode(struct chunk* chunk); 42 | const enum binary_operator chunk_read_bin_op(struct chunk* chunk); 43 | const enum binary_operator chunk_read_uni_op(struct chunk* chunk); 44 | 45 | const void* chunk_read_size(struct chunk* chunk, const uint64_t size); 46 | 47 | void chunk_jump_to(struct chunk* chunk, const uint64_t pos); 48 | #endif // !CHUNK_H 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # Minima 6 | Minima is an object-oriented, imperative scripting language focused on being practical and pragmatic. Minima aims to be simple, easy to write, yet powerful enough to create readable, performant and concise code for any problem. You can read [Minima's documentation here](https://github.com/TheRealMichaelWang/minima/wiki), and view the [installation guide here](https://github.com/TheRealMichaelWang/minima/wiki/Installation). 7 | 8 | Among many things, it's... 9 | * **Small** - The compiler and virutal-machine clock slightly under 5000 lines of code. 10 | * **Portable** - Minima compiles byte-code and runs it on a virtual-machine. It's the same way Java does it. 11 | * **Powerful** - Minima is designed for quick-and-messy object-oriented programming on the fly. 12 | * **Embeddable** - It's very easy to imbed it into your C/C++ project. 13 | * **Easy To Learn** - You could probably read the documentation, and learn the language, in under half-an-hour. 14 | 15 | ## Examples 16 | 17 | ### Count Down 18 | ``` 19 | set i to 10 20 | while dec i { 21 | extern printl(i) 22 | } 23 | ``` 24 | 25 | ### Fizz Buzz 26 | ``` 27 | set i to 0 28 | while inc i <= 99 { 29 | if i % 15 == 0 { 30 | extern print("fizzbuzz") 31 | } 32 | elif i % 5 == 0{ 33 | extern print("fizz") 34 | } 35 | elif i % 3 == 0{ 36 | extern print("buzz") 37 | } 38 | else { 39 | extern print(i) 40 | } 41 | extern printl 42 | } 43 | ``` 44 | 45 | ### Fibonacci 46 | ``` 47 | proc fib(n) { 48 | if n <= 1 { 49 | return n 50 | } 51 | return goproc fib(n - 1) + goproc fib(n - 2) 52 | } 53 | extern print(goproc fib(25)) 54 | ``` 55 | 56 | ### Factorial 57 | ``` 58 | proc fact(n) { 59 | if n { 60 | return n * goproc fact(n - 1) 61 | } 62 | return 1 63 | } 64 | extern print(goproc fact(50)) 65 | ``` 66 | -------------------------------------------------------------------------------- /src/variable.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "include/error.h" 4 | #include "include/variable.h" 5 | 6 | const int init_var_context(struct var_context* var_context, struct garbage_collector* garbage_collector) { 7 | var_context->hash_limit = 8; 8 | var_context->garbage_collector = garbage_collector; 9 | ERROR_ALLOC_CHECK(var_context->buckets = malloc(var_context->hash_limit * sizeof(struct var_bucket))); 10 | 11 | for (uint_fast64_t i = 0; i < var_context->hash_limit; i++) 12 | var_context->buckets[i].set_flag = 0; 13 | gc_new_frame(garbage_collector); 14 | 15 | return 1; 16 | } 17 | 18 | const int free_var_context(struct var_context* var_context) { 19 | free(var_context->buckets); 20 | return gc_collect(var_context->garbage_collector); 21 | } 22 | 23 | static const int rehash(struct var_context* var_context) { 24 | struct var_bucket* old_buckets = var_context->buckets; 25 | uint64_t old_limit = var_context->hash_limit; 26 | 27 | var_context->hash_limit *= 2; 28 | 29 | ERROR_ALLOC_CHECK(var_context->buckets = malloc(var_context->hash_limit * sizeof(struct var_bucket))); 30 | for (uint_fast64_t i = 0; i < var_context->hash_limit; i++) 31 | var_context->buckets[i].set_flag = 0; 32 | 33 | for (uint_fast64_t i = 0; i < old_limit; i++) 34 | if (old_buckets[i].set_flag) 35 | emplace_var(var_context, old_buckets[i].identifier, old_buckets[i].value); 36 | 37 | free(old_buckets); 38 | return 1; 39 | } 40 | 41 | struct value*retrieve_var(struct var_context* var_context, const uint64_t id) { 42 | for (uint_fast64_t i = id & (var_context->hash_limit - 1); i < var_context->hash_limit && var_context->buckets[i].set_flag; i++) { 43 | if (var_context->buckets[i].identifier == id) 44 | return var_context->buckets[i].value; 45 | } 46 | return NULL; 47 | } 48 | 49 | const int emplace_var(struct var_context* var_context, const uint64_t id, struct value*value) { 50 | ERROR_ALLOC_CHECK(value); 51 | 52 | for (uint_fast64_t i = id & (var_context->hash_limit - 1); i < var_context->hash_limit; i++) { 53 | if (!var_context->buckets[i].set_flag || (var_context->buckets[i].identifier == id)) { 54 | var_context->buckets[i].identifier = id; 55 | var_context->buckets[i].value = value; 56 | var_context->buckets[i].set_flag = 1; 57 | return 1; 58 | } 59 | } 60 | 61 | if (!rehash(var_context)) 62 | return 0; 63 | return emplace_var(var_context, id, value); 64 | } -------------------------------------------------------------------------------- /examples/union.min: -------------------------------------------------------------------------------- 1 | include "map.min" 2 | include "list.min" 3 | 4 | record union { 5 | parents 6 | keys 7 | indicies 8 | size 9 | capacity 10 | 11 | proc init(capacity) { 12 | set this.capacity to capacity 13 | set this.parents to alloc[capacity] 14 | set this.keys to alloc[capacity] 15 | set this.indicies to new map(capacity) 16 | set this.size to 0 17 | } 18 | 19 | proc insert(key) { 20 | if goproc find as this.indicies(key) { 21 | return false 22 | } 23 | elif this.size == this.capacity { 24 | return false 25 | } 26 | goproc insert as this.indicies(key, this.size) 27 | set this.keys[this.size] to key 28 | inc this.size 29 | return true 30 | } 31 | 32 | proc index_parent(i) { 33 | if this.parents[i] == null { 34 | return i 35 | } 36 | set this.parents[i] to goproc index_parent as this(this.parents[i]) 37 | return this.parents[i] 38 | } 39 | 40 | proc parent(key) { 41 | set i to goproc find as this.indicies(key) 42 | if i == null { 43 | return null 44 | } 45 | set j to goproc index_parent as this(i) 46 | return this.keys[j] 47 | } 48 | 49 | proc unite(parent, child) { 50 | set p_i to goproc find as this.indicies(parent) 51 | set c_i to goproc find as this.indicies(child) 52 | if p_i == null { 53 | set p_i to this.size 54 | if !goproc insert as this(parent) { 55 | return false 56 | } 57 | } 58 | if c_i == null { 59 | set c_i to this.size 60 | if !goproc insert as this(child) { 61 | return false 62 | } 63 | } 64 | 65 | if goproc index_parent as this(p_i) == c_i { 66 | return false 67 | } 68 | 69 | set this.parents[c_i] to goproc index_parent as this(p_i) 70 | return true 71 | } 72 | 73 | proc children(parent) { 74 | set i to goproc find as this.indicies(parent) 75 | if i == null { 76 | return null 77 | } 78 | 79 | set found to new list 80 | 81 | set j to extern len(this.parents) 82 | while dec j { 83 | if goproc index_parent as this(j) == i { 84 | goproc push_back as found(this.keys[j]) 85 | } 86 | } 87 | return goproc array as found 88 | } 89 | 90 | proc roots { 91 | set found to new list 92 | 93 | set j to extern len(this.parents) 94 | while dec j { 95 | if goproc index_parent as this(j) == j and this.keys[j] { 96 | goproc push_back as found(this.keys[j]) 97 | } 98 | } 99 | return goproc array as found 100 | } 101 | } -------------------------------------------------------------------------------- /stl/union.min: -------------------------------------------------------------------------------- 1 | include "map.min" 2 | include "list.min" 3 | 4 | record union { 5 | parents 6 | keys 7 | indicies 8 | size 9 | capacity 10 | 11 | proc init(capacity) { 12 | set this.capacity to capacity 13 | set this.parents to alloc[capacity] 14 | set this.keys to alloc[capacity] 15 | set this.indicies to new map(capacity) 16 | set this.size to 0 17 | } 18 | 19 | proc insert(key) { 20 | if goproc find as this.indicies(key) { 21 | return false 22 | } 23 | elif this.size == this.capacity { 24 | return false 25 | } 26 | goproc insert as this.indicies(key, this.size) 27 | set this.keys[this.size] to key 28 | inc this.size 29 | return true 30 | } 31 | 32 | proc index_parent(i) { 33 | if this.parents[i] == null { 34 | return i 35 | } 36 | set this.parents[i] to goproc index_parent as this(this.parents[i]) 37 | return this.parents[i] 38 | } 39 | 40 | proc parent(key) { 41 | set i to goproc find as this.indicies(key) 42 | if i == null { 43 | return null 44 | } 45 | set j to goproc index_parent as this(i) 46 | return this.keys[j] 47 | } 48 | 49 | proc unite(parent, child) { 50 | set p_i to goproc find as this.indicies(parent) 51 | set c_i to goproc find as this.indicies(child) 52 | if p_i == null { 53 | set p_i to this.size 54 | if !goproc insert as this(parent) { 55 | return false 56 | } 57 | } 58 | if c_i == null { 59 | set c_i to this.size 60 | if !goproc insert as this(child) { 61 | return false 62 | } 63 | } 64 | 65 | if goproc index_parent as this(p_i) == c_i { 66 | return false 67 | } 68 | 69 | set this.parents[c_i] to goproc index_parent as this(p_i) 70 | return true 71 | } 72 | 73 | proc children(parent) { 74 | set i to goproc find as this.indicies(parent) 75 | if i == null { 76 | return null 77 | } 78 | 79 | set found to new list 80 | 81 | set j to extern len(this.parents) 82 | while dec j { 83 | if goproc index_parent as this(j) == i { 84 | goproc push_back as found(this.keys[j]) 85 | } 86 | } 87 | return goproc array as found 88 | } 89 | 90 | proc roots { 91 | set found to new list 92 | 93 | set j to extern len(this.parents) 94 | while dec j { 95 | if goproc index_parent as this(j) == j and this.keys[j] != null { 96 | goproc push_back as found(this.keys[j]) 97 | } 98 | } 99 | return goproc array as found 100 | } 101 | } -------------------------------------------------------------------------------- /examples/symbol/print.txt: -------------------------------------------------------------------------------- 1 | include "symbol/core.txt" 2 | 3 | record eq_leaf { 4 | proc print { 5 | extern print(this.obj) 6 | } 7 | } 8 | 9 | record match { 10 | proc print { 11 | extern print("match:",this.id) 12 | } 13 | } 14 | 15 | record eq_bin_op { 16 | proc print_child(child) { 17 | if extern implements(child, #eq_bin_op) { 18 | goproc print as child(this.op_prec) 19 | } 20 | else { 21 | goproc print as child 22 | } 23 | } 24 | 25 | proc print_left(op_prec) { 26 | if this.op_prec < op_prec { 27 | extern print('(') 28 | } 29 | goproc print_child as this(this.left) 30 | } 31 | 32 | proc print_right(op_prec) { 33 | goproc print_child as this(this.right) 34 | if this.op_prec < op_prec { 35 | extern print(')') 36 | } 37 | } 38 | 39 | proc print { 40 | goproc print as this(0) 41 | } 42 | } 43 | 44 | record eq_op_add { 45 | proc print(op_prec) { 46 | goproc print_left as this(op_prec) 47 | extern print(" + ") 48 | goproc print_right as this(op_prec) 49 | } 50 | } 51 | 52 | record eq_op_subtract { 53 | proc print(op_prec) { 54 | goproc print_left as this(op_prec) 55 | extern print(" - ") 56 | goproc print_right as this(op_prec) 57 | } 58 | } 59 | 60 | record eq_op_multiply { 61 | proc print(op_prec) { 62 | if this.left == -1 { 63 | extern print('-') 64 | goproc print as this.right 65 | } 66 | elif this.right == -1 { 67 | extern print('-') 68 | goproc print as this.left 69 | } 70 | else { 71 | goproc print_left as this(op_prec) 72 | extern print(" * ") 73 | goproc print_right as this(op_prec) 74 | } 75 | } 76 | } 77 | 78 | record eq_op_divide { 79 | proc print(op_prec) { 80 | goproc print_left as this(op_prec) 81 | extern print(" / ") 82 | goproc print_right as this(op_prec) 83 | } 84 | } 85 | 86 | record eq_op_mod { 87 | proc print(op_prec) { 88 | goproc print_left as this(op_prec) 89 | extern print(" % ") 90 | goproc print_right as this(op_prec) 91 | } 92 | } 93 | 94 | record eq_op_pow { 95 | proc print(op_prec) { 96 | goproc print_left as this(op_prec) 97 | extern print(" ^ ") 98 | goproc print_right as this(op_prec) 99 | } 100 | } 101 | 102 | record eq_op_log { 103 | proc print(op_prec) { 104 | extern print("log(") 105 | goproc print as this.left 106 | extern print(", ") 107 | goproc print as this.right 108 | extern print(')') 109 | } 110 | } 111 | 112 | record eq_op_abs { 113 | proc print { 114 | extern print("abs(") 115 | goproc print as this.operand 116 | extern print(')') 117 | } 118 | } -------------------------------------------------------------------------------- /examples/symbol/derive.txt: -------------------------------------------------------------------------------- 1 | include "symbol/core.txt" 2 | 3 | rem Purpose: Implements a deriviative function for variables 4 | record eq_var { 5 | proc derive(var) { 6 | return new eq_num(this.obj == var) 7 | } 8 | } 9 | 10 | rem Purpose: Implements a derivative function for numericals 11 | record eq_num { 12 | proc derive(var) { 13 | return new eq_num(0) 14 | } 15 | } 16 | 17 | rem Purpose: Implements a derivative function for addition 18 | record eq_op_add { 19 | proc derive(var) { 20 | return goproc derive as this.left(var) + goproc derive as this.right(var) 21 | } 22 | } 23 | 24 | rem Purpose: Implements a derivative function for subtraction 25 | record eq_op_subtract { 26 | proc derive(var) { 27 | return goproc derive as this.left(var) - goproc derive as this.right(var) 28 | } 29 | } 30 | 31 | rem Purpose: Implements a derivative function for multiplication 32 | record eq_op_multiply { 33 | proc derive(var) { 34 | return (goproc derive as this.left(var) * this.right) + 35 | (goproc derive as this.right(var) * this.left) 36 | } 37 | } 38 | 39 | rem Purpose: Implements a derivative function for division 40 | record eq_op_divide { 41 | proc derive(var) { 42 | return ((goproc derive as this.left(var) * this.right) + 43 | (goproc derive as this.right(var) * this.left)) / 44 | (this.right ^ 2) 45 | } 46 | } 47 | 48 | rem Purpose: Implements a derivative function for modulous 49 | record eq_op_mod { 50 | proc derive(var) { 51 | return goproc derive as this.left(var) - 52 | (this.left / this.right) * goproc derive as this.right(var) 53 | } 54 | } 55 | 56 | rem Purpose: Implements a derivative function for exponentials 57 | record eq_op_pow { 58 | proc derive(var) { 59 | if extern typeof(this.right) == extern typeof(#eq_num) { 60 | return this.right * this.left ^ (this.right - 1) * goproc derive as this.left(var) 61 | } 62 | elif extern typeof(this.left) == extern typeof(#eq_num) { 63 | return this.right ^ this.left * goproc log(this.left) * goproc derive as this.right(var) 64 | } 65 | return this.left ^ this.right * (goproc derive as this.left(var) * this.right / this.left + goproc log(this.left) * goproc derive as this.right(var)) 66 | } 67 | } 68 | 69 | rem Purpose: Implements a derivative function for logarithms 70 | record eq_op_log { 71 | proc derive(var) { 72 | return (goproc derive as this.left(var) / this.left * goproc log(this.right) - 73 | goproc log(this.left) * goproc derive as this.right(var) / this.right) / 74 | (goproc log(this.right) ^ 2) 75 | } 76 | } 77 | 78 | rem Purpose: Implements a derivative function for absoloute value 79 | record eq_op_abs { 80 | rem Signum of abs is always 1 81 | proc derive(var) { 82 | return goproc derive as this.operand(var) 83 | } 84 | } -------------------------------------------------------------------------------- /examples/conways.txt: -------------------------------------------------------------------------------- 1 | include "random.min" 2 | 3 | rem Instance of one game of conways game of life 4 | record cell_batch { 5 | matrix 6 | epoch 7 | 8 | rem initializes an instance with a specified number of cells and rows 9 | proc init (rows, cols) { 10 | set this.epoch to 0 11 | set this.matrix to alloc[rows] 12 | set ri to rows 13 | while dec ri { 14 | set this.matrix[ri] to alloc[cols] 15 | set ci to cols 16 | while dec ci { 17 | set this.matrix[ri][ci] to goproc randbit 18 | } 19 | } 20 | } 21 | 22 | rem default initializer 23 | proc init { 24 | goproc init as this(10, 10) 25 | } 26 | 27 | rem prints the cell_game onto the console 28 | proc print { 29 | set ri to extern len(this.matrix) 30 | while dec ri { 31 | set cols to extern len(this.matrix[ri]) 32 | set ci to 0 33 | while ci < cols { 34 | if this.matrix[ri][ci] { 35 | extern print('#') 36 | } 37 | else { 38 | extern print(' ') 39 | } 40 | inc ci 41 | } 42 | extern printl 43 | } 44 | } 45 | 46 | rem calculates the amount of living neighboors of a cell 47 | proc neighboors(row, col) { 48 | set dx to {0 - 1, 0, 1} 49 | set dy to {0 - 1, 0, 1} 50 | set neighboors to 0 51 | 52 | set dxi to 3 53 | while dec dxi { 54 | set crow to row + dx[dxi] 55 | if crow >= 0 and crow < extern len(this.matrix) { 56 | set dyi to 3 57 | set cols to extern len(this.matrix[crow]) 58 | while dec dyi { 59 | set ccol to col + dy[dyi] 60 | if ccol >= 0 and ccol < cols { 61 | set neighboors to neighboors + this.matrix[crow][ccol] 62 | } 63 | } 64 | } 65 | } 66 | return neighboors 67 | } 68 | 69 | rem evolves the cell game by one generation, according to the rules of conways game of life 70 | proc evolve { 71 | set ri to extern len(this.matrix) 72 | set next_gen to alloc[ri] 73 | while dec ri { 74 | set next_gen[ri] to alloc[extern len(this.matrix[ri])] 75 | } 76 | 77 | set ri to extern len(this.matrix) 78 | while dec ri { 79 | set ci to extern len(this.matrix[ri]) 80 | while dec ci { 81 | set neighboors to goproc neighboors as this(ri, ci) 82 | if this.matrix[ri][ci] { 83 | if neighboors < 2 or neighboors > 3 { 84 | set next_gen[ri][ci] to false 85 | } 86 | else { 87 | set next_gen[ri][ci] to true 88 | } 89 | } 90 | else { 91 | if neighboors == 3 { 92 | set next_gen[ri][ci] to true 93 | } 94 | else { 95 | set next_gen[ri][ci] to false 96 | } 97 | } 98 | } 99 | } 100 | set this.matrix to next_gen 101 | inc this.epoch 102 | } 103 | } 104 | 105 | set cells to new cell_batch 106 | set i to 1000 107 | 108 | while dec i { 109 | goproc evolve as cells 110 | } 111 | 112 | goproc print as cells 113 | 114 | while true {} 115 | -------------------------------------------------------------------------------- /src/record.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "include/error.h" 3 | #include "include/value.h" 4 | #include "include/machine.h" 5 | #include "include/record.h" 6 | 7 | #define MAX_SIZE 256 8 | 9 | inline static const uint8_t retrieve_property_index(struct record_prototype* prototype, uint64_t property) { 10 | return prototype->property_map[property & 255]; 11 | } 12 | 13 | void init_record_prototype(struct record_prototype* prototype, uint64_t identifier) { 14 | prototype->identifier = identifier; 15 | prototype->property_map = calloc(MAX_SIZE, sizeof(uint_fast8_t)); 16 | prototype->size = 0; 17 | prototype->base_prototype = NULL; 18 | } 19 | 20 | void free_record_prototype(struct record_prototype* prototype) { 21 | free(prototype->property_map); 22 | } 23 | 24 | const int record_append_property(struct record_prototype* prototype, uint64_t property) { 25 | uint_fast8_t* slot = &prototype->property_map[property & 255]; 26 | if (*slot) 27 | return 0; 28 | *slot = 1 + prototype->size++; 29 | return 1; 30 | } 31 | 32 | const int record_inherit(struct record_prototype* child, struct record_prototype* parent) { 33 | if (child->base_prototype) 34 | return 0; 35 | child->base_prototype = parent; 36 | record_append_property(child, RECORD_BASE_PROPERTY); 37 | return 1; 38 | } 39 | 40 | const int init_record(struct record* record, struct record_prototype* prototype, struct machine* machine) { 41 | record->prototype = prototype; 42 | ERROR_ALLOC_CHECK(record->properties = malloc(prototype->size * sizeof(struct value*))); 43 | for (uint_fast8_t i = 0; i < prototype->size; i++) { 44 | struct value property = const_value_null; 45 | 46 | if (prototype->base_prototype && i == retrieve_property_index(prototype, RECORD_BASE_PROPERTY) - 1) { 47 | struct record* base_record = malloc(sizeof(struct record)); 48 | ERROR_ALLOC_CHECK(record); 49 | init_record(base_record, prototype->base_prototype, machine); 50 | struct object object; 51 | init_object_rec(&object, base_record); 52 | init_obj_value(&property, object); 53 | } 54 | ERROR_ALLOC_CHECK(record->properties[i] = machine_push_const(machine, property, 0)); 55 | } 56 | return 1; 57 | } 58 | 59 | void free_record(struct record* record) { 60 | free(record->properties); 61 | } 62 | 63 | struct value* record_get_property(struct record* record, const uint64_t property) { 64 | uint_fast8_t i = retrieve_property_index(record->prototype, property); 65 | if (i) 66 | return record->properties[i - 1]; 67 | else if (record->prototype->base_prototype) { 68 | struct value* record_val = record_get_property(record, RECORD_BASE_PROPERTY); 69 | if (record_val && IS_RECORD(*record_val)) { 70 | return record_get_property(record_val->payload.object.ptr.record, property); 71 | } 72 | } 73 | return NULL; 74 | } 75 | 76 | const int record_set_property(struct record* record, const uint64_t property, const struct value* value) { 77 | ERROR_ALLOC_CHECK(value); 78 | 79 | uint_fast8_t i = retrieve_property_index(record->prototype, property); 80 | if (i) { 81 | record->properties[i - 1] = value; 82 | return 1; 83 | } 84 | else if (record->prototype->base_prototype) { 85 | struct value* record_val = record_get_property(record, RECORD_BASE_PROPERTY); 86 | if (record_val && IS_RECORD(*record_val)) { 87 | return record_set_property(record_val->payload.object.ptr.record, property, value); 88 | } 89 | } 90 | return 0; 91 | } -------------------------------------------------------------------------------- /examples/list.min: -------------------------------------------------------------------------------- 1 | rem Minima, written by Michael Wang 2020-21 2 | 3 | rem Purpose: Implements a generic, dynamically sized collection as a doubly-linked list. 4 | rem Note that list does not have a fixed capacity, as additional nodes 5 | rem record can be allocated/freed on demand, depending on the requested 6 | rem operation. 7 | 8 | record node { 9 | next 10 | prev 11 | value 12 | 13 | proc init(value, next, prev){ 14 | set this.value to value 15 | set this.next to next 16 | set this.prev to prev 17 | } 18 | } 19 | 20 | record list { 21 | head 22 | tail 23 | size 24 | 25 | rem Purpose: Constructs a new list 26 | rem The list is initally empty. 27 | proc init { 28 | set this.size to 0 29 | } 30 | 31 | rem Purpose: Returns a reference to the fist element in the list. 32 | rem Returns null if the list is empty. 33 | proc front { 34 | if this.head == null { 35 | return null 36 | } 37 | return ref this.head.value 38 | } 39 | 40 | rem Purpose: Returns a reference to the last element in the list 41 | rem Returns null if the list is empty. 42 | proc back { 43 | if this.tail == null { 44 | return null 45 | } 46 | return ref this.tail.value 47 | } 48 | 49 | rem Purpose: Constructs a new list from an array 50 | rem Calls the default initializer, then calls the push_front method on every item in the array 51 | proc init(array){ 52 | goproc init as this 53 | set i to extern len(array) 54 | while dec i { 55 | goproc push_front as this(array[i]) 56 | } 57 | } 58 | 59 | rem Purpose: Pushes an item to the end of the list 60 | proc push_back(value) { 61 | set node to new node(value, null, null) 62 | if this.size == 0 { 63 | set this.head to node 64 | set this.tail to node 65 | } 66 | else { 67 | set node.prev to this.tail 68 | set this.tail.next to node 69 | set this.tail to node 70 | } 71 | inc this.size 72 | } 73 | 74 | rem Purpose: Pushes an item to the front of the list 75 | proc push_front(value) { 76 | set node to new node(value, null, null) 77 | if this.size == 0 { 78 | set this.head to node 79 | set this.tail to node 80 | } 81 | else { 82 | set node.next to this.head 83 | set this.head.prev to node 84 | set this.head to node 85 | } 86 | inc this.size 87 | } 88 | 89 | rem Purpose: Removes the back of the list and returns it's value 90 | rem Returns null if the list is empty 91 | proc pop_back { 92 | if this.size == 0 { 93 | return null 94 | } 95 | elif this.size == 1 { 96 | dec this.size 97 | set value to this.head.value 98 | set this.head to null 99 | set this.tail to null 100 | return value 101 | } 102 | dec this.size 103 | set value to this.tail.value 104 | set prev to this.tail.prev 105 | set prev.next to null 106 | set this.tail to prev 107 | return value 108 | } 109 | 110 | rem Purpose: Removes the front of the list and returns it's value 111 | rem Returns null if the list is empty 112 | proc pop_front { 113 | if this.size == 0 { 114 | return null 115 | } 116 | elif this.size == 1 { 117 | dec this.size 118 | set value to this.head.value 119 | set this.head to null 120 | set this.tail to null 121 | return value 122 | } 123 | dec this.size 124 | set value to this.head.value 125 | set next to this.head.next 126 | set next.prev to null 127 | set this.head to next 128 | return value 129 | } 130 | 131 | rem Purpose: Converts a list to an array 132 | rem The array is a copy, however each element is a reference to it's original list element 133 | proc array { 134 | set array to alloc[this.size] 135 | set current to this.head 136 | set i to 0 137 | while current { 138 | set array[i] to ref current.value 139 | inc i 140 | set current to ref current.next 141 | } 142 | return array 143 | } 144 | } -------------------------------------------------------------------------------- /examples/symbol/sort.txt: -------------------------------------------------------------------------------- 1 | include "list.min" 2 | include "union.min" 3 | include "symbol/core.txt" 4 | 5 | record eq_entity { 6 | proc terms { 7 | return goproc array as goproc linear_children as this(0) 8 | } 9 | 10 | proc factors { 11 | return goproc array as goproc linear_children as this(2) 12 | } 13 | } 14 | 15 | record eq_leaf { 16 | proc linear_children(op_rec) { 17 | set list to new list 18 | goproc push_back as list(this) 19 | return list 20 | } 21 | } 22 | 23 | record eq_node { 24 | proc sort_comp(eq_entity, level) { 25 | if !extern implements(eq_entity, #eq_node) { 26 | return false 27 | } 28 | 29 | if level == 3 and extern implements(eq_entity, #eq_bin_op) and extern implements(this, #eq_bin_op) { 30 | return eq_entity.op_prec - (eq_entity.op_prec % 2) == this.op_prec - (this.op_prec % 2) 31 | } 32 | if level == 2 and extern typeof(this) == extern typeof(eq_entity) { 33 | return true 34 | } 35 | 36 | set same to 0 37 | set i to extern len(eq_entity.children) 38 | while dec i { 39 | if goproc sort_comp as this.children[i](eq_entity.children[i], level) { 40 | inc same 41 | } 42 | } 43 | 44 | if level > 1 { 45 | return same >= 1 46 | } 47 | else { 48 | return same == extern len(this.children) 49 | } 50 | } 51 | } 52 | 53 | record eq_bin_op { 54 | proc linear_children(op_prec) { 55 | set list to new list 56 | if this.op_prec - (this.op_prec % 2) == op_prec - (op_prec % 2) { 57 | goproc push_range as list(goproc linear_children as this.left(op_prec)) 58 | goproc push_range as list(goproc linear_children as this.right(op_prec)) 59 | } 60 | else { 61 | goproc push_back as list(this) 62 | } 63 | return list 64 | } 65 | } 66 | 67 | record op_uni_op { 68 | proc linear_children(op_rec) { 69 | set list to new list 70 | goproc push_back as list(this) 71 | return list 72 | } 73 | } 74 | 75 | record eq_num { 76 | proc sort_comp(eq_entity, level) { 77 | if extern typeof(eq_entity) != extern typeof(this) { 78 | return false 79 | } 80 | if level == 3 { 81 | return true 82 | } 83 | return this.obj == eq_entity.obj 84 | } 85 | } 86 | 87 | record eq_var { 88 | proc sort_comp(eq_entity, level) { 89 | if extern typeof(eq_entity) != extern typeof(this) { 90 | return false 91 | } 92 | return this.obj == eq_entity.obj 93 | } 94 | } 95 | 96 | proc sort_terms(terms, level) { 97 | set union to new union(extern len(terms)) 98 | 99 | set i to extern len(terms) 100 | while dec i { 101 | goproc insert as union(i) 102 | set j to extern len(terms) 103 | while dec j { 104 | if i != j and goproc sort_comp as terms[i](terms[j], level) { 105 | goproc unite as union(i, j) 106 | } 107 | } 108 | } 109 | 110 | set roots to goproc roots as union 111 | 112 | set list to new list 113 | 114 | set i to extern len(roots) 115 | 116 | while dec i { 117 | set children_i to goproc children as union(roots[i]) 118 | set j to extern len(children_i) 119 | set children to alloc[j] 120 | 121 | while dec j { 122 | set children[j] to terms[children_i[j]] 123 | } 124 | 125 | if level { 126 | goproc push_back as list(goproc sort_terms(children, level - 1)) 127 | } 128 | else { 129 | set j to extern len(children) 130 | while dec j { 131 | goproc push_back as list(children[j]) 132 | } 133 | } 134 | } 135 | return goproc multi_hang(goproc array as list) 136 | } 137 | 138 | proc multi_hang(terms, offset, length) { 139 | if !length { 140 | return null 141 | } 142 | elif length == 1 { 143 | return terms[offset] 144 | } 145 | elif length == 2 { 146 | return new eq_op_add(terms[offset], terms[offset + 1]) 147 | } 148 | set mid to extern floor(length / 2) 149 | return new eq_op_add(goproc multi_hang(terms, 0, mid), 150 | goproc multi_hang(terms, mid, length - mid)) 151 | } 152 | 153 | proc multi_hang(terms) { 154 | return goproc multi_hang(terms, 0, extern len(terms)) 155 | } -------------------------------------------------------------------------------- /stl/list.min: -------------------------------------------------------------------------------- 1 | rem Minima, written by Michael Wang 2020-21 2 | 3 | rem Purpose: Implements a generic, dynamically sized collection as a doubly-linked list. 4 | rem Note that list does not have a fixed capacity, as additional nodes 5 | rem record can be allocated/freed on demand, depending on the requested 6 | rem operation. 7 | 8 | record node { 9 | next 10 | prev 11 | value 12 | 13 | proc init(value, next, prev){ 14 | set this.value to value 15 | set this.next to next 16 | set this.prev to prev 17 | } 18 | } 19 | 20 | record list { 21 | head 22 | tail 23 | size 24 | 25 | rem Purpose: Constructs a new list 26 | rem The list is initally empty. 27 | proc init { 28 | set this.size to 0 29 | } 30 | 31 | rem Purpose: Returns a reference to the fist element in the list. 32 | rem Returns null if the list is empty. 33 | proc front { 34 | if this.head == null { 35 | return null 36 | } 37 | return ref this.head.value 38 | } 39 | 40 | rem Purpose: Returns a reference to the last element in the list 41 | rem Returns null if the list is empty. 42 | proc back { 43 | if this.tail == null { 44 | return null 45 | } 46 | return ref this.tail.value 47 | } 48 | 49 | rem Purpose: Constructs a new list from an array 50 | rem Calls the default initializer, then calls the push_front method on every item in the array 51 | proc init(array){ 52 | goproc init as this 53 | set i to extern len(array) 54 | while dec i { 55 | goproc push_front as this(array[i]) 56 | } 57 | } 58 | 59 | rem Purpose: Pushes an item to the end of the list 60 | proc push_back(value) { 61 | set node to new node(value, null, null) 62 | if this.size == 0 { 63 | set this.head to node 64 | set this.tail to node 65 | } 66 | else { 67 | set node.prev to this.tail 68 | set this.tail.next to node 69 | set this.tail to node 70 | } 71 | inc this.size 72 | } 73 | 74 | rem Purpose: Pushes an item to the front of the list 75 | proc push_front(value) { 76 | set node to new node(value, null, null) 77 | if this.size == 0 { 78 | set this.head to node 79 | set this.tail to node 80 | } 81 | else { 82 | set node.next to this.head 83 | set this.head.prev to node 84 | set this.head to node 85 | } 86 | inc this.size 87 | } 88 | 89 | proc push_range(list) { 90 | if this.size == 0 { 91 | set this.head to list.head 92 | set this.tail to list.tail 93 | } 94 | else { 95 | set this.tail.next to list.head 96 | set list.head.prev to this.tail 97 | set this.tail to list.tail 98 | } 99 | set this.size to this.size + list.size 100 | set list to null 101 | } 102 | 103 | rem Purpose: Removes the back of the list and returns it's value 104 | rem Returns null if the list is empty 105 | proc pop_back { 106 | if this.size == 0 { 107 | return null 108 | } 109 | elif this.size == 1 { 110 | dec this.size 111 | set value to this.head.value 112 | set this.head to null 113 | set this.tail to null 114 | return value 115 | } 116 | dec this.size 117 | set value to this.tail.value 118 | set prev to this.tail.prev 119 | set prev.next to null 120 | set this.tail to prev 121 | return value 122 | } 123 | 124 | rem Purpose: Removes the front of the list and returns it's value 125 | rem Returns null if the list is empty 126 | proc pop_front { 127 | if this.size == 0 { 128 | return null 129 | } 130 | elif this.size == 1 { 131 | dec this.size 132 | set value to this.head.value 133 | set this.head to null 134 | set this.tail to null 135 | return value 136 | } 137 | dec this.size 138 | set value to this.head.value 139 | set next to this.head.next 140 | set next.prev to null 141 | set this.head to next 142 | return value 143 | } 144 | 145 | rem Purpose: Converts a list to an array 146 | rem The array is a copy, however each element is a reference to it's original list element 147 | proc array { 148 | set array to alloc[this.size] 149 | set current to this.head 150 | set i to 0 151 | while current { 152 | set array[i] to ref current.value 153 | inc i 154 | set current to ref current.next 155 | } 156 | return array 157 | } 158 | } -------------------------------------------------------------------------------- /src/chunk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "include/error.h" 4 | #include "include/chunk.h" 5 | 6 | void init_chunk(struct chunk* chunk, uint8_t* code, const uint64_t size) { 7 | chunk->pos = 0; 8 | chunk->size = size; 9 | chunk->code = code; 10 | chunk_read_opcode(chunk); 11 | } 12 | 13 | void free_chunk(struct chunk* chunk) { 14 | free(chunk->code); 15 | } 16 | 17 | const int init_chunk_builder(struct chunk_builder* builder) { 18 | ERROR_ALLOC_CHECK(builder->code_buf = malloc(100 * sizeof(uint8_t))); 19 | builder->size = 0; 20 | builder->alloc_size = 100; 21 | } 22 | 23 | struct chunk build_chunk(struct chunk_builder* builder) { 24 | struct chunk mychunk; 25 | init_chunk(&mychunk, builder->code_buf, builder->size); 26 | return mychunk; 27 | } 28 | 29 | const int chunk_write_ulong(struct chunk_builder* chunk_builder, const uint64_t ulong) { 30 | return chunk_write_size(chunk_builder, &ulong, sizeof(uint64_t)); 31 | } 32 | 33 | const int chunk_write_value(struct chunk_builder* chunk_builder, struct value value) { 34 | if (value.type == VALUE_TYPE_OBJ) 35 | return 0; 36 | chunk_write_size(chunk_builder, &value, sizeof(struct value)); 37 | return 1; 38 | } 39 | 40 | const int chunk_write_opcode(struct chunk_builder* chunk_builder, enum op_code opcode) { 41 | uint8_t small_cast = (uint8_t)opcode; 42 | ERROR_ALLOC_CHECK(chunk_write_size(chunk_builder, &small_cast, sizeof(uint8_t))); 43 | if (opcode == MACHINE_LABEL || opcode == MACHINE_COND_SKIP || opcode == MACHINE_FLAG_SKIP) 44 | ERROR_ALLOC_CHECK(chunk_write_size(chunk_builder, NULL, sizeof(uint64_t))); 45 | return 1; 46 | } 47 | 48 | const int chunk_write_bin_op(struct chunk_builder* chunk_builder, enum binary_operator bin_op) { 49 | uint8_t small_cast = (uint8_t)bin_op; 50 | return chunk_write_size(chunk_builder, &small_cast, sizeof(uint8_t)); 51 | } 52 | 53 | const int chunk_write_uni_op(struct chunk_builder* chunk_builder, enum unary_operator uni_op) { 54 | uint8_t small_cast = (uint8_t)uni_op; 55 | return chunk_write_size(chunk_builder, &small_cast, sizeof(uint8_t)); 56 | } 57 | 58 | const int chunk_write_chunk(struct chunk_builder* dest, struct chunk src, const int free_chunk) { 59 | ERROR_ALLOC_CHECK(chunk_write_size(dest, src.code, src.size)); 60 | if (free_chunk) 61 | free(src.code); 62 | return 1; 63 | } 64 | 65 | const void* chunk_write_size(struct chunk_builder* chunk_builder, const void* ptr, const uint64_t size) { 66 | if (chunk_builder->size + size >= chunk_builder->alloc_size) { 67 | void* new_ptr = realloc(chunk_builder->code_buf, (chunk_builder->alloc_size * 2 + size) * sizeof(uint8_t)); 68 | ERROR_ALLOC_CHECK(new_ptr); 69 | chunk_builder->code_buf = new_ptr; 70 | chunk_builder->alloc_size *= 2; 71 | chunk_builder->alloc_size += size; 72 | } 73 | void* dest = &chunk_builder->code_buf[chunk_builder->size]; 74 | if(ptr) 75 | memcpy(dest, ptr, size); 76 | chunk_builder->size += size; 77 | return dest; 78 | } 79 | 80 | const uint64_t chunk_read_ulong(struct chunk* chunk) { 81 | return *(uint64_t*)chunk_read_size(chunk, sizeof(uint64_t)); 82 | } 83 | 84 | const struct value chunk_read_value(struct chunk* chunk) { 85 | return *(struct value*)chunk_read_size(chunk, sizeof(struct value)); 86 | } 87 | 88 | const enum op_code chunk_read_opcode(struct chunk* chunk) { 89 | if (chunk->pos >= chunk->size) 90 | return chunk->last_code = MACHINE_END; 91 | return chunk->last_code = *(uint8_t*)chunk_read_size(chunk, sizeof(uint8_t)); 92 | } 93 | 94 | const enum binary_operator chunk_read_bin_op(struct chunk* chunk) { 95 | return *(uint8_t*)chunk_read_size(chunk, sizeof(uint8_t)); 96 | } 97 | 98 | const enum binary_operator chunk_read_uni_op(struct chunk* chunk) { 99 | return *(uint8_t*)chunk_read_size(chunk, sizeof(uint8_t)); 100 | } 101 | 102 | const void* chunk_read_size(struct chunk* chunk, const uint64_t size) { 103 | if (chunk->pos + size >= chunk->size) { 104 | chunk->last_code = MACHINE_END; 105 | if(chunk->pos + size > chunk->size) 106 | return NULL; 107 | } 108 | const void* position = &chunk->code[chunk->pos]; 109 | chunk->pos += size; 110 | return position; 111 | } 112 | 113 | void chunk_jump_to(struct chunk* chunk, const uint64_t pos) { 114 | if (pos >= chunk->size) { 115 | chunk->pos = chunk->size; 116 | chunk->last_code = MACHINE_END; 117 | } 118 | chunk->pos = pos; 119 | } -------------------------------------------------------------------------------- /src/globals.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "include/error.h" 4 | #include "include/machine.h" 5 | #include "include/globals.h" 6 | 7 | #define MAX_SIZE 255 8 | 9 | void init_global_cache(struct global_cache* global_cache) { 10 | global_cache->buckets = calloc(MAX_SIZE + 1, sizeof(struct cache_bucket*)); 11 | } 12 | 13 | void free_global_cache(struct global_cache* global_cache) { 14 | for (uint_fast8_t i = 0; i < MAX_SIZE; i++) { 15 | struct cache_bucket* bucket = global_cache->buckets[i]; 16 | while (bucket != NULL) 17 | { 18 | if (bucket->type == CACHE_TYPE_PROTO) { 19 | free_record_prototype(bucket->payload.prototype); 20 | free(bucket->payload.prototype); 21 | } 22 | struct cache_bucket* old = bucket; 23 | bucket = bucket->next; 24 | free(old); 25 | } 26 | } 27 | free(global_cache->buckets); 28 | } 29 | 30 | static const int insert_bucket(struct global_cache* global_cache, struct cache_bucket to_insert) { 31 | struct cache_bucket** bucket = &global_cache->buckets[to_insert.id & MAX_SIZE]; 32 | while (*bucket != NULL) 33 | { 34 | if ((*bucket)->id == to_insert.id && (*bucket)->type == to_insert.type) 35 | return 0; 36 | bucket = &(*bucket)->next; 37 | } 38 | *bucket = malloc(sizeof(struct cache_bucket)); 39 | ERROR_ALLOC_CHECK(*bucket); 40 | **bucket = to_insert; 41 | (*bucket)->next = NULL; 42 | return 1; 43 | } 44 | 45 | static struct cache_bucket* get_cache_bucket(struct global_cache* global_cache, uint64_t id, enum cache_type type) { 46 | struct cache_bucket* bucket = global_cache->buckets[id & MAX_SIZE]; 47 | while (bucket != NULL) 48 | { 49 | if (bucket->id == id && bucket->type == type) 50 | return bucket; 51 | bucket = bucket->next; 52 | } 53 | return NULL; 54 | } 55 | 56 | const int cache_insert_label(struct global_cache* global_cache, uint64_t id, uint64_t pos) { 57 | struct cache_bucket to_insert; 58 | to_insert.id = id; 59 | to_insert.type = CACHE_TYPE_POS; 60 | to_insert.payload.pos = pos; 61 | return insert_bucket(global_cache, to_insert); 62 | } 63 | 64 | uint64_t cache_retrieve_pos(struct global_cache* global_cache, uint64_t id) { 65 | struct cache_bucket* bucket = get_cache_bucket(global_cache, id, CACHE_TYPE_POS); 66 | if (bucket) 67 | return bucket->payload.pos; 68 | return 0; 69 | } 70 | 71 | struct record_prototype* cache_insert_prototype(struct global_cache* global_cache, uint64_t id) { 72 | struct cache_bucket* bucket = get_cache_bucket(global_cache, id, CACHE_TYPE_PROTO); 73 | if (!bucket) { 74 | struct cache_bucket to_insert; 75 | to_insert.id = id; 76 | to_insert.type = CACHE_TYPE_PROTO; 77 | to_insert.payload.prototype = malloc(sizeof(struct record_prototype)); 78 | ERROR_ALLOC_CHECK(to_insert.payload.prototype); 79 | init_record_prototype(to_insert.payload.prototype, id); 80 | insert_bucket(global_cache, to_insert); 81 | return to_insert.payload.prototype; 82 | } 83 | else 84 | return bucket->payload.prototype; 85 | } 86 | 87 | const int cache_init_record(struct global_cache* global_cache, uint64_t proto_id, struct record* record, struct machine* machine) { 88 | struct cache_bucket* bucket = get_cache_bucket(global_cache, proto_id, CACHE_TYPE_PROTO); 89 | if (bucket) { 90 | init_record(record, bucket->payload.prototype, machine); 91 | return 1; 92 | } 93 | return 0; 94 | } 95 | 96 | const int cache_merge_proto(struct global_cache* global_cache, uint64_t child, uint64_t parent) { 97 | struct cache_bucket* child_bucket = get_cache_bucket(global_cache, child, CACHE_TYPE_PROTO); 98 | struct cache_bucket* parent_bucket = get_cache_bucket(global_cache, parent, CACHE_TYPE_PROTO); 99 | if (child_bucket && parent_bucket) 100 | return record_inherit(child_bucket->payload.prototype, parent_bucket->payload.prototype); 101 | return 0; 102 | } 103 | 104 | const int cache_declare_builtin(struct global_cache* global_cache, uint64_t id, DECL_BUILT_IN(*delegate)) { 105 | struct cache_bucket to_insert; 106 | to_insert.id = id; 107 | to_insert.type = CACHE_TYPE_BUILTIN; 108 | to_insert.payload.delegate = delegate; 109 | return insert_bucket(global_cache, to_insert); 110 | } 111 | 112 | struct value cache_invoke_builtin(struct global_cache* global_cache, uint64_t id, struct value** argv, uint32_t argc, struct machine* machine) { 113 | struct cache_bucket* bucket = get_cache_bucket(global_cache, id, CACHE_TYPE_BUILTIN); 114 | if (bucket) 115 | return (*bucket->payload.delegate)(argv, argc, machine); 116 | return const_value_null; 117 | } -------------------------------------------------------------------------------- /src/io.c: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_DEPRECATE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "include/error.h" 8 | #include "include/io.h" 9 | 10 | static const char* error_names[] = { 11 | "Program Success", 12 | "Out of Memory", 13 | "Insufficient Evaluations", 14 | "Insufficient Call Size", 15 | "Unrecognized/Invalid OpCode", 16 | 17 | "Attempted Label Redefine", 18 | "Undefined Label", 19 | "Attempted Record-Prototype Redefine", 20 | "Undefined Record-Prototype", 21 | "Attempted Record-Property Redefine", 22 | "Unedfined Record-Property", 23 | 24 | "Unexpected Type", 25 | "Index out of Range", 26 | "Stack Overflow", 27 | "Undefined Variable", 28 | 29 | "Unrecognized Token", 30 | "Unrecognized Control-Sequence", 31 | "Unexpected Character", 32 | "Unexpected Token", 33 | 34 | "Cannot Open File" 35 | }; 36 | 37 | static void print_data_char(const char data_char) { 38 | switch (data_char) 39 | { 40 | case '\t': 41 | printf("\\t"); 42 | break; 43 | case '\r': 44 | printf("\\r"); 45 | break; 46 | case '\n': 47 | printf("\\n"); 48 | break; 49 | case '\b': 50 | printf("\\b"); 51 | break; 52 | case 0: 53 | printf("\\0"); 54 | break; 55 | case '\\': 56 | printf("\\"); 57 | break; 58 | case '\'': 59 | printf("\\\'"); 60 | break; 61 | case '\"': 62 | printf("\\\""); 63 | break; 64 | default: 65 | printf("%c", data_char); 66 | } 67 | } 68 | 69 | static const int print_str(struct collection str, const int print_mode) { 70 | for (uint_fast64_t i = 0; i < str.size; i++) { 71 | if (str.inner_collection[i]->type != VALUE_TYPE_CHAR) 72 | return 0; 73 | if (print_mode) 74 | printf("%c", str.inner_collection[i]->payload.character); 75 | else 76 | print_data_char(str.inner_collection[i]->payload.character); 77 | } 78 | return 1; 79 | } 80 | 81 | static const int is_str(struct value value) { 82 | if (!IS_COLLECTION(value)) 83 | return 0; 84 | struct collection* collection = value.payload.object.ptr.collection; 85 | for (uint_fast64_t i = 0; i < collection->size; i++) 86 | if (collection->inner_collection[i]->type != VALUE_TYPE_CHAR) 87 | return 0; 88 | return 1; 89 | } 90 | 91 | static void print_collection(struct collection collection) { 92 | printf("%c", '['); 93 | for (uint_fast64_t i = 0; i < collection.size; i++) { 94 | if(i) 95 | printf(", "); 96 | print_value(*collection.inner_collection[i], 0); 97 | } 98 | printf("%c", ']'); 99 | } 100 | 101 | static void print_record(struct record record) { 102 | printf("<%p>", &record); 103 | for (uint_fast8_t i = 0; i < record.prototype->size; i++) { 104 | printf("\n\t"); 105 | print_value(*record.properties[i], 0); 106 | } 107 | } 108 | 109 | void print_value(struct value value, const int print_mode) { 110 | if (value.type == VALUE_TYPE_NUM) 111 | printf("%g", value.payload.numerical); 112 | else if (value.type == VALUE_TYPE_CHAR) { 113 | if (print_mode) 114 | printf("%c", value.payload.character); 115 | else { 116 | printf("\'"); 117 | print_data_char(value.payload.character); 118 | printf("\'"); 119 | } 120 | } 121 | else if (value.type == VALUE_TYPE_NULL) 122 | printf("null"); 123 | else if (value.type == VALUE_TYPE_ID) 124 | printf("identifier(%" PRIu64 ")", value.payload.identifier); 125 | else if (IS_COLLECTION(value)) 126 | if (is_str(value)) 127 | print_str(*value.payload.object.ptr.collection, print_mode); 128 | else 129 | print_collection(*value.payload.object.ptr.collection); 130 | else if (IS_RECORD(value)) 131 | if (print_mode) 132 | print_record(*value.payload.object.ptr.record); 133 | else 134 | printf("<%p>", value.payload.object.ptr.record); 135 | else 136 | printf("[Print Error]"); 137 | } 138 | 139 | const int error_info(enum error error) { 140 | printf("Error: %s",error_names[error]); 141 | char* doc_path = malloc(25); 142 | ERROR_ALLOC_CHECK(doc_path); 143 | 144 | sprintf(doc_path, "docs/error%d.txt", error); 145 | 146 | FILE* infile = fopen(doc_path, "rb"); 147 | free(doc_path); 148 | 149 | if (infile) { 150 | fseek(infile, 0, SEEK_END); 151 | uint64_t fsize = ftell(infile); 152 | fseek(infile, 0, SEEK_SET); 153 | char* source = malloc(fsize + 1); 154 | ERROR_ALLOC_CHECK(source); 155 | fread(source, 1, fsize, infile); 156 | fclose(infile); 157 | source[fsize] = 0; 158 | printf("\n%s",source); 159 | free(source); 160 | fclose(infile); 161 | } 162 | else 163 | printf("\nNo local help documentation found. Please refer to, https://github.com/TheRealMichaelWang/minima/wiki/Lists, for more information."); 164 | return 1; 165 | } -------------------------------------------------------------------------------- /examples/symbol/core.txt: -------------------------------------------------------------------------------- 1 | include "symbol/util.txt" 2 | include "symbol/compare.txt" 3 | include "symbol/simplify.txt" 4 | 5 | rem Purpose: An abstract record that represents items in an mathematical expression 6 | rem Practically, it implements operators for all it's derived records 7 | 8 | record eq_entity { 9 | proc + (op) { 10 | if op_order { 11 | return goproc simplify as new eq_op_add(this, op) 12 | } 13 | else { 14 | return goproc simplify as new eq_op_add(op, this) 15 | } 16 | } 17 | 18 | proc - (op) { 19 | if op_order { 20 | return goproc simplify as new eq_op_subtract(this, op) 21 | } 22 | else { 23 | return goproc simplify as new eq_op_subtract(op, this) 24 | } 25 | } 26 | 27 | proc * (op) { 28 | if op_order { 29 | return goproc simplify as new eq_op_multiply(this, op) 30 | } 31 | else { 32 | return goproc simplify as new eq_op_multiply(op, this) 33 | } 34 | } 35 | 36 | proc / (op) { 37 | if op_order { 38 | return goproc simplify as new eq_op_divide(this, op) 39 | } 40 | else { 41 | return goproc simplify as new eq_op_divide(op, this) 42 | } 43 | } 44 | 45 | proc % (op) { 46 | if op_order { 47 | return goproc simplify as new eq_op_mod(this, op) 48 | } 49 | else { 50 | return goproc simplify as new eq_op_mod(op, this) 51 | } 52 | } 53 | 54 | proc ^ (op) { 55 | if op_order { 56 | return goproc simplify as new eq_op_pow(this, op) 57 | } 58 | else { 59 | return goproc simplify as new eq_op_pow(op, this) 60 | } 61 | } 62 | 63 | proc - { 64 | return -1 * this 65 | } 66 | } 67 | 68 | rem Purpose: An abstract record that represents an singlar expression item. 69 | rem Derived records included numbers and variables. 70 | record eq_leaf extends eq_entity { 71 | obj 72 | 73 | proc init(obj) { 74 | set this.obj to obj 75 | } 76 | } 77 | 78 | rem Purpose: An abstract record that represents an expression with more than one child 79 | rem Derived records include unary operators and binary operators 80 | record eq_node extends eq_entity { 81 | children 82 | 83 | proc init(children) { 84 | set this.children to children 85 | } 86 | } 87 | 88 | rem Purpose: An abstract record that implements a binary operator. 89 | record eq_bin_op extends eq_node { 90 | left 91 | right 92 | op_prec 93 | 94 | proc init(left, right, op_prec) { 95 | set this.left to goproc num_check(left) 96 | set this.right to goproc num_check(right) 97 | set this.op_prec to op_prec 98 | goproc init as this.base({this.left, this.right}) 99 | } 100 | } 101 | 102 | rem Purpose: An abstract record that implements a unary operator 103 | record eq_uni_op extends eq_node { 104 | operand 105 | 106 | proc init(operand) { 107 | set this.operand to goproc num_check(operand) 108 | goproc init as this.base({this.operand}) 109 | } 110 | } 111 | 112 | rem Purpose: A record that implements an abstract variable 113 | record eq_var extends eq_leaf 114 | 115 | rem Purpose: A record that implements an number in an expression 116 | record eq_num extends eq_leaf 117 | 118 | rem Purpose: A record that implements an addition operator 119 | record eq_op_add extends eq_bin_op { 120 | proc init(left, right) { 121 | goproc init as this.base(left, right, 0) 122 | } 123 | } 124 | 125 | rem Purpose: A record that implements the subtraction operator 126 | record eq_op_subtract extends eq_bin_op { 127 | proc init(left, right) { 128 | goproc init as this.base(left, right, 1) 129 | } 130 | } 131 | 132 | rem Purpose: A record that implements the multiplication operator 133 | record eq_op_multiply extends eq_bin_op { 134 | proc init(left, right) { 135 | goproc init as this.base(left, right, 2) 136 | } 137 | } 138 | 139 | rem Purpose: A record that implements the division operator 140 | record eq_op_divide extends eq_bin_op { 141 | proc init(left, right) { 142 | goproc init as this.base(left, right, 3) 143 | } 144 | } 145 | 146 | rem Purpose: A record that implements the remainder, not absoloute value operator 147 | record eq_op_mod extends eq_bin_op { 148 | proc init(left, right) { 149 | goproc init as this.base(left, right, 3) 150 | } 151 | } 152 | 153 | rem Purpose: A record that implements the power operator 154 | record eq_op_pow extends eq_bin_op { 155 | proc init(left, right) { 156 | goproc init as this.base(left, right, 4) 157 | } 158 | } 159 | 160 | rem Purpose: A record that implements the logarithm operator 161 | record eq_op_log extends eq_bin_op { 162 | proc init(val, base) { 163 | goproc init as this.base(val, base, 4) 164 | } 165 | } 166 | 167 | rem Purpose: A record that implements the absoloute value operator 168 | record eq_op_abs extends eq_uni_op { 169 | proc init(operand) { 170 | goproc init as this.base(operand) 171 | } 172 | } -------------------------------------------------------------------------------- /examples/symbol/match.txt: -------------------------------------------------------------------------------- 1 | include "map.min" 2 | include "symbol/core.txt" 3 | include "symbol/sort.txt" 4 | 5 | record match extends eq_entity{ 6 | id 7 | expected_type 8 | 9 | proc init(id, expected_type) { 10 | set this.id to id 11 | set this.expected_type to expected_type 12 | } 13 | 14 | proc match(eq_entity, match_map) { 15 | if this.expected_type != #any { 16 | if extern typeof(eq_entity) != extern typeof(this.expected_type) { 17 | return false 18 | } 19 | } 20 | 21 | set matched_ent to goproc find as match_map(this.id) 22 | if matched_ent { 23 | return goproc compare as matched_ent(eq_entity) 24 | } 25 | else { 26 | goproc insert as match_map(this.id, eq_entity) 27 | return true 28 | } 29 | } 30 | 31 | proc substitute(match_map) { 32 | return goproc find as match_map(this.id) 33 | } 34 | } 35 | 36 | record eq_leaf { 37 | proc substitute(match_map) { 38 | return this 39 | } 40 | 41 | proc match(eq_entity, match_map) { 42 | return goproc compare as this(eq_entity) 43 | } 44 | } 45 | 46 | record eq_bin_op { 47 | proc match(eq_entity, match_map) { 48 | if extern typeof(eq_entity) != extern typeof(this) { 49 | return false 50 | } 51 | return goproc match as this.left(eq_entity.left, match_map) and 52 | goproc match as this.right(eq_entity.right, match_map) 53 | } 54 | } 55 | 56 | record eq_uni_op { 57 | proc match(eq_entity, match_map) { 58 | if extern typeof(eq_entity) != extern typeof(this) { 59 | return false 60 | } 61 | return goproc match as this.operand(eq_entity.operand, match_map) 62 | } 63 | } 64 | 65 | record eq_op_add { 66 | proc substitute(match_map) { 67 | set lhs to goproc substitute as this.left(match_map) 68 | set rhs to goproc substitute as this.right(match_map) 69 | if !lhs or !rhs { 70 | return null 71 | } 72 | return lhs + rhs 73 | } 74 | } 75 | 76 | record eq_op_subtract { 77 | proc substitute(match_map) { 78 | set lhs to goproc substitute as this.left(match_map) 79 | set rhs to goproc substitute as this.right(match_map) 80 | if !lhs or !rhs { 81 | return null 82 | } 83 | return lhs - rhs 84 | } 85 | } 86 | 87 | record eq_op_multiply { 88 | proc substitute(match_map) { 89 | set lhs to goproc substitute as this.left(match_map) 90 | set rhs to goproc substitute as this.right(match_map) 91 | if !lhs or !rhs { 92 | return null 93 | } 94 | return lhs * rhs 95 | } 96 | } 97 | 98 | record eq_op_divide { 99 | proc substitute(match_map) { 100 | set lhs to goproc substitute as this.left(match_map) 101 | set rhs to goproc substitute as this.right(match_map) 102 | if !lhs or !rhs { 103 | return null 104 | } 105 | return lhs / rhs 106 | } 107 | } 108 | 109 | record eq_op_mod { 110 | proc substitute(match_map) { 111 | set lhs to goproc substitute as this.left(match_map) 112 | set rhs to goproc substitute as this.right(match_map) 113 | if !lhs or !rhs { 114 | return null 115 | } 116 | return lhs % rhs 117 | } 118 | } 119 | 120 | record eq_op_pow { 121 | proc substitute(match_map) { 122 | set lhs to goproc substitute as this.left(match_map) 123 | set rhs to goproc substitute as this.right(match_map) 124 | if !lhs or !rhs { 125 | return null 126 | } 127 | return lhs ^ rhs 128 | } 129 | } 130 | 131 | record eq_op_log { 132 | proc substitute(match_map) { 133 | set lhs to goproc substitute as this.left(match_map) 134 | set rhs to goproc substitute as this.right(match_map) 135 | if !lhs or !rhs { 136 | return null 137 | } 138 | return goproc log(lhs, rhs) 139 | } 140 | } 141 | 142 | record eq_op_abs { 143 | proc substitute(match_map) { 144 | set operand to goproc substitute as this.operand(match_map) 145 | if !operand { 146 | return null 147 | } 148 | return goproc abs(operand) 149 | } 150 | } 151 | 152 | record rule { 153 | match_rule 154 | replace_rule 155 | 156 | proc init(match_rule, replace_rule) { 157 | set this.match_rule to match_rule 158 | set this.replace_rule to replace_rule 159 | } 160 | 161 | proc apply(eq_entity) { 162 | set match_map to new map 163 | if goproc match as this.match_rule(eq_entity, match_map) { 164 | return goproc substitute as this.replace_rule(match_map) 165 | } 166 | return null 167 | } 168 | } 169 | 170 | record rule_set { 171 | rules 172 | 173 | proc apply(eq_entity) { 174 | set simp_flag to true 175 | while simp_flag { 176 | set simp_flag to false 177 | 178 | if extern implements(eq_entity, #eq_bin_op) { 179 | set eq_entity.left to goproc sort_apply as this(eq_entity.left) 180 | set eq_entity.right to goproc sort_apply as this(eq_entity.right) 181 | } 182 | elif extern implements(eq_entity, #eq_uni_op) { 183 | set eq_entity.operand to goproc sort_apply as this(eq_entity.operand) 184 | } 185 | else { 186 | return eq_entity 187 | } 188 | 189 | set i to extern len(this.rules) 190 | while dec i { 191 | set m_stat to goproc apply as this.rules[i](eq_entity) 192 | if m_stat { 193 | set eq_entity to m_stat 194 | set simp_flag to true 195 | inc i 196 | } 197 | } 198 | } 199 | return eq_entity 200 | } 201 | 202 | proc sort_apply(eq_entity) { 203 | set sorted to goproc sort_terms(goproc terms as eq_entity, 3) 204 | return goproc apply as this(sorted) 205 | } 206 | } -------------------------------------------------------------------------------- /src/machine.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "include/error.h" 5 | #include "include/builtins.h" 6 | #include "include/opcodes.h" 7 | #include "include/machine.h" 8 | 9 | const int init_machine(struct machine* machine) { 10 | ERROR_ALLOC_CHECK(machine->position_stack = malloc(MACHINE_MAX_POSITIONS * sizeof(uint64_t))); 11 | ERROR_ALLOC_CHECK(machine->position_flags = malloc(MACHINE_MAX_POSITIONS * sizeof(uint8_t))); 12 | ERROR_ALLOC_CHECK(machine->var_stack = malloc(MACHINE_MAX_CALLS * sizeof(struct var_context))); 13 | ERROR_ALLOC_CHECK(machine->evaluation_stack = malloc(MACHINE_MAX_EVALS * sizeof(struct value*))); 14 | ERROR_ALLOC_CHECK(machine->constant_stack = malloc(MACHINE_MAX_EVALS * sizeof(struct value))); 15 | 16 | machine->evals = 0; 17 | machine->constants = 0; 18 | machine->positions = 0; 19 | machine->std_flag = 0; 20 | machine->call_size = 0; 21 | 22 | ERROR_ALLOC_CHECK(init_gcollect(&machine->garbage_collector)); 23 | init_global_cache(&machine->global_cache); 24 | 25 | cache_declare_builtin(&machine->global_cache, 210724587794, builtin_print); 26 | cache_declare_builtin(&machine->global_cache, 6953911397310, builtin_print_line); 27 | cache_declare_builtin(&machine->global_cache, 6954037470346, builtin_system_cmd); 28 | cache_declare_builtin(&machine->global_cache, 6953969676070, builtin_random); 29 | cache_declare_builtin(&machine->global_cache, 210716150453, builtin_get_input); 30 | cache_declare_builtin(&machine->global_cache, 193498052, builtin_get_length); 31 | cache_declare_builtin(&machine->global_cache, 6385287881, builtin_get_hash); 32 | cache_declare_builtin(&machine->global_cache, 193506174, builtin_to_str); 33 | cache_declare_builtin(&machine->global_cache, 193500757, builtin_to_num); 34 | cache_declare_builtin(&machine->global_cache, 6954076481916, builtin_get_type); 35 | cache_declare_builtin(&machine->global_cache, 8246457940939440931, builtin_implements); 36 | cache_declare_builtin(&machine->global_cache, 193485979, builtin_abs); 37 | cache_declare_builtin(&machine->global_cache, 6385112226, builtin_ceil); 38 | cache_declare_builtin(&machine->global_cache, 210712519527, builtin_floor); 39 | return 1; 40 | } 41 | 42 | void machine_reset(struct machine* machine) { 43 | machine->evals = 0; 44 | machine->constants = 0; 45 | machine->positions = 0; 46 | while (machine->call_size > 1) 47 | free_var_context(&machine->var_stack[--machine->call_size]); 48 | } 49 | 50 | void free_machine(struct machine* machine) { 51 | machine_reset(machine); 52 | if (machine->call_size > 0) 53 | free_var_context(&machine->var_stack[--machine->call_size]); 54 | 55 | free(machine->position_stack); 56 | free(machine->position_flags); 57 | free(machine->var_stack); 58 | free(machine->evaluation_stack); 59 | free(machine->constant_stack); 60 | 61 | free_gcollect(&machine->garbage_collector); 62 | free_global_cache(&machine->global_cache); 63 | } 64 | 65 | struct value* machine_pop_eval(struct machine* machine) { 66 | if (!machine->evals) 67 | return NULL; 68 | 69 | struct value* top = machine->evaluation_stack[--machine->evals]; 70 | 71 | if (top->gc_flag == GARBAGE_CONSTANT) { 72 | if (!machine->constants) 73 | return NULL; 74 | 75 | machine->constants--; 76 | if (top->type == VALUE_TYPE_OBJ) { 77 | uint64_t children_count; 78 | struct value**children = object_get_children(&top->payload.object, &children_count); 79 | 80 | while (children_count--) 81 | if (children[children_count]->gc_flag == GARBAGE_CONSTANT) 82 | machine_pop_eval(machine); 83 | } 84 | } 85 | return top; 86 | } 87 | 88 | struct value* machine_push_eval(struct machine* machine, struct value* value) 89 | { 90 | if (machine->evals == MACHINE_MAX_EVALS) 91 | return NULL; 92 | if (value->gc_flag == GARBAGE_CONSTANT) 93 | return machine_push_const(machine, *value, 1); 94 | else 95 | return machine->evaluation_stack[machine->evals++] = value; 96 | } 97 | 98 | struct value* machine_push_const(struct machine* machine, struct value const_value, int push_obj_children) { 99 | if (machine->evals == MACHINE_MAX_EVALS) 100 | return NULL; 101 | 102 | if (push_obj_children && const_value.type == VALUE_TYPE_OBJ) { 103 | uint64_t children_count; 104 | struct value** children = object_get_children(&const_value.payload.object, &children_count); 105 | 106 | for (uint_fast64_t i = 0; i < children_count; i++) 107 | if (children[i]->gc_flag == GARBAGE_CONSTANT) 108 | if(!(children[i] = machine_push_const(machine, *children[i], 1))) 109 | return NULL; 110 | } 111 | machine->constant_stack[machine->constants] = const_value; 112 | machine->constant_stack[machine->constants].gc_flag = GARBAGE_CONSTANT; 113 | return machine->evaluation_stack[machine->evals++] = &machine->constant_stack[machine->constants++]; 114 | } 115 | 116 | const int machine_condition_check(struct machine* machine) { 117 | struct value*valptr = machine_pop_eval(machine); 118 | if (!valptr) 119 | return 0; 120 | 121 | int cond = 1; 122 | if (valptr->type == VALUE_TYPE_NULL || (valptr->type == VALUE_TYPE_NUM && valptr->payload.numerical == 0)) 123 | cond = 0; 124 | 125 | return cond; 126 | } 127 | 128 | const enum error machine_execute(struct machine* machine, struct chunk* chunk) { 129 | while (chunk->last_code != MACHINE_END) 130 | { 131 | if (!handle_opcode(chunk->last_code, machine, chunk)) 132 | return machine->last_err; 133 | chunk_read_opcode(chunk); 134 | } 135 | return ERROR_SUCCESS; 136 | } -------------------------------------------------------------------------------- /src/source.c: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_DEPRECATE 2 | //buffer security is imoortant, but not as much as portability 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "include/debug.h" 9 | #include "include/hash.h" 10 | #include "include/compiler.h" 11 | #include "include/opcodes.h" 12 | #include "include/machine.h" 13 | 14 | static struct machine machine; 15 | 16 | static uint32_t block_check(const char* src, uint32_t length) { 17 | uint32_t blocks = 0; 18 | for (uint32_t i = 0; i < length; i++) { 19 | if (src[i] == '{') 20 | blocks++; 21 | else if (src[i] == '}') 22 | blocks--; 23 | } 24 | return blocks; 25 | } 26 | 27 | int main(uint32_t argc, char** argv) { 28 | if (!init_machine(&machine)) 29 | exit(EXIT_FAILURE); 30 | struct compiler compiler; 31 | struct loc_table loc_table; 32 | 33 | size_t i = strlen(argv[0]); 34 | while (i--) 35 | if (argv[0][i] == '\\' || argv[0][i] == '/' || !i) 36 | break; 37 | if(i) 38 | argv[0][i + 1] = 0; 39 | 40 | if (argc > 1) { 41 | FILE* infile = fopen(argv[1], "rb"); 42 | if (!infile) { 43 | printf("Minima couldn't open the file, \"%s\".", argv[1]); 44 | exit(EXIT_FAILURE); 45 | } 46 | fseek(infile, 0, SEEK_END); 47 | uint64_t fsize = ftell(infile); 48 | fseek(infile, 0, SEEK_SET); 49 | char* source = malloc(fsize + 1); 50 | if (source == NULL) { 51 | fclose(infile); 52 | exit(EXIT_FAILURE); 53 | } 54 | fread(source, 1, fsize, infile); 55 | fclose(infile); 56 | source[fsize] = 0; 57 | 58 | init_compiler(&compiler, &machine, argv[0], source, argv[1]); 59 | init_loc_table(&loc_table, argv[1]); 60 | 61 | compiler.imported_file_hashes[compiler.imported_files++] = hash(argv[1], strlen(argv[1])); 62 | 63 | if (!compile(&compiler, &loc_table, 0, 1, 0)) { 64 | printf("\n***Syntax Error***\n"); 65 | error_info(compiler.last_err); 66 | printf("\n\n"); 67 | debug_print_scanner(compiler.scanner); 68 | } 69 | else { 70 | enum error err = machine_execute(&machine, &compiler.result); 71 | if (err != ERROR_SUCCESS) { 72 | printf("\n***Runtime Error***\n"); 73 | error_info(err); 74 | printf("\n"); 75 | loc_table_finalize(&loc_table, &compiler, 0); 76 | debug_print_trace(&machine, &loc_table, compiler.result.pos); 77 | } 78 | } 79 | free_loc_table(&loc_table); 80 | free(source); 81 | 82 | if (argc > 2 && !strcmp(argv[2], "--debug")) { 83 | printf("\nPress ENTER to exit."); 84 | while(getchar() != '\n'); 85 | } 86 | } 87 | else { 88 | printf("North-Hollywood Minima, version 1.0\n"); 89 | printf("Written by Michael Wang in 2021\n\n"); 90 | printf("Type \"dump\" to produce a bytecode dump of your current program. Type \"quit\" to exit.\n\n"); 91 | 92 | printf("Include Directory: %s\n\n", argv[0]); 93 | printf("READY\n"); 94 | 95 | struct chunk_builder global_build; //contains our source 96 | uint64_t ip = 0; 97 | uint32_t imported_files = 0; 98 | init_chunk_builder(&global_build); 99 | chunk_write_opcode(&global_build, MACHINE_NEW_FRAME); 100 | init_loc_table(&loc_table, NULL); 101 | loc_table.global_offset = 1; 102 | 103 | while (1) 104 | { 105 | char src_buf[4096]; 106 | uint32_t index = 0; 107 | printf("\n"); 108 | while (block_check(src_buf, index) || !index) 109 | { 110 | printf(">>> "); 111 | while (scanf("%c", &src_buf[index])) { 112 | if (src_buf[index] == '\n') 113 | break; 114 | index++; 115 | } 116 | src_buf[index++] = '\n'; 117 | } 118 | src_buf[index] = 0; 119 | 120 | if (!strcmp(src_buf, "quit\n")) 121 | break; 122 | else if (!strcmp(src_buf, "dump\n")) { 123 | printf("\nGLOBAL DUMP:\n"); 124 | struct chunk global_chunk = build_chunk(&global_build); 125 | global_chunk.pos = ip; 126 | debug_print_dump(global_chunk); 127 | } 128 | else { 129 | init_compiler(&compiler, &machine, argv[0], src_buf, NULL); 130 | compiler.imported_files = imported_files; 131 | 132 | if (!compile(&compiler, &loc_table, 1, 1, global_build.size)) { 133 | loc_table_dispose(&loc_table); 134 | printf("\n***Syntax Error***\n"); 135 | error_info(compiler.last_err); 136 | printf("\n\n"); 137 | debug_print_scanner(compiler.scanner); 138 | printf("\n"); 139 | } 140 | else { 141 | chunk_write_chunk(&global_build, compiler.result, 1); 142 | imported_files = compiler.imported_files; 143 | 144 | struct chunk global_chunk = build_chunk(&global_build); 145 | chunk_jump_to(&global_chunk, ip); 146 | chunk_read_opcode(&global_chunk); 147 | 148 | enum error err = machine_execute(&machine, &global_chunk); 149 | if (err != ERROR_SUCCESS) { 150 | loc_table_dispose(&loc_table); 151 | printf("\n***Runtime Error***\n"); 152 | error_info(err); 153 | printf("\n"); 154 | 155 | loc_table_finalize(&loc_table, &compiler, 0); 156 | debug_print_trace(&machine, &loc_table, global_chunk.pos); 157 | 158 | global_build.size = ip; 159 | machine_reset(&machine); 160 | } 161 | else { 162 | ip = global_chunk.pos; 163 | loc_table_finalize(&loc_table, &compiler, 1); 164 | } 165 | } 166 | loc_table.global_offset = ip; 167 | } 168 | } 169 | struct chunk global_chunk = build_chunk(&global_build); 170 | free_chunk(&global_chunk); 171 | free_loc_table(&loc_table); 172 | } 173 | free_machine(&machine); 174 | exit(EXIT_SUCCESS); 175 | } -------------------------------------------------------------------------------- /src/builtins.c: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "include/io.h" 8 | #include "include/hash.h" 9 | #include "include/error.h" 10 | #include "include/machine.h" 11 | #include "include/object.h" 12 | #include "include/builtins.h" 13 | 14 | static char* value_to_str(struct value value) { 15 | if (!IS_COLLECTION(value)) 16 | return NULL; 17 | 18 | struct collection* collection = value.payload.object.ptr.collection; 19 | 20 | char* buffer = malloc((collection->size + 1) * sizeof(char)); 21 | ERROR_ALLOC_CHECK(buffer); 22 | 23 | for (uint64_t i = 0; i < collection->size; i++) 24 | buffer[i] = collection->inner_collection[i]->payload.character; 25 | buffer[collection->size] = 0; 26 | 27 | return buffer; 28 | } 29 | 30 | struct value str_to_value(const char* buffer, const size_t length, struct machine* machine) { 31 | struct value toret; 32 | struct collection* collection = malloc(sizeof(struct collection)); 33 | if (collection == NULL) 34 | return const_value_null; 35 | 36 | init_collection(collection, length); 37 | for(uint_fast32_t i = 0; i < length; i++) 38 | collection->inner_collection[i] = machine_push_const(machine, CHAR_VALUE(buffer[i]), 0); 39 | 40 | struct object obj; 41 | init_object_col(&obj, collection); 42 | init_obj_value(&toret, obj); 43 | return toret; 44 | } 45 | 46 | DECL_BUILT_IN(builtin_print) { 47 | for (uint64_t i = 0; i < argc; i++) { 48 | print_value(*argv[i], 1); 49 | } 50 | return const_value_null; 51 | } 52 | 53 | DECL_BUILT_IN(builtin_print_line) { 54 | builtin_print(argv, argc, machine); 55 | printf("\n"); 56 | return const_value_null; 57 | } 58 | 59 | DECL_BUILT_IN(builtin_system_cmd) { 60 | if (argc < 1) 61 | return const_value_null; 62 | 63 | char* buffer = value_to_str(*argv[0]); 64 | if(buffer == NULL) 65 | return const_value_null; 66 | 67 | system(buffer); 68 | free(buffer); 69 | 70 | return const_value_null; 71 | } 72 | 73 | DECL_BUILT_IN(builtin_random) { 74 | double random_double = (double)rand() / RAND_MAX; 75 | return NUM_VALUE(random_double); 76 | } 77 | 78 | DECL_BUILT_IN(builtin_get_input) { 79 | char format_flag = 0; 80 | if (argc > 0 && argv[0]->type == VALUE_TYPE_CHAR) 81 | format_flag = argv[0]->payload.character; 82 | 83 | char buffer[4096]; 84 | uint32_t length = 0; 85 | while (scanf("%c", &buffer[length])) { 86 | if (buffer[length] == '\n') 87 | break; 88 | length++; 89 | } 90 | buffer[length] = 0; 91 | 92 | if (format_flag == 'n' || format_flag == 'N') 93 | return NUM_VALUE(strtod(buffer, NULL)); 94 | else 95 | return str_to_value(buffer, length, machine); 96 | } 97 | 98 | DECL_BUILT_IN(builtin_get_length) { 99 | if (argc < 1 || !IS_COLLECTION(*argv[0])) 100 | return const_value_null; 101 | return NUM_VALUE(argv[0]->payload.object.ptr.collection->size); 102 | } 103 | 104 | DECL_BUILT_IN(builtin_get_hash) { 105 | if (argc == 1) 106 | return NUM_VALUE(value_hash(*argv[0])); 107 | else if (argc == 2) 108 | return NUM_VALUE(combine_hash(argv[0]->payload.numerical, argv[1]->payload.numerical)); 109 | return const_value_null; 110 | } 111 | 112 | DECL_BUILT_IN(builtin_to_num) { 113 | if (argc < 1) 114 | return const_value_null; 115 | 116 | char* buffer = value_to_str(*argv[0]); 117 | if (buffer == NULL) 118 | return const_value_null; 119 | struct value toret = NUM_VALUE(strtod(buffer, NULL)); 120 | free(buffer); 121 | return toret; 122 | } 123 | 124 | DECL_BUILT_IN(builtin_to_str) { 125 | if (argc < 1) 126 | return const_value_null; 127 | 128 | if (argv[0]->type != VALUE_TYPE_NUM) 129 | return const_value_null; 130 | 131 | char* buffer = malloc(150); 132 | if (buffer == NULL) 133 | return const_value_null; 134 | 135 | sprintf(buffer, "%g", argv[0]->payload.numerical); 136 | struct value toret = str_to_value(buffer, strlen(buffer), machine); 137 | free(buffer); 138 | 139 | return toret; 140 | } 141 | 142 | DECL_BUILT_IN(builtin_get_type) { 143 | if (argc < 1) 144 | return const_value_null; 145 | if (!IS_RECORD(*argv[0]) && argv[0]->type != VALUE_TYPE_ID) 146 | return CHAR_VALUE(argv[0]->type); 147 | uint64_t type_hash = combine_hash(VALUE_TYPE_OBJ, argv[0]->type == VALUE_TYPE_ID ? argv[0]->payload.identifier : argv[0]->payload.object.ptr.record->prototype->identifier); 148 | return NUM_VALUE(type_hash); 149 | } 150 | 151 | DECL_BUILT_IN(builtin_implements) { 152 | if (argc < 2 || !IS_RECORD(*argv[0]) || argv[1]->type != VALUE_TYPE_ID) 153 | return const_value_null; 154 | struct value* record = argv[0]; 155 | while (record != NULL) 156 | { 157 | if (record->payload.object.ptr.record->prototype->identifier == argv[1]->payload.identifier) 158 | return const_value_true; 159 | record = record_get_property(record->payload.object.ptr.record, RECORD_BASE_PROPERTY); 160 | } 161 | return const_value_false; 162 | } 163 | 164 | DECL_BUILT_IN(builtin_abs) { 165 | if (argc < 1 || argv[0]->type != VALUE_TYPE_NUM) 166 | return const_value_null; 167 | if (argv[0]->payload.numerical >= 0) 168 | return NUM_VALUE(argv[0]->payload.numerical); 169 | else 170 | return NUM_VALUE(-argv[0]->payload.numerical); 171 | } 172 | 173 | DECL_BUILT_IN(builtin_max) { 174 | if (argc < 2) 175 | return const_value_null; 176 | return NUM_VALUE(fmax(argv[0]->payload.numerical, argv[1]->payload.numerical)); 177 | } 178 | 179 | DECL_BUILT_IN(builtin_min) { 180 | if (argc < 2) 181 | return const_value_null; 182 | return NUM_VALUE(fmin(argv[0]->payload.numerical, argv[1]->payload.numerical)); 183 | } 184 | 185 | DECL_BUILT_IN(builtin_ceil) { 186 | if (argc < 1) 187 | return const_value_null; 188 | return NUM_VALUE(ceil(argv[0]->payload.numerical)); 189 | } 190 | 191 | DECL_BUILT_IN(builtin_floor) { 192 | if (argc < 1) 193 | return const_value_null; 194 | return NUM_VALUE(floor(argv[0]->payload.numerical)); 195 | } -------------------------------------------------------------------------------- /src/operators.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "include/error.h" 5 | #include "include/value.h" 6 | #include "include/machine.h" 7 | #include "include/operators.h" 8 | 9 | #define DECL_BINARY_OPERATOR(METHOD_NAME) static struct value* METHOD_NAME(struct value* a, struct value* b, struct machine* machine) 10 | #define DECL_UNARY_OPERATOR(METHOD_NAME) static struct value* METHOD_NAME(struct value* a, struct machine* machine) 11 | 12 | #define MATCH_OP_TYPE(OPERAND, VALUE_TYPE) if(OPERAND->type != VALUE_TYPE) { return 0; } 13 | 14 | DECL_BINARY_OPERATOR(op_equals) { 15 | return machine_push_const(machine, NUM_VALUE(compare_value(a, b) == 0), 0); 16 | } 17 | 18 | DECL_BINARY_OPERATOR(op_not_equals) { 19 | return machine_push_const(machine, NUM_VALUE(compare_value(a, b) != 0), 0); 20 | } 21 | 22 | DECL_BINARY_OPERATOR(op_more) { 23 | return machine_push_const(machine, NUM_VALUE(compare_value(a, b) > 0), 0); 24 | } 25 | 26 | DECL_BINARY_OPERATOR(op_less) { 27 | return machine_push_const(machine, NUM_VALUE(compare_value(a, b) < 0), 0); 28 | } 29 | 30 | DECL_BINARY_OPERATOR(op_more_equal) { 31 | return machine_push_const(machine, NUM_VALUE(compare_value(a, b) >= 0), 0); 32 | } 33 | 34 | DECL_BINARY_OPERATOR(op_less_equal) { 35 | return machine_push_const(machine, NUM_VALUE(compare_value(a, b) <= 0), 0); 36 | } 37 | 38 | DECL_BINARY_OPERATOR(op_and) { 39 | if (a->type == VALUE_TYPE_NULL || b->type == VALUE_TYPE_NULL) 40 | return machine_push_const(machine, const_value_false, 0); 41 | else if ((a->type == VALUE_TYPE_NUM && a->payload.numerical == 0) || 42 | (b->type == VALUE_TYPE_NUM && b->payload.numerical == 0)) 43 | return machine_push_const(machine, const_value_false, 0); 44 | else 45 | return machine_push_const(machine, const_value_true, 0); 46 | } 47 | 48 | DECL_BINARY_OPERATOR(op_or) { 49 | if ((a->type == VALUE_TYPE_NUM && a->payload.numerical == 1) || 50 | (b->type == VALUE_TYPE_NUM && b->payload.numerical == 1)) 51 | return machine_push_const(machine, const_value_true, 0); 52 | else 53 | return machine_push_const(machine, const_value_false, 0); 54 | } 55 | 56 | DECL_BINARY_OPERATOR(op_add) { 57 | MATCH_OP_TYPE(a, VALUE_TYPE_NUM); 58 | MATCH_OP_TYPE(b, VALUE_TYPE_NUM); 59 | return machine_push_const(machine, NUM_VALUE(a->payload.numerical + b->payload.numerical), 0); 60 | } 61 | 62 | DECL_BINARY_OPERATOR(op_subtract) { 63 | MATCH_OP_TYPE(a, VALUE_TYPE_NUM); 64 | MATCH_OP_TYPE(b, VALUE_TYPE_NUM); 65 | return machine_push_const(machine, NUM_VALUE(a->payload.numerical - b->payload.numerical), 0); 66 | } 67 | 68 | DECL_BINARY_OPERATOR(op_multiply) { 69 | MATCH_OP_TYPE(a, VALUE_TYPE_NUM); 70 | MATCH_OP_TYPE(b, VALUE_TYPE_NUM); 71 | return machine_push_const(machine, NUM_VALUE(a->payload.numerical * b->payload.numerical), 0); 72 | } 73 | 74 | DECL_BINARY_OPERATOR(op_divide) { 75 | MATCH_OP_TYPE(a, VALUE_TYPE_NUM); 76 | MATCH_OP_TYPE(b, VALUE_TYPE_NUM); 77 | return machine_push_const(machine, NUM_VALUE(a->payload.numerical / b->payload.numerical), 0); 78 | } 79 | 80 | DECL_BINARY_OPERATOR(op_modulo) { 81 | MATCH_OP_TYPE(a, VALUE_TYPE_NUM); 82 | MATCH_OP_TYPE(b, VALUE_TYPE_NUM); 83 | return machine_push_const(machine, NUM_VALUE(fmod(a->payload.numerical, b->payload.numerical)), 0); 84 | } 85 | 86 | DECL_BINARY_OPERATOR(op_power) { 87 | MATCH_OP_TYPE(a, VALUE_TYPE_NUM); 88 | MATCH_OP_TYPE(b, VALUE_TYPE_NUM); 89 | return machine_push_const(machine, NUM_VALUE(powf(a->payload.numerical, b->payload.numerical)), 0); 90 | } 91 | 92 | DECL_UNARY_OPERATOR(op_copy) { 93 | if (a->type == VALUE_TYPE_OBJ) 94 | return machine_push_eval(machine, a); 95 | return machine_push_const(machine, *a, 1); 96 | } 97 | 98 | DECL_UNARY_OPERATOR(op_invert) { 99 | if (a->type == VALUE_TYPE_NULL || (a->type == VALUE_TYPE_NUM && a->payload.numerical == 0)) 100 | return machine_push_const(machine, const_value_true, 0); 101 | return machine_push_const(machine, const_value_false, 0); 102 | } 103 | 104 | DECL_UNARY_OPERATOR(op_negate) { 105 | return machine_push_const(machine, NUM_VALUE(-a->payload.numerical), 0); 106 | } 107 | 108 | DECL_UNARY_OPERATOR(op_alloc) { 109 | struct value c; 110 | 111 | uint64_t i = a->payload.numerical; 112 | if (i > 1000000) 113 | return NULL; 114 | 115 | struct collection* collection = malloc(sizeof(struct collection)); 116 | ERROR_ALLOC_CHECK(collection); 117 | 118 | init_collection(collection, i); 119 | 120 | while (i--) 121 | ERROR_ALLOC_CHECK(collection->inner_collection[i] = machine_push_const(machine, const_value_null, 0)); 122 | 123 | struct object obj; 124 | init_object_col(&obj, collection); 125 | init_obj_value(&c, obj); 126 | 127 | return machine_push_const(machine, c, 0); 128 | } 129 | 130 | DECL_UNARY_OPERATOR(op_increment) { 131 | MATCH_OP_TYPE(a, VALUE_TYPE_NUM); 132 | return machine_push_const(machine, NUM_VALUE(a->payload.numerical++), 0); 133 | } 134 | 135 | DECL_UNARY_OPERATOR(op_decriment) { 136 | MATCH_OP_TYPE(a, VALUE_TYPE_NUM); 137 | return machine_push_const(machine, NUM_VALUE(a->payload.numerical--), 0); 138 | } 139 | 140 | DECL_BINARY_OPERATOR((*binary_operators[14])) = { 141 | op_equals, 142 | op_not_equals, 143 | op_more, 144 | op_less, 145 | op_more_equal, 146 | op_less_equal, 147 | op_and, 148 | op_or, 149 | op_add, 150 | op_subtract, 151 | op_multiply, 152 | op_divide, 153 | op_modulo, 154 | op_power 155 | }; 156 | 157 | DECL_UNARY_OPERATOR((*unary_operators[6])) = { 158 | op_copy, 159 | op_invert, 160 | op_negate, 161 | op_alloc, 162 | op_increment, 163 | op_decriment 164 | }; 165 | 166 | struct value* invoke_binary_op(enum binary_operator operator, struct value* a, struct value* b, struct machine* machine) { 167 | return (*binary_operators[operator])(a, b, machine); 168 | } 169 | 170 | struct value* invoke_unary_op(enum unary_operator operator, struct value* a, struct machine* machine) { 171 | return (*unary_operators[operator])(a, machine); 172 | } -------------------------------------------------------------------------------- /src/garbage.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "include/io.h" 3 | #include "include/error.h" 4 | #include "include/object.h" 5 | #include "include/garbage.h" 6 | 7 | #define MAX_FRAMES 10000 8 | #define MAX_VALUES 500000 9 | #define MAX_TRACE 30000 10 | 11 | const int init_gcollect(struct garbage_collector* garbage_collector) { 12 | ERROR_ALLOC_CHECK(garbage_collector->frame_stack = malloc(MAX_FRAMES * sizeof(struct garbage_frame))); 13 | ERROR_ALLOC_CHECK(garbage_collector->value_stack = malloc(MAX_VALUES * sizeof(struct value*))); 14 | ERROR_ALLOC_CHECK(garbage_collector->trace_stack = malloc(MAX_TRACE * sizeof(struct value*))); 15 | garbage_collector->frames = 0; 16 | garbage_collector->to_collect = 0; 17 | garbage_collector->to_trace = 0; 18 | return 1; 19 | } 20 | 21 | void free_gcollect(struct garbage_collector* garbage_collector) { 22 | while (garbage_collector->frames > 0) { 23 | if (!gc_collect(garbage_collector)) 24 | garbage_collector->frames--; 25 | } 26 | free(garbage_collector->frame_stack); 27 | free(garbage_collector->value_stack); 28 | free(garbage_collector->trace_stack); 29 | } 30 | 31 | static void init_gframe(struct garbage_frame* garbage_frame, const struct value** value_begin, const struct value** trace_begin) { 32 | garbage_frame->to_collect = value_begin; 33 | garbage_frame->to_trace = trace_begin; 34 | garbage_frame->collect_values = 0; 35 | garbage_frame->trace_values = 0; 36 | } 37 | 38 | const int gc_register_trace(struct garbage_collector* garbage_collector, const struct value* value) { 39 | if (garbage_collector->to_trace == MAX_TRACE) 40 | return 0; 41 | struct garbage_frame* top = &garbage_collector->frame_stack[garbage_collector->frames - 1]; 42 | if (top->trace_values == MAX_FRAMES) 43 | return 0; 44 | top->to_trace[top->trace_values++] = value; 45 | garbage_collector->to_trace++; 46 | return 1; 47 | } 48 | 49 | const int gc_register_children(struct garbage_collector* garbage_collector, struct value* head) { 50 | if (!head || head->gc_flag == GARBAGE_COLLECT) 51 | return 0; 52 | head->gc_flag = GARBAGE_COLLECT; 53 | 54 | if (head->type == VALUE_TYPE_OBJ) { 55 | uint64_t size = 0; 56 | struct value** children = object_get_children(&head->payload.object, &size); 57 | 58 | for (uint64_t i = 0; i < size; i++) 59 | if (children[i]->gc_flag == GARBAGE_CONSTANT) { 60 | children[i] = gc_register_value(garbage_collector, *children[i]); 61 | if (!children[i]) { 62 | children[i] = NULL; 63 | return NULL; 64 | } 65 | } 66 | } 67 | return 1; 68 | } 69 | 70 | struct value*gc_register_value(struct garbage_collector* garbage_collector, struct value value) { 71 | if (value.gc_flag != GARBAGE_CONSTANT || garbage_collector->to_collect == MAX_VALUES) 72 | return NULL; 73 | 74 | struct value* alloc_apartment = malloc(sizeof(struct value)); 75 | 76 | ERROR_ALLOC_CHECK(alloc_apartment); 77 | *alloc_apartment = value; 78 | 79 | struct garbage_frame* gframe = &garbage_collector->frame_stack[garbage_collector->frames - 1]; 80 | gframe->to_collect[gframe->collect_values++] = alloc_apartment; 81 | garbage_collector->to_collect++; 82 | 83 | ERROR_ALLOC_CHECK(gc_register_children(garbage_collector, alloc_apartment)); 84 | return alloc_apartment; 85 | } 86 | 87 | void gc_new_frame(struct garbage_collector* garbage_collector) { 88 | struct value** begin = garbage_collector->value_stack; 89 | struct value** trace_begin = garbage_collector->trace_stack; 90 | if (garbage_collector->frames > 0) { 91 | struct garbage_frame* prev_frame = &garbage_collector->frame_stack[garbage_collector->frames - 1]; 92 | begin = &prev_frame->to_collect[prev_frame->collect_values]; 93 | trace_begin = &prev_frame->to_trace[prev_frame->trace_values]; 94 | } 95 | init_gframe(&garbage_collector->frame_stack[garbage_collector->frames++], begin, trace_begin); 96 | } 97 | 98 | static const int trace_value(struct garbage_collector* garbage_collector, struct value* value, struct value** reset_stack, uint32_t* stack_top) { 99 | ERROR_ALLOC_CHECK(value); 100 | if (value->gc_flag != GARBAGE_CONSTANT) { 101 | if (value->gc_flag == GARBAGE_KEEP) 102 | return 1; 103 | 104 | value->gc_flag = GARBAGE_KEEP; 105 | 106 | if (garbage_collector->to_trace == MAX_TRACE) 107 | return 0; 108 | 109 | reset_stack[*stack_top] = value; 110 | (*stack_top)++; 111 | garbage_collector->to_trace++; 112 | } 113 | if (value->type == VALUE_TYPE_OBJ) { 114 | uint64_t size = 0; 115 | struct value**children = object_get_children(&value->payload.object, &size); 116 | for (uint64_t i = 0; i < size; i++) 117 | ERROR_ALLOC_CHECK(trace_value(garbage_collector, children[i], reset_stack, stack_top)); 118 | } 119 | return 1; 120 | } 121 | 122 | const int gc_collect(struct garbage_collector* garbage_collector) { 123 | struct garbage_frame* top = &garbage_collector->frame_stack[garbage_collector->frames - 1]; 124 | 125 | struct value** reset_stack = &top->to_trace[top->trace_values]; 126 | uint32_t reset_top = 0; 127 | 128 | int err_flag = 0; 129 | 130 | for (uint_fast64_t i = 0; i < top->trace_values; i++) 131 | if (!trace_value(garbage_collector, top->to_trace[i], reset_stack, &reset_top)) { 132 | err_flag = 1; 133 | break; 134 | } 135 | 136 | for (uint64_t i = 0; i < top->collect_values; i++) { 137 | if (garbage_collector->frames < 2 || (top->to_collect[i]->gc_flag == GARBAGE_COLLECT && !err_flag)) { 138 | free_value(top->to_collect[i]); 139 | free(top->to_collect[i]); 140 | garbage_collector->to_collect--; 141 | } 142 | else { 143 | struct garbage_frame* prev = &garbage_collector->frame_stack[garbage_collector->frames - 2]; 144 | prev->to_collect[prev->collect_values++] = top->to_collect[i]; 145 | garbage_collector->to_collect++; 146 | } 147 | } 148 | 149 | garbage_collector->to_trace -= reset_top; 150 | while (reset_top--) 151 | reset_stack[reset_top]->gc_flag = GARBAGE_COLLECT; 152 | 153 | garbage_collector->to_trace -= top->trace_values; 154 | garbage_collector->frames--; 155 | return 1; 156 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator, Michael Wang 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /src/compiler.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "include/opcodes.h" 4 | #include "include/tokens.h" 5 | #include "include/globals.h" 6 | #include "include/statements.h" 7 | #include "include/compiler.h" 8 | 9 | static const int compiler_link_chunk(struct compiler* compiler, struct chunk* chunk, uint64_t offset) { 10 | uint64_t** skip_stack = malloc(64); 11 | ERROR_ALLOC_CHECK(skip_stack); 12 | 13 | uint8_t skips = 0; 14 | 15 | while (chunk->last_code != MACHINE_END) 16 | { 17 | enum op_code op = chunk->last_code; 18 | switch (op) 19 | { 20 | case MACHINE_LABEL: 21 | case MACHINE_COND_SKIP: 22 | case MACHINE_FLAG_SKIP: { 23 | skip_stack[skips++] = chunk_read_size(chunk, sizeof(uint64_t)); 24 | if (op == MACHINE_LABEL) { 25 | uint64_t label_id = chunk_read_ulong(chunk); 26 | if (!cache_insert_label(&compiler->machine->global_cache, label_id, chunk->pos + offset)) { 27 | compiler->last_err = ERROR_LABEL_REDEFINE; 28 | return 0; 29 | } 30 | } 31 | break; 32 | } 33 | case MACHINE_END_SKIP: { 34 | *skip_stack[--skips] = chunk->pos - 1 + offset; 35 | break; 36 | } 37 | case MACHINE_CALL_EXTERN: 38 | case MACHINE_INHERIT_REC: 39 | chunk_read_size(chunk, sizeof(uint64_t)); 40 | case MACHINE_GOTO: 41 | case MACHINE_GOTO_AS: 42 | case MACHINE_STORE_VAR: 43 | case MACHINE_LOAD_VAR: 44 | case MACHINE_BUILD_COL: 45 | case MACHINE_SET_PROPERTY: 46 | case MACHINE_GET_PROPERTY: 47 | case MACHINE_BUILD_RECORD: 48 | case MACHINE_TRACE: 49 | chunk_read_size(chunk, sizeof(uint64_t)); 50 | break; 51 | case MACHINE_LOAD_CONST: 52 | chunk_read_size(chunk, sizeof(struct value)); 53 | break; 54 | case MACHINE_EVAL_BIN_OP: 55 | case MACHINE_EVAL_UNI_OP: 56 | chunk_read_size(chunk, sizeof(uint8_t)); 57 | break; 58 | case MACHINE_BUILD_PROTO: { 59 | chunk_read_size(chunk, sizeof(uint64_t)); 60 | uint64_t i = chunk_read_ulong(chunk); 61 | while (i--) 62 | chunk_read_size(chunk, sizeof(uint64_t)); 63 | break; 64 | } 65 | } 66 | chunk_read_opcode(chunk); 67 | } 68 | free(skip_stack); 69 | chunk->pos = 0; 70 | chunk_read_opcode(chunk); 71 | if (skips) 72 | return 0; 73 | while (chunk->last_code != MACHINE_END) 74 | { 75 | enum op_code op = chunk->last_code; 76 | switch (op) 77 | { 78 | case MACHINE_LABEL: 79 | chunk_read_size(chunk, sizeof(uint64_t)); 80 | case MACHINE_COND_SKIP: 81 | case MACHINE_FLAG_SKIP: { 82 | chunk_read_size(chunk, sizeof(uint64_t)); 83 | break; 84 | } 85 | case MACHINE_CALL_EXTERN: 86 | case MACHINE_INHERIT_REC: 87 | chunk_read_size(chunk, sizeof(uint64_t)); 88 | case MACHINE_GOTO_AS: 89 | case MACHINE_STORE_VAR: 90 | case MACHINE_LOAD_VAR: 91 | case MACHINE_BUILD_COL: 92 | case MACHINE_SET_PROPERTY: 93 | case MACHINE_GET_PROPERTY: 94 | case MACHINE_BUILD_RECORD: 95 | case MACHINE_TRACE: 96 | chunk_read_size(chunk, sizeof(uint64_t)); 97 | break; 98 | case MACHINE_GOTO: { 99 | uint64_t* pos_slot = chunk_read_size(chunk, sizeof(uint64_t)); 100 | uint64_t pos = cache_retrieve_pos(&compiler->machine->global_cache, *pos_slot); 101 | if (!pos) { 102 | compiler->last_err = ERROR_LABEL_UNDEFINED; 103 | return 0; 104 | } 105 | *pos_slot = pos; 106 | break; 107 | } 108 | case MACHINE_LOAD_CONST: 109 | chunk_read_size(chunk, sizeof(struct value)); 110 | break; 111 | case MACHINE_EVAL_BIN_OP: 112 | case MACHINE_EVAL_UNI_OP: 113 | chunk_read_size(chunk, sizeof(uint8_t)); 114 | break; 115 | case MACHINE_BUILD_PROTO: { 116 | chunk_read_size(chunk, sizeof(uint64_t)); 117 | uint64_t i = chunk_read_ulong(chunk); 118 | while (i--) 119 | chunk_read_size(chunk, sizeof(uint64_t)); 120 | break; 121 | } 122 | } 123 | chunk_read_opcode(chunk); 124 | } 125 | chunk->pos = 0; 126 | chunk_read_opcode(chunk); 127 | return 1; 128 | } 129 | 130 | void init_compiler(struct compiler* compiler, struct machine* machine, const char* include_dir, const char* source, const char* file) { 131 | compiler->imported_files = 0; 132 | compiler->include_dir = include_dir; 133 | compiler->machine = machine; 134 | compiler->include_dir_len = strlen(include_dir); 135 | init_scanner(&compiler->scanner, source, file); 136 | init_chunk_builder(&compiler->code_builder); 137 | init_chunk_builder(&compiler->data_builder); 138 | compiler_read_tok(compiler); 139 | } 140 | 141 | const int compile(struct compiler* compiler, struct loc_table* loc_table, const int repl_mode, const int link_output, uint64_t prev_offset) { 142 | if(!repl_mode) 143 | chunk_write_opcode(&compiler->code_builder, MACHINE_NEW_FRAME); 144 | 145 | while (compiler->last_tok.type != TOK_END) 146 | if (!compile_statement(compiler, &compiler->code_builder, loc_table, 0, 0, 0)) 147 | return 0; 148 | 149 | if(!repl_mode) 150 | chunk_write_opcode(&compiler->code_builder, MACHINE_CLEAN); 151 | 152 | if (link_output) { 153 | struct chunk_builder sum; 154 | init_chunk_builder(&sum); 155 | 156 | if (!chunk_write_chunk(&sum, build_chunk(&compiler->data_builder), 1) || 157 | !chunk_write_chunk(&sum, build_chunk(&compiler->code_builder), 1)) { 158 | compiler->last_err = ERROR_OUT_OF_MEMORY; 159 | return 0; 160 | } 161 | 162 | compiler->result = build_chunk(&sum); 163 | ERROR_ALLOC_CHECK(compiler_link_chunk(compiler, &compiler->result, prev_offset)); 164 | } 165 | 166 | compiler->last_err = ERROR_SUCCESS; 167 | return 1; 168 | } 169 | 170 | struct token compiler_read_tok(struct compiler* compiler) { 171 | do { 172 | compiler->last_tok = scanner_read_tok(&compiler->scanner); 173 | } while (compiler->last_tok.type == TOK_REMARK); 174 | if (compiler->last_tok.type == TOK_ERROR) { 175 | compiler->last_err = compiler->scanner.last_err; 176 | } 177 | return compiler->last_tok; 178 | } 179 | 180 | const int compile_expression(struct compiler* compiler, struct chunk_builder* builder, enum op_precedence min_prec, const int optimize_copy, uint64_t optimize_goto) { 181 | char is_id = compiler->last_tok.type == TOK_IDENTIFIER; 182 | if (!compile_value(compiler, builder, !optimize_goto, optimize_goto)) //lhs 183 | return 0; 184 | if (compiler->last_tok.type != TOK_BINARY_OP) { 185 | if (is_id && !optimize_copy && !optimize_goto) { 186 | chunk_write_opcode(builder, MACHINE_EVAL_UNI_OP); 187 | chunk_write_uni_op(builder, OPERATOR_COPY); 188 | } 189 | return 1; 190 | } 191 | while (compiler->last_tok.type == TOK_BINARY_OP && op_precedence[compiler->last_tok.payload.bin_op] > min_prec) 192 | { 193 | enum binary_operator op = compiler->last_tok.payload.bin_op; 194 | compiler_read_tok(compiler); 195 | if (!compile_expression(compiler, builder, op_precedence[op], 1, optimize_goto)) 196 | return 0; 197 | chunk_write_opcode(builder, MACHINE_EVAL_BIN_OP); 198 | chunk_write_bin_op(builder, op); 199 | } 200 | return 1; 201 | } 202 | 203 | const int compile_body(struct compiler* compiler, struct chunk_builder* builder, struct loc_table* loc_table, uint64_t callee, uint64_t proc_encapsulated) { 204 | if (compiler->last_tok.type != TOK_OPEN_BRACE) { 205 | compiler->last_err = ERROR_UNEXPECTED_TOKEN; 206 | return 0; 207 | } 208 | 209 | compiler_read_tok(compiler); 210 | 211 | while (compiler->last_tok.type != TOK_END && compiler->last_tok.type != TOK_CLOSE_BRACE) 212 | { 213 | if (!compile_statement(compiler, builder, loc_table, callee, 1, proc_encapsulated)) 214 | return 0; 215 | } 216 | compiler_read_tok(compiler); 217 | return 1; 218 | } -------------------------------------------------------------------------------- /examples/symbol/simplify.txt: -------------------------------------------------------------------------------- 1 | include "symbol/core.txt" 2 | include "symbol/match.txt" 3 | 4 | record eq_node { 5 | proc can_simplify { 6 | set i to extern len(this.children) 7 | while dec i { 8 | if extern typeof(this.children[i]) != extern typeof(#eq_num) { 9 | return false 10 | } 11 | } 12 | return true 13 | } 14 | 15 | proc simplify_children { 16 | set i to extern len(this.children) 17 | while dec i { 18 | set this.children[i] to goproc simplify as this.children[i] 19 | } 20 | } 21 | } 22 | 23 | record eq_bin_op { 24 | proc simplify_children { 25 | goproc simplify_children as this.base 26 | set this.left to this.base.children[0] 27 | set this.right to this.base.children[1] 28 | } 29 | } 30 | 31 | record eq_uni_op { 32 | proc simplify_children { 33 | goproc simplify_children as this.base 34 | set this.operand to this.base.children[0] 35 | } 36 | } 37 | 38 | record eq_entity { 39 | proc simplify { 40 | return this 41 | } 42 | } 43 | 44 | record eq_op_add { 45 | proc simplify { 46 | goproc simplify_children as this 47 | if goproc can_simplify as this { 48 | return new eq_num(this.left.obj + this.right.obj) 49 | } 50 | elif goproc compare as this.left(0) { 51 | return this.right 52 | } 53 | elif goproc compare as this.right(0) { 54 | return this.left 55 | } 56 | return this 57 | } 58 | } 59 | 60 | record eq_op_subtract { 61 | proc simplify { 62 | goproc simplify_children as this 63 | if goproc can_simplify as this { 64 | return new eq_num(this.left.obj - this.right.obj) 65 | } 66 | elif goproc compare as this.left(this.right) { 67 | return new eq_num(0) 68 | } 69 | elif goproc compare as this.left(0) { 70 | return -this.left 71 | } 72 | elif goproc compare as this.right(0) { 73 | return this.left 74 | } 75 | return this 76 | } 77 | } 78 | 79 | record eq_op_multiply { 80 | proc simplify { 81 | goproc simplify_children as this 82 | if goproc can_simplify as this { 83 | return new eq_num(this.left.obj * this.right.obj) 84 | } 85 | elif goproc compare as this.right(0) or goproc compare as this.left(0) { 86 | return new eq_num(0) 87 | } 88 | elif goproc compare as this.left(1) { 89 | return this.right 90 | } 91 | elif goproc compare as this.right(1) { 92 | return this.left 93 | } 94 | return this 95 | } 96 | } 97 | 98 | record eq_op_divide { 99 | proc simplify { 100 | goproc simplify_children as this 101 | if goproc can_simplify as this { 102 | return new eq_num(this.left.obj / this.right.obj) 103 | } 104 | elif goproc compare as this.left(this.right) { 105 | return new eq_num(1) 106 | } 107 | elif goproc compare as this.left(0) { 108 | return new eq_num(0) 109 | } 110 | elif goproc compare as this.right(1) { 111 | return this.left 112 | } 113 | return this 114 | } 115 | } 116 | 117 | record eq_op_mod { 118 | proc simplify { 119 | goproc simplify_children as this 120 | if goproc can_simplify as this { 121 | return new eq_num(this.left.obj % this.right.obj) 122 | } 123 | elif goproc compare as this.left(this.right) { 124 | return new eq_num(0) 125 | } 126 | elif goproc compare as this.left(0) { 127 | return new eq_num(0) 128 | } 129 | elif goproc compare as this.right(1) { 130 | return this.left 131 | } 132 | return this 133 | } 134 | } 135 | 136 | record eq_op_pow { 137 | proc simplify { 138 | goproc simplify_children as this 139 | if goproc can_simplify as this { 140 | return new eq_num(this.left.obj ^ this.right.obj) 141 | } 142 | elif goproc compare as this.left(1) or goproc compare as this.right(0) { 143 | return new eq_num(1) 144 | } 145 | elif goproc compare as this.right(1) { 146 | return this.left 147 | } 148 | return this 149 | } 150 | } 151 | 152 | rem TODO: Implement a built-in method for evaluating logarithms 153 | record eq_op_log { 154 | proc simplify { 155 | goproc simplify_children as this 156 | return this 157 | } 158 | } 159 | 160 | record eq_op_abs { 161 | proc simplify { 162 | goproc simplify_children as this 163 | if goproc can_simplify as this { 164 | return new eq_num(extern abs(this.operand.obj)) 165 | } 166 | return this 167 | } 168 | } 169 | 170 | record simplifier extends rule_set { 171 | proc init { 172 | set any1 to new match(100, #any) 173 | set any2 to new match(101, #any) 174 | set any3 to new match(102, #any) 175 | set const1 to new match(200, #eq_num) 176 | set const2 to new match(201, #eq_num) 177 | set const3 to new match(201, #eq_num) 178 | set var1 to new match(300, #eq_var) 179 | set var2 to new match(301, #eq_var) 180 | set var3 to new match(302, #eq_var) 181 | 182 | set this.base.rules to { 183 | new rule(any1 / (any2 / any3), (any1 * any3) / any2), 184 | new rule(any1 * (any2 / any3), (any1 * any2) / any3), 185 | 186 | new rule(var1 * const1, const1 * var1), 187 | new rule(const1 + var1, var1 + const1), 188 | 189 | new rule(const1 * var1 + const2 * var1, (const1 + const2) * var1), 190 | new rule(const1 * var1 - const2 * var1, (const1 - const2) * var1), 191 | 192 | new rule(any1 * any2 + any1 * any3, any1 * (any2 + any3)), 193 | new rule(any1 * any2 + any3 * any1, any1 * (any2 + any3)), 194 | new rule(any2 * any1 + any1 * any3, any1 * (any2 + any3)), 195 | new rule(any2 * any1 + any3 * any1, any1 * (any2 + any3)), 196 | 197 | new rule(any1 + any1 * any2, any1 * (1 + any2)), 198 | new rule(any1 + any2 * any1, any1 * (1 + any2)), 199 | new rule(any1 * any2 + any1, any1 * (1 + any2)), 200 | new rule(any2 * any1 + any1, any1 * (1 + any2)), 201 | 202 | new rule(any1 + any1, 2 * any1), 203 | 204 | new rule(any1 * any2 - any1 * any3, any1 * (any2 - any3)), 205 | new rule(var1 * var1, var1^2), 206 | 207 | new rule(var1 * any1 ^ any2, any1 ^ any2 * var1), 208 | 209 | new rule(any1^any2 * any1, any1 ^ (any2 + 1)), 210 | new rule(any1 * any1^any2, any1 ^ (any2 + 1)), 211 | 212 | new rule(any1 ^ any2 * any1 ^ any3, any1 ^ (any2 + any3)), 213 | 214 | new rule(any1 ^ any2 / any1 ^ any3, any1 ^ (any2 - any3)), 215 | 216 | new rule(any1 ^ (any2 ^ any3), any1 ^ (any2 * any3)), 217 | 218 | new rule(const1 ^ goproc log(any1, const2), any1), 219 | 220 | new rule((const1 * var1) * const2, (const1 * const2) * var1), 221 | new rule(const2 * (const1 * var1), (const1 * const2) * var1), 222 | 223 | new rule((var1 + const1) + const2, (const1 + const2) + var1), 224 | new rule(const2 + (var1 + const1), (const1 + const2) + var1), 225 | 226 | new rule(any1 * (any1 * any2), (any1 * any1) * any2), 227 | new rule(any1 * (any2 * any1), (any1 * any1) * any2), 228 | new rule((any1 * any2) * any1, (any1 * any1) * any2), 229 | new rule((any2 * any1) * any1, (any1 * any1) * any2), 230 | 231 | new rule((any1 ^ any3) * (any1 * any2), (any1 ^ (any3 + 1)) * any2), 232 | new rule((any1 ^ any3) * (any2 * any1), (any1 ^ (any3 + 1)) * any2), 233 | new rule((any1 * any2) * (any1 ^ any3), (any1 ^ (any3 + 1)) * any2), 234 | new rule((any2 * any1) * (any1 ^ any3), (any1 ^ (any3 + 1)) * any2), 235 | 236 | new rule(any1^any3 * any2^any3, (any1 * any2) ^ any3), 237 | 238 | new rule((const1 * any1) ^ const1, const1 ^ const1 * any1 ^ const2), 239 | 240 | new rule(any1 / any1, new eq_num(1)), 241 | 242 | new rule((any1 * any2) / any2, any1), 243 | new rule((any2 * any1) / any2, any1), 244 | 245 | new rule((any1 * any2) / (any2 * any3), any1 / any3), 246 | new rule((any1 * any2) / (any3 * any2), any1 / any3), 247 | new rule((any2 * any1) / (any2 * any3), any1 / any3), 248 | new rule((any2 * any1) / (any3 * any2), any1 / any3), 249 | 250 | new rule((any1 - any2) / (any2 - any1), new eq_num(-1)), 251 | 252 | new rule((any1 + any2) / (any2 + any1), new eq_num(-1)), 253 | 254 | new rule(const1 / (const2 * any1), (const1 / const2) / any1), 255 | new rule(const1 / (any1 * const2), (const1 / const2) / any1), 256 | 257 | new rule(const1 * (const2 * any1), (const1 * const2) * any1), 258 | 259 | new rule(any1 - any1, new eq_num(0)), 260 | 261 | new rule(any1 - any2 * any1, any1 * (1 - any2)), 262 | new rule(any1 - any1 * any2, any1 * (1 - any2)), 263 | 264 | new rule(any1 ^ -1, 1 / any1), 265 | 266 | new rule(-1 * any1 + any2, any2 - any1), 267 | new rule(any1 / any2 * any3, any1 * any3 / any2) 268 | } 269 | } 270 | } -------------------------------------------------------------------------------- /src/scanner.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "include/hash.h" 6 | #include "include/error.h" 7 | #include "include/scanner.h" 8 | 9 | static const char read_char(struct scanner* scanner) { 10 | if (scanner->pos == scanner->size) 11 | return scanner->last_char = 0; 12 | if (scanner->source[scanner->pos] == '\n') { 13 | scanner->row++; 14 | scanner->col = 1; 15 | } 16 | else 17 | scanner->col++; 18 | return scanner->last_char = scanner->source[scanner->pos++]; 19 | } 20 | 21 | void init_scanner(struct scanner* scanner, const char* source, const char* file) { 22 | scanner->source = source; 23 | scanner->file = file; 24 | scanner->pos = 0; 25 | scanner->row = 1; 26 | scanner->col = 1; 27 | scanner->size = strlen(source); 28 | read_char(scanner); 29 | } 30 | 31 | static const char peek_char(struct scanner* scanner) { 32 | if (scanner->pos == scanner->size) 33 | return 0; 34 | return scanner->source[scanner->pos]; 35 | } 36 | 37 | static const char read_data_char(struct scanner* scanner) 38 | { 39 | char c = scanner->last_char; 40 | read_char(scanner); 41 | if (c == '\\') { 42 | switch (scanner->last_char) 43 | { 44 | case 'b': 45 | read_char(scanner); 46 | return '\b'; 47 | case 't': 48 | read_char(scanner); 49 | return '\t'; 50 | case 'r': 51 | read_char(scanner); 52 | return '\r'; 53 | case 'n': 54 | read_char(scanner); 55 | return '\n'; 56 | case '0': 57 | read_char(scanner); 58 | return 0; 59 | case '\'': 60 | case '\"': 61 | case '\\': { 62 | char toret = scanner->last_char; 63 | read_char(scanner); 64 | return toret; 65 | } 66 | default: 67 | scanner->last_err = ERROR_UNRECOGNIZED_CONTROL_SEQ; 68 | } 69 | } 70 | return c; 71 | } 72 | 73 | const int scanner_read_str(struct scanner* scanner, char* str, const int data_mode) { 74 | uint32_t len = 0; 75 | while (scanner->last_char == '\t' || scanner->last_char == '\r' || scanner->last_char == ' ' || scanner->last_char == '\n') 76 | read_char(scanner); 77 | if (scanner->last_char != '\"') { 78 | scanner->last_err = ERROR_UNEXPECTED_CHAR; 79 | return 0; 80 | } 81 | if (data_mode) { 82 | read_char(scanner); 83 | while (scanner->last_char != '\"') 84 | str[len++] = read_data_char(scanner); 85 | } 86 | else { 87 | while (read_char(scanner) != '\"') { 88 | if (!scanner->last_char) { 89 | scanner->last_err = ERROR_UNEXPECTED_TOKEN; 90 | return 0; 91 | } 92 | str[len++] = scanner->last_char; 93 | } 94 | } 95 | str[len] = 0; 96 | read_char(scanner); 97 | return 1; 98 | } 99 | 100 | struct token scanner_read_tok(struct scanner* scanner) { 101 | while (scanner->last_char == '\t' || scanner->last_char == '\r' || scanner->last_char == ' ' || scanner->last_char == '\n') 102 | read_char(scanner); 103 | struct token tok; 104 | const char* start = &scanner->source[scanner->pos - 1]; 105 | uint64_t length = 0; 106 | if (isalpha(scanner->last_char) || scanner->last_char == '_') { 107 | while (isalpha(scanner->last_char) || isalnum(scanner->last_char) || scanner->last_char == '_') { 108 | read_char(scanner); 109 | length++; 110 | } 111 | uint64_t id_hash = hash(start, length); 112 | switch (id_hash) 113 | { 114 | case 5863476: //ifs 115 | tok.type = TOK_IF; 116 | break; 117 | case 6385191717: //elif 118 | tok.type = TOK_ELIF; 119 | break; 120 | case 6385192046: //else 121 | tok.type = TOK_ELSE; 122 | break; 123 | case 210732529790: //while 124 | tok.type = TOK_WHILE; 125 | break; 126 | case 5863320: 127 | tok.type = TOK_DO; 128 | break; 129 | case 6385593753: //proc 130 | tok.type = TOK_PROC; 131 | break; 132 | case 6953974036516: //record 133 | tok.type = TOK_RECORD; 134 | break; 135 | case 5863225: //as 136 | tok.type = TOK_AS; 137 | break; 138 | case 193500239: //new 139 | tok.type = TOK_NEW; 140 | break; 141 | case 193505681: //set 142 | tok.type = TOK_SET; 143 | break; 144 | case 5863848: //to 145 | tok.type = TOK_TO; 146 | break; 147 | case 193504578://ref 148 | tok.type = TOK_REF; 149 | break; 150 | case 6953555876751: //goproc 151 | tok.type = TOK_GOTO; 152 | break; 153 | case 6953974653989: //return 154 | tok.type = TOK_RETURN; 155 | break; 156 | case 229469872107401: //include 157 | tok.type = TOK_INCLUDE; 158 | break; 159 | case 210706586640: //alloc 160 | tok.type = TOK_ALLOC; 161 | break; 162 | case 6953488408955: //extern 163 | tok.type = TOK_EXTERN; 164 | break; 165 | case 229465117490944: //extend 166 | tok.type = TOK_EXTEND; 167 | break; 168 | case 193486360: //and 169 | tok.type = TOK_BINARY_OP; 170 | tok.payload.bin_op = OPERATOR_AND; 171 | break; 172 | case 5863686: //or 173 | tok.type = TOK_BINARY_OP; 174 | tok.payload.bin_op = OPERATOR_OR; 175 | break; 176 | case 193495071: //inc 177 | tok.type = TOK_UNARY_OP; 178 | tok.payload.uni_op = OPERATOR_INCREMENT; 179 | break; 180 | case 193489329: //dec 181 | tok.type = TOK_UNARY_OP; 182 | tok.payload.uni_op = OPERATOR_DECRIMENT; 183 | break; 184 | case 6385525056: //null 185 | tok.type = TOK_PRIMATIVE; 186 | tok.payload.primative = const_value_null; 187 | break; 188 | case 193495074: 189 | tok.type = TOK_PRIMATIVE; 190 | tok.payload.primative = NUM_VALUE(INFINITY); 191 | break; 192 | case 6385737701: //true 193 | tok.type = TOK_PRIMATIVE; 194 | tok.payload.primative = const_value_true; 195 | break; 196 | case 210712121072: //false 197 | tok.type = TOK_PRIMATIVE; 198 | tok.payload.primative = const_value_false; 199 | break; 200 | case 193504585: //remark 201 | while (scanner->last_char != '\n' && scanner->last_char != 0) 202 | read_char(scanner); 203 | read_char(scanner); 204 | tok.type = TOK_REMARK; 205 | break; 206 | default: 207 | tok.type = TOK_IDENTIFIER; 208 | tok.payload.identifier = id_hash; 209 | } 210 | } 211 | else if (isalnum(scanner->last_char)) { 212 | while (isalnum(scanner->last_char) || scanner->last_char == '.') { 213 | length++; 214 | read_char(scanner); 215 | } 216 | tok.type = TOK_PRIMATIVE; 217 | tok.payload.primative = NUM_VALUE(strtod(start, NULL)); 218 | } 219 | else if (scanner->last_char == '\'') { 220 | read_char(scanner); 221 | tok.payload.primative = CHAR_VALUE(read_data_char(scanner)); 222 | if (scanner->last_err == ERROR_UNRECOGNIZED_CONTROL_SEQ) { 223 | tok.type = TOK_ERROR; 224 | return tok; 225 | } 226 | if (scanner->last_char != '\'') { 227 | scanner->last_err = ERROR_UNEXPECTED_CHAR; 228 | tok.type = TOK_ERROR; 229 | return tok; 230 | } 231 | tok.type = TOK_PRIMATIVE; 232 | read_char(scanner); 233 | } 234 | else { 235 | switch (scanner->last_char) 236 | { 237 | case ',': 238 | tok.type = TOK_COMMA; 239 | break; 240 | case '.': 241 | tok.type = TOK_PERIOD; 242 | break; 243 | case '[': 244 | tok.type = TOK_OPEN_BRACKET; 245 | break; 246 | case ']': 247 | tok.type = TOK_CLOSE_BRACKET; 248 | break; 249 | case '(': 250 | tok.type = TOK_OPEN_PAREN; 251 | break; 252 | case ')': 253 | tok.type = TOK_CLOSE_PAREN; 254 | break; 255 | case '{': 256 | tok.type = TOK_OPEN_BRACE; 257 | break; 258 | case '}': 259 | tok.type = TOK_CLOSE_BRACE; 260 | break; 261 | case '#': 262 | tok.type = TOK_HASHTAG; 263 | break; 264 | case '+': 265 | tok.type = TOK_BINARY_OP; 266 | tok.payload.bin_op = OPERATOR_ADD; 267 | break; 268 | case '-': 269 | tok.type = TOK_BINARY_OP; 270 | tok.payload.bin_op = OPERATOR_SUBTRACT; 271 | break; 272 | case '*': 273 | tok.type = TOK_BINARY_OP; 274 | tok.payload.bin_op = OPERATOR_MULTIPLY; 275 | break; 276 | case '/': 277 | tok.type = TOK_BINARY_OP; 278 | tok.payload.bin_op = OPERATOR_DIVIDE; 279 | break; 280 | case '%': 281 | tok.type = TOK_BINARY_OP; 282 | tok.payload.bin_op = OPERATOR_MODULO; 283 | break; 284 | case '^': 285 | tok.type = TOK_BINARY_OP; 286 | tok.payload.bin_op = OPERATOR_POWER; 287 | break; 288 | case '=': 289 | if (read_char(scanner) != '=') { 290 | tok.type = TOK_ERROR; 291 | scanner->last_err = ERROR_UNEXPECTED_CHAR; 292 | } 293 | else { 294 | tok.type = TOK_BINARY_OP; 295 | tok.payload.bin_op = OPERATOR_EQUALS; 296 | } 297 | break; 298 | case '!': 299 | if (peek_char(scanner) == '=') { 300 | tok.type = TOK_BINARY_OP; 301 | tok.payload.bin_op = OPERATOR_NOT_EQUAL; 302 | read_char(scanner); 303 | } 304 | else { 305 | tok.type = TOK_UNARY_OP; 306 | tok.payload.uni_op = OPERATOR_INVERT; 307 | } 308 | break; 309 | case '>': 310 | tok.type = TOK_BINARY_OP; 311 | if (peek_char(scanner) == '=') { 312 | tok.payload.bin_op = OPERATOR_MORE_EQUAL; 313 | read_char(scanner); 314 | } 315 | else 316 | tok.payload.bin_op = OPERATOR_MORE; 317 | break; 318 | case '<': 319 | tok.type = TOK_BINARY_OP; 320 | if (peek_char(scanner) == '=') { 321 | tok.payload.bin_op = OPERATOR_LESS_EQUAL; 322 | read_char(scanner); 323 | } 324 | else 325 | tok.payload.bin_op = OPERATOR_LESS; 326 | break; 327 | case '\"': 328 | tok.type = TOK_STR; 329 | return tok; 330 | case 0: 331 | tok.type = TOK_END; 332 | break; 333 | default: 334 | tok.type = TOK_ERROR; 335 | scanner->last_err = ERROR_UNRECOGNIZED_TOKEN; 336 | } 337 | read_char(scanner); 338 | } 339 | return tok; 340 | } -------------------------------------------------------------------------------- /src/debug.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "include/opcodes.h" 5 | #include "include/io.h" 6 | #include "include/error.h" 7 | #include "include/debug.h" 8 | 9 | const int init_loc_table(struct loc_table* loc_table, char* initial_file) { 10 | ERROR_ALLOC_CHECK(loc_table->locations = malloc((loc_table->alloced_locs = 64) * sizeof(struct loc_info))); 11 | ERROR_ALLOC_CHECK(loc_table->to_free = malloc((loc_table->alloced_files = 64) * sizeof(char*))); 12 | ERROR_ALLOC_CHECK(loc_table->file_stack = malloc(64 * sizeof(const char*))); 13 | loc_table->files = 0; 14 | loc_table->current_file = 0; 15 | loc_table->loc_entries = 0; 16 | loc_table->global_offset = 0; 17 | loc_table->finilazed_flag = 0; 18 | loc_table->file_stack[0] = initial_file; 19 | return 1; 20 | } 21 | 22 | void free_loc_table(struct loc_table* loc_table) { 23 | for (uint_fast64_t i = 0; i < loc_table->files; i++) 24 | free(loc_table->to_free[i]); 25 | free(loc_table->to_free); 26 | free(loc_table->locations); 27 | free(loc_table->file_stack); 28 | } 29 | 30 | const int loc_table_include(struct loc_table* loc_table, char* file) { 31 | if (loc_table->files == loc_table->alloced_files) { 32 | char** new_free = realloc(loc_table->to_free, (loc_table->alloced_files *= 2) * sizeof(char*)); 33 | if (!new_free) 34 | return 0; 35 | loc_table->to_free = new_free; 36 | } 37 | loc_table->to_free[loc_table->files++] = file; 38 | loc_table->file_stack[++loc_table->current_file] = file; 39 | return 1; 40 | } 41 | 42 | void loc_table_uninclude(struct loc_table* loc_table) { 43 | loc_table->current_file--; 44 | } 45 | 46 | const int loc_table_insert(struct loc_table* loc_table, struct compiler* compiler, struct chunk_builder* builder) { 47 | loc_table->finilazed_flag = 0; 48 | if (loc_table->loc_entries == loc_table->alloced_locs) { 49 | struct loc_info** new_entries = realloc(loc_table->locations, (loc_table->alloced_locs *= 2) * sizeof(struct loc_info)); 50 | if (!new_entries) 51 | return 0; 52 | loc_table->locations = new_entries; 53 | } 54 | loc_table->locations[loc_table->loc_entries++] = (struct loc_info){ 55 | .file = loc_table->file_stack[loc_table->current_file], 56 | .chunk_loc = builder->size + loc_table->global_offset, 57 | .src_col = compiler->scanner.col, 58 | .src_row = compiler->scanner.row, 59 | .offset_flag = (builder == &compiler->code_builder), 60 | .keep_flag = 0 61 | }; 62 | return 1; 63 | } 64 | 65 | void loc_table_finalize(struct loc_table* loc_table, struct compiler* compiler, int keep) { 66 | loc_table->finilazed_flag = 1; 67 | for (uint_fast64_t i = 0; i < loc_table->loc_entries; i++) { 68 | if (loc_table->locations[i].offset_flag) { 69 | loc_table->locations[i].chunk_loc += compiler->data_builder.size; 70 | loc_table->locations[i].offset_flag = 0; 71 | } 72 | if (keep) 73 | loc_table->locations[i].keep_flag = 1; 74 | } 75 | int sorted = 0; 76 | do { 77 | sorted = 1; 78 | for(uint_fast64_t i = 1; i < loc_table->loc_entries; i++) 79 | if (loc_table->locations[i - 1].chunk_loc > loc_table->locations[i].chunk_loc) { 80 | struct loc_info temp = loc_table->locations[i]; 81 | loc_table->locations[i] = loc_table->locations[i - 1]; 82 | loc_table->locations[i - 1] = temp; 83 | sorted = 0; 84 | } 85 | } while (!sorted); 86 | } 87 | 88 | void loc_table_dispose(struct loc_table* loc_table) { 89 | if (loc_table->finilazed_flag) { 90 | while (!loc_table->locations[loc_table->loc_entries - 1].keep_flag) 91 | loc_table->loc_entries--; 92 | } 93 | else { 94 | uint64_t upper_bound = loc_table->loc_entries; 95 | for (uint_fast64_t i = 0; i < upper_bound; i++) 96 | if (!loc_table->locations[i].keep_flag) { 97 | while (upper_bound && !loc_table->locations[upper_bound - 1].keep_flag) 98 | upper_bound--; 99 | if (upper_bound > 0 && i < upper_bound) { 100 | struct loc_info temp = loc_table->locations[i]; 101 | loc_table->locations[i] = loc_table->locations[upper_bound - 1]; 102 | loc_table->locations[upper_bound - 1] = temp; 103 | } 104 | else 105 | break; 106 | } 107 | loc_table->loc_entries = upper_bound; 108 | } 109 | } 110 | 111 | void loc_table_print(struct loc_table* loc_table, const uint64_t chunk_loc) { 112 | uint64_t low = 0; 113 | uint64_t high = loc_table->loc_entries; 114 | 115 | do { 116 | uint64_t middle = (high - low) / 2 + low; 117 | if (chunk_loc > loc_table->locations[middle].chunk_loc) 118 | low = middle; 119 | else 120 | high = middle; 121 | } while (high - low > 1); 122 | 123 | printf("ROW: %" PRIu64 ", COL: %" PRIu64, loc_table->locations[low].src_row, loc_table->locations[low].src_col); 124 | if (loc_table->locations[low].file) 125 | printf(" in %s.", loc_table->locations[low].file); 126 | else 127 | printf(" from the interactive REPL."); 128 | } 129 | 130 | void debug_print_scanner(struct scanner scanner) { 131 | if (scanner.pos >= scanner.size) 132 | scanner.pos = scanner.size - 1; 133 | while (--scanner.pos) 134 | if (scanner.source[scanner.pos] == '\n') { 135 | scanner.pos++; 136 | break; 137 | } 138 | 139 | while (scanner.pos < scanner.size && scanner.source[scanner.pos] != '\n') 140 | printf("%c", scanner.source[scanner.pos++]); 141 | printf("\nROW: %" PRIu64 ", COL: %" PRIu64, scanner.row, scanner.col); 142 | if (scanner.file) 143 | printf(" in %s", scanner.file); 144 | else 145 | printf(" from the interactive REPL."); 146 | } 147 | 148 | static void print_instruction_dump(struct chunk* chunk, uint32_t* indent) { 149 | printf("%" PRIu64, chunk->pos); 150 | enum op_code op = chunk_read_opcode(chunk); 151 | 152 | for (uint32_t i = 0; i < *indent; i++) 153 | printf("\t"); 154 | 155 | switch (op) 156 | { 157 | case MACHINE_LOAD_VAR: 158 | printf("LOAD VAR, id:%"PRIu64, chunk_read_ulong(chunk)); 159 | break; 160 | case MACHINE_STORE_VAR: 161 | printf("STORE VAR, id:%"PRIu64, chunk_read_ulong(chunk)); 162 | break; 163 | case MACHINE_LOAD_CONST: 164 | printf("LOAD CONST, const:"); 165 | print_value(chunk_read_value(chunk), 0); 166 | break; 167 | case MACHINE_EVAL_BIN_OP: 168 | printf("EVAL BIN OP, op: %d", chunk_read_bin_op(chunk)); 169 | break; 170 | case MACHINE_EVAL_UNI_OP: 171 | printf("EVAL UNI OP, op: %d", chunk_read_uni_op(chunk)); 172 | break; 173 | case MACHINE_END_SKIP: 174 | printf("END_SKIP"); 175 | (*indent)--; 176 | break; 177 | case MACHINE_MARK: 178 | printf("MARK"); 179 | break; 180 | case MACHINE_RETURN_GOTO: 181 | printf("RETURN"); 182 | break; 183 | case MACHINE_GOTO: 184 | printf("GOTO, id:%" PRIu64, chunk_read_ulong(chunk)); 185 | break; 186 | case MACHINE_GOTO_AS: 187 | printf("GOTO AS, id:%" PRIu64, chunk_read_ulong(chunk)); 188 | break; 189 | case MACHINE_LABEL: 190 | chunk_read_ulong(chunk); 191 | printf("LABEL, id:%" PRIu64, chunk_read_ulong(chunk)); 192 | (*indent)++; 193 | break; 194 | case MACHINE_COND_SKIP: 195 | chunk_read_ulong(chunk); 196 | printf("COND SKIP"); 197 | (*indent)++; 198 | break; 199 | case MACHINE_COND_RETURN: 200 | printf("COND RETURN"); 201 | break; 202 | case MACHINE_FLAG: 203 | printf("SET FLAG"); 204 | break; 205 | case MACHINE_RESET_FLAG: 206 | printf("RESET FLAG"); 207 | break; 208 | case MACHINE_FLAG_SKIP: 209 | chunk_read_ulong(chunk); 210 | printf("FLAG SKIP"); 211 | (*indent)++; 212 | break; 213 | case MACHINE_NEW_FRAME: 214 | printf("NEW FRAME"); 215 | break; 216 | case MACHINE_CLEAN: 217 | printf("GC CLEAN"); 218 | break; 219 | case MACHINE_BUILD_COL: 220 | printf("BUILD COL, size:%" PRIu64, chunk_read_ulong(chunk)); 221 | break; 222 | case MACHINE_BUILD_PROTO: { 223 | printf("BUILD RECORD-PROTO, id:%" PRIu64, chunk_read_ulong(chunk)); 224 | uint64_t properties = chunk_read_ulong(chunk); 225 | printf(", properties:%" PRIu64, properties); 226 | while (properties--) 227 | printf(", id:%" PRIu64, chunk_read_ulong(chunk)); 228 | break; 229 | } 230 | case MACHINE_BUILD_RECORD: 231 | printf("BUILD RECORD, proto-id:%" PRIu64, chunk_read_ulong(chunk)); 232 | break; 233 | case MACHINE_INHERIT_REC: 234 | printf("INHERIT RECORD, child:%" PRIu64 ", parent:%"PRIu64, chunk_read_ulong(chunk), chunk_read_ulong(chunk)); 235 | break; 236 | case MACHINE_SET_INDEX: 237 | printf("SET INDEX"); 238 | break; 239 | case MACHINE_GET_INDEX: 240 | printf("GET INDEX"); 241 | break; 242 | case MACHINE_GET_PROPERTY: 243 | printf("GET PROPERTY, property:%" PRIu64, chunk_read_ulong(chunk)); 244 | break; 245 | case MACHINE_SET_PROPERTY: 246 | printf("SET PROPERTY, property:%" PRIu64, chunk_read_ulong(chunk)); 247 | break; 248 | case MACHINE_TRACE: 249 | printf("GC SET TRACE, args: %" PRIu64, chunk_read_ulong(chunk)); 250 | break; 251 | case MACHINE_POP: 252 | printf("POP EVAL"); 253 | break; 254 | case MACHINE_CALL_EXTERN: 255 | printf("CALL EXTERN, args:% " PRIu64 " id:%" PRIu64, chunk_read_ulong(chunk), chunk_read_ulong(chunk)); 256 | break; 257 | case MACHINE_END: 258 | printf("END-DUMP"); 259 | break; 260 | } 261 | 262 | } 263 | 264 | void debug_print_dump(struct chunk chunk) { 265 | uint64_t old_pos = chunk.pos; 266 | chunk.pos = 0; 267 | uint32_t indent = 1; 268 | chunk.last_code = -1; 269 | 270 | int pos_flag = 1; 271 | 272 | while (chunk.last_code != MACHINE_END) { 273 | print_instruction_dump(&chunk, &indent); 274 | if (chunk.pos >= old_pos && chunk.last_code != MACHINE_END && pos_flag) { 275 | printf(" <<< IP"); 276 | pos_flag = 0; 277 | } 278 | printf("\n"); 279 | } 280 | } 281 | 282 | void debug_print_trace(struct machine* machine, struct loc_table* table, uint64_t last_pos) { 283 | for (uint_fast64_t i = 0; i < machine->positions; i++) { 284 | printf("in "); 285 | loc_table_print(table, machine->position_stack[i]); 286 | printf("\n"); 287 | } 288 | printf("\t"); 289 | loc_table_print(table, last_pos); 290 | } -------------------------------------------------------------------------------- /src/opcodes.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "include/error.h" 4 | #include "include/hash.h" 5 | #include "include/machine.h" 6 | #include "include/operators.h" 7 | #include "include/opcodes.h" 8 | 9 | #define NULL_CHECK(PTR, ERROR) if(!(PTR)) { machine->last_err = ERROR; return 0; } 10 | 11 | #define MPUSH_EVAL(EVAL) NULL_CHECK(machine_push_eval(machine, EVAL), ERROR_STACK_OVERFLOW) 12 | #define MPUSH_CONST(EVAL, HAS_CHILDREN) NULL_CHECK(machine_push_const(machine, EVAL, HAS_CHILDREN), ERROR_STACK_OVERFLOW); 13 | 14 | #define MACHINE_ERROR(ERROR) {machine->last_err = ERROR; return 0;} 15 | #define DECL_OPCODE_HANDLER(METHOD_NAME) static const int METHOD_NAME(struct machine* machine, struct chunk* chunk) 16 | 17 | DECL_OPCODE_HANDLER(opcode_load_const) { 18 | MPUSH_CONST(chunk_read_value(chunk), 0); 19 | return 1; 20 | } 21 | 22 | DECL_OPCODE_HANDLER(opcode_load_var) { 23 | struct value*var_ptr = retrieve_var(&machine->var_stack[machine->call_size - 1], chunk_read_ulong(chunk)); 24 | NULL_CHECK(var_ptr, ERROR_VARIABLE_UNDEFINED); 25 | MPUSH_EVAL(var_ptr); 26 | return 1; 27 | } 28 | 29 | DECL_OPCODE_HANDLER(opcode_store_var) { 30 | uint64_t id = chunk_read_ulong(chunk); 31 | 32 | struct value*src = machine_pop_eval(machine); 33 | NULL_CHECK(src, ERROR_INSUFFICIENT_EVALS); 34 | 35 | if (src->gc_flag == GARBAGE_CONSTANT) { 36 | struct value* dest = retrieve_var(&machine->var_stack[machine->call_size - 1], id); 37 | if (dest) { 38 | if (src->type == VALUE_TYPE_NULL) { 39 | if (!emplace_var(&machine->var_stack[machine->call_size - 1], id, gc_register_value(&machine->garbage_collector, *src))) 40 | MACHINE_ERROR(ERROR_OUT_OF_MEMORY); 41 | } 42 | else { 43 | free_value(dest); 44 | *dest = *src; 45 | NULL_CHECK(gc_register_children(&machine->garbage_collector, dest), ERROR_OUT_OF_MEMORY); 46 | } 47 | } 48 | else if (!emplace_var(&machine->var_stack[machine->call_size - 1], id, gc_register_value(&machine->garbage_collector, *src))) 49 | MACHINE_ERROR(ERROR_OUT_OF_MEMORY); 50 | } 51 | else if (!emplace_var(&machine->var_stack[machine->call_size - 1], id, src)) 52 | MACHINE_ERROR(ERROR_OUT_OF_MEMORY); 53 | 54 | return 1; 55 | } 56 | 57 | DECL_OPCODE_HANDLER(opcode_eval_bin_op) { 58 | struct value*value_b = machine_pop_eval(machine); 59 | NULL_CHECK(value_b, ERROR_INSUFFICIENT_EVALS); 60 | struct value*value_a = machine_pop_eval(machine); 61 | NULL_CHECK(value_a, ERROR_INSUFFICIENT_EVALS); 62 | 63 | enum binary_operator op = chunk_read_bin_op(chunk); 64 | 65 | if (IS_RECORD(*value_a) || IS_RECORD(*value_b)) { 66 | int in_order = IS_RECORD(*value_a); 67 | struct value* record_val = in_order ? value_a : value_b; 68 | struct value* operand = in_order ? value_b : value_a; 69 | 70 | struct value* original_record_operand = record_val; 71 | 72 | while (record_val != NULL) 73 | { 74 | uint64_t pos = cache_retrieve_pos(&machine->global_cache, combine_hash(combine_hash(combine_hash((uint64_t)op, BINARY_OVERLOAD), 3), record_val->payload.object.ptr.record->prototype->identifier)); 75 | if (!pos) 76 | record_val = record_get_property(record_val->payload.object.ptr.record, RECORD_BASE_PROPERTY); 77 | else { 78 | if (machine->positions == MACHINE_MAX_POSITIONS) 79 | MACHINE_ERROR(ERROR_STACK_OVERFLOW); 80 | machine->position_stack[machine->positions] = chunk->pos; 81 | machine->position_flags[machine->positions++] = 1; 82 | 83 | MPUSH_EVAL(operand); 84 | MPUSH_EVAL(original_record_operand); 85 | MPUSH_CONST(NUM_VALUE(in_order), 0); 86 | 87 | chunk_jump_to(chunk, pos); 88 | return 1; 89 | } 90 | } 91 | } 92 | NULL_CHECK(invoke_binary_op(op, value_a, value_b, machine), ERROR_UNEXPECTED_TYPE); 93 | return 1; 94 | } 95 | 96 | DECL_OPCODE_HANDLER(opcode_eval_uni_op) { 97 | struct value*operand = machine_pop_eval(machine); 98 | NULL_CHECK(operand, ERROR_INSUFFICIENT_EVALS); 99 | enum unary_operator op = chunk_read_uni_op(chunk); 100 | 101 | if (IS_RECORD(*operand)) { 102 | struct value* record_val = operand; 103 | struct value* original_record_operand = record_val; 104 | while (record_val != NULL) 105 | { 106 | uint64_t pos = cache_retrieve_pos(&machine->global_cache, combine_hash(combine_hash(combine_hash((uint64_t)op, UNARY_OVERLOAD), 1), record_val->payload.object.ptr.record->prototype->identifier)); 107 | if (!pos) 108 | record_val = record_get_property(record_val->payload.object.ptr.record, RECORD_BASE_PROPERTY); 109 | else { 110 | if (machine->positions == MACHINE_MAX_POSITIONS) 111 | MACHINE_ERROR(ERROR_STACK_OVERFLOW); 112 | machine->position_stack[machine->positions] = chunk->pos; 113 | machine->position_flags[machine->positions++] = 1; 114 | 115 | MPUSH_EVAL(original_record_operand); 116 | chunk_jump_to(chunk, pos); 117 | return 1; 118 | } 119 | } 120 | } 121 | NULL_CHECK(invoke_unary_op(op, operand, machine), ERROR_UNEXPECTED_TYPE); 122 | return 1; 123 | } 124 | 125 | static struct value* argv[1000]; 126 | 127 | DECL_OPCODE_HANDLER(opcode_eval_builtin) { 128 | uint64_t id = chunk_read_ulong(chunk); 129 | uint64_t arguments = chunk_read_ulong(chunk); 130 | 131 | uint_fast64_t i = arguments; 132 | while (i--) 133 | NULL_CHECK(argv[i] = machine_pop_eval(machine), ERROR_INSUFFICIENT_EVALS); 134 | 135 | MPUSH_CONST(cache_invoke_builtin(&machine->global_cache, id, &argv, arguments, machine), 0); 136 | 137 | i = arguments; 138 | while (i--) 139 | if(argv[i]->gc_flag == GARBAGE_CONSTANT) 140 | free_value(argv[i]); 141 | 142 | return 1; 143 | } 144 | 145 | DECL_OPCODE_HANDLER(opcode_mark) { 146 | if (machine->positions == MACHINE_MAX_POSITIONS) 147 | MACHINE_ERROR(ERROR_STACK_OVERFLOW); 148 | machine->position_stack[machine->positions] = chunk->pos - 1; 149 | machine->position_flags[machine->positions++] = 0; 150 | return 1; 151 | } 152 | 153 | DECL_OPCODE_HANDLER(opcode_goto) { 154 | if (machine->positions == MACHINE_MAX_POSITIONS) 155 | MACHINE_ERROR(ERROR_STACK_OVERFLOW); 156 | machine->position_stack[machine->positions] = chunk->pos + sizeof(uint64_t); 157 | machine->position_flags[machine->positions++] = 1; 158 | chunk_jump_to(chunk, chunk_read_ulong(chunk)); 159 | return 1; 160 | } 161 | 162 | DECL_OPCODE_HANDLER(opcode_goto_as) { 163 | NULL_CHECK(machine->evals, ERROR_INSUFFICIENT_EVALS); 164 | struct value* record_eval = machine->evaluation_stack[machine->evals - 1]; 165 | 166 | if (!IS_RECORD(*record_eval)) 167 | MACHINE_ERROR(ERROR_UNEXPECTED_TYPE); 168 | 169 | struct record* current_record = record_eval->payload.object.ptr.record; 170 | uint64_t id = chunk_read_ulong(chunk); 171 | 172 | if (machine->positions == MACHINE_MAX_POSITIONS) 173 | MACHINE_ERROR(ERROR_STACK_OVERFLOW); 174 | machine->position_stack[machine->positions] = chunk->pos; // +sizeof(uint64_t); 175 | machine->position_flags[machine->positions++] = 1; 176 | 177 | while (record_eval != NULL) 178 | { 179 | uint64_t pos = cache_retrieve_pos(&machine->global_cache, combine_hash(id, current_record->prototype->identifier)); 180 | if (pos) { 181 | chunk_jump_to(chunk, pos); 182 | return 1; 183 | } 184 | record_eval = record_get_property(current_record, RECORD_BASE_PROPERTY); 185 | if (record_eval) 186 | if(!IS_RECORD(*record_eval)) 187 | MACHINE_ERROR(ERROR_UNEXPECTED_TYPE) 188 | else 189 | current_record = record_eval->payload.object.ptr.record; 190 | } 191 | MACHINE_ERROR(ERROR_LABEL_UNDEFINED); 192 | } 193 | 194 | DECL_OPCODE_HANDLER(opcode_return_goto) { 195 | while (!machine->position_flags[machine->positions - 1]) 196 | machine->positions--; 197 | chunk_jump_to(chunk, machine->position_stack[--machine->positions]); 198 | return 1; 199 | } 200 | 201 | DECL_OPCODE_HANDLER(opcode_label) { 202 | uint64_t pos = chunk_read_ulong(chunk); 203 | chunk_read_size(chunk, sizeof(uint64_t)); 204 | chunk_jump_to(chunk, pos); 205 | chunk_read_opcode(chunk); 206 | return 1; 207 | } 208 | 209 | DECL_OPCODE_HANDLER(opcode_cond_skip) { 210 | uint64_t skip_pos = chunk_read_ulong(chunk); 211 | if (!machine_condition_check(machine)) { 212 | chunk_jump_to(chunk, skip_pos); 213 | chunk_read_opcode(chunk); 214 | } 215 | return 1; 216 | } 217 | 218 | DECL_OPCODE_HANDLER(opcode_cond_return) { 219 | --machine->positions; 220 | if (machine_condition_check(machine)) 221 | chunk_jump_to(chunk, machine->position_stack[machine->positions]); 222 | return 1; 223 | } 224 | 225 | DECL_OPCODE_HANDLER(opcode_flag) { 226 | machine->std_flag = 1; 227 | return 1; 228 | } 229 | 230 | DECL_OPCODE_HANDLER(opcode_reset_flag) { 231 | machine->std_flag = 0; 232 | return 1; 233 | } 234 | 235 | DECL_OPCODE_HANDLER(opcode_flag_skip) { 236 | uint64_t skip_pos = chunk_read_ulong(chunk); 237 | if (machine->std_flag) { 238 | chunk_jump_to(chunk, skip_pos); 239 | chunk_read_opcode(chunk); 240 | } 241 | return 1; 242 | } 243 | 244 | DECL_OPCODE_HANDLER(opcode_new_frame) { 245 | if (machine->call_size == MACHINE_MAX_CALLS) 246 | MACHINE_ERROR(ERROR_STACK_OVERFLOW); 247 | if (!init_var_context(&machine->var_stack[machine->call_size++], &machine->garbage_collector)) 248 | MACHINE_ERROR(ERROR_OUT_OF_MEMORY); 249 | return 1; 250 | } 251 | 252 | DECL_OPCODE_HANDLER(opcode_clean) { 253 | if (!machine->call_size) 254 | MACHINE_ERROR(ERROR_INSUFFICIENT_CALLS); 255 | free_var_context(&machine->var_stack[--machine->call_size]); 256 | return 1; 257 | } 258 | 259 | DECL_OPCODE_HANDLER(opcode_trace) { 260 | NULL_CHECK(machine->evals >= machine->evals, ERROR_INSUFFICIENT_EVALS); 261 | uint64_t to_trace = chunk_read_ulong(chunk); 262 | for(uint_fast64_t i = 0; i < to_trace; i++) 263 | gc_register_trace(&machine->garbage_collector, machine->evaluation_stack[machine->evals - 1 - i]); 264 | return 1; 265 | } 266 | 267 | DECL_OPCODE_HANDLER(opcode_pop) { 268 | NULL_CHECK(machine->evals, ERROR_INSUFFICIENT_EVALS); 269 | NULL_CHECK(machine_pop_eval(machine), ERROR_INSUFFICIENT_EVALS); 270 | return 1; 271 | } 272 | 273 | DECL_OPCODE_HANDLER(opcode_get_index) { 274 | struct value*index_val = machine_pop_eval(machine); 275 | NULL_CHECK(index_val, ERROR_INSUFFICIENT_EVALS); 276 | struct value*collection_val = machine_pop_eval(machine); 277 | NULL_CHECK(collection_val, ERROR_INSUFFICIENT_EVALS); 278 | 279 | if (index_val->type != VALUE_TYPE_NUM || !IS_COLLECTION(*collection_val)) 280 | MACHINE_ERROR(ERROR_UNEXPECTED_TYPE); 281 | 282 | struct collection* collection = collection_val->payload.object.ptr.collection; 283 | uint64_t index = index_val->payload.numerical; 284 | 285 | if (index >= collection->size) 286 | MACHINE_ERROR(ERROR_INDEX_OUT_OF_RANGE); 287 | 288 | MPUSH_EVAL(collection->inner_collection[index]); 289 | 290 | return 1; 291 | } 292 | 293 | DECL_OPCODE_HANDLER(opcode_set_index) { 294 | struct value*set_val = machine_pop_eval(machine); 295 | NULL_CHECK(set_val, ERROR_INSUFFICIENT_EVALS); 296 | struct value*index_val = machine_pop_eval(machine); 297 | NULL_CHECK(index_val, ERROR_INSUFFICIENT_EVALS); 298 | struct value*collection_val = machine_pop_eval(machine); 299 | NULL_CHECK(collection_val, ERROR_INSUFFICIENT_EVALS); 300 | 301 | if (index_val->type != VALUE_TYPE_NUM || collection_val->type != VALUE_TYPE_OBJ || collection_val->payload.object.type != OBJ_TYPE_COL) 302 | MACHINE_ERROR(ERROR_UNEXPECTED_TYPE); 303 | 304 | struct collection* collection = collection_val->payload.object.ptr.collection; 305 | uint64_t index = index_val->payload.numerical; 306 | 307 | if (index >= collection->size) 308 | MACHINE_ERROR(ERROR_INDEX_OUT_OF_RANGE); 309 | 310 | if (set_val->gc_flag == GARBAGE_CONSTANT) { 311 | if(collection->inner_collection[index] == GARBAGE_CONSTANT) 312 | collection->inner_collection[index] = set_val; 313 | else { 314 | if (set_val->type == VALUE_TYPE_NULL) { 315 | NULL_CHECK(collection->inner_collection[index] = gc_register_value(&machine->garbage_collector, *set_val), ERROR_OUT_OF_MEMORY); 316 | } 317 | else { 318 | free_value(collection->inner_collection[index]); 319 | *collection->inner_collection[index] = *set_val; 320 | NULL_CHECK(gc_register_children(&machine->garbage_collector, collection->inner_collection[index]), ERROR_OUT_OF_MEMORY); 321 | } 322 | } 323 | } 324 | else 325 | collection->inner_collection[index] = set_val; 326 | 327 | return 1; 328 | } 329 | 330 | DECL_OPCODE_HANDLER(opcode_get_property) { 331 | struct value*record_eval = machine_pop_eval(machine); 332 | NULL_CHECK(record_eval, ERROR_INSUFFICIENT_EVALS); 333 | 334 | if (!IS_RECORD(*record_eval)) 335 | MACHINE_ERROR(ERROR_UNEXPECTED_TYPE); 336 | 337 | struct value* property_val = record_get_property(record_eval->payload.object.ptr.record, chunk_read_ulong(chunk)); 338 | NULL_CHECK(property_val, ERROR_PROPERTY_UNDEFINED); 339 | MPUSH_EVAL(property_val); 340 | return 1; 341 | } 342 | 343 | DECL_OPCODE_HANDLER(opcode_set_property) { 344 | uint64_t property = chunk_read_ulong(chunk); 345 | 346 | struct value*set_val = machine_pop_eval(machine); 347 | NULL_CHECK(set_val, ERROR_INSUFFICIENT_EVALS); 348 | struct value*record_eval = machine_pop_eval(machine); 349 | NULL_CHECK(record_eval, ERROR_INSUFFICIENT_EVALS); 350 | 351 | if (!IS_RECORD(*record_eval)) 352 | MACHINE_ERROR(ERROR_UNEXPECTED_TYPE); 353 | struct record* record = record_eval->payload.object.ptr.record; 354 | 355 | if (set_val->gc_flag == GARBAGE_CONSTANT) { 356 | struct value* property_val = record_get_property(record, property); 357 | 358 | if (property_val->gc_flag == GARBAGE_CONSTANT) { 359 | if (!record_set_property(record, property, set_val)) 360 | MACHINE_ERROR(ERROR_PROPERTY_UNDEFINED); 361 | } 362 | else { 363 | if (set_val->gc_flag == VALUE_TYPE_NULL) { 364 | if (!record_set_property(record, property, gc_register_value(&machine->garbage_collector, *set_val))) 365 | MACHINE_ERROR(ERROR_OUT_OF_MEMORY); 366 | } 367 | else { 368 | free_value(property_val); 369 | *property_val = *set_val; 370 | NULL_CHECK(gc_register_children(&machine->garbage_collector, property_val), ERROR_OUT_OF_MEMORY); 371 | } 372 | } 373 | } 374 | else if (!record_set_property(record, property, set_val)) 375 | MACHINE_ERROR(ERROR_PROPERTY_UNDEFINED); 376 | 377 | return 1; 378 | } 379 | 380 | DECL_OPCODE_HANDLER(opcode_build_collection) { 381 | uint64_t req_size = chunk_read_ulong(chunk); 382 | 383 | struct collection* collection = malloc(sizeof(struct collection)); 384 | NULL_CHECK(collection, ERROR_OUT_OF_MEMORY); 385 | NULL_CHECK(init_collection(collection, req_size), ERROR_OUT_OF_MEMORY); 386 | 387 | while (req_size--) 388 | NULL_CHECK(collection->inner_collection[req_size] = machine_pop_eval(machine), ERROR_INSUFFICIENT_EVALS); 389 | 390 | struct value new_val; 391 | 392 | struct object obj; 393 | init_object_col(&obj, collection); 394 | init_obj_value(&new_val, obj); 395 | 396 | MPUSH_CONST(new_val, 1); 397 | 398 | return 1; 399 | } 400 | 401 | DECL_OPCODE_HANDLER(opcode_build_record_proto) { 402 | uint64_t id = chunk_read_ulong(chunk); 403 | uint64_t properties = chunk_read_ulong(chunk); 404 | 405 | struct record_prototype* prototype = cache_insert_prototype(&machine->global_cache, id); 406 | NULL_CHECK(prototype, ERROR_OUT_OF_MEMORY); 407 | 408 | while (properties--) 409 | if (!record_append_property(prototype, chunk_read_ulong(chunk))) 410 | MACHINE_ERROR(ERROR_PROPERTY_REDEFINE); 411 | 412 | return 1; 413 | } 414 | 415 | DECL_OPCODE_HANDLER(opcode_build_record) { 416 | uint64_t id = chunk_read_ulong(chunk); 417 | 418 | struct record* new_rec = malloc(sizeof(struct record)); 419 | NULL_CHECK(new_rec, ERROR_OUT_OF_MEMORY); 420 | 421 | if (!cache_init_record(&machine->global_cache, id, new_rec, machine)) 422 | MACHINE_ERROR(ERROR_RECORD_UNDEFINED); 423 | 424 | struct value rec_val; 425 | struct object obj; 426 | 427 | init_object_rec(&obj, new_rec); 428 | init_obj_value(&rec_val, obj); 429 | 430 | MPUSH_CONST(rec_val, 0); 431 | return 1; 432 | } 433 | 434 | DECL_OPCODE_HANDLER(opcode_inherit_record) { 435 | uint64_t child_id = chunk_read_ulong(chunk); 436 | uint64_t parent_id = chunk_read_ulong(chunk); 437 | 438 | if (!cache_merge_proto(&machine->global_cache, child_id, parent_id)) 439 | MACHINE_ERROR(ERROR_RECORD_UNDEFINED); 440 | return 1; 441 | } 442 | 443 | DECL_OPCODE_HANDLER((*opcode_handler[30])) = { 444 | opcode_load_const, 445 | opcode_load_var, 446 | 447 | opcode_store_var, 448 | 449 | opcode_eval_bin_op, 450 | opcode_eval_uni_op, 451 | 452 | NULL, 453 | 454 | opcode_mark, 455 | 456 | opcode_goto, 457 | opcode_goto_as, 458 | opcode_return_goto, 459 | opcode_label, 460 | 461 | opcode_cond_skip, 462 | opcode_cond_return, 463 | 464 | opcode_flag, 465 | opcode_reset_flag, 466 | opcode_flag_skip, 467 | 468 | opcode_new_frame, 469 | opcode_clean, 470 | opcode_trace, 471 | opcode_pop, 472 | 473 | opcode_build_collection, 474 | opcode_build_record_proto, 475 | opcode_build_record, 476 | opcode_inherit_record, 477 | 478 | opcode_get_index, 479 | opcode_set_index, 480 | opcode_get_property, 481 | opcode_set_property, 482 | 483 | opcode_eval_builtin, 484 | NULL 485 | }; 486 | 487 | const int handle_opcode(enum op_code op, struct machine* machine, struct chunk* chunk) { 488 | if (op >= MACHINE_END || op < 0) 489 | MACHINE_ERROR(ERROR_UNRECOGNIZED_OPCODE); 490 | if (opcode_handler[op]) 491 | return (*opcode_handler[op])(machine, chunk); 492 | return 1; 493 | } -------------------------------------------------------------------------------- /src/statments.c: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_DEPRECATE 2 | //buffer security is imoortant, but not as much as portability 3 | 4 | #include 5 | #include 6 | #include 7 | #include "include/opcodes.h" 8 | #include "include/globals.h" 9 | #include "include/chunk.h" 10 | #include "include/compiler.h" 11 | #include "include/statements.h" 12 | 13 | #define MATCH_TOK(TOK, TYPE) if(TOK.type != TYPE) { compiler->last_err = ERROR_UNEXPECTED_TOKEN; return 0; } 14 | #define NULL_CHECK(PTR) if (PTR == NULL) { compiler->last_err = ERROR_OUT_OF_MEMORY; return 0; } 15 | 16 | #define DECL_VALUE_COMPILER(METHOD_NAME) static const int METHOD_NAME(struct compiler* compiler, struct chunk_builder* builder, const int optimize_copy, uint64_t optimize_goto) 17 | #define DECL_STATMENT_COMPILER(METHOD_NAME) static const int METHOD_NAME(struct compiler* compiler, struct chunk_builder* builder, struct loc_table* loc_table, const uint64_t callee, const int encapsulated, uint64_t proc_encapsulated) 18 | 19 | DECL_VALUE_COMPILER(compile_primative) { 20 | MATCH_TOK(compiler->last_tok, TOK_PRIMATIVE); 21 | chunk_write_opcode(builder, MACHINE_LOAD_CONST); 22 | chunk_write_value(builder, compiler->last_tok.payload.primative); 23 | compiler_read_tok(compiler); 24 | return 1; 25 | } 26 | 27 | DECL_VALUE_COMPILER(compile_string) { 28 | MATCH_TOK(compiler->last_tok, TOK_STR); 29 | char* buffer = malloc(150); 30 | NULL_CHECK(buffer); 31 | if (!scanner_read_str(&compiler->scanner, buffer, 1)) { 32 | free(buffer); 33 | return 0; 34 | } 35 | uint64_t len = strlen(buffer); 36 | for (uint_fast64_t i = 0; i < len; i++) { 37 | chunk_write_opcode(builder, MACHINE_LOAD_CONST); 38 | chunk_write_value(builder, CHAR_VALUE(buffer[i])); 39 | } 40 | free(buffer); 41 | chunk_write_opcode(builder, MACHINE_BUILD_COL); 42 | chunk_write_ulong(builder, len); 43 | compiler_read_tok(compiler); 44 | return 1; 45 | } 46 | 47 | DECL_VALUE_COMPILER(compile_reference) { 48 | const int is_ref = (compiler->last_tok.type == TOK_REF); 49 | if (is_ref) 50 | compiler_read_tok(compiler); 51 | MATCH_TOK(compiler->last_tok, TOK_IDENTIFIER); 52 | chunk_write_opcode(builder, MACHINE_LOAD_VAR); 53 | chunk_write_ulong(builder, compiler->last_tok.payload.identifier); 54 | compiler_read_tok(compiler); 55 | while (compiler->last_tok.type == TOK_OPEN_BRACKET || compiler->last_tok.type == TOK_PERIOD) 56 | { 57 | if (compiler->last_tok.type == TOK_OPEN_BRACKET) { 58 | compiler_read_tok(compiler); 59 | if (!compile_expression(compiler, builder, 0, 1, 0)) 60 | return 0; 61 | MATCH_TOK(compiler->last_tok, TOK_CLOSE_BRACKET); 62 | chunk_write_opcode(builder, MACHINE_GET_INDEX); 63 | } 64 | else { 65 | MATCH_TOK(compiler_read_tok(compiler), TOK_IDENTIFIER); 66 | chunk_write_opcode(builder, MACHINE_GET_PROPERTY); 67 | chunk_write_ulong(builder, compiler->last_tok.payload.identifier); 68 | } 69 | compiler_read_tok(compiler); 70 | } 71 | 72 | if (!is_ref && !optimize_copy) { 73 | chunk_write_opcode(builder, MACHINE_EVAL_UNI_OP); 74 | chunk_write_opcode(builder, OPERATOR_COPY); 75 | } 76 | return 1; 77 | } 78 | 79 | DECL_VALUE_COMPILER(compile_array) { 80 | MATCH_TOK(compiler->last_tok, TOK_OPEN_BRACE); 81 | uint64_t array_size = 0; 82 | do { 83 | compiler_read_tok(compiler); 84 | if (!compile_expression(compiler, builder, 0, 0, 0)) 85 | return 0; 86 | array_size++; 87 | } while (compiler->last_tok.type == TOK_COMMA); 88 | MATCH_TOK(compiler->last_tok, TOK_CLOSE_BRACE); 89 | 90 | chunk_write_opcode(builder, MACHINE_BUILD_COL); 91 | chunk_write_ulong(builder, array_size); 92 | compiler_read_tok(compiler); 93 | return 1; 94 | } 95 | 96 | DECL_VALUE_COMPILER(compile_unary) { 97 | if (compiler->last_tok.type == TOK_ALLOC) { 98 | MATCH_TOK(compiler_read_tok(compiler), TOK_OPEN_BRACKET); 99 | compiler_read_tok(compiler); 100 | if (!compile_expression(compiler, builder, PREC_BEGIN, 1, 0)) 101 | return 0; 102 | chunk_write_opcode(builder, MACHINE_EVAL_UNI_OP); 103 | chunk_write_opcode(builder, OPERATOR_ALLOC); 104 | MATCH_TOK(compiler->last_tok, TOK_CLOSE_BRACKET); 105 | compiler_read_tok(compiler); 106 | } 107 | else if (compiler->last_tok.type == TOK_BINARY_OP && compiler->last_tok.payload.bin_op == OPERATOR_SUBTRACT) { 108 | compiler_read_tok(compiler); 109 | if (!compile_value(compiler, builder, 1, optimize_goto)) 110 | return 0; 111 | chunk_write_opcode(builder, MACHINE_EVAL_UNI_OP); 112 | chunk_write_uni_op(builder, OPERATOR_NEGATE); 113 | } 114 | else { 115 | MATCH_TOK(compiler->last_tok, TOK_UNARY_OP); 116 | enum unary_operator op = compiler->last_tok.payload.uni_op; 117 | compiler_read_tok(compiler); 118 | if (!compile_value(compiler, builder, 1, optimize_goto)) 119 | return 0; 120 | chunk_write_opcode(builder, MACHINE_EVAL_UNI_OP); 121 | chunk_write_uni_op(builder, op); 122 | } 123 | return 1; 124 | } 125 | 126 | DECL_VALUE_COMPILER(compile_paren) { 127 | MATCH_TOK(compiler->last_tok, TOK_OPEN_PAREN); 128 | compiler_read_tok(compiler); 129 | if (!compile_expression(compiler, builder, PREC_BEGIN, 1, 0)) 130 | return 0; 131 | MATCH_TOK(compiler->last_tok, TOK_CLOSE_PAREN); 132 | compiler_read_tok(compiler); 133 | return 1; 134 | } 135 | 136 | DECL_VALUE_COMPILER(compile_goto) { 137 | int is_proc = compiler->last_tok.type == TOK_GOTO; 138 | if (!is_proc) 139 | MATCH_TOK(compiler->last_tok, TOK_EXTERN); 140 | MATCH_TOK(compiler_read_tok(compiler), TOK_IDENTIFIER); 141 | uint64_t proc_id = compiler->last_tok.payload.identifier; 142 | uint64_t arguments = 0; 143 | int is_record_proc = 0; 144 | 145 | struct chunk_builder temp_builder; 146 | 147 | if (compiler_read_tok(compiler).type == TOK_AS) { 148 | compiler_read_tok(compiler); 149 | init_chunk_builder(&temp_builder); 150 | if (!compile_value(compiler, &temp_builder, 1, optimize_goto)) 151 | return 0; 152 | is_record_proc = 1; 153 | arguments++; 154 | } 155 | 156 | if (compiler->last_tok.type == TOK_OPEN_PAREN) { 157 | while (1) 158 | { 159 | compiler_read_tok(compiler); 160 | if (!compile_expression(compiler, builder, PREC_BEGIN, 1, 0)) 161 | return 0; 162 | arguments++; 163 | if (compiler->last_tok.type != TOK_COMMA) 164 | break; 165 | } 166 | MATCH_TOK(compiler->last_tok, TOK_CLOSE_PAREN); 167 | compiler_read_tok(compiler); 168 | } 169 | if (is_proc) { 170 | if (is_record_proc) { 171 | chunk_write_chunk(builder, build_chunk(&temp_builder), 1); 172 | chunk_write_opcode(builder, MACHINE_GOTO_AS); 173 | chunk_write_ulong(builder, combine_hash(proc_id, arguments)); 174 | } 175 | else { 176 | uint64_t label_id = combine_hash(combine_hash(proc_id, arguments), 0); 177 | if(optimize_goto && compiler->last_tok.type != TOK_BINARY_OP && label_id == optimize_goto){ 178 | chunk_write_opcode(builder, MACHINE_GOTO); 179 | chunk_write_ulong(builder, label_id); 180 | } 181 | else { 182 | chunk_write_opcode(builder, MACHINE_NEW_FRAME); 183 | if (arguments) { 184 | chunk_write_opcode(builder, MACHINE_TRACE); 185 | chunk_write_ulong(builder, arguments); 186 | } 187 | chunk_write_opcode(builder, MACHINE_GOTO); 188 | chunk_write_ulong(builder, label_id); 189 | chunk_write_opcode(builder, MACHINE_TRACE); 190 | chunk_write_ulong(builder, 1); 191 | chunk_write_opcode(builder, MACHINE_CLEAN); 192 | } 193 | } 194 | } 195 | else { 196 | chunk_write_opcode(builder, MACHINE_CALL_EXTERN); 197 | chunk_write_ulong(builder, proc_id); 198 | chunk_write_ulong(builder, arguments); 199 | } 200 | return 1; 201 | } 202 | 203 | DECL_VALUE_COMPILER(compile_new_record) { 204 | MATCH_TOK(compiler->last_tok, TOK_NEW); 205 | MATCH_TOK(compiler_read_tok(compiler), TOK_IDENTIFIER); 206 | uint64_t record_id = compiler->last_tok.payload.identifier; 207 | uint64_t arguments = 1; 208 | if (compiler_read_tok(compiler).type == TOK_OPEN_PAREN) { 209 | while (1) 210 | { 211 | compiler_read_tok(compiler); 212 | if (!compile_expression(compiler, builder, PREC_BEGIN, 1, 0)) 213 | return 0; 214 | arguments++; 215 | if (compiler->last_tok.type != TOK_COMMA) 216 | break; 217 | } 218 | MATCH_TOK(compiler->last_tok, TOK_CLOSE_PAREN); 219 | compiler_read_tok(compiler); 220 | } 221 | chunk_write_opcode(builder, MACHINE_BUILD_RECORD); 222 | chunk_write_ulong(builder, record_id); 223 | chunk_write_opcode(builder, MACHINE_GOTO_AS); 224 | chunk_write_ulong(builder, combine_hash(RECORD_INIT_PROC, arguments)); 225 | return 1; 226 | } 227 | 228 | DECL_VALUE_COMPILER(compile_hashtag) { 229 | MATCH_TOK(compiler->last_tok, TOK_HASHTAG); 230 | MATCH_TOK(compiler_read_tok(compiler), TOK_IDENTIFIER); 231 | chunk_write_opcode(builder, MACHINE_LOAD_CONST); 232 | chunk_write_value(builder, ID_VALUE(compiler->last_tok.payload.identifier)); 233 | compiler_read_tok(compiler); 234 | return 1; 235 | } 236 | 237 | DECL_STATMENT_COMPILER(compile_value_statement) { 238 | if (!compile_value(compiler, builder, 1, proc_encapsulated)) 239 | return 0; 240 | chunk_write_opcode(builder, MACHINE_POP); 241 | return 1; 242 | } 243 | 244 | DECL_STATMENT_COMPILER(compile_set) { 245 | MATCH_TOK(compiler->last_tok, TOK_SET); 246 | MATCH_TOK(compiler_read_tok(compiler), TOK_IDENTIFIER); 247 | uint64_t var_id = compiler->last_tok.payload.identifier; 248 | if (compiler_read_tok(compiler).type == TOK_TO) { 249 | compiler_read_tok(compiler); 250 | if (!compile_expression(compiler, builder, PREC_BEGIN, 0, 0)) 251 | return 0; 252 | chunk_write_opcode(builder, MACHINE_STORE_VAR); 253 | chunk_write_ulong(builder, var_id); 254 | } 255 | else { 256 | chunk_write_opcode(builder, MACHINE_LOAD_VAR); 257 | chunk_write_ulong(builder, var_id); 258 | while (1) 259 | { 260 | if (compiler->last_tok.type == TOK_OPEN_BRACKET) { 261 | compiler_read_tok(compiler); 262 | if (!compile_expression(compiler, builder, PREC_BEGIN, 1, 0)) 263 | return 0; 264 | MATCH_TOK(compiler->last_tok, TOK_CLOSE_BRACKET); 265 | if (compiler_read_tok(compiler).type == TOK_TO) { 266 | compiler_read_tok(compiler); 267 | if (!compile_expression(compiler, builder, PREC_BEGIN, 0, 0)) 268 | return 0; 269 | chunk_write_opcode(builder, MACHINE_SET_INDEX); 270 | break; 271 | } 272 | else { 273 | chunk_write_opcode(builder, MACHINE_GET_INDEX); 274 | } 275 | } 276 | else if (compiler->last_tok.type == TOK_PERIOD) { 277 | MATCH_TOK(compiler_read_tok(compiler), TOK_IDENTIFIER); 278 | uint64_t property = compiler->last_tok.payload.identifier; 279 | if (compiler_read_tok(compiler).type == TOK_TO) { 280 | compiler_read_tok(compiler); 281 | if (!compile_expression(compiler, builder, PREC_BEGIN, 0, 0)) 282 | return 0; 283 | chunk_write_opcode(builder, MACHINE_SET_PROPERTY); 284 | chunk_write_ulong(builder, property); 285 | break; 286 | } 287 | else { 288 | chunk_write_opcode(builder, MACHINE_GET_PROPERTY); 289 | chunk_write_ulong(builder, property); 290 | } 291 | } 292 | else { 293 | compiler->last_err = ERROR_UNEXPECTED_TOKEN; 294 | return 0; 295 | } 296 | } 297 | } 298 | return 1; 299 | } 300 | 301 | DECL_STATMENT_COMPILER(compile_if) { 302 | MATCH_TOK(compiler->last_tok, TOK_IF); 303 | chunk_write_opcode(builder, MACHINE_RESET_FLAG); 304 | 305 | compiler_read_tok(compiler); 306 | if (!compile_expression(compiler, builder, PREC_BEGIN, 1, 0)) 307 | return 0; 308 | 309 | chunk_write_opcode(builder, MACHINE_COND_SKIP); 310 | 311 | if (!compile_body(compiler, builder, loc_table, callee, proc_encapsulated)) 312 | return 0; 313 | 314 | chunk_write_opcode(builder, MACHINE_FLAG); 315 | chunk_write_opcode(builder, MACHINE_END_SKIP); 316 | 317 | while (compiler->last_tok.type == TOK_ELIF || compiler->last_tok.type == TOK_ELSE) 318 | { 319 | if (compiler->last_tok.type == TOK_ELIF) { 320 | compiler_read_tok(compiler); 321 | chunk_write_opcode(builder, MACHINE_FLAG_SKIP); 322 | if (!compile_expression(compiler, builder, PREC_BEGIN, 1,0)) 323 | return 0; 324 | chunk_write_opcode(builder, MACHINE_COND_SKIP); 325 | 326 | if (!compile_body(compiler, builder, loc_table, callee, proc_encapsulated)) 327 | return 0; 328 | 329 | chunk_write_opcode(builder, MACHINE_FLAG); 330 | chunk_write_opcode(builder, MACHINE_END_SKIP); 331 | chunk_write_opcode(builder, MACHINE_END_SKIP); 332 | } 333 | else { 334 | compiler_read_tok(compiler); 335 | chunk_write_opcode(builder, MACHINE_FLAG_SKIP); 336 | if (!compile_body(compiler, builder, loc_table, callee, proc_encapsulated)) 337 | return 0; 338 | chunk_write_opcode(builder, MACHINE_END_SKIP); 339 | } 340 | } 341 | return 1; 342 | } 343 | 344 | DECL_STATMENT_COMPILER(compile_while) { 345 | MATCH_TOK(compiler->last_tok, TOK_WHILE); 346 | compiler_read_tok(compiler); 347 | 348 | struct chunk_builder temp_expr_builder; 349 | init_chunk_builder(&temp_expr_builder); 350 | 351 | if (!compile_expression(compiler, &temp_expr_builder, PREC_BEGIN, 1, 0)) 352 | return 0; 353 | 354 | struct chunk temp_expr_chunk = build_chunk(&temp_expr_builder); 355 | 356 | chunk_write_chunk(builder, temp_expr_chunk, 0); 357 | chunk_write_opcode(builder, MACHINE_COND_SKIP); 358 | 359 | chunk_write_opcode(builder, MACHINE_MARK); 360 | 361 | if (!compile_body(compiler, builder, loc_table, callee, proc_encapsulated)) 362 | return 0; 363 | 364 | chunk_write_chunk(builder, temp_expr_chunk, 1); 365 | 366 | chunk_write_opcode(builder, MACHINE_COND_RETURN); 367 | chunk_write_opcode(builder, MACHINE_END_SKIP); 368 | return 1; 369 | } 370 | 371 | DECL_STATMENT_COMPILER(compile_do_while) { 372 | MATCH_TOK(compiler->last_tok, TOK_DO); 373 | compiler_read_tok(compiler); 374 | 375 | chunk_write_opcode(builder, MACHINE_MARK); 376 | 377 | if (!compile_body(compiler, builder, loc_table, callee, proc_encapsulated)) 378 | return 0; 379 | 380 | MATCH_TOK(compiler->last_tok, TOK_WHILE); 381 | compiler_read_tok(compiler); 382 | 383 | if (!compile_expression(compiler, builder, PREC_BEGIN, 1 ,0)) 384 | return 0; 385 | 386 | chunk_write_opcode(builder, MACHINE_COND_RETURN); 387 | return 1; 388 | } 389 | 390 | DECL_STATMENT_COMPILER(compile_proc) { 391 | MATCH_TOK(compiler->last_tok, TOK_PROC); 392 | if (encapsulated) { 393 | compiler->last_err = ERROR_UNEXPECTED_TOKEN; 394 | return 0; 395 | } 396 | 397 | int bin_op_flag = 0; 398 | uint64_t proc_id; 399 | compiler_read_tok(compiler); 400 | if (callee && compiler->last_tok.type == TOK_BINARY_OP) { 401 | proc_id = combine_hash((uint64_t)compiler->last_tok.payload.bin_op, BINARY_OVERLOAD); 402 | bin_op_flag = 1; 403 | } 404 | else if (callee && compiler->last_tok.type == TOK_UNARY_OP) 405 | proc_id = combine_hash((uint64_t)compiler->last_tok.payload.bin_op, UNARY_OVERLOAD); 406 | else { 407 | MATCH_TOK(compiler->last_tok, TOK_IDENTIFIER); 408 | proc_id = compiler->last_tok.payload.identifier; 409 | } 410 | 411 | chunk_write_opcode(&compiler->data_builder, MACHINE_LABEL); 412 | uint64_t reverse_buffer[50]; 413 | uint32_t buffer_size = 0; 414 | if (compiler_read_tok(compiler).type == TOK_OPEN_PAREN) { 415 | while (1) 416 | { 417 | MATCH_TOK(compiler_read_tok(compiler), TOK_IDENTIFIER); 418 | reverse_buffer[buffer_size++] = compiler->last_tok.payload.identifier; 419 | if (compiler_read_tok(compiler).type != TOK_COMMA) { 420 | break; 421 | } 422 | } 423 | MATCH_TOK(compiler->last_tok, TOK_CLOSE_PAREN); 424 | compiler_read_tok(compiler); 425 | } 426 | if (callee) { 427 | reverse_buffer[buffer_size++] = RECORD_THIS; 428 | if (proc_id == 5862393 && buffer_size == 1) { 429 | proc_id = combine_hash((uint64_t)OPERATOR_NEGATE, UNARY_OVERLOAD); 430 | bin_op_flag = 0; 431 | } 432 | if (bin_op_flag) 433 | reverse_buffer[buffer_size++] = RECORD_OVERLOAD_ORDER; 434 | } 435 | 436 | uint64_t label_id = combine_hash(combine_hash(proc_id, buffer_size), callee); 437 | chunk_write_ulong(&compiler->data_builder, label_id); 438 | 439 | if(callee) 440 | chunk_write_opcode(&compiler->data_builder, MACHINE_NEW_FRAME); 441 | 442 | if (callee && buffer_size) { 443 | chunk_write_opcode(&compiler->data_builder, MACHINE_TRACE); 444 | chunk_write_ulong(&compiler->data_builder, buffer_size); 445 | } 446 | while (buffer_size--) { 447 | chunk_write_opcode(&compiler->data_builder, MACHINE_STORE_VAR); 448 | chunk_write_ulong(&compiler->data_builder, reverse_buffer[buffer_size]); 449 | } 450 | 451 | if (!compile_body(compiler, &compiler->data_builder, loc_table, callee, label_id)) 452 | return 0; 453 | if (callee && proc_id == RECORD_INIT_PROC) { 454 | chunk_write_opcode(&compiler->data_builder, MACHINE_LOAD_VAR); 455 | chunk_write_ulong(&compiler->data_builder, RECORD_THIS); 456 | chunk_write_opcode(&compiler->data_builder, MACHINE_TRACE); 457 | chunk_write_ulong(&compiler->data_builder, 1); 458 | } 459 | else { 460 | chunk_write_opcode(&compiler->data_builder, MACHINE_LOAD_CONST); 461 | chunk_write_value(&compiler->data_builder, const_value_null); 462 | } 463 | 464 | if(callee) 465 | chunk_write_opcode(&compiler->data_builder, MACHINE_CLEAN); 466 | chunk_write_opcode(&compiler->data_builder, MACHINE_RETURN_GOTO); 467 | chunk_write_opcode(&compiler->data_builder, MACHINE_END_SKIP); 468 | return 1; 469 | } 470 | 471 | DECL_STATMENT_COMPILER(compile_record) { 472 | MATCH_TOK(compiler->last_tok, TOK_RECORD); 473 | if (encapsulated) { 474 | compiler->last_err = ERROR_UNEXPECTED_TOKEN; 475 | return 0; 476 | } 477 | 478 | MATCH_TOK(compiler_read_tok(compiler), TOK_IDENTIFIER); 479 | uint64_t record_id = compiler->last_tok.payload.identifier; 480 | uint64_t base_record_id = 0; 481 | if (compiler_read_tok(compiler).type == TOK_EXTEND) { 482 | MATCH_TOK(compiler_read_tok(compiler), TOK_IDENTIFIER); 483 | base_record_id = compiler->last_tok.payload.identifier; 484 | compiler_read_tok(compiler); 485 | } 486 | 487 | uint64_t property_buffer[500]; 488 | uint64_t properties = 0; 489 | 490 | if (compiler->last_tok.type == TOK_OPEN_BRACE) { 491 | compiler_read_tok(compiler); 492 | while (compiler->last_tok.type != TOK_CLOSE_BRACE) 493 | { 494 | if (compiler->last_tok.type == TOK_IDENTIFIER) { 495 | property_buffer[properties++] = compiler->last_tok.payload.identifier; 496 | compiler_read_tok(compiler); 497 | } 498 | else if (compiler->last_tok.type == TOK_PROC) { 499 | if (!compile_proc(compiler, &compiler->data_builder, loc_table, record_id, 0, 1)) 500 | return 0; 501 | } 502 | else { 503 | compiler->last_err = ERROR_UNEXPECTED_TOKEN; 504 | return 0; 505 | } 506 | } 507 | compiler_read_tok(compiler); 508 | } 509 | else if (!base_record_id) 510 | MATCH_TOK(compiler->last_tok, TOK_OPEN_BRACE); 511 | 512 | chunk_write_opcode(&compiler->data_builder, MACHINE_BUILD_PROTO); 513 | chunk_write_ulong(&compiler->data_builder, record_id); 514 | chunk_write_ulong(&compiler->data_builder, properties); 515 | while (properties--) 516 | chunk_write_ulong(&compiler->data_builder, property_buffer[properties]); 517 | if (base_record_id) { 518 | chunk_write_opcode(&compiler->code_builder, MACHINE_INHERIT_REC); 519 | chunk_write_ulong(&compiler->code_builder, record_id); 520 | chunk_write_ulong(&compiler->code_builder, base_record_id); 521 | } 522 | return 1; 523 | } 524 | 525 | DECL_STATMENT_COMPILER(compile_return) { 526 | MATCH_TOK(compiler->last_tok, TOK_RETURN); 527 | if (!proc_encapsulated) { 528 | compiler->last_err = ERROR_UNEXPECTED_TOKEN; 529 | return 0; 530 | } 531 | compiler_read_tok(compiler); 532 | if (IS_VALUE_TOK(compiler->last_tok)) { 533 | if (!compile_expression(compiler, &compiler->data_builder, PREC_BEGIN, 0, callee ? 0 : proc_encapsulated)) 534 | return 0; 535 | if (callee) { 536 | chunk_write_opcode(&compiler->data_builder, MACHINE_TRACE); 537 | chunk_write_ulong(&compiler->data_builder, 1); 538 | } 539 | } 540 | else { 541 | chunk_write_opcode(&compiler->data_builder, MACHINE_LOAD_CONST); 542 | chunk_write_value(&compiler->data_builder, const_value_null); 543 | } 544 | if(callee) 545 | chunk_write_opcode(&compiler->data_builder, MACHINE_CLEAN); 546 | chunk_write_opcode(&compiler->data_builder, MACHINE_RETURN_GOTO); 547 | return 1; 548 | } 549 | 550 | DECL_STATMENT_COMPILER(compile_include) { 551 | MATCH_TOK(compiler->last_tok, TOK_INCLUDE); 552 | if (proc_encapsulated) { 553 | compiler->last_err = ERROR_UNEXPECTED_TOKEN; 554 | return 0; 555 | } 556 | 557 | char* file_path = malloc(150); 558 | NULL_CHECK(file_path); 559 | scanner_read_str(&compiler->scanner, file_path, 0); 560 | 561 | size_t path_length = strlen(file_path); 562 | 563 | FILE* infile = fopen(file_path, "rb"); 564 | if (!infile) { 565 | memmove(&file_path[compiler->include_dir_len], file_path, path_length); 566 | memcpy(file_path, compiler->include_dir, compiler->include_dir_len); 567 | path_length += compiler->include_dir_len; 568 | file_path[path_length] = 0; 569 | 570 | infile = fopen(file_path, "rb"); 571 | if (!infile) { 572 | free(file_path); 573 | compiler->last_err = ERROR_CANNOT_OPEN_FILE; 574 | return 0; 575 | } 576 | } 577 | 578 | uint64_t path_hash = hash(file_path, path_length); 579 | for (uint_fast8_t i = 0; i < compiler->imported_files; i++) 580 | if (compiler->imported_file_hashes[i] == path_hash) { 581 | free(file_path); 582 | compiler_read_tok(compiler); 583 | return 1; 584 | } 585 | compiler->imported_file_hashes[compiler->imported_files++] = path_hash; 586 | 587 | fseek(infile, 0, SEEK_END); 588 | uint64_t fsize = ftell(infile); 589 | fseek(infile, 0, SEEK_SET); 590 | char* source = malloc((fsize + 1) * sizeof(char)); 591 | NULL_CHECK(source); 592 | fread(source, sizeof(char), fsize, infile); 593 | fclose(infile); 594 | source[fsize] = 0; 595 | 596 | struct scanner my_scanner = compiler->scanner; 597 | init_scanner(&compiler->scanner, source, file_path); 598 | 599 | loc_table_include(loc_table, file_path); 600 | 601 | compiler_read_tok(compiler); 602 | if (!compile(compiler, loc_table, 0, 0, 0)) { 603 | return 0; 604 | } 605 | free(source); 606 | 607 | loc_table_uninclude(loc_table); 608 | 609 | compiler->scanner = my_scanner; 610 | compiler_read_tok(compiler); 611 | 612 | return 1; 613 | } 614 | 615 | DECL_VALUE_COMPILER((*value_compilers[13])) = { 616 | compile_primative, 617 | compile_string, 618 | compile_reference, 619 | compile_reference, 620 | compile_array, 621 | compile_new_record, 622 | compile_paren, 623 | compile_unary, 624 | compile_unary, 625 | compile_hashtag, 626 | compile_unary, 627 | compile_goto, 628 | compile_goto 629 | }; 630 | 631 | DECL_STATMENT_COMPILER((*statment_compilers[11])) = { 632 | compile_value_statement, 633 | compile_value_statement, 634 | compile_value_statement, 635 | compile_set, 636 | compile_if, 637 | compile_while, 638 | compile_do_while, 639 | compile_proc, 640 | compile_record, 641 | compile_return, 642 | compile_include 643 | }; 644 | 645 | const int compile_value(struct compiler* compiler, struct chunk_builder* builder, const int optimize_copy, uint64_t optimize_goto) { 646 | if (IS_VALUE_TOK(compiler->last_tok)) 647 | return (*value_compilers[compiler->last_tok.type])(compiler, builder, optimize_copy, optimize_goto); 648 | compiler->last_err = ERROR_UNRECOGNIZED_TOKEN; 649 | return 0; 650 | } 651 | 652 | const int compile_statement(struct compiler* compiler, struct chunk_builder* builder, struct loc_table* loc_table, const uint64_t callee, const int encapsulated, uint64_t proc_encapsulated){ 653 | loc_table_insert(loc_table, compiler, builder); 654 | if (IS_STATMENT_TOK(compiler->last_tok)) { 655 | int stat = (*statment_compilers[compiler->last_tok.type - TOK_UNARY_OP])(compiler, builder, loc_table, callee, encapsulated, proc_encapsulated); 656 | loc_table_insert(loc_table, compiler, builder); 657 | return stat; 658 | } 659 | compiler->last_err = ERROR_UNRECOGNIZED_TOKEN; 660 | return 0; 661 | } --------------------------------------------------------------------------------