├── .gitignore ├── .vscode └── settings.json ├── CMakeLists.txt ├── README.md ├── TinyScript.cpp ├── c_code ├── io.c ├── io.h ├── test.c └── test.h ├── compiler ├── include │ ├── CodeGen │ │ ├── CCode.hpp │ │ └── TinyVMCode.hpp │ ├── Logger.hpp │ ├── Parser │ │ ├── Class.hpp │ │ ├── DataType.hpp │ │ ├── Expression.hpp │ │ ├── Function.hpp │ │ ├── Module.hpp │ │ ├── Node.hpp │ │ ├── Program.hpp │ │ └── Symbol.hpp │ └── Tokenizer.hpp └── src │ ├── CodeGen │ ├── C │ │ ├── CCode.cpp │ │ ├── CExpression.cpp │ │ ├── CFunction.cpp │ │ └── CModule.cpp │ └── TinyVM │ │ ├── TinyVMClass.cpp │ │ ├── TinyVMCode.cpp │ │ ├── TinyVMExpression.cpp │ │ ├── TinyVMFunction.cpp │ │ └── TinyVMModule.cpp │ ├── Logger.cpp │ ├── Parser │ ├── Class.cpp │ ├── DataType.cpp │ ├── Expression.cpp │ ├── Function.cpp │ ├── Module.cpp │ ├── Node.cpp │ ├── Program.cpp │ └── Symbol.cpp │ └── Tokenizer.cpp ├── flags.h ├── std └── io.tiny ├── syntax.tiny └── vm ├── include ├── bytecode.h ├── std.h └── vm.h └── src ├── bytecode.c ├── io.c └── vm.c /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | test_scripts/ 3 | .vscode/ 4 | test.sh 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "array": "cpp", 4 | "*.tcc": "cpp", 5 | "cctype": "cpp", 6 | "clocale": "cpp", 7 | "cmath": "cpp", 8 | "cstdint": "cpp", 9 | "cstdio": "cpp", 10 | "cstdlib": "cpp", 11 | "cwchar": "cpp", 12 | "cwctype": "cpp", 13 | "unordered_map": "cpp", 14 | "vector": "cpp", 15 | "exception": "cpp", 16 | "fstream": "cpp", 17 | "initializer_list": "cpp", 18 | "iosfwd": "cpp", 19 | "iostream": "cpp", 20 | "istream": "cpp", 21 | "limits": "cpp", 22 | "new": "cpp", 23 | "optional": "cpp", 24 | "ostream": "cpp", 25 | "sstream": "cpp", 26 | "stdexcept": "cpp", 27 | "streambuf": "cpp", 28 | "string_view": "cpp", 29 | "system_error": "cpp", 30 | "type_traits": "cpp", 31 | "tuple": "cpp", 32 | "typeinfo": "cpp", 33 | "utility": "cpp", 34 | "ctime": "cpp", 35 | "deque": "cpp", 36 | "functional": "cpp", 37 | "algorithm": "cpp", 38 | "bit": "cpp", 39 | "cstdarg": "cpp", 40 | "map": "cpp", 41 | "memory": "cpp", 42 | "string": "cpp" 43 | } 44 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | project(TinyScript) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | set(CMAKE_CXX_FLAGS "-O") 6 | set(CMAKE_C_FLAGS "-O3") 7 | include_directories(${PROJECT_SOURCE_DIR}) 8 | include_directories(${PROJECT_SOURCE_DIR}/compiler/include) 9 | include_directories(${PROJECT_SOURCE_DIR}/vm/include) 10 | 11 | file(GLOB all_SRCS 12 | "${PROJECT_SOURCE_DIR}/compiler/src/*.cpp" 13 | "${PROJECT_SOURCE_DIR}/compiler/src/Parser/*.cpp" 14 | "${PROJECT_SOURCE_DIR}/compiler/src/CodeGen/TinyVM/*.cpp" 15 | "${PROJECT_SOURCE_DIR}/compiler/src/CodeGen/C/*.cpp" 16 | "${PROJECT_SOURCE_DIR}/vm/src/*.c" 17 | "${PROJECT_SOURCE_DIR}/TinyScript.cpp" 18 | ) 19 | 20 | add_executable(TinyScript ${all_SRCS}) 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TinyScript 2 | A small lightweight, scripting language. Compiles into low level bytecode and has a strict but easy to understand type system 3 | 4 | # Example programs 5 | 6 | ## Hello world 7 | import io 8 | 9 | func main() 10 | io.log("Hello, world!") 11 | 12 | ## Fibonacci 13 | import io 14 | 15 | func fib(int i) -> int 16 | { 17 | if i < 3 18 | return fib(i - 1) + fib(i - 2) 19 | return 1 20 | } 21 | 22 | func main() 23 | io.log(fib(10)) 24 | 25 | ## Arrays 26 | import io 27 | 28 | func main() 29 | { 30 | # Arrays are static, size cannot change 31 | let arr = [1, 2, 3] 32 | 33 | # The type will be 'int array[3]' 34 | io.log(typename arr) 35 | 36 | # This will output '[1, 2, 3]' 37 | io.log(arr) 38 | } 39 | 40 | ## Auto types 41 | import io 42 | 43 | # Type auto will allow any type 44 | func add(auto a, auto b) 45 | -> typeof a + b 46 | { 47 | return a + b 48 | } 49 | 50 | func main() 51 | { 52 | # Type will automatically be determined 53 | let result = add(3, 5.7) 54 | 55 | # int + float -> float, so will output 'float' 56 | io.log(typename result) 57 | } 58 | 59 | -------------------------------------------------------------------------------- /TinyScript.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "Parser/Program.hpp" 4 | #include "CodeGen/TinyVMCode.hpp" 5 | #include "CodeGen/CCode.hpp" 6 | #include "flags.h" 7 | extern "C" 8 | { 9 | #include "vm.h" 10 | #include "std.h" 11 | #include "bytecode.h" 12 | } 13 | using namespace TinyScript; 14 | 15 | void import_std(NodeProgram &prog) 16 | { 17 | prog.add_src("../std/io.tiny"); 18 | } 19 | 20 | int run_bin(string path) 21 | { 22 | FILE *file = fopen(path.c_str(), "rb"); 23 | fseek(file, 0L, SEEK_END); 24 | int len = ftell(file); 25 | rewind(file); 26 | 27 | int main_func; 28 | char *code = (char*)malloc(len - sizeof(int)); 29 | fread(&main_func, 1, sizeof(int), file); 30 | fread(code, 1, len - sizeof(int), file); 31 | 32 | vm_init(); 33 | register_std(); 34 | vm_run(code, main_func, NULL); 35 | free(code); 36 | 37 | return 0; 38 | } 39 | 40 | int main(int argc, char *argv[]) 41 | { 42 | C::Code code("c_code"); 43 | NodeProgram prog; 44 | string output = ""; 45 | string bin = ""; 46 | //import_std(prog); 47 | 48 | // Include all files parsed into compiler 49 | for (int i = 1; i < argc; i++) 50 | { 51 | string arg = argv[i]; 52 | if (arg == "-o" || arg == "--output") 53 | { 54 | if (i >= argc - 1) 55 | Logger::link_error("Expected output file"); 56 | else 57 | output = argv[++i]; 58 | } 59 | else if (arg == "-b" || arg == "--bin") 60 | { 61 | if (i >= argc - 1) 62 | Logger::link_error("Expected bin file"); 63 | else 64 | return run_bin(argv[++i]); 65 | } 66 | else 67 | { 68 | prog.add_src(argv[i]); 69 | } 70 | } 71 | 72 | prog.parse(); 73 | code.compile_program(prog); 74 | /* 75 | vector bytecode = code.link(); 76 | int main_func = code.find_funcion("test.main"); 77 | 78 | char *raw_code = (char*)malloc(bytecode.size()); 79 | memcpy(raw_code, &bytecode[0], bytecode.size()); 80 | 81 | #if DEBUG_ASSEMBLY 82 | printf("\nDisassembly: "); 83 | disassemble(raw_code, bytecode.size()); 84 | printf("\n"); 85 | #endif 86 | 87 | if (!Logger::has_error()) 88 | { 89 | if (output != "") 90 | { 91 | FILE *file = fopen(output.c_str(), "wb"); 92 | fwrite(&main_func, 1, sizeof(int), file); 93 | fwrite(raw_code, 1, bytecode.size(), file); 94 | fclose(file); 95 | return 0; 96 | } 97 | 98 | vm_init(); 99 | register_std(); 100 | vm_run(raw_code, main_func, NULL); 101 | } 102 | 103 | free(raw_code); 104 | */ 105 | } 106 | -------------------------------------------------------------------------------- /c_code/io.c: -------------------------------------------------------------------------------- 1 | #include "io.h" 2 | 3 | -------------------------------------------------------------------------------- /c_code/io.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenJilks/TinyScript/6d132059cda9b6d81f4451b6b37f4606a6c99bdf/c_code/io.h -------------------------------------------------------------------------------- /c_code/test.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenJilks/TinyScript/6d132059cda9b6d81f4451b6b37f4606a6c99bdf/c_code/test.c -------------------------------------------------------------------------------- /c_code/test.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenJilks/TinyScript/6d132059cda9b6d81f4451b6b37f4606a6c99bdf/c_code/test.h -------------------------------------------------------------------------------- /compiler/include/CodeGen/CCode.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "flags.h" 3 | 4 | #if ARC_C 5 | 6 | #include "Parser/Program.hpp" 7 | #include "Parser/Module.hpp" 8 | #include "Parser/Function.hpp" 9 | #include 10 | 11 | namespace TinyScript::C 12 | { 13 | 14 | struct ExpressionData 15 | { 16 | int temp_count; 17 | vector lines; 18 | string value; 19 | }; 20 | 21 | class Code 22 | { 23 | public: 24 | Code(string project_dir); 25 | 26 | ExpressionData compile_expression(NodeExpression *node); 27 | void compile_return(NodeReturn *node); 28 | void compile_assign(NodeAssign *node); 29 | void compile_let(NodeLet *node); 30 | void compile_block(NodeBlock *node); 31 | void compile_function(NodeFunction *node); 32 | void compile_module(NodeModule *node); 33 | void compile_program(NodeProgram &node); 34 | 35 | private: 36 | void start_file(string name); 37 | void write_line(string line); 38 | void write_header(string line); 39 | void start_scope() { scope += 1; } 40 | void end_scope() { scope -= 1; } 41 | 42 | string compile_local_type(DataType type, string name); 43 | string compile_param_type(DataType type); 44 | string compile_function_symbol(Symbol symb); 45 | string compile_expresion_node(ExpressionData &data, ExpDataNode *node); 46 | string compile_rterm(ExpressionData &data, ExpDataNode *node); 47 | string compile_operation(ExpressionData &data, ExpDataNode *node); 48 | string compile_name(ExpressionData &data, ExpDataNode *node); 49 | string compile_array(ExpressionData &data, ExpDataNode *node); 50 | 51 | string project_dir; 52 | std::ofstream curr_file; 53 | std::ofstream curr_file_header; 54 | int scope; 55 | bool has_file; 56 | 57 | }; 58 | 59 | } 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /compiler/include/CodeGen/TinyVMCode.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Parser/Expression.hpp" 3 | #include "Parser/Function.hpp" 4 | #include "Parser/Class.hpp" 5 | #include "Parser/Module.hpp" 6 | #include "Parser/Program.hpp" 7 | #include 8 | using std::tuple; 9 | 10 | namespace TinyScript::TinyVM 11 | { 12 | 13 | class Code 14 | { 15 | public: 16 | Code() {} 17 | 18 | vector link(); 19 | 20 | // Code gen functions 21 | void write_byte(char b); 22 | void write_int(int i); 23 | void write_float(float f); 24 | void write_string(string str); 25 | void write_label(string label); 26 | void assign_label(string label); 27 | int find_funcion(string name); 28 | string gen_label(); 29 | 30 | // Compile nodes 31 | void compile_rexpression(NodeExpression *node); 32 | void compile_lexpression(NodeExpression *node); 33 | void compile_function(NodeFunction *node); 34 | void compile_block(NodeBlock *node); 35 | void compile_let(NodeLet *node); 36 | void compile_assign(NodeAssign *node); 37 | void compile_return(NodeReturn *node); 38 | void compile_if(NodeIf *node); 39 | void compile_for(NodeFor *node); 40 | void compile_while(NodeWhile *node); 41 | void compile_import(NodeImport *node); 42 | void compile_import_from(NodeImportFrom *node); 43 | void compile_external(NodeExtern *node); 44 | void compile_class(NodeClass *node); 45 | void compile_module(NodeModule *node); 46 | void compile_program(NodeProgram &node); 47 | 48 | private: 49 | 50 | // Expression 51 | Symbol find_lvalue_location(ExpDataNode *node); 52 | bool is_static_lvalue(ExpDataNode *node); 53 | void compile_operation(Token op, DataType ltype, DataType rtype); 54 | void compile_call(const Symbol &symb, ExpDataNode *node); 55 | void compile_rname(ExpDataNode *node); 56 | void compile_ref(ExpDataNode *node); 57 | void compile_copy(ExpDataNode *node); 58 | void compile_array(ExpDataNode *node); 59 | void compile_cast(ExpDataNode *node); 60 | void compile_rterm(ExpDataNode *node); 61 | void compile_index(ExpDataNode *node); 62 | void compile_rin(ExpDataNode *node); 63 | void compile_lname(ExpDataNode *node); 64 | void compile_lterm(ExpDataNode *node); 65 | void compile_lin(ExpDataNode *node); 66 | void compile_typesize(ExpDataNode *node); 67 | void compile_typename(ExpDataNode *node); 68 | void compile_arraysize(ExpDataNode *node); 69 | void compile_rvalue(ExpDataNode *node); 70 | void compile_lvalue(ExpDataNode *node); 71 | 72 | // Code data 73 | vector code; 74 | vector externals; 75 | map, int>> labels; 76 | vector used_labels; 77 | 78 | }; 79 | 80 | } 81 | -------------------------------------------------------------------------------- /compiler/include/Logger.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | using std::string; 4 | 5 | namespace TinyScript 6 | { 7 | 8 | struct DebugInfo 9 | { 10 | int line_no; 11 | int char_no; 12 | int file_pos; 13 | string file_name; 14 | }; 15 | 16 | namespace Logger 17 | { 18 | 19 | void reset(); 20 | void start_scope(); 21 | void end_scope(); 22 | 23 | void log(const DebugInfo &debug_info, string msg); 24 | 25 | void warning(const DebugInfo &debug_info, string msg); 26 | void error(const DebugInfo &debug_info, string msg); 27 | void link_error(string msg); 28 | bool has_error(); 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /compiler/include/Parser/Class.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Node.hpp" 3 | #include "DataType.hpp" 4 | 5 | namespace TinyScript 6 | { 7 | 8 | class NodeClass : public NodeBlock 9 | { 10 | public: 11 | NodeClass(Node *parent) : 12 | NodeBlock(parent, true), 13 | is_complete(false), 14 | being_proccessed(false) {} 15 | 16 | ~NodeClass(); 17 | 18 | virtual NodeType get_type() { return NodeType::Class; } 19 | virtual void parse(Tokenizer &tk); 20 | virtual Node *copy(Node *parent); 21 | void register_class(); 22 | void register_methods(); 23 | 24 | private: 25 | void parse_attr(Tokenizer &tk); 26 | void parse_method(Tokenizer &tk); 27 | 28 | Token name; 29 | DataConstruct *construct; 30 | vector> attrs; 31 | bool is_complete; 32 | bool being_proccessed; 33 | 34 | }; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /compiler/include/Parser/DataType.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Node.hpp" 3 | 4 | namespace TinyScript 5 | { 6 | 7 | struct DataTypeModifier 8 | { 9 | Token name; 10 | int size; 11 | }; 12 | 13 | class NodeDataType : public Node 14 | { 15 | public: 16 | NodeDataType(Node *parent) : 17 | Node(parent), is_auto_flag(false) {} 18 | 19 | virtual void parse(Tokenizer &tk); 20 | virtual NodeType get_type() { return NodeType::DataType; } 21 | virtual Node *copy(Node *parent); 22 | 23 | DataType compile(); 24 | inline bool is_auto() const { return is_auto_flag; } 25 | 26 | private: 27 | Token type_name; 28 | vector modifiers; 29 | bool is_auto_flag; 30 | 31 | void parse_array(Tokenizer &tk); 32 | DataType compile_ref(DataType base); 33 | DataType compile_array(DataType base, DataTypeModifier mod); 34 | 35 | }; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /compiler/include/Parser/Expression.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Node.hpp" 3 | #include "Tokenizer.hpp" 4 | #include "Symbol.hpp" 5 | #include 6 | using std::function; 7 | 8 | #define NODE_OPERATION 0b10000000 9 | #define NODE_CALL 0b01000000 10 | #define NODE_INDEX 0b00100000 11 | #define NODE_CAST 0b00010000 12 | #define NODE_ARRAY 0b00001000 13 | #define NODE_TEMP_REF 0b00000100 14 | #define NODE_IN 0b00000010 15 | #define NODE_ARGS_LIST 0b00000001 16 | 17 | namespace TinyScript 18 | { 19 | 20 | struct ExpDataNode 21 | { 22 | ExpDataNode *left; 23 | ExpDataNode *right; 24 | DataType type; 25 | int flags; 26 | 27 | Token token; 28 | vector args; 29 | Symbol symb; 30 | }; 31 | 32 | class NodeExpression : public Node 33 | { 34 | public: 35 | NodeExpression(Node *parent) : 36 | Node(parent) {} 37 | 38 | virtual NodeType get_type() { return NodeType::Expression; } 39 | virtual void parse(Tokenizer &tk); 40 | virtual Node *copy(Node *parent); 41 | void symbolize(); 42 | 43 | inline DataType get_data_type() const { return exp->type; } 44 | inline ExpDataNode *get_data() const { return exp; } 45 | bool is_static_value() const; 46 | int get_int_value() const; 47 | 48 | ~NodeExpression() { free_node(exp); } 49 | 50 | private: 51 | // Compiler functions 52 | ExpDataNode *parse_type_name(Tokenizer &tk, ExpDataNode *node); 53 | ExpDataNode *parse_type_size(Tokenizer &tk, ExpDataNode *node); 54 | ExpDataNode *parse_array_size(Tokenizer &tk, ExpDataNode *node); 55 | ExpDataNode *parse_in(Tokenizer &tk, ExpDataNode *node); 56 | ExpDataNode *parse_cast(Tokenizer &tk, ExpDataNode *node); 57 | ExpDataNode *parse_indies(Tokenizer &tk, ExpDataNode *node); 58 | ExpDataNode *parse_transforms(Tokenizer &tk, ExpDataNode *node); 59 | 60 | DataType parse_array_type(Tokenizer &tk, DataType of); 61 | ExpDataNode *parse_term(Tokenizer &tk); 62 | void parse_array(Tokenizer &tk, ExpDataNode *node); 63 | void parse_ref(Tokenizer &tk, ExpDataNode *node); 64 | void parse_copy(Tokenizer &tk, ExpDataNode *node); 65 | void parse_negate(Tokenizer &tk, ExpDataNode *node); 66 | void parse_name(Tokenizer &tk, ExpDataNode *node); 67 | void parse_args(Tokenizer &tk, ExpDataNode *node); 68 | ExpDataNode *parse_sub_expression(Tokenizer &tk, ExpDataNode *node); 69 | 70 | template 71 | ExpDataNode *parse_operation(Tokenizer &tk, Func next_term, 72 | vector ops); 73 | 74 | DataType parse_operation_type(ExpDataNode *left, ExpDataNode *right, Token op); 75 | ExpDataNode *parse_expression(Tokenizer &tk); 76 | void free_node(ExpDataNode *node); 77 | void symbolize_node(ExpDataNode *node); 78 | 79 | // Symbolizer 80 | void symbolize_module_attr(ExpDataNode *node, const Symbol &left_symb); 81 | void symbolize_ref(ExpDataNode *node); 82 | void symbolize_copy(ExpDataNode *node); 83 | void symbolize_typesize(ExpDataNode *node); 84 | void symbolize_typename(ExpDataNode *node); 85 | void symbolize_arraysize(ExpDataNode *node); 86 | void symbolize_array(ExpDataNode *node); 87 | bool symbolize_special(ExpDataNode *node); 88 | 89 | // Expression data 90 | ExpDataNode *exp; 91 | }; 92 | 93 | } 94 | -------------------------------------------------------------------------------- /compiler/include/Parser/Function.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Node.hpp" 3 | #include "Expression.hpp" 4 | #include "DataType.hpp" 5 | 6 | namespace TinyScript 7 | { 8 | 9 | class NodeCodeBlock : public NodeBlock 10 | { 11 | public: 12 | NodeCodeBlock(Node *parent, bool is_allocator = false) : 13 | NodeBlock(parent, is_allocator) {} 14 | 15 | // Copy constructor 16 | NodeCodeBlock(NodeCodeBlock *other) : 17 | NodeBlock(other) {} 18 | 19 | protected: 20 | void parse_statement(Tokenizer &tk); 21 | void parse_block(Tokenizer &tk); 22 | 23 | }; 24 | 25 | class NodeLet : public Node 26 | { 27 | public: 28 | NodeLet(Node *parent) : 29 | Node(parent) {} 30 | 31 | ~NodeLet(); 32 | 33 | virtual NodeType get_type() { return NodeType::Let; } 34 | virtual void parse(Tokenizer &tk); 35 | virtual Node *copy(Node *parent); 36 | void symbolize(); 37 | 38 | inline Token get_name() const { return name; } 39 | inline Symbol get_symb() const { return symb; } 40 | inline NodeExpression *get_value() const { return value; } 41 | 42 | private: 43 | Token name; 44 | Symbol symb; 45 | NodeExpression *value; 46 | 47 | NodeDataType *static_type; 48 | bool use_static_type; 49 | 50 | }; 51 | 52 | class NodeAssign : public Node 53 | { 54 | public: 55 | NodeAssign(Node *parent) : 56 | Node(parent) {} 57 | 58 | ~NodeAssign(); 59 | 60 | virtual NodeType get_type() { return NodeType::Assign; } 61 | virtual void parse(Tokenizer &tk); 62 | virtual Node *copy(Node *parent); 63 | inline NodeExpression *get_left() const { return left; } 64 | inline NodeExpression *get_right() const { return right; } 65 | 66 | private: 67 | NodeExpression *left, *right; 68 | 69 | }; 70 | 71 | class NodeReturn : public Node 72 | { 73 | public: 74 | NodeReturn(Node *parent) : 75 | Node(parent) {} 76 | 77 | ~NodeReturn(); 78 | 79 | virtual NodeType get_type() { return NodeType::Return; } 80 | virtual void parse(Tokenizer &tk); 81 | virtual Node *copy(Node *parent); 82 | inline NodeExpression *get_value() const { return value; } 83 | 84 | private: 85 | NodeExpression *value; 86 | 87 | }; 88 | 89 | class NodeIf : public NodeCodeBlock 90 | { 91 | public: 92 | NodeIf(Node *parent) : 93 | NodeCodeBlock(parent) {} 94 | 95 | ~NodeIf(); 96 | 97 | virtual NodeType get_type() { return NodeType::If; } 98 | virtual void parse(Tokenizer &tk); 99 | virtual Node *copy(Node *parent); 100 | inline NodeExpression *get_condition() const { return condition; } 101 | 102 | private: 103 | NodeExpression *condition; 104 | 105 | }; 106 | 107 | class NodeFor : public NodeCodeBlock 108 | { 109 | public: 110 | NodeFor(Node *parent) : 111 | NodeCodeBlock(parent) {} 112 | 113 | ~NodeFor(); 114 | 115 | virtual NodeType get_type() { return NodeType::For; } 116 | virtual void parse(Tokenizer &tk); 117 | virtual Node *copy(Node *parent); 118 | 119 | inline NodeExpression *get_left() const { return left; } 120 | inline NodeExpression *get_from() const { return from; } 121 | inline NodeExpression *get_to() const { return to; } 122 | 123 | private: 124 | NodeExpression *left; 125 | NodeExpression *from, *to; 126 | 127 | }; 128 | 129 | class NodeWhile : public NodeCodeBlock 130 | { 131 | public: 132 | NodeWhile(Node *parent) : 133 | NodeCodeBlock(parent) {} 134 | 135 | ~NodeWhile(); 136 | 137 | virtual NodeType get_type() { return NodeType::While; } 138 | virtual void parse(Tokenizer &tk); 139 | virtual Node *copy(Node *parent); 140 | 141 | inline NodeExpression *get_condition() const { return condition; } 142 | 143 | private: 144 | NodeExpression *condition; 145 | 146 | }; 147 | 148 | struct FunctionParam 149 | { 150 | Token name; 151 | NodeDataType *type_node; 152 | DataType type; 153 | }; 154 | 155 | class NodeFunction : public NodeCodeBlock 156 | { 157 | public: 158 | NodeFunction(Node *parent) : 159 | NodeCodeBlock(parent, true), 160 | is_compiled_flag(false), 161 | arg_size(0) {} 162 | 163 | // Copy constructor 164 | NodeFunction(NodeFunction *other) : 165 | NodeCodeBlock(other), 166 | name(other->name), 167 | symb(other->symb), 168 | arg_size(other->arg_size), 169 | is_template_flag(other->is_template_flag) {} 170 | 171 | ~NodeFunction(); 172 | 173 | virtual NodeType get_type() { return NodeType::Function; } 174 | virtual void parse(Tokenizer &tk); 175 | virtual Node *copy(Node *parent); 176 | inline Token get_name() const { return name; } 177 | inline Symbol get_symb() const { return symb; } 178 | inline int get_arg_size() const { return arg_size; } 179 | void make_method(DataConstruct *construct); 180 | void register_func(); 181 | 182 | const Symbol &implement(vector params); 183 | inline bool is_template() const { return is_template_flag; } 184 | inline bool is_compiled() const { return is_compiled_flag; } 185 | inline void set_compiled() { is_compiled_flag = true; } 186 | inline vector get_params() const { return params; } 187 | 188 | private: 189 | void parse_params(Tokenizer &tk); 190 | void parse_return_type(Tokenizer &tk); 191 | 192 | Token name; 193 | Symbol symb; 194 | vector params; 195 | NodeDataType *return_type_node; 196 | 197 | int arg_size; 198 | bool is_template_flag; 199 | bool is_compiled_flag; 200 | 201 | }; 202 | 203 | } 204 | -------------------------------------------------------------------------------- /compiler/include/Parser/Module.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Node.hpp" 3 | #include "DataType.hpp" 4 | 5 | namespace TinyScript 6 | { 7 | 8 | class NodeModule; 9 | class NodeImportFrom : public Node 10 | { 11 | public: 12 | NodeImportFrom(Node *parent) : 13 | Node(parent) {} 14 | 15 | virtual NodeType get_type() { return NodeType::ImportFrom; } 16 | virtual void parse(Tokenizer &tk); 17 | virtual Node *copy(Node *parent); 18 | void symbolize(); 19 | void register_types(); 20 | 21 | private: 22 | Token module; 23 | Token attr; 24 | NodeModule *mod; 25 | 26 | }; 27 | 28 | class NodeImport : public Node 29 | { 30 | public: 31 | NodeImport(Node *parent) : 32 | Node(parent) {} 33 | 34 | virtual NodeType get_type() { return NodeType::Import; } 35 | virtual void parse(Tokenizer &tk); 36 | virtual Node *copy(Node *parent); 37 | void symbolize(); 38 | 39 | private: 40 | Token module; 41 | 42 | }; 43 | 44 | class NodeExtern : public Node 45 | { 46 | public: 47 | NodeExtern(Node *parent) : 48 | Node(parent) {} 49 | 50 | ~NodeExtern(); 51 | 52 | virtual NodeType get_type() { return NodeType::Extern; } 53 | virtual void parse(Tokenizer &tk); 54 | virtual Node *copy(Node *parent); 55 | void register_extern(); 56 | 57 | inline Symbol get_symb() const { return symb; } 58 | 59 | private: 60 | Token name; 61 | vector param_nodes; 62 | NodeDataType *return_type_node; 63 | Symbol symb; 64 | 65 | }; 66 | 67 | class NodeModule : public NodeBlock 68 | { 69 | public: 70 | NodeModule(Node *parent = nullptr) : 71 | NodeBlock(parent), 72 | is_compiled_flag(false) {} 73 | 74 | virtual NodeType get_type() { return NodeType::Module; } 75 | virtual void parse(Tokenizer &tk); 76 | virtual Node *copy(Node *parent); 77 | 78 | void set_name(Token name); 79 | inline Token get_name() const { return name; } 80 | inline void flag_compiled() { is_compiled_flag = true; } 81 | bool is_compiled() const; 82 | 83 | void register_types(); 84 | void register_functions(); 85 | 86 | private: 87 | Token name; 88 | bool is_compiled_flag; 89 | 90 | }; 91 | 92 | } 93 | -------------------------------------------------------------------------------- /compiler/include/Parser/Node.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Tokenizer.hpp" 3 | #include "Symbol.hpp" 4 | #include 5 | using std::vector; 6 | 7 | namespace TinyScript 8 | { 9 | 10 | enum class NodeType 11 | { 12 | Program, 13 | Module, 14 | Import, 15 | ImportFrom, 16 | Extern, 17 | Class, 18 | Function, 19 | Let, 20 | Assign, 21 | Return, 22 | If, 23 | For, 24 | While, 25 | Expression, 26 | DataType, 27 | }; 28 | 29 | class Node 30 | { 31 | public: 32 | Node(Node *parent, bool is_allocator = false) : 33 | parent(parent), is_allocator(is_allocator) {} 34 | 35 | virtual ~Node() {} 36 | 37 | Node *get_parent() const { return parent; } 38 | Node *get_parent(NodeType type); 39 | string get_prefix() const; 40 | virtual NodeType get_type() = 0; 41 | virtual void parse(Tokenizer &tk) = 0; 42 | virtual Node *copy(Node *parent) = 0; 43 | 44 | // Symbol table functions 45 | vector lookup_all(string name) const; 46 | vector lookup_all() const; 47 | const Symbol &lookup(string name) const; 48 | const Symbol &lookup(string name, vector params) const; 49 | DataConstruct *find_construct(string name); 50 | vector find_construct(); 51 | int allocate(int size); 52 | void push_symbol(Symbol symb); 53 | inline int get_scope_size() const { return table.get_scope_size(); } 54 | inline DataConstruct *create_construct(string name) { return table.create_construct(name); } 55 | inline void add_construct(DataConstruct *construct) { table.add_construct(construct); } 56 | 57 | protected: 58 | void copy_node(Node *other); 59 | SymbolTable table; 60 | string prefix; 61 | 62 | template 63 | T *parse_node(Tokenizer &tk) 64 | { 65 | T *node = new T(this); 66 | node->parse(tk); 67 | return node; 68 | } 69 | 70 | private: 71 | DataType parse_array_type(Tokenizer &tk, DataType of); 72 | 73 | Node *parent; 74 | bool is_allocator; 75 | 76 | }; 77 | 78 | class NodeBlock : public Node 79 | { 80 | public: 81 | NodeBlock(Node *parent, bool is_allocator = false) : 82 | Node(parent, is_allocator) {} 83 | 84 | virtual ~NodeBlock(); 85 | 86 | inline int get_child_size() const { return children.size(); } 87 | inline void add_child(Node *child) { children.push_back(child); } 88 | inline Node *operator[] (int index) { return children[index]; } 89 | 90 | protected: 91 | void copy_block(NodeBlock *to); 92 | vector children; 93 | 94 | template 95 | void parse_child(Tokenizer &tk) 96 | { 97 | add_child(parse_node(tk)); 98 | } 99 | 100 | }; 101 | 102 | } 103 | -------------------------------------------------------------------------------- /compiler/include/Parser/Program.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Node.hpp" 3 | #include "Module.hpp" 4 | 5 | namespace TinyScript 6 | { 7 | 8 | class NodeProgram : public NodeBlock 9 | { 10 | public: 11 | NodeProgram(); 12 | 13 | inline void add_src(string path) { srcs.push_back(path); } 14 | inline void add_module(NodeModule *mod) { add_child(mod); } 15 | virtual NodeType get_type() { return NodeType::Program; } 16 | virtual void parse(Tokenizer &tk) {} 17 | virtual Node *copy(Node *parent) { return this; } 18 | void parse(); 19 | 20 | NodeModule *find_module(string name) const; 21 | NodeModule *load_module(string name); 22 | void pre_process(); 23 | 24 | private: 25 | vector srcs; 26 | string cwd; 27 | 28 | }; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /compiler/include/Parser/Symbol.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include "Logger.hpp" 6 | using std::string; 7 | using std::vector; 8 | using std::shared_ptr; 9 | 10 | #define DATATYPE_REF 0b1000 11 | #define DATATYPE_ARRAY 0b0100 12 | #define DATATYPE_AUTO 0b0010 13 | #define DATATYPE_NULL 0b0001 14 | 15 | #define SYMBOL_FUNCTION 0b10000000000 16 | #define SYMBOL_LOCAL 0b01000000000 17 | #define SYMBOL_ARG 0b00100000000 18 | #define SYMBOL_GLOBAL 0b00010000000 19 | #define SYMBOL_CONST 0b00001000000 20 | #define SYMBOL_PRIVATE 0b00000100000 21 | #define SYMBOL_READONLY 0b00000010000 22 | #define SYMBOL_EXTERNAL 0b00000001000 23 | #define SYMBOL_TEMPLATE 0b00000000100 24 | #define SYMBOL_MODULE 0b00000000010 25 | #define SYMBOL_NULL 0b00000000001 26 | 27 | namespace TinyScript 28 | { 29 | 30 | struct DataConstruct; 31 | struct DataType 32 | { 33 | DataConstruct *construct; 34 | int flags; 35 | 36 | int array_size; 37 | shared_ptr sub_type; 38 | 39 | static int find_size(const DataType &type); 40 | static bool equal(const DataType &a, const DataType &b); 41 | static bool can_cast_to(const DataType &from, const DataType &to, bool &warning); 42 | static string printout(const DataType &type); 43 | }; 44 | 45 | class Node; 46 | struct Symbol 47 | { 48 | Symbol() {} 49 | 50 | Symbol(string name, DataType type, int flags, int location, Node *parent) : 51 | name(name), type(type), flags(flags), location(location), parent(parent) {} 52 | 53 | static string printout(const Symbol &symb); 54 | 55 | string name; 56 | DataType type; 57 | int flags; 58 | 59 | vector params; 60 | int location; 61 | Node *parent; 62 | }; 63 | 64 | class SymbolTable 65 | { 66 | public: 67 | SymbolTable() : 68 | allocator(0), 69 | scope_size(0) {} 70 | 71 | ~SymbolTable(); 72 | 73 | void push(Symbol symb); 74 | void push_all(vector symbs); 75 | void patch_params(vector params); 76 | 77 | vector lookup_all(string name) const; 78 | vector lookup_all() const { return symbols; } 79 | const Symbol &lookup(string name) const; 80 | const Symbol &lookup(string name, vector params) const; 81 | 82 | vector find_externals() const; 83 | vector find_construct(); 84 | DataConstruct *find_construct(string name); 85 | DataConstruct *create_construct(string name); 86 | inline void add_construct(DataConstruct *construct) { external_constructs.push_back(construct); } 87 | 88 | void new_allocation_space(); 89 | int allocate(int size); 90 | int get_scope_size() const; 91 | 92 | static bool is_null(const Symbol &symb); 93 | static const Symbol &get_null() { return null_symbol; } 94 | 95 | private: 96 | vector symbols; 97 | vector constructs; 98 | vector external_constructs; 99 | int scope_size; 100 | int allocator; 101 | static Symbol null_symbol; 102 | 103 | }; 104 | 105 | struct DataConstruct 106 | { 107 | string name; 108 | int size; 109 | Node *parent; 110 | }; 111 | 112 | class PrimTypes 113 | { 114 | public: 115 | PrimTypes() {} 116 | 117 | static inline DataConstruct* type_int() { return &dt_int; } 118 | static inline DataConstruct* type_float() { return &dt_float; } 119 | static inline DataConstruct* type_char() { return &dt_char; } 120 | static inline DataConstruct* type_bool() { return &dt_bool; } 121 | static inline DataConstruct* type_null() { return &dt_null; } 122 | 123 | private: 124 | static DataConstruct dt_int; 125 | static DataConstruct dt_float; 126 | static DataConstruct dt_char; 127 | static DataConstruct dt_bool; 128 | static DataConstruct dt_null; 129 | 130 | }; 131 | 132 | } 133 | -------------------------------------------------------------------------------- /compiler/include/Tokenizer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Logger.hpp" 7 | using std::string; 8 | using std::vector; 9 | using std::map; 10 | 11 | namespace TinyScript 12 | { 13 | 14 | enum class TokenType 15 | { 16 | // Datatypes 17 | Name, 18 | Int, 19 | Float, 20 | Bool, 21 | Char, 22 | String, 23 | Auto, 24 | 25 | // Operations 26 | Add, 27 | Subtract, 28 | Multiply, 29 | Divide, 30 | MoreThan, 31 | LessThan, 32 | MoreThanEquals, 33 | LessThanEquals, 34 | Equals, 35 | 36 | // Memory management 37 | Assign, 38 | Ref, 39 | Array, 40 | Copy, 41 | 42 | // Scope definers 43 | OpenBlock, 44 | CloseBlock, 45 | OpenArg, 46 | CloseArg, 47 | OpenIndex, 48 | CloseIndex, 49 | Next, 50 | Gives, 51 | Of, 52 | As, 53 | In, 54 | 55 | // Key words 56 | Func, 57 | Class, 58 | Let, 59 | Return, 60 | If, 61 | For, 62 | While, 63 | From, 64 | To, 65 | Import, 66 | Extern, 67 | 68 | // Compiler functions 69 | TypeName, 70 | TypeSize, 71 | ArraySize, 72 | TypeOf, 73 | 74 | // Misc 75 | Eof, 76 | }; 77 | 78 | struct Token 79 | { 80 | string data; 81 | TokenType type; 82 | DebugInfo debug_info; 83 | }; 84 | 85 | class Tokenizer 86 | { 87 | 88 | public: 89 | Tokenizer() {} 90 | Tokenizer(string file_path); 91 | Tokenizer(Tokenizer &other); 92 | inline const Token &get_look() const { return look; }; 93 | inline const DebugInfo &get_debug_info() const { return debug_info; } 94 | bool is_eof() const; 95 | void set_pos(const DebugInfo &pos); 96 | 97 | Token match(TokenType type, string name); 98 | Token skip_token(); 99 | void skip_scope(); 100 | 101 | private: 102 | std::ifstream file; 103 | string file_path; 104 | Token look; 105 | bool eof_override; 106 | 107 | vector back_buffer; 108 | DebugInfo debug_info; 109 | 110 | Token next_token(); 111 | Token parse_double(char c, TokenType def, map m, DebugInfo dbi); 112 | Token parse_name(char c, DebugInfo dbi); 113 | Token parse_number(char c, DebugInfo dbi); 114 | Token parse_char(DebugInfo dbi); 115 | Token parse_string(DebugInfo dbi); 116 | char next_escaped_char(); 117 | char next_char(); 118 | void skip_whitespace(); 119 | void parse_comment(); 120 | 121 | }; 122 | 123 | } 124 | -------------------------------------------------------------------------------- /compiler/src/CodeGen/C/CCode.cpp: -------------------------------------------------------------------------------- 1 | #include "flags.h" 2 | 3 | #if ARC_C 4 | 5 | #include "CodeGen/CCode.hpp" 6 | #include 7 | using namespace TinyScript::C; 8 | 9 | Code::Code(string project_dir) : 10 | project_dir(project_dir), 11 | has_file(false) 12 | { 13 | // Create the C project folder 14 | mkdir(project_dir.c_str(), 15 | S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); 16 | } 17 | 18 | void Code::start_file(string name) 19 | { 20 | // If a file was open, close it 21 | if (has_file) 22 | { 23 | curr_file.close(); 24 | curr_file_header.close(); 25 | } 26 | 27 | // Open up a new source file 28 | curr_file = std::ofstream(project_dir + "/" + name + ".c"); 29 | curr_file_header = std::ofstream(project_dir + "/" + name + ".h"); 30 | has_file = true; 31 | scope = 0; 32 | } 33 | 34 | void Code::write_line(string line) 35 | { 36 | if (has_file) 37 | { 38 | // Write the scope tabs and end of line tokens 39 | for (int i = 0; i < scope; i++) 40 | line = " " + line; 41 | line += '\n'; 42 | 43 | // Write the line to the file 44 | curr_file.write(line.c_str(), line.length()); 45 | } 46 | } 47 | 48 | void Code::write_header(string line) 49 | { 50 | if (has_file) 51 | { 52 | line += '\n'; 53 | curr_file_header.write(line.c_str(), line.length()); 54 | } 55 | } 56 | 57 | string Code::compile_local_type(DataType type, string name) 58 | { 59 | string construct = "error"; 60 | int ref_count = 0; 61 | vector array_sizes; 62 | 63 | // Count refs and arrays 64 | DataType curr_type = type; 65 | while (true) 66 | { 67 | if (curr_type.flags & DATATYPE_REF) 68 | { 69 | // Turn all arrays into pointers 70 | ref_count += array_sizes.size() + 1; 71 | array_sizes.clear(); 72 | } 73 | else if (curr_type.flags & DATATYPE_ARRAY) 74 | array_sizes.push_back(curr_type.array_size); 75 | else 76 | break; 77 | 78 | curr_type = *curr_type.sub_type; 79 | } 80 | 81 | // Set base type name 82 | if (curr_type.construct != nullptr) 83 | { 84 | if (curr_type.construct == PrimTypes::type_null()) 85 | construct = "void"; 86 | else 87 | construct = curr_type.construct->name; 88 | } 89 | 90 | // Build name string 91 | string out = construct; 92 | for (int i = 0; i < ref_count; i++) 93 | out += "*"; 94 | out += " " + name; 95 | for (int array_size : array_sizes) 96 | out += "[" + std::to_string(array_size) + "]"; 97 | return out; 98 | } 99 | 100 | string Code::compile_param_type(DataType type) 101 | { 102 | if (type.flags & DATATYPE_REF) 103 | return compile_param_type(type) + "*"; 104 | 105 | if (type.flags & DATATYPE_ARRAY) 106 | { 107 | return compile_param_type(type) + 108 | "[" + std::to_string(type.array_size) + "]"; 109 | } 110 | 111 | if (type.construct == nullptr) 112 | return "error"; 113 | if (type.construct == PrimTypes::type_null()) 114 | return "void"; 115 | return type.construct->name; 116 | } 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /compiler/src/CodeGen/C/CExpression.cpp: -------------------------------------------------------------------------------- 1 | #include "flags.h" 2 | #if ARC_C 3 | 4 | #include "CodeGen/CCode.hpp" 5 | using namespace TinyScript::C; 6 | 7 | ExpressionData Code::compile_expression(NodeExpression *node) 8 | { 9 | ExpressionData data; 10 | data.temp_count = 0; 11 | 12 | node->symbolize(); 13 | data.value = compile_expresion_node(data, node->get_data()); 14 | return data; 15 | } 16 | 17 | string Code::compile_operation(ExpressionData &data, ExpDataNode *node) 18 | { 19 | string left = compile_expresion_node(data, node->left); 20 | string right = compile_expresion_node(data, node->right); 21 | 22 | string op = "error"; 23 | switch (node->token.type) 24 | { 25 | case TokenType::Add: op = "+"; break; 26 | case TokenType::Subtract: op = "-"; break; 27 | case TokenType::Multiply: op = "*"; break; 28 | case TokenType::Divide: op = "/"; break; 29 | } 30 | 31 | return "(" + left + " " + op + " " + right + ")"; 32 | } 33 | 34 | string Code::compile_name(ExpressionData &data, ExpDataNode *node) 35 | { 36 | string out = node->token.data; 37 | if (node->flags & NODE_ARGS_LIST) 38 | { 39 | out += "("; 40 | for (int i = 0; i < node->args.size(); i++) 41 | { 42 | out += compile_expresion_node(data, node->args[i]); 43 | if (i < node->args.size() - 1) 44 | out += ", "; 45 | } 46 | out += ")"; 47 | } 48 | 49 | return out; 50 | } 51 | 52 | string Code::compile_array(ExpressionData &data, ExpDataNode *node) 53 | { 54 | string out = "{"; 55 | for (int i = 0; i < node->args.size(); i++) 56 | { 57 | ExpDataNode *value = node->args[i]; 58 | out += compile_expresion_node(data, value); 59 | if (i < node->args.size() - 1) 60 | out += ", "; 61 | } 62 | 63 | return out + "}"; 64 | } 65 | 66 | string Code::compile_rterm(ExpressionData &data, ExpDataNode *node) 67 | { 68 | string value = node->token.data; 69 | switch (node->token.type) 70 | { 71 | case TokenType::Int: return value; 72 | case TokenType::Float: return value + "f"; 73 | case TokenType::Char: return "'" + value + "'"; 74 | case TokenType::Bool: return value == "true" ? "1" : "0"; 75 | case TokenType::String: return "\"" + value + "\""; 76 | case TokenType::Name: return compile_name(data, node); 77 | case TokenType::OpenIndex: return compile_array(data, node); 78 | case TokenType::Ref: return "&" + compile_expresion_node(data, node->left); 79 | case TokenType::Copy: return "*" + compile_expresion_node(data, node->left); 80 | } 81 | 82 | return "error"; 83 | } 84 | 85 | string Code::compile_expresion_node(ExpressionData &data, ExpDataNode *node) 86 | { 87 | if (node->flags & NODE_OPERATION) 88 | return compile_operation(data, node); 89 | 90 | if (node->flags & NODE_INDEX) 91 | { 92 | return compile_expresion_node(data, node->left) + 93 | "[" + compile_expresion_node(data, node->right) + "]"; 94 | } 95 | 96 | if (node->flags & NODE_IN) 97 | { 98 | return compile_expresion_node(data, node->left) + 99 | "." + compile_expresion_node(data, node->right); 100 | } 101 | 102 | return compile_rterm(data, node); 103 | } 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /compiler/src/CodeGen/C/CFunction.cpp: -------------------------------------------------------------------------------- 1 | #include "flags.h" 2 | #if ARC_C 3 | 4 | #include "CodeGen/CCode.hpp" 5 | using namespace TinyScript::C; 6 | 7 | void Code::compile_return(NodeReturn *node) 8 | { 9 | ExpressionData data = compile_expression(node->get_value()); 10 | write_line("return " + data.value + ";"); 11 | } 12 | 13 | void Code::compile_assign(NodeAssign *node) 14 | { 15 | NodeExpression *lvalue = node->get_left(); 16 | NodeExpression *rvalue = node->get_right(); 17 | if (rvalue) rvalue->symbolize(); 18 | lvalue->symbolize(); 19 | 20 | ExpressionData ldata = compile_expression(lvalue); 21 | string assign = ldata.value; 22 | if (rvalue) 23 | { 24 | ExpressionData rdata = compile_expression(rvalue); 25 | assign += " = " + rdata.value; 26 | } 27 | write_line(assign + ";"); 28 | } 29 | 30 | void Code::compile_let(NodeLet *node) 31 | { 32 | NodeExpression *value = node->get_value(); 33 | if (value != nullptr) 34 | value->symbolize(); 35 | node->symbolize(); 36 | 37 | Symbol symb = node->get_symb(); 38 | string let = compile_local_type(symb.type, symb.name); 39 | 40 | if (value != nullptr) 41 | { 42 | ExpressionData data = compile_expression(value); 43 | let += " = " + data.value; 44 | } 45 | write_line(let + ";"); 46 | } 47 | 48 | void Code::compile_block(NodeBlock *node) 49 | { 50 | for (int i = 0; i < node->get_child_size(); i++) 51 | { 52 | Node *child = (*node)[i]; 53 | switch (child->get_type()) 54 | { 55 | case NodeType::Let: compile_let((NodeLet*)child); break; 56 | case NodeType::Assign: compile_assign((NodeAssign*)child); break; 57 | case NodeType::Return: compile_return((NodeReturn*)child); break; 58 | } 59 | } 60 | } 61 | 62 | string Code::compile_function_symbol(Symbol symb) 63 | { 64 | NodeFunction *node = nullptr; 65 | DataType return_type = symb.type; 66 | string name = symb.name; 67 | 68 | if (symb.parent != nullptr) 69 | if (symb.parent->get_type() == NodeType::Function) 70 | node = (NodeFunction*)symb.parent; 71 | 72 | string func = compile_param_type(return_type) + " " + name + "("; 73 | for (int i = 0; i < symb.params.size(); i++) 74 | { 75 | DataType type = symb.params[i]; 76 | string name = node ? node->get_params()[i].name.data : ""; 77 | 78 | func += compile_local_type(type, name); 79 | if (i < symb.params.size() - 1) 80 | func += ", "; 81 | } 82 | return func + ")"; 83 | } 84 | 85 | void Code::compile_function(NodeFunction *node) 86 | { 87 | if (node->is_template()) 88 | return; 89 | 90 | string header = compile_function_symbol(node->get_symb()); 91 | write_line("// func " + Symbol::printout(node->get_symb())); 92 | write_line(header); 93 | write_line("{"); 94 | start_scope(); 95 | compile_block((NodeBlock*)node); 96 | end_scope(); 97 | write_line("}\n"); 98 | } 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /compiler/src/CodeGen/C/CModule.cpp: -------------------------------------------------------------------------------- 1 | #include "flags.h" 2 | 3 | #if ARC_C 4 | 5 | #include "CodeGen/CCode.hpp" 6 | #include "Parser/Class.hpp" 7 | using namespace TinyScript::C; 8 | 9 | void Code::compile_module(NodeModule *node) 10 | { 11 | // Start a new file for the module 12 | string name = node->get_name().data; 13 | start_file(name); 14 | write_line("#include \"" + name + ".h\"\n"); 15 | 16 | // Compile function headers 17 | for (Symbol symb : node->lookup_all()) 18 | { 19 | if (symb.flags & SYMBOL_FUNCTION) 20 | { 21 | string header = compile_function_symbol(symb); 22 | write_header(header + ";"); 23 | } 24 | } 25 | 26 | // Compile type headers 27 | for (DataConstruct *construct : node->find_construct()) 28 | { 29 | write_header("typedef struct " + construct->name); 30 | write_header("{"); 31 | for (Symbol attr : ((NodeClass*)construct->parent)->lookup_all()) 32 | if (attr.flags & SYMBOL_LOCAL) 33 | write_header(" " + compile_local_type(attr.type, attr.name) + ";"); 34 | write_header("} " + construct->name + ";"); 35 | } 36 | 37 | for (int i = 0; i < node->get_child_size(); i++) 38 | { 39 | Node *child = (*node)[i]; 40 | switch(child->get_type()) 41 | { 42 | case NodeType::Function: compile_function((NodeFunction*)child); break; 43 | } 44 | } 45 | } 46 | 47 | void Code::compile_program(NodeProgram &node) 48 | { 49 | node.pre_process(); 50 | for (int i = 0; i < node.get_child_size(); i++) 51 | { 52 | NodeModule *mod = (NodeModule*)node[i]; 53 | compile_module(mod); 54 | } 55 | } 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /compiler/src/CodeGen/TinyVM/TinyVMClass.cpp: -------------------------------------------------------------------------------- 1 | #include "CodeGen/TinyVMCode.hpp" 2 | using namespace TinyScript::TinyVM; 3 | 4 | void Code::compile_class(NodeClass *node) 5 | { 6 | for (int i = 0; i < node->get_child_size(); i++) 7 | { 8 | Node *child = (*node)[i]; 9 | switch(child->get_type()) 10 | { 11 | case NodeType::Function: compile_function((NodeFunction*)child); break; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /compiler/src/CodeGen/TinyVM/TinyVMCode.cpp: -------------------------------------------------------------------------------- 1 | #include "CodeGen/TinyVMCode.hpp" 2 | #include "memory.h" 3 | #include "flags.h" 4 | #include 5 | using namespace TinyScript::TinyVM; 6 | 7 | vector Code::link() 8 | { 9 | vector code_out = code; 10 | for (auto label : labels) 11 | { 12 | auto label_info = label.second; 13 | vector addrs = std::get<0>(label_info); 14 | int location = std::get<1>(label_info); 15 | 16 | if (location == -1) 17 | { 18 | Logger::link_error("Label '" + label.first + 19 | "' was referenced but never assigned"); 20 | } 21 | 22 | for (int addr : addrs) 23 | memcpy(&code_out[addr], &location, sizeof(int)); 24 | } 25 | 26 | vector header; 27 | header.push_back(externals.size()); 28 | for (Symbol symb : externals) 29 | { 30 | string name = Symbol::printout(symb); 31 | int id = symb.location; 32 | 33 | int start = header.size(); 34 | header.resize(start + 4 + 1 + name.length()); 35 | memcpy(&header[start], &id, sizeof(int)); 36 | memcpy(&header[start + 5], name.c_str(), name.length()); 37 | header[start + 4] = name.length(); 38 | } 39 | 40 | vector out; 41 | out.insert(out.end(), header.begin(), header.end()); 42 | out.insert(out.end(), code_out.begin(), code_out.end()); 43 | 44 | return out; 45 | } 46 | 47 | void Code::write_byte(char b) 48 | { 49 | code.push_back(b); 50 | } 51 | 52 | void Code::write_int(int i) 53 | { 54 | for (int j = 0; j < sizeof(int); j++) 55 | code.push_back((i >> j*8) & 0xFF); 56 | } 57 | 58 | void Code::write_float(float f) 59 | { 60 | int i = *(int*)&f; 61 | write_int(i); 62 | } 63 | 64 | void Code::write_string(string str) 65 | { 66 | write_byte(str.length() + 1); 67 | for (char c : str) 68 | write_byte(c); 69 | write_byte('\0'); 70 | } 71 | 72 | void Code::write_label(string label) 73 | { 74 | #if DEBUG_LINK 75 | printf("Call %s\n", label.c_str()); 76 | #endif 77 | 78 | if (labels.find(label) == labels.end()) 79 | labels[label] = std::make_tuple(vector(), -1); 80 | std::get<0>(labels[label]).push_back(code.size()); 81 | write_int(0); 82 | } 83 | 84 | void Code::assign_label(string label) 85 | { 86 | #if DEBUG_LINK 87 | printf("Assign %s\n", label.c_str()); 88 | #endif 89 | 90 | int location = code.size(); 91 | if (labels.find(label) == labels.end()) 92 | labels[label] = std::make_tuple(vector(), location); 93 | else 94 | { 95 | if (std::get<1>(labels[label]) != -1) 96 | Logger::link_error("The symbol '" + label + "' has already been defined"); 97 | std::get<1>(labels[label]) = location; 98 | } 99 | } 100 | 101 | int Code::find_funcion(string name) 102 | { 103 | if (labels.find(name) == labels.end()) 104 | { 105 | Logger::link_error("Could not find function '" + name + "'"); 106 | return -1; 107 | } 108 | 109 | return std::get<1>(labels[name]); 110 | } 111 | 112 | string Code::gen_label() 113 | { 114 | int id; 115 | do 116 | { 117 | id = random(); 118 | } while(std::find( 119 | used_labels.begin(), used_labels.end(), id) 120 | != used_labels.end()); 121 | 122 | used_labels.push_back(id); 123 | return "$(" + std::to_string(id) + ")"; 124 | } 125 | -------------------------------------------------------------------------------- /compiler/src/CodeGen/TinyVM/TinyVMExpression.cpp: -------------------------------------------------------------------------------- 1 | #include "CodeGen/TinyVMCode.hpp" 2 | extern "C" 3 | { 4 | #include "bytecode.h" 5 | } 6 | using namespace TinyScript::TinyVM; 7 | using namespace TinyScript; 8 | 9 | static char find_op_code(string name) 10 | { 11 | int i; 12 | for (i = 0; i < BC_SIZE; i++) 13 | if (string(bytecode_names[i]) == name) 14 | return i; 15 | return -1; 16 | } 17 | 18 | static string str_upper(string str) 19 | { 20 | string out; 21 | for (char c : str) 22 | out += toupper(c); 23 | return out; 24 | } 25 | 26 | void Code::compile_operation(Token op, DataType ltype, DataType rtype) 27 | { 28 | // Find the bytecode name of the operation 29 | string op_name; 30 | switch(op.type) 31 | { 32 | case TokenType::Add: op_name = "ADD"; break; 33 | case TokenType::Subtract: op_name = "SUB"; break; 34 | case TokenType::Multiply: op_name = "MUL"; break; 35 | case TokenType::Divide: op_name = "DIV"; break; 36 | case TokenType::MoreThan: op_name = "MORE_THAN"; break; 37 | case TokenType::LessThan: op_name = "LESS_THAN"; break; 38 | case TokenType::MoreThanEquals: op_name = "MORE_THAN_EQUALS"; break; 39 | case TokenType::LessThanEquals: op_name = "MORE_THAN_EQUALS"; break; 40 | case TokenType::Equals: op_name = "EQUALS"; break; 41 | } 42 | 43 | // Create the bytecode name string for the types and find the code 44 | string code_name = "BC_" + op_name + "_" + str_upper(ltype.construct->name) + 45 | "_" + str_upper(rtype.construct->name); 46 | char bytecode = find_op_code(code_name); 47 | if (bytecode == -1) 48 | { 49 | // If that operation has not been implemented 50 | Logger::error(op.debug_info, "Cannot do operation " + 51 | ltype.construct->name + 52 | " " + op.data + " " + 53 | rtype.construct->name); 54 | return; 55 | } 56 | 57 | // Write this final operation to the code 58 | write_byte(bytecode); 59 | } 60 | 61 | void Code::compile_call(const Symbol &symb, ExpDataNode *node) 62 | { 63 | // Allocate the return space 64 | int return_size = DataType::find_size(symb.type); 65 | if (return_size > 0) 66 | { 67 | write_byte(BC_ALLOC); 68 | write_byte(return_size); 69 | } 70 | 71 | // Push the arguments onto the stack 72 | for (int i = node->args.size() - 1; i >= 0; i--) 73 | compile_rvalue(node->args[i]); 74 | 75 | // Call the function depending on type 76 | if (symb.flags & SYMBOL_EXTERNAL) 77 | { 78 | // Call external symbol 79 | write_byte(BC_CALL_EXTERNAL); 80 | write_int(symb.location); 81 | } 82 | else 83 | { 84 | // Call local tinyscript function 85 | write_byte(BC_CALL); 86 | write_label(Symbol::printout(symb)); 87 | } 88 | } 89 | 90 | void Code::compile_rname(ExpDataNode *node) 91 | { 92 | Token name = node->token; 93 | Symbol symb = node->symb; 94 | 95 | // Compile function call 96 | 97 | if (node->flags & NODE_CALL) 98 | { 99 | compile_call(symb, node); 100 | return; 101 | } 102 | 103 | // Push local to the stack 104 | if (symb.flags & SYMBOL_LOCAL) 105 | { 106 | write_byte(BC_LOAD_LOCAL_X); 107 | write_int(DataType::find_size(symb.type)); 108 | write_byte(symb.location); 109 | } 110 | } 111 | 112 | void Code::compile_ref(ExpDataNode *node) 113 | { 114 | Symbol lvalue = find_lvalue_location(node->left); 115 | write_byte(BC_LOCAL_REF); 116 | write_byte(lvalue.location); 117 | } 118 | 119 | void Code::compile_copy(ExpDataNode *node) 120 | { 121 | compile_rvalue(node->left); 122 | write_byte(BC_COPY); 123 | write_byte(DataType::find_size(node->type)); 124 | } 125 | 126 | void Code::compile_array(ExpDataNode *node) 127 | { 128 | // Push all items to the stack 129 | for (ExpDataNode *item : node->args) 130 | compile_rvalue(item); 131 | } 132 | 133 | void Code::compile_cast(ExpDataNode *node) 134 | { 135 | compile_rvalue(node->left); 136 | DataType ltype = node->left->type; 137 | DataType rtype = node->right->type; 138 | 139 | Logger::log(node->token.debug_info, "Cast from " 140 | + DataType::printout(ltype) 141 | + " to " + DataType::printout(rtype)); 142 | 143 | // If converting from a ref, don't do anything 144 | if (!(ltype.flags & DATATYPE_REF)) 145 | { 146 | string code_name = "BC_CAST_" + str_upper(ltype.construct->name) + 147 | "_" + str_upper(rtype.construct->name); 148 | write_byte(find_op_code(code_name)); 149 | } 150 | } 151 | 152 | void Code::compile_typesize(ExpDataNode *node) 153 | { 154 | DataType type = node->left->type; 155 | int size = DataType::find_size(type); 156 | 157 | write_byte(BC_PUSH_4); 158 | write_int(size); 159 | } 160 | 161 | void Code::compile_typename(ExpDataNode *node) 162 | { 163 | DataType type = node->left->type; 164 | string name = DataType::printout(type); 165 | 166 | write_byte(BC_PUSH_X); 167 | write_string(name); 168 | } 169 | 170 | void Code::compile_arraysize(ExpDataNode *node) 171 | { 172 | DataType type = node->left->type; 173 | int size = type.array_size; 174 | 175 | write_byte(BC_PUSH_4); 176 | write_int(size); 177 | } 178 | 179 | void Code::compile_rterm(ExpDataNode *node) 180 | { 181 | Token value = node->token; 182 | const char *str = value.data.c_str(); 183 | 184 | // Compile term based on type 185 | switch(value.type) 186 | { 187 | case TokenType::Int: write_byte(BC_PUSH_4); write_int(atoi(str)); break; 188 | case TokenType::Float: write_byte(BC_PUSH_4); write_float(atof(str)); break; 189 | case TokenType::Bool: write_byte(BC_PUSH_1); write_byte(value.data == "true" ? 1 : 0); break; 190 | case TokenType::Char: write_byte(BC_PUSH_1); write_byte(value.data[0]); break; 191 | case TokenType::String: write_byte(BC_PUSH_X); write_string(value.data); break; 192 | case TokenType::Name: compile_rname(node); break; 193 | case TokenType::Ref: compile_ref(node); break; 194 | case TokenType::As: compile_cast(node); break; 195 | case TokenType::Copy: compile_copy(node); break; 196 | case TokenType::OpenIndex: compile_array(node); break; 197 | case TokenType::TypeSize: compile_typesize(node); break; 198 | case TokenType::TypeName: compile_typename(node); break; 199 | case TokenType::ArraySize: compile_arraysize(node); break; 200 | } 201 | } 202 | 203 | void Code::compile_index(ExpDataNode *node) 204 | { 205 | compile_rvalue(node->left); 206 | compile_rvalue(node->right); 207 | 208 | int element_size = DataType::find_size(*node->left->type.sub_type); 209 | if (node->left->type.flags & DATATYPE_REF) 210 | { 211 | write_byte(BC_PUSH_4); 212 | write_int(element_size); 213 | write_byte(BC_MUL_INT_INT); 214 | write_byte(BC_ADD_INT_INT); 215 | write_byte(BC_COPY); 216 | write_byte(DataType::find_size(node->type)); 217 | } 218 | else 219 | { 220 | write_byte(BC_GET_ARRAY_INDEX); 221 | write_int(node->left->type.array_size * element_size); 222 | write_int(element_size); 223 | } 224 | } 225 | 226 | void Code::compile_rin(ExpDataNode *node) 227 | { 228 | Symbol symb = node->right->symb; 229 | if (symb.flags & SYMBOL_FUNCTION) 230 | { 231 | compile_call(symb, node->right); 232 | return; 233 | } 234 | compile_rvalue(node->left); 235 | 236 | int offset = symb.location; 237 | int size = DataType::find_size(node->right->type); 238 | int type_size = DataType::find_size(node->left->type); 239 | if (node->left->type.flags & DATATYPE_REF) 240 | { 241 | write_byte(BC_PUSH_4); 242 | write_int(offset); 243 | write_byte(BC_ADD_INT_INT); 244 | write_byte(BC_COPY); 245 | write_byte(size); 246 | } 247 | else 248 | { 249 | write_byte(BC_GET_ATTR); 250 | write_int(offset); 251 | write_int(size); 252 | write_int(type_size); 253 | } 254 | } 255 | 256 | void Code::compile_rvalue(ExpDataNode *node) 257 | { 258 | // Compile the code for pushing the value on to the stack 259 | if (node->flags & NODE_OPERATION) 260 | { 261 | compile_rvalue(node->left); 262 | compile_rvalue(node->right); 263 | compile_operation(node->token, node->left->type, 264 | node->right->type); 265 | } 266 | else if (node->flags & NODE_INDEX) 267 | { 268 | compile_index(node); 269 | } 270 | else if (node->flags & NODE_IN) 271 | { 272 | compile_rin(node); 273 | } 274 | else 275 | { 276 | compile_rterm(node); 277 | } 278 | } 279 | 280 | bool Code::is_static_lvalue(ExpDataNode *node) 281 | { 282 | if (node->token.type == TokenType::Name) 283 | { 284 | Symbol symb = node->symb; 285 | if (!(symb.flags & SYMBOL_FUNCTION)) 286 | return true; 287 | } 288 | 289 | return false; 290 | } 291 | 292 | Symbol Code::find_lvalue_location(ExpDataNode *node) 293 | { 294 | if (node->token.type != TokenType::Name) 295 | Logger::error(node->token.debug_info, "Not a valid lvalue"); 296 | return node->symb; 297 | } 298 | 299 | void Code::compile_lname(ExpDataNode *node) 300 | { 301 | Token name = node->token; 302 | Symbol symb = node->symb; 303 | 304 | if (symb.type.flags & DATATYPE_REF) 305 | { 306 | write_byte(BC_LOAD_LOCAL_4); 307 | write_byte(symb.location); 308 | return; 309 | } 310 | 311 | if (symb.flags & SYMBOL_LOCAL) 312 | { 313 | write_byte(BC_LOCAL_REF); 314 | write_byte(symb.location); 315 | } 316 | } 317 | 318 | void Code::compile_lterm(ExpDataNode *node) 319 | { 320 | if (node->token.type == TokenType::Name) 321 | compile_lname(node); 322 | else 323 | compile_rterm(node); 324 | } 325 | 326 | void Code::compile_lin(ExpDataNode *node) 327 | { 328 | Symbol attr = node->right->symb; 329 | 330 | compile_lvalue(node->left); 331 | write_byte(BC_PUSH_4); 332 | write_int(attr.location); 333 | write_byte(BC_ADD_INT_INT); 334 | } 335 | 336 | void Code::compile_lvalue(ExpDataNode *node) 337 | { 338 | if (node->flags & NODE_OPERATION) 339 | { 340 | compile_lvalue(node->left); 341 | compile_lvalue(node->right); 342 | compile_operation(node->token, node->left->type, 343 | node->right->type); 344 | return; 345 | } 346 | 347 | if (node->flags & NODE_INDEX) 348 | { 349 | compile_lvalue(node->left); 350 | compile_rvalue(node->right); 351 | 352 | int element_size = DataType::find_size(*node->left->type.sub_type); 353 | write_byte(BC_PUSH_4); 354 | write_int(element_size); 355 | write_byte(BC_MUL_INT_INT); 356 | write_byte(BC_ADD_INT_INT); 357 | return; 358 | } 359 | 360 | if (node->flags & NODE_IN) 361 | { 362 | compile_lin(node); 363 | return; 364 | } 365 | 366 | compile_lterm(node); 367 | } 368 | 369 | void Code::compile_rexpression(NodeExpression *node) 370 | { 371 | node->symbolize(); 372 | compile_rvalue(node->get_data()); 373 | } 374 | 375 | void Code::compile_lexpression(NodeExpression *node) 376 | { 377 | node->symbolize(); 378 | compile_lvalue(node->get_data()); 379 | } 380 | -------------------------------------------------------------------------------- /compiler/src/CodeGen/TinyVM/TinyVMFunction.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "CodeGen/TinyVMCode.hpp" 3 | extern "C" 4 | { 5 | #include "bytecode.h" 6 | } 7 | using namespace TinyScript::TinyVM; 8 | 9 | void Code::compile_function(NodeFunction *node) 10 | { 11 | // Don't compile tempate functions 12 | if (node->is_template() || node->is_compiled()) 13 | return; 14 | 15 | Token name = node->get_name(); 16 | Logger::log(name.debug_info, "Compiling function '" + 17 | Symbol::printout(node->get_symb()) + "'"); 18 | 19 | // Create stack frame 20 | assign_label(Symbol::printout(node->get_symb())); 21 | write_byte(BC_CREATE_FRAME); 22 | int scope_size_loc = code.size(); 23 | write_int(0); 24 | 25 | // Compile body 26 | compile_block((NodeBlock*)node); 27 | 28 | // Write default return statement 29 | write_byte(BC_RETURN); 30 | write_byte(0); 31 | write_byte(node->get_arg_size()); 32 | 33 | int scope_size = node->get_scope_size(); 34 | memcpy(&code[scope_size_loc], &scope_size, sizeof(int)); 35 | node->set_compiled(); 36 | } 37 | 38 | void Code::compile_return(NodeReturn *node) 39 | { 40 | Logger::log(node->get_value()->get_data()->token.debug_info, "Compile Return"); 41 | 42 | NodeExpression *value = node->get_value(); 43 | NodeFunction *func = (NodeFunction*)node->get_parent(NodeType::Function); 44 | int size; 45 | 46 | compile_rexpression(value); 47 | size = DataType::find_size(value->get_data_type()); 48 | write_byte(BC_RETURN); 49 | write_byte(size); 50 | write_byte(func->get_arg_size()); 51 | } 52 | 53 | void Code::compile_block(NodeBlock *node) 54 | { 55 | Logger::start_scope(); 56 | for (int i = 0; i < node->get_child_size(); i++) 57 | { 58 | Node *child = (*node)[i]; 59 | switch (child->get_type()) 60 | { 61 | case NodeType::Let: compile_let((NodeLet*)child); break; 62 | case NodeType::Assign: compile_assign((NodeAssign*)child); break; 63 | case NodeType::Return: compile_return((NodeReturn*)child); break; 64 | case NodeType::If: compile_if((NodeIf*)child); break; 65 | case NodeType::For: compile_for((NodeFor*)child); break; 66 | case NodeType::While: compile_while((NodeWhile*)child); break; 67 | } 68 | } 69 | Logger::end_scope(); 70 | } 71 | 72 | void Code::compile_let(NodeLet *node) 73 | { 74 | Logger::log(node->get_name().debug_info, "Compile let"); 75 | 76 | NodeExpression *value = node->get_value(); 77 | Symbol symb; 78 | int size; 79 | 80 | if (value != nullptr) 81 | { 82 | compile_rexpression(value); 83 | node->symbolize(); 84 | symb = node->get_symb(); 85 | 86 | size = DataType::find_size(symb.type); 87 | write_byte(BC_STORE_LOCAL_X); 88 | write_int(size); 89 | write_byte(symb.location); 90 | } 91 | else 92 | node->symbolize(); 93 | } 94 | 95 | void Code::compile_assign(NodeAssign *node) 96 | { 97 | Logger::log(node->get_left()->get_data()->token.debug_info, "Compile assign"); 98 | 99 | NodeExpression *left = node->get_left(); 100 | NodeExpression *right = node->get_right(); 101 | 102 | if (right != nullptr) 103 | { 104 | compile_rexpression(right); 105 | compile_lexpression(left); 106 | 107 | int size = DataType::find_size(right->get_data_type()); 108 | write_byte(BC_ASSIGN_REF_X); 109 | write_int(size); 110 | } 111 | else 112 | { 113 | compile_rexpression(left); 114 | 115 | int size = DataType::find_size(left->get_data_type()); 116 | if (size > 0) 117 | { 118 | write_byte(BC_POP); 119 | write_byte(size); 120 | } 121 | } 122 | } 123 | 124 | void Code::compile_if(NodeIf *node) 125 | { 126 | string end = gen_label(); 127 | 128 | compile_rexpression(node->get_condition()); 129 | write_byte(BC_JUMP_IF_NOT); 130 | write_label(end); 131 | compile_block((NodeBlock*)node); 132 | assign_label(end); 133 | } 134 | 135 | void Code::compile_for(NodeFor *node) 136 | { 137 | string start = gen_label(); 138 | string end = gen_label(); 139 | 140 | // Assign initial value 141 | NodeExpression *from = node->get_from(); 142 | compile_rexpression(from); 143 | compile_lexpression(node->get_left()); 144 | 145 | int size = DataType::find_size(from->get_data_type()); 146 | write_byte(BC_ASSIGN_REF_X); 147 | write_int(size); 148 | 149 | // Check to see if within loop 150 | assign_label(start); 151 | compile_rexpression(node->get_to()); 152 | compile_rexpression(node->get_left()); 153 | write_byte(BC_MORE_THAN_INT_INT); 154 | write_byte(BC_JUMP_IF_NOT); 155 | write_label(end); 156 | 157 | compile_block((NodeCodeBlock*)node); 158 | 159 | // Increment by 1 160 | compile_rexpression(node->get_left()); 161 | write_byte(BC_PUSH_4); 162 | write_int(1); 163 | write_byte(BC_ADD_INT_INT); 164 | compile_lexpression(node->get_left()); 165 | write_byte(BC_ASSIGN_REF_X); 166 | write_int(size); 167 | 168 | // Jump to the start 169 | write_byte(BC_JUMP); 170 | write_label(start); 171 | assign_label(end); 172 | } 173 | 174 | void Code::compile_while(NodeWhile *node) 175 | { 176 | string start = gen_label(); 177 | string end = gen_label(); 178 | 179 | // Exit loop if condition is not met 180 | assign_label(start); 181 | compile_rexpression(node->get_condition()); 182 | write_byte(BC_JUMP_IF_NOT); 183 | write_label(end); 184 | 185 | // Compile main code 186 | compile_block((NodeCodeBlock*)node); 187 | 188 | // Jump to the start of the loop 189 | write_byte(BC_JUMP); 190 | write_label(start); 191 | assign_label(end); 192 | } 193 | -------------------------------------------------------------------------------- /compiler/src/CodeGen/TinyVM/TinyVMModule.cpp: -------------------------------------------------------------------------------- 1 | #include "CodeGen/TinyVMCode.hpp" 2 | using namespace TinyScript::TinyVM; 3 | 4 | void Code::compile_import(NodeImport *node) 5 | { 6 | node->symbolize(); 7 | } 8 | 9 | void Code::compile_import_from(NodeImportFrom *node) 10 | { 11 | node->symbolize(); 12 | } 13 | 14 | void Code::compile_external(NodeExtern *node) 15 | { 16 | Symbol symb = node->get_symb(); 17 | 18 | // If the external has already been added, don't add it 19 | for (Symbol external : externals) 20 | if (external.location == symb.location) 21 | return; 22 | 23 | externals.push_back(symb); 24 | } 25 | 26 | void Code::compile_module(NodeModule *node) 27 | { 28 | Token name = node->get_name(); 29 | Logger::log(name.debug_info, "Compiling module '" + name.data + "'"); 30 | Logger::start_scope(); 31 | 32 | for (int i = 0; i < node->get_child_size(); i++) 33 | { 34 | Node *child = (*node)[i]; 35 | switch (child->get_type()) 36 | { 37 | case NodeType::Function: compile_function((NodeFunction*)child); break; 38 | case NodeType::ImportFrom: compile_import_from((NodeImportFrom*)child); break; 39 | case NodeType::Import: compile_import((NodeImport*)child); break; 40 | case NodeType::Extern: compile_external((NodeExtern*)child); break; 41 | case NodeType::Class: compile_class((NodeClass*)child); break; 42 | } 43 | } 44 | 45 | Logger::end_scope(); 46 | } 47 | 48 | void Code::compile_program(NodeProgram &node) 49 | { 50 | Logger::log({}, "Compiling program"); 51 | Logger::start_scope(); 52 | node.pre_process(); 53 | 54 | bool is_compiled = false; 55 | while (is_compiled == false) 56 | { 57 | for (int i = 0; i < node.get_child_size(); i++) 58 | { 59 | NodeModule *mod = (NodeModule*)node[i]; 60 | if (!mod->is_compiled()) 61 | compile_module(mod); 62 | mod->flag_compiled(); 63 | } 64 | 65 | is_compiled = true; 66 | for (int i = 0; i < node.get_child_size(); i++) 67 | { 68 | NodeModule *mod = (NodeModule*)node[i]; 69 | if (!mod->is_compiled()) 70 | is_compiled = false; 71 | } 72 | } 73 | Logger::end_scope(); 74 | } 75 | -------------------------------------------------------------------------------- /compiler/src/Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "Logger.hpp" 2 | #include "flags.h" 3 | #include 4 | #include 5 | #include 6 | using namespace TinyScript; 7 | 8 | static bool has_error_flag = false; 9 | static int scope = 0; 10 | 11 | #define RED "\033[1;31m" 12 | #define ORANGE "\033[01;33m" 13 | #define RESET "\033[0m" 14 | 15 | void Logger::reset() 16 | { 17 | has_error_flag = false; 18 | scope = 0; 19 | } 20 | 21 | static void log_info(const DebugInfo &debug_info) 22 | { 23 | std::stringstream stream; 24 | stream << "(" << debug_info.file_name << ") " << 25 | "[" << debug_info.line_no << ":" << 26 | debug_info.char_no << "]"; 27 | 28 | string info = stream.str(); 29 | std::cout << info; 30 | 31 | int space_len = debug_info.file_name.length() + 15 - info.length(); 32 | for (int i = 0; i < space_len; i++) 33 | std::cout << " "; 34 | } 35 | 36 | static void log_location(const DebugInfo &debug_info) 37 | { 38 | string line; 39 | std::ifstream file(debug_info.file_name); 40 | for(int i = 0; i < debug_info.line_no; ++i) 41 | std::getline(file, line); 42 | file.close(); 43 | 44 | std::cout << line << std::endl; 45 | for (int i = 0; i < debug_info.char_no - 1; i++) 46 | std::cout << " "; 47 | std::cout << "^^^^\n" << std::endl; 48 | } 49 | 50 | void Logger::warning(const DebugInfo &debug_info, string msg) 51 | { 52 | std::cout << ORANGE << "Warning: "; 53 | log_info(debug_info); 54 | std::cout << msg << std::endl; 55 | log_location(debug_info); 56 | std::cout << RESET; 57 | } 58 | 59 | void Logger::error(const DebugInfo &debug_info, string msg) 60 | { 61 | std::cout << RED << "Error: "; 62 | log_info(debug_info); 63 | std::cout << msg << std::endl; 64 | log_location(debug_info); 65 | std::cout << RESET; 66 | 67 | has_error_flag = true; 68 | } 69 | 70 | void Logger::link_error(string msg) 71 | { 72 | std::cout << RED << "Link Error: " << 73 | msg << std::endl << RESET; 74 | has_error_flag = true; 75 | } 76 | 77 | bool Logger::has_error() 78 | { 79 | return has_error_flag; 80 | } 81 | 82 | #if DEBUG_COMPILER 83 | 84 | 85 | void Logger::start_scope() 86 | { 87 | scope++; 88 | } 89 | 90 | void Logger::end_scope() 91 | { 92 | scope--; 93 | } 94 | 95 | void Logger::log(const DebugInfo &debug_info, string msg) 96 | { 97 | log_info(debug_info); 98 | for (int i = 0; i < scope; i++) 99 | std::cout << " "; 100 | 101 | if (scope > 0) 102 | std::cout << "=> "; 103 | std::cout << msg << std::endl; 104 | } 105 | 106 | #else 107 | 108 | void Logger::start_scope() {} 109 | void Logger::end_scope() {} 110 | void Logger::log(const DebugInfo &debug_info, string msg) {} 111 | 112 | #endif 113 | -------------------------------------------------------------------------------- /compiler/src/Parser/Class.cpp: -------------------------------------------------------------------------------- 1 | #include "Parser/Class.hpp" 2 | #include "Parser/Function.hpp" 3 | using namespace TinyScript; 4 | 5 | void NodeClass::parse_attr(Tokenizer &tk) 6 | { 7 | NodeDataType *type = parse_node(tk); 8 | Token name = tk.match(TokenType::Name, "Attr name"); 9 | attrs.push_back(std::make_pair(type, name)); 10 | 11 | Logger::log(name.debug_info, "Parsing attr '" + name.data + "'"); 12 | } 13 | 14 | void NodeClass::parse_method(Tokenizer &tk) 15 | { 16 | NodeFunction *func = new NodeFunction(get_parent()); 17 | func->parse(tk); 18 | func->make_method(construct); 19 | 20 | add_child(func); 21 | } 22 | 23 | void NodeClass::parse(Tokenizer &tk) 24 | { 25 | tk.match(TokenType::Class, "class"); 26 | name = tk.match(TokenType::Name, "Class Name"); 27 | construct = get_parent(NodeType::Module)->create_construct(name.data); 28 | construct->parent = this; 29 | prefix = name.data + "."; 30 | 31 | Logger::log(name.debug_info, "Parsing class '" + name.data + "'"); 32 | Logger::start_scope(); 33 | tk.match(TokenType::OpenBlock, "{"); 34 | while (tk.get_look().type != TokenType::CloseBlock) 35 | { 36 | switch (tk.get_look().type) 37 | { 38 | case TokenType::Func: parse_method(tk); break; 39 | default: parse_attr(tk); break; 40 | } 41 | } 42 | tk.match(TokenType::CloseBlock, "}"); 43 | Logger::end_scope(); 44 | } 45 | 46 | Node *NodeClass::copy(Node *parent) 47 | { 48 | NodeClass *other = new NodeClass(parent); 49 | copy_block(other); 50 | other->name = name; 51 | other->construct = construct; 52 | return other; 53 | } 54 | 55 | void NodeClass::register_class() 56 | { 57 | if (is_complete) 58 | return; 59 | 60 | if (being_proccessed) 61 | { 62 | Logger::error(name.debug_info, "Type loop detected"); 63 | return; 64 | } 65 | 66 | Logger::log({}, "Registering type '" + name.data + "'"); 67 | Logger::start_scope(); 68 | 69 | being_proccessed = true; 70 | for (auto attr : attrs) 71 | { 72 | NodeDataType *type_node = attr.first; 73 | Token name = attr.second; 74 | 75 | // Find type and register it if it's not complete 76 | DataType type = type_node->compile(); 77 | Node *type_parent = type.construct->parent; 78 | if (type_parent != nullptr) 79 | { 80 | NodeClass *type_class = (NodeClass*)type_parent; 81 | type_class->register_class(); 82 | } 83 | 84 | // Create symbol for each attribute 85 | int size = DataType::find_size(type); 86 | int location = allocate(size); 87 | push_symbol(Symbol(name.data, type, SYMBOL_LOCAL, location, this)); 88 | construct->size += size; 89 | 90 | Logger::log({}, "Registered attr '" + name.data + "' of type '" + 91 | DataType::printout(type) + "' size " + std::to_string(size)); 92 | } 93 | is_complete = true; 94 | being_proccessed = false; 95 | 96 | Logger::end_scope(); 97 | } 98 | 99 | void NodeClass::register_methods() 100 | { 101 | Logger::log({}, "Registering methods in class '" + name.data + "'"); 102 | Logger::start_scope(); 103 | 104 | for (Node *child : children) 105 | { 106 | switch(child->get_type()) 107 | { 108 | case NodeType::Function: ((NodeFunction*)child)->register_func(); break; 109 | default: break; 110 | } 111 | } 112 | 113 | Logger::end_scope(); 114 | } 115 | 116 | NodeClass::~NodeClass() 117 | { 118 | // Clean up 119 | for (auto attr : attrs) 120 | delete attr.first; 121 | } 122 | -------------------------------------------------------------------------------- /compiler/src/Parser/DataType.cpp: -------------------------------------------------------------------------------- 1 | #include "Parser/DataType.hpp" 2 | #include "Parser/Expression.hpp" 3 | using namespace TinyScript; 4 | 5 | void NodeDataType::parse(Tokenizer &tk) 6 | { 7 | // Parse auto type 8 | if (tk.get_look().type == TokenType::Auto) 9 | { 10 | type_name = tk.match(TokenType::Auto, "auto"); 11 | is_auto_flag = true; 12 | return; 13 | } 14 | 15 | // Parse base type name 16 | type_name = tk.match(TokenType::Name, "Type name"); 17 | 18 | // Parse all modifiers 19 | bool has_modifier = true; 20 | while (has_modifier) 21 | { 22 | switch (tk.get_look().type) 23 | { 24 | case TokenType::Ref: modifiers.push_back({ tk.skip_token() }); break; 25 | case TokenType::Array: parse_array(tk); break; 26 | default: has_modifier = false; break; 27 | } 28 | } 29 | } 30 | 31 | void NodeDataType::parse_array(Tokenizer &tk) 32 | { 33 | DataTypeModifier mod; 34 | mod.name = tk.match(TokenType::Array, "array"); 35 | 36 | // Parse array size 37 | tk.match(TokenType::OpenIndex, "["); 38 | NodeExpression *size_exp = parse_node(tk); 39 | tk.match(TokenType::CloseIndex, "]"); 40 | 41 | // Must be a static int value so it's known at compile time 42 | if (!size_exp->is_static_value() || 43 | size_exp->get_data_type().construct != PrimTypes::type_int()) 44 | { 45 | Logger::error(mod.name.debug_info, 46 | "Array size must be a static int value"); 47 | } 48 | 49 | mod.size = size_exp->get_int_value(); 50 | modifiers.push_back(mod); 51 | } 52 | 53 | Node *NodeDataType::copy(Node *parent) 54 | { 55 | NodeDataType *other = new NodeDataType(parent); 56 | other->type_name = type_name; 57 | other->modifiers = modifiers; 58 | return other; 59 | } 60 | 61 | DataType NodeDataType::compile() 62 | { 63 | if (is_auto_flag) 64 | return { PrimTypes::type_null(), DATATYPE_AUTO }; 65 | 66 | // Find base construct 67 | DataConstruct *construct = find_construct(type_name.data); 68 | if (construct == PrimTypes::type_null()) 69 | { 70 | Logger::error(type_name.debug_info, 71 | "Could not find type '" + type_name.data + "'"); 72 | } 73 | 74 | // Apply modifiers 75 | DataType type = { construct, 0 }; 76 | for (DataTypeModifier mod : modifiers) 77 | { 78 | switch (mod.name.type) 79 | { 80 | case TokenType::Ref: type = compile_ref(type); break; 81 | case TokenType::Array: type = compile_array(type, mod); break; 82 | } 83 | } 84 | return type; 85 | } 86 | 87 | DataType NodeDataType::compile_ref(DataType base) 88 | { 89 | DataType type; 90 | type.construct = PrimTypes::type_null(); 91 | type.flags = DATATYPE_REF; 92 | type.sub_type = std::make_shared(base); 93 | return type; 94 | } 95 | 96 | DataType NodeDataType::compile_array(DataType base, DataTypeModifier mod) 97 | { 98 | DataType type; 99 | type.construct = base.construct; 100 | type.flags = DATATYPE_ARRAY; 101 | type.sub_type = std::make_shared(base); 102 | type.array_size = mod.size; 103 | return type; 104 | } 105 | -------------------------------------------------------------------------------- /compiler/src/Parser/Expression.cpp: -------------------------------------------------------------------------------- 1 | #include "Parser/Expression.hpp" 2 | #include "Parser/Function.hpp" 3 | #include "Parser/Class.hpp" 4 | #include 5 | #include 6 | #include 7 | using namespace TinyScript; 8 | 9 | void NodeExpression::parse_args(Tokenizer &tk, ExpDataNode *node) 10 | { 11 | // Only parse args is there is any provided 12 | if (tk.get_look().type == TokenType::OpenArg) 13 | { 14 | // Loop through all the arguments in a function call 15 | tk.match(TokenType::OpenArg, "("); 16 | while (tk.get_look().type != TokenType::CloseArg) 17 | { 18 | // Parse the NodeExpression and add it to the nodes list of args 19 | ExpDataNode *arg = parse_expression(tk); 20 | node->args.push_back(arg); 21 | 22 | if (tk.get_look().type != TokenType::CloseArg) 23 | tk.match(TokenType::Next, ","); 24 | } 25 | tk.match(TokenType::CloseArg, ")"); 26 | node->flags |= NODE_ARGS_LIST; 27 | } 28 | } 29 | 30 | ExpDataNode *NodeExpression::parse_indies(Tokenizer &tk, ExpDataNode *node) 31 | { 32 | // Create a new index operation 33 | // left - array 34 | // right - index 35 | 36 | Token open = tk.match(TokenType::OpenIndex, "["); 37 | ExpDataNode *index = parse_expression(tk); 38 | ExpDataNode *operation = new ExpDataNode; 39 | operation->left = node; 40 | operation->right = index; 41 | operation->flags = NODE_INDEX; 42 | operation->token = open; 43 | tk.match(TokenType::CloseIndex, "]"); 44 | 45 | return operation; 46 | } 47 | 48 | static ExpDataNode *cast(ExpDataNode *node, DataType to) 49 | { 50 | // If an array cast 51 | if (node->type.flags & DATATYPE_ARRAY) 52 | { 53 | DataType original = node->type; 54 | node->type = to; 55 | node->type.sub_type = std::make_shared(original); 56 | node->flags |= NODE_TEMP_REF; 57 | return node; 58 | } 59 | 60 | ExpDataNode *cast = new ExpDataNode; 61 | cast->left = node; 62 | cast->right = new ExpDataNode { nullptr, nullptr, to }; 63 | cast->type = to; 64 | cast->flags = NODE_CAST; 65 | cast->token = { "as", TokenType::As, cast->left->token.debug_info }; 66 | return cast; 67 | } 68 | 69 | ExpDataNode *NodeExpression::parse_cast(Tokenizer &tk, ExpDataNode *node) 70 | { 71 | // Create a new cast operation 72 | // left - original 73 | // right - new type 74 | 75 | Token as = tk.match(TokenType::As, "as"); 76 | NodeDataType *type_node = parse_node(tk); 77 | DataType type = type_node->compile(); 78 | bool warning = false; 79 | if (!DataType::can_cast_to(node->type, type, warning)) 80 | { 81 | Logger::error(as.debug_info, "Cannot cast from '" + 82 | DataType::printout(node->type) + "' to '" + 83 | DataType::printout(type) + "'"); 84 | } 85 | 86 | if (warning) 87 | { 88 | Logger::warning(as.debug_info, "Possible loss of data casting from '" + 89 | DataType::printout(node->type) + "' to '" + 90 | DataType::printout(type) + "'"); 91 | } 92 | 93 | delete type_node; 94 | return cast(node, type); 95 | } 96 | 97 | ExpDataNode *NodeExpression::parse_in(Tokenizer &tk, ExpDataNode *node) 98 | { 99 | // Find operation info 100 | Token in = tk.match(TokenType::In, "in"); 101 | Token attr = tk.match(TokenType::Name, "Name"); 102 | 103 | // Create the right hand node 104 | ExpDataNode *attr_node = new ExpDataNode { nullptr, nullptr }; 105 | parse_args(tk, attr_node); 106 | attr_node->token = attr; 107 | 108 | // Create operation node 109 | ExpDataNode *operation = new ExpDataNode; 110 | operation->left = node; 111 | operation->right = attr_node; 112 | operation->token = in; 113 | operation->flags = NODE_IN; 114 | return operation; 115 | } 116 | 117 | ExpDataNode *NodeExpression::parse_transforms(Tokenizer &tk, ExpDataNode *node) 118 | { 119 | bool has_next_transform = true; 120 | 121 | // Keep persing transform operation until there is none left 122 | while (has_next_transform && !tk.is_eof()) 123 | { 124 | switch (tk.get_look().type) 125 | { 126 | case TokenType::In: node = parse_in(tk, node); break; 127 | case TokenType::OpenIndex: node = parse_indies(tk, node); break; 128 | case TokenType::As: node = parse_cast(tk, node); break; 129 | default: has_next_transform = false; break; 130 | } 131 | } 132 | 133 | return node; 134 | } 135 | 136 | static const Symbol &mold_function_call(Node *parent, ExpDataNode *node, Symbol overload, 137 | vector warnings, vector params) 138 | { 139 | // If they can be, modify the args to do so 140 | for (int i = 0; i < overload.params.size(); i++) 141 | { 142 | DataType param = params[i]; 143 | DataType target = overload.params[i]; 144 | if (warnings[i]) 145 | { 146 | Logger::warning(node->args[i]->token.debug_info, 147 | "Possible loss of data casting from '" + 148 | DataType::printout(param) + "' to '" + 149 | DataType::printout(target) + "'"); 150 | } 151 | 152 | // Cast the argument if need be, or ignore if auto type, 153 | // as autos do not need to be casted 154 | if (!DataType::equal(param, target) && 155 | !(target.flags & DATATYPE_AUTO)) 156 | { 157 | node->args[i] = cast(node->args[i], target); 158 | } 159 | } 160 | 161 | // If the function is a template, create this new implementations 162 | if (overload.flags & SYMBOL_TEMPLATE) 163 | { 164 | NodeFunction *template_func = (NodeFunction*)overload.parent; 165 | const Symbol &symb = template_func->implement(params); 166 | parent->get_parent(NodeType::Module)->push_symbol(symb); 167 | return symb; 168 | } 169 | return parent->lookup(overload.name, overload.params); 170 | } 171 | 172 | static const Symbol &find_alternet_overload(Node *parent, ExpDataNode *node, 173 | Token name, vector params) 174 | { 175 | vector overloads = parent->lookup_all(name.data); 176 | Symbol best_fit; 177 | vector best_warnings; 178 | bool has_best_fit = false; 179 | 180 | for (Symbol overload : overloads) 181 | { 182 | // Param size must be the same 183 | if (overload.params.size() != params.size()) 184 | continue; 185 | 186 | // Find if all params can be casted to the target ones 187 | bool can_cast = true; 188 | vector warnings; 189 | for (int i = 0; i < overload.params.size(); i++) 190 | { 191 | DataType param = params[i]; 192 | DataType target = overload.params[i]; 193 | if (target.flags & DATATYPE_AUTO) 194 | { 195 | // Ignore auto types 196 | warnings.push_back(false); 197 | continue; 198 | } 199 | 200 | bool warning = false; 201 | if (!DataType::equal(param, target) && 202 | !DataType::can_cast_to(param, target, warning)) 203 | { 204 | can_cast = false; 205 | break; 206 | } 207 | warnings.push_back(warning); 208 | } 209 | 210 | if (can_cast) 211 | { 212 | // Set the new best fit 213 | best_fit = overload; 214 | best_warnings = warnings; 215 | has_best_fit = true; 216 | 217 | // If a best fit was found that is not a template, 218 | // no need to continue searching 219 | if (best_fit.flags & SYMBOL_TEMPLATE) 220 | break; 221 | } 222 | } 223 | 224 | // If an appropriate overload was found, mold the call 225 | // to fit that function 226 | if (has_best_fit) 227 | { 228 | return mold_function_call(parent, node, best_fit, 229 | best_warnings, params); 230 | } 231 | 232 | // Otherwise, throw an error 233 | Symbol temp; 234 | temp.name = name.data; 235 | temp.params = params; 236 | Logger::error(name.debug_info, "Could not find a function that matches " + 237 | Symbol::printout(temp)); 238 | return SymbolTable::get_null(); 239 | } 240 | 241 | static const Symbol &find_symbol(Node *parent, ExpDataNode *node) 242 | { 243 | Logger::log(node->token.debug_info, "Fiding symbol '" + node->token.data + "'"); 244 | 245 | // Find the names symbol in the table 246 | Token name = node->token; 247 | if (node->args.size() > 0) 248 | { 249 | // If the node has arguments, then find the symbol with them 250 | vector params; 251 | params.reserve(node->args.size()); 252 | 253 | for (ExpDataNode *arg : node->args) 254 | params.push_back(arg->type); 255 | const Symbol &symb = parent->lookup(name.data, params); 256 | 257 | // If the symbol could not be found, search of an overload 258 | if (symb.flags & SYMBOL_NULL) 259 | return find_alternet_overload(parent, node, name, params); 260 | 261 | return symb; 262 | } 263 | 264 | const Symbol &symb = parent->lookup(name.data); 265 | if (symb.flags & SYMBOL_NULL) 266 | { 267 | Logger::error(name.debug_info, 268 | "Could not find symbol '" + 269 | name.data + "'"); 270 | } 271 | return symb; 272 | } 273 | 274 | void NodeExpression::parse_name(Tokenizer &tk, ExpDataNode *node) 275 | { 276 | // Find the tokens name and parse args if there is any 277 | parse_args(tk, node); 278 | } 279 | 280 | ExpDataNode *NodeExpression::parse_sub_expression(Tokenizer &tk, ExpDataNode *node) 281 | { 282 | ExpDataNode *sub = parse_expression(tk); 283 | delete node; 284 | tk.match(TokenType::CloseArg, ")"); 285 | 286 | return sub; 287 | } 288 | 289 | void NodeExpression::parse_ref(Tokenizer &tk, ExpDataNode *node) 290 | { 291 | // Parse the value to take a ref of 292 | ExpDataNode *lnode = parse_expression(tk); 293 | node->left = lnode; 294 | } 295 | 296 | void NodeExpression::parse_copy(Tokenizer &tk, ExpDataNode *node) 297 | { 298 | ExpDataNode *lnode = parse_expression(tk); 299 | node->left = lnode; 300 | } 301 | 302 | void NodeExpression::parse_negate(Tokenizer &tk, ExpDataNode *node) 303 | { 304 | // Create an operation of 0 - to negate 305 | ExpDataNode *lnode = parse_expression(tk); 306 | node->left = new ExpDataNode { nullptr, nullptr, lnode->type, 0, 307 | { "0", lnode->token.type, lnode->token.debug_info } }; 308 | node->right = lnode; 309 | node->flags |= NODE_OPERATION; 310 | node->type = lnode->type; 311 | } 312 | 313 | void NodeExpression::parse_array(Tokenizer &tk, ExpDataNode *node) 314 | { 315 | // Make this an array node 316 | node->flags = NODE_ARRAY; 317 | DataType arr_type; 318 | 319 | // Read all array items 320 | while (!tk.is_eof() && tk.get_look().type != TokenType::CloseIndex) 321 | { 322 | ExpDataNode *item = parse_expression(tk); 323 | node->args.push_back(item); 324 | 325 | // If there's more items, then match a next token 326 | if (tk.get_look().type != TokenType::CloseIndex) 327 | tk.match(TokenType::Next, ","); 328 | } 329 | 330 | tk.match(TokenType::CloseIndex, "]"); 331 | } 332 | 333 | ExpDataNode *NodeExpression::parse_type_name(Tokenizer &tk, ExpDataNode *node) 334 | { 335 | ExpDataNode *arg = parse_expression(tk); 336 | node->left = arg; 337 | return node; 338 | } 339 | 340 | ExpDataNode *NodeExpression::parse_type_size(Tokenizer &tk, ExpDataNode *node) 341 | { 342 | ExpDataNode *arg = parse_expression(tk); 343 | node->left = arg; 344 | return node; 345 | } 346 | 347 | ExpDataNode *NodeExpression::parse_array_size(Tokenizer &tk, ExpDataNode *node) 348 | { 349 | ExpDataNode *arg = parse_expression(tk); 350 | node->left = arg; 351 | return node; 352 | } 353 | 354 | ExpDataNode *NodeExpression::parse_term(Tokenizer &tk) 355 | { 356 | // Create a basic term node 357 | Token term = tk.skip_token(); 358 | ExpDataNode *node = new ExpDataNode; 359 | node->type = { PrimTypes::type_null(), 0, 0 }; 360 | node->left = nullptr; 361 | node->right = nullptr; 362 | node->flags = 0; 363 | node->token = term; 364 | 365 | // Fill in node data based on type 366 | switch (term.type) 367 | { 368 | case TokenType::Int: node->type = { PrimTypes::type_int(), 0 }; break; 369 | case TokenType::Float: node->type = { PrimTypes::type_float(), 0 }; break; 370 | case TokenType::Bool: node->type = { PrimTypes::type_bool(), 0 }; break; 371 | case TokenType::Char: node->type = { PrimTypes::type_char(), 0 }; break; 372 | case TokenType::String: node->type = { PrimTypes::type_null(), DATATYPE_ARRAY, (int)term.data.length() + 1, 373 | shared_ptr(new DataType { PrimTypes::type_char(), 0 }) }; break; 374 | case TokenType::Name: parse_name(tk, node); break; 375 | case TokenType::OpenArg: node = parse_sub_expression(tk, node); break; 376 | case TokenType::OpenIndex: parse_array(tk, node); break; 377 | case TokenType::Subtract: parse_negate(tk, node); break; 378 | case TokenType::Ref: parse_ref(tk, node); break; 379 | case TokenType::Copy: parse_copy(tk, node); break; 380 | case TokenType::TypeName: node = parse_type_name(tk, node); break; 381 | case TokenType::TypeSize: node = parse_type_size(tk, node); break; 382 | case TokenType::ArraySize: node = parse_array_size(tk, node); break; 383 | default: Logger::error(term.debug_info, "Unexpected token '" + term.data + "', expected NodeExpression"); 384 | } 385 | 386 | // Parse extra term transform operations 387 | return parse_transforms(tk, node); 388 | } 389 | 390 | static auto add_ops = { TokenType::Add, TokenType::Subtract }; 391 | static auto mul_ops = { TokenType::Multiply, TokenType::Divide }; 392 | static auto logic_ops = { TokenType::MoreThan, TokenType::LessThan, 393 | TokenType::MoreThanEquals, TokenType::LessThanEquals, TokenType::Equals }; 394 | 395 | DataType NodeExpression::parse_operation_type(ExpDataNode *left, ExpDataNode *right, Token op) 396 | { 397 | DataType l = left->type; 398 | DataType r = right->type; 399 | 400 | // if the operation is a logical one, then it returns a bool value 401 | if (std::find(logic_ops.begin(), logic_ops.end(), op.type) != logic_ops.end()) 402 | return { PrimTypes::type_bool(), 0, 0 }; 403 | 404 | if (l.construct == PrimTypes::type_int()) 405 | { 406 | if (r.construct == PrimTypes::type_int()) return l; 407 | if (r.construct == PrimTypes::type_float()) return r; 408 | if (r.construct == PrimTypes::type_char()) return l; 409 | } 410 | if (l.construct == PrimTypes::type_float()) 411 | { 412 | if (r.construct == PrimTypes::type_int()) return l; 413 | if (r.construct == PrimTypes::type_float()) return l; 414 | if (r.construct == PrimTypes::type_char()) return l; 415 | } 416 | if (l.construct == PrimTypes::type_char()) 417 | { 418 | if (r.construct == PrimTypes::type_int()) return l; 419 | if (r.construct == PrimTypes::type_float()) return l; 420 | if (r.construct == PrimTypes::type_char()) return l; 421 | } 422 | 423 | return l; 424 | } 425 | 426 | template 427 | ExpDataNode *NodeExpression::parse_operation(Tokenizer &tk, 428 | Func next_term, vector ops) 429 | { 430 | ExpDataNode *left = next_term(); 431 | while (std::find(ops.begin(), ops.end(), tk.get_look().type) != ops.end()) 432 | { 433 | Token op = tk.skip_token(); 434 | ExpDataNode *right = next_term(); 435 | DataType type = parse_operation_type(left, right, op); 436 | left = new ExpDataNode { left, right, type, NODE_OPERATION, op }; 437 | } 438 | return left; 439 | } 440 | 441 | ExpDataNode *NodeExpression::parse_expression(Tokenizer &tk) 442 | { 443 | auto term = [this, &tk] { return parse_term(tk); }; 444 | auto logic = [this, &tk, &term] { return parse_operation(tk, term, logic_ops); }; 445 | auto value = [this, &tk, &logic] { return parse_operation(tk, logic, mul_ops); }; 446 | auto factor = [this, &tk, &value] { return parse_operation(tk, value, add_ops); }; 447 | return factor(); 448 | } 449 | 450 | void NodeExpression::parse(Tokenizer &tk) 451 | { 452 | exp = parse_expression(tk); 453 | } 454 | 455 | void NodeExpression::free_node(ExpDataNode *node) 456 | { 457 | if (node->left != nullptr) 458 | free_node(node->left); 459 | 460 | if (node->right != nullptr) 461 | free_node(node->right); 462 | 463 | for (ExpDataNode *arg : node->args) 464 | free_node(arg); 465 | 466 | delete node; 467 | } 468 | 469 | bool NodeExpression::is_static_value() const 470 | { 471 | if (exp->flags & NODE_OPERATION) 472 | return false; 473 | return true; 474 | } 475 | 476 | int NodeExpression::get_int_value() const 477 | { 478 | return std::atoi(exp->token.data.c_str()); 479 | } 480 | 481 | static void symbolize_name(Node *parent, ExpDataNode *node) 482 | { 483 | if (parent == nullptr) 484 | return; 485 | 486 | Symbol symb = find_symbol(parent, node); 487 | 488 | // Copy the symbols type data to the node 489 | node->type = symb.type; 490 | node->symb = symb; 491 | 492 | // If the symbol is a function call, mark the node as one 493 | if (symb.flags & SYMBOL_FUNCTION) 494 | node->flags |= NODE_CALL; 495 | } 496 | 497 | void NodeExpression::symbolize_ref(ExpDataNode *node) 498 | { 499 | // Create the new ref 500 | ExpDataNode *lnode = node->left; 501 | DataType type; 502 | if (lnode->type.flags & DATATYPE_ARRAY) 503 | type = *lnode->type.sub_type; 504 | else 505 | type = lnode->type; 506 | 507 | node->type = DataType { PrimTypes::type_null(), DATATYPE_REF, 508 | 0, std::make_shared(type) }; 509 | } 510 | 511 | void NodeExpression::symbolize_copy(ExpDataNode *node) 512 | { 513 | ExpDataNode *lnode = node->left; 514 | if (!(lnode->type.flags & DATATYPE_REF)) 515 | Logger::error(node->token.debug_info, "Only a ref value can be copied"); 516 | 517 | node->type = *lnode->type.sub_type; 518 | } 519 | 520 | void NodeExpression::symbolize_typesize(ExpDataNode *node) 521 | { 522 | node->type = DataType { PrimTypes::type_int(), 0 }; 523 | } 524 | 525 | void NodeExpression::symbolize_typename(ExpDataNode *node) 526 | { 527 | string name = DataType::printout(node->left->type); 528 | 529 | node->type = DataType { PrimTypes::type_null(), DATATYPE_ARRAY, (int)name.length()+1, 530 | std::make_shared(DataType { PrimTypes::type_char(), 0 }) }; 531 | } 532 | 533 | void NodeExpression::symbolize_arraysize(ExpDataNode *node) 534 | { 535 | DataType type = node->left->type; 536 | if (!(type.flags & DATATYPE_ARRAY)) 537 | { 538 | Logger::error(node->left->token.debug_info, 539 | "Must be an array type to find the size"); 540 | } 541 | 542 | node->type = DataType { PrimTypes::type_int(), 0 }; 543 | } 544 | 545 | void NodeExpression::symbolize_array(ExpDataNode *node) 546 | { 547 | DataType arr_type; 548 | bool found_type = false; 549 | for (ExpDataNode *node : node->args) 550 | { 551 | if (!found_type) 552 | { 553 | arr_type = node->type; 554 | found_type = true; 555 | } 556 | 557 | if (!DataType::equal(arr_type, node->type)) 558 | { 559 | Logger::error(node->token.debug_info, 560 | "Arrays must contain only the same type"); 561 | } 562 | } 563 | 564 | node->type = DataType { PrimTypes::type_null(), DATATYPE_ARRAY, 565 | (int)node->args.size(), std::make_shared(arr_type) }; 566 | } 567 | 568 | void NodeExpression::symbolize_module_attr(ExpDataNode *node, const Symbol &left_symb) 569 | { 570 | Node *attrs = left_symb.parent; 571 | ExpDataNode *right = node->right; 572 | for (ExpDataNode *arg : right->args) 573 | symbolize_node(arg); 574 | symbolize_name(attrs, right); 575 | 576 | node->symb = right->symb; 577 | node->type = right->type; 578 | node->flags = right->flags; 579 | node->left = right->left; 580 | node->right = right->right; 581 | node->token = right->token; 582 | node->args = right->args; 583 | delete right; 584 | } 585 | 586 | bool NodeExpression::symbolize_special(ExpDataNode *node) 587 | { 588 | if (node->flags & NODE_IN) 589 | { 590 | symbolize_node(node->left); 591 | 592 | const Symbol &left_symb = node->left->symb; 593 | if (left_symb.flags & SYMBOL_MODULE) 594 | { 595 | symbolize_module_attr(node, left_symb); 596 | return true; 597 | } 598 | 599 | // Find attr container 600 | DataType type = node->left->type; 601 | while (type.flags & DATATYPE_REF) 602 | type = *type.sub_type; 603 | Node *attrs = type.construct->parent; 604 | 605 | // Find the symbol within that container 606 | for (ExpDataNode *arg : node->right->args) 607 | symbolize_node(arg); 608 | symbolize_name(attrs, node->right); 609 | node->type = node->right->type; 610 | return true; 611 | } 612 | 613 | if (node->flags & NODE_INDEX) 614 | { 615 | symbolize_node(node->left); 616 | symbolize_node(node->right); 617 | node->type = *node->left->type.sub_type; 618 | return true; 619 | } 620 | 621 | return false; 622 | } 623 | 624 | void NodeExpression::symbolize_node(ExpDataNode *node) 625 | { 626 | // Symbolize arguments 627 | for (ExpDataNode *arg : node->args) 628 | symbolize_node(arg); 629 | 630 | if (!symbolize_special(node)) 631 | { 632 | // Symbolize left an right nodes 633 | if (node->left != nullptr) 634 | symbolize_node(node->left); 635 | if (node->right != nullptr) 636 | symbolize_node(node->right); 637 | 638 | // Find operation type 639 | if (node->flags & NODE_OPERATION) 640 | { 641 | node->type = parse_operation_type(node->left, 642 | node->right, node->token); 643 | } 644 | 645 | switch (node->token.type) 646 | { 647 | case TokenType::Name: symbolize_name(this, node); break; 648 | case TokenType::Ref: symbolize_ref(node); break; 649 | case TokenType::Copy: symbolize_copy(node); break; 650 | case TokenType::TypeSize: symbolize_typesize(node); break; 651 | case TokenType::TypeName: symbolize_typename(node); break; 652 | case TokenType::ArraySize: symbolize_arraysize(node); break; 653 | case TokenType::OpenIndex: symbolize_array(node); break; 654 | } 655 | } 656 | } 657 | 658 | void NodeExpression::symbolize() 659 | { 660 | Logger::start_scope(); 661 | symbolize_node(exp); 662 | Logger::end_scope(); 663 | } 664 | 665 | static ExpDataNode *copy_data_node(ExpDataNode *other) 666 | { 667 | ExpDataNode *node = new ExpDataNode; 668 | node->left = other->left ? copy_data_node(other->left) : nullptr; 669 | node->right = other->right ? copy_data_node(other->right) : nullptr; 670 | for (ExpDataNode *arg : other->args) 671 | node->args.push_back(copy_data_node(arg)); 672 | node->flags = other->flags; 673 | node->symb = other->symb; 674 | node->token = other->token; 675 | node->type = other->type; 676 | return node; 677 | } 678 | 679 | Node *NodeExpression::copy(Node *parent) 680 | { 681 | NodeExpression *other = new NodeExpression(parent); 682 | other->exp = copy_data_node(exp); 683 | return other; 684 | } 685 | -------------------------------------------------------------------------------- /compiler/src/Parser/Function.cpp: -------------------------------------------------------------------------------- 1 | #include "Parser/Function.hpp" 2 | #include "Parser/Module.hpp" 3 | using namespace TinyScript; 4 | 5 | void NodeLet::parse(Tokenizer &tk) 6 | { 7 | // Parse let 8 | tk.match(TokenType::Let, "let"); 9 | name = tk.match(TokenType::Name, "name"); 10 | use_static_type = false; 11 | value = nullptr; 12 | 13 | if (tk.get_look().type == TokenType::Of) 14 | { 15 | tk.match(TokenType::Of, ":"); 16 | static_type = parse_node(tk); 17 | use_static_type = true; 18 | } 19 | 20 | if (!use_static_type || tk.get_look().type == TokenType::Assign) 21 | { 22 | tk.match(TokenType::Assign, "="); 23 | value = parse_node(tk); 24 | } 25 | 26 | Logger::log(name.debug_info, "Created new var '" + 27 | name.data + "'"); 28 | } 29 | 30 | void NodeLet::symbolize() 31 | { 32 | // Create new var symbol 33 | DataType type = use_static_type ? 34 | static_type->compile() : value->get_data_type(); 35 | int size = DataType::find_size(type); 36 | int location = allocate(size); 37 | 38 | if (use_static_type && value != nullptr) 39 | { 40 | if (!DataType::equal(type, value->get_data_type())) 41 | { 42 | Logger::error(value->get_data()->token.debug_info, 43 | "Cannot assign '" + DataType::printout(value->get_data_type()) + 44 | "' to '" + DataType::printout(type) + "'"); 45 | } 46 | } 47 | 48 | symb = Symbol(name.data, type, SYMBOL_LOCAL, location, this); 49 | get_parent()->push_symbol(symb); 50 | } 51 | 52 | Node *NodeLet::copy(Node *parent) 53 | { 54 | NodeLet *other = new NodeLet(parent); 55 | other->name = name; 56 | other->symb = symb; 57 | other->value = (NodeExpression*)value->copy(other); 58 | other->static_type = static_type; 59 | other->use_static_type = use_static_type; 60 | return other; 61 | } 62 | 63 | NodeLet::~NodeLet() 64 | { 65 | delete value; 66 | if (use_static_type) 67 | delete static_type; 68 | } 69 | 70 | void NodeAssign::parse(Tokenizer &tk) 71 | { 72 | Logger::log(tk.get_debug_info(), "Parsing assign statement"); 73 | 74 | left = parse_node(tk); 75 | right = nullptr; 76 | if (tk.get_look().type == TokenType::Assign) 77 | { 78 | tk.match(TokenType::Assign, "="); 79 | right = parse_node(tk); 80 | } 81 | } 82 | 83 | Node *NodeAssign::copy(Node *parent) 84 | { 85 | NodeAssign *other = new NodeAssign(parent); 86 | other->left = (NodeExpression*)left->copy(other); 87 | 88 | if (right != nullptr) 89 | other->right = (NodeExpression*)right->copy(other); 90 | else 91 | other->right = nullptr; 92 | return other; 93 | } 94 | 95 | NodeAssign::~NodeAssign() 96 | { 97 | delete left; 98 | if (right != nullptr) 99 | delete right; 100 | } 101 | 102 | void NodeReturn::parse(Tokenizer &tk) 103 | { 104 | tk.match(TokenType::Return, "return"); 105 | value = parse_node(tk); 106 | } 107 | 108 | Node *NodeReturn::copy(Node *parent) 109 | { 110 | NodeReturn *other = new NodeReturn(parent); 111 | other->value = (NodeExpression*)value->copy(other); 112 | return other; 113 | } 114 | 115 | NodeReturn::~NodeReturn() 116 | { 117 | delete value; 118 | } 119 | 120 | void NodeIf::parse(Tokenizer &tk) 121 | { 122 | tk.match(TokenType::If, "if"); 123 | condition = parse_node(tk); 124 | parse_block(tk); 125 | } 126 | 127 | Node *NodeIf::copy(Node *parent) 128 | { 129 | NodeIf *other = new NodeIf(parent); 130 | copy_block(other); 131 | other->condition = (NodeExpression*)condition->copy(other); 132 | return other; 133 | } 134 | 135 | NodeIf::~NodeIf() 136 | { 137 | delete condition; 138 | } 139 | 140 | void NodeFor::parse(Tokenizer &tk) 141 | { 142 | tk.match(TokenType::For, "for"); 143 | left = parse_node(tk); 144 | tk.match(TokenType::Assign, "="); 145 | from = parse_node(tk); 146 | tk.match(TokenType::To, "to"); 147 | to = parse_node(tk); 148 | 149 | parse_block(tk); 150 | } 151 | 152 | Node *NodeFor::copy(Node *parent) 153 | { 154 | NodeFor *other = new NodeFor(parent); 155 | copy_block(other); 156 | other->left = (NodeExpression*)left->copy(other); 157 | other->from = (NodeExpression*)from->copy(other); 158 | other->to = (NodeExpression*)to->copy(other); 159 | return other; 160 | } 161 | 162 | NodeFor::~NodeFor() 163 | { 164 | delete left; 165 | delete from; 166 | delete to; 167 | } 168 | 169 | void NodeWhile::parse(Tokenizer &tk) 170 | { 171 | tk.match(TokenType::While, "while"); 172 | condition = parse_node(tk); 173 | parse_block(tk); 174 | } 175 | 176 | Node *NodeWhile::copy(Node *parent) 177 | { 178 | NodeWhile *other = new NodeWhile(parent); 179 | copy_block(other); 180 | other->condition = (NodeExpression*)condition->copy(other); 181 | return other; 182 | } 183 | 184 | NodeWhile::~NodeWhile() 185 | { 186 | delete condition; 187 | } 188 | 189 | void NodeCodeBlock::parse_statement(Tokenizer &tk) 190 | { 191 | switch (tk.get_look().type) 192 | { 193 | case TokenType::Let: parse_child(tk); break; 194 | case TokenType::Return: parse_child(tk); break; 195 | case TokenType::If: parse_child(tk); break; 196 | case TokenType::For: parse_child(tk); break; 197 | case TokenType::While: parse_child(tk); break; 198 | default: parse_child(tk); break; 199 | } 200 | } 201 | 202 | void NodeCodeBlock::parse_block(Tokenizer &tk) 203 | { 204 | if (tk.get_look().type == TokenType::OpenBlock) 205 | { 206 | tk.match(TokenType::OpenBlock, "{"); 207 | while (tk.get_look().type != TokenType::CloseBlock && !tk.is_eof()) 208 | parse_statement(tk); 209 | tk.match(TokenType::CloseBlock, "}"); 210 | 211 | return; 212 | } 213 | 214 | parse_statement(tk); 215 | } 216 | 217 | void NodeFunction::parse(Tokenizer &tk) 218 | { 219 | tk.match(TokenType::Func, "func"); 220 | name = tk.match(TokenType::Name, "name"); 221 | Logger::log(name.debug_info, "Parsing function '" + name.data + "'"); 222 | 223 | is_template_flag = false; 224 | parse_params(tk); 225 | parse_return_type(tk); 226 | 227 | // Parse function body 228 | Logger::start_scope(); 229 | parse_block(tk); 230 | Logger::end_scope(); 231 | } 232 | 233 | void NodeFunction::parse_params(Tokenizer &tk) 234 | { 235 | tk.match(TokenType::OpenArg, "("); 236 | while(tk.get_look().type != TokenType::CloseArg && !tk.is_eof()) 237 | { 238 | // Parse param info 239 | NodeDataType *type = parse_node(tk); 240 | Token name = tk.match(TokenType::Name, "name"); 241 | 242 | params.push_back({ name, type }); 243 | if (type->is_auto()) 244 | is_template_flag = true; 245 | 246 | Logger::log(name.debug_info, "Found param '" + 247 | name.data + "'"); 248 | 249 | if (tk.get_look().type != TokenType::CloseArg) 250 | tk.match(TokenType::Next, ","); 251 | } 252 | 253 | tk.match(TokenType::CloseArg, ")"); 254 | } 255 | 256 | void NodeFunction::parse_return_type(Tokenizer &tk) 257 | { 258 | return_type_node = nullptr; 259 | 260 | if (tk.get_look().type == TokenType::Gives) 261 | { 262 | tk.match(TokenType::Gives, "->"); 263 | return_type_node = parse_node(tk); 264 | } 265 | } 266 | 267 | const Symbol &NodeFunction::implement(vector param_types) 268 | { 269 | NodeModule *mod = (NodeModule*)get_parent(NodeType::Module); 270 | NodeFunction *imp = (NodeFunction*)copy(mod); 271 | 272 | imp->symb = Symbol(imp->name.data, symb.type, SYMBOL_FUNCTION, 0, imp); 273 | imp->symb.params = param_types; 274 | imp->is_template_flag = false; 275 | int allocator = -8; 276 | for (int i = 0; i < params.size(); i++) 277 | { 278 | Token name = params[i].name; 279 | DataType type = param_types[i]; 280 | 281 | int size = DataType::find_size(type); 282 | allocator -= size; 283 | arg_size += size; 284 | imp->push_symbol(Symbol(name.data, type, 285 | SYMBOL_LOCAL, allocator, imp)); 286 | } 287 | Logger::log(name.debug_info, "implementing '" + 288 | Symbol::printout(imp->symb) + "'"); 289 | 290 | mod->add_child(imp); 291 | mod->push_symbol(imp->symb); 292 | return mod->lookup(imp->name.data, param_types); 293 | } 294 | 295 | Node *NodeFunction::copy(Node *parent) 296 | { 297 | NodeFunction *other = new NodeFunction(parent); 298 | copy_block(other); 299 | other->name = name; 300 | other->symb = symb; 301 | other->arg_size = arg_size; 302 | other->is_template_flag = is_template_flag; 303 | other->return_type_node = nullptr; 304 | 305 | if (return_type_node) 306 | other->return_type_node = (NodeDataType*)return_type_node->copy(other); 307 | 308 | for (auto param : params) 309 | { 310 | FunctionParam other_param; 311 | other_param.name = name; 312 | other_param.type_node = param.type_node ? 313 | (NodeDataType*)param.type_node->copy(other) : nullptr; 314 | other_param.type = param.type; 315 | other->params.push_back(other_param); 316 | } 317 | return other; 318 | } 319 | 320 | void NodeFunction::make_method(DataConstruct *construct) 321 | { 322 | // Create self type as 'Self ref self' 323 | DataType base_type = { construct, 0 }; 324 | DataType self_type = { PrimTypes::type_null(), DATATYPE_REF }; 325 | self_type.sub_type = std::make_shared(base_type); 326 | 327 | // Create self param and add it to the begining of the param list 328 | FunctionParam self; 329 | self.name = { "self", TokenType::Name, name.debug_info }; 330 | self.type_node = nullptr; 331 | self.type = self_type; 332 | params.insert(params.begin(), self); 333 | } 334 | 335 | void NodeFunction::register_func() 336 | { 337 | Logger::log({}, "Regestering function '" + name.data + "'"); 338 | 339 | // Don't register template types 340 | vector param_types; 341 | int allocator = -8; 342 | for (auto param : params) 343 | { 344 | Token name = param.name; 345 | NodeDataType *type_node = param.type_node; 346 | DataType type = type_node ? type_node->compile() : param.type; 347 | param_types.push_back(type); 348 | 349 | if (!is_template_flag) 350 | { 351 | int size = DataType::find_size(type); 352 | allocator -= size; 353 | arg_size += size; 354 | push_symbol(Symbol(name.data, type, 355 | SYMBOL_LOCAL, allocator, this)); 356 | } 357 | } 358 | 359 | // Find return type 360 | DataType return_type = { PrimTypes::type_null(), 0 }; 361 | if (return_type_node != nullptr) 362 | return_type = return_type_node->compile(); 363 | 364 | // Create function symbol 365 | int flags = SYMBOL_FUNCTION; 366 | if (is_template_flag) 367 | flags |= SYMBOL_TEMPLATE; 368 | symb = Symbol(name.data, return_type, flags, 0, this); 369 | symb.params = param_types; 370 | 371 | if (get_parent() != nullptr) 372 | get_parent()->push_symbol(symb); 373 | } 374 | 375 | NodeFunction::~NodeFunction() 376 | { 377 | for (auto param : params) 378 | if (param.type_node) 379 | delete param.type_node; 380 | 381 | if (return_type_node != nullptr) 382 | delete return_type_node; 383 | } 384 | -------------------------------------------------------------------------------- /compiler/src/Parser/Module.cpp: -------------------------------------------------------------------------------- 1 | #include "Parser/Module.hpp" 2 | #include "Parser/Function.hpp" 3 | #include "Parser/Class.hpp" 4 | #include "Parser/Program.hpp" 5 | using namespace TinyScript; 6 | 7 | void NodeImportFrom::parse(Tokenizer &tk) 8 | { 9 | // Parse import from statement 10 | tk.match(TokenType::From, "from"); 11 | module = tk.match(TokenType::Name, "Module Name"); 12 | tk.match(TokenType::Import, "import"); 13 | attr = tk.match(TokenType::Name, "Attr Name"); 14 | 15 | // Load module 16 | NodeProgram *program = (NodeProgram*)get_parent(NodeType::Program); 17 | mod = program->load_module(module.data); 18 | 19 | Logger::log(module.debug_info, "Parsing import '" + 20 | attr.data + "' from '" + module.data + "'"); 21 | } 22 | 23 | Node *NodeImportFrom::copy(Node *parent) 24 | { 25 | NodeImportFrom *other = new NodeImportFrom(parent); 26 | other->module = module; 27 | other->attr = attr; 28 | return other; 29 | } 30 | 31 | void NodeImportFrom::symbolize() 32 | { 33 | if (mod == nullptr) 34 | return; 35 | 36 | vector attrs = mod->lookup_all(attr.data); 37 | DataConstruct *construct = mod->find_construct(attr.data); 38 | if (attrs.size() == 0 && construct == PrimTypes::type_null()) 39 | { 40 | Logger::error(attr.debug_info, "Could not find symbol '" + 41 | attr.data + "'"); 42 | } 43 | 44 | for (Symbol symb : attrs) 45 | get_parent(NodeType::Module)->push_symbol(symb); 46 | } 47 | 48 | void NodeImportFrom::register_types() 49 | { 50 | if (mod == nullptr) 51 | return; 52 | 53 | DataConstruct *construct = mod->find_construct(attr.data); 54 | if (construct != PrimTypes::type_null()) 55 | get_parent(NodeType::Module)->add_construct(construct); 56 | Logger::log({}, "Importing types from '" + mod->get_name().data + "'"); 57 | } 58 | 59 | void NodeImport::parse(Tokenizer &tk) 60 | { 61 | tk.match(TokenType::Import, "import"); 62 | module = tk.match(TokenType::Name, "Module Name"); 63 | } 64 | 65 | Node *NodeImport::copy(Node *parent) 66 | { 67 | NodeImport *other = new NodeImport(parent); 68 | other->module = module; 69 | return other; 70 | } 71 | 72 | void NodeImport::symbolize() 73 | { 74 | NodeProgram *program = (NodeProgram*)get_parent(NodeType::Program); 75 | NodeModule *mod = program->load_module(module.data); 76 | if (mod == nullptr) 77 | return; 78 | 79 | Symbol symb(module.data, {}, SYMBOL_MODULE, 0, this); 80 | symb.parent = mod; 81 | get_parent(NodeType::Module)->push_symbol(symb); 82 | } 83 | 84 | void NodeExtern::parse(Tokenizer &tk) 85 | { 86 | tk.match(TokenType::Extern, "extern"); 87 | name = tk.match(TokenType::Name, "name"); 88 | return_type_node = nullptr; 89 | 90 | // Parse type only params 91 | if (tk.get_look().type == TokenType::OpenArg) 92 | { 93 | tk.match(TokenType::OpenArg, "("); 94 | while (tk.get_look().type != TokenType::CloseArg && !tk.is_eof()) 95 | { 96 | param_nodes.push_back(parse_node(tk)); 97 | 98 | if (tk.get_look().type != TokenType::CloseArg) 99 | tk.match(TokenType::Next, ","); 100 | } 101 | tk.match(TokenType::CloseArg, ")"); 102 | } 103 | 104 | Logger::log(name.debug_info, "Parsing extern '" + name.data + "'"); 105 | } 106 | 107 | void NodeExtern::register_extern() 108 | { 109 | // Compile return type 110 | DataType return_type = { PrimTypes::type_null(), 0 }; 111 | if (return_type_node) 112 | return_type = return_type_node->compile(); 113 | 114 | // Compile param types 115 | vector params; 116 | for (NodeDataType *type : param_nodes) 117 | params.push_back(type->compile()); 118 | 119 | // Create external symbol 120 | symb = Symbol(name.data, return_type, SYMBOL_FUNCTION | SYMBOL_EXTERNAL, rand(), this); 121 | symb.params = params; 122 | get_parent(NodeType::Module)->push_symbol(symb); 123 | } 124 | 125 | NodeExtern::~NodeExtern() 126 | { 127 | if (return_type_node) 128 | delete return_type_node; 129 | 130 | for (NodeDataType *type : param_nodes) 131 | delete type; 132 | } 133 | 134 | Node *NodeExtern::copy(Node *parent) 135 | { 136 | NodeExtern *other = new NodeExtern(parent); 137 | other->symb = symb; 138 | return other; 139 | } 140 | 141 | void NodeModule::parse(Tokenizer &tk) 142 | { 143 | Logger::start_scope(); 144 | 145 | while (!tk.is_eof()) 146 | { 147 | switch(tk.get_look().type) 148 | { 149 | case TokenType::Func: parse_child(tk); break; 150 | case TokenType::From: parse_child(tk); break; 151 | case TokenType::Import: parse_child(tk); break; 152 | case TokenType::Extern: parse_child(tk); break; 153 | case TokenType::Class: parse_child(tk); break; 154 | default: 155 | { 156 | Token error = tk.skip_token(); 157 | Logger::error(tk.get_debug_info(), 158 | "Unexpected token '" + error.data + "'"); 159 | } 160 | } 161 | } 162 | 163 | Logger::end_scope(); 164 | } 165 | 166 | Node *NodeModule::copy(Node *parent) 167 | { 168 | NodeModule *other = new NodeModule(parent); 169 | copy_block(other); 170 | other->set_name(name); 171 | return other; 172 | } 173 | 174 | bool NodeModule::is_compiled() const 175 | { 176 | if (!is_compiled_flag) 177 | return false; 178 | 179 | for (Node *child : children) 180 | { 181 | if (child->get_type() == NodeType::Function) 182 | { 183 | NodeFunction *func = (NodeFunction*)child; 184 | if (!func->is_compiled() && !func->is_template()) 185 | return false; 186 | } 187 | } 188 | 189 | return true; 190 | } 191 | 192 | void NodeModule::set_name(Token name) 193 | { 194 | this->name = name; 195 | prefix = name.data + "."; 196 | } 197 | 198 | void NodeModule::register_types() 199 | { 200 | Logger::log({}, "Registering types in '" + name.data + "' module"); 201 | Logger::start_scope(); 202 | 203 | for (Node *child : children) 204 | { 205 | switch (child->get_type()) 206 | { 207 | case NodeType::ImportFrom: ((NodeImportFrom*)child)->register_types(); break; 208 | case NodeType::Class: ((NodeClass*)child)->register_class(); break; 209 | default: break; 210 | } 211 | } 212 | 213 | Logger::end_scope(); 214 | } 215 | 216 | void NodeModule::register_functions() 217 | { 218 | Logger::log({}, "Registering functions in '" + name.data + "' module"); 219 | Logger::start_scope(); 220 | 221 | for (Node *child : children) 222 | { 223 | switch (child->get_type()) 224 | { 225 | case NodeType::Extern: ((NodeExtern*)child)->register_extern(); break; 226 | case NodeType::Function: ((NodeFunction*)child)->register_func(); break; 227 | case NodeType::Class: ((NodeClass*)child)->register_methods(); break; 228 | default: break; 229 | } 230 | } 231 | 232 | Logger::end_scope(); 233 | } 234 | -------------------------------------------------------------------------------- /compiler/src/Parser/Node.cpp: -------------------------------------------------------------------------------- 1 | #include "Parser/Node.hpp" 2 | #include "Parser/Expression.hpp" 3 | using namespace TinyScript; 4 | 5 | Node *Node::get_parent(NodeType type) 6 | { 7 | if (parent == nullptr || get_type() == type) 8 | return this; 9 | 10 | return parent->get_parent(type); 11 | } 12 | 13 | string Node::get_prefix() const 14 | { 15 | string out = prefix; 16 | if (parent != nullptr) 17 | out = parent->get_prefix() + out; 18 | 19 | return out; 20 | } 21 | 22 | vector Node::lookup_all(string name) const 23 | { 24 | vector out = table.lookup_all(name); 25 | if (parent != nullptr) 26 | for (Symbol symb : parent->lookup_all(name)) 27 | out.push_back(symb); 28 | return out; 29 | } 30 | 31 | vector Node::lookup_all() const 32 | { 33 | vector out = table.lookup_all(); 34 | if (parent != nullptr) 35 | for (Symbol symb : parent->lookup_all()) 36 | out.push_back(symb); 37 | return out; 38 | } 39 | 40 | const Symbol &Node::lookup(string name) const 41 | { 42 | const Symbol &symb = table.lookup(name); 43 | if (symb.flags & SYMBOL_NULL && parent != nullptr) 44 | return parent->lookup(name); 45 | return symb; 46 | } 47 | 48 | const Symbol &Node::lookup(string name, vector params) const 49 | { 50 | const Symbol &symb = table.lookup(name, params); 51 | if (symb.flags & SYMBOL_NULL && parent != nullptr) 52 | return parent->lookup(name, params); 53 | return symb; 54 | } 55 | 56 | void Node::push_symbol(Symbol symb) 57 | { 58 | if (lookup(symb.name, symb.params).flags & SYMBOL_NULL) 59 | table.push(symb); 60 | } 61 | 62 | DataConstruct *Node::find_construct(string name) 63 | { 64 | auto construct = table.find_construct(name); 65 | if (construct == PrimTypes::type_null() && parent != nullptr) 66 | return parent->find_construct(name); 67 | return construct; 68 | } 69 | 70 | vector Node::find_construct() 71 | { 72 | auto constructs = table.find_construct(); 73 | if (parent != nullptr) 74 | { 75 | auto parent_constructs = parent->find_construct(); 76 | constructs.insert(constructs.end(), 77 | parent_constructs.begin(), 78 | parent_constructs.end()); 79 | } 80 | 81 | return constructs; 82 | } 83 | 84 | NodeBlock::~NodeBlock() 85 | { 86 | for (Node *child : children) 87 | delete child; 88 | } 89 | 90 | int Node::allocate(int size) 91 | { 92 | if (is_allocator) 93 | return table.allocate(size); 94 | 95 | if (parent != nullptr) 96 | return parent->allocate(size); 97 | 98 | Logger::link_error("Could not find allocator node"); 99 | return 0; 100 | } 101 | 102 | void Node::copy_node(Node *other) 103 | { 104 | other->table = table; 105 | other->is_allocator = is_allocator; 106 | } 107 | 108 | void NodeBlock::copy_block(NodeBlock *to) 109 | { 110 | copy_node(to); 111 | for (Node *child : children) 112 | to->add_child(child->copy(to)); 113 | } 114 | -------------------------------------------------------------------------------- /compiler/src/Parser/Program.cpp: -------------------------------------------------------------------------------- 1 | #include "Parser/Program.hpp" 2 | #include "Parser/Module.hpp" 3 | using namespace TinyScript; 4 | 5 | NodeProgram::NodeProgram() : 6 | NodeBlock(nullptr) 7 | { 8 | cwd = ""; 9 | } 10 | 11 | NodeModule *NodeProgram::find_module(string name) const 12 | { 13 | for (Node *child : children) 14 | { 15 | NodeModule *mod = (NodeModule*)child; 16 | if (mod->get_name().data == name) 17 | return mod; 18 | } 19 | 20 | return nullptr; 21 | } 22 | 23 | void NodeProgram::parse() 24 | { 25 | for (string src : srcs) 26 | { 27 | int n_start = src.find_last_of('/') + 1; 28 | int n_end = src.find_last_of('.'); 29 | int n_len = n_end - n_start; 30 | string name = src.substr(n_start, n_len); 31 | cwd = src.substr(0, n_start); 32 | 33 | Tokenizer tk(src); 34 | Logger::log(tk.get_debug_info(), "Parsing module '" + name + "'"); 35 | NodeModule *mod = new NodeModule(this); 36 | add_child(mod); 37 | mod->set_name({ name, TokenType::Eof, tk.get_debug_info() }); 38 | mod->parse(tk); 39 | } 40 | } 41 | 42 | NodeModule *NodeProgram::load_module(string name) 43 | { 44 | NodeModule *mod = find_module(name); 45 | if (mod != nullptr) 46 | return mod; 47 | 48 | string src = name + ".tiny"; 49 | Tokenizer tk(cwd + src); 50 | Logger::log(tk.get_debug_info(), "Parsing module '" + name + "'"); 51 | 52 | mod = new NodeModule(this); 53 | add_child(mod); 54 | mod->set_name({ name, TokenType::Eof, tk.get_debug_info() }); 55 | mod->parse(tk); 56 | return mod; 57 | } 58 | 59 | void NodeProgram::pre_process() 60 | { 61 | Logger::log({}, "Preprossing program"); 62 | Logger::start_scope(); 63 | 64 | for (int i = 0; i < get_child_size(); i++) 65 | { 66 | NodeModule *mod = (NodeModule*)children[i]; 67 | mod->register_types(); 68 | mod->register_functions(); 69 | } 70 | 71 | Logger::end_scope(); 72 | } 73 | -------------------------------------------------------------------------------- /compiler/src/Parser/Symbol.cpp: -------------------------------------------------------------------------------- 1 | #include "Parser/Symbol.hpp" 2 | #include "Parser/Node.hpp" 3 | using namespace TinyScript; 4 | 5 | #define REF_SIZE 4 6 | 7 | // Define prim type info 8 | DataConstruct PrimTypes::dt_int = { "int", 4, nullptr }; 9 | DataConstruct PrimTypes::dt_float = { "float", 4, nullptr }; 10 | DataConstruct PrimTypes::dt_char = { "char", 1, nullptr }; 11 | DataConstruct PrimTypes::dt_bool = { "bool", 1, nullptr }; 12 | DataConstruct PrimTypes::dt_null = { "null", 0, nullptr }; 13 | 14 | Symbol SymbolTable::null_symbol = Symbol("null", { PrimTypes::type_null(), 0, 0 }, SYMBOL_NULL, 0, nullptr); 15 | 16 | int DataType::find_size(const DataType &type) 17 | { 18 | // If the type is a referance, it's of a constant size 19 | if (type.flags & DATATYPE_REF) 20 | return REF_SIZE; 21 | 22 | // If it's an array, then return the total array size 23 | if (type.flags & DATATYPE_ARRAY) 24 | return find_size(*type.sub_type) * type.array_size; 25 | 26 | // Otherwise, return the type size 27 | if (type.construct == nullptr) 28 | return 0; 29 | return type.construct->size; 30 | } 31 | 32 | #include 33 | 34 | bool DataType::equal(const DataType &a, const DataType &b) 35 | { 36 | if (a.construct == b.construct && a.flags == b.flags) 37 | { 38 | if (a.flags & DATATYPE_ARRAY && 39 | a.array_size != b.array_size) 40 | { 41 | return false; 42 | } 43 | 44 | if (a.flags & (DATATYPE_REF | DATATYPE_ARRAY) && 45 | !DataType::equal(*a.sub_type, *b.sub_type)) 46 | { 47 | return false; 48 | } 49 | 50 | return true; 51 | } 52 | 53 | return false; 54 | } 55 | 56 | void SymbolTable::push(Symbol symb) 57 | { 58 | symbols.push_back(symb); 59 | } 60 | 61 | void SymbolTable::push_all(vector symbs) 62 | { 63 | for (Symbol symb : symbs) 64 | symbols.push_back(symb); 65 | } 66 | 67 | string DataType::printout(const DataType &type) 68 | { 69 | if (type.flags & DATATYPE_AUTO) 70 | return "auto"; 71 | 72 | string type_name = type.construct->name; 73 | if (type.flags & DATATYPE_ARRAY) 74 | { 75 | type_name = printout(*type.sub_type) + 76 | " array[" + std::to_string(type.array_size) + "]"; 77 | } 78 | 79 | if (type.flags & DATATYPE_REF) 80 | { 81 | type_name = printout(*type.sub_type) + 82 | " ref"; 83 | } 84 | 85 | return type_name; 86 | } 87 | 88 | bool DataType::can_cast_to(const DataType &from, const DataType &to, bool &warning) 89 | { 90 | if (from.construct == PrimTypes::type_int()) 91 | { 92 | if (to.construct == PrimTypes::type_int()) return true; 93 | if (to.construct == PrimTypes::type_float()) return true; 94 | if (to.construct == PrimTypes::type_char()) { warning = true; return true; } 95 | if (to.construct == PrimTypes::type_bool()) return true; 96 | } 97 | else if (from.construct == PrimTypes::type_float()) 98 | { 99 | warning = true; 100 | if (to.construct == PrimTypes::type_int()) return true; 101 | if (to.construct == PrimTypes::type_float()) return true; 102 | if (to.construct == PrimTypes::type_char()) return true; 103 | if (to.construct == PrimTypes::type_bool()) return true; 104 | } 105 | else if (from.construct == PrimTypes::type_char()) 106 | { 107 | if (to.construct == PrimTypes::type_int()) return true; 108 | if (to.construct == PrimTypes::type_float()) return true; 109 | if (to.construct == PrimTypes::type_char()) return true; 110 | if (to.construct == PrimTypes::type_bool()) return true; 111 | } 112 | else if (from.construct == PrimTypes::type_bool()) 113 | { 114 | if (to.construct == PrimTypes::type_int()) return true; 115 | if (to.construct == PrimTypes::type_float()) return true; 116 | if (to.construct == PrimTypes::type_char()) return true; 117 | if (to.construct == PrimTypes::type_bool()) return true; 118 | } 119 | 120 | return false; 121 | } 122 | 123 | static string param_printout(const vector ¶ms) 124 | { 125 | string param_str = "("; 126 | for (int i = 0; i < params.size(); i++) 127 | { 128 | param_str += DataType::printout(params[i]); 129 | if (i < params.size() - 1) 130 | param_str += ", "; 131 | } 132 | return param_str + ")"; 133 | } 134 | 135 | string Symbol::printout(const Symbol &symb) 136 | { 137 | string prefix = ""; 138 | if (symb.parent != nullptr) 139 | prefix = symb.parent->get_prefix(); 140 | 141 | string str = prefix + symb.name; 142 | if (symb.params.size() > 0) 143 | str += param_printout(symb.params); 144 | return str; 145 | } 146 | 147 | vector SymbolTable::lookup_all(string name) const 148 | { 149 | vector out; 150 | for (const Symbol &symb : symbols) 151 | { 152 | if (symb.name == name) 153 | out.push_back(symb); 154 | } 155 | 156 | return out; 157 | } 158 | 159 | const Symbol &SymbolTable::lookup(string name) const 160 | { 161 | for (const Symbol &symb : symbols) 162 | if (symb.name == name) 163 | return symb; 164 | 165 | return null_symbol; 166 | } 167 | 168 | const Symbol &SymbolTable::lookup(string name, vector params) const 169 | { 170 | for (const Symbol &symb : symbols) 171 | { 172 | if (symb.name == name && symb.params.size() == params.size()) 173 | { 174 | bool is_equal = true; 175 | for (int i = 0; i < params.size(); i++) 176 | { 177 | DataType p1 = symb.params[i]; 178 | DataType p2 = params[i]; 179 | if (!DataType::equal(p1, p2)) 180 | is_equal = false; 181 | } 182 | 183 | if (is_equal) 184 | return symb; 185 | } 186 | } 187 | 188 | return null_symbol; 189 | } 190 | 191 | vector SymbolTable::find_externals() const 192 | { 193 | vector out; 194 | for (const Symbol &symb : symbols) 195 | if (symb.flags & SYMBOL_EXTERNAL) 196 | out.push_back(symb); 197 | return out; 198 | } 199 | 200 | DataConstruct *SymbolTable::find_construct(string name) 201 | { 202 | // Search built in constructs 203 | if (name == "int") return PrimTypes::type_int(); 204 | else if (name == "float") return PrimTypes::type_float(); 205 | else if (name == "char") return PrimTypes::type_char(); 206 | else if (name == "bool") return PrimTypes::type_bool(); 207 | 208 | // Search internal constructs 209 | for (DataConstruct *construct : constructs) 210 | if (construct->name == name) 211 | return construct; 212 | 213 | // Search external constructs 214 | for (DataConstruct *construct : external_constructs) 215 | if (construct->name == name) 216 | return construct; 217 | 218 | // Could not be found 219 | return PrimTypes::type_null(); 220 | } 221 | 222 | vector SymbolTable::find_construct() 223 | { 224 | vector out; 225 | out.insert(out.end(), constructs.begin(), constructs.end()); 226 | out.insert(out.end(), external_constructs.begin(), external_constructs.end()); 227 | return out; 228 | } 229 | 230 | DataConstruct *SymbolTable::create_construct(string name) 231 | { 232 | DataConstruct *construct = new DataConstruct { name, 0, nullptr }; 233 | constructs.push_back(construct); 234 | return construct; 235 | } 236 | 237 | void SymbolTable::new_allocation_space() 238 | { 239 | allocator = 0; 240 | scope_size = 0; 241 | } 242 | 243 | int SymbolTable::allocate(int size) 244 | { 245 | int start = allocator; 246 | allocator += size; 247 | return start; 248 | } 249 | 250 | int SymbolTable::get_scope_size() const 251 | { 252 | return std::max(allocator, scope_size); 253 | } 254 | 255 | bool SymbolTable::is_null(const Symbol &symb) 256 | { 257 | return symb.flags & SYMBOL_NULL; 258 | } 259 | 260 | void SymbolTable::patch_params(vector params) 261 | { 262 | int allocator = -8; 263 | 264 | for (int i = 0; i < params.size(); i++) 265 | { 266 | allocator -= DataType::find_size(params[i]); 267 | symbols[i].type = params[i]; 268 | symbols[i].location = allocator; 269 | } 270 | } 271 | 272 | SymbolTable::~SymbolTable() 273 | { 274 | for (DataConstruct *construct : constructs) 275 | delete construct; 276 | } 277 | -------------------------------------------------------------------------------- /compiler/src/Tokenizer.cpp: -------------------------------------------------------------------------------- 1 | #include "Tokenizer.hpp" 2 | #include 3 | using namespace TinyScript; 4 | 5 | // Define keywords 6 | static map keywords = 7 | { 8 | std::make_pair("ref", TokenType::Ref), 9 | std::make_pair("array", TokenType::Array), 10 | std::make_pair("copy", TokenType::Copy), 11 | std::make_pair("as", TokenType::As), 12 | std::make_pair("func", TokenType::Func), 13 | std::make_pair("class", TokenType::Class), 14 | std::make_pair("let", TokenType::Let), 15 | std::make_pair("return", TokenType::Return), 16 | std::make_pair("if", TokenType::If), 17 | std::make_pair("for", TokenType::For), 18 | std::make_pair("while", TokenType::While), 19 | std::make_pair("from", TokenType::From), 20 | std::make_pair("to", TokenType::To), 21 | std::make_pair("import", TokenType::Import), 22 | std::make_pair("extern", TokenType::Extern), 23 | std::make_pair("true", TokenType::Bool), 24 | std::make_pair("false", TokenType::Bool), 25 | std::make_pair("typename", TokenType::TypeName), 26 | std::make_pair("typesize", TokenType::TypeSize), 27 | std::make_pair("arraysize", TokenType::ArraySize), 28 | std::make_pair("typeof", TokenType::TypeOf), 29 | std::make_pair("auto", TokenType::Auto) 30 | }; 31 | 32 | static map sub = { std::make_pair('>', TokenType::Gives) }; 33 | static map more = { std::make_pair('=', TokenType::MoreThanEquals) }; 34 | static map less = { std::make_pair('=', TokenType::LessThanEquals) }; 35 | static map equal = { std::make_pair('=', TokenType::Equals) }; 36 | 37 | Tokenizer::Tokenizer(string file_path) : 38 | debug_info { 1, 0, 0 }, 39 | file_path(file_path) 40 | { 41 | // Open file stream and find the first token 42 | file = std::ifstream(file_path); 43 | eof_override = false; 44 | debug_info.file_name = file_path; 45 | 46 | if (!file.good()) 47 | { 48 | Logger::error(debug_info, "Cannot open file '" + file_path + "'"); 49 | eof_override = true; 50 | } 51 | 52 | look = next_token(); 53 | } 54 | 55 | Tokenizer::Tokenizer(Tokenizer &other) 56 | { 57 | // Open a new stream in the exact same state as other 58 | debug_info = other.debug_info; 59 | file_path = other.file_path; 60 | file = std::ifstream(file_path); 61 | file.seekg(other.file.tellg()); 62 | look = other.look; 63 | eof_override = other.eof_override; 64 | } 65 | 66 | Token Tokenizer::match(TokenType type, string name) 67 | { 68 | if (look.type != type) 69 | { 70 | Logger::error(debug_info, "Expected token '" + name + 71 | "', got '" + look.data + "' instead"); 72 | } 73 | 74 | return skip_token(); 75 | } 76 | 77 | Token Tokenizer::skip_token() 78 | { 79 | Token tk = look; 80 | look = next_token(); 81 | return tk; 82 | } 83 | 84 | void Tokenizer::skip_scope() 85 | { 86 | int scope_depth = 1; 87 | while (scope_depth >= 1 && !is_eof()) 88 | { 89 | char c = next_char(); 90 | switch (c) 91 | { 92 | case '{': scope_depth++; break; 93 | case '}': scope_depth--; break; 94 | } 95 | } 96 | 97 | skip_token(); 98 | } 99 | 100 | void Tokenizer::parse_comment() 101 | { 102 | char c = next_char(); 103 | while (c != '\n') 104 | c = next_char(); 105 | } 106 | 107 | Token Tokenizer::next_token() 108 | { 109 | skip_whitespace(); 110 | DebugInfo dbi = debug_info; 111 | 112 | while (!is_eof()) 113 | { 114 | char c = next_char(); 115 | if (c == '#') 116 | { 117 | parse_comment(); 118 | continue; 119 | } 120 | 121 | switch (c) 122 | { 123 | case '+': return Token { string(1, c), TokenType::Add, dbi }; 124 | case '-': return parse_double(c, TokenType::Subtract, sub, dbi); 125 | case '*': return Token { string(1, c), TokenType::Multiply, dbi }; 126 | case '/': return Token { string(1, c), TokenType::Divide, dbi }; 127 | case '=': return parse_double(c, TokenType::Assign, equal, dbi); 128 | case ':': return parse_double(c, TokenType::Of, equal, dbi); 129 | 130 | case '>': return parse_double(c, TokenType::MoreThan, more, dbi); 131 | case '<': return parse_double(c, TokenType::LessThan, more, dbi); 132 | 133 | case '{': return Token { string(1, c), TokenType::OpenBlock, dbi }; 134 | case '}': return Token { string(1, c), TokenType::CloseBlock, dbi }; 135 | case '(': return Token { string(1, c), TokenType::OpenArg, dbi }; 136 | case ')': return Token { string(1, c), TokenType::CloseArg, dbi }; 137 | case '[': return Token { string(1, c), TokenType::OpenIndex, dbi }; 138 | case ']': return Token { string(1, c), TokenType::CloseIndex, dbi }; 139 | case ',': return Token { string(1, c), TokenType::Next, dbi }; 140 | case '.': return Token { string(1, c), TokenType::In, dbi }; 141 | 142 | case '\'': return parse_char(dbi); 143 | case '"': return parse_string(dbi); 144 | } 145 | 146 | if (isalpha(c)) return parse_name(c, dbi); 147 | else if (isdigit(c)) return parse_number(c, dbi); 148 | 149 | if (c != 0) 150 | Logger::error(dbi, "Unkown symbol '" + string(1, c) + "'"); 151 | } 152 | 153 | return Token { "EOF", TokenType::Eof }; 154 | } 155 | 156 | Token Tokenizer::parse_double(char c, TokenType def, map m, DebugInfo dbi) 157 | { 158 | char next = next_char(); 159 | string buffer = string(1, c); 160 | 161 | if (m.find(next) != m.end()) 162 | return Token { buffer + next, m[next], dbi }; 163 | 164 | back_buffer.push_back(next); 165 | return Token { buffer, def, dbi }; 166 | } 167 | 168 | Token Tokenizer::parse_number(char c, DebugInfo dbi) 169 | { 170 | string buffer = ""; 171 | int point_count = 0; 172 | while ((isdigit(c) || c == '.') && !is_eof()) 173 | { 174 | if (c == '.') 175 | point_count++; 176 | 177 | buffer += c; 178 | c = next_char(); 179 | } 180 | 181 | // if the number contains more then 1 point, 182 | // then it's an invalid number 183 | if (point_count > 1) 184 | Logger::error(debug_info, "Invalid float format '" + buffer + "'"); 185 | 186 | back_buffer.push_back(c); 187 | return Token { buffer, point_count == 0 ? 188 | TokenType::Int : TokenType::Float, dbi }; 189 | } 190 | 191 | Token Tokenizer::parse_name(char c, DebugInfo dbi) 192 | { 193 | string buffer = ""; 194 | while ((isalnum(c) || c == '_') && !is_eof()) 195 | { 196 | buffer += c; 197 | c = next_char(); 198 | } 199 | 200 | back_buffer.push_back(c); 201 | if (keywords.find(buffer) != keywords.end()) 202 | return Token { buffer, keywords[buffer], dbi }; 203 | return Token { buffer, TokenType::Name, dbi }; 204 | } 205 | 206 | char Tokenizer::next_escaped_char() 207 | { 208 | char c = next_char(); 209 | if (c == '\\') 210 | { 211 | // Parse escape char 212 | switch (next_char()) 213 | { 214 | case '0': c = '\0'; break; 215 | case 'n': c = '\n'; break; 216 | case '\\': c = '\\'; break; 217 | } 218 | } 219 | return c; 220 | } 221 | 222 | Token Tokenizer::parse_char(DebugInfo dbi) 223 | { 224 | char c = next_escaped_char(); 225 | next_char(); // skip "'" 226 | return Token { string(&c, 1), TokenType::Char, dbi }; 227 | } 228 | 229 | Token Tokenizer::parse_string(DebugInfo dbi) 230 | { 231 | string buffer = ""; 232 | char c; 233 | while ((c = next_escaped_char()) != '"') 234 | buffer += c; 235 | return Token { buffer, TokenType::String, dbi }; 236 | } 237 | 238 | // Go to the next non whitespace char 239 | void Tokenizer::skip_whitespace() 240 | { 241 | char c; 242 | while (isspace(c = next_char()) && !is_eof()) 243 | continue; 244 | back_buffer.push_back(c); 245 | } 246 | 247 | // Fetch the next char to be parsed 248 | char Tokenizer::next_char() 249 | { 250 | char c = '\0'; 251 | if (back_buffer.size() > 0) 252 | { 253 | c = back_buffer.back(); 254 | back_buffer.pop_back(); 255 | return c; 256 | } 257 | 258 | file >> std::noskipws >> c; 259 | debug_info.char_no++; 260 | debug_info.file_pos++; 261 | if (c == '\n') 262 | { 263 | debug_info.line_no++; 264 | debug_info.char_no = 0; 265 | } 266 | return c; 267 | } 268 | 269 | // Return if the end of the stream as been reached 270 | bool Tokenizer::is_eof() const 271 | { 272 | return file.eof() || eof_override; 273 | } 274 | 275 | void Tokenizer::set_pos(const DebugInfo &pos) 276 | { 277 | file.close(); 278 | file = std::ifstream(file_path); 279 | 280 | debug_info = pos; 281 | file.seekg(pos.file_pos - 1); 282 | back_buffer.clear(); 283 | } 284 | -------------------------------------------------------------------------------- /flags.h: -------------------------------------------------------------------------------- 1 | #ifndef FLAG_H 2 | #define FLAG_H 3 | 4 | // Debug output setting 5 | #define DEBUG_COMPILER 0 6 | #define DEBUG_VM 0 7 | #define DEBUG_LINK 0 8 | #define DEBUG_STACK 0 9 | #define DEBUG_ASSEMBLY 0 10 | 11 | // Enabled arcitectures 12 | #define ARC_C 1 13 | 14 | // VM settings 15 | #define STACK_MEMORY 1024 * 1024 // 1mb 16 | 17 | #endif // FLAG_H 18 | -------------------------------------------------------------------------------- /std/io.tiny: -------------------------------------------------------------------------------- 1 | 2 | extern log(int) 3 | extern log(float) 4 | extern log(char) 5 | extern log(bool) 6 | extern log(int ref, int) 7 | extern log(float ref, int) 8 | extern log(char ref, int) 9 | extern log(bool ref, int) 10 | 11 | # Auto ref 12 | func log(auto obj) 13 | log(ref obj, arraysize obj) 14 | -------------------------------------------------------------------------------- /syntax.tiny: -------------------------------------------------------------------------------- 1 | # Import an external module 2 | import io 3 | 4 | # All prim datatypes 5 | let i as int 6 | let s as short 7 | let b as byte 8 | let f as float 9 | let f as int array[10] 10 | 11 | # All builtin class utils 12 | let str as string 13 | let lst as list of int 14 | let bx as box of int 15 | let f as file 16 | 17 | # Create a new class 18 | class Cat 19 | { 20 | # Give it attribs 21 | public readonly name as string 22 | public weight as int 23 | 24 | # Define the constructor 25 | constructor(string name, int weight) 26 | { 27 | self.name = name 28 | self.weight = weight 29 | } 30 | 31 | # Define a method 32 | public method print() 33 | { 34 | io.log("Cat") 35 | io.log("Name: ", name) 36 | io.log("Weight: ", weight) 37 | } 38 | } 39 | 40 | # Template class 41 | class Template of T 42 | { 43 | x as T array[10] 44 | index as int 45 | } 46 | 47 | # Defining return types 48 | func add(int a, int b) -> int 49 | { 50 | return a + b 51 | } 52 | 53 | func set_cat_weight(Cat ref cat) 54 | { 55 | cat.weight = 69 56 | } 57 | 58 | # Main function is where things start 59 | func main() 60 | { 61 | # Let will make a new variable and auto it's type 62 | let x = "testing" 63 | io.log(x) 64 | 65 | # Create a new object 66 | cat = new Cat("Bob", 21) 67 | set_cat_weight(ref cat) 68 | cat.print() 69 | } 70 | -------------------------------------------------------------------------------- /vm/include/bytecode.h: -------------------------------------------------------------------------------- 1 | #ifndef BYTECODE_H 2 | #define BYTECODE_H 3 | 4 | #define BC_OPERATION_SET(GEN, types) \ 5 | GEN(BC_ADD_##types, 0) \ 6 | GEN(BC_SUB_##types, 0) \ 7 | GEN(BC_MUL_##types, 0) \ 8 | GEN(BC_DIV_##types, 0) \ 9 | GEN(BC_MORE_THAN_##types, 0) \ 10 | GEN(BC_LESS_THAN_##types, 0) \ 11 | GEN(BC_MORE_THAN_EQUALS_##types, 0) \ 12 | GEN(BC_LESS_THAN_EQUALS_##types, 0) \ 13 | GEN(BC_EQUALS_##types, 0) \ 14 | 15 | #define PRIM_CAST_SET(GEN, to) \ 16 | GEN(BC_CAST_INT_##to, 0) \ 17 | GEN(BC_CAST_FLOAT_##to, 0) \ 18 | GEN(BC_CAST_CHAR_##to, 0) \ 19 | GEN(BC_CAST_BOOL_##to, 0) 20 | 21 | #define FOR_EACH_CODE(GEN) \ 22 | GEN(BC_PUSH_1, 1) \ 23 | GEN(BC_PUSH_4, 4) \ 24 | GEN(BC_PUSH_X, -1) \ 25 | GEN(BC_POP, 1) \ 26 | GEN(BC_ALLOC, 1) \ 27 | \ 28 | GEN(BC_STORE_LOCAL_4, 1) \ 29 | GEN(BC_STORE_LOCAL_X, 5) \ 30 | GEN(BC_LOAD_LOCAL_4, 1) \ 31 | GEN(BC_LOAD_LOCAL_X, 5) \ 32 | GEN(BC_LOCAL_REF, 1) \ 33 | GEN(BC_COPY, 1) \ 34 | GEN(BC_GET_ARRAY_INDEX, 8) \ 35 | GEN(BC_GET_ATTR, 12) \ 36 | GEN(BC_ASSIGN_REF_X, 4) \ 37 | \ 38 | GEN(BC_CREATE_FRAME, 4) \ 39 | GEN(BC_CALL, 4) \ 40 | GEN(BC_CALL_EXTERNAL, 4) \ 41 | GEN(BC_RETURN, 2) \ 42 | GEN(BC_JUMP, 4) \ 43 | GEN(BC_JUMP_IF_NOT, 4) \ 44 | \ 45 | /* Operations */ \ 46 | BC_OPERATION_SET(GEN, INT_INT) \ 47 | BC_OPERATION_SET(GEN, INT_FLOAT) \ 48 | BC_OPERATION_SET(GEN, INT_CHAR) \ 49 | BC_OPERATION_SET(GEN, FLOAT_INT) \ 50 | BC_OPERATION_SET(GEN, FLOAT_FLOAT) \ 51 | BC_OPERATION_SET(GEN, FLOAT_CHAR) \ 52 | BC_OPERATION_SET(GEN, CHAR_INT) \ 53 | BC_OPERATION_SET(GEN, CHAR_FLOAT) \ 54 | BC_OPERATION_SET(GEN, CHAR_CHAR) \ 55 | \ 56 | /* Casts */ \ 57 | PRIM_CAST_SET(GEN, INT) \ 58 | PRIM_CAST_SET(GEN, FLOAT) \ 59 | PRIM_CAST_SET(GEN, CHAR) \ 60 | PRIM_CAST_SET(GEN, BOOL) \ 61 | \ 62 | GEN(BC_SIZE, 0) 63 | 64 | #define GENERATE_ENUM(ENUM, size) ENUM, 65 | #define GENERATE_STRING(STRING, size) #STRING, 66 | #define GENERATE_SIZE(STRING, size) size, 67 | 68 | typedef enum Bytecode 69 | { 70 | FOR_EACH_CODE(GENERATE_ENUM) 71 | } Bytecode; 72 | 73 | static const char *bytecode_names[] = 74 | { 75 | FOR_EACH_CODE(GENERATE_STRING) 76 | }; 77 | 78 | static const int bytecode_size[] = 79 | { 80 | FOR_EACH_CODE(GENERATE_SIZE) 81 | }; 82 | 83 | void disassemble(char *code, int size); 84 | 85 | #endif // BYTECODE_H 86 | -------------------------------------------------------------------------------- /vm/include/std.h: -------------------------------------------------------------------------------- 1 | #ifndef STD_H 2 | #define STD_H 3 | 4 | void register_io(); 5 | 6 | static void register_std() 7 | { 8 | register_io(); 9 | } 10 | 11 | #endif // STD_H 12 | -------------------------------------------------------------------------------- /vm/include/vm.h: -------------------------------------------------------------------------------- 1 | #ifndef VM_H 2 | #define VM_H 3 | 4 | typedef struct VMState 5 | { 6 | int pc; 7 | int sp; 8 | int bp; 9 | int depth; 10 | char *stack; 11 | } VMState; 12 | typedef void (*VMFunc)(VMState *state); 13 | 14 | void vm_init(); 15 | void register_external(const char *name, VMFunc func); 16 | int vm_run(char *code, int start, char *return_value); 17 | 18 | #endif // VM_H 19 | -------------------------------------------------------------------------------- /vm/src/bytecode.c: -------------------------------------------------------------------------------- 1 | #include "bytecode.h" 2 | #include 3 | 4 | static int decode_header(char *data) 5 | { 6 | int pc = 0, i, j; 7 | int external_count = data[pc++]; 8 | char name[80]; 9 | 10 | for (i = 0; i < external_count; i++) 11 | { 12 | int id = *(int*)(data + pc); pc += 4; 13 | int name_len = data[pc++]; 14 | for (j = 0; j < name_len; j++) 15 | name[j] = data[pc++]; 16 | name[name_len] = '\0'; 17 | printf("External '%s' with id %i\n", name, id); 18 | } 19 | 20 | return pc; 21 | } 22 | 23 | void disassemble(char *code, int size) 24 | { 25 | int i = decode_header(code); 26 | int j = 0; 27 | int start = i; 28 | 29 | printf("%i / %i\n", start, size); 30 | while (i < size) 31 | { 32 | int bytecode = code[i++]; 33 | int code_size = bytecode_size[bytecode]; 34 | if (code_size == -1) 35 | code_size = code[i++]; 36 | 37 | printf("%i %s (", i - start - 1, bytecode_names[bytecode]); 38 | for (j = 0; j < code_size; j++) 39 | printf("%i%s", code[i++], j == code_size-1 ? "" : ", "); 40 | printf(")\n"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /vm/src/io.c: -------------------------------------------------------------------------------- 1 | #include "std.h" 2 | #include "vm.h" 3 | #include 4 | 5 | static void log_int(VMState *state) 6 | { 7 | int i = *(int*)(state->stack + state->sp - 4); 8 | printf("%i\n", i); 9 | state->sp -= 4; 10 | } 11 | 12 | static void log_float(VMState *state) 13 | { 14 | float f = *(float*)(state->stack + state->sp - 4); 15 | printf("%.06g\n", f); 16 | state->sp -= 4; 17 | } 18 | 19 | static void log_char(VMState *state) 20 | { 21 | printf("%c\n", state->stack[--state->sp]); 22 | } 23 | 24 | static void log_bool(VMState *state) 25 | { 26 | printf("%s\n", state->stack[--state->sp] ? "true" : "false"); 27 | } 28 | 29 | static void log_raw_string(VMState *state) 30 | { 31 | int ref = *(char*)(state->stack + state->sp - 4); 32 | int len = *(char*)(state->stack + state->sp - 8); 33 | printf("%s\n", state->stack + ref); 34 | state->sp -= 8; 35 | } 36 | 37 | #define LOG_ARRAY_FUNC(name, type, printfunc) \ 38 | static void name(VMState *state) \ 39 | { \ 40 | int ref = *(char*)(state->stack + state->sp - 4); \ 41 | int len = *(char*)(state->stack + state->sp - 8); \ 42 | printf("["); \ 43 | for (int i = 0; i < len; i++) \ 44 | { \ 45 | printf(printfunc "%s", *(type*)(state->stack + ref + i * sizeof(type)), \ 46 | i == len-1 ? "" : ", "); \ 47 | } \ 48 | printf("]\n"); \ 49 | state->sp -= 8; \ 50 | } 51 | 52 | LOG_ARRAY_FUNC(log_int_array, int, "%i") 53 | LOG_ARRAY_FUNC(log_float_array, float, "%.06g") 54 | LOG_ARRAY_FUNC(log_bool_array, char, "%i") 55 | 56 | 57 | void register_io() 58 | { 59 | register_external("io.log(int)", log_int); 60 | register_external("io.log(float)", log_float); 61 | register_external("io.log(char)", log_char); 62 | register_external("io.log(bool)", log_bool); 63 | register_external("io.log(int ref, int)", log_int_array); 64 | register_external("io.log(float ref, int)", log_float_array); 65 | register_external("io.log(char ref, int)", log_raw_string); 66 | register_external("io.log(bool ref, int)", log_bool_array); 67 | } 68 | -------------------------------------------------------------------------------- /vm/src/vm.c: -------------------------------------------------------------------------------- 1 | #include "vm.h" 2 | #include "bytecode.h" 3 | #include "flags.h" 4 | #include 5 | #include 6 | #include 7 | 8 | #if DEBUG_VM 9 | #define LOG(...) printf(__VA_ARGS__) 10 | #else 11 | #define LOG(...) ; 12 | #endif 13 | 14 | typedef struct VMExternal 15 | { 16 | char name[80]; 17 | VMFunc func; 18 | } VMExternal; 19 | 20 | typedef struct VMLink 21 | { 22 | int id; 23 | VMFunc func; 24 | } VMLink; 25 | 26 | static VMExternal exernals[80]; 27 | static int external_size = 0; 28 | 29 | void vm_init() 30 | { 31 | external_size = 0; 32 | } 33 | 34 | void register_external(const char *name, VMFunc func) 35 | { 36 | strcpy(exernals[external_size].name, name); 37 | exernals[external_size].func = func; 38 | external_size += 1; 39 | } 40 | 41 | #define BYTE code[s.pc] 42 | #define NBYTE code[s.pc++] 43 | #define INT *(int*)(code + s.pc) 44 | 45 | #define MAX(a, b) (a) > (b) ? (a) : (b) 46 | 47 | #define OPERATION(ltype, op, rtype, restype) \ 48 | { \ 49 | LOG("%s %s %s = %s\n", #ltype, #op, #rtype, #restype); \ 50 | ltype left = *(ltype*)(s.stack + s.sp - sizeof(ltype) - sizeof(rtype)); \ 51 | rtype right = *(rtype*)(s.stack + s.sp - sizeof(rtype)); \ 52 | restype res = left op right; \ 53 | memcpy(s.stack + s.sp - sizeof(ltype) - sizeof(rtype), &res, sizeof(restype)); \ 54 | s.sp += sizeof(restype) - sizeof(ltype) - sizeof(rtype); \ 55 | break; \ 56 | } 57 | 58 | #define LOGIC_SET(types, left, right) \ 59 | case BC_MORE_THAN_##types: OPERATION(left, >, right, char); \ 60 | case BC_LESS_THAN_##types: OPERATION(left, <, right, char); \ 61 | case BC_MORE_THAN_EQUALS_##types: OPERATION(left, >=, right, char); \ 62 | case BC_LESS_THAN_EQUALS_##types: OPERATION(left, <=, right, char); \ 63 | case BC_EQUALS_##types: OPERATION(left, ==, right, char); 64 | 65 | #define OPERATION_SET(types, left, right, res) \ 66 | case BC_ADD_##types: OPERATION(left, +, right, res); \ 67 | case BC_SUB_##types: OPERATION(left, -, right, res); \ 68 | case BC_MUL_##types: OPERATION(left, *, right, res); \ 69 | case BC_DIV_##types: OPERATION(left, /, right, res); \ 70 | LOGIC_SET(types, left, right) 71 | 72 | #define CAST(name, from, to) \ 73 | case BC_CAST_##name: \ 74 | LOG("Cast from %s to %s\n", #from, #to); \ 75 | *(to*)(s.stack + s.sp - sizeof(from)) = (to)*(from*)(s.stack + s.sp - sizeof(from)); \ 76 | s.sp -= MAX(sizeof(from) - sizeof(to), 0); \ 77 | break; \ 78 | 79 | #define CAST_SET(name, to) \ 80 | CAST(INT_##name, int, to) \ 81 | CAST(FLOAT_##name, float, to) \ 82 | CAST(CHAR_##name, char, to) \ 83 | CAST(BOOL_##name, char, to) \ 84 | 85 | static int decode_header(char *data, VMLink **links, int *link_size) 86 | { 87 | int pc = 0, i, j; 88 | int external_count = data[pc++]; 89 | char name[80]; 90 | 91 | *link_size = external_count; 92 | *links = malloc(external_count * sizeof(VMLink)); 93 | 94 | for (i = 0; i < external_count; i++) 95 | { 96 | VMLink link; 97 | 98 | link.id = *(int*)(data + pc); pc += 4; 99 | int name_len = data[pc++]; 100 | for (j = 0; j < name_len; j++) 101 | name[j] = data[pc++]; 102 | name[name_len] = '\0'; 103 | LOG("Searching for external '%s' with id %i", name, link.id); 104 | 105 | int found = 0; 106 | for (j = 0; j < external_size; j++) 107 | { 108 | if (!strcmp(exernals[j].name, name)) 109 | { 110 | link.func = exernals[j].func; 111 | found = 1; 112 | break; 113 | } 114 | } 115 | 116 | (*links)[i] = link; 117 | if (!found) 118 | { 119 | LOG("\nError: Could not find external '%s'\n", name); 120 | } 121 | else 122 | { 123 | LOG(" [Found]\n"); 124 | } 125 | } 126 | 127 | return pc; 128 | } 129 | 130 | int vm_run(char *data, int start, char *return_value) 131 | { 132 | 133 | VMState s; 134 | s.pc = start; 135 | s.sp = 0; 136 | s.bp = 0; 137 | s.depth = 0; 138 | s.stack = (char*)malloc(STACK_MEMORY); 139 | 140 | VMLink *links; 141 | int link_size = 0; 142 | int code_start = decode_header(data, &links, &link_size); 143 | char *code = data + code_start; 144 | 145 | int running = 1; 146 | while (running) 147 | { 148 | LOG("%i: ", s.pc); 149 | 150 | switch(code[s.pc++]) 151 | { 152 | case BC_PUSH_1: 153 | LOG("push 1b %i\n", BYTE); 154 | s.stack[s.sp++] = code[s.pc++]; 155 | break; 156 | 157 | case BC_PUSH_4: 158 | LOG("push 4b %i\n", INT); 159 | memcpy(s.stack + s.sp, code + s.pc, 4); 160 | s.sp += 4; s.pc += 4; 161 | break; 162 | 163 | case BC_PUSH_X: 164 | LOG("push %ib\n", BYTE); 165 | memcpy(s.stack + s.sp, code + s.pc + 1, BYTE); 166 | s.sp += BYTE; s.pc += NBYTE; 167 | break; 168 | 169 | case BC_POP: 170 | LOG("pop %ib\n", BYTE); 171 | s.sp -= code[s.pc++]; 172 | break; 173 | 174 | case BC_ALLOC: 175 | LOG("alloc %ib\n", BYTE); 176 | s.sp += code[s.pc++]; 177 | break; 178 | 179 | case BC_STORE_LOCAL_4: 180 | LOG("store 4b at %i\n", BYTE); 181 | memcpy(s.stack + s.bp + NBYTE, s.stack + s.sp - 4, 4); s.sp -= 4; 182 | break; 183 | 184 | case BC_STORE_LOCAL_X: 185 | { 186 | LOG("store %ib at %i\n", INT, code[s.pc + 4]); 187 | int size = INT; s.pc += 4; 188 | memcpy(s.stack + s.bp + NBYTE, s.stack + s.sp - size, size); s.sp -= size; 189 | break; 190 | } 191 | 192 | case BC_LOAD_LOCAL_4: 193 | LOG("load 4b at %i\n", BYTE); 194 | memcpy(s.stack + s.sp, s.stack + s.bp + NBYTE, 4); s.sp += 4; 195 | break; 196 | 197 | case BC_LOAD_LOCAL_X: 198 | { 199 | LOG("load %ib at %i\n", INT, code[s.pc + 4]); 200 | int size = INT; s.pc += 4; 201 | memcpy(s.stack + s.sp, s.stack + s.bp + NBYTE, size); s.sp += size; 202 | break; 203 | } 204 | 205 | case BC_LOCAL_REF: 206 | { 207 | LOG("return ref of local at %ib\n", BYTE); 208 | int loc = s.bp + NBYTE; 209 | memcpy(s.stack + s.sp, &loc, 4); s.sp += 4; 210 | break; 211 | } 212 | 213 | case BC_COPY: 214 | { 215 | LOG("copy %ib\n", BYTE); 216 | int loc = *(int*)(s.stack + s.sp - 4); s.sp -= 4; 217 | memcpy(s.stack + s.sp, s.stack + loc, BYTE); 218 | s.sp += NBYTE; 219 | break; 220 | } 221 | 222 | case BC_GET_ARRAY_INDEX: 223 | { 224 | LOG("Get array (size %ib, element size %ib) element at index\n", INT, *(int*)(code + s.pc + 4)); 225 | int array_size = INT; s.pc += 4; 226 | int element_size = INT; s.pc += 4; 227 | int index = *(int*)(s.stack + s.sp - 4); s.sp -= 4; 228 | memcpy(s.stack + s.sp - array_size, 229 | s.stack + s.sp - array_size + element_size * index, 230 | element_size); 231 | s.sp -= array_size - element_size; 232 | break; 233 | } 234 | 235 | case BC_GET_ATTR: 236 | { 237 | LOG("Get attr at %i of size %ib (type size %ib)\n", INT, *(int*)(code + s.pc + 4), *(int*)(code + s.pc + 8)); 238 | int offset = INT; s.pc += 4; 239 | int size = INT; s.pc += 4; 240 | int type_size = INT; s.pc += 4; 241 | memcpy(s.stack + s.sp - type_size, s.stack + s.sp - type_size + offset, size); 242 | s.sp -= type_size - size; 243 | break; 244 | } 245 | 246 | case BC_ASSIGN_REF_X: 247 | { 248 | LOG("Assign ref %ib\n", INT); 249 | int loc = *(int*)(s.stack + s.sp - 4); s.sp -= 4; 250 | memcpy(s.stack + loc, s.stack + s.sp - INT, INT); s.sp -= INT; 251 | s.pc += 4; 252 | break; 253 | } 254 | 255 | case BC_CREATE_FRAME: 256 | LOG("create stack frame of size %i\n", INT); 257 | memcpy(s.stack + s.sp, &s.bp, 4); s.sp += 4; 258 | s.bp = s.sp; 259 | s.sp += INT; 260 | s.pc += 4; 261 | break; 262 | 263 | case BC_CALL: 264 | LOG("call function at %i\n", INT); 265 | memcpy(s.stack + s.sp, &s.pc, 4); s.sp += 4; 266 | s.pc = INT; 267 | s.depth++; 268 | break; 269 | 270 | case BC_CALL_EXTERNAL: 271 | { 272 | LOG("call external function %i\n", INT); 273 | int i; 274 | for (i = 0; i < link_size; i++) 275 | { 276 | if (links[i].id == INT) 277 | { 278 | links[i].func(&s); 279 | break; 280 | } 281 | } 282 | s.pc += 4; 283 | break; 284 | } 285 | 286 | case BC_RETURN: 287 | { 288 | LOG("Return size %i with arg size %i\n", BYTE, code[s.pc+1]); 289 | int return_size = NBYTE; 290 | int arg_size = NBYTE; 291 | if (s.depth <= 0) 292 | { 293 | memcpy(return_value, s.stack + s.sp - return_size, return_size); 294 | running = 0; 295 | break; 296 | } 297 | 298 | // Copy return value into correct slot 299 | memcpy(s.stack + s.bp - 8 - arg_size - return_size, 300 | s.stack + s.sp - return_size, return_size); 301 | s.sp -= return_size; 302 | 303 | s.sp = s.bp; 304 | memcpy(&s.bp, s.stack + s.sp - 4, 4); s.sp -= 4; 305 | memcpy(&s.pc, s.stack + s.sp - 4, 4); s.sp -= 4; 306 | s.sp -= arg_size; 307 | s.pc += 4; 308 | s.depth--; 309 | break; 310 | } 311 | 312 | case BC_JUMP: 313 | LOG("Jump to %i\n", INT); 314 | s.pc = INT; 315 | break; 316 | 317 | case BC_JUMP_IF_NOT: 318 | LOG("Jump if not %s to %i\n", s.stack[s.sp-1] ? "true" : "false", INT); 319 | if (!s.stack[--s.sp]) 320 | s.pc = INT; 321 | else 322 | s.pc += 4; 323 | break; 324 | 325 | OPERATION_SET(INT_INT, int, int, int) 326 | OPERATION_SET(INT_FLOAT, int, float, float) 327 | OPERATION_SET(INT_CHAR, int, char, int) 328 | OPERATION_SET(FLOAT_INT, float, int, float) 329 | OPERATION_SET(FLOAT_FLOAT, float, float, float) 330 | OPERATION_SET(FLOAT_CHAR, float, char, float) 331 | OPERATION_SET(CHAR_INT, char, int, int) 332 | OPERATION_SET(CHAR_FLOAT, char, float, float) 333 | OPERATION_SET(CHAR_CHAR, char, char, char) 334 | CAST_SET(INT, int) 335 | CAST_SET(FLOAT, float) 336 | CAST_SET(CHAR, char) 337 | CAST_SET(BOOL, char) 338 | 339 | default: 340 | printf("Error: Unkown bytecode %i\n", code[s.pc-1]); 341 | running = 0; 342 | } 343 | 344 | #if DEBUG_STACK 345 | int i; 346 | printf("Stack: "); 347 | for (i = 0; i < s.sp; i++) 348 | printf("%i ", s.stack[i]); 349 | printf("\n"); 350 | #endif 351 | } 352 | 353 | free(s.stack); 354 | free(links); 355 | return 0; 356 | } 357 | --------------------------------------------------------------------------------