├── .gitignore ├── CMakeLists.txt ├── README.md ├── docker ├── Dockerfile ├── build_cmake.sh └── docker-compose.yml ├── docker_build.sh ├── src ├── common │ ├── alloc.c │ ├── common.h │ ├── data_structures │ │ ├── arraylist.c │ │ └── map.c │ ├── error.c │ └── types.h ├── compiler │ ├── AddressCalculator.cpp │ ├── AstGenerator.cpp │ ├── Compiler.cpp │ ├── ExprssionCompiler.cpp │ ├── ast │ │ ├── ArgumentsList.cpp │ │ ├── Body.cpp │ │ ├── ClassDeclaration.cpp │ │ ├── Conditional.cpp │ │ ├── Function.cpp │ │ ├── FunctionKind.cpp │ │ ├── Loop.cpp │ │ ├── Statement.cpp │ │ ├── TryCatch.cpp │ │ ├── Var.cpp │ │ ├── ast.h │ │ └── expression │ │ │ ├── BinaryExpression.cpp │ │ │ ├── EmptyExpression.cpp │ │ │ ├── Expression.cpp │ │ │ ├── ExpressionList.cpp │ │ │ ├── MethodCall.cpp │ │ │ ├── PostfixExpression.cpp │ │ │ ├── PrefixExpression.cpp │ │ │ ├── TerminalExpression.cpp │ │ │ └── expression.h │ ├── compiler.h │ └── parser │ │ └── parser.h ├── interpreter │ ├── event_queue.c │ ├── garbage_collector.h │ ├── interpreter_dd.c │ ├── native_functions.c │ ├── object.c │ └── object.h ├── main.cpp ├── program │ ├── Assembler.cpp │ ├── BytecodeOptimizer.cpp │ ├── Program.cpp │ ├── instruction.h │ └── program.h └── tester │ └── tester.cpp ├── test_scripts ├── AsyncFunctionTest.zs ├── AsyncFunctionTest2.zs ├── AsyncModifySharedDataTest.zs ├── BinaryTree.zs ├── ClassInstanceTest.zs ├── CombinatorFunctionTest.zs ├── EventQueueTest.zs ├── Hello.zs ├── ImportTest.zs ├── JsonTest.zs ├── Math.zs ├── OperatorsTest.zs ├── Osman.zs ├── PrivateVariablesTest.zs ├── RecursiveFibonacci.zs ├── ScopesTest.zs ├── StaticVariables.zs ├── ThrowTest.zs ├── VarargFunctionTest.zs ├── operators.zs └── primetest.zs ├── todo_list.txt └── zeroscript.g4 /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | cmake-build-debug/ 3 | generated/ -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(Zeroscript) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | set(CMAKE_C_STANDARD 11) 6 | 7 | set(ANTLR_TOOL_PATH /usr/local/lib/antlr-4.7.1-complete.jar) 8 | 9 | add_custom_target(Antlr 10 | COMMAND java -jar ${ANTLR_TOOL_PATH} zeroscript.g4 -Dlanguage=Cpp -o src/compiler/parser/generated 11 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 12 | SOURCES zeroscript.g4) 13 | 14 | add_executable(zero src/main.cpp) 15 | target_link_libraries(zero antlr4-runtime) 16 | add_executable(tester src/tester/tester.cpp) 17 | 18 | target_link_libraries(zero pthread) 19 | 20 | add_dependencies(zero Antlr) 21 | 22 | set_target_properties(zero PROPERTIES COMPILE_FLAGS " -Ofast") 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## zeroscript 2 | 3 | It is a dynamically typed, object oriented scripting language written in c and c++. 4 | The name does not mean that it includes no scripting part. Zero here states that it is in the 0th stage, has a long way to go. 5 | 6 | 7 | **basic syntax**: 8 | 9 | It resembles javascript a lot and yet has some different sides 10 | 11 | - class and member declaration: 12 | 13 | ``` 14 | // classses are top level entities, 15 | // meaning that everything must belong to a class. 16 | class Hello(/*constructor arguments*/){ 17 | 18 | //member variable 19 | var helloMessage = "hello world!"; 20 | 21 | // function call 22 | sayHello(); 23 | 24 | // set method for helloMessage 25 | function setHelloMessage(message){ 26 | 27 | // notice that this refers to the class itself 28 | this.helloMessage = message; 29 | 30 | } 31 | // writing `this` is not mandatory 32 | function getHelloMessage(){ 33 | return helloMessage; 34 | } 35 | 36 | } 37 | ``` 38 | 39 | - static functions and variables: 40 | 41 | ``` 42 | class Math(){ 43 | 44 | static var someStaticVariable = 100; 45 | 46 | static function floor(n){ 47 | return n - (n % 1); 48 | } 49 | 50 | } 51 | ``` 52 | 53 | - lambda expressions and function references: 54 | 55 | ``` 56 | helloObject.fncRefTest(function(x){ 57 | print(x); 58 | }); 59 | 60 | helloObject.fncRefTest((x)=>{ 61 | print(x); 62 | }); 63 | 64 | helloObject.fncRefTest(x=>print(x)); 65 | 66 | 67 | ``` 68 | 69 | - object and array creation 70 | 71 | ``` 72 | // instance of a class 73 | var helloObject = new Hello(); 74 | 75 | // json 76 | var obj = Object(); 77 | var obj2 = { "a":0, "c": "d" }; 78 | 79 | //array 80 | var arr = []; 81 | 82 | ``` 83 | 84 | - import class files from path 85 | 86 | ``` 87 | import "../test_scripts/Math" as Math 88 | import "../test_scripts/Hello" as Hello 89 | 90 | class ImportTest(){ 91 | 92 | static var a = Math.floor(3.7); 93 | print(Math.round(2.34)); 94 | var hello = new Hello(); 95 | 96 | } 97 | 98 | ``` 99 | 100 | - declare private variables 101 | 102 | ``` 103 | // private variables cannot get accessed from outside this class 104 | class PrivateVriablesTest(){ 105 | 106 | private var privateVariable; 107 | 108 | private function privateFnc(){ 109 | print("i cant be called outside this class unless accessed by an accessor :("); 110 | } 111 | 112 | function getPrivateFnc(){ 113 | return this.privateFnc; 114 | } 115 | 116 | } 117 | ``` 118 | 119 | - asynchronous functions 120 | 121 | ``` 122 | // async functions return immediatelly and run on a separate thread 123 | async function asyncFnc(){ 124 | new Primetest(); 125 | } 126 | 127 | // you can pass arguments 128 | asyncCount(()=>{ 129 | print("finished async 3"); 130 | }); 131 | 132 | // can capture arguments in an async fnc 133 | async function asyncCount(arg){ 134 | for(var i = 0;i<1000;i++){ 135 | print(i); 136 | } 137 | arg(); 138 | } 139 | ``` 140 | 141 | 142 | **ZeroscriptOs** 143 | 144 | The reason behind writing this language is to use it in an another hobby operating system development project (ZeroscriptOS). 145 | Zeroscript will work as a way to write user space applications for the os project without actually implementing a user space. The whole kernel will act as a Zeroscript interpreter. 146 | Native bindings will displace system calls. Every process being implemented in a scripting language will provide isolation. 147 | 148 | Although this model is questionable and performance of such an os would not be comparable to a "normal" one but as it is said before, it is a hobby project and every single line written is for pleasure. 149 | After all no one cares if implementing things this way is the best or not for an os that no one will use :) 150 | 151 | Zeroscript is initial step of writing ZeroscriptOS. However when it is done, Zeroscript will be ready to use not only under the operating system project that I have mentioned above, but also under the Desktop Linux environments and may be in Windows if pthread-related parts are converted to their Windows equivalents. 152 | 153 | It is now too early to use it in real world, and there are tons of things to do. I have an untidy road map and implementing these steps one by one. Apart from having basics, the road map is as follows: 154 | 155 | **roadmap:** 156 | 157 | - [X] json 158 | 159 | - [X] foreach loop 160 | 161 | - [X] lambda expressions 162 | 163 | - [X] object creation from class 164 | 165 | - [X] static objects and functions 166 | 167 | - [X] import classes from different folders 168 | 169 | - [X] throw errors 170 | 171 | - [X] try/catch 172 | 173 | - [X] access modifiers 174 | 175 | - [ ] class inheritance 176 | 177 | - [X] garbage collector 178 | 179 | - [X] asynchronous functions 180 | 181 | - [ ] packaging of compiled bytecode into .zar files 182 | 183 | - [ ] standard library implementation 184 | 185 | - [ ] semantic analyzer 186 | 187 | - [ ] optimizer 188 | 189 | - [ ] intelliJ plugin 190 | 191 | - [ ] jit compiler 192 | 193 | 194 | **build from source**: 195 | 196 | It is a C/C++ mixed project and the whole project is dependent to the ANTLR c++ runtime. I am pretty sure that there is a wiser way to use antlr with cmake but for now it requires header files and binaries to be in the default include path due to my poor build tool knowledge. 197 | 198 | It is only tested on gcc 6 and 7. Also requires gcc's labels as values extension and uses posix threads so I am not sure about how it will behave under Windows. 199 | Note that portability is not a concern for this project. At least for now. 200 | 201 | **testing**: 202 | 203 | Tests are intended to be more like "real world" problems rather than unit testing of each feature. There is no easy way to test features in an isolated way for me. 204 | For example, I can not test correctness of math operations without relying on correctness of assert function. 205 | For this reason, I write Zeroscript equivalents of well known algorithms, data structures and design patterns such as binary trees, recursive fibonacci etc and expect them to give correct results. 206 | 207 | To run tests, build tester and run it in the project's root folder. 208 | 209 | **run scripts**: 210 | 211 | ``` 212 | ./zero 213 | ``` 214 | 215 | **compile scripts and generate .zar files**: 216 | 217 | not yet. 218 | 219 | **run zar files**: 220 | 221 | not yet. 222 | 223 | **contributions**: 224 | 225 | any help will be appreciated. just make a pull request. 226 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gcc:6.5.0 2 | RUN mkdir /antlr && \ 3 | cd /antlr && \ 4 | wget https://github.com/antlr/antlr4/archive/master.zip 5 | 6 | RUN curl -sSL https://cmake.org/files/v3.5/cmake-3.5.2-Linux-x86_64.tar.gz | tar -xzC /opt 7 | 8 | ENV PATH="/opt/cmake-3.5.2-Linux-x86_64/bin/:${PATH}" 9 | 10 | RUN apt-get update && apt-get install uuid-dev 11 | 12 | RUN cd /antlr && \ 13 | unzip master.zip &&\ 14 | cd antlr4-master/runtime/Cpp &&\ 15 | mkdir build && mkdir run && cd build &&\ 16 | cmake .. && make && ls 17 | 18 | RUN cp /antlr/antlr4-master/runtime/Cpp/dist/libantlr4-runtime.a /usr/lib 19 | # TODO: why not using include directory instead? 20 | RUN cp -R /antlr/antlr4-master/runtime/Cpp/runtime/src/* /usr/local/include/ 21 | 22 | # install java 23 | RUN apt-get install -y default-jre 24 | 25 | # download antlr jar 26 | RUN curl https://www.antlr.org/download/antlr-4.7.1-complete.jar -o /usr/local/lib/antlr-4.7.1-complete.jar 27 | 28 | # install valgrind 29 | RUN apt-get install -y valgrind 30 | -------------------------------------------------------------------------------- /docker/build_cmake.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # build 3 | mkdir -p /build \ 4 | && cd /build \ 5 | && cmake /Zscript \ 6 | && make \ 7 | && echo "testing..." \ 8 | && echo "file list:" \ 9 | && ls -ll ../test_scripts/ \ 10 | && echo "running tester" \ 11 | && ./tester 12 | cp /build/zero /Zscript/cmake-build-debug 13 | -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | build: 5 | image: antrl_cpp_env 6 | container_name: zscript_build_container 7 | build: . 8 | volumes: 9 | - ..:/Zscript 10 | - ../test_scripts:/test_scripts 11 | command: 12 | - /Zscript/docker/build_cmake.sh -------------------------------------------------------------------------------- /docker_build.sh: -------------------------------------------------------------------------------- 1 | cd docker && docker-compose build && docker-compose up -------------------------------------------------------------------------------- /src/common/alloc.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 11.05.2018. 3 | // 4 | int_t heap_limit = 1 * (1024 * 1024); 5 | int_t used_heap = 0; 6 | int_t total_thread_count = 1; 7 | int_t gc_barrier_unstable = 0; 8 | 9 | pthread_mutex_t used_heap_lock = PTHREAD_MUTEX_INITIALIZER; 10 | pthread_mutex_t gc_list_lock = PTHREAD_MUTEX_INITIALIZER; 11 | pthread_mutex_t gc_barrier_unstable_lock = PTHREAD_MUTEX_INITIALIZER; 12 | pthread_mutex_t thread_list_lock = PTHREAD_MUTEX_INITIALIZER; 13 | 14 | pthread_barrier_t* gc_safe_barrier; 15 | pthread_barrier_t gc_safe_barrier2; 16 | pthread_barrier_t gc_safe_barrier1; 17 | 18 | #define USED_HEAP_LOCK pthread_mutex_lock(&used_heap_lock); 19 | #define USED_HEAP_UNLOCK pthread_mutex_unlock(&used_heap_lock); 20 | 21 | #define GC_LIST_LOCK pthread_mutex_lock(&gc_list_lock); 22 | #define GC_LIST_UNLOCK pthread_mutex_unlock(&gc_list_lock); 23 | 24 | #define THREAD_LIST_LOCK pthread_mutex_lock(&thread_list_lock); 25 | #define THREAD_LIST_UNLOCK pthread_mutex_unlock(&thread_list_lock); 26 | 27 | void schedule_gc(); 28 | 29 | Z_INLINE any_ptr_t z_decorate_ptr(any_ptr_t ptr, uint_t size) { 30 | *((uint_t *) ptr) = size; 31 | return ptr + sizeof(int_t); 32 | } 33 | 34 | Z_INLINE any_ptr_t z_undecorate_ptr(any_ptr_t ptr) { 35 | ptr -= sizeof(uint_t); 36 | USED_HEAP_LOCK 37 | used_heap -= *((uint_t *) ptr); 38 | USED_HEAP_UNLOCK 39 | return ptr; 40 | } 41 | 42 | void z_free(any_ptr_t ptr); 43 | 44 | any_ptr_t z_alloc_or_die(size_t size); 45 | 46 | int_t gc(); 47 | 48 | Z_INLINE void z_free(any_ptr_t ptr) { 49 | Z_FREE(z_undecorate_ptr(ptr)); 50 | } 51 | 52 | any_ptr_t z_alloc_or_die(size_t size) { 53 | size += sizeof(uint_t); 54 | USED_HEAP_LOCK 55 | used_heap += size; 56 | USED_HEAP_UNLOCK 57 | any_ptr_t ptr = Z_MALLOC(size); 58 | if (ptr == NULL) { 59 | err_out_of_memory(); 60 | } 61 | return z_decorate_ptr(ptr, size); 62 | } 63 | 64 | any_ptr_t z_alloc_or_gc(size_t size) { 65 | size += sizeof(uint_t); 66 | USED_HEAP_LOCK 67 | used_heap += size; 68 | USED_HEAP_UNLOCK 69 | if (used_heap > heap_limit) { 70 | schedule_gc(); 71 | } 72 | any_ptr_t ptr = Z_MALLOC(size); 73 | if (ptr == NULL) { 74 | err_out_of_memory(); 75 | } 76 | ptr = z_decorate_ptr(ptr, size); 77 | return ptr; 78 | } 79 | 80 | void init_gc_barrier(pthread_barrier_t* barrier,int_t count) { 81 | pthread_barrier_destroy(barrier); 82 | pthread_barrier_init(barrier, NULL, (unsigned int) count); 83 | } 84 | 85 | int gc_count = 0; 86 | void schedule_gc() { 87 | // pick the barrier to wait 88 | gc_safe_barrier = gc_count % 2 == 0 ? &gc_safe_barrier1 : &gc_safe_barrier2; 89 | z_log("awaiting the first gc barrier. total thread count: %d\n", total_thread_count); 90 | int ret = pthread_barrier_wait(gc_safe_barrier); 91 | if (ret == PTHREAD_BARRIER_SERIAL_THREAD) { 92 | gc_count++; 93 | // gc.. 94 | gc(); 95 | // initialize the other barrier 96 | pthread_barrier_t* barrier_to_reinitialize = gc_count % 2 == 0 ? &gc_safe_barrier1 : &gc_safe_barrier2; 97 | init_gc_barrier(barrier_to_reinitialize,total_thread_count); 98 | } 99 | pthread_barrier_wait(gc_safe_barrier); 100 | } 101 | 102 | -------------------------------------------------------------------------------- /src/common/common.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 11.05.2018. 3 | // 4 | 5 | #ifndef ZEROSCRIPT_COMMON_H 6 | #define ZEROSCRIPT_COMMON_H 7 | 8 | #include 9 | #define Z_INLINE inline 10 | #define Z_MALLOC malloc 11 | #define Z_REALLOC realloc 12 | #define Z_FREE free 13 | #define FLOAT_SUPPORT 14 | #define TRUE 1 15 | #define FALSE 0 16 | //#define PRINT_LOGS 17 | 18 | long long current_milliseconds(void) { 19 | struct timeval tv; 20 | gettimeofday(&tv,NULL); 21 | return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000); 22 | } 23 | 24 | void* z_log(const char *format, ...); 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "types.h" 32 | #include "error.c" 33 | #include "alloc.c" 34 | 35 | #include "data_structures/arraylist.c" 36 | #include "data_structures/map.c" 37 | 38 | arraylist_t* thread_list; 39 | 40 | #ifdef PRINT_LOGS 41 | 42 | void* z_log(const char *format, ...) 43 | { 44 | char buff[500]; 45 | snprintf(buff,499,"[%ld][THREAD:%p]%s", current_milliseconds(),(void *)(pthread_self()),format); 46 | va_list args; 47 | va_start(args, format); 48 | vfprintf(stderr,buff, args); 49 | va_end(args); 50 | } 51 | 52 | #else 53 | void* z_log(const char *format, ...){ 54 | 55 | } 56 | #endif 57 | 58 | #endif //ZEROSCRIPT_COMMON_H 59 | -------------------------------------------------------------------------------- /src/common/data_structures/arraylist.c: -------------------------------------------------------------------------------- 1 | #include "../types.h" 2 | 3 | #define ARRAY_LIST_INITIAL_CAPACITY 3 4 | 5 | typedef struct arraylist_t { 6 | int_t size; 7 | int_t capacity; 8 | char *data; 9 | char *top; 10 | size_t size_of_item; 11 | } arraylist_t; 12 | 13 | Z_INLINE static void extend_capacity(arraylist_t *self); 14 | 15 | arraylist_t *arraylist_new_capacity(size_t sizeof_item, int_t initial_capacity) { 16 | arraylist_t *self = (arraylist_t *) z_alloc_or_die(sizeof(arraylist_t)); 17 | self->capacity = initial_capacity; 18 | self->size_of_item = sizeof_item; 19 | self->data = (char *) z_alloc_or_die(sizeof_item * initial_capacity); 20 | self->size = 0; 21 | self->top = &self->data[0]; 22 | return self; 23 | } 24 | 25 | /** 26 | * initializes a new arraylist. 27 | * @param sizeof_item size of an item in the list. 28 | * @return arraylist ptr. 29 | */ 30 | arraylist_t *arraylist_new(size_t sizeof_item) { 31 | return arraylist_new_capacity(sizeof_item, ARRAY_LIST_INITIAL_CAPACITY); 32 | } 33 | 34 | 35 | 36 | /** 37 | * add an item to the list. 38 | * @param self this ptr. 39 | * @param item ptr of item to be added. 40 | * @return size of the list. 41 | */ 42 | Z_INLINE int_t arraylist_push(arraylist_t *self, any_ptr_t item) { 43 | if (self->size >= self->capacity) { 44 | extend_capacity(self); 45 | } 46 | memcpy(self->top, (char *) item, self->size_of_item); 47 | self->top += self->size_of_item; 48 | self->size++; 49 | return self->size - 1; 50 | } 51 | 52 | /** 53 | * set the specific index of the array. 54 | * @param self this ptr. 55 | * @param item ptr of item to be added. 56 | * @param index the index. 57 | */ 58 | void arraylist_set(arraylist_t *self, any_ptr_t item, int_t index) { 59 | if (self->size >= self->capacity) { 60 | extend_capacity(self); 61 | } 62 | memcpy(self->data + (self->size_of_item * index), (char *) item, self->size_of_item); 63 | } 64 | 65 | Z_INLINE any_ptr_t arraylist_top(arraylist_t *self) { 66 | return self->top - self->size_of_item; 67 | } 68 | 69 | Z_INLINE any_ptr_t arraylist_pop(arraylist_t *self) { 70 | self->size--; 71 | self->top -= self->size_of_item; 72 | return self->top; 73 | } 74 | 75 | Z_INLINE any_ptr_t arraylist_get(arraylist_t *self, int_t index) { 76 | return (any_ptr_t) (self->data + index * self->size_of_item); 77 | } 78 | 79 | Z_INLINE static void extend_capacity(arraylist_t *self) { 80 | char* new_data = (char*) z_alloc_or_die(self->capacity * self->size_of_item * 2); 81 | memcpy(new_data, self->data, self->size * self->size_of_item); 82 | z_free(self->data); 83 | self->data = new_data; 84 | self->capacity = self->capacity * 2; 85 | self->top = &self->data[self->size * self->size_of_item]; 86 | } 87 | 88 | void arraylist_remove_item(arraylist_t *self, any_ptr_t ptr) { 89 | for (int_t i = 0; i < self->size; i++) { 90 | any_ptr_t next = arraylist_get(self, i); 91 | int_t found = TRUE; 92 | for (int_t j = 0; j < self->size_of_item; j++) { 93 | if (((char *) ptr)[j] != ((char *) next)[j]) { 94 | found = FALSE; 95 | break; 96 | } 97 | } 98 | if (found) { 99 | self->size--; 100 | memcpy(next, arraylist_get(self, i + 1), (size_t) (self->size - i)); 101 | break; 102 | } 103 | } 104 | } 105 | 106 | void arraylist_remove_index(arraylist_t *self, int_t i) { 107 | any_ptr_t next = arraylist_get(self, i); 108 | self->size--; 109 | memcpy(next, arraylist_get(self, i + 1), (size_t) (self->size - i)); 110 | } 111 | 112 | void arraylist_free(arraylist_t *self) { 113 | z_free(self->data); 114 | z_free(self); 115 | } 116 | -------------------------------------------------------------------------------- /src/common/data_structures/map.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 05.06.2018. 3 | // 4 | // TODO : dynamically increase and decrease size 5 | #define MAP_BAG_SIZE 10 6 | //2^0 7 | #define MAP_FLAG_ENUMERABLE 1 8 | //2^1 9 | #define MAP_FLAG_PRIVATE 2 10 | 11 | typedef struct map_t { 12 | uint_t size; 13 | uint_t size_of_an_item; 14 | uint_t is_immutable; 15 | arraylist_t *keys[MAP_BAG_SIZE]; 16 | arraylist_t *values[MAP_BAG_SIZE]; 17 | arraylist_t *flags[MAP_BAG_SIZE]; 18 | } map_t; 19 | 20 | void map_insert_flags(map_t *self, char *key, any_ptr_t value, int_t is_enumerable); 21 | 22 | any_ptr_t map_get_flags(map_t *self, char *key, int_t* flags) ; 23 | 24 | Z_INLINE static uint_t get_hash(char *value) { 25 | uint_t hash = 0; 26 | while (*value) 27 | hash = (hash * 10) + *value++ - '0'; 28 | return hash % MAP_BAG_SIZE; 29 | } 30 | 31 | map_t *map_new(uint_t siz_of_an_item) { 32 | map_t *self = (map_t *) z_alloc_or_die(sizeof(map_t)); 33 | self->size = 0; 34 | self->size_of_an_item = siz_of_an_item; 35 | self->is_immutable = 0; 36 | for (int i = 0; i < MAP_BAG_SIZE; i++) { 37 | self->keys[i] = NULL; 38 | self->values[i] = NULL; 39 | self->flags[i] = NULL; 40 | } 41 | return self; 42 | } 43 | 44 | void map_insert(map_t *self, char *key, any_ptr_t value) { 45 | map_insert_flags(self, key, value, 0|MAP_FLAG_ENUMERABLE); 46 | } 47 | 48 | void map_insert_non_enumerable(map_t *self, char *key, any_ptr_t value) { 49 | map_insert_flags(self, key, value, 0); 50 | } 51 | 52 | void map_insert_flags(map_t *self, char *key, any_ptr_t value, int_t flag) { 53 | if (self->is_immutable) return; 54 | uint_t hash = get_hash(key); 55 | arraylist_t *vbag = self->values[hash]; 56 | arraylist_t *kbag = self->keys[hash]; 57 | arraylist_t *flags = self->flags[hash]; 58 | if (!vbag) { 59 | vbag = arraylist_new(self->size_of_an_item); 60 | kbag = arraylist_new(sizeof(char *)); 61 | flags = arraylist_new(sizeof(int_t)); 62 | self->values[hash] = vbag; 63 | self->keys[hash] = kbag; 64 | self->flags[hash] = flags; 65 | } 66 | for (uint_t i = 0; i < kbag->size; i++) { 67 | if (strcmp(*(char **) arraylist_get(kbag, i), key) == 0) { 68 | arraylist_set(vbag, value, i); 69 | arraylist_set(flags, &flag, i); 70 | return; 71 | } 72 | } 73 | self->size += flag & MAP_FLAG_ENUMERABLE; 74 | arraylist_push(kbag, &key); 75 | arraylist_push(vbag, value); 76 | arraylist_push(flags, &flag); 77 | } 78 | 79 | Z_INLINE any_ptr_t map_get(map_t *self, char *key) { 80 | return map_get_flags(self,key,NULL); 81 | } 82 | 83 | Z_INLINE any_ptr_t map_get_flags(map_t *self, char *key, int_t* flags) { 84 | uint_t hash = get_hash(key); 85 | arraylist_t *kbag = self->keys[hash]; 86 | arraylist_t *vbag = self->values[hash]; 87 | if (kbag) { 88 | for (uint_t i = 0; i < kbag->size; i++) { 89 | if (strcmp(*(char **) arraylist_get(kbag, i), key) == 0) { 90 | if(flags){ 91 | *flags = *((int *) arraylist_get(self->flags[hash], i)); 92 | } 93 | return arraylist_get(vbag, i); 94 | } 95 | } 96 | } 97 | return NULL; 98 | } 99 | 100 | arraylist_t *map_key_list(map_t *self) { 101 | arraylist_t *keys = arraylist_new(sizeof(char *)); 102 | for (int i = 0; i < MAP_BAG_SIZE; i++) { 103 | arraylist_t *kbag = self->keys[i]; 104 | arraylist_t *fbag = self->flags[i]; 105 | if (kbag) 106 | for (int_t j = 0; j < kbag->size; j++) { 107 | int_t flag = *((int *) arraylist_get(fbag, j)); 108 | if (flag & MAP_FLAG_ENUMERABLE) 109 | arraylist_push(keys, &(*(char **) arraylist_get(kbag, j))); 110 | } 111 | } 112 | return keys; 113 | } 114 | 115 | void map_free(map_t* self){ 116 | for (int_t i = 0; i < MAP_BAG_SIZE; i++) { 117 | arraylist_t *kbag = self->keys[i]; 118 | arraylist_t *fbag = self->flags[i]; 119 | arraylist_t *vbag = self->values[i]; 120 | if (kbag){ 121 | arraylist_free(kbag); 122 | arraylist_free(vbag); 123 | arraylist_free(fbag); 124 | } 125 | } 126 | z_free(self); 127 | } -------------------------------------------------------------------------------- /src/common/error.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 11.05.2018. 3 | // 4 | 5 | 6 | int error_and_exit_fmt(const char *format, ...) 7 | { 8 | va_list args; 9 | va_start(args, format); 10 | vfprintf(stderr ,format, args); 11 | va_end(args); 12 | exit(1); 13 | } 14 | 15 | void error_and_exit(char *message) ; 16 | 17 | void err_out_of_memory() { 18 | error_and_exit("cannot allocate memory"); 19 | } 20 | 21 | void error_and_exit(char *message) { 22 | //TODO:print stack trace here 23 | fprintf(stderr, "%s\n", message); 24 | exit(1); 25 | } 26 | -------------------------------------------------------------------------------- /src/common/types.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 11.05.2018. 3 | // 4 | 5 | #ifndef ZEROSCRIPT_TYPES_H 6 | #define ZEROSCRIPT_TYPES_H 7 | 8 | //integral types size 9 | //#define MODE_32_BITS 10 | 11 | #include 12 | 13 | typedef void* any_ptr_t; 14 | 15 | #ifdef MODE_32_BITS 16 | typedef uint32_t uint_t; 17 | typedef int32_t int_t; 18 | typedef uint16_t uhalf_int_t; 19 | typedef int16_t half_int_t; 20 | typedef uint8_t uquarter_int_t; 21 | typedef int8_t quarter_int_t; 22 | typedef float FLOAT; 23 | #else 24 | typedef uint64_t uint_t; 25 | typedef int64_t int_t; 26 | typedef uint32_t uhalf_int_t; 27 | typedef int32_t half_int_t; 28 | typedef uint16_t uquarter_int_t; 29 | typedef int16_t quarter_int_t; 30 | #ifdef FLOAT_SUPPORT 31 | typedef double FLOAT; 32 | #else 33 | typedef int_t FLOAT; 34 | #endif 35 | #endif 36 | 37 | #endif //ZEROSCRIPT_TYPES_H 38 | -------------------------------------------------------------------------------- /src/compiler/AddressCalculator.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 26.05.2018. 3 | // 4 | 5 | class AddressCalculator { 6 | 7 | private: 8 | stack *functionsStack = new stack(); 9 | 10 | map *getCurrentSymbolTable() { 11 | return functionsStack->top()->symbolTable; 12 | }; 13 | 14 | map *getCurrentPrivatesTable() { 15 | return functionsStack->top()->privatesTable; 16 | }; 17 | 18 | void addToCurrentPrivatesMap(char* identifier){ 19 | getCurrentPrivatesTable()->insert(pair(identifier, TRUE)); 20 | } 21 | 22 | void addToCurrentTable(char *identifier) { 23 | uint_t count = getCurrentSymbolTable()->size() + 1; 24 | getCurrentSymbolTable()->insert(pair(identifier, count)); 25 | } 26 | 27 | public: 28 | AddressCalculator(AST *ast) { 29 | calculateClass(dynamic_cast(ast)); 30 | } 31 | 32 | void calculateFunction(Function *func) { 33 | addToCurrentTable(func->identifier); 34 | if(func->isPrivate) 35 | addToCurrentPrivatesMap(func->identifier); 36 | functionsStack->push(func); 37 | addToCurrentTable(const_cast("this")); 38 | addToCurrentTable(func->identifier); 39 | for (int i = 0; i < func->arguments->identifiers->size(); i++) { 40 | addToCurrentTable(func->arguments->identifiers->at(i)->data); 41 | } 42 | calculateBody(func->body); 43 | functionsStack->pop(); 44 | } 45 | 46 | void calculateLoop(Loop *pLoop) { 47 | calculateBody(pLoop->body); 48 | } 49 | 50 | void calculateConditional(Conditional *pCond) { 51 | calculateBody(pCond->body); 52 | if (pCond->elseBody) 53 | calculateBody(pCond->elseBody); 54 | } 55 | 56 | void calculateStatement(Statement *statement) { 57 | AST *stmt = statement->stmt; 58 | if (stmt->kind == AST::AST_KIND_FUNCTION) { 59 | calculateFunction(dynamic_cast(stmt)); 60 | } 61 | if (stmt->kind == AST::AST_KIND_LOOP) { 62 | calculateLoop(dynamic_cast(stmt)); 63 | } 64 | if (stmt->kind == AST::AST_KIND_BODY) { 65 | calculateBody(dynamic_cast(stmt)); 66 | } 67 | if (stmt->kind == AST::AST_KIND_CONDITIONAL) { 68 | calculateConditional(dynamic_cast(stmt)); 69 | } 70 | if (stmt->kind == AST::AST_KIND_VAR) { 71 | calculateVar(dynamic_cast(stmt)); 72 | } if(stmt->kind == AST::AST_KIND_TRY_CATCH){ 73 | char* ident = dynamic_cast(stmt)->catchIdent->data; 74 | addToCurrentTable(ident); 75 | } 76 | } 77 | 78 | void calculateBody(Body *pBody) { 79 | vector *statements = pBody->getStatements(); 80 | for (int i = 0; i < statements->size(); i++) { 81 | Statement *stmt = statements->at(i); 82 | calculateStatement(stmt); 83 | } 84 | } 85 | 86 | void calculateClass(ClassDeclaration *cls) { 87 | functionsStack->push(cls); 88 | addToCurrentTable(const_cast("this")); 89 | for (int i = 0; i < cls->arguments->identifiers->size(); i++) { 90 | addToCurrentTable(cls->arguments->identifiers->at(i)->data); 91 | } 92 | calculateBody(cls->body); 93 | } 94 | 95 | void calculateVar(Var *var) { 96 | if (!var->isStatic){ 97 | if(!var->isSynchronized){ 98 | addToCurrentTable(var->identifier); 99 | } 100 | if(var->isPrivate){ 101 | addToCurrentPrivatesMap(var->identifier); 102 | } 103 | } 104 | } 105 | 106 | }; 107 | 108 | -------------------------------------------------------------------------------- /src/compiler/AstGenerator.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 12.07.2018. 3 | // 4 | 5 | #include "compiler.h" 6 | 7 | class AstGenerator { 8 | 9 | public: 10 | ClassDeclaration *cls = NULL; 11 | Function *staticConstructor = new Function(); 12 | 13 | 14 | AstGenerator(zeroscriptParser::ClassDeclarationContext *classDeclaration) { 15 | staticConstructor->setIdentifier("__static__constructor__"); 16 | staticConstructor->body = new Body(); 17 | staticConstructor->arguments = new ArgumentList(); 18 | visitClassDeclaration(classDeclaration); 19 | Statement *stmt = new Statement(); 20 | this->cls->body->statements->push_back(stmt); 21 | stmt->stmt = staticConstructor; 22 | } 23 | 24 | void addStaticVar(Var *var) { 25 | Statement *stmt = new Statement(); 26 | //static[ident] = value 27 | char *ident = var->identifier; 28 | BinaryExpression *bop = new BinaryExpression(); 29 | bop->setOp("."); 30 | bop->left = TerminalExpression::identifier("__static__"); 31 | bop->right = TerminalExpression::stringWithoutTrim(ident); 32 | 33 | BinaryExpression *assign = new BinaryExpression(); 34 | assign->setOp("="); 35 | assign->left = bop; 36 | assign->right = var->value; 37 | 38 | stmt->stmt = assign; 39 | staticConstructor->body->statements->push_back(stmt); 40 | } 41 | 42 | Var *visitVarDeclaration(zeroscriptParser::VariableDeclarationPartContext *part, Body *pKind, bool isStatic, 43 | bool isPrivate, bool isSynchronized) { 44 | Var *var = new Var(); 45 | var->setIdentifier(part->variableName->IDENT()->getText().data()); 46 | var->isStatic = isStatic; 47 | var->isPrivate = isPrivate; 48 | var->isSynchronized = isSynchronized; 49 | if (part->expression()) { 50 | var->value = visitExpression(part->expression(), pKind); 51 | } else var->value = new EmptyExpression(); 52 | return var; 53 | } 54 | 55 | vector *visitVar(zeroscriptParser::VarContext *varcontext, Body *pKind) { 56 | vector *vars = new vector(); 57 | vector 58 | declarations = varcontext->variableDeclarationPart(); 59 | if (varcontext->STATIC() != NULL) { 60 | for (int i = 0; i < declarations.size(); i++) { 61 | addStaticVar(visitVarDeclaration(declarations.at(i), pKind, varcontext->STATIC() != NULL, 62 | varcontext->PRIVATE() != NULL, false)); 63 | } 64 | } else { 65 | for (int i = 0; i < declarations.size(); i++) { 66 | vars->push_back(visitVarDeclaration(declarations.at(i), pKind, varcontext->STATIC() != NULL, 67 | varcontext->PRIVATE() != NULL, varcontext->TSHARED() != NULL)); 68 | } 69 | } 70 | return vars; 71 | } 72 | 73 | TerminalExpression *visitIdent(zeroscriptParser::IdentifierContext *context, Body *pKind) { 74 | return TerminalExpression::identifier(context->IDENT()->getText().data()); 75 | } 76 | 77 | ArgumentList *visitArguments(zeroscriptParser::ArgumentsListContext *pContext, Body *pKind) { 78 | ArgumentList *args = new ArgumentList(); 79 | vector terminals = pContext->identifier(); 80 | for (int i = 0; i < terminals.size(); i++) { 81 | args->addIdent(visitIdent(terminals.at(i), pKind)); 82 | } 83 | return args; 84 | } 85 | 86 | Function *visitFunction(zeroscriptParser::FunctionContext *pContext, Body *pKind) { 87 | Function *func = new Function(); 88 | func->setIdentifier(pContext->functionName->IDENT()->getText().data()); 89 | func->body = visitBody(pContext->body(), pKind); 90 | func->arguments = visitArguments(pContext->argumentsList(), pKind); 91 | func->isStatic = pContext->STATIC() != NULL; 92 | func->isAsync = pContext->ASYNC() != NULL; 93 | func->isPrivate = pContext->PRIVATE() != NULL; 94 | return func; 95 | } 96 | 97 | Expression *visitAtom(zeroscriptParser::AtomContext *pContext, Body *parentContext) { 98 | if (pContext->identifier()) 99 | return TerminalExpression::identifier(pContext->identifier()->IDENT()->getText().data()); 100 | if (pContext->string()) return TerminalExpression::string(pContext->string()->STRING()->getText().data()); 101 | if (pContext->number()) { 102 | if (pContext->number()->DECIMAL()) { 103 | return TerminalExpression::number(pContext->number()->DECIMAL()->getText().data()); 104 | } else if (pContext->number()->INT()) { 105 | return TerminalExpression::number(pContext->number()->INT()->getText().data()); 106 | } else if (pContext->number()->FALSE_() || pContext->number()->NULL_()) { 107 | return TerminalExpression::number("0"); 108 | } else if (pContext->number()->TRUE_()) { 109 | return TerminalExpression::number("1"); 110 | } 111 | } 112 | if (pContext->json()) { 113 | //TODO 114 | zeroscriptParser::JsonContext *jsonObj = pContext->json(); 115 | return visitJson(jsonObj, parentContext); 116 | } 117 | 118 | return nullptr; 119 | } 120 | 121 | ExpressionList *visitExpressionList(zeroscriptParser::ExpressionListContext *pContext, Body *pKind) { 122 | ExpressionList *expressions = new ExpressionList(); 123 | if (pContext != NULL) 124 | for (auto &expr : pContext->expression()) { 125 | expressions->addExpression(visitExpression(expr, pKind)); 126 | } 127 | return expressions; 128 | } 129 | 130 | int counter = 0; 131 | 132 | Body *visitBodyOrExpression(zeroscriptParser::ExpressionContext *context, Body *pBody) { 133 | Body *body = new Body(); 134 | Statement *stmt = new Statement(); 135 | stmt->stmt = visitExpression(context, body); 136 | stmt->hasReturn = true; 137 | body->statements->push_back(stmt); 138 | return body; 139 | } 140 | 141 | Expression *visitAnonymousFunction(zeroscriptParser::AnonymousFunctionContext *pContext, Body *pKind) { 142 | std::string name = "$anonymous_" + std::to_string(++counter); 143 | Function *func = new Function(); 144 | func->setIdentifier(name.data()); 145 | if (pContext->body()) { 146 | func->body = visitBody(pContext->body(), pKind); 147 | } else if (pContext->bodyOrExpression()->body()) { 148 | func->body = visitBody(pContext->bodyOrExpression()->body(), pKind); 149 | } else if (pContext->bodyOrExpression()->expression()) { 150 | func->body = visitBodyOrExpression(pContext->bodyOrExpression()->expression(), pKind); 151 | } 152 | if (pContext->argumentsList()) { 153 | func->arguments = visitArguments(pContext->argumentsList(), pKind); 154 | } else { 155 | func->arguments = new ArgumentList(); 156 | func->arguments->addIdent( 157 | TerminalExpression::identifier(pContext->identifier()->IDENT()->getText().data())); 158 | } 159 | Statement *stmt = new Statement(); 160 | stmt->stmt = func; 161 | pKind->statements->push_back(stmt); 162 | return TerminalExpression::identifier(name.data()); 163 | } 164 | 165 | // a && b -> 166 | // var __tmp__0 = 0; 167 | // if (a) { 168 | // if(b){ 169 | // __tmp__0 = 1; 170 | // } 171 | // } 172 | Expression *andToIf(zeroscriptParser::ExpressionContext *pContext, Body *pBody) { 173 | char *templateStr = const_cast("(()=>{ if(%s){ if(%s){ return 1; }}return 0;})()"); 174 | char *a = (strdup(pContext->expression(0)->getText().data())); 175 | char *b = (strdup(pContext->expression(1)->getText().data())); 176 | char *buff = static_cast(malloc( 177 | strlen(templateStr) + strlen(a) * 2 + strlen(b) * 2 + 1)); 178 | sprintf(buff, templateStr, a, b); 179 | ANTLRInputStream in(buff, strlen(buff)); 180 | zeroscriptLexer lexer(&in); 181 | CommonTokenStream tokens(&lexer); 182 | zeroscriptParser parser(&tokens); 183 | free(buff); 184 | free(a); 185 | free(b); 186 | return visitExpression(parser.expression(), pBody); 187 | } 188 | 189 | // a || b -> 190 | // if (a) { 191 | // return a; 192 | // } else if(b){ 193 | // return b; 194 | // } return 0; 195 | Expression *orToIf(zeroscriptParser::ExpressionContext *pContext, Body *pBody) { 196 | char *templateStr = const_cast("(()=>{ if(%s){ return %s; } else if(%s){ return %s; } return 0;})()"); 197 | char *a = (strdup(pContext->expression(0)->getText().data())); 198 | char *b = (strdup(pContext->expression(1)->getText().data())); 199 | char *buff = static_cast(malloc( 200 | strlen(templateStr) + strlen(a) * 2 + strlen(b) * 2 + 1)); 201 | sprintf(buff, templateStr, a, a, b, b); 202 | ANTLRInputStream in(buff, strlen(buff)); 203 | zeroscriptLexer lexer(&in); 204 | CommonTokenStream tokens(&lexer); 205 | zeroscriptParser parser(&tokens); 206 | free(buff); 207 | free(a); 208 | free(b); 209 | return visitExpression(parser.expression(), pBody); 210 | } 211 | 212 | Expression *visitExpression(zeroscriptParser::ExpressionContext *pContext, Body *pKind) { 213 | if (pContext->primaryExpresssion()) { 214 | return visitPrimaryExpression(pContext->primaryExpresssion(), pKind); 215 | } else if (pContext->bop) { 216 | // turn logical operators into if statements for short circuit evaluation 217 | if (strcmp("and", pContext->bop->getText().data()) == 0) { 218 | //turn and into if statement 219 | return andToIf(pContext, pKind); 220 | } else if (strcmp("or", pContext->bop->getText().data()) == 0) { 221 | //turn or into if statement 222 | return orToIf(pContext, pKind); 223 | } else { 224 | Expression *left = visitExpression(pContext->expression(0), pKind); 225 | Expression *right = visitExpression(pContext->expression(1), pKind); 226 | BinaryExpression *expr = new BinaryExpression(); 227 | expr->left = left; 228 | expr->right = right; 229 | expr->setOp(pContext->bop->getText().data()); 230 | if (strcmp(expr->op, ".") == 0) { 231 | if (expr->right->kind == AST::AST_KIND_TERMINAL) { 232 | TerminalExpression *ident = dynamic_cast(expr->right); 233 | if (ident->type == TerminalExpression::TYPE_IDENTIFIER) { 234 | ident->type = TerminalExpression::TYPE_STRING; 235 | } 236 | } 237 | } 238 | return expr; 239 | } 240 | } else if (pContext->prefix) { 241 | Expression *right = visitExpression(pContext->expression(0), pKind); 242 | PrefixExpression *expr = new PrefixExpression(); 243 | expr->expr = right; 244 | expr->setOp(pContext->prefix->getText().data()); 245 | return expr; 246 | } else if (pContext->postfix) { 247 | Expression *left = visitExpression(pContext->expression(0), pKind); 248 | PostfixExpression *expr = new PostfixExpression(); 249 | expr->expr = left; 250 | expr->setOp(pContext->postfix->getText().data()); 251 | return expr; 252 | } else if (pContext->methodCall) { 253 | MethodCall *methodCall = visitMethodCall(pContext, pKind); 254 | return methodCall; 255 | } else if (pContext->anonymousFunction()) { 256 | return visitAnonymousFunction(pContext->anonymousFunction(), pKind); 257 | } else if (pContext->arrayIndexer) { 258 | Expression *left = visitExpression(pContext->expression(0), pKind); 259 | Expression *right = visitExpression(pContext->expression(1), pKind); 260 | BinaryExpression *expr = new BinaryExpression(); 261 | expr->left = left; 262 | expr->right = right; 263 | expr->setOp("."); 264 | return expr; 265 | } else if (pContext->newObject()) { 266 | return visitMethodCall(pContext->newObject()->expression(), pKind); 267 | } 268 | return nullptr; 269 | } 270 | 271 | MethodCall *visitMethodCall(zeroscriptParser::ExpressionContext *pContext, Body *pKind) { 272 | MethodCall *methodCall = new MethodCall(); 273 | Expression *callee = visitExpression(pContext->expression(0), pKind); 274 | methodCall->callee = callee; 275 | methodCall->argumentsList = visitExpressionList(pContext->expressionList(), pKind); 276 | return methodCall; 277 | } 278 | 279 | Expression *visitPrimaryExpression(zeroscriptParser::PrimaryExpresssionContext *pContext, Body *pKind) { 280 | if (pContext->expression()) { 281 | return visitExpression(pContext->expression(), pKind); 282 | } else { 283 | return visitAtom(pContext->atom(), pKind); 284 | } 285 | } 286 | 287 | int forIndexerCount = 0; 288 | 289 | //for in loop is a syntactic sugar 290 | //we convert it into a for loop 291 | Loop *visitForInLoop(zeroscriptParser::ForInLoopContext *pContext, Body *parentBody) { 292 | Loop *loop = new Loop(); 293 | Var *iterItem = new Var(); 294 | iterItem->setIdentifier(pContext->iterElement->getText().data()); 295 | iterItem->value = TerminalExpression::number("0"); 296 | Statement *stmt = new Statement(); 297 | stmt->stmt = iterItem; 298 | parentBody->statements->push_back(stmt); 299 | Var *indexer = new Var(); 300 | indexer->value = TerminalExpression::number("0"); 301 | char indexerName[100]; 302 | snprintf(indexerName, 99, "$indexer%d", forIndexerCount++); 303 | indexer->setIdentifier(indexerName); 304 | Statement *stmt2 = new Statement(); 305 | stmt2->stmt = indexer; 306 | parentBody->statements->push_back(stmt2); 307 | //objectToIterate = iter 308 | Expression *objectToIterate = visitExpression(pContext->expression(), parentBody); 309 | MethodCall *objectKeysCall = new MethodCall(); 310 | objectKeysCall->argumentsList = new ExpressionList(); 311 | //objectKeys = iter.keys 312 | BinaryExpression *objectKeys = new BinaryExpression(); 313 | objectKeys->left = objectToIterate; 314 | objectKeys->right = TerminalExpression::string(" keys "); 315 | objectKeys->setOp("."); 316 | //objectKeysCall = iter.keys() 317 | objectKeysCall->callee = objectKeys; 318 | //objectKeysSize = iter.size 319 | BinaryExpression *objectKeysSize = new BinaryExpression(); 320 | objectKeysSize->left = objectToIterate; 321 | objectKeysSize->right = TerminalExpression::string(" size "); 322 | objectKeysSize->setOp("."); 323 | //objectKeysSizeCall = iter.size() 324 | MethodCall *objectKeysSizeCall = new MethodCall(); 325 | objectKeysSizeCall->argumentsList = new ExpressionList(); 326 | objectKeysSizeCall->callee = objectKeysSize; 327 | 328 | //condition = iter.size() > i 329 | BinaryExpression *condition = new BinaryExpression(); 330 | condition->left = objectKeysSizeCall; 331 | condition->right = TerminalExpression::identifier(indexerName); 332 | condition->setOp(">"); 333 | 334 | loop->condition = condition; 335 | loop->startExpr = NULL; 336 | 337 | PostfixExpression *iter = new PostfixExpression(); 338 | iter->expr = TerminalExpression::identifier(indexerName); 339 | iter->setOp("++"); 340 | 341 | loop->iterExpr = iter; 342 | 343 | 344 | if (pContext->bodyOrStatement()->body()) { 345 | loop->body = visitBody(pContext->bodyOrStatement()->body(), parentBody); 346 | } else { 347 | loop->body = new Body(); 348 | loop->body->statements = visitStatement(pContext->bodyOrStatement()->statement(), parentBody); 349 | } 350 | 351 | Statement *elemAccessStmt = new Statement(); 352 | loop->body->statements->insert(loop->body->statements->begin(), elemAccessStmt); 353 | 354 | //elemAccessExpression => iterElement = objectKeysCall[indexer] 355 | BinaryExpression *elemAccessExpression = new BinaryExpression(); 356 | elemAccessExpression->setOp("="); 357 | elemAccessExpression->left = TerminalExpression::identifier(pContext->iterElement->getText().data()); 358 | 359 | //objectKeysCall[indexer] 360 | BinaryExpression *keyExpr = new BinaryExpression(); 361 | keyExpr->setOp("."); 362 | keyExpr->left = objectKeysCall; 363 | keyExpr->right = TerminalExpression::identifier(indexerName); 364 | 365 | elemAccessExpression->right = keyExpr; 366 | 367 | elemAccessStmt->stmt = elemAccessExpression; 368 | return loop; 369 | } 370 | 371 | Loop *visitForLoop(zeroscriptParser::ForLoopContext *pContext, Body *parentBody) { 372 | Loop *loop = new Loop(); 373 | size_t hasStartExpr = 0; 374 | if (pContext->var()) { 375 | vector *variables = visitVar(pContext->var(), parentBody); 376 | for (int i = 0; i < variables->size(); i++) { 377 | Statement *stmt = new Statement(); 378 | stmt->stmt = variables->at(i); 379 | stmt->line_number = static_cast(pContext->var()->VAR()->getSymbol()->getLine()); 380 | parentBody->statements->push_back(stmt); 381 | } 382 | } else if (pContext->expression().size() > 2) { 383 | hasStartExpr = 1; 384 | loop->startExpr = visitExpression(pContext->expression(0), parentBody); 385 | } else { 386 | loop->startExpr = NULL; 387 | } 388 | if (pContext->bodyOrStatement()->body()) { 389 | loop->body = visitBody(pContext->bodyOrStatement()->body(), parentBody); 390 | } else { 391 | loop->body = new Body(); 392 | loop->body->statements = visitStatement(pContext->bodyOrStatement()->statement(), parentBody); 393 | } 394 | if (pContext->expression(hasStartExpr)) { 395 | loop->condition = visitExpression(pContext->expression(hasStartExpr), parentBody); 396 | } 397 | if (pContext->expression(hasStartExpr + 1)) { 398 | loop->iterExpr = visitExpression(pContext->expression(hasStartExpr + 1), parentBody); 399 | } 400 | return loop; 401 | } 402 | 403 | Loop *visitWhileLoop(zeroscriptParser::WhileLoopContext *pContext, Body *pKind) { 404 | Loop *loop = new Loop(); 405 | loop->startExpr = NULL; 406 | loop->condition = visitExpression(pContext->expression(), pKind); 407 | if (pContext->bodyOrStatement()->body()) { 408 | loop->body = visitBody(pContext->bodyOrStatement()->body(), pKind); 409 | } else { 410 | loop->body = new Body(); 411 | loop->body->statements = visitStatement(pContext->bodyOrStatement()->statement(), pKind); 412 | } 413 | loop->iterExpr = NULL; 414 | return loop; 415 | } 416 | 417 | Conditional *visitConditional(zeroscriptParser::ConditionalContext *pContext, Body *pKind) { 418 | Conditional *cond = new Conditional(); 419 | cond->condition = visitExpression(pContext->expression(), pKind); 420 | if (pContext->bodyOrStatement(0)->body()) { 421 | cond->body = visitBody(pContext->bodyOrStatement(0)->body(), pKind); 422 | } else { 423 | cond->body = new Body(); 424 | cond->body->statements = visitStatement(pContext->bodyOrStatement(0)->statement(), pKind); 425 | } 426 | if (pContext->ELSE()) { 427 | if (pContext->bodyOrStatement(1)->body()) { 428 | cond->elseBody = visitBody(pContext->bodyOrStatement(1)->body(), pKind); 429 | } else { 430 | cond->elseBody = new Body(); 431 | cond->elseBody->statements = visitStatement(pContext->bodyOrStatement(1)->statement(), pKind); 432 | } 433 | } 434 | return cond; 435 | } 436 | 437 | int jsonObjectCount = 0; 438 | 439 | Expression *visitJson(zeroscriptParser::JsonContext *json, Body *pBody) { 440 | 441 | Statement *stmt = new Statement(); 442 | //json is a syntactic sugar. 443 | //we are going to build this template. 444 | //var $obj{jsonObjectCount} = new Object(); 445 | MethodCall *call = new MethodCall(); 446 | TerminalExpression *expr = TerminalExpression::identifier("Object"); 447 | call->callee = expr; 448 | call->argumentsList = new ExpressionList(); 449 | 450 | char *nameOfTheTempObject = const_cast((std::string("$tempJson") + 451 | std::to_string(++jsonObjectCount)).data()); 452 | 453 | Var *var = new Var(); 454 | var->setIdentifier(nameOfTheTempObject); 455 | var->value = call; 456 | 457 | stmt->stmt = var; 458 | pBody->statements->push_back(stmt); 459 | 460 | if (json->jsonObject()) { 461 | visitJsonObject(json->jsonObject(), nameOfTheTempObject, pBody); 462 | } else { 463 | visitJsonArray(json->jsonArray(), nameOfTheTempObject, pBody); 464 | } 465 | 466 | return TerminalExpression::identifier(nameOfTheTempObject); 467 | } 468 | 469 | void visitJsonPair(zeroscriptParser::JsonPairContext *pair, char *nameOfTheTempObj, Body *pBody) { 470 | zeroscriptParser::ExpressionContext *keyContext = pair->key; 471 | Expression *key = visitExpression(keyContext, pBody); 472 | Expression *value = visitExpression(pair->expression(1), pBody); 473 | doHandleJsonPair(nameOfTheTempObj, pBody, key, value); 474 | 475 | } 476 | 477 | void doHandleJsonPair(const char *nameOfTheTempObj, const Body *pBody, Expression *key, 478 | Expression *value) const {//we will build the following template 479 | //{nameOfTheTempObj}[key] = value; 480 | BinaryExpression *keyAddressOpereation = new BinaryExpression(); 481 | keyAddressOpereation->setOp("."); 482 | keyAddressOpereation->left = TerminalExpression::identifier( 483 | nameOfTheTempObj 484 | ); 485 | keyAddressOpereation->right = key; 486 | 487 | BinaryExpression *assignmentOperation = new BinaryExpression(); 488 | assignmentOperation->left = keyAddressOpereation; 489 | assignmentOperation->right = value; 490 | assignmentOperation->setOp("="); 491 | 492 | Statement *stmt = new Statement(); 493 | stmt->stmt = assignmentOperation; 494 | 495 | pBody->statements->push_back(stmt); 496 | } 497 | 498 | void visitJsonObject(zeroscriptParser::JsonObjectContext *json, char *nameOfTheTempObj, Body *pBody) { 499 | 500 | for (int i = 0; i < json->jsonPair().size(); i++) { 501 | visitJsonPair(json->jsonPair(i), nameOfTheTempObj, pBody); 502 | } 503 | 504 | } 505 | 506 | void visitJsonArray(zeroscriptParser::JsonArrayContext *json, char *nameOfTheTempObj, Body *pBody) { 507 | 508 | for (size_t i = 0; i < json->expression().size(); i++) { 509 | doHandleJsonPair(nameOfTheTempObj, pBody, TerminalExpression::string( 510 | (std::string(" ") + std::to_string(i) + std::string(" ")).data() 511 | ), visitExpression(json->expression(i), pBody)); 512 | } 513 | 514 | } 515 | 516 | Expression *visitThrow(zeroscriptParser::Throw_Context *pContext, Body *pBody) { 517 | PrefixExpression *expr = new PrefixExpression(); 518 | expr->setOp("throw"); 519 | expr->expr = visitExpression(pContext->expression(), pBody); 520 | return expr; 521 | } 522 | 523 | AST *visitTryCatch(zeroscriptParser::TryCatchContext *pContext, Body *pBody) { 524 | TryCatch *tryCatch = new TryCatch(); 525 | tryCatch->tryBody = visitBody(pContext->body(0), pBody); 526 | tryCatch->catchBody = visitBody(pContext->body(1), pBody); 527 | if (pContext->FINALLY()) { 528 | tryCatch->finallyBody = visitBody(pContext->body(2), pBody); 529 | } 530 | tryCatch->catchIdent = visitIdent(pContext->identifier(), pBody); 531 | return tryCatch; 532 | } 533 | 534 | vector *visitStatement(zeroscriptParser::StatementContext *statement, Body *pKind) { 535 | vector *resultStatements = new vector(); 536 | Statement *stmt = new Statement(); 537 | resultStatements->push_back(stmt); 538 | if (statement->semicolon) { 539 | stmt->stmt = new EmptyExpression(); 540 | return resultStatements; 541 | } 542 | if (statement->body()) { 543 | stmt->stmt = visitBody(statement->body(), pKind); 544 | } else if (statement->var()) { 545 | vector *variables = visitVar(statement->var(), pKind); 546 | resultStatements = new vector(); 547 | for (int i = 0; i < variables->size(); i++) { 548 | Statement *s = new Statement(); 549 | s->stmt = variables->at(i); 550 | s->line_number = statement->var()->VAR()->getSymbol()->getLine(); 551 | resultStatements->push_back(s); 552 | } 553 | } else if (statement->function()) { 554 | stmt->stmt = visitFunction(statement->function(), pKind); 555 | } else if (statement->expression()) { 556 | stmt->stmt = visitExpression(statement->expression(), pKind); 557 | } else if (statement->forLoop()) { 558 | stmt->stmt = visitForLoop(statement->forLoop(), pKind); 559 | } else if (statement->forInLoop()) { 560 | stmt->stmt = visitForInLoop(statement->forInLoop(), pKind); 561 | } else if (statement->whileLoop()) { 562 | stmt->stmt = visitWhileLoop(statement->whileLoop(), pKind); 563 | } else if (statement->conditional()) { 564 | stmt->stmt = visitConditional(statement->conditional(), pKind); 565 | } else if (statement->tryCatch()) { 566 | stmt->stmt = visitTryCatch(statement->tryCatch(), pKind); 567 | } else if (statement->throw_()) { 568 | stmt->stmt = visitThrow(statement->throw_(), pKind); 569 | } 570 | if (resultStatements->size() > 0) { 571 | stmt = resultStatements->at(0); 572 | stmt->hasBreak = statement->BREAK() != NULL; 573 | stmt->hasContinue = statement->CONTINUE() != NULL; 574 | stmt->hasReturn = statement->RET() != NULL; 575 | if (stmt->hasBreak || stmt->hasContinue) { 576 | stmt->stmt = new EmptyExpression(); 577 | } 578 | } 579 | return resultStatements; 580 | } 581 | 582 | Body *visitBody(zeroscriptParser::BodyContext *context, Body *parentBody) { 583 | Body *body = new Body(); 584 | vector statements = context->statement(); 585 | for (int i = 0; i < statements.size(); i++) { 586 | vector *resultStatements = visitStatement(statements.at(i), body); 587 | for (int j = 0; j < resultStatements->size(); j++) { 588 | body->statements->push_back(resultStatements->at(j)); 589 | } 590 | } 591 | return body; 592 | } 593 | 594 | void visitClassDeclaration(zeroscriptParser::ClassDeclarationContext *context) { 595 | cls = new ClassDeclaration(); 596 | cls->body = visitBody(context->body(), NULL); 597 | cls->setIdentifier(context->identifier().at(0)->getText().data()); 598 | cls->arguments = visitArguments(context->argumentsList(), NULL); 599 | //hanlde imports 600 | for (int_t i = 0; i < context->importStmt().size(); i++) { 601 | std::string path = (context->importStmt(i)->STRING()->getText()); 602 | std::string as = (context->importStmt(i)->IDENT()->getText()); 603 | cls->importsMap.insert(std::pair( 604 | path.substr(1, path.size() - 2), as 605 | )); 606 | } 607 | } 608 | 609 | 610 | ClassDeclaration *getRootClass() { 611 | return cls; 612 | } 613 | 614 | }; 615 | -------------------------------------------------------------------------------- /src/compiler/Compiler.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 26.05.2018. 3 | // 4 | 5 | #include "compiler.h" 6 | 7 | class Compiler { 8 | 9 | Program *program = new Program(); 10 | stack *functionsStack = new stack(); 11 | stack *loopEndLabelsStack = new stack(); 12 | stack *loopStartLabelsStack = new stack(); 13 | // when a return inside try is encountered, prepend finally body 14 | //TODO: crashes if i use vector instead of custom written arraylist. why? 15 | arraylist_t *finallyBlocksToPrependReturn = arraylist_new(sizeof(TryCatch)); 16 | vector *compiledStaticFunctions = new vector(); 17 | ClassDeclaration *cls; 18 | int labelCount = 0; 19 | 20 | uint_t getRegister(char *ident) { 21 | return functionsStack->top()->getRegister(ident); 22 | } 23 | 24 | void freeRegister(uint_t index) { 25 | functionsStack->top()->freeRegister(index); 26 | } 27 | 28 | map *getCurrentSymbolTable() { 29 | return functionsStack->top()->symbolTable; 30 | }; 31 | char *bytes = NULL; 32 | size_t len = 0; 33 | public : 34 | Compiler(ClassDeclaration *ast) { 35 | this->cls = ast; 36 | AddressCalculator *addressCalculator = new AddressCalculator(ast); 37 | //ast->print(); 38 | compileClass((ast)); 39 | program->optimize(); 40 | //program->print(); 41 | //exit(0); 42 | Assembler assembler; 43 | bytes = assembler.toBytes(program, &len); 44 | arraylist_free(program->instructions); 45 | delete(addressCalculator); 46 | } 47 | 48 | char *toBytes(size_t *len) { 49 | *len = this->len; 50 | return bytes; 51 | } 52 | 53 | uint_t compileExpression(Expression *pExpr, uint_t requestedDestinationRegister = 0) { 54 | ExpressionCompiler *expressionCompiler = new ExpressionCompiler(program, functionsStack->top()); 55 | uint_t ret = expressionCompiler->compileExpression(pExpr, requestedDestinationRegister); 56 | delete (expressionCompiler); 57 | return ret; 58 | } 59 | 60 | void compileFunction(Function *func) { 61 | char* endLabel = static_cast(malloc(100)); 62 | snprintf(endLabel,100,"end%s",func->identifier); 63 | program->addLabel("%s", func->identifier); 64 | uint_t index = program->addInstruction(FFRAME, (uint_t) NULL, (uint_t) NULL, 65 | (uint_t) NULL); 66 | functionsStack->push(func); 67 | /*int i = (int) (func->arguments->identifiers->size()) - 1; 68 | for (i = 0; i < (int) (func->arguments->identifiers->size()); i++) { 69 | char *ident = func->arguments->identifiers->at(i)->data; 70 | uint_t identReg = (uint_t) (func->getRegister(ident)); 71 | program->addComment("pop arg %i (%s)", i, ident); 72 | program->addInstruction(POP, identReg, (uint_t) NULL, 73 | (uint_t) NULL); 74 | }*/ 75 | program->addComment("mov function ptr to register for %s", func->identifier); 76 | program->addInstruction(MOV_FNC, (uint_t) getRegister(func->identifier), 77 | (uint64_t) func->identifier, static_cast(func->isAsync)); 78 | if (strcmp(func->identifier, "__static__constructor__") == 0) { 79 | //if this is the static constructor, add all other static functions and imports to the __static__ variable 80 | for (auto elem : cls->importsMap) { 81 | char *first = (char *) malloc(strlen(elem.first.c_str()) + 1); 82 | char *second = (char *) malloc(strlen(elem.second.c_str()) + 1); 83 | strcpy(first, elem.first.c_str()); 84 | strcpy(second, elem.second.c_str()); 85 | //add import instruction 86 | program->addInstruction(IMPORT_CLS, (uint_t) first, (uint_t) second, NULL); 87 | } 88 | uint_t staticsRegister = static_cast(getRegister(NULL)); 89 | program->addInstruction(GET_FIELD_IMMEDIATE, 1, (uint_t) "__static__", staticsRegister); 90 | uint_t fncNameRegister = static_cast(getRegister(NULL)); 91 | uint_t fncRefRegister = static_cast(getRegister(NULL)); 92 | for (int_t i = 0; i < compiledStaticFunctions->size(); i++) { 93 | char *fncName = compiledStaticFunctions->at(i); 94 | program->addComment("mov function ptr to register for %s", fncName); 95 | program->addInstruction(MOV_FNC, (uint_t) fncRefRegister, 96 | (uint_t) fncName, NULL); 97 | program->addInstruction(MOV_STR, fncNameRegister, (uint_t) fncName, 0); 98 | program->addInstruction(SET_FIELD, staticsRegister, fncNameRegister, fncRefRegister); 99 | } 100 | } else if (func->isStatic) { 101 | //if this is a static method, register it to further usage 102 | compiledStaticFunctions->push_back(func->identifier); 103 | } 104 | compileBody(func->body); 105 | program->addInstruction(RETURN, (uint_t) NULL, (uint_t) NULL, (uint_t) NULL); 106 | program->addLabel("end%s",func->identifier); 107 | compileRemainingFunctions(); 108 | uint_t locals_count = func->registerTable->size() + func->symbolTable->size(); 109 | //set locals count 110 | ((z_instruction_t *) arraylist_get(program->instructions, index))->r0 = locals_count; 111 | ((z_instruction_t *) arraylist_get(program->instructions, index))->r1 = (uint_t) (func); 112 | ((z_instruction_t *) arraylist_get(program->instructions, index))->r2 = (uint_t) (endLabel); 113 | functionsStack->pop(); 114 | } 115 | 116 | void compileLoop(Loop *pLoop) { 117 | char *lstart_label = static_cast(malloc(100)); 118 | sprintf(lstart_label, ".lstart_%d", ++labelCount); 119 | char *lend_label = static_cast(malloc(100)); 120 | sprintf(lend_label, ".lend_%d", ++labelCount); 121 | loopEndLabelsStack->push(lend_label); 122 | loopStartLabelsStack->push(lstart_label); 123 | char *lcond_label = static_cast(malloc(100)); 124 | sprintf(lcond_label, ".lcond_%d", ++labelCount); 125 | 126 | if (pLoop->startExpr) { 127 | program->addComment("loop start expression"); 128 | uint_t startReg = compileExpression(pLoop->startExpr); 129 | freeRegister(startReg); 130 | } 131 | program->addComment("jump to condition"); 132 | program->addInstruction(JMP, (uint_t) lcond_label, (uint_t) NULL, 133 | (uint_t) NULL); 134 | program->addLabel(lstart_label); 135 | program->addComment("loop body"); 136 | compileBody(pLoop->body); 137 | if (pLoop->iterExpr) { 138 | program->addComment("loop iteration"); 139 | uint_t iterReg = compileExpression(pLoop->iterExpr); 140 | freeRegister(iterReg); 141 | } 142 | program->addLabel(lcond_label); 143 | if (pLoop->condition) { 144 | program->addComment("loop condition"); 145 | uint_t condReg = compileExpression(pLoop->condition); 146 | program->addComment("jump to the end if loop condition fails"); 147 | 148 | program->addInstruction(JMP_TRUE, condReg, (uint_t) lstart_label, 149 | (uint_t) NULL); 150 | freeRegister(condReg); 151 | } 152 | program->addLabel(lend_label); 153 | loopEndLabelsStack->pop(); 154 | } 155 | 156 | void compileConditional(Conditional *pConditional) { 157 | char *if_fail_label = static_cast(malloc(100)); 158 | sprintf(if_fail_label, ".else_%d", ++labelCount); 159 | char *con_end_label = static_cast(malloc(100)); 160 | sprintf(con_end_label, ".end_cond_%d", ++labelCount); 161 | program->addComment("if condition"); 162 | uint_t cond_reg = compileExpression(pConditional->condition); 163 | program->addComment("jump if condition is not satisfied"); 164 | program->addInstruction(JMP_NOT_TRUE, (uint_t) cond_reg, (uint_t) if_fail_label, 165 | (uint_t) NULL); 166 | freeRegister(cond_reg); 167 | compileBody(pConditional->body); 168 | if (pConditional->elseBody) { 169 | // why are you here? 170 | // program->addInstruction(NOP, (uint_t) NULL, (uint_t) NULL, (uint_t) NULL); 171 | program->addInstruction(JMP, (uint_t) con_end_label, (uint_t) NULL, 172 | (uint_t) NULL); 173 | } 174 | program->addLabel(if_fail_label); 175 | if (pConditional->elseBody) { 176 | compileBody(pConditional->elseBody); 177 | } 178 | program->addLabel(con_end_label); 179 | } 180 | 181 | void compileVar(Var *pVar) { 182 | uint_t reg = (uint_t) getRegister(pVar->identifier); 183 | uint_t valueReg = compileExpression(pVar->value, reg); 184 | if(pVar->isSynchronized){ 185 | uint_t tempReg = getRegister(NULL); 186 | program->addInstruction(MOV_STR,tempReg,(uint_t)pVar->identifier,0); 187 | program->addInstruction(SET_FIELD,1,tempReg,valueReg); 188 | freeRegister(tempReg); 189 | return; 190 | } 191 | if (valueReg - reg) { 192 | program->addInstruction(MOV, reg, valueReg, (uint_t) NULL); 193 | freeRegister(valueReg); 194 | } 195 | } 196 | 197 | void compileTryCatch(TryCatch *pCatch) { 198 | // push this finally block so that it appears before return 199 | if (pCatch->finallyBody) 200 | arraylist_push(finallyBlocksToPrependReturn, pCatch); 201 | char *catchLabel = (char *) malloc(100); 202 | char *finallyLabel = (char *) malloc(100); 203 | snprintf(catchLabel, 100, ".catch_%d", ++labelCount); 204 | snprintf(finallyLabel, 100, ".finally_%d", ++labelCount); 205 | program->addComment("try start"); 206 | program->addInstruction(SET_CATCH, (uint_t) (catchLabel), (uint_t) finallyLabel, 0); 207 | compileBody(pCatch->tryBody); 208 | program->addComment("end try body"); 209 | program->addInstruction(JMP, (uint_t) finallyLabel, 0, 0); 210 | program->addLabel(catchLabel); 211 | uint_t exceptionInfoRegister = (uint_t) getRegister(pCatch->catchIdent->data); 212 | program->addInstruction(POP, exceptionInfoRegister, 0, 0); 213 | compileBody(pCatch->catchBody); 214 | program->addLabel(finallyLabel); 215 | if (pCatch->finallyBody) 216 | compileBody(pCatch->finallyBody); 217 | program->addInstruction(CLEAR_CATCH, 0, 0, 0); 218 | if (pCatch->finallyBody) 219 | arraylist_pop(finallyBlocksToPrependReturn); 220 | } 221 | 222 | void compileStmt(Statement *statement) { 223 | if (statement->hasBreak) { 224 | program->addComment("break "); 225 | char *lend_label = loopEndLabelsStack->top(); 226 | program->addInstruction(JMP, (uint_t) lend_label, 227 | (uint_t) NULL, (uint_t) NULL); 228 | } 229 | AST *stmt = statement->stmt; 230 | if (stmt->kind == AST::AST_KIND_FUNCTION) { 231 | addFunctionToCompileLater(dynamic_cast(stmt)); 232 | } else if (stmt->kind == AST::AST_KIND_VAR) { 233 | program->addComment("init variable %s", dynamic_cast(stmt)->identifier); 234 | compileVar(dynamic_cast(stmt)); 235 | } else if (stmt->kind == AST::AST_KIND_LOOP) { 236 | compileLoop(dynamic_cast(stmt)); 237 | } else if (stmt->kind == AST::AST_KIND_BODY) { 238 | compileBody(dynamic_cast(stmt)); 239 | } else if (stmt->kind == AST::AST_KIND_CONDITIONAL) { 240 | compileConditional(dynamic_cast(stmt)); 241 | } else if (stmt->kind == AST::AST_KIND_TRY_CATCH) { 242 | compileTryCatch(dynamic_cast(stmt)); 243 | } else if (dynamic_cast(stmt)) { 244 | uint_t reg = compileExpression(dynamic_cast(stmt)); 245 | if (statement->hasReturn) { 246 | //this loop compiler every finally block in the lexical scope before return statement appears. 247 | program->addComment(" prepend finally blocks", reg); 248 | for (int_t i = 0; i < finallyBlocksToPrependReturn->size; i++) { 249 | compileBody(((TryCatch *) arraylist_get(finallyBlocksToPrependReturn, i))->finallyBody); 250 | } 251 | program->addComment(" return %d", reg); 252 | program->addInstruction(RETURN, reg, (uint_t) NULL, (uint_t) NULL); 253 | } 254 | freeRegister(reg); 255 | } 256 | } 257 | 258 | void compileBody(Body *pBody) { 259 | vector *statements = pBody->getStatements(); 260 | //hoisting 261 | for (int i = 0; i < statements->size(); i++) { 262 | AST *stmt = statements->at(i)->stmt; 263 | if (stmt->kind == AST::AST_KIND_FUNCTION) { 264 | Function *func = dynamic_cast(stmt); 265 | program->addComment("mov function ptr to register for %s", func->identifier); 266 | program->addInstruction(MOV_FNC, (uint_t) (getRegister(func->identifier)), 267 | (uint_t) func->identifier, static_cast(func->isAsync)); 268 | } 269 | 270 | } 271 | //actual compiling 272 | for (int i = 0; i < statements->size(); i++) { 273 | Statement *stmt = statements->at(i); 274 | compileStmt(stmt); 275 | } 276 | } 277 | 278 | void addFunctionToCompileLater(Function *fnc) { 279 | functionsStack->top()->functionsToCompile->push_back(fnc); 280 | } 281 | 282 | void compileRemainingFunctions() { 283 | vector *functionsToCompile = functionsStack->top()->functionsToCompile; 284 | for (int i = 0; i < functionsToCompile->size(); i++) { 285 | compileFunction(dynamic_cast(functionsToCompile->at(i))); 286 | } 287 | functionsStack->top()->functionsToCompile->clear(); 288 | } 289 | 290 | void compileClass(ClassDeclaration *cls) { 291 | char* endLabel = static_cast(malloc(100)); 292 | snprintf(endLabel,100,"end%s",cls->identifier); 293 | program->addLabel("%s", cls->identifier); 294 | uint_t index = program->addInstruction(FFRAME, NULL, NULL, NULL); 295 | program->addInstruction(CREATE_THIS, (uint_t) NULL, (uint_t) NULL, 296 | (uint_t) NULL); 297 | functionsStack->push(cls); 298 | for (int i = 0; i < (cls->arguments->identifiers->size()); i++) { 299 | char *ident = cls->arguments->identifiers->at(i)->data; 300 | uint_t identReg = (uint_t) (cls->getRegister(ident)); 301 | program->addComment("pop arg %i (%s)", i, ident); 302 | program->addInstruction(POP, identReg, (uint_t) NULL, 303 | (uint_t) NULL); 304 | } 305 | compileBody(cls->body); 306 | program->addInstruction(RETURN, (uint_t) NULL, (uint_t) NULL, (uint_t) NULL); 307 | program->addLabel("end%s", cls->identifier); 308 | compileRemainingFunctions(); 309 | uint_t locals_count = cls->registerTable->size() + cls->symbolTable->size(); 310 | z_instruction_t *fframe = (z_instruction_t *) arraylist_get(program->instructions, index); 311 | //set args of FFRAME 312 | fframe->r0 = locals_count; 313 | //this will later be used to assemble required information 314 | fframe->r1 = (uint_t) (cls); 315 | //this will later be used to assemble required information 316 | fframe->r2 = (uint_t) (endLabel); 317 | functionsStack->pop(); 318 | } 319 | 320 | }; -------------------------------------------------------------------------------- /src/compiler/ExprssionCompiler.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 27.05.2018. 3 | // 4 | 5 | class ExpressionCompiler { 6 | 7 | Program *program; 8 | 9 | FunctionKind *function = NULL; 10 | 11 | uint_t getRegister(char *ident) { 12 | return function->getRegister(ident); 13 | } 14 | 15 | void freeRegister(uint_t index) { 16 | function->freeRegister(index); 17 | } 18 | 19 | uint_t toOpcode(char *operation) { 20 | if (strcmp(operation, ".") == 0) return GET_FIELD; 21 | if (strcmp(operation, "+") == 0) return ADD; 22 | if (strcmp(operation, "-") == 0) return SUB; 23 | if (strcmp(operation, "*") == 0) return MUL; 24 | if (strcmp(operation, "/") == 0) return DIV; 25 | if (strcmp(operation, "<") == 0) return CMP_LESS; 26 | if (strcmp(operation, "<=") == 0) return CMP_LESS_OR_EQUAL; 27 | if (strcmp(operation, ">") == 0) return CMP_GREATER; 28 | if (strcmp(operation, ">=") == 0) return CMP_GREATER_OR_EQUAL; 29 | if (strcmp(operation, "++") == 0) return INC; 30 | if (strcmp(operation, "--") == 0) return SUB; 31 | if (strcmp(operation, "!=") == 0) return CMP_N_EQUAL; 32 | if (strcmp(operation, "==") == 0) return CMP_EQUAL; 33 | if (strcmp(operation, "%") == 0) return MOD; 34 | if (strcmp(operation, "throw") == 0) return THROW_EXCEPTION; 35 | } 36 | 37 | public: 38 | uint_t compilePostfix(PostfixExpression *pExpression, uint_t requestedDestinationRegister = 0) { 39 | uint_t leftReg = (uint_t) compileExpression(pExpression->expr); 40 | uint_t target = requestedDestinationRegister == 0 ? getRegister(NULL) : requestedDestinationRegister; 41 | program->addInstruction(MOV, target, leftReg, NULL); 42 | if (strcmp(pExpression->op, "++") == 0) { 43 | program->addInstruction(INC, leftReg, leftReg, NULL); 44 | } else if (strcmp(pExpression->op, "--") == 0) { 45 | program->addInstruction(DEC, leftReg, leftReg, NULL); 46 | } 47 | // if expression is of the identifier and this identifier is not in the current scopre 48 | // write its value back via a virtual set 49 | TerminalExpression *ident = dynamic_cast(pExpression->expr); 50 | if (ident && ident->type == TerminalExpression::TYPE_IDENTIFIER) { 51 | if (!getRegister(ident->data)) { 52 | uint_t temp = getRegister(NULL); 53 | program->addInstruction(MOV_STR, temp, (uint_t) ident->data, NULL); 54 | program->addInstruction(SET_FIELD, 1, temp, leftReg); 55 | freeRegister(temp); 56 | } 57 | } 58 | return target; 59 | } 60 | 61 | uint_t compilePrefix(PrefixExpression *pExpression, int_t requestedDestinationRegister = 0) { 62 | uint_t leftReg = (uint_t) compileExpression(pExpression->expr); 63 | uint_t target = requestedDestinationRegister == 0 ? getRegister(NULL) : requestedDestinationRegister; 64 | if (strcmp(pExpression->op, "++") == 0) { 65 | target = (uint_t) requestedDestinationRegister == 0 ? leftReg : requestedDestinationRegister; 66 | program->addInstruction(INC, leftReg, target, NULL); 67 | } else if (strcmp(pExpression->op, "--") == 0) { 68 | target = (uint_t) requestedDestinationRegister == 0 ? leftReg : requestedDestinationRegister; 69 | program->addInstruction(DEC, leftReg, target, NULL); 70 | } else if (strcmp(pExpression->op, "-") == 0) { 71 | if (!requestedDestinationRegister) { freeRegister(target); } 72 | uint_t rightReg = (uint_t) compileTerminal(TerminalExpression::number("-1")); 73 | target = (uint_t) requestedDestinationRegister == 0 ? leftReg : requestedDestinationRegister; 74 | program->addInstruction(MUL, leftReg, rightReg, target); 75 | freeRegister(rightReg); 76 | return target; 77 | } else if (strcmp(pExpression->op, "throw") == 0) { 78 | if (!requestedDestinationRegister) { freeRegister(target); } 79 | program->addInstruction(THROW_EXCEPTION, leftReg, NULL, NULL); 80 | return target; 81 | } 82 | // if expression is of the identifier and this identifier is not in the current scopre 83 | // write its value back via a virtual set 84 | TerminalExpression *ident = dynamic_cast(pExpression->expr); 85 | if (ident && ident->type == TerminalExpression::TYPE_IDENTIFIER) { 86 | if (!getRegister(ident->data)) { 87 | uint_t temp = getRegister(NULL); 88 | program->addInstruction(MOV_STR, temp, (uint_t) ident->data, NULL); 89 | program->addInstruction(SET_FIELD, 1, temp, leftReg); 90 | freeRegister(temp); 91 | } 92 | } 93 | return target; 94 | } 95 | 96 | void compileDotForSetField(BinaryExpression *pExpression, uint_t result) { 97 | uint_t leftReg = compileExpression(pExpression->left); 98 | uint_t rightReg = compileExpression(pExpression->right); 99 | program->addInstruction(SET_FIELD, leftReg, rightReg, result); 100 | } 101 | 102 | uint_t compileMethodCall(MethodCall *pMethodCall, uint_t destinationRegister = 0) { 103 | ExpressionList *argumentsList = pMethodCall->argumentsList; 104 | vector *expressions = argumentsList->expressions; 105 | uint_t resultReg = destinationRegister; 106 | if (resultReg == 0) resultReg = getRegister(NULL); 107 | uint_t exprResultReg = getRegister(NULL); 108 | for (int_t i = 0; i < expressions->size(); i++) { 109 | exprResultReg = compileExpression(expressions->at(expressions->size() - 1 - i), exprResultReg); 110 | // program->addComment("push arg number %i for %s ", i, pMethodCall->identifier); 111 | program->addInstruction(PUSH, (uint_t) (exprResultReg), (uint_t) NULL, 112 | (uint_t) NULL); 113 | } 114 | freeRegister(exprResultReg); 115 | exprResultReg = compileExpression(pMethodCall->callee);//compileIdentifier(pMethodCall->identifier); 116 | program->addInstruction(CALL, 117 | (uint_t) exprResultReg, (uint_t) resultReg, 118 | (uint_t) expressions->size()); 119 | freeRegister(exprResultReg); 120 | return resultReg; 121 | } 122 | 123 | uint_t compileIdentifier(char *identifier, uint_t destinationRegister = 0) { 124 | uint_t tempRegister = getRegister(identifier); 125 | //value not found in the symbol table, virtual lookup 126 | if (tempRegister == 0) { 127 | if (destinationRegister == 0) 128 | tempRegister = getRegister(NULL); 129 | else tempRegister = destinationRegister; 130 | TerminalExpression *temp = new TerminalExpression(); 131 | temp->type = TerminalExpression::TYPE_STRING; 132 | temp->data = identifier; 133 | uint_t str_reg = compileTerminal(temp); 134 | program->addInstruction(GET_FIELD, 1, (uint_t) (str_reg), 135 | (uint_t) (tempRegister)); 136 | } 137 | return tempRegister; 138 | } 139 | 140 | uint_t compileIdentifierImmediate(char *identifier, uint_t destinationRegister = 0) { 141 | uint_t tempRegister = getRegister(identifier); 142 | //value not found in the symbol table, virtual lookup 143 | if (tempRegister == 0) { 144 | if (destinationRegister == 0) 145 | tempRegister = getRegister(NULL); 146 | else tempRegister = destinationRegister; 147 | program->addInstruction(GET_FIELD_IMMEDIATE, 1, (uint_t) (identifier), 148 | (uint_t) (tempRegister)); 149 | } 150 | return tempRegister; 151 | } 152 | 153 | uint_t compileBinary(BinaryExpression *pExpression, uint_t destinationRegister = 0) { 154 | 155 | uint_t leftReg = 0; 156 | uint_t rightReg = 0; 157 | uint_t resultReg = 0; 158 | int_t isSetField = 0; 159 | 160 | int_t isAssignment = strcmp(pExpression->op, "=") == 0; 161 | int_t isDot = strcmp(pExpression->op, ".") == 0; 162 | if (isAssignment && pExpression->left->kind == AST::AST_KIND_BINARY) { 163 | BinaryExpression *leftBinaryExpr = dynamic_cast(pExpression->left); 164 | if (strcmp(leftBinaryExpr->op, ".") == 0) { 165 | rightReg = compileExpression(pExpression->right); 166 | compileDotForSetField(leftBinaryExpr, rightReg); 167 | return rightReg; 168 | } else { 169 | error_and_exit(const_cast("left value expected")); 170 | } 171 | } 172 | // if we are setting a variable from a parent scope... 173 | if (isAssignment && pExpression->left->kind == AST::AST_KIND_TERMINAL) { 174 | TerminalExpression *leftValue = (TerminalExpression *) (pExpression->left); 175 | if (leftValue->type == TerminalExpression::TYPE_IDENTIFIER) { 176 | char *ident = leftValue->data; 177 | int_t is_left_variable_on_scope = getRegister(ident) != NULL; 178 | if (!is_left_variable_on_scope) { 179 | leftReg = getRegister(NULL); 180 | program->addInstruction(MOV_STR, (uint_t) leftReg, 181 | (uint_t) (ident), (uint_t) 0); 182 | rightReg = compileExpression(pExpression->right); 183 | //setfield this, $leftReg, $resutReg 184 | program->addInstruction(SET_FIELD, (uint_t) (1), 185 | (uint_t) (leftReg), (uint_t) (rightReg)); 186 | //result of this operation is null 187 | return (uint_t) 0; 188 | } 189 | } else { 190 | error_and_exit(const_cast("left value expected")); 191 | } 192 | } 193 | 194 | leftReg = compileExpression(pExpression->left); 195 | uint_t requestedDestinationRegister = 0; 196 | if (isAssignment) { 197 | requestedDestinationRegister = leftReg; 198 | } 199 | if (isDot) rightReg = compileExpression(pExpression->right, requestedDestinationRegister, 1); 200 | else 201 | rightReg = compileExpression(pExpression->right, requestedDestinationRegister); 202 | if (destinationRegister != 0) { 203 | resultReg = destinationRegister; 204 | } else { 205 | resultReg = getRegister(NULL); 206 | } 207 | if (!isAssignment) { 208 | program->addInstruction(toOpcode(pExpression->op), (uint_t) (leftReg), 209 | (uint_t) (rightReg), (uint_t) (resultReg)); 210 | } 211 | if (rightReg != resultReg) 212 | freeRegister(rightReg); 213 | freeRegister(leftReg); 214 | 215 | return resultReg; 216 | } 217 | 218 | uint_t compileTerminal(TerminalExpression *right, uint_t destinationRegister = 0, int_t has_lookup_object = 0) { 219 | if (right->type == TerminalExpression::TYPE_IDENTIFIER) { 220 | if (has_lookup_object) compileIdentifier(right->data, destinationRegister); 221 | return compileIdentifierImmediate(right->data, destinationRegister); 222 | } 223 | uint_t reg = destinationRegister; 224 | if (reg == 0) reg = getRegister(NULL); 225 | if (right->type == TerminalExpression::TYPE_NUMBER) { 226 | program->addInstruction(MOV_NUMBER, (uint_t) (reg), (uint_t) right->data, 227 | (uint_t) NULL); 228 | return reg; 229 | } 230 | if (right->type == TerminalExpression::TYPE_STRING) { 231 | program->addInstruction(MOV_STR, (uint_t) (reg), (uint_t) right->data, 232 | (uint_t) NULL); 233 | return reg; 234 | } 235 | } 236 | 237 | uint_t compileExpression(Expression *expr, uint_t requestedDestinationRegister = 0, int_t has_lookup_object = 0) { 238 | uint_t reg; 239 | if (expr->kind == AST::AST_KIND_METHOD_CALL) { 240 | return compileMethodCall(dynamic_cast(expr), 241 | requestedDestinationRegister); 242 | } else if (expr->kind == AST::AST_KIND_BINARY) { 243 | return compileBinary(dynamic_cast(expr), 244 | requestedDestinationRegister); 245 | } else if (expr->kind == AST::AST_KIND_POSTFIX) { 246 | return compilePostfix(dynamic_cast(expr), 247 | requestedDestinationRegister); 248 | } else if (expr->kind == AST::AST_KIND_PREFIX) { 249 | return compilePrefix(dynamic_cast(expr), 250 | requestedDestinationRegister); 251 | } else if (expr->kind == AST::AST_KIND_TERMINAL) { 252 | return compileTerminal(dynamic_cast(expr), 253 | requestedDestinationRegister, has_lookup_object); 254 | } 255 | return 0; 256 | } 257 | 258 | ExpressionCompiler(Program *program, FunctionKind *function) { 259 | this->program = program; 260 | this->function = function; 261 | } 262 | }; -------------------------------------------------------------------------------- /src/compiler/ast/ArgumentsList.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 22.05.2018. 3 | // 4 | 5 | class ArgumentList : public Expression { 6 | public: 7 | vector *identifiers = new vector(); 8 | void addIdent(TerminalExpression *ident) { 9 | this->identifiers->push_back(ident); 10 | } 11 | 12 | void print() override { 13 | cout << ""; 14 | for (int i = 0; i < identifiers->size(); i++) { 15 | this->identifiers->at(identifiers->size() - 1 - i)->print(); 16 | } 17 | cout << ""; 18 | } 19 | 20 | }; -------------------------------------------------------------------------------- /src/compiler/ast/Body.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 18.05.2018. 3 | // 4 | #include 5 | 6 | class Body : public AST { 7 | 8 | public: 9 | 10 | Body() { 11 | this->kind = AST_KIND_BODY; 12 | } 13 | 14 | vector *statements = new vector(); 15 | 16 | vector* getStatements(){ 17 | /*vector *list = new vector(); 18 | for (int i = 0; i < statements->size(); i++) { 19 | Statement *stmt = statements->at(statements->size() - i - 1); 20 | if (stmt == NULL) continue; 21 | list->push_back(stmt); 22 | }*/ 23 | return statements; 24 | } 25 | 26 | void print() override { 27 | cout << "{\n"; 28 | int count = 0; 29 | for (int i = 0; i < statements->size(); i++) { 30 | AST *stmt = statements->at(i); 31 | if (stmt == NULL) continue; 32 | count++; 33 | cout << "\n" << ""; 34 | stmt->print(); 35 | } 36 | cout << "}\n"; 37 | } 38 | 39 | }; 40 | -------------------------------------------------------------------------------- /src/compiler/ast/ClassDeclaration.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 18.05.2018. 3 | // 4 | 5 | class ClassDeclaration : public FunctionKind { 6 | 7 | public: 8 | char *identifier = NULL; 9 | ArgumentList *arguments = NULL; 10 | Body *body = NULL; 11 | //imported classes map 12 | map importsMap = map(); 13 | 14 | void setIdentifier(const char *data) { 15 | char *temp = (char *) (malloc(sizeof(char) * strlen(data) + 1)); 16 | memset(temp, 0, strlen(data) + 1); 17 | strcpy(temp, data); 18 | this->identifier = temp; 19 | } 20 | 21 | void print() override { 22 | cout << "class " << identifier << "("; 23 | if (arguments) 24 | arguments->print(); 25 | cout << ")\n"; 26 | printSymbolTable(); 27 | body->print(); 28 | cout << "\nend class " << identifier << "\n"; 29 | } 30 | 31 | 32 | }; -------------------------------------------------------------------------------- /src/compiler/ast/Conditional.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 22.05.2018. 3 | // 4 | 5 | class Conditional : public AST { 6 | 7 | public: 8 | 9 | Conditional(){ 10 | this->kind = AST_KIND_CONDITIONAL; 11 | } 12 | 13 | Expression *condition = NULL; 14 | Body *body = NULL; 15 | Body *elseBody = NULL; 16 | 17 | void print() override { 18 | 19 | cout << "if ("; 20 | condition->print(); 21 | cout << ")"; 22 | body->print(); 23 | if (elseBody) { 24 | cout << "else "; 25 | elseBody->print(); 26 | } 27 | } 28 | 29 | }; -------------------------------------------------------------------------------- /src/compiler/ast/Function.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 18.05.2018. 3 | // 4 | 5 | class Function : public FunctionKind { 6 | 7 | public: 8 | 9 | Function() { 10 | this->kind = AST_KIND_FUNCTION; 11 | } 12 | 13 | char *identifier; 14 | ArgumentList *arguments = NULL; 15 | Body *body; 16 | 17 | void setIdentifier(const char *data) { 18 | char *temp = (char *) (malloc(sizeof(char) * strlen(data)+1)); 19 | memset(temp, 0, strlen(data) + 1); 20 | strcpy(temp, data); 21 | this->identifier = temp; 22 | } 23 | 24 | void print() override { 25 | cout << "function " << identifier << "("; 26 | arguments->print(); 27 | cout << ")\n"; 28 | printSymbolTable(); 29 | body->print(); 30 | cout << "\nend function " << identifier << "\n"; 31 | } 32 | 33 | 34 | }; -------------------------------------------------------------------------------- /src/compiler/ast/FunctionKind.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 26.05.2018. 3 | // 4 | 5 | class FunctionKind : public Expression { 6 | 7 | public: 8 | map *symbolTable = new map(); 9 | map *privatesTable = new map(); 10 | map *registerTable = new map(); 11 | vector *functionsToCompile = new vector(); 12 | int_t isStatic = false; 13 | int_t isAsync = false; 14 | int_t isPrivate = false; 15 | 16 | void freeRegister(uint_t index) { 17 | if (index > symbolTable->size()) { 18 | map::iterator it = registerTable->find(index); 19 | if(it != registerTable->end()) 20 | { 21 | (*registerTable)[index] = 1; 22 | } 23 | } 24 | } 25 | 26 | uint_t getRegister(char *ident) { 27 | if (!ident) { 28 | uint_t ret = 0; 29 | uint_t size = symbolTable->size() + 1; 30 | //get a temporary register 31 | for (uint_t i = 0; i < registerTable->size(); i++) { 32 | uint_t index = i + size; 33 | if (registerTable->at(index) == 1) { 34 | //mark as used 35 | (*registerTable)[index] = 0; 36 | ret = index; 37 | break; 38 | } 39 | } 40 | if (!ret) { 41 | uint_t nextReg = registerTable->size() + symbolTable->size() + 1; 42 | (*registerTable)[nextReg] = 0; 43 | ret = nextReg; 44 | } 45 | // cout << "a:" << ret << "\n"; 46 | return ret; 47 | } else { 48 | map::const_iterator pos = symbolTable->find(ident); 49 | if (pos == symbolTable->end()) { 50 | return 0; 51 | } else { 52 | return pos->second; 53 | } 54 | } 55 | } 56 | 57 | void printSymbolTable() { 58 | cout << "======= symbol table ========\n"; 59 | for (auto elem : *symbolTable) { 60 | cout << elem.first << ":" << elem.second << "\n"; 61 | } 62 | cout << "==============================\n"; 63 | } 64 | void printRegisterTable() { 65 | cout << "======= register table ========\n"; 66 | for (auto elem : *registerTable) { 67 | cout << elem.first << ":" << elem.second << "\n"; 68 | } 69 | } 70 | 71 | }; 72 | -------------------------------------------------------------------------------- /src/compiler/ast/Loop.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 22.05.2018. 3 | // 4 | 5 | class Loop : public AST { 6 | 7 | public: 8 | 9 | Loop(){ 10 | this->kind = AST_KIND_LOOP; 11 | } 12 | Expression *startExpr = NULL; 13 | Expression *iterExpr = NULL; 14 | Expression *condition = NULL; 15 | Body *body = NULL; 16 | 17 | void print() override { 18 | 19 | cout << "loop ( "; 20 | if (startExpr) { 21 | startExpr->print(); 22 | } 23 | cout << ";"; 24 | if (condition) { 25 | condition->print(); 26 | } 27 | cout << ";"; 28 | if (iterExpr) { 29 | iterExpr->print(); 30 | } 31 | cout << ") \n"; 32 | if (body) { 33 | body->print(); 34 | } 35 | 36 | } 37 | 38 | }; -------------------------------------------------------------------------------- /src/compiler/ast/Statement.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 18.05.2018. 3 | // 4 | class Statement : public AST { 5 | 6 | public: 7 | 8 | AST* stmt; 9 | bool hasReturn; 10 | bool hasBreak; 11 | bool hasContinue; 12 | 13 | 14 | void print() override { 15 | if(hasReturn) 16 | cout << "return "; 17 | if(hasBreak) 18 | cout << "break "; 19 | if(hasContinue) 20 | cout << "continue "; 21 | stmt->print(); 22 | } 23 | 24 | }; 25 | -------------------------------------------------------------------------------- /src/compiler/ast/TryCatch.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 28.09.2018. 3 | // 4 | 5 | class TryCatch : public AST { 6 | 7 | public: 8 | TryCatch() { 9 | this->kind = AST_KIND_TRY_CATCH; 10 | } 11 | 12 | Body* tryBody; 13 | Body* catchBody; 14 | Body* finallyBody = NULL; 15 | 16 | TerminalExpression* catchIdent; 17 | 18 | void print(){ 19 | 20 | 21 | } 22 | 23 | }; -------------------------------------------------------------------------------- /src/compiler/ast/Var.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 18.05.2018. 3 | // 4 | #include 5 | 6 | class Var : public AST { 7 | 8 | public: 9 | 10 | Var() { 11 | this->kind = AST_KIND_VAR; 12 | } 13 | 14 | char *identifier; 15 | Expression *value; 16 | bool isStatic = false; 17 | bool isPrivate = false; 18 | bool isSynchronized = false; 19 | 20 | void setIdentifier(const char *data) { 21 | char *temp = (char *) (malloc(sizeof(char) * strlen(data) + 1)); 22 | memset(temp, 0, strlen(data) ); 23 | strcpy(temp, data); 24 | this->identifier = temp; 25 | } 26 | 27 | void print() override { 28 | if(isStatic){ 29 | cout << "static "; 30 | } 31 | if(isPrivate){ 32 | cout << "private "; 33 | } 34 | cout << "var " << identifier << " = "; 35 | value->print(); 36 | } 37 | 38 | }; 39 | -------------------------------------------------------------------------------- /src/compiler/ast/ast.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 18.05.2018. 3 | // 4 | 5 | #ifndef ZEROSCRIPT_AST_H 6 | #define ZEROSCRIPT_AST_H 7 | 8 | class AST { 9 | public : 10 | char* filename; 11 | int line_number; 12 | int kind = -1; 13 | 14 | static const int AST_KIND_TERMINAL = 0; 15 | static const int AST_KIND_PREFIX = 1; 16 | static const int AST_KIND_POSTFIX = 2; 17 | static const int AST_KIND_METHOD_CALL = 3; 18 | static const int AST_KIND_EXPRESSION_LIST = 4; 19 | static const int AST_KIND_EMPTY = 5; 20 | static const int AST_KIND_BINARY = 6; 21 | static const int AST_KIND_BODY = 7; 22 | static const int AST_KIND_CLASS = 8; 23 | static const int AST_KIND_FUNCTION = 9; 24 | static const int AST_KIND_LOOP = 10; 25 | static const int AST_KIND_VAR = 11; 26 | static const int AST_KIND_CONDITIONAL = 12; 27 | static const int AST_KIND_TRY_CATCH = 13; 28 | 29 | virtual void print() = 0; 30 | 31 | }; 32 | 33 | #include "expression/expression.h" 34 | #include "Statement.cpp" 35 | #include "Body.cpp" 36 | #include "ArgumentsList.cpp" 37 | #include "FunctionKind.cpp" 38 | #include "ClassDeclaration.cpp" 39 | #include "Function.cpp" 40 | #include "Conditional.cpp" 41 | #include "Var.cpp" 42 | #include "Loop.cpp" 43 | #include "TryCatch.cpp" 44 | 45 | #endif //ZEROSCRIPT_AST_H 46 | -------------------------------------------------------------------------------- /src/compiler/ast/expression/BinaryExpression.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 18.05.2018. 3 | // 4 | 5 | class BinaryExpression : public Expression { 6 | public: 7 | 8 | BinaryExpression() { 9 | this->kind = AST_KIND_BINARY; 10 | } 11 | 12 | char *op; 13 | Expression *left; 14 | Expression *right; 15 | 16 | static BinaryExpression *create(const char *op, stack *astStack) { 17 | BinaryExpression *self = new BinaryExpression(); 18 | self->setOp(op); 19 | Expression *left = dynamic_cast(astStack->top()); 20 | astStack->pop(); 21 | Expression *right = dynamic_cast(astStack->top()); 22 | astStack->pop(); 23 | self->left = right; 24 | self->right = left; 25 | return self; 26 | } 27 | 28 | void setOp(const char *data) { 29 | char *temp = (char *) (malloc(sizeof(char) * strlen(data) + 1)); 30 | strcpy(temp, data); 31 | this->op = temp; 32 | } 33 | 34 | void print() override { 35 | left->print(); 36 | printf(" %s ", op); 37 | right->print(); 38 | } 39 | }; -------------------------------------------------------------------------------- /src/compiler/ast/expression/EmptyExpression.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 22.05.2018. 3 | // 4 | class EmptyExpression : public Expression{ 5 | 6 | public: 7 | 8 | EmptyExpression(){ 9 | this->kind = AST_KIND_EMPTY; 10 | } 11 | 12 | void print() override { 13 | } 14 | 15 | }; 16 | -------------------------------------------------------------------------------- /src/compiler/ast/expression/Expression.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 18.05.2018. 3 | // 4 | 5 | class Expression : public AST{ 6 | 7 | public: 8 | 9 | int registerNumber; 10 | 11 | void print() override { 12 | } 13 | 14 | }; -------------------------------------------------------------------------------- /src/compiler/ast/expression/ExpressionList.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 18.05.2018. 3 | // 4 | 5 | class ExpressionList : public Expression { 6 | 7 | public: 8 | vector *expressions = new vector(); 9 | void addExpression(Expression *expr) { 10 | this->expressions->push_back(expr); 11 | } 12 | 13 | void print() override { 14 | cout << ""; 15 | for (int i = 0; i < expressions->size(); i++) { 16 | this->expressions->at(expressions->size() - 1 - i)->print(); 17 | } 18 | cout << ""; 19 | } 20 | 21 | }; -------------------------------------------------------------------------------- /src/compiler/ast/expression/MethodCall.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 18.05.2018. 3 | // 4 | 5 | class MethodCall : public Expression { 6 | 7 | public: 8 | 9 | MethodCall(){ 10 | this->kind = AST_KIND_METHOD_CALL; 11 | } 12 | 13 | Expression* callee; 14 | ExpressionList* argumentsList; 15 | 16 | void print() override { 17 | callee->print(); 18 | printf("("); 19 | argumentsList->print(); 20 | printf(")"); 21 | } 22 | 23 | }; -------------------------------------------------------------------------------- /src/compiler/ast/expression/PostfixExpression.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 18.05.2018. 3 | // 4 | 5 | class PostfixExpression : public Expression { 6 | public: 7 | char *op; 8 | Expression* expr; 9 | 10 | PostfixExpression(){ 11 | this->kind = AST_KIND_POSTFIX; 12 | } 13 | 14 | static PostfixExpression *create(const char *op, stack *astStack) { 15 | PostfixExpression *self = new PostfixExpression(); 16 | self->setOp(op); 17 | Expression *expr = dynamic_cast(astStack->top()); 18 | astStack->pop(); 19 | self->expr = expr; 20 | return self; 21 | } 22 | 23 | void setOp(const char *data) { 24 | char *temp = (char *) (malloc(sizeof(char) * strlen(data) + 1)); 25 | strcpy(temp, data); 26 | this->op = temp; 27 | } 28 | 29 | void print() override { 30 | expr->print(); 31 | printf(" %s ", op); 32 | } 33 | }; -------------------------------------------------------------------------------- /src/compiler/ast/expression/PrefixExpression.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 18.05.2018. 3 | // 4 | 5 | class PrefixExpression : public Expression { 6 | public: 7 | char *op; 8 | Expression *expr; 9 | 10 | PrefixExpression() { 11 | this->kind = AST_KIND_PREFIX; 12 | } 13 | 14 | static PrefixExpression *create(const char *op, stack *astStack) { 15 | PrefixExpression *self = new PrefixExpression(); 16 | self->setOp(op); 17 | Expression *expr = dynamic_cast(astStack->top()); 18 | astStack->pop(); 19 | self->expr = expr; 20 | return self; 21 | } 22 | 23 | void setOp(const char *data) { 24 | char *temp = (char *) (malloc(sizeof(char) * strlen(data) + 1)); 25 | strcpy(temp, data); 26 | this->op = temp; 27 | } 28 | 29 | void print() override { 30 | printf(" %s ", op); 31 | expr->print(); 32 | } 33 | }; -------------------------------------------------------------------------------- /src/compiler/ast/expression/TerminalExpression.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 18.05.2018. 3 | // 4 | 5 | class TerminalExpression : public Expression { 6 | 7 | private: 8 | 9 | static TerminalExpression *getOne(const char *data) { 10 | TerminalExpression *self = new TerminalExpression(); 11 | char *temp = (char *) (malloc(sizeof(char) * strlen(data) + 1)); 12 | memset(temp, 0, strlen(data) + 1); 13 | strcpy(temp, data); 14 | self->data = temp; 15 | return self; 16 | } 17 | 18 | public: 19 | static const int TYPE_IDENTIFIER = 0; 20 | static const int TYPE_NUMBER = 1; 21 | static const int TYPE_STRING = 2; 22 | 23 | TerminalExpression() { 24 | kind = AST::AST_KIND_TERMINAL; 25 | } 26 | 27 | static TerminalExpression *identifier(const char *data) { 28 | TerminalExpression *self = getOne(data); 29 | self->type = TYPE_IDENTIFIER; 30 | return self; 31 | } 32 | 33 | static TerminalExpression *number(const char *data) { 34 | TerminalExpression *self = getOne(data); 35 | self->type = TYPE_NUMBER; 36 | return self; 37 | } 38 | 39 | static TerminalExpression *stringWithoutTrim(const char *data) { 40 | TerminalExpression *self = getOne(data); 41 | self->type = TYPE_STRING; 42 | return self; 43 | } 44 | 45 | static TerminalExpression *string(const char *data) { 46 | TerminalExpression *self = getOne(data + 1); 47 | self->type = TYPE_STRING; 48 | self->data[strlen(self->data) - 1] = 0; 49 | return self; 50 | } 51 | 52 | int type; 53 | char *data; 54 | 55 | void print() override { 56 | printf(" %s ", data); 57 | } 58 | 59 | private: 60 | 61 | const char *typeToStr(int type) { 62 | if (type == TYPE_IDENTIFIER) return "IDENT"; 63 | if (type == TYPE_NUMBER) return "NUM"; 64 | if (type == TYPE_STRING) return "STR"; 65 | } 66 | 67 | }; 68 | -------------------------------------------------------------------------------- /src/compiler/ast/expression/expression.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 18.05.2018. 3 | // 4 | 5 | #ifndef ZEROSCRIPT_EXPRESSION_H 6 | #define ZEROSCRIPT_EXPRESSION_H 7 | 8 | #include "Expression.cpp" 9 | #include "EmptyExpression.cpp" 10 | #include "BinaryExpression.cpp" 11 | #include "PrefixExpression.cpp" 12 | #include "PostfixExpression.cpp" 13 | #include "TerminalExpression.cpp" 14 | #include "ExpressionList.cpp" 15 | #include "MethodCall.cpp" 16 | 17 | #endif //ZEROSCRIPT_EXPRESSION_H 18 | -------------------------------------------------------------------------------- /src/compiler/compiler.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 18.05.2018. 3 | // 4 | 5 | #ifndef ZEROSCRIPT_COMPILER_H 6 | #define ZEROSCRIPT_COMPILER_H 7 | 8 | #include "map" 9 | #include "ast/ast.h" 10 | //#include "../program/program.h" 11 | #include "../program/Program.cpp" 12 | #include "../program/Assembler.cpp" 13 | #include "parser/parser.h" 14 | #include "AstGenerator.cpp" 15 | #include "ExprssionCompiler.cpp" 16 | #include "AddressCalculator.cpp" 17 | #endif //ZEROSCRIPT_COMPILER_H 18 | -------------------------------------------------------------------------------- /src/compiler/parser/parser.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 18.05.2018. 3 | // 4 | 5 | #ifndef ZEROSCRIPT_PARSER_H 6 | #define ZEROSCRIPT_PARSER_H 7 | 8 | #include "generated/zeroscriptLexer.h" 9 | #include "generated/zeroscriptLexer.cpp" 10 | #include "generated/zeroscriptParser.h" 11 | #include "generated/zeroscriptParser.cpp" 12 | #include "generated/zeroscriptBaseListener.h" 13 | 14 | #endif //ZEROSCRIPT_PARSER_H 15 | -------------------------------------------------------------------------------- /src/interpreter/event_queue.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 12.10.2018. 3 | // 4 | arraylist_t *event_queue = NULL; 5 | 6 | void call_next_event(); 7 | 8 | void wait_for_event() { 9 | while (event_queue) { 10 | if (event_queue->size) { 11 | call_next_event(); 12 | } 13 | }; 14 | } 15 | 16 | void enqueue_event(int_t *function) { 17 | arraylist_push(event_queue, function); 18 | } 19 | 20 | void call_next_event() { 21 | z_object_t *function_ref = *(z_object_t **) arraylist_get(event_queue, 0); 22 | z_interpreter_state_t *other_state = function_ref->function_ref_object.responsible_interpreter_state; 23 | z_object_t *called_fnc = context_new(); 24 | called_fnc->context_object.parent_context = function_ref->function_ref_object.parent_context; 25 | called_fnc->context_object.return_context = NULL; 26 | called_fnc->context_object.return_address = NULL; 27 | called_fnc->context_object.requested_return_register_index = NULL; 28 | other_state->instruction_pointer = (function_ref->function_ref_object.start_address); 29 | other_state->current_context = called_fnc; 30 | other_state = z_interpreter_run(other_state); 31 | if (other_state->return_code) { 32 | error_and_exit(other_state->exception_details); 33 | } 34 | if (event_queue) 35 | arraylist_remove_index(event_queue, 0); 36 | } -------------------------------------------------------------------------------- /src/interpreter/garbage_collector.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 07.10.2018. 3 | // 4 | 5 | #ifndef ZEROSCRIPT_GARBAGE_COLLECTOR_H 6 | #define ZEROSCRIPT_GARBAGE_COLLECTOR_H 7 | 8 | #include "object.h" 9 | 10 | void gc_visit_state(z_interpreter_state_t *state); 11 | 12 | void gc_visit_object(z_object_t *object); 13 | 14 | void gc_visit_object_with_map(z_object_t *object); 15 | 16 | void gc_visit_context(const z_object_t *context); 17 | 18 | void gc_visit_instance(z_object_t *object); 19 | 20 | void gc_visit_function_ref(z_object_t *object); 21 | 22 | void gc_free_object(z_object_t *object); 23 | 24 | void gc_visit_stack(z_reg_t *stack_start, z_reg_t *stack_ptr); 25 | 26 | void gc_visit_register(z_reg_t *reg); 27 | 28 | uhalf_int_t gc_version = 0; 29 | 30 | int_t gc() { 31 | gc_version++; 32 | // iterate all the objects 33 | for (int_t i = 0; i < interpreter_states_list->size; i++) { 34 | z_interpreter_state_t *state = *(z_interpreter_state_t **) arraylist_get(interpreter_states_list, i); 35 | // if it is a removed gc root, skip it 36 | if (!state->removed_as_a_root) { 37 | gc_visit_state(state); 38 | } 39 | } 40 | // iterate static variables 41 | arraylist_t* known_types = object_manager_get_known_classes(); 42 | for(int i = 0; i < known_types->size;i++){ 43 | z_type_info_t* type = (z_type_info_t*)arraylist_get(known_types,i); 44 | map_t* static_variable_map = type->static_variables; 45 | arraylist_t* static_variable_names = map_key_list(static_variable_map); 46 | for(int j = 0;jsize;j++){ 47 | gc_visit_register((z_reg_t*)map_get(static_variable_map,*(char**)arraylist_get(static_variable_names,j))); 48 | } 49 | } 50 | 51 | arraylist_t *new_list = arraylist_new_capacity(sizeof(int_t),gc_objects_list->size ); 52 | for (int_t i = 0; i < gc_objects_list->size; i++) { 53 | z_object_t *object = *(z_object_t **) arraylist_get(gc_objects_list, i); 54 | if (object->gc_version != gc_version) { 55 | gc_free_object(object); 56 | } else { 57 | arraylist_push(new_list, &object); 58 | } 59 | } 60 | z_free(gc_objects_list->data); 61 | z_free(gc_objects_list); 62 | gc_objects_list = new_list; 63 | } 64 | 65 | void gc_free_object(z_object_t *object) { 66 | switch (object->type) { 67 | case TYPE_STR: 68 | z_free(object->string_object.value); 69 | break; 70 | case TYPE_CONTEXT: 71 | if (object->context_object.locals) 72 | z_free(object->context_object.locals); 73 | if (object->context_object.catches_list) 74 | arraylist_free(object->context_object.catches_list); 75 | if (object->context_object.symbol_table) 76 | map_free(object->context_object.symbol_table); 77 | break; 78 | case TYPE_FUNCTION_REF: 79 | break; 80 | case TYPE_CLASS_REF: 81 | break; 82 | case TYPE_INSTANCE: 83 | z_free(object->instance_object.saved_state); 84 | break; 85 | case TYPE_OBJ: 86 | map_free(object->properties); 87 | if (object->key_list_cache) { 88 | z_free(object->key_list_cache); 89 | } 90 | break; 91 | } 92 | z_free(object); 93 | } 94 | 95 | void gc_visit_state(z_interpreter_state_t *state) { 96 | z_object_t *root_context = (z_object_t *) (state->root_context); 97 | z_object_t *context = (z_object_t *) (state->current_context); 98 | gc_visit_object(context); 99 | gc_visit_object(root_context); 100 | gc_visit_stack(state->stack_start, state->stack_ptr); 101 | } 102 | 103 | void gc_visit_stack(z_reg_t *stack_start, z_reg_t *stack_ptr) { 104 | for (int_t i = 0; i < stack_ptr - stack_start; i++) { 105 | z_reg_t reg = stack_start[i + 1]; 106 | gc_visit_register(®); 107 | } 108 | } 109 | 110 | void gc_visit_register(z_reg_t *reg) { 111 | if (reg->type != TYPE_NUMBER && reg->type != TYPE_NATIVE_FUNC) { 112 | z_object_t *value = (z_object_t *) reg->val; 113 | gc_visit_object(value); 114 | } 115 | } 116 | 117 | void gc_visit_context(const z_object_t *context) { 118 | z_reg_t *locals = (z_reg_t *) (context->context_object.locals); 119 | uint_t locals_count = context->context_object.locals_count; 120 | for (int_t i = 2; i < locals_count; i++) { 121 | z_reg_t reg = locals[i]; 122 | gc_visit_register(®); 123 | } 124 | gc_visit_object((z_object_t *) (context->context_object.parent_context)); 125 | gc_visit_object((z_object_t *) (context->context_object.return_context)); 126 | } 127 | 128 | void gc_visit_object(z_object_t *object) { 129 | if (!object) return; 130 | if (object->gc_version == gc_version) return; 131 | object->gc_version = gc_version; 132 | switch (object->type) { 133 | case TYPE_OBJ: 134 | gc_visit_object_with_map(object); 135 | break; 136 | case TYPE_CONTEXT: 137 | gc_visit_context(object); 138 | break; 139 | case TYPE_INSTANCE: 140 | gc_visit_instance(object); 141 | break; 142 | case TYPE_FUNCTION_REF: 143 | gc_visit_function_ref(object); 144 | break; 145 | } 146 | } 147 | 148 | void gc_visit_function_ref(z_object_t *object) { 149 | gc_visit_object((z_object_t *) (object->function_ref_object.parent_context)); 150 | gc_visit_state(object->function_ref_object.responsible_interpreter_state); 151 | } 152 | 153 | void gc_visit_instance(z_object_t *object) { 154 | gc_visit_state(object->instance_object.saved_state); 155 | } 156 | 157 | void gc_visit_object_with_map(z_object_t *object) { 158 | map_t *self = object->properties; 159 | for (int i = 0; i < MAP_BAG_SIZE; i++) { 160 | arraylist_t *kbag = self->keys[i]; 161 | arraylist_t *vbag = self->values[i]; 162 | if (kbag) { 163 | for (int_t j = 0; j < kbag->size; j++) { 164 | gc_visit_register((z_reg_t *) arraylist_get(vbag, j)); 165 | } 166 | } 167 | } 168 | } 169 | 170 | #endif //ZEROSCRIPT_GARBAGE_COLLECTOR_H 171 | -------------------------------------------------------------------------------- /src/interpreter/native_functions.c: -------------------------------------------------------------------------------- 1 | #include "object.h" 2 | 3 | // 4 | // Created by onur on 10.06.2018. 5 | // 6 | typedef struct z_native_return_value (*z_native_fnc_t)(z_reg_t *, z_reg_t *, z_object_t *); 7 | 8 | #define RETURN_NATIVE(s, e) return (z_native_return_value) {s,e}; 9 | 10 | map_t *native_functions = NULL; 11 | 12 | void z_bind_native_function(char *function_name, z_native_fnc_t fnc) { 13 | map_insert(native_functions, function_name, &fnc); 14 | } 15 | 16 | void z_bind_native_string_function(char *function_name, z_native_fnc_t fnc) { 17 | if (!string_native_properties_map) { 18 | string_native_properties_map = map_new(sizeof(z_reg_t)); 19 | } 20 | z_reg_t temp; 21 | temp.type = TYPE_NATIVE_FUNC; 22 | temp.val = (int_t) fnc; 23 | map_insert_non_enumerable(string_native_properties_map, function_name, &temp); 24 | } 25 | 26 | z_native_return_value native_exit(z_reg_t *stack, z_reg_t *return_reg, z_object_t *ignore) { 27 | exit((int) (stack--)->val); 28 | } 29 | 30 | z_native_return_value native_gc(z_reg_t *stack, z_reg_t *return_reg, z_object_t *ignore) { 31 | if (used_heap > heap_limit) { 32 | schedule_gc(); 33 | } 34 | RETURN_NATIVE(stack, NULL); 35 | } 36 | 37 | z_native_return_value native_enqueue(z_reg_t *stack, z_reg_t *return_reg, z_object_t *ignore) { 38 | if (!event_queue) { 39 | event_queue = arraylist_new(sizeof(int_t)); 40 | } 41 | z_reg_t *arg = stack--; 42 | enqueue_event(&arg->val); 43 | RETURN_NATIVE(stack, NULL); 44 | } 45 | 46 | z_native_return_value native_number(z_reg_t *stack, z_reg_t *return_reg, z_object_t *str) { 47 | z_reg_t *arg = stack--; 48 | if (arg->type == TYPE_NUMBER) { 49 | return_reg->number_val = arg->number_val; 50 | } else { 51 | return_reg->number_val = (FLOAT) (atof(((z_object_t *) arg->val)->operations.to_string((void *) arg->val))); 52 | } 53 | return_reg->type = TYPE_NUMBER; 54 | RETURN_NATIVE(stack, NULL); 55 | } 56 | 57 | z_native_return_value native_strlen(z_reg_t *stack, z_reg_t *return_reg, z_object_t *str) { 58 | return_reg->type = TYPE_NUMBER; 59 | return_reg->number_val = strlen(str->operations.to_string(str)); 60 | RETURN_NATIVE(stack, NULL); 61 | } 62 | 63 | z_native_return_value native_assert(z_reg_t *stack, z_reg_t *return_reg, z_object_t *ignore) { 64 | z_reg_t *arg = stack--; 65 | if(arg->type == TYPE_NUMBER && arg->number_val == 1){ 66 | RETURN_NATIVE(stack, NULL); 67 | } else 68 | RETURN_NATIVE(stack, "assertion failed"); 69 | } 70 | 71 | z_native_return_value native_str_equals(z_reg_t *stack, z_reg_t *return_reg, z_object_t *str) { 72 | z_reg_t *arg = stack--; 73 | char *other = 0; 74 | int_t ret = 0; 75 | if (arg->type != TYPE_NUMBER) { 76 | other = ((z_object_t *) arg->val)->operations.to_string((void *) arg->val); 77 | ret = strcmp(other, str->operations.to_string(str)) == 0; 78 | } 79 | return_reg->type = TYPE_NUMBER; 80 | return_reg->number_val = ret; 81 | RETURN_NATIVE(stack, NULL); 82 | } 83 | 84 | z_native_return_value native_str_startswith(z_reg_t *stack, z_reg_t *return_reg, z_object_t *str) { 85 | z_reg_t *arg = stack--; 86 | char *other = 0; 87 | int_t ret = 0; 88 | if (arg->type != TYPE_NUMBER) { 89 | other = ((z_object_t *) arg->val)->operations.to_string((void *) arg->val); 90 | char *this_str = str->operations.to_string(str); 91 | ret = strncmp(other, this_str, strlen(other)) == 0; 92 | } 93 | return_reg->type = TYPE_NUMBER; 94 | return_reg->number_val = ret; 95 | RETURN_NATIVE(stack, NULL); 96 | } 97 | 98 | z_native_return_value native_str_substring(z_reg_t *stack, z_reg_t *return_reg, z_object_t *str) { 99 | z_reg_t *arg = stack--; 100 | char *this_str = str->operations.to_string(str); 101 | if (arg->type == TYPE_NUMBER) { 102 | char *new_str = (this_str + (int_t) arg->number_val); 103 | char *copied = (char *) z_alloc_or_die(strlen(new_str) + 1); 104 | strcpy(copied, new_str); 105 | return_reg->val = (int_t) string_new(copied); 106 | return_reg->type = TYPE_STR; 107 | ADD_OBJECT_TO_GC_LIST(return_reg); 108 | RETURN_NATIVE(stack, NULL); 109 | } 110 | return_reg->type = TYPE_NUMBER; 111 | return_reg->number_val = 0; 112 | RETURN_NATIVE(stack, NULL); 113 | } 114 | 115 | z_native_return_value native_object_new(z_reg_t *stack, z_reg_t *return_reg, z_object_t *ignore) { 116 | return_reg->val = (int_t) object_new(NULL, NULL, NULL, NULL, NULL); 117 | return_reg->type = TYPE_OBJ; 118 | ADD_OBJECT_TO_GC_LIST(return_reg->val); 119 | RETURN_NATIVE(stack, NULL); 120 | } 121 | 122 | z_native_return_value native_object_key_size(z_reg_t *stack, z_reg_t *return_reg, z_object_t *object) { 123 | return_reg->number_val = object->properties->size; 124 | return_reg->type = TYPE_NUMBER; 125 | RETURN_NATIVE(stack, NULL); 126 | } 127 | 128 | z_native_return_value native_object_key_list(z_reg_t *stack, z_reg_t *return_reg, z_object_t *object) { 129 | z_object_t *ret = (z_object_t *) object->key_list_cache; 130 | if (!ret) { 131 | ret = object_new(NULL, NULL, NULL, NULL, NULL); 132 | arraylist_t *keys = map_key_list(object->properties); 133 | for (int_t i = 0; i < keys->size; i++) { 134 | char *value = *(char **) arraylist_get(keys, i); 135 | char *copied = (char *) z_alloc_or_gc(strlen(value) + 1); 136 | strcpy(copied, value); 137 | z_reg_t value_reg; 138 | value_reg.val = (int_t) string_new(copied); 139 | value_reg.type = TYPE_STR; 140 | map_insert(ret->properties, num_to_str(i), &value_reg); 141 | ADD_OBJECT_TO_GC_LIST(value_reg.val); 142 | } 143 | object->key_list_cache = ret; 144 | ret->properties->is_immutable = 1; 145 | } 146 | return_reg->val = (int_t) ret; 147 | return_reg->type = TYPE_OBJ; 148 | RETURN_NATIVE(stack, NULL); 149 | } 150 | 151 | z_native_return_value native_readln(z_reg_t *stack, z_reg_t *return_reg, z_object_t *ignore) { 152 | char c; 153 | size_t length = 0; 154 | // for decoration 155 | char *buff = (char *) Z_MALLOC(sizeof(uint_t)); 156 | // extremely inefficient but who cares 157 | while ((c = getchar()) != -1 && c != '\n') { 158 | length++; 159 | buff = (char *) Z_REALLOC(buff, length + sizeof(uint_t)); 160 | buff[length - 1 + sizeof(uint_t)] = c; 161 | } 162 | USED_HEAP_LOCK 163 | used_heap += length + sizeof(uint_t); 164 | USED_HEAP_UNLOCK 165 | buff[length + sizeof(uint_t)] = 0; 166 | return_reg->val = (int_t) string_new((char *) z_decorate_ptr(buff, length)); 167 | return_reg->type = TYPE_STR; 168 | ADD_OBJECT_TO_GC_LIST(return_reg->val); 169 | RETURN_NATIVE(stack, NULL); 170 | } 171 | 172 | z_native_return_value native_print(z_reg_t *stack, z_reg_t *return_reg, z_object_t *ignore) { 173 | z_reg_t *arg = stack--; 174 | switch (arg->type) { 175 | case TYPE_NUMBER: { 176 | #ifdef FLOAT_SUPPORT 177 | FLOAT val = (FLOAT) arg->number_val; 178 | int_t ival = (int_t) val; 179 | if (val == ival) { 180 | printf("%d\n", ival); 181 | } else { 182 | printf("%f\n", val); 183 | } 184 | #else 185 | printf("%d\n", arg->val); 186 | #endif 187 | break; 188 | } 189 | case TYPE_STR: { 190 | puts(((z_object_t *) arg->val)->string_object.value); 191 | break; 192 | } 193 | default: 194 | puts(((z_object_t *) arg->val)->operations.to_string((void *) arg->val)); 195 | } 196 | RETURN_NATIVE(stack, NULL); 197 | } 198 | 199 | z_native_return_value native_to_int(z_reg_t *stack, z_reg_t *return_reg, z_object_t *object) { 200 | z_reg_t *arg = stack--; 201 | return_reg->number_val = ((int_t) arg->number_val); 202 | return_reg->type = TYPE_NUMBER; 203 | RETURN_NATIVE(stack, NULL); 204 | } 205 | 206 | 207 | void z_native_funcions_init() { 208 | native_functions = map_new(sizeof(z_native_fnc_t)); 209 | 210 | z_bind_native_function("print", native_print); 211 | z_bind_native_function("Object", native_object_new); 212 | z_bind_native_function("number", native_number); 213 | z_bind_native_function("int", native_to_int); 214 | z_bind_native_function("gc", native_gc); 215 | z_bind_native_function("assert", native_assert); 216 | z_bind_native_function("exit", native_exit); 217 | z_bind_native_function("read", native_readln); 218 | 219 | z_bind_native_string_function("length", native_strlen); 220 | z_bind_native_string_function("startsWith", native_str_startswith); 221 | z_bind_native_string_function("equals", native_str_equals); 222 | z_bind_native_string_function("substring", native_str_substring); 223 | 224 | } 225 | -------------------------------------------------------------------------------- /src/interpreter/object.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 11.07.2018. 3 | // 4 | 5 | #ifndef ZEROSCRIPT_OBJECT_C 6 | #define ZEROSCRIPT_OBJECT_C 7 | 8 | #include "object.h" 9 | 10 | z_object_t *string_new(char *data); 11 | 12 | void load_class_code(const char *class_name, char **bytes, size_t *fsize); 13 | 14 | void object_manager_register_object_type(char *class_name, char *bytecodes, int_t size); 15 | 16 | char *resolve_imported_class_name(char *class_name, map_t *imports_table); 17 | 18 | arraylist_t* known_types_list = 0; 19 | 20 | #include "garbage_collector.h" 21 | 22 | char *class_path = 0; 23 | 24 | /** 25 | * caller is responsible for freeing the pointer; 26 | * @param _self object pointer. 27 | * @return a string representation of the object. 28 | */ 29 | char *obj_to_string(void *_self) { 30 | char *buff = (char *) z_alloc_or_gc(20); 31 | snprintf(buff, 20, "obj@%p", _self); 32 | return buff; 33 | } 34 | 35 | static map_t *known_types_map = NULL; 36 | static z_native_fnc_t *native_keysize_wrapper = NULL; 37 | static z_native_fnc_t *native_keylist_wrapper = NULL; 38 | 39 | 40 | char *str_to_string(void *_self) { 41 | return ((z_object_t *) _self)->string_object.value; 42 | } 43 | 44 | struct operations string_operations = { 45 | str_to_string 46 | }; 47 | struct operations object_operations = { 48 | obj_to_string 49 | }; 50 | 51 | /** 52 | * initialize the object manager with the specified class path. 53 | * @param cp 54 | */ 55 | void object_manager_init(char *cp) { 56 | gc_objects_list = arraylist_new(sizeof(int_t)); 57 | interpreter_states_list = arraylist_new(sizeof(int_t)); 58 | z_native_funcions_init(); 59 | 60 | class_path = cp; 61 | if (cp == NULL) { 62 | class_path = "."; 63 | } 64 | 65 | known_types_map = map_new(sizeof(z_type_info_t)); 66 | known_types_list = arraylist_new(sizeof(z_type_info_t)); 67 | } 68 | 69 | /** 70 | * tries to retrieve a known type. if fails, loads it. 71 | * 72 | * @param class_name class -or type- name to load. 73 | * @return z_type_info_t. 74 | */ 75 | z_type_info_t *object_manager_get_or_load_type_info(char *class_name, map_t *imports_table) { 76 | z_type_info_t *type_info = (z_type_info_t *) map_get(known_types_map, class_name); 77 | if (type_info == NULL) { 78 | char *bytes = 0; 79 | size_t fsize; 80 | char *alias = class_name; 81 | char *resolved = resolve_imported_class_name(class_name, imports_table); 82 | load_class_code(resolved, &bytes, &fsize); 83 | object_manager_register_object_type(class_name, bytes, fsize); 84 | interpreter_run_static_constructor(bytes, fsize, class_name); 85 | type_info = (z_type_info_t *) map_get(known_types_map, class_name); 86 | map_insert(type_info->imports_table, alias, &resolved); 87 | } 88 | return type_info; 89 | } 90 | 91 | arraylist_t* object_manager_get_known_classes(){ 92 | return known_types_list; 93 | } 94 | 95 | char *resolve_imported_class_name(char *class_name, map_t *imports_table) { 96 | if (imports_table != NULL) { 97 | char **imported_path_ptr = (char **) map_get(imports_table, class_name); 98 | if (imported_path_ptr != NULL) { 99 | class_name = *imported_path_ptr; 100 | } 101 | } 102 | return class_name; 103 | } 104 | 105 | /** 106 | * register a type. 107 | * @param class_name name of this type. 108 | * @param bytecodes compiled byte codes. 109 | * @param size size of bytecodes. 110 | */ 111 | void object_manager_register_object_type(char *class_name, char *bytecodes, int_t size) { 112 | z_type_info_t *type_info = (z_type_info_t *) (z_alloc_or_gc(sizeof(z_type_info_t))); 113 | type_info->bytecode_stream = bytecodes; 114 | type_info->class_name = class_name; 115 | type_info->bytecode_size = size; 116 | type_info->static_variables = map_new(sizeof(z_reg_t)); 117 | type_info->imports_table = map_new(sizeof(char *)); 118 | map_insert(known_types_map, class_name, type_info); 119 | arraylist_push(known_types_list, type_info); 120 | } 121 | 122 | /** 123 | * create an object with the specified class name. 124 | * 125 | * if the class name is null, creates a json object. * 126 | * else loads the class -if not loaded yet- and creates an instance of it. 127 | * 128 | * @param class_name class name to load. can be null. 129 | * @return z_object. 130 | */ 131 | Z_INLINE z_object_t * 132 | object_new(char *class_name, map_t *imports_table, z_reg_t *stack_start, z_reg_t *stack_ptr, z_reg_t *gc_fix) { 133 | z_object_t *obj = (z_object_t *) z_alloc_or_gc(sizeof(z_object_t)); 134 | obj->key_list_cache = NULL; 135 | obj->gc_version = 0; 136 | obj->operations = object_operations; 137 | if (class_name) { 138 | z_type_info_t *object_type_info = object_manager_get_or_load_type_info(class_name, imports_table); 139 | char *bytes = 0; 140 | size_t fsize; 141 | if (object_type_info == NULL) { 142 | //search on classpath and load 143 | load_class_code(class_name, &bytes, &fsize); 144 | object_manager_register_object_type(class_name, bytes, fsize); 145 | object_type_info = (z_type_info_t *) map_get(known_types_map, class_name); 146 | } 147 | obj->instance_object.type_info = object_type_info; 148 | z_interpreter_state_t *initial_state = interpreter_state_new( 149 | NULL, 150 | obj->instance_object.type_info->bytecode_stream, 151 | obj->instance_object.type_info->bytecode_size, 152 | class_name, 153 | stack_ptr, 154 | stack_start 155 | ); 156 | gc_fix->val = (int_t) obj; 157 | gc_fix->type = TYPE_INSTANCE; 158 | obj->instance_object.saved_state = initial_state; 159 | obj->type = TYPE_INSTANCE; 160 | ADD_OBJECT_TO_GC_LIST(obj); 161 | z_interpreter_set_arguments_count(initial_state,0); 162 | z_interpreter_run(initial_state); 163 | return obj; 164 | } 165 | obj->properties = map_new(sizeof(z_reg_t)); 166 | obj->type = TYPE_OBJ; 167 | z_reg_t temp; 168 | temp.type = TYPE_NATIVE_FUNC; 169 | temp.val = (int_t) native_object_key_size; 170 | map_insert_non_enumerable(obj->properties, "size", &temp); 171 | temp.val = (int_t) native_object_key_list; 172 | map_insert_non_enumerable(obj->properties, "keys", &temp); 173 | return obj; 174 | } 175 | 176 | /** 177 | * loads a class into known types map. 178 | * 179 | * first checks if a compiles .zcl file is exists. if so, loads. 180 | * if not, tries to find the source file and compile it. 181 | * 182 | * @param class_name class name to load. 183 | * @param bytes pointer to pointer of compiled bytes. 184 | * @param fsize pointer to file size. 185 | */ 186 | void load_class_code(const char *class_name, char **bytes, size_t *fsize) { 187 | size_t size = (int_t) strlen(class_name) + (int_t) strlen(class_path) + 5; 188 | char *file_to_load = (char *) z_alloc_or_gc(size); 189 | snprintf(file_to_load, size, "%s/%s.zcl", class_path, class_name); 190 | FILE *f = fopen(file_to_load, "rb"); 191 | if (f == NULL) { 192 | #ifndef NO_DYNAMIC_COMPILATION 193 | //maybe not compiled yet? 194 | snprintf(file_to_load, size, "%s/%s.zs", class_path, class_name); 195 | f = fopen(file_to_load, "rb"); 196 | if (f == NULL) { 197 | error_and_exit_fmt("cannot find class or variable %s",class_name); 198 | } else { 199 | fclose(f); 200 | *bytes = compile_file(file_to_load, fsize); 201 | } 202 | #else 203 | error_and_exit("cannot find class"); 204 | #endif 205 | } else { 206 | fseek(f, 0, SEEK_END); 207 | (*fsize) = (size_t) ftell(f); 208 | fseek(f, 0, SEEK_SET); 209 | (*bytes) = (char *) z_alloc_or_gc((*fsize) + 1); 210 | fread((*bytes), (*fsize), 1, f); 211 | fclose(f); 212 | } 213 | } 214 | /** 215 | * create a function call context object. 216 | * @return z_object_t. 217 | */ 218 | Z_INLINE z_object_t *context_new() { 219 | z_object_t *obj = (z_object_t *) z_alloc_or_gc(sizeof(z_object_t)); 220 | memset(obj, 0, sizeof(z_object_t)); 221 | obj->type = TYPE_CONTEXT; 222 | return obj; 223 | } 224 | 225 | Z_INLINE z_object_t * 226 | function_ref_new(uint_t start_addr, void *parent_context, z_interpreter_state_t *state, uint_t is_async) { 227 | z_object_t *obj = (z_object_t *) z_alloc_or_gc(sizeof(z_object_t)); 228 | obj->function_ref_object.start_address = start_addr; 229 | obj->function_ref_object.parent_context = parent_context; 230 | obj->function_ref_object.responsible_interpreter_state = state; 231 | obj->function_ref_object.is_async = is_async; 232 | obj->operations = object_operations; 233 | obj->gc_version = 0; 234 | obj->type = TYPE_FUNCTION_REF; 235 | return obj; 236 | } 237 | 238 | Z_INLINE z_object_t *class_ref_new(char *name) { 239 | z_object_t *obj = (z_object_t *) z_alloc_or_gc(sizeof(z_object_t)); 240 | obj->class_ref_object.value = name; 241 | obj->operations = object_operations; 242 | obj->gc_version = 0; 243 | obj->type = TYPE_CLASS_REF; 244 | return obj; 245 | } 246 | 247 | 248 | z_object_t *string_new(char *data) { 249 | z_object_t *obj = (z_object_t *) z_alloc_or_gc(sizeof(z_object_t)); 250 | obj->string_object.value = data; 251 | obj->operations = string_operations; 252 | obj->properties = string_native_properties_map; 253 | obj->type = TYPE_STR; 254 | obj->gc_version = 0; 255 | //ADD_OBJECT_TO_GC_LIST(obj); 256 | return obj; 257 | } 258 | 259 | map_t *build_symbol_table(const char *data) { 260 | map_t *symbol_table = map_new(sizeof(int_t)); 261 | int_t pos = 0; 262 | uint_t size = *((uint_t *) (data + pos)); 263 | pos += sizeof(int_t); 264 | for (int_t i = 0; i < size; i++) { 265 | int_t value = i + 1; 266 | // used strdup because bytestream is copied and freed during calls. this will cause invalid reads from freed sections. 267 | char *item = strdup((char *) (data + pos)); 268 | uint_t size_of_item = strlen(item); 269 | pos += size_of_item + 2; //1 for isPrivate 270 | char is_private = *(char *) (data + pos - 1); 271 | int_t flag = 0 | MAP_FLAG_ENUMERABLE; 272 | if (is_private) { 273 | flag |= MAP_FLAG_PRIVATE; 274 | } 275 | map_insert_flags(symbol_table, item, &value, flag); 276 | } 277 | return symbol_table; 278 | } 279 | 280 | 281 | #endif //ZEROSCRIPT_OBJECT_C 282 | -------------------------------------------------------------------------------- /src/interpreter/object.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 16.07.2018. 3 | // 4 | 5 | #ifndef ZEROSCRIPT_OBJECT_H 6 | #define ZEROSCRIPT_OBJECT_H 7 | 8 | typedef struct z_type_info_t { 9 | char *class_name; 10 | char *bytecode_stream; 11 | int_t bytecode_size; 12 | map_t *static_variables; 13 | map_t *imports_table; 14 | } z_type_info_t; 15 | 16 | struct operations { 17 | char* (*to_string)(void* self); 18 | }; 19 | 20 | typedef struct z_object { 21 | uhalf_int_t gc_version; 22 | uhalf_int_t type; 23 | struct operations operations; 24 | union { 25 | struct { 26 | map_t *properties; 27 | void* key_list_cache; 28 | }; 29 | struct { 30 | z_type_info_t* type_info; 31 | z_interpreter_state_t* saved_state; 32 | } instance_object; 33 | struct { 34 | //parent scope 35 | void *parent_context; 36 | //return context 37 | void *return_context; 38 | void *locals; 39 | uint_t locals_count; 40 | map_t *symbol_table; 41 | arraylist_t *catches_list; 42 | uint_t symbols_address; 43 | uint_t requested_return_register_index; 44 | z_instruction_t *return_address; 45 | } context_object; 46 | struct { 47 | map_t *properties; 48 | void* key_list_cache; 49 | char *value; 50 | } string_object; 51 | struct { 52 | uint_t start_address; 53 | uint_t is_async; 54 | void *parent_context; 55 | z_interpreter_state_t *responsible_interpreter_state; 56 | } function_ref_object; 57 | struct { 58 | char *value; 59 | } class_ref_object; 60 | }; 61 | } z_object_t; 62 | 63 | map_t* string_native_properties_map = 0; 64 | arraylist_t* gc_objects_list = 0; 65 | arraylist_t* interpreter_states_list = 0; 66 | 67 | Z_INLINE z_object_t *object_new(char *class_name, map_t* imports_table, z_reg_t* stack_start, z_reg_t* stack_ptr, z_reg_t* gc_fix); 68 | Z_INLINE z_object_t *string_new(char *data); 69 | Z_INLINE z_object_t *context_new(); 70 | arraylist_t* object_manager_get_known_classes(); 71 | 72 | #endif //ZEROSCRIPT_OBJECT_H 73 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | using namespace antlr4; 9 | using namespace tree; 10 | 11 | char *compile_file(const char *filename, size_t *len); 12 | 13 | #include "common/common.h" 14 | #include "compiler/Compiler.cpp" 15 | #include "interpreter/interpreter_dd.c" 16 | 17 | char *compile_file(const char *filename, size_t *len) { 18 | std::ifstream stream; 19 | stream.open(filename); 20 | ANTLRInputStream input(stream); 21 | zeroscriptLexer lexer(&input); 22 | CommonTokenStream tokens(&lexer); 23 | zeroscriptParser parser(&tokens); 24 | z_log("%s\n", filename); 25 | AstGenerator astGenerator = AstGenerator(parser.classDeclaration()); 26 | ClassDeclaration *cls = astGenerator.getRootClass(); 27 | Compiler compiler = Compiler(cls); 28 | size_t mlen = 0; 29 | char *bytes = compiler.toBytes(&mlen); 30 | *len = mlen; 31 | return bytes; 32 | } 33 | 34 | int main(int argc, const char *argv[]) { 35 | bool runMode = false; 36 | const char *compile_flag = NULL; 37 | const char *filename = NULL; 38 | const char *oFilename = NULL; 39 | char *class_path = NULL; 40 | //compile to file 41 | if (argc == 4) { 42 | compile_flag = argv[1]; 43 | filename = argv[2]; 44 | oFilename = argv[3]; 45 | } else { 46 | //run as a script 47 | filename = argv[1]; 48 | if (argc == 3) 49 | class_path = const_cast(argv[2]); 50 | runMode = true; 51 | } 52 | size_t len = 0; 53 | char *file_path = NULL; 54 | if (class_path == NULL) { 55 | file_path = (char *) (filename); 56 | } else { 57 | file_path = (char *) z_alloc_or_gc(strlen(class_path) + strlen(filename) + 2); 58 | sprintf(file_path, "%s/%s", class_path, filename); 59 | } 60 | char *bytes = compile_file(file_path, &len); 61 | object_manager_init(class_path); 62 | if (runMode) { 63 | printf("run script %s\n", filename); 64 | clock_t begin = clock(); 65 | pthread_barrier_init(&gc_safe_barrier1, NULL, 1); 66 | pthread_barrier_init(&gc_safe_barrier2, NULL, 1); 67 | thread_list = arraylist_new(sizeof(int_t)); 68 | char *class_name = (char *) (z_alloc_or_gc(strlen(filename) + 1)); 69 | strcpy(class_name, filename); 70 | class_name[strlen(filename) - 3] = 0; 71 | z_interpreter_state_t *initial_state = interpreter_state_new(context_new(), bytes, len, class_name, NULL, NULL); 72 | ADD_ROOT_TO_GC_LIST(initial_state); 73 | object_manager_register_object_type(class_name, bytes, len); 74 | z_interpreter_set_arguments_count(initial_state, 0); 75 | interpreter_run_static_constructor(bytes, len, class_name); 76 | initial_state = z_interpreter_run(initial_state); 77 | if (initial_state->return_code) { 78 | error_and_exit(initial_state->exception_details); 79 | } 80 | z_thread_gc_safe_end_thread(); 81 | for (int_t i = 0; i < thread_list->size; i++) { 82 | pthread_t *thread = *(pthread_t **) arraylist_get(thread_list, i); 83 | pthread_join(*thread, NULL); 84 | } 85 | clock_t end = clock(); 86 | double time_spent = (double) (end - begin) / CLOCKS_PER_SEC; 87 | printf("time spent: %lf\nused heap:%ldmb\n", time_spent, used_heap / (1024 * 1024)); 88 | } else { 89 | FILE *ofile = fopen(oFilename, "wb"); 90 | fwrite(bytes, static_cast(len), 1, ofile); 91 | fclose(ofile); 92 | } 93 | return 0; 94 | } -------------------------------------------------------------------------------- /src/program/Assembler.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 06.06.2018. 3 | // 4 | 5 | class Assembler { 6 | 7 | char *bytes = NULL; 8 | uint_t pos = 0; 9 | 10 | struct cmp_str { 11 | bool operator()(char const *a, char const *b) { 12 | return std::strcmp(a, b) < 0; 13 | } 14 | }; 15 | 16 | map label_position_map = map(); 17 | 18 | uint_t addData(char *data, uint_t len) { 19 | bytes = static_cast(realloc(bytes, pos + len)); 20 | memcpy(bytes + pos, data, len); 21 | pos += len; 22 | return pos - len; 23 | } 24 | 25 | void assemble(Program *program) { 26 | uint_t sizeof_constants = 0; 27 | addData((char *) &sizeof_constants, sizeof(uint_t)); 28 | 29 | //place and calculate strings aka constants 30 | int_t ip_of_static_const = addData(" ", sizeof(int_t)); 31 | for (int i = 0; i < program->instructions->size; i++) { 32 | z_instruction_t instruction = *((z_instruction_t *) arraylist_get(program->instructions, i)); 33 | if (instruction.opcode == IMPORT_CLS) { 34 | char* import = (char *) instruction.r0; 35 | char* as = (char *) instruction.r1; 36 | z_instruction_t *instruction_ptr = ((z_instruction_t *) arraylist_get(program->instructions, i)); 37 | instruction_ptr->r1 = (addData(as, (uint_t) (strlen(as) + 1))); 38 | instruction_ptr->r0 = (addData(import, (uint_t) (strlen(import) + 1))); 39 | } else if (instruction.opcode == MOV_STR || instruction.opcode == GET_FIELD_IMMEDIATE) { 40 | char *str = (char *) instruction.r1; 41 | z_instruction_t *instruction_ptr = ((z_instruction_t *) arraylist_get(program->instructions, i)); 42 | instruction_ptr->r1 = (addData(str, (uint_t) (strlen(str) + 1))); 43 | } else if (instruction.opcode == FFRAME) { 44 | z_instruction_t *instruction_ptr = ((z_instruction_t *) arraylist_get(program->instructions, i)); 45 | FunctionKind* func = (FunctionKind*) instruction.r1; 46 | map symbolTable = *func->symbolTable;//*(map*)instruction.r1; 47 | map *privatesTable = func->privatesTable;;// (map*)instruction.r2; 48 | 49 | typedef std::function, std::pair)> Comparator; 50 | 51 | // Defining a lambda function to compare two pairs. It will compare two pairs using second field 52 | Comparator compFunctor = 53 | [](std::pair elem1, std::pair elem2) { 54 | return elem1.second < elem2.second; 55 | }; 56 | 57 | // Declaring a set that will store the pairs using above comparision logic 58 | std::set, Comparator> setOfWords( 59 | symbolTable.begin(), symbolTable.end(), compFunctor); 60 | int_t i = 0; 61 | int_t size = symbolTable.size(); 62 | instruction_ptr->r1 = addData((char *) &size, sizeof(int_t)); 63 | // Iterate over a set using range base for loop 64 | for (std::pair pair : setOfWords) { 65 | char *str = (char *) pair.first.data(); 66 | uint_t pos = (addData(str, (uint_t) (strlen(str) + 1))); 67 | char isPrivate = privatesTable!= NULL && privatesTable->find(str)!= privatesTable->end(); 68 | (addData(&isPrivate,sizeof(char))); 69 | i++; 70 | } 71 | } 72 | } 73 | 74 | //calculate labels 75 | sizeof_constants = pos; 76 | uint_t lpos = 0; 77 | for (int i = 0; i < program->instructions->size; i++) { 78 | z_instruction_t instruction = *((z_instruction_t *) arraylist_get(program->instructions, i)); 79 | if (instruction.opcode == LABEL) { 80 | // cout << " PUT: `" << (char *) instruction.r0 << "` " << ftell(o_file) + lpos << "\n"; 81 | label_position_map[(char *) instruction.r0] = sizeof_constants + lpos; 82 | //put position of static constructor 83 | if (strcmp((char *) instruction.r0, "__static__constructor__") == 0) { 84 | uint_t *second_int = (uint_t *) (bytes + sizeof(int_t)); 85 | *second_int = sizeof_constants + lpos; 86 | } 87 | } else if (instruction.opcode != COMMENT) { 88 | lpos += sizeof(uint_t) * 4; 89 | } 90 | } 91 | //assemble 92 | for (uint_t i = 0; i < program->instructions->size; i++) { 93 | z_instruction_t instruction = *((z_instruction_t *) arraylist_get(program->instructions, i)); 94 | if (instruction.opcode != COMMENT && instruction.opcode != LABEL) { 95 | if (instruction.opcode >= JMP_LESS && instruction.opcode <= JMP_N_EQUAL_I) { 96 | instruction.r2 = label_position_map[(char *) instruction.r2]; 97 | } else if (instruction.opcode == JMP_NOT_TRUE || instruction.opcode == JMP_TRUE) { 98 | instruction.r1 = label_position_map[(char *) instruction.r1]; 99 | } else if (instruction.opcode == JMP) { 100 | instruction.r0 = label_position_map[(char *) instruction.r0]; 101 | } else if (instruction.opcode == MOV_FNC) { 102 | instruction.r1 = label_position_map[(char *) instruction.r1]; 103 | } else if (instruction.opcode == SET_CATCH) { 104 | instruction.r0 = label_position_map[(char *) instruction.r0]; 105 | } else if (instruction.opcode == FFRAME) { 106 | // FFRAME 107 | instruction.r2 = label_position_map[(char *) instruction.r2]; 108 | } 109 | uint_t opcode = (instruction.opcode); 110 | uint_t r0 = (instruction.r0); 111 | uint_t r1 = (instruction.r1); 112 | uint_t r2 = (instruction.r2); 113 | 114 | addData((char *) &opcode, sizeof(uint_t)); 115 | addData((char *) &r0, sizeof(uint_t)); 116 | if (instruction.opcode == MOV_NUMBER || (instruction.opcode >= JMP_LESS_I && instruction.opcode <= JMP_N_EQUAL_I)) { 117 | #ifdef FLOAT_SUPPORT 118 | FLOAT num = 0; 119 | #else 120 | int_t num = 0; 121 | #endif 122 | char *num_str = (char *) instruction.r1; 123 | if (strcmp(num_str, "true") == 0) { 124 | num = 1; 125 | } else if (strcmp(num_str, "false") == 0) { 126 | num = 0; 127 | } else { 128 | #ifdef FLOAT_SUPPORT 129 | num = atof(num_str); 130 | #else 131 | num = atoi(num_str); 132 | #endif 133 | } 134 | addData((char *) &num, sizeof(int_t)); 135 | } else { 136 | addData((char *) &r1, sizeof(int_t)); 137 | } 138 | addData((char *) &r2, sizeof(uint_t)); 139 | } 140 | } 141 | uint_t *fist_int = (uint_t *) (bytes); 142 | *fist_int = sizeof_constants; 143 | } 144 | 145 | public: 146 | char *toBytes(Program *z_program, size_t *len) { 147 | assemble(z_program); 148 | *len = pos; 149 | return bytes; 150 | } 151 | }; 152 | 153 | -------------------------------------------------------------------------------- /src/program/BytecodeOptimizer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 19.10.2018. 3 | // 4 | 5 | class BytecodeOptimizer { 6 | 7 | public: 8 | arraylist_t *optimize(arraylist_t *instructions) { 9 | //return instructions; 10 | arraylist_t *ret = arraylist_new(sizeof(z_instruction_t)); 11 | for (int_t i = 0; i < instructions->size; i++) { 12 | z_instruction_t *current = (z_instruction_t *) arraylist_get(instructions, i); 13 | z_instruction_t *next = (z_instruction_t *) arraylist_get(instructions, i + 1); 14 | z_instruction_t *next_next = (z_instruction_t *) arraylist_get(instructions, i + 2); 15 | 16 | z_instruction_t instruction = {current->opcode, current->r0, current->r1, current->r2}; 17 | 18 | int_t opcode = current->opcode; 19 | // remove dead ret 20 | if (opcode == RETURN && next->opcode == RETURN){ 21 | i++; 22 | goto add; 23 | } 24 | // remove dead mov 25 | if (opcode == MOV){ 26 | if(current->r0 == current->r1) { 27 | continue; 28 | } 29 | } 30 | // mov_n jmp -> jmp immediate 31 | if (opcode == MOV_NUMBER) { 32 | uint_t next_opcode = next->opcode; 33 | if (current->r0 == next->r1) { 34 | if (next_opcode == JMP_N_EQUAL) { 35 | instruction.r1 = current->r1; 36 | instruction.opcode = JMP_N_EQUAL_I; 37 | instruction.r0 = next->r0; 38 | instruction.r2 = next->r2; 39 | i++; 40 | } else if (next_opcode == JMP_EQUAL) { 41 | instruction.r1 = current->r1; 42 | instruction.opcode = JMP_EQUAL_I; 43 | instruction.r0 = next->r0; 44 | instruction.r2 = next->r2; 45 | i++; 46 | } else if (next_opcode == JMP_LESS) { 47 | instruction.r1 = current->r1; 48 | instruction.opcode = JMP_LESS_I; 49 | instruction.r0 = next->r0; 50 | instruction.r2 = next->r2; 51 | i++; 52 | } else if (next_opcode == JMP_LESS_OR_EQUAL) { 53 | instruction.r1 = current->r1; 54 | instruction.opcode = JMP_LESS_OR_EQUAL_I; 55 | instruction.r0 = next->r0; 56 | instruction.r2 = next->r2; 57 | i++; 58 | } else if (next_opcode == JMP_GREATER) { 59 | instruction.r1 = current->r1; 60 | instruction.opcode = JMP_GREATER_I; 61 | instruction.r0 = next->r0; 62 | instruction.r2 = next->r2; 63 | i++; 64 | } else if (next_opcode == JMP_GREATER_OR_EQUAL) { 65 | instruction.r1 = current->r1; 66 | instruction.opcode = JMP_GREATER_OR_EQUAL_I; 67 | instruction.r0 = next->r0; 68 | instruction.r2 = next->r2; 69 | i++; 70 | } 71 | } 72 | } 73 | // compare and mov 74 | // cne $4 $9 $10 75 | // mov $5 $10 76 | if (opcode >= CMP_EQUAL && opcode <= CMP_GREATER_OR_EQUAL) { 77 | uint_t next_opcode = next->opcode; 78 | if(next->r1 == current->r2){ 79 | i++; 80 | instruction.r2 = next->r1; 81 | goto add; 82 | } 83 | } 84 | // compare and jump 85 | if (opcode >= CMP_EQUAL && opcode <= CMP_GREATER_OR_EQUAL) { 86 | uint_t next_opcode = next->opcode; 87 | int neg = next_opcode == JMP_NOT_TRUE; 88 | if (next_opcode != JMP_NOT_TRUE && next_opcode != JMP_TRUE) goto add; 89 | if (opcode == CMP_EQUAL) { 90 | instruction.opcode = neg ? JMP_N_EQUAL : JMP_EQUAL; 91 | instruction.r2 = next->r1; 92 | i++; 93 | } else if (opcode == CMP_N_EQUAL) { 94 | instruction.opcode = neg ? JMP_EQUAL : JMP_N_EQUAL; 95 | instruction.r2 = next->r1; 96 | i++; 97 | } else if (opcode == CMP_LESS) { 98 | instruction.opcode = neg ? JMP_GREATER_OR_EQUAL : JMP_LESS; 99 | instruction.r2 = next->r1; 100 | i++; 101 | } else if (opcode == CMP_LESS_OR_EQUAL) { 102 | instruction.opcode = neg ? JMP_GREATER : JMP_LESS_OR_EQUAL; 103 | instruction.r2 = next->r1; 104 | i++; 105 | } else if (opcode == CMP_GREATER) { 106 | instruction.opcode = neg ? JMP_LESS_OR_EQUAL : JMP_GREATER; 107 | instruction.r2 = next->r1; 108 | i++; 109 | } else if (opcode == CMP_GREATER_OR_EQUAL) { 110 | instruction.opcode = neg ? JMP_LESS : JMP_GREATER_OR_EQUAL; 111 | instruction.r2 = next->r1; 112 | i++; 113 | } 114 | } 115 | 116 | add: 117 | arraylist_push(ret, &instruction); 118 | } 119 | int_t optimized_count = instructions->size - ret->size; 120 | if (optimized_count == 0) { 121 | return ret; 122 | } 123 | arraylist_free(instructions); 124 | return optimize(ret); 125 | } 126 | }; -------------------------------------------------------------------------------- /src/program/Program.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 10.07.2018. 3 | // 4 | 5 | #include "instruction.h" 6 | #include "BytecodeOptimizer.cpp" 7 | 8 | class Program { 9 | public: 10 | arraylist_t *instructions; 11 | BytecodeOptimizer optimizer; 12 | 13 | Program() { 14 | this->instructions = arraylist_new(sizeof(z_instruction_t)); 15 | } 16 | 17 | void addLabel(const char *format, ...) { 18 | va_list args; 19 | va_start(args, format); 20 | char *buffer = (char *) (malloc(100)); 21 | vsnprintf(buffer, 99, format, args); 22 | va_end(args); 23 | addInstruction(LABEL, (uint_t) (buffer), (uint_t) NULL, 24 | (uint_t) NULL); 25 | } 26 | 27 | void addComment(const char *format, ...) { 28 | 29 | } 30 | 31 | 32 | int_t addInstruction(uint_t opcode, uint_t r0, uint_t r1, uint_t r2) { 33 | int_t retVal = (arraylist_push(this->instructions, 34 | instruction_new(opcode, (uint_t) r0, (uint_t) r1, (uint_t) r2))); 35 | return retVal; 36 | } 37 | 38 | void print() { 39 | for (int i = 0; i < this->instructions->size; i++) { 40 | instruction_print(*((z_instruction_t *) arraylist_get(this->instructions, i))); 41 | printf("\n"); 42 | } 43 | } 44 | 45 | void optimize() { 46 | this->instructions = optimizer.optimize(this->instructions); 47 | } 48 | }; -------------------------------------------------------------------------------- /src/program/instruction.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 24.05.2018. 3 | // 4 | 5 | #ifndef ZEROSCRIPT_INSTRUCTION_H 6 | #define ZEROSCRIPT_INSTRUCTION_H 7 | 8 | #include 9 | 10 | //-----------instructions 11 | //mov immediate number at r1 into r0 12 | #define MOV_NUMBER 0 13 | //mov immediate string at r1 into r0 14 | #define MOV_STR 1 15 | //mov contents of r1 to r0 16 | #define MOV 2 17 | //r2 = r1 + r2 18 | #define ADD 3 19 | //r2 = r1 - r2 20 | #define SUB 4 21 | //r2 = r1 / r2 22 | #define DIV 5 23 | //r2 = r1 * r2 24 | #define MUL 6 25 | //r2 = r1 % r2 26 | #define MOD 7 27 | //r2 = r1 == r2 28 | #define CMP_EQUAL 8 29 | //r2 = r0 < r1 30 | #define CMP_LESS 9 31 | //r2 = r0 <= r1 32 | #define CMP_LESS_OR_EQUAL 10 33 | //r2 = r0 > r1 34 | #define CMP_GREATER 11 35 | //r2 = r0 == r1 36 | #define CMP_N_EQUAL 12 37 | //r2 = r0 != r1 38 | #define CMP_GREATER_OR_EQUAL 13 39 | //jmp to r1 if r0==1 40 | #define JMP_TRUE 14 41 | //jmp to r1 if r0!=1 42 | #define JMP_NOT_TRUE 15 43 | //jmp to r0 44 | #define JMP 16 45 | //get r1 field of r0 into r2 46 | #define GET_FIELD 17 47 | 48 | #define GET_FIELD_IMMEDIATE 18 49 | //set r1 field of r0 into r2 50 | #define SET_FIELD 19 51 | //call function in label r0 into r1 52 | #define CALL 20 53 | //push r1 54 | #define PUSH 21 55 | //pop into r1 56 | #define POP 22 57 | 58 | #define RETURN 23 59 | 60 | #define RETURN_I 24 61 | 62 | #define NOP 25 63 | 64 | #define FFRAME 26 65 | 66 | #define INC 27 67 | 68 | #define DEC 28 69 | 70 | #define MOV_FNC 29 71 | 72 | //****super instructions****// 73 | //jump to r2 if r0 < r1 74 | #define JMP_LESS 30 75 | 76 | //jump to r2 if r0 > r1 77 | #define JMP_GREATER 31 78 | 79 | //jump to r2 if r0 > r1 80 | #define JMP_LESS_OR_EQUAL 32 81 | 82 | //jump to r2 if r0 > r1 83 | #define JMP_GREATER_OR_EQUAL 33 84 | 85 | //jump to r2 if r0 == r1 86 | #define JMP_EQUAL 34 87 | 88 | //jump to r2 if r0 != r1 89 | #define JMP_N_EQUAL 35 90 | 91 | //jump to r2 if r0 < i1 92 | #define JMP_LESS_I 36 93 | 94 | //jump to r2 if r0 > i1 95 | #define JMP_GREATER_I 37 96 | 97 | //jump to r2 if r0 > i1 98 | #define JMP_LESS_OR_EQUAL_I 38 99 | 100 | //jump to r2 if r0 > i1 101 | #define JMP_GREATER_OR_EQUAL_I 39 102 | 103 | //jump to r2 if r0 == i1 104 | #define JMP_EQUAL_I 40 105 | 106 | //jump to r2 if r0 != i1 107 | #define JMP_N_EQUAL_I 41 108 | 109 | //import table builder 110 | #define IMPORT_CLS 42 111 | 112 | 113 | //throw r0 114 | #define THROW_EXCEPTION 43 115 | 116 | //set catch point at r0 117 | #define SET_CATCH 44 118 | 119 | //clear topmost catch point 120 | #define CLEAR_CATCH 45 121 | 122 | #define CREATE_THIS 46 123 | 124 | //****pseudo instructions****// 125 | #define COMMENT 47 126 | 127 | #define LABEL 48 128 | 129 | 130 | typedef struct z_instruction_t { 131 | union { 132 | struct { 133 | uint_t opcode; 134 | }; 135 | struct { 136 | uhalf_int_t short_opcode; 137 | uhalf_int_t line_number; 138 | }; 139 | }; 140 | uint_t r0; //index of a local 141 | union { 142 | uint_t r1; //index of a local 143 | FLOAT r1_float; 144 | }; 145 | uint_t r2; //index of a local 146 | } z_instruction_t; 147 | 148 | z_instruction_t *instruction_new(uint_t opcode, uint_t r0, uint_t r1, uint_t r2) { 149 | z_instruction_t *self = (z_instruction_t *) (z_alloc_or_die(sizeof(z_instruction_t))); 150 | self->opcode = opcode; 151 | self->r0 = r0; 152 | self->r1 = r1; 153 | self->r2 = r2; 154 | return self; 155 | } 156 | 157 | static const char *name_opcode(uint_t opcode); 158 | 159 | void instruction_print(z_instruction_t instruction) { 160 | if (instruction.opcode == CREATE_THIS) { 161 | printf("\tcreate_this"); 162 | } else if (instruction.opcode == SET_CATCH) { 163 | printf("\t%-5s %-5s", name_opcode(SET_CATCH), (char *) (instruction.r0)); 164 | } else if (instruction.opcode == CLEAR_CATCH) { 165 | printf("\t%-5s", name_opcode(CLEAR_CATCH)); 166 | } else if (instruction.opcode >= JMP_LESS && instruction.opcode <= JMP_N_EQUAL) { 167 | printf("\t%-5s $%-5d $%-5d %-5s ", name_opcode(instruction.opcode), (int) instruction.r0, (int) instruction.r1, 168 | (char *) instruction.r2); 169 | } else if (instruction.opcode >= JMP_LESS_I && instruction.opcode <= JMP_N_EQUAL_I) { 170 | printf("\t%-5s $%-5d %-5s %-5s ", name_opcode(instruction.opcode), (int) instruction.r0, (char*) instruction.r1, 171 | (char *) instruction.r2); 172 | } else if (instruction.opcode == THROW_EXCEPTION) { 173 | printf("\t%s $%-5d", name_opcode(instruction.opcode), (int) (instruction.r0)); 174 | } else if (instruction.opcode == IMPORT_CLS) { 175 | printf("\t%s %-5s %-5s:", name_opcode(instruction.opcode), (char *) instruction.r0, (char *) instruction.r1); 176 | } else if (instruction.opcode == LABEL) { 177 | printf("%s %-5s:", name_opcode(instruction.opcode), (char *) instruction.r0); 178 | } else if (instruction.opcode == COMMENT) { 179 | printf("\t#%s %-5s", name_opcode(instruction.opcode), (char *) instruction.r0); 180 | } else if (instruction.opcode == FFRAME) { 181 | printf("\t%-5s %-5d", name_opcode(instruction.opcode), (int) instruction.r0); 182 | } else if (instruction.opcode == CALL) { 183 | printf("\t%-5s $%-5d $%-5d %-5d", name_opcode(instruction.opcode), (int) instruction.r0, (int) instruction.r1, (int) instruction.r2); 184 | } else if (instruction.opcode == GET_FIELD || instruction.opcode == SET_FIELD) { 185 | printf("\t%-5s $%-5d $%-5d $%-5d", name_opcode(instruction.opcode), (int) instruction.r0, 186 | (int) instruction.r1, (int) instruction.r2); 187 | } else if (instruction.opcode == GET_FIELD_IMMEDIATE) { 188 | printf("\t%-5s $%-5d %-5s $%-5d", name_opcode(instruction.opcode), (int) instruction.r0, 189 | (char *) instruction.r1, (int) instruction.r2); 190 | } else if (instruction.opcode == RETURN) { 191 | printf("\t%-5s $%-5d ", name_opcode(instruction.opcode), (int) instruction.r0); 192 | } else if (instruction.opcode == RETURN_I) { 193 | printf("\t%-5s %-5d ", name_opcode(instruction.opcode), (int) instruction.r0); 194 | } else if (instruction.opcode == JMP_TRUE || instruction.opcode == JMP_NOT_TRUE) { 195 | printf("\t%-5s $%-5d %-5s ", name_opcode(instruction.opcode), (int) instruction.r0, (char *) instruction.r1); 196 | } else if (instruction.opcode == JMP) { 197 | printf("\t%-5s %-5s ", name_opcode(instruction.opcode), (char *) instruction.r0); 198 | } else if (instruction.opcode == PUSH || instruction.opcode == POP) { 199 | printf("\t%-5s $%-5d ", name_opcode(instruction.opcode), (int) instruction.r0); 200 | } else if (instruction.opcode == MOV_STR || instruction.opcode == MOV_NUMBER || instruction.opcode == MOV_FNC) { 201 | printf("\t%-5s $%-5d %-5s ", name_opcode(instruction.opcode), (int) instruction.r0, (char *) instruction.r1); 202 | } else if (instruction.opcode == MOV) { 203 | printf("\t%-5s $%-5d $%-5d ", name_opcode(instruction.opcode), (int) instruction.r0, (int) instruction.r1); 204 | } else { 205 | printf("\t%-5s $%-5d $%-5d $%-5d ", name_opcode(instruction.opcode), (int) instruction.r0, (int) instruction.r1, 206 | (int) instruction.r2); 207 | } 208 | } 209 | 210 | static const char *name_opcode(uint_t opcode) { 211 | switch (opcode) { 212 | case SET_CATCH: 213 | return "set_catch"; 214 | case CLEAR_CATCH: 215 | return "clear_catch"; 216 | case THROW_EXCEPTION: 217 | return "throw"; 218 | case JMP_GREATER_OR_EQUAL: 219 | return "jge"; 220 | case JMP_GREATER: 221 | return "jg"; 222 | case JMP_LESS_OR_EQUAL: 223 | return "jle"; 224 | case JMP_EQUAL: 225 | return "je"; 226 | case JMP_N_EQUAL: 227 | return "jne"; 228 | case JMP_LESS: 229 | return "jl"; 230 | case JMP_GREATER_OR_EQUAL_I: 231 | return "jge_i"; 232 | case JMP_GREATER_I: 233 | return "jg_i"; 234 | case JMP_LESS_OR_EQUAL_I: 235 | return "jle_i"; 236 | case JMP_EQUAL_I: 237 | return "je_i"; 238 | case JMP_N_EQUAL_I: 239 | return "jne_i"; 240 | case JMP_LESS_I: 241 | return "jl_i"; 242 | case DEC: 243 | return "dec"; 244 | case INC: 245 | return "inc"; 246 | case MOV_FNC: 247 | return "mov_f"; 248 | case LABEL: 249 | return ""; 250 | case COMMENT: 251 | return ""; 252 | case FFRAME: 253 | return "fframe"; 254 | case MOV_NUMBER: 255 | return "mov_n"; 256 | case MOV_STR: 257 | return "mov_s"; 258 | case MOV: 259 | return "mov"; 260 | case ADD: 261 | return "add"; 262 | case SUB: 263 | return "sub"; 264 | case MUL: 265 | return "mul"; 266 | case MOD: 267 | return "mod"; 268 | case DIV: 269 | return "div"; 270 | case CMP_EQUAL: 271 | return "ce"; 272 | case CMP_N_EQUAL: 273 | return "cne"; 274 | case CMP_GREATER: 275 | return "cg"; 276 | case CMP_GREATER_OR_EQUAL: 277 | return "cge"; 278 | case CMP_LESS: 279 | return "cl"; 280 | case CMP_LESS_OR_EQUAL: 281 | return "cle"; 282 | case JMP: 283 | return "jmp"; 284 | case JMP_NOT_TRUE: 285 | return "jnt"; 286 | case JMP_TRUE: 287 | return "jt"; 288 | case GET_FIELD: 289 | return "get_f"; 290 | case GET_FIELD_IMMEDIATE: 291 | return "get_fi"; 292 | case SET_FIELD: 293 | return "set_f"; 294 | case CALL: 295 | return "call"; 296 | case RETURN: 297 | return "ret"; 298 | case RETURN_I: 299 | return "ret_i"; 300 | case PUSH: 301 | return "push"; 302 | case POP: 303 | return "pop"; 304 | case NOP: 305 | return "nop"; 306 | case IMPORT_CLS: 307 | return "import"; 308 | default: 309 | return "UNKNOWN"; 310 | } 311 | } 312 | 313 | #endif //ZEROSCRIPT_INSTRUCTION_H 314 | -------------------------------------------------------------------------------- /src/program/program.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 24.05.2018. 3 | // 4 | 5 | #ifndef ZEROSCRIPT_PROGRAM_H 6 | #define ZEROSCRIPT_PROGRAM_H 7 | 8 | #include "instruction.h" 9 | 10 | typedef struct z_program_t { 11 | arraylist_t *instructions; 12 | } z_program_t; 13 | 14 | #include "Assembler.cpp" 15 | 16 | uint_t z_program_add_instruction(z_program_t *program, uint_t opcode, uint_t r0, uint_t r1, 17 | uint_t r2); 18 | 19 | z_instruction_t *z_program_get_latest_instruction(z_program_t *program) { 20 | uint_t i = program->instructions->size - 1; 21 | z_instruction_t *latest = static_cast(arraylist_get(program->instructions, i)); 22 | while (latest->opcode == COMMENT && i > 0) { 23 | latest = static_cast(arraylist_get(program->instructions, i--)); 24 | } 25 | return latest; 26 | } 27 | 28 | z_program_t *z_program_new() { 29 | z_program_t *self = (z_program_t *) (z_alloc_or_die(sizeof(z_program_t))); 30 | self->instructions = arraylist_new(sizeof(z_instruction_t)); 31 | return self; 32 | }; 33 | 34 | void z_program_add_label(z_program_t *program, const char *format, ...) { 35 | va_list args; 36 | va_start(args, format); 37 | char *buffer = (char *) (malloc(100)); 38 | vsnprintf(buffer, 99, format, args); 39 | va_end(args); 40 | z_program_add_instruction(program, LABEL, (uint_t) (buffer), (uint_t) NULL, 41 | (uint_t) NULL); 42 | } 43 | 44 | void z_program_add_comment(z_program_t *program, const char *format, ...) { 45 | va_list args; 46 | va_start(args, format); 47 | char *buffer = (char *) (malloc(100)); 48 | vsnprintf(buffer, 99, format, args); 49 | va_end(args); 50 | z_program_add_instruction(program, COMMENT, (uint_t) (buffer), (uint_t) NULL, 51 | (uint_t) NULL); 52 | } 53 | 54 | uint_t z_program_add_instruction(z_program_t *program, uint_t opcode, uint_t r0, uint_t r1, 55 | uint_t r2) { 56 | //optimize compare and jmp path 57 | if (opcode == JMP_TRUE || opcode == JMP_NOT_TRUE) { 58 | int neg = opcode == JMP_NOT_TRUE; 59 | z_instruction_t *latest_inst = z_program_get_latest_instruction(program); 60 | uint_t prev_opcode = latest_inst->opcode; 61 | if (prev_opcode == CMP_EQUAL) { 62 | latest_inst->opcode = neg ? JMP_N_EQUAL : JMP_EQUAL; 63 | latest_inst->r2 = r1; 64 | } else if (prev_opcode == CMP_N_EQUAL) { 65 | latest_inst->opcode = neg ? JMP_EQUAL : JMP_N_EQUAL; 66 | latest_inst->r2 = r1; 67 | } else if (prev_opcode == CMP_LESS) { 68 | latest_inst->opcode = neg ? JMP_GREATER_OR_EQUAL : JMP_LESS; 69 | latest_inst->r2 = r1; 70 | } else if (prev_opcode == CMP_LESS_OR_EQUAL) { 71 | latest_inst->opcode = neg ? JMP_GREATER : JMP_LESS_OR_EQUAL; 72 | latest_inst->r2 = r1; 73 | } else if (prev_opcode == CMP_GREATER) { 74 | latest_inst->opcode = neg ? JMP_LESS_OR_EQUAL : JMP_GREATER; 75 | latest_inst->r2 = r1; 76 | } else if (prev_opcode == CMP_GREATER_OR_EQUAL) { 77 | latest_inst->opcode = neg ? JMP_LESS : JMP_GREATER_OR_EQUAL; 78 | latest_inst->r2 = r1; 79 | } else { 80 | goto ret; 81 | } 82 | return (uint_t) program->instructions->size - 1; 83 | } 84 | //optimize mov constant and mov path 85 | else if (opcode == MOV) { 86 | z_instruction_t *latest_inst = z_program_get_latest_instruction(program); 87 | uint_t prev_opcode = latest_inst->opcode; 88 | if ((prev_opcode == MOV_NUMBER || prev_opcode == MOV_STR) && latest_inst->r0 == r1) { 89 | latest_inst->r0 = r0; 90 | return (uint_t) program->instructions->size - 1; 91 | } else { 92 | goto ret; 93 | } 94 | } 95 | ret:; 96 | return (uint_t) (arraylist_push(program->instructions, 97 | instruction_new(opcode, (uint_t) r0, (uint_t) r1, (uint_t) r2))); 98 | } 99 | 100 | void z_program_print(z_program_t *program) { 101 | for (int i = 0; i < program->instructions->size; i++) { 102 | instruction_print(*((z_instruction_t *) arraylist_get(program->instructions, i))); 103 | printf("\n"); 104 | } 105 | } 106 | 107 | #endif //ZEROSCRIPT_PROGRAM_H 108 | -------------------------------------------------------------------------------- /src/tester/tester.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by onur on 25.10.2018. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // test if a given script behaves as expected without corrupting memory 11 | void test(const char *script) { 12 | printf("running %s..............\n",script); 13 | const char *template_str = "valgrind --quiet --memcheck:leak-check=no --error-exitcode=1 ./zero %s >> /dev/null"; 14 | size_t len = strlen(template_str) + strlen(script) + 1; 15 | char *buff = (char *) malloc(len); 16 | snprintf(buff, len, template_str, script); 17 | assert(system(buff) == 0); 18 | printf("OK\n"); 19 | } 20 | 21 | int main() { 22 | test("../test_scripts/OperatorsTest.zs"); 23 | test("../test_scripts/primetest.zs"); 24 | test("../test_scripts/ClassInstanceTest.zs"); 25 | test("../test_scripts/JsonTest.zs"); 26 | test("../test_scripts/ThrowTest.zs"); 27 | test("../test_scripts/ScopesTest.zs"); 28 | test("../test_scripts/AsyncFunctionTest.zs"); 29 | test("../test_scripts/RecursiveFibonacci.zs"); 30 | test("../test_scripts/CombinatorFunctionTest.zs"); 31 | test("../test_scripts/AsyncModifySharedDataTest.zs"); 32 | test("../test_scripts/AsyncFunctionTest2.zs"); 33 | test("../test_scripts/VarargFunctionTest.zs"); 34 | return 0; 35 | } -------------------------------------------------------------------------------- /test_scripts/AsyncFunctionTest.zs: -------------------------------------------------------------------------------- 1 | import "../test_scripts/Math" as Math 2 | import "../test_scripts/primetest" as Primetest 3 | import "../test_scripts/ClassInstanceTest" as ClassInstanceTest 4 | 5 | class AsyncFucntionTest(){ 6 | 7 | asyncFnc(); 8 | synchronousFnc(); 9 | asyncFnc2(); 10 | asynFncWithThrow(); 11 | 12 | // you can pass callback arguments 13 | asyncFnc3(()=>{ 14 | print("finished async 3"); 15 | }); 16 | 17 | function synchronousFnc(){ 18 | //these primetests both require gc and yet they should not corrupt each other's data 19 | new Primetest(); 20 | } 21 | 22 | async function asyncFnc(){ 23 | new Primetest(); 24 | } 25 | 26 | async function asynFncWithThrow(){ 27 | try { 28 | throw "why are you calling me?"; 29 | }catch(e){ 30 | print(e); 31 | } 32 | } 33 | // access other classes inside an async function 34 | async function asyncFnc2(){ 35 | print("rounded:"+Math.round(11.2)); 36 | } 37 | 38 | // can pass arguments to an async fnc 39 | async function asyncFnc3(arg){ 40 | for(var i = 0;i<1000;i++){ 41 | print(i); 42 | //this will trigger gc also 43 | var a = {}; 44 | } 45 | arg(); 46 | } 47 | } -------------------------------------------------------------------------------- /test_scripts/AsyncFunctionTest2.zs: -------------------------------------------------------------------------------- 1 | class AsyncFunctionTest2(){ 2 | 3 | tshared var limit = 100000; 4 | 5 | // when more than one thread tries to call the same function state it used to crash the vm. 6 | // this test tests if this bug still exists or not 7 | function longRunningFunction(){ 8 | var i = 0; 9 | while(i{}); 11 | modifyOsman({}); 12 | 13 | async modifyOsmanAsync(val){ 14 | while(opCount < limit) { 15 | osman = val; 16 | print("osman:"+osman); 17 | opCount ++; 18 | print("op count :"+opCount); 19 | } 20 | print("finished"); 21 | } 22 | 23 | modifyOsman(val){ 24 | while(opCount < limit) { 25 | osman = val; 26 | print("osman:"+osman); 27 | opCount ++; 28 | print("op count :"+opCount); 29 | } 30 | print("finished"); 31 | } 32 | } -------------------------------------------------------------------------------- /test_scripts/BinaryTree.zs: -------------------------------------------------------------------------------- 1 | class BinaryTree(data){ 2 | 3 | var left; 4 | var right; 5 | 6 | function getData(){ 7 | return this.data; 8 | } 9 | 10 | function getLeft(){ 11 | return this.left; 12 | } 13 | 14 | function getRight(){ 15 | return this.right; 16 | } 17 | 18 | function add(data){ 19 | print("add "+ data); 20 | return addRecursive(this,data); 21 | } 22 | 23 | function find(data){ 24 | print("search "+ data); 25 | return findRecursive(this,data); 26 | } 27 | 28 | private function findRecursive(current,data){ 29 | if (current == null){ 30 | print("not found:"+data); 31 | return null; 32 | } 33 | if (data < current.data) { 34 | print("search left"); 35 | findRecursive(current.left, data); 36 | } else if (data > current.data) { 37 | print("search right"); 38 | findRecursive(current.right, data); 39 | } else { 40 | print("found:"+data); 41 | return current; 42 | } 43 | } 44 | 45 | private function addRecursive(current,data){ 46 | if(current == null){ 47 | return new BinaryTree(data); 48 | } 49 | if (data < current.data) { 50 | print("left"); 51 | current.left = addRecursive(current.left, data); 52 | } else if (data > current.data) { 53 | print("right"); 54 | current.right = addRecursive(current.right, data); 55 | } else { 56 | // value already exists 57 | return current; 58 | } 59 | 60 | return current; 61 | } 62 | } -------------------------------------------------------------------------------- /test_scripts/ClassInstanceTest.zs: -------------------------------------------------------------------------------- 1 | import "../test_scripts/StaticVariables" as StaticVariables 2 | import "../test_scripts/Math" as Math 3 | import "../test_scripts/Hello" as Hello 4 | import "../test_scripts/ThrowTest" as ThrowTest 5 | import "../test_scripts/PrivateVariablesTest" as PrivateVariablesTest 6 | 7 | class ClassInstanceTest() { 8 | print("************ STATIC VARIABLES TEST **********************"); 9 | print("static varibale in StaticVariables.staticVariable is :"+StaticVariables.staticVariable); 10 | print("make it 100"); 11 | StaticVariables.staticVariable = 100; 12 | print("static varibale in StaticVariables.staticVariable is :"+StaticVariables.staticVariable); 13 | assert(StaticVariables.staticVariable == 100); 14 | print("************ STATIC METHOD TEST **********************"); 15 | StaticVariables.osman(); 16 | assert(Math.floor(5.25) == 5); 17 | assert(Math.round(5.65) == 6); 18 | print("************ CLASS INSTANCE TEST **********************"); 19 | print("creating an instance of Hello class..."); 20 | var helloObject = new Hello(); 21 | var message = helloObject.helloMessage; 22 | print("hello object's message is " + message); 23 | print("now set it to osman..."); 24 | helloObject.helloMessage = "osman is awesome!"; 25 | print("hello object's message is " + helloObject.helloMessage); 26 | assert(helloObject.helloMessage.equals("osman is awesome!")); 27 | print("now call sayHello on helloObject..."); 28 | helloObject.sayHello(); 29 | print("now set it via setter method"); 30 | helloObject.setHelloMessage("osman again!"); 31 | assert(helloObject.helloMessage.equals("osman again!")); 32 | helloObject.sayHello(); 33 | print("invoke closure on helloObject"); 34 | helloObject.closureTest()(); 35 | print("fncRef test"); 36 | helloObject.fncRefTest(x => { 37 | print(x); 38 | assert(x.equals("hi")); 39 | }); 40 | var a = "test"; 41 | helloObject.fncRefTest(x => { 42 | print(a); 43 | assert(a.equals("test")); 44 | }); 45 | print("************ CATCH EXCEPTIONS INSIDE ANOTHER CLASSS TEST **********************"); 46 | var throwTest = new ThrowTest(); 47 | 48 | try { 49 | throwTest.throwTest2Inner(); 50 | } catch(e){ 51 | print("this time I caught it: "+e); 52 | } 53 | try { 54 | // must throw an exception since there is no property defined on selami 55 | helloObject.selami = 10; 56 | } catch(e){ 57 | print("and this too "+e); 58 | } 59 | 60 | print("************ PRIVATE VARIABLES TEST **********************"); 61 | 62 | var objectWithPrivateVariables = new PrivateVariablesTest(); 63 | try { 64 | print(objectWithPrivateVariables.privateVariable); 65 | } catch(e) { 66 | print(e); 67 | } 68 | 69 | try { 70 | print(objectWithPrivateVariables.privateFnc()); 71 | } catch(e) { 72 | print(e); 73 | } 74 | 75 | objectWithPrivateVariables.getPrivateFnc()(); 76 | objectWithPrivateVariables.accessPrivateVariableFromTheSameClass(); 77 | } -------------------------------------------------------------------------------- /test_scripts/CombinatorFunctionTest.zs: -------------------------------------------------------------------------------- 1 | class CombinatorFunctionTest(){ 2 | 3 | assert(inSequence( 4 | (a)=> "arg was"+ a, 5 | ()=> "f" 6 | )().equals("arg wasf")); 7 | 8 | assert(flip( 9 | (a,b)=>(a+b) 10 | )("hello","world").equals("worldhello")); 11 | 12 | function inSequence(f,g){ 13 | return ()=> f(g()); 14 | } 15 | 16 | function flip(f){ 17 | return (a,b)=>f(b,a); 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /test_scripts/EventQueueTest.zs: -------------------------------------------------------------------------------- 1 | import "../test_scripts/primetest" as Primetest 2 | 3 | class EventQueueTest(){ 4 | 5 | enqueue(function(){ 6 | new Primetest(); 7 | exit(); 8 | }); 9 | 10 | } -------------------------------------------------------------------------------- /test_scripts/Hello.zs: -------------------------------------------------------------------------------- 1 | import "../test_scripts/primetest" as Primetest 2 | 3 | class Hello(){ 4 | 5 | var helloMessage = "hello world!"; 6 | 7 | sayHello(); 8 | 9 | function setHelloMessage(message){ 10 | this.helloMessage = message; 11 | } 12 | 13 | function getHelloMessage(){ 14 | return this.helloMessage; 15 | } 16 | 17 | function sayHello(){ 18 | print(this.helloMessage); 19 | } 20 | 21 | function closureTest(){ 22 | var x = 100; 23 | return ()=>{ 24 | print("variable in my parent scope is :"+x); 25 | }; 26 | } 27 | 28 | function fncRefTest(fncRef){ 29 | fncRef("hi"); 30 | } 31 | 32 | function runPrimeTest(){ 33 | new Primetest(); 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /test_scripts/ImportTest.zs: -------------------------------------------------------------------------------- 1 | import "../test_scripts/Math" as Math 2 | import "../test_scripts/Hello" as Hello 3 | 4 | class ImportTest(){ 5 | 6 | static var a = Math.floor(3); 7 | print(Math.round(2.34)); 8 | new Hello(); 9 | 10 | } -------------------------------------------------------------------------------- /test_scripts/JsonTest.zs: -------------------------------------------------------------------------------- 1 | class JsonTest(){ 2 | 3 | var obj = { "a":0,"b":{"c":0,"d":1}, "e":[1,2,3] }; 4 | 5 | assert(obj.a == 0); 6 | assert(obj["a"] == 0); 7 | assert(obj.b.c == 0); 8 | assert(obj["b"]["d"] == 1); 9 | assert(obj.e[0] == 1); 10 | assert(obj["e"][1] == 2); 11 | 12 | var keys = obj.keys(); 13 | assert(keys.size() == 3); 14 | 15 | var obj2 = {"098766543212343435":"asd"}; 16 | assert(obj2.keys()[0].equals("098766543212343435")); 17 | 18 | var count = 0; 19 | for (var key in obj) { 20 | count++; 21 | } 22 | assert(count == 3); 23 | 24 | } -------------------------------------------------------------------------------- /test_scripts/Math.zs: -------------------------------------------------------------------------------- 1 | class Math(){ 2 | 3 | static function floor(n){ 4 | return n - (n % 1); 5 | } 6 | 7 | static function round(x){ 8 | if (x < 0.0) 9 | return int(x - 0.5); 10 | else 11 | return int(x + 0.5); 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /test_scripts/OperatorsTest.zs: -------------------------------------------------------------------------------- 1 | class OperatorsTest(){ 2 | 3 | print("basic math operations test:"); 4 | 5 | assert(1 + 2 == 3); 6 | assert(1 + 0 == 1); 7 | assert(-1 + -2 == -3); 8 | assert(-1 + 0 == -1); 9 | assert(1.0 + 2 == 3); 10 | assert(1.5 + 2.5 == 4); 11 | assert(2.5 + -1.5 == 1); 12 | assert(1 - 2 == -1); 13 | assert(-1 - 2 == -3); 14 | 15 | assert(("a" + 1).equals("a1")); 16 | assert(("a" + "b").equals("ab")); 17 | 18 | assert(1 * 2 == 2); 19 | assert(1 * 0 == 0); 20 | assert(-1 * -2 == 2); 21 | assert(-1 * 0 == 0); 22 | assert(1.0 * 2 == 2); 23 | assert(1.5 * 2.5 == 3.75); 24 | assert(2.5 * -1.5 == -3.75); 25 | 26 | assert(1 / 2 == 0.5); 27 | assert(-1 / 2 == -0.5); 28 | assert(1.0 / 2 == 0.5); 29 | assert(1.5 / 2.5 == 0.6); 30 | assert(1.5 / -2.5 == -0.6); 31 | 32 | print("prefix and postfix operators test:"); 33 | 34 | var a = 1; 35 | var b = ++a; 36 | assert(b == 2); 37 | assert(a == 2); 38 | b = a++; 39 | assert(b == 2); 40 | assert(a == 3); 41 | assert(++a == 4); 42 | assert(a++ == 4); 43 | assert(a == 5); 44 | 45 | a = 1; 46 | var b = --a; 47 | assert(b == 0); 48 | assert(a == 0); 49 | b = a--; 50 | assert(b == 0); 51 | assert(a == -1); 52 | assert(--a == -2); 53 | assert(a-- == -2); 54 | assert(a == -3); 55 | 56 | print("boolean operators test:"); 57 | assert(0 and 1 == 0); 58 | assert(0 and 0 == 0); 59 | assert(1 and 0 == 0); 60 | assert(1 and 1 == 1); 61 | 62 | assert(0 or 1 == 1); 63 | assert(0 or 0 == 0); 64 | assert(1 or 0 == 1); 65 | assert(1 or 1 == 1); 66 | 67 | print("boolean operators short circuit test:"); 68 | assert(0 and (()=>{throw "not good";})() == 0); 69 | assert(1 or (()=>{throw "not good";})() == 1); 70 | 71 | 72 | } -------------------------------------------------------------------------------- /test_scripts/Osman.zs: -------------------------------------------------------------------------------- 1 | import "../test_scripts/BinaryTree" as BinaryTree 2 | 3 | class Osman(){ 4 | var root = null; 5 | 6 | print("put to insert a node"); 7 | print("get to find a node"); 8 | print("q to quit"); 9 | 10 | while(true){ 11 | 12 | var line = read(); 13 | 14 | if(line.equals("q")){ 15 | exit(0); 16 | } else if (line.startsWith("put")){ 17 | var num = number(line.substring(3)); 18 | if(root == null){ 19 | print("root null, creating"); 20 | root = new BinaryTree(num); 21 | } else { 22 | root.add(num); 23 | } 24 | } else { 25 | var num = number(line.substring(3)); 26 | if(root!=null){ 27 | root.find(num); 28 | } else { 29 | throw "tried to find before adding any node"; 30 | } 31 | } 32 | 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /test_scripts/PrivateVariablesTest.zs: -------------------------------------------------------------------------------- 1 | class PrivateVariablesTest(){ 2 | 3 | private var privateVariable; 4 | 5 | private function privateFnc(){ 6 | print("i cant be called outside this class unless accessed by an accessor :("); 7 | } 8 | 9 | function accessPrivateVariableFromTheSameClass(){ 10 | var instanceOfTheSameClass = new PrivateVariablesTest(); 11 | instanceOfTheSameClass.privateFnc(); 12 | } 13 | 14 | function getPrivateFnc(){ 15 | return this.privateFnc; 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /test_scripts/RecursiveFibonacci.zs: -------------------------------------------------------------------------------- 1 | class Recursive() 2 | { 3 | 4 | var results = [ 5 | 0, 6 | 1, 7 | 1, 8 | 2, 9 | 3, 10 | 5, 11 | 8, 12 | 13, 13 | 21, 14 | 34, 15 | 55, 16 | 89, 17 | 144, 18 | 233, 19 | 377, 20 | 610, 21 | 987, 22 | 1597, 23 | 2584, 24 | 4181, 25 | 6765, 26 | 10946, 27 | 17711, 28 | 28657, 29 | 46368, 30 | 75025 31 | ]; 32 | 33 | function fibonacci(num) { 34 | if (num == 0) return 0; 35 | if (num <= 1) return 1; 36 | return fibonacci(num - 1) + fibonacci(num - 2); 37 | } 38 | 39 | for (var i = 0; i < 26; i++) { 40 | var fib = fibonacci(i); 41 | print("fib(" + i + "):" + fib); 42 | assert(fib == results[i]); 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /test_scripts/ScopesTest.zs: -------------------------------------------------------------------------------- 1 | class ScopesTest(){ 2 | 3 | var a = 10; 4 | function test1() { 5 | //read from the parent scope 6 | assert(a == 10); 7 | } 8 | function closureTest(){ 9 | var x = "x"; 10 | return ()=>{ 11 | assert(a == 10); 12 | // make sure that closure parent scopes don't disappear 13 | assert(x.equals("x")); 14 | }; 15 | } 16 | 17 | test1(); 18 | closureTest()(); 19 | } -------------------------------------------------------------------------------- /test_scripts/StaticVariables.zs: -------------------------------------------------------------------------------- 1 | class StaticVariables(){ 2 | static var staticVariable = 0; 3 | 4 | static function osman(){ 5 | print("i am a static function"); 6 | print("i am a static variable:"+staticVariable); 7 | } 8 | 9 | } -------------------------------------------------------------------------------- /test_scripts/ThrowTest.zs: -------------------------------------------------------------------------------- 1 | class ThrowTest(){ 2 | 3 | throwTest0(); 4 | 5 | throwTest1(); 6 | 7 | throwTest2(); 8 | 9 | throwTest3(); 10 | 11 | function throwTest0(){ 12 | print("this tests runtime errors"); 13 | try { 14 | 10/0; 15 | }catch(e){ 16 | print(e); 17 | } 18 | } 19 | 20 | function throwTest1(){ 21 | print("this tests catching thrown errors"); 22 | try { 23 | throw "what happened?"; 24 | } catch(e) { 25 | print("test 1 is passed. message:"+e); 26 | } 27 | print("and something to print after..."); 28 | } 29 | 30 | function throwTest2(){ 31 | print("this tests catching thrown errors inside another function"); 32 | try { 33 | throwTest2Inner(); 34 | } catch(e){ 35 | print("test 2 is passed. exception message:"+e); 36 | } 37 | print("and something to print after..."); 38 | } 39 | 40 | function throwTest2Inner(){ 41 | throw "something happened :("; 42 | } 43 | 44 | function throwTest3(){ 45 | print("this tests finally block"); 46 | try { 47 | return 0; 48 | } catch (e){ 49 | print("something happened"); 50 | } finally { 51 | print("finally"); 52 | } 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /test_scripts/VarargFunctionTest.zs: -------------------------------------------------------------------------------- 1 | class VarargFunctionTest(){ 2 | 3 | // call with no arg 4 | vararg(); 5 | // cal with 1 arg 6 | vararg("a"); 7 | // call with 2 args 8 | vararg("a","b"); 9 | // call with 3 args 10 | vararg("a","b","c"); 11 | // call with 4 agrs 12 | vararg("a","b","c","d"); 13 | 14 | function vararg(a,b,c){ 15 | print(""+a+"|"+b+"|"+c); 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /test_scripts/operators.zs: -------------------------------------------------------------------------------- 1 | class Operators() { 2 | 3 | var strLength = "length"; 4 | print("length of `test` is :" + "test".length()); 5 | print("length of `test` is :" + "test"["length"]()); 6 | print("length of `test` is :" + "test"[strLength]()); 7 | 8 | print("length of `selami` is "+ "selami".length()); 9 | 10 | var testObj = new Object(); 11 | testObj.osman = "osman value"; 12 | testObj[2] = "VELI"; 13 | print("an arbitrary property (osman) that is defined on an object is :" + testObj.osman); 14 | print("an arbitrary numeric property (2) that is defined on an object is :" + testObj[2]); 15 | print("length of tesObj.osman is "+ testObj.osman.length()); 16 | 17 | print("size of testObj is "+ testObj.size()); 18 | 19 | for(var i = 0; i < testObj.keys().size();i++){ 20 | print("keys[" + i + "] is :" + testObj.keys()[i]+" and the value is :" + testObj[testObj.keys()[i]]); 21 | } 22 | 23 | testObj["asd"] = "a new value. this must invalidate key cache"; 24 | 25 | print("a new value is added..."); 26 | 27 | for(var i = 0; i < testObj.keys().size();i++){ 28 | print("keys[" + i + "] is :" + testObj.keys()[i]); 29 | } 30 | 31 | print("traversing values in a foreach loop..."); 32 | 33 | for(var key in testObj){ 34 | print(key + " = " + testObj[key]); 35 | } 36 | 37 | print("prefix and postfix operators test:"); 38 | 39 | var a = 1; 40 | 41 | print(a++); //1 42 | a = 1; 43 | print(-a++); //-1 44 | a = 1; 45 | print(++a); //2 46 | a = 1; 47 | print(-++a); //-2 48 | a = 1; 49 | print(---a); //-2 //longest match first: -- -a 50 | 51 | print("prefix and postfix operators for floating point types test:"); 52 | a = 1.5; 53 | print(a++); //1 54 | a = 1.5; 55 | print(-a++); //-1 56 | a = 1.5; 57 | print(++a); //2 58 | a = 1.5; 59 | print(-++a); //-2 60 | a = 1.5; 61 | print(---a); //-2 //longest match first: -- -a 62 | 63 | print("1 and 0:"+ (1 and 0)); 64 | print("1 and 1:"+ (1 and 1)); 65 | 66 | print("1 or 0:"+ (1 or 0)); 67 | print("1 or 1:"+ (1 or 1)); 68 | 69 | for(var i = 1; i > 0 and i < 4;i++ ){ 70 | print(i); 71 | } 72 | 73 | print("1 or 1 and 0:"+ (1 or 1 and 0)); 74 | 75 | } -------------------------------------------------------------------------------- /test_scripts/primetest.zs: -------------------------------------------------------------------------------- 1 | class Primetest() { 2 | 3 | var testLimit = 100; 4 | var inlineTest = false; 5 | 6 | if (inlineTest) { 7 | for (var i = 0; i < testLimit; i = i ++) { 8 | var _isPrime = i != 1; 9 | for (var j = 2;j < i; j++) { 10 | if (i % j == 0) { 11 | _isPrime = false; 12 | break; 13 | } 14 | } 15 | print(i + " isPrime:" + _isPrime); 16 | } 17 | print("inline prime test is done"); 18 | } else { 19 | for(var i= 0;i' '>' '>' | '>' '>') expression 15 | | expression bop=('<=' | '>=' | '>' | '<') expression 16 | | expression bop='and' expression 17 | | expression bop='or' expression 18 | | expression bop=('==' | '!=') expression 19 | | expression 20 | bop=('=' | '+=' | '-=' | '*=' | '/=' | '%=') 21 | expression 22 | | anonymousFunction 23 | ; 24 | 25 | expressionList 26 | : expression (',' expression)* 27 | ; 28 | 29 | newObject : 30 | NEW expression 31 | ; 32 | 33 | 34 | primaryExpresssion 35 | : '(' expression ')' 36 | | atom 37 | ; 38 | 39 | statement : 40 | semicolon = ';' 41 | | body 42 | | var 43 | | function 44 | | expression ';' 45 | | forLoop 46 | | whileLoop 47 | | forInLoop 48 | | conditional 49 | | tryCatch 50 | | throw_ ';' 51 | | BREAK ';' 52 | | CONTINUE ';' 53 | | RET (expression) ';' 54 | ; 55 | 56 | argumentsList: 57 | | 58 | identifier (',' identifier)* 59 | ; 60 | 61 | bodyOrStatement: 62 | body 63 | | statement 64 | ; 65 | bodyOrExpression: 66 | body 67 | | expression 68 | ; 69 | 70 | body: 71 | '{' (statement)* '}' 72 | ; 73 | 74 | anonymousFunction: 75 | FUNC '(' argumentsList ')' body 76 | | '(' argumentsList ')' '=>' bodyOrExpression 77 | | identifier '=>' bodyOrExpression 78 | ; 79 | 80 | function: 81 | PRIVATE? (STATIC|ASYNC)? FUNC? functionName = identifier '(' argumentsList ')' body 82 | ; 83 | 84 | var: 85 | PRIVATE? (STATIC|TSHARED)? VAR variableDeclarationPart (',' variableDeclarationPart)* 86 | ; 87 | 88 | variableDeclarationPart: 89 | variableName = identifier ('=' expression)? 90 | ; 91 | 92 | forLoop: 93 | FOR '(' (expression|var)? ';' expression? ';' expression? ')' bodyOrStatement 94 | ; 95 | 96 | forInLoop: 97 | FOR '(' VAR iterElement = IDENT? IN expression ')' bodyOrStatement 98 | ; 99 | 100 | whileLoop: 101 | WHILE '(' expression? ')' bodyOrStatement 102 | ; 103 | 104 | conditional: 105 | IF '(' expression ')' bodyOrStatement (ELSE bodyOrStatement)? 106 | ; 107 | 108 | importStmt: 109 | IMPORT clsPath = STRING AS clsName = IDENT 110 | ; 111 | 112 | classDeclaration: 113 | importStmt* 114 | CLS className = identifier ('(' argumentsList ')')? (EXTENDS extendedClassName = identifier)? body 115 | ; 116 | 117 | tryCatch: 118 | TRY body CATCH '(' identifier ')' body (FINALLY body)? 119 | ; 120 | 121 | throw_: 122 | THROW expression 123 | ; 124 | 125 | jsonPair: 126 | key = expression ':' expression 127 | ; 128 | 129 | jsonObject: 130 | '{' (jsonPair (',' jsonPair)*)? '}' 131 | ; 132 | jsonArray: 133 | '[' (expression (',' expression )*)? ']' 134 | ; 135 | 136 | json: 137 | jsonObject 138 | | jsonArray 139 | ; 140 | 141 | string: STRING; 142 | number: (INT|DECIMAL|FALSE_|TRUE_|NULL_); 143 | identifier: (IDENT); 144 | 145 | atom: (string|number|identifier|json); 146 | 147 | BlockComment 148 | : '/*' .*? '*/' 149 | -> skip 150 | ; 151 | 152 | LineComment 153 | : '//' ~[\r\n]* 154 | -> skip 155 | ; 156 | 157 | // Whitespace 158 | NEWLINE : ['\r\n' | '\r' | '\n']+ ->skip ; 159 | WS : [\t ]+ -> skip ; 160 | 161 | // Keywords 162 | NULL_ : 'null' ; 163 | TRUE_ : 'true' ; 164 | FALSE_ : 'false' ; 165 | EXTENDS : 'extends' ; 166 | NEW : 'new' ; 167 | ELSE : 'else' ; 168 | IF : 'if' ; 169 | RET : 'return' ; 170 | CONTINUE : 'continue' ; 171 | BREAK : 'break' ; 172 | WHILE : 'while' ; 173 | FOR : 'for' ; 174 | VAR : 'var' ; 175 | FUNC : 'function' ; 176 | CLS : 'class' ; 177 | THROW : 'throw' ; 178 | TRY : 'try'; 179 | CATCH : 'catch'; 180 | FINALLY : 'finally'; 181 | SWITCH : 'switch'; 182 | IN : 'in'; 183 | CASE : 'case'; 184 | DEFAULT : 'default'; 185 | STATIC : 'static'; 186 | IMPORT : 'import'; 187 | AS : 'as'; 188 | ASYNC : 'async'; 189 | PRIVATE : 'private'; 190 | TSHARED : 'tshared'; 191 | 192 | // Literals 193 | INT : '0'|[1-9][0-9]* ; 194 | DECIMAL : [0-9][0-9]* '.' [0-9]+ ; 195 | STRING 196 | : '"' SCharSequence? '"' 197 | ; 198 | 199 | fragment 200 | SCharSequence 201 | : SChar+ 202 | ; 203 | 204 | fragment 205 | SChar 206 | : ~["\\\r\n] 207 | | EscapeSequence 208 | | '\\\n' // Added line 209 | | '\\\r\n' // Added line 210 | ; 211 | fragment 212 | EscapeSequence 213 | : '\\' ['"?abfnrtv\\] 214 | ; 215 | 216 | // Identifiers 217 | IDENT : [_]*[A-Za-z0-9_]+ ; --------------------------------------------------------------------------------