├── LICENSE ├── README.md ├── examples └── binary_search.txt ├── src ├── Source.cpp ├── builtins.h ├── collection.h ├── errors.h ├── garbage.cpp ├── garbage.h ├── hash.cpp ├── hash.h ├── io.cpp ├── io.h ├── lexer.cpp ├── lexer.h ├── linq.cpp ├── linq.h ├── math.cpp ├── math.h ├── objects.cpp ├── operators.cpp ├── operators.h ├── references.cpp ├── references.h ├── runtime.cpp ├── runtime.h ├── stdlib.cpp ├── structure.h ├── tokens.cpp ├── tokens.h ├── types.cpp ├── types.h ├── value.cpp ├── value.h ├── variables.cpp └── variables.h └── stl ├── char.txt ├── csv.txt ├── fcon.txt ├── list.txt ├── map.txt ├── pattern.txt ├── set.txt ├── strlib.txt └── windows.txt /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fastcode 2 | A perfect blend of C, Java, and Python tailored for those who desire a simple yet powerful programming language. FastCode is a procedural/structural, dynamically typed, and iterpreted programming language. 3 | 4 | # Some Example Code 5 | 6 | 1. Linked List 7 | ``` 8 | struct node { 9 | value 10 | next 11 | } 12 | struct list { 13 | head 14 | } 15 | proc push(list, value) { 16 | newhead = new node 17 | newhead.value = value 18 | newhead.next = list.head 19 | list.head = newhead 20 | } 21 | list = new list 22 | i = 10 23 | while i-- => push(list, i) 24 | ``` 25 | 26 | 2. Fizz Buzz (0-99) 27 | ``` 28 | i = 100 29 | while i-- { 30 | print(i) 31 | if i % 5 == 0{ 32 | print("fizz") 33 | } 34 | if i % 3 == 0 { 35 | print("buzz") 36 | } 37 | printl() 38 | } 39 | ``` 40 | or alternativley for those of use who like one-liners. 41 | ``` 42 | i = 100 43 | while i-- => if i % 15 == 0 => printl(i, "fizzbuzz") elif i % 5 == 0 => printl(i, "fizz") elif i % 3 == 0 => printl(i, "buzz") else => printl(i) 44 | ``` 45 | 3. Fibonacci 46 | ``` 47 | proc fib(n) { 48 | if n < 2 { 49 | return n 50 | } 51 | return fib(n - 1) + fib(n - 2) 52 | } 53 | ``` 54 | again as a single line, 55 | ``` 56 | proc fib(n) => if n < 2 => return n else => return fib(n - 1) + fib(n - 2) 57 | ``` 58 | 59 | # The Philosophy and Rationale 60 | FastCode is meant to be a simple-to-use, portable, and fast programming language that only uses a minimal amount of computing resources. It's best suited for usage as an extension language as well as a general purpose language. FastCode grabs the best features of Python, Java, and C and leaves the worst out while adhering to to it's philosophy. 61 | ### Simplified External Dependencies 62 | With any moderatly or large program in C or C++, one has wrestle with complex header-body declerations and all the other messy items (such as headerguards, pragma onces, etc...) that it often entails. These niche dependency systems are hard to learn, bog down the development process, and that often aren't very useful beyond the realm of c and c++. FastCode automatically handles all aspects of including external dependencies so you can focus purley on writing your code. 63 | ### No Undefined Behavior 64 | Theres no undefined behavior - when a runtime error occurs, your program is immediatly halted. 65 | ### Variable Access Protection 66 | Often times (in C and C++), varaibles that aren't used globally are allowed to be accessed as globally (all throught the program), which often cluters the identifier namespace and provides a myriad of naming, accessibilty issues. In FastCode, each varaible (aside from global variables) cannot be accessed outside of the call frame it was declared in, and will be deleted once that call frame is finished. 67 | ### Minimal Verbosity 68 | For small programs or snippets, extreme verbosity is cumbersome and often self defeating since small snippets of code doesn't need to document it's functionality. And frankly, if you don't have an IDE that does extreme auto-suggest/complete you're going to waste more time typing. This is why FastCode is a superb extension language, especially if you only need to do some minor scripting within your application. 69 | ### Small Project Sizes 70 | With C# or Java your soloution would have a dozen different, often reduntant and unnecessary configuration files. With FastCode, you do not need to load a million different configuration files with your project. This is a waste of resources especially when it comes to small programs/snippets. FastCode on the other hand doesn't compell you to use unnecessary configuration files. 71 | ### Streamlined Syntax 72 | Python's syntax, albeit simple, relies heavily on indentation and is inconsistent (see the great schism of python 2.7). People who learn python often have lot's of trouble acclimating to other programming languages because of python's unique yet niche syntax. FastCode is not only a good first step for beginners, programmers expeiened in other languages can easily read and understand it. 73 | ### Read-Execute-Print-Loop 74 | Since FastCode is based off of the read-execute-print-loop model, one can dynamically execute/debug, unlike C++, C, or Java. 75 | ### Interopability and Portability 76 | Since FastCode is written in C++, it can be easily be used in your C/C++ project and it can easily be ported to any machine, so long as you have the correct toolchain. 77 | -------------------------------------------------------------------------------- /examples/binary_search.txt: -------------------------------------------------------------------------------- 1 | struct node { 2 | left 3 | right 4 | value 5 | } 6 | struct tree { 7 | head 8 | } 9 | proc insert(tree, value) { 10 | parent = null 11 | current = tree.head 12 | while current != null { 13 | parent = current 14 | if value > current.value { 15 | current = ref current.right 16 | } 17 | else { 18 | current = ref current.left 19 | } 20 | } 21 | if parent == null { 22 | tree.head = new node 23 | tree.head.value = value 24 | } 25 | else { 26 | if value > parent.value { 27 | parent.right = new node 28 | parent.right.value = value 29 | } 30 | else { 31 | parent.left = new node 32 | parent.left.value = value 33 | } 34 | } 35 | } 36 | i = 10 37 | tree = new tree 38 | while i-- => insert(tree, i) -------------------------------------------------------------------------------- /src/Source.cpp: -------------------------------------------------------------------------------- 1 | #include "runtime.h" 2 | #include "builtins.h" 3 | #include 4 | #include 5 | 6 | //standard libraries 7 | #include "math.h" 8 | 9 | using namespace fastcode; 10 | 11 | bool stop = false; 12 | 13 | runtime::reference_apartment* quit_repl(const std::vector arguments, runtime::garbage_collector* gc) { 14 | stop = true; 15 | return gc->new_apartment(new value(VALUE_TYPE_NULL, nullptr)); 16 | } 17 | 18 | runtime::reference_apartment* get_help(const std::vector arguments, runtime::garbage_collector* gc) { 19 | std::cout << "Welcome to FastCode!\n\n\tIf this is your first time using FastCode, we urge you to read the documentation at https://github.com/TheRealMichaelWang/fastcode/wiki, or at least check out the section labled ,A Quick Guide, before reading the entirety of this document."<new_apartment(new value(VALUE_TYPE_CHAR, new char(' '))); 21 | } 22 | 23 | unsigned int code_checksum(const char* code) { 24 | int brackets = 0; 25 | int braces = 0; 26 | int params = 0; 27 | for (unsigned int i = 0; i < std::strlen(code); i++) 28 | { 29 | switch (code[i]) { 30 | case '{': braces++; break; 31 | case '}': braces--; break; 32 | case '[': brackets++; break; 33 | case ']': brackets--; break; 34 | case '(': params++; break; 35 | case ')': params--; break; 36 | } 37 | } 38 | return std::abs(braces) + std::abs(brackets) + std::abs(params); 39 | } 40 | 41 | char* str_append(char* buf, char* toappend) { 42 | char* newbuf = new char[strlen(buf) + strlen(toappend) + 1]; 43 | for (unsigned int i = 0; i < strlen(buf); i++) 44 | newbuf[i] = buf[i]; 45 | for (unsigned int i = 0; i < strlen(toappend); i++) 46 | newbuf[i + strlen(buf)] = toappend[i]; 47 | newbuf[strlen(buf) + strlen(toappend)] = 0; 48 | delete[] buf; 49 | return newbuf; 50 | } 51 | 52 | inline bool has_flag(unsigned int argc, char** argv, const char* flag) { 53 | for (unsigned int i = 0; i < argc; i++) 54 | if (strcmp(argv[i], flag) == 0) 55 | return true; 56 | return false; 57 | } 58 | 59 | int main(unsigned int argc, char** argv) { 60 | const char* working_dir = argv[0]; 61 | runtime::interpreter interpreter(has_flag(argc, argv, "-gc")); 62 | 63 | interpreter.new_constant("pi@math", new value(VALUE_TYPE_NUMERICAL, new long double(3.1415926))); 64 | interpreter.new_constant("e@math", new value(VALUE_TYPE_NUMERICAL, new long double(2.71828182))); 65 | 66 | interpreter.import_func("quit", quit_repl); 67 | interpreter.import_func("help", get_help); 68 | 69 | interpreter.import_func("abs", builtins::math::numabs); 70 | interpreter.import_func("random", builtins::math::random); 71 | interpreter.import_func("sin@math", builtins::math::sin); 72 | interpreter.import_func("cos@math", builtins::math::cos); 73 | interpreter.import_func("tan@math", builtins::math::tan); 74 | interpreter.import_func("asin@math", builtins::math::asin); 75 | interpreter.import_func("acos@math", builtins::math::acos); 76 | interpreter.import_func("atan@math", builtins::math::atan); 77 | interpreter.import_func("log@math", builtins::math::log); 78 | 79 | if (argc > 1) { 80 | std::ifstream infile(argv[1], std::ifstream::binary); 81 | if (!infile.is_open()) { 82 | std::cout << "Cannot open source file \"" << argv[1] << "\"."; 83 | return 1; 84 | } 85 | infile.seekg(0, std::ios::end); 86 | unsigned int buffer_length = infile.tellg(); 87 | infile.seekg(0, std::ios::beg); 88 | char* buffer = new char[buffer_length + 1]; 89 | infile.read(buffer, buffer_length); 90 | infile.close(); 91 | buffer[buffer_length] = '\0'; 92 | long double exit_code = interpreter.run(buffer, false); 93 | delete[] buffer; 94 | if (exit_code != 0) 95 | return (int)exit_code; 96 | } 97 | else { 98 | std::cout << "FastCode [Version 2.0, written and designed by Michael Wang]" << std::endl << "Type \"quit()\" to exit the REPL, type \"help()\" for help. " << std::endl << "For more information or documentation, go to https://github.com/TheRealMichaelWang/fastcode/wiki." << std::endl; 99 | 100 | while (!stop) 101 | { 102 | std::cout << std::endl; 103 | char* buf = nullptr; 104 | while (true) 105 | { 106 | std::cout << ">>> "; 107 | char* line = new char[250]; 108 | std::cin.getline(line, 250); 109 | unsigned int line_size = (unsigned int)strlen(line); 110 | line[line_size++] = '\n'; 111 | line[line_size] = 0; 112 | if (buf == nullptr) 113 | buf = line; 114 | else { 115 | buf = str_append(buf, line); 116 | delete[] line; 117 | } 118 | if (code_checksum(buf) == 0) 119 | break; 120 | } 121 | interpreter.run(buf, true); 122 | delete[] buf; 123 | } 124 | } 125 | return 0; 126 | } -------------------------------------------------------------------------------- /src/builtins.h: -------------------------------------------------------------------------------- 1 | #ifndef BUILTINS_H 2 | #define BUILTINS_H 3 | 4 | #include 5 | #include "errors.h" 6 | #include "value.h" 7 | #include "references.h" 8 | #include "collection.h" 9 | #include "garbage.h" 10 | 11 | namespace fastcode { 12 | namespace builtins { 13 | typedef runtime::reference_apartment* (*built_in_function)(const std::vector arguments, runtime::garbage_collector* gc); 14 | 15 | inline void match_arg_len(const std::vector arguments, unsigned int expected_size) { 16 | if (arguments.size() != expected_size) 17 | throw ERROR_UNEXPECTED_ARGUMENT_SIZE; 18 | } 19 | 20 | inline void match_arg_type(value* value, char expected_type) { 21 | if (value->type != expected_type) 22 | throw ERROR_INVALID_VALUE_TYPE; 23 | } 24 | 25 | inline char* to_c_str(value* value) { 26 | match_arg_type(value, VALUE_TYPE_COLLECTION); 27 | runtime::collection* collection = (class runtime::collection*)value->ptr; 28 | char* c = new char[collection->size + 1]; 29 | for (unsigned int i = 0; i < collection->size; i++) 30 | { 31 | match_arg_type(collection->get_value(i), VALUE_TYPE_CHAR); 32 | c[i] = *collection->get_value(i)->get_char(); 33 | } 34 | c[collection->size] = 0; 35 | return c; 36 | } 37 | 38 | inline runtime::collection* from_c_str(const char* str, runtime::garbage_collector* gc) { 39 | runtime::collection* str_col = new runtime::collection((unsigned long)strlen(str), gc); 40 | for (unsigned long i = 0; i < str_col->size; i++) 41 | { 42 | str_col->set_value(i, new value(VALUE_TYPE_CHAR, new char(str[i]))); 43 | } 44 | return str_col; 45 | } 46 | 47 | runtime::reference_apartment* get_handle(const std::vector arguments, runtime::garbage_collector* gc); 48 | runtime::reference_apartment* set_struct_property(const std::vector arguments, runtime::garbage_collector* gc); 49 | runtime::reference_apartment* abort_program(const std::vector arguments, runtime::garbage_collector* gc); 50 | runtime::reference_apartment* get_hash(const std::vector arguments, runtime::garbage_collector* gc); 51 | } 52 | } 53 | 54 | #endif // !BUILTINS_H 55 | -------------------------------------------------------------------------------- /src/collection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef COLLECTION_H 4 | #define COLLECTION_H 5 | 6 | #include "references.h" 7 | #include "garbage.h" 8 | #include "value.h" 9 | 10 | namespace fastcode { 11 | namespace runtime { 12 | class collection { 13 | private: 14 | reference_apartment* parent_reference; 15 | reference_apartment** inner_collection; 16 | collection(unsigned long size, reference_apartment* parent_reference); 17 | 18 | public: 19 | unsigned long size; 20 | collection(unsigned long size, garbage_collector* gc); 21 | collection(collection* a, collection* b, reference_apartment* parent_reference); 22 | ~collection(); 23 | inline void set_reference(unsigned long index, reference_apartment* reference) { 24 | this->inner_collection[index]->remove_parent_references(parent_reference); 25 | reference->add_parent_references(parent_reference); 26 | this->inner_collection[index] = reference; 27 | } 28 | 29 | inline reference_apartment* get_reference(unsigned long index) { 30 | return this->inner_collection[index]; 31 | } 32 | 33 | inline void set_value(unsigned long index, value* value) { 34 | get_reference(index)->set_value(value); 35 | } 36 | 37 | inline value* get_value(unsigned long index) { 38 | return get_reference(index)->value; 39 | } 40 | 41 | inline reference_apartment** get_children() { 42 | return this->inner_collection; 43 | } 44 | 45 | collection* clone(reference_apartment* parent_reference); 46 | 47 | inline reference_apartment* get_parent_ref() { 48 | return this->parent_reference; 49 | } 50 | 51 | int hash(); 52 | }; 53 | } 54 | } 55 | 56 | #endif // !COLLECTION_H -------------------------------------------------------------------------------- /src/errors.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef ERRORS_H 4 | #define ERRORS_H 5 | 6 | //value errors 7 | #define ERROR_INVALID_VALUE_TYPE 1 8 | #define ERROR_INDEX_OUT_OF_RANGE 2 9 | #define ERROR_PROPERTY_NOT_FOUND 3 10 | #define ERROR_MUST_HAVE_NUM_TYPE 4 11 | #define ERROR_MUST_HAVE_STRUCT_TYPE 5 12 | #define ERROR_MUST_HAVE_COLLECTION_TYPE 6 13 | #define ERROR_MUST_HAVE_CHAR_TYPE 7 14 | 15 | //variable errors 16 | #define ERROR_VARIABLE_ALREADY_DEFINED 10 17 | #define ERROR_VARIABLE_NOT_DEFINED 11 18 | #define ERROR_CANNOT_DEREFERENCE 12 19 | 20 | //identifier errors 21 | #define ERROR_INVALID_ACCESSOR_MODIFIERS 30 22 | 23 | //operator erros 24 | #define ERROR_INVALID_BINARY_OPERATOR 41 25 | #define ERROR_INVALID_unary_OPERATOR 42 26 | 27 | //lexographical errors 28 | #define ERROR_UNEXPECTED_TOKEN 50 29 | #define ERROR_UNEXPECTED_END 51 30 | #define ERROR_UNRECOGNIZED_TOKEN 52 31 | #define ERROR_UNRECOGNIZED_ESCAPE_SEQ 53 32 | 33 | //runtime errors 34 | #define ERROR_OP_NOT_IMPLEMENTED 60 35 | #define ERROR_UNRECOGNIZED_VARIABLE 62 36 | #define ERROR_DIVIDE_BY_ZERO 64 37 | #define ERROR_UNEXPECTED_ARGUMENT_SIZE 65 38 | #define ERROR_UNEXPECTED_BREAK 66 39 | 40 | //prototype erros 41 | #define ERROR_STRUCT_PROTO_ALREADY_DEFINED 70 42 | #define ERROR_FUNCTION_PROTO_ALREADY_DEFINED 71 43 | #define ERROR_STRUCT_PROTO_NOT_DEFINED 72 44 | #define ERROR_FUNCTION_PROTO_NOT_DEFINED 73 45 | 46 | #define ERROR_ABORTED 74 47 | 48 | //import errors 49 | #define ERROR_CANNOT_INCLUDE_FILE 75 50 | 51 | inline const char* get_err_info(int err) { 52 | switch (err) 53 | { 54 | case ERROR_INVALID_VALUE_TYPE: 55 | return "Invalid Value Type"; 56 | case ERROR_INDEX_OUT_OF_RANGE: 57 | return "Index Out of Range"; 58 | case ERROR_PROPERTY_NOT_FOUND: 59 | return "Property Not Found"; 60 | case ERROR_MUST_HAVE_NUM_TYPE: 61 | return "Must Have Numerical Type"; 62 | case ERROR_MUST_HAVE_CHAR_TYPE: 63 | return "Must Have Character Type"; 64 | case ERROR_MUST_HAVE_COLLECTION_TYPE: 65 | return "Must Have Array/Collection Type"; 66 | case ERROR_MUST_HAVE_STRUCT_TYPE: 67 | return "Must Have Structure Type"; 68 | case ERROR_INVALID_ACCESSOR_MODIFIERS: 69 | return "Invalid Accessor Modifiers"; 70 | case ERROR_INVALID_BINARY_OPERATOR: 71 | return "Invalid Binary Operator"; 72 | case ERROR_INVALID_unary_OPERATOR: 73 | return "Invalid Unary Operator"; 74 | case ERROR_UNEXPECTED_TOKEN: 75 | return "Unexpected Token"; 76 | case ERROR_UNEXPECTED_END: 77 | return "Unexpected End"; 78 | case ERROR_UNRECOGNIZED_TOKEN: 79 | return "Unrecognized Token"; 80 | case ERROR_UNRECOGNIZED_ESCAPE_SEQ: 81 | return "Unrecognized Escape Sequence"; 82 | case ERROR_OP_NOT_IMPLEMENTED: 83 | return "Operator Not Implemented"; 84 | case ERROR_UNRECOGNIZED_VARIABLE: 85 | return "Unrecognized Variable"; 86 | case ERROR_DIVIDE_BY_ZERO: 87 | return "Cannot Divide By Zero"; 88 | case ERROR_UNEXPECTED_ARGUMENT_SIZE: 89 | return "Unexpected Argument Size"; 90 | case ERROR_UNEXPECTED_BREAK: 91 | return "Unexpected Break Statment"; 92 | case ERROR_STRUCT_PROTO_ALREADY_DEFINED: 93 | return "Structure Prototype Already Defined"; 94 | case ERROR_STRUCT_PROTO_NOT_DEFINED: 95 | return "Structure Prototype Not Defined"; 96 | case ERROR_FUNCTION_PROTO_ALREADY_DEFINED: 97 | return "Procedure Prototype Already Defined"; 98 | case ERROR_FUNCTION_PROTO_NOT_DEFINED: 99 | return "Procedure Prototype Not Defined"; 100 | case ERROR_ABORTED: 101 | return "The program has been manually aborted."; 102 | case ERROR_CANNOT_INCLUDE_FILE: 103 | return "Cannot Include File"; 104 | default: 105 | return "Unkown Error"; 106 | } 107 | } 108 | 109 | #endif // !ERRORS_H -------------------------------------------------------------------------------- /src/garbage.cpp: -------------------------------------------------------------------------------- 1 | #include "garbage.h" 2 | 3 | namespace fastcode { 4 | namespace runtime { 5 | garbage_collector::garbage_collector() { 6 | this->size = 0; 7 | this->head = nullptr; 8 | this->tail = nullptr; 9 | this->sweep_frames = std::stack(); 10 | } 11 | 12 | garbage_collector::~garbage_collector() { 13 | while (size > 0) 14 | { 15 | sweep(true); 16 | } 17 | } 18 | 19 | reference_apartment* garbage_collector::new_apartment(value* initial_value) { 20 | if (size == 0) { 21 | size++; 22 | return (tail = (head = new reference_apartment(initial_value))); 23 | } 24 | else { 25 | size++; 26 | tail->next_apartment = new reference_apartment(initial_value); 27 | tail = tail->next_apartment; 28 | return tail; 29 | } 30 | } 31 | 32 | unsigned int garbage_collector::sweep(bool pop_frame) { 33 | unsigned int destroyed_values = 0; 34 | reference_apartment* current = sweep_frames.empty() ? head : sweep_frames.top()->next_apartment; 35 | reference_apartment* previous = sweep_frames.empty() ? nullptr : sweep_frames.top(); 36 | while (current != nullptr) 37 | { 38 | if (current->can_delete()) { 39 | reference_apartment* to_delete = current; 40 | current = current->next_apartment; 41 | if (previous == nullptr) 42 | head = current; 43 | else 44 | previous->next_apartment = current; 45 | delete to_delete; 46 | size--; 47 | destroyed_values++; 48 | } 49 | else { 50 | previous = current; 51 | current = current->next_apartment; 52 | } 53 | } 54 | tail = previous; 55 | if (!sweep_frames.empty() && pop_frame) 56 | sweep_frames.pop(); 57 | return destroyed_values; 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/garbage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef GARBAGE_H 4 | #define GARBAGE_H 5 | 6 | #include 7 | #include "references.h" 8 | 9 | namespace fastcode { 10 | namespace runtime { 11 | class garbage_collector { 12 | private: 13 | unsigned int size; 14 | reference_apartment* head; 15 | reference_apartment* tail; 16 | std::stack sweep_frames; 17 | 18 | public: 19 | garbage_collector(); 20 | ~garbage_collector(); 21 | 22 | //initializes a new garbage collection frame 23 | inline void new_frame() { 24 | if (tail != nullptr) 25 | this->sweep_frames.push(tail); 26 | } 27 | 28 | //creates a new variable apartment within the garbage collector 29 | reference_apartment* new_apartment(value* initial_value); 30 | 31 | //de-allocates unused variable appartments within the current garbage collection frame 32 | unsigned int sweep(bool pop_frame); 33 | }; 34 | } 35 | } 36 | 37 | #endif // !GARBAGE_H -------------------------------------------------------------------------------- /src/hash.cpp: -------------------------------------------------------------------------------- 1 | #include "hash.h" 2 | #include 3 | 4 | unsigned long insecure_hash(const char* str){ 5 | unsigned long hash = 5381; 6 | for (int i = std::strlen(str) - 1; i >= 0; i--) 7 | { 8 | hash = ((hash << 5) + hash) + str[i]; 9 | } 10 | return hash; 11 | } -------------------------------------------------------------------------------- /src/hash.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef HASH_H 4 | #define HASH_H 5 | 6 | //don't use to hash sensitive data 7 | unsigned long insecure_hash(const char* str); 8 | 9 | //also an insecure hash 10 | inline int combine_hash(int hash_a, int hash_b) { 11 | return (hash_a << 5) + hash_b; 12 | } 13 | 14 | #endif // !DJ2B_H -------------------------------------------------------------------------------- /src/io.cpp: -------------------------------------------------------------------------------- 1 | #include "errors.h" 2 | #include "structure.h" 3 | #include "collection.h" 4 | #include "tokens.h" 5 | #include "operators.h" 6 | #include "builtins.h" 7 | #include "io.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | namespace fastcode { 14 | inline void print_indent(unsigned int indent) { 15 | for (unsigned int i = 0; i < indent; i++) 16 | std::cout << '\t'; 17 | } 18 | 19 | void print_value(value* val, bool primitive_mode); 20 | 21 | namespace parsing { 22 | void print_top_lvl_tok(token* token, int indent = 0) { 23 | print_indent(indent); 24 | switch (token->type) 25 | { 26 | case TOKEN_INCLUDE: 27 | ((include_token*)token)->print(); 28 | break; 29 | case TOKEN_SET: 30 | ((set_token*)token)->print(); 31 | break; 32 | case TOKEN_FUNCTION_CALL: 33 | ((function_call_token*)token)->print(); 34 | break; 35 | case TOKEN_RETURN: 36 | ((return_token*)token)->print(); 37 | break; 38 | case TOKEN_UNARY_OP: 39 | ((unary_operator_token*)token)->print(); 40 | break; 41 | case TOKEN_IF: 42 | case TOKEN_ELIF: 43 | case TOKEN_ELSE: 44 | case TOKEN_WHILE: 45 | ((conditional_token*)token)->print(indent); 46 | break; 47 | case TOKEN_FOR: 48 | ((for_token*)token)->print(indent); 49 | break; 50 | case TOKEN_BREAK: 51 | std::cout << "break"; 52 | break; 53 | case TOKEN_STRUCT_PROTO: 54 | ((structure_prototype*)token)->print(); 55 | break; 56 | case TOKEN_FUNC_PROTO: 57 | ((function_prototype*)token)->print(); 58 | break; 59 | default: 60 | throw ERROR_UNEXPECTED_TOKEN; 61 | } 62 | } 63 | 64 | void print_value_tok(token* token) { 65 | switch (token->type) 66 | { 67 | case TOKEN_VALUE: 68 | ((value_token*)token)->print(); 69 | break; 70 | case TOKEN_VAR_ACCESS: 71 | ((variable_access_token*)token)->print(); 72 | break; 73 | case TOKEN_FUNCTION_CALL: 74 | ((function_call_token*)token)->print(); 75 | break; 76 | case TOKEN_BINARY_OP: 77 | ((binary_operator_token*)token)->print(); 78 | break; 79 | case TOKEN_UNARY_OP: 80 | ((unary_operator_token*)token)->print(); 81 | break; 82 | case TOKEN_GET_REFERENCE: 83 | ((get_reference_token*)token)->print(); 84 | break; 85 | case TOKEN_CREATE_ARRAY: 86 | ((create_array_token*)token)->print(); 87 | break; 88 | case TOKEN_CREATE_STRUCT: 89 | ((create_struct_token*)token)->print(); 90 | break; 91 | case TOKEN_SET: 92 | ((set_token*)token)->print(); 93 | break; 94 | default: 95 | throw ERROR_UNEXPECTED_TOKEN; 96 | } 97 | } 98 | 99 | void print_token_body(std::list tokens, int indent) { 100 | std::cout << " {" << std::endl; 101 | for (auto i = tokens.begin(); i != tokens.end(); ++i) { 102 | print_top_lvl_tok(*i, indent + 1); 103 | std::cout << std::endl; 104 | } 105 | print_indent(indent); 106 | std::cout << '}'; 107 | } 108 | 109 | void value_token::print() { 110 | print_value(this->inner_value_ptr, false); 111 | } 112 | 113 | void identifier_token::print() { 114 | std::cout << this->id_str_ptr; 115 | } 116 | 117 | void variable_access_token::print() { 118 | for (auto i = modifiers.begin(); i != modifiers.end(); ++i) { 119 | if ((*i)->type == TOKEN_IDENTIFIER) { 120 | if (i != modifiers.begin()) 121 | std::cout << '.'; 122 | identifier_token* id_tok = (identifier_token*)*i; 123 | id_tok->print(); 124 | } 125 | else if ((*i)->type == TOKEN_INDEX) { 126 | index_token* index_tok = (index_token*)*i; 127 | index_tok->print(); 128 | } 129 | else 130 | throw ERROR_UNEXPECTED_TOKEN; 131 | } 132 | } 133 | 134 | void index_token::print() { 135 | std::cout << '['; 136 | print_value_tok(this->value); 137 | std::cout << ']'; 138 | } 139 | 140 | void get_reference_token::print() { 141 | std::cout << "ref "; 142 | this->var_access->print(); 143 | } 144 | 145 | void set_token::print() { 146 | this->destination->print(); 147 | std::cout << " = "; 148 | print_value_tok(this->value); 149 | } 150 | 151 | void function_call_token::print() { 152 | this->identifier->print(); 153 | std::cout << "("; 154 | for (auto i = this->arguments.begin(); i != this->arguments.end(); ++i) { 155 | if (i != this->arguments.begin()) 156 | std::cout << ", "; 157 | print_value_tok(*i); 158 | } 159 | std::cout << ')'; 160 | } 161 | 162 | void return_token::print() { 163 | std::cout << "return "; 164 | print_value_tok(this->value); 165 | } 166 | 167 | void conditional_token::print(int indent) { 168 | switch (this->type) 169 | { 170 | case TOKEN_IF: 171 | std::cout << "if "; 172 | print_value_tok(this->condition); 173 | break; 174 | case TOKEN_ELIF: 175 | std::cout << "elif "; 176 | print_value_tok(this->condition); 177 | break; 178 | case TOKEN_ELSE: 179 | std::cout << "else"; 180 | break; 181 | case TOKEN_WHILE: 182 | std::cout << "while "; 183 | print_value_tok(this->condition); 184 | break; 185 | default: 186 | throw ERROR_UNEXPECTED_TOKEN; 187 | } 188 | print_token_body(this->instructions, indent); 189 | } 190 | 191 | void for_token::print(int indent) { 192 | std::cout << "for "; 193 | this->identifier->print(); 194 | std::cout << " in "; 195 | print_value_tok(this->collection); 196 | print_token_body(this->instructions, indent); 197 | } 198 | 199 | void create_array_token::print() { 200 | std::cout << '['; 201 | for (auto i = this->values.begin(); i != this->values.end(); ++i) { 202 | if (i != this->values.begin()) 203 | std::cout << ", "; 204 | print_value_tok(*i); 205 | } 206 | std::cout << ']'; 207 | } 208 | 209 | void create_struct_token::print() { 210 | std::cout << "new "; 211 | this->identifier->print(); 212 | } 213 | 214 | void function_prototype::print() { 215 | std::cout << "proc "; 216 | this->identifier->print(); 217 | std::cout << "("; 218 | for (auto i = this->argument_identifiers.begin(); i != this->argument_identifiers.end(); ++i) { 219 | if (i != this->argument_identifiers.begin()) 220 | std::cout << ", "; 221 | (*i)->print(); 222 | } 223 | std::cout << ')'; 224 | print_token_body(this->tokens, 0); 225 | } 226 | 227 | void structure_prototype::print() { 228 | std::cout << "struct "; 229 | this->identifier->print(); 230 | std::cout << " {"; 231 | 232 | for (auto i = this->properties.begin(); i != this->properties.end(); ++i) { 233 | std::cout << std::endl << '\t'; 234 | (*i)->print(); 235 | } 236 | 237 | std::cout << std::endl << '}'; 238 | } 239 | 240 | void include_token::print() { 241 | std::cout << "include " << this->file_path; 242 | } 243 | 244 | void binary_operator_token::print() { 245 | print_value_tok(this->left); 246 | switch (this->op) 247 | { 248 | case OP_AND: 249 | std::cout << " and "; 250 | break; 251 | case OP_OR: 252 | std::cout << " or "; 253 | break; 254 | case OP_EQUALS: 255 | std::cout << " == "; 256 | break; 257 | case OP_NOT_EQUAL: 258 | std::cout << " != "; 259 | break; 260 | case OP_LESS: 261 | std::cout << " < "; 262 | break; 263 | case OP_MORE: 264 | std::cout << " > "; 265 | break; 266 | case OP_LESS_EQUAL: 267 | std::cout << " <= "; 268 | break; 269 | case OP_MORE_EQUAL: 270 | std::cout << " >= "; 271 | break; 272 | case OP_ADD: 273 | std::cout << " + "; 274 | break; 275 | case OP_SUBTRACT: 276 | std::cout << " - "; 277 | break; 278 | case OP_MULTIPLY: 279 | std::cout << " * "; 280 | break; 281 | case OP_DIVIDE: 282 | std::cout << " / "; 283 | break; 284 | case OP_MODULOUS: 285 | std::cout << " % "; 286 | break; 287 | case OP_POWER: 288 | std::cout << " ^ "; 289 | break; 290 | default: 291 | throw ERROR_OP_NOT_IMPLEMENTED; 292 | } 293 | print_value_tok(this->right); 294 | } 295 | 296 | void unary_operator_token::print() { 297 | switch (this->op) 298 | { 299 | case OP_NEGATE: 300 | std::cout << '-'; 301 | break; 302 | case OP_INVERT: 303 | std::cout << '!'; 304 | break; 305 | } 306 | print_value_tok(this->value); 307 | switch (this->op) { 308 | case OP_INCRIMENT: 309 | std::cout << "++"; 310 | break; 311 | case OP_DECRIMENT: 312 | std::cout << "--"; 313 | break; 314 | } 315 | } 316 | } 317 | 318 | void handle_syntax_err(int syntax_error, unsigned int pos, const char* source) { 319 | std::cout << std::endl << "***Syntax Error: " << get_err_info(syntax_error) << "***" << std::endl; 320 | std::cout << "Error Code: " << syntax_error << '\t' << "Lexer Index: " << pos << std::endl; 321 | for (unsigned int i = pos >= 20 ? pos - 20 : 0; i < strlen(source) && i < pos + 20; i++) 322 | std::cout << source[i]; 323 | std::cout << "" << std::endl; 324 | } 325 | 326 | void handle_runtime_err(int runtime_error, parsing::token* err_tok) { 327 | std::cout << std::endl << "***Runtime Error: " << get_err_info(runtime_error) << "***" << std::endl; 328 | std::cout << "Error Code: " << runtime_error << '\t' << "Error Tok Type: " << (int)err_tok->type << std::endl << std::endl;; 329 | parsing::print_top_lvl_tok(err_tok); 330 | std::cout << std::endl; 331 | } 332 | 333 | void print_call_stack(std::stack call_stack) { 334 | std::cout << std::endl << "Stack Trace:"; 335 | if (call_stack.empty()) { 336 | std::cout << " Empty"; 337 | return; 338 | } 339 | while (!call_stack.empty()) 340 | { 341 | std::cout << std::endl << "\tin " << call_stack.top()->identifier->get_identifier(); 342 | call_stack.pop(); 343 | } 344 | } 345 | 346 | void xprint(runtime::structure* structure, unsigned int indent) { 347 | //print_indent(indent); 348 | std::cout << '<' << structure->get_identifier()->get_identifier() << '>'; 349 | runtime::reference_apartment** children = structure->get_children(); 350 | std::list props = structure->get_proto()->get_properties(); 351 | auto it = props.begin(); 352 | for (unsigned int i = 0; i < structure->get_size(); i++) 353 | { 354 | std::cout << std::endl; 355 | print_indent(indent + 1); 356 | (*it)->print(); 357 | std::cout << ": "; 358 | ++it; 359 | print_value(children[i]->value, false); 360 | } 361 | } 362 | 363 | void print_array(runtime::collection* collection, bool primitive_mode) { 364 | bool is_str = true; 365 | for (unsigned int i = 0; i < collection->size; i++) 366 | { 367 | if (collection->get_value(i)->type != VALUE_TYPE_CHAR) { 368 | is_str = false; 369 | break; 370 | } 371 | } 372 | if (is_str) { 373 | if (primitive_mode) 374 | std::cout << '\"'; 375 | for (unsigned int i = 0; i < collection->size; i++) 376 | { 377 | std::cout << *collection->get_value(i)->get_char(); 378 | } 379 | if (primitive_mode) 380 | std::cout << '\"'; 381 | } 382 | else { 383 | if (collection->size > 25) { 384 | std::cout << ""; 385 | return; 386 | } 387 | std::cout << '['; 388 | for (unsigned int i = 0; i < collection->size; i++) 389 | { 390 | print_value(collection->get_value(i), false); 391 | if (i != collection->size - 1) 392 | std::cout << ", "; 393 | } 394 | std::cout << ']'; 395 | } 396 | } 397 | 398 | void print_value(value* val, bool primitive_mode) { 399 | switch (val->type) 400 | { 401 | case VALUE_TYPE_NULL: 402 | std::cout << "null"; 403 | break; 404 | case VALUE_TYPE_CHAR: 405 | if (primitive_mode) 406 | std::cout << *val->get_char(); 407 | else 408 | std::cout << '\'' << *val->get_char() << '\''; 409 | break; 410 | case VALUE_TYPE_NUMERICAL: 411 | std::cout << *val->get_numerical(); 412 | break; 413 | case VALUE_TYPE_COLLECTION: 414 | print_array((runtime::collection*)val->ptr, primitive_mode); 415 | break; 416 | case VALUE_TYPE_STRUCT: 417 | if(primitive_mode) 418 | xprint((runtime::structure*)val->ptr, 0); 419 | else 420 | std::cout << '<' << ((runtime::structure*)val->ptr)->get_identifier()->get_identifier() << '>'; 421 | break; 422 | case VALUE_TYPE_HANDLE: 423 | std::cout << "ptr << ">"; 424 | break; 425 | default: 426 | throw ERROR_INVALID_VALUE_TYPE; 427 | } 428 | } 429 | 430 | namespace builtins { 431 | runtime::reference_apartment* print(const std::vector arguments, runtime::garbage_collector* gc) { 432 | for (auto it = arguments.begin(); it != arguments.end(); ++it) { 433 | print_value(*it, true); 434 | } 435 | return gc->new_apartment(new value(VALUE_TYPE_NULL, nullptr)); 436 | } 437 | 438 | runtime::reference_apartment* print_line(const std::vector arguments, runtime::garbage_collector* gc) { 439 | runtime::reference_apartment* appt = print(arguments, gc); 440 | std::cout << std::endl; 441 | return appt; 442 | } 443 | 444 | runtime::reference_apartment* get_input(const std::vector arguments, runtime::garbage_collector* gc) { 445 | char* input = new char[250]; 446 | std::cin.getline(input, 250); 447 | runtime::collection* str = from_c_str(input, gc); 448 | return str->get_parent_ref(); 449 | } 450 | 451 | runtime::reference_apartment* file_read_text(const std::vector arguments, runtime::garbage_collector* gc) { 452 | match_arg_len(arguments, 1); 453 | match_arg_type(arguments[0], VALUE_TYPE_COLLECTION); 454 | 455 | char* file_path = to_c_str(arguments[0]); 456 | 457 | std::ifstream infile(file_path, std::ifstream::binary); 458 | 459 | if (!infile.is_open()) 460 | return gc->new_apartment(new value(VALUE_TYPE_NUMERICAL, new long double(0))); 461 | delete[] file_path; 462 | 463 | infile.seekg(0, std::ios::end); 464 | unsigned long buffer_length = infile.tellg(); 465 | infile.seekg(0, std::ios::beg); 466 | char* buffer = new char[buffer_length + 1]; 467 | infile.read(buffer, buffer_length); 468 | infile.close(); 469 | buffer[buffer_length] = 0; 470 | 471 | runtime::collection* strcol = from_c_str(buffer, gc); 472 | delete[] buffer; 473 | return strcol->get_parent_ref(); 474 | } 475 | 476 | runtime::reference_apartment* file_write_text(const std::vector arguments, runtime::garbage_collector* gc) { 477 | match_arg_len(arguments, 2); 478 | match_arg_type(arguments[0], VALUE_TYPE_COLLECTION); 479 | match_arg_type(arguments[1], VALUE_TYPE_COLLECTION); 480 | 481 | char* file_path = to_c_str(arguments[0]); 482 | 483 | std::ofstream infile(file_path, std::ofstream::binary); 484 | 485 | if (!infile.is_open()) 486 | return gc->new_apartment(new value(VALUE_TYPE_NUMERICAL, new long double(0))); 487 | delete[] file_path; 488 | 489 | char* buffer = to_c_str(arguments[1]); 490 | infile.write(buffer, strlen(buffer)); 491 | infile.close(); 492 | delete[] buffer; 493 | 494 | return gc->new_apartment(new value(VALUE_TYPE_NUMERICAL, new long double(1))); 495 | } 496 | 497 | runtime::reference_apartment* system_call(const std::vector arguments, runtime::garbage_collector* gc) { 498 | match_arg_len(arguments, 1); 499 | match_arg_type(arguments[0], VALUE_TYPE_COLLECTION); 500 | 501 | char* command = to_c_str(arguments[0]); 502 | system(command); 503 | delete[] command; 504 | 505 | return gc->new_apartment(new value(VALUE_TYPE_NULL, nullptr)); 506 | } 507 | } 508 | } -------------------------------------------------------------------------------- /src/io.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef IO_H 4 | #define IO_H 5 | 6 | #include 7 | #include "tokens.h" 8 | #include "references.h" 9 | #include "value.h" 10 | 11 | namespace fastcode { 12 | namespace builtins { 13 | runtime::reference_apartment* print(const std::vector arguments, runtime::garbage_collector* gc); 14 | runtime::reference_apartment* print_line(const std::vector arguments, runtime::garbage_collector* gc); 15 | runtime::reference_apartment* get_input(const std::vector arguments, runtime::garbage_collector* gc); 16 | 17 | runtime::reference_apartment* file_read_text(const std::vector arguments, runtime::garbage_collector* gc); 18 | runtime::reference_apartment* file_write_text(const std::vector arguments, runtime::garbage_collector* gc); 19 | 20 | runtime::reference_apartment* system_call(const std::vector arguments, runtime::garbage_collector* gc); 21 | } 22 | void handle_syntax_err(int syntax_error, unsigned int pos, const char* source); 23 | void handle_runtime_err(int runtime_error, parsing::token* err_tok); 24 | void print_call_stack(std::stack call_stack); 25 | } 26 | 27 | #endif // !IO_H -------------------------------------------------------------------------------- /src/lexer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "errors.h" 3 | #include "hash.h" 4 | #include "operators.h" 5 | #include "structure.h" 6 | #include "lexer.h" 7 | 8 | //encapsulation chars 9 | #define TOKEN_OPEN_PARAM 0 + MAX_TOKEN_LIMIT 10 | #define TOKEN_CLOSE_PARAM 1 + MAX_TOKEN_LIMIT 11 | #define TOKEN_OPEN_BRACE 2 + MAX_TOKEN_LIMIT 12 | #define TOKEN_CLOSE_BRACE 3 + MAX_TOKEN_LIMIT 13 | #define TOKEN_OPEN_BRACKET 4 + MAX_TOKEN_LIMIT 14 | #define TOKEN_CLOSE_BRACKET 5 + MAX_TOKEN_LIMIT 15 | 16 | #define TOKEN_PERIOD 6 + MAX_TOKEN_LIMIT 17 | #define TOKEN_COMMA 7 + MAX_TOKEN_LIMIT 18 | 19 | #define TOKEN_STATIC 8 + MAX_TOKEN_LIMIT 20 | #define TOKEN_CONST 9 + MAX_TOKEN_LIMIT 21 | 22 | #define TOKEN_QUICK_BODY 10 + MAX_TOKEN_LIMIT 23 | 24 | #define TOKEN_GROUP 11 + MAX_TOKEN_LIMIT 25 | #define TOKEN_END_GROUP 12 + MAX_TOKEN_LIMIT 26 | 27 | #define TOKEN_IN 13 + MAX_TOKEN_LIMIT 28 | #define TOKEN_PARAMS 14 + MAX_TOKEN_LIMIT 29 | 30 | namespace fastcode { 31 | namespace parsing { 32 | //throws an exception if the token type doesn't match the expected type 33 | inline void match_tok(token* token, unsigned char type) { 34 | if (token == nullptr) 35 | throw ERROR_UNEXPECTED_END; 36 | if (token->type != type) 37 | throw ERROR_UNEXPECTED_TOKEN; 38 | } 39 | 40 | //prints a statement 41 | inline function_call_token* print_encapsulate(token* token) { 42 | if (token->type == TOKEN_FUNCTION_CALL) { 43 | function_call_token* call_tok = (function_call_token*)token; 44 | if (call_tok->identifier->id_hash == 275790354) { 45 | return call_tok; 46 | } 47 | } 48 | std::list args; 49 | args.push_back(token); 50 | return new function_call_token(new identifier_token("print"), args); 51 | } 52 | 53 | lexer::lexer(const char* source, unsigned long source_length, struct lexer_state* lexer_state) { 54 | this->source = source; 55 | this->source_length = source_length; 56 | this->position = 0; 57 | this->last_char = 0; 58 | this->last_tok = nullptr; 59 | 60 | this->lexer_state = lexer_state; 61 | 62 | read_char(); 63 | read_token(); 64 | } 65 | 66 | char lexer::read_char() { 67 | if (position == source_length) { 68 | this->last_char = 0; 69 | return 0; 70 | } 71 | return last_char = source[position++]; 72 | } 73 | 74 | token* lexer::read_token() { 75 | while (last_char == ' ' || last_char == '\t' || last_char == '\r' || last_char == '\n' || last_char == ';') { 76 | read_char(); 77 | } 78 | if (isalpha(last_char) || last_char == '_' || last_char == '@') { 79 | std::list id_chars; 80 | do { 81 | id_chars.push_back(last_char); 82 | } while (isalnum(read_char()) || last_char == '_' || last_char == '@'); 83 | char* id_buf = new char[id_chars.size() + 1]; 84 | int index = 0; 85 | for (auto it = id_chars.begin(); it != id_chars.end(); ++it) 86 | id_buf[index++] = *it; 87 | id_buf[index] = 0; //remeber to add a nul terminator 88 | unsigned long hash = insecure_hash(id_buf); 89 | //switches use pre-computed hashes because only constants are allowed 90 | switch (hash) 91 | { 92 | case 257929342: //while 93 | delete[] id_buf; 94 | return last_tok = new token(TOKEN_WHILE); 95 | case 5863380: //if 96 | delete[] id_buf; 97 | return last_tok = new token(TOKEN_IF); 98 | case 2090257189: //elif 99 | delete[] id_buf; 100 | return last_tok = new token(TOKEN_ELIF); 101 | case 2090232142: //else 102 | delete[] id_buf; 103 | return last_tok = new token(TOKEN_ELSE); 104 | case 193510031: // new 105 | delete[] id_buf; 106 | return last_tok = new token(TOKEN_CREATE_STRUCT); 107 | case 498533450: //struct 108 | case 4184890820: //record 109 | delete[] id_buf; 110 | return last_tok = new token(TOKEN_STRUCT_PROTO); 111 | case 998468366: //proc 112 | case 2090156121: //procedure 113 | case 1574308811: //function 114 | delete[] id_buf; 115 | return last_tok = new token(TOKEN_FUNC_PROTO); 116 | case 193491522: //ref 117 | delete[] id_buf; 118 | return last_tok = new token(TOKEN_GET_REFERENCE); 119 | case 281511589: //return 120 | delete[] id_buf; 121 | return last_tok = new token(TOKEN_RETURN); 122 | case 193489624: //and 123 | delete[] id_buf; 124 | return last_tok = new token(OP_AND); 125 | case 5863782: //or 126 | delete[] id_buf; 127 | return last_tok = new token(OP_OR); 128 | case 4135260141: 129 | delete[] id_buf; 130 | return last_tok = new token(TOKEN_STATIC); 131 | case 275975372: 132 | delete[] id_buf; 133 | return last_tok = new token(TOKEN_CONST); 134 | case 1413452809: 135 | delete[] id_buf; 136 | return last_tok = new token(TOKEN_INCLUDE); 137 | case 264645514: //break 138 | delete[] id_buf; 139 | return last_tok = new token(TOKEN_BREAK); 140 | case 271304754: 141 | delete[] id_buf; 142 | return last_tok = new token(TOKEN_GROUP); 143 | case 303295209: 144 | delete[] id_buf; 145 | return last_tok = new token(TOKEN_END_GROUP); 146 | case 193504908: 147 | delete[] id_buf; 148 | return last_tok = new token(TOKEN_FOR); 149 | case 5863644: 150 | delete[] id_buf; 151 | return last_tok = new token(TOKEN_IN); 152 | case 470537897: 153 | delete[] id_buf; 154 | return last_tok = new token(TOKEN_PARAMS); 155 | case 193499145: 156 | delete[] id_buf; 157 | while (last_char != '\n') 158 | read_char(); 159 | return read_token(); 160 | default: { 161 | return last_tok = new identifier_token(id_buf, hash); 162 | } 163 | } 164 | } 165 | else if (isdigit(last_char)) { 166 | std::list num_chars; 167 | do { 168 | num_chars.push_back(last_char); 169 | } while (isdigit(read_char()) || last_char == '.'); 170 | char* num_buf = new char[num_chars.size() + 1]; 171 | int index = 0; 172 | for (auto it = num_chars.begin(); it != num_chars.end(); ++it) 173 | num_buf[index++] = *it; 174 | num_buf[index] = 0; 175 | value_token* to_ret = new value_token(new value(VALUE_TYPE_NUMERICAL, new long double(std::strtold(num_buf, NULL)))); 176 | delete[] num_buf; 177 | return last_tok = to_ret; 178 | } 179 | else if (last_char == '\"') { 180 | std::list chars; 181 | read_char(); 182 | while (last_char != 0 && last_char != '\"') 183 | { 184 | chars.push_back(new value_token(new value(VALUE_TYPE_CHAR, new char(read_data_char())))); 185 | } 186 | if (last_char == 0) 187 | throw ERROR_UNEXPECTED_END; 188 | read_char(); 189 | return last_tok = new create_array_token(chars); 190 | } 191 | else if (last_char == '\'') { 192 | read_char(); 193 | char dat_char = read_data_char(); 194 | read_char(); 195 | return last_tok = new value_token(new value(VALUE_TYPE_CHAR, new char(dat_char))); 196 | } 197 | char old = last_char; 198 | read_char(); 199 | switch (old) 200 | { 201 | case '(': 202 | return last_tok = new token(TOKEN_OPEN_PARAM); 203 | case ')': 204 | return last_tok = new token(TOKEN_CLOSE_PARAM); 205 | case '{': 206 | return last_tok = new token(TOKEN_OPEN_BRACE); 207 | case '}': 208 | return last_tok = new token(TOKEN_CLOSE_BRACE); 209 | case '[': 210 | return last_tok = new token(TOKEN_OPEN_BRACKET); 211 | case ']': 212 | return last_tok = new token(TOKEN_CLOSE_BRACKET); 213 | case '.': 214 | return last_tok = new token(TOKEN_PERIOD); 215 | case ',': 216 | return last_tok = new token(TOKEN_COMMA); 217 | case '=': 218 | if (last_char == '=') { 219 | read_char(); 220 | return last_tok = new token(OP_EQUALS); 221 | } 222 | else if (last_char == '>') { 223 | read_char(); 224 | return last_tok = new token(TOKEN_QUICK_BODY); 225 | } 226 | return last_tok = new token(TOKEN_SET); 227 | case '!': 228 | if (last_char == '=') { 229 | read_char(); 230 | return last_tok = new token(OP_NOT_EQUAL); 231 | } 232 | return last_tok = new token(OP_INVERT); 233 | case '>': 234 | if (last_char == '=') { 235 | read_char(); 236 | return last_tok = new token(OP_MORE_EQUAL); 237 | } 238 | return last_tok = new token(OP_MORE); 239 | case '<': 240 | if (last_char == '=') { 241 | read_char(); 242 | return last_tok = new token(OP_LESS_EQUAL); 243 | } 244 | return last_tok = new token(OP_LESS); 245 | case '+': 246 | if (last_char == '+') { 247 | read_char(); 248 | return last_tok = new token(OP_INCRIMENT); 249 | } 250 | return last_tok = new token(OP_ADD); 251 | case '-': 252 | if (last_char == '-') { 253 | read_char(); 254 | return last_tok = new token(OP_DECRIMENT); 255 | } 256 | return last_tok = new token(OP_SUBTRACT); 257 | case '*': 258 | return last_tok = new token(OP_MULTIPLY); 259 | case '/': 260 | return last_tok = new token(OP_DIVIDE); 261 | case '%': 262 | return last_tok = new token(OP_MODULOUS); 263 | case '^': 264 | return last_tok = new token(OP_POWER); 265 | case 0: 266 | return last_tok = nullptr; 267 | } 268 | throw ERROR_UNRECOGNIZED_TOKEN; 269 | } 270 | 271 | char lexer::read_data_char() { 272 | if (last_char == 0) 273 | throw ERROR_UNEXPECTED_END; 274 | char ret_char; 275 | if (last_char == '\\') { 276 | switch (read_char()) 277 | { 278 | case 'N': 279 | case 'n': 280 | ret_char = '\n'; 281 | break; 282 | case 'T': 283 | case 't': 284 | ret_char = '\t'; 285 | break; 286 | case 'R': 287 | case 'r': 288 | ret_char = '\r'; 289 | break; 290 | case 'B': 291 | case 'b': 292 | ret_char = '\b'; 293 | break; 294 | case '\\': 295 | ret_char = '\\'; 296 | break; 297 | case '\"': 298 | ret_char = '\"'; 299 | break; 300 | case '\'': 301 | ret_char = '\''; 302 | break; 303 | case '0': 304 | ret_char = 0; 305 | break; 306 | default: 307 | throw ERROR_UNRECOGNIZED_ESCAPE_SEQ; 308 | } 309 | } 310 | else 311 | ret_char = last_char; 312 | read_char(); 313 | return ret_char; 314 | } 315 | 316 | std::list lexer::tokenize(bool interactive_mode) { 317 | std::list tokens; 318 | while (last_tok != nullptr && last_tok->type != TOKEN_CLOSE_BRACE) 319 | { 320 | token* tok = tokenize_statement(interactive_mode); 321 | if (tok != nullptr) 322 | tokens.push_back(tok); 323 | } 324 | if (last_tok != nullptr) 325 | match_tok(last_tok, TOKEN_CLOSE_BRACE); 326 | if (interactive_mode) 327 | lexer_state->group_all_references(); 328 | return tokens; 329 | } 330 | 331 | token* lexer::tokenize_statement(bool interactive_mode) { 332 | switch (last_tok->type) 333 | { 334 | case TOKEN_GROUP: { 335 | delete last_tok; 336 | match_tok(read_token(), TOKEN_IDENTIFIER); 337 | identifier_token* id = (identifier_token*)last_tok; 338 | 339 | lexer_state->new_group(id); 340 | 341 | read_token(); 342 | 343 | return nullptr; 344 | } 345 | case TOKEN_END_GROUP: { 346 | delete last_tok; 347 | lexer_state->pop_group(); 348 | 349 | read_token(); 350 | 351 | return nullptr; 352 | } 353 | case TOKEN_CONST: { 354 | delete last_tok; 355 | match_tok(read_token(), TOKEN_IDENTIFIER); 356 | identifier_token* id = (identifier_token*)last_tok; 357 | if (lexer_state->constants.count(id->id_hash)) { 358 | delete lexer_state->constants[id->id_hash]; 359 | } 360 | match_tok(read_token(), TOKEN_SET); 361 | delete last_tok; 362 | match_tok(read_token(), TOKEN_VALUE); 363 | value_token* value_tok = (value_token*)last_tok; 364 | lexer_state->constants[id->id_hash] = value_tok; 365 | delete id; 366 | read_token(); 367 | return nullptr; 368 | } 369 | case TOKEN_STATIC: { 370 | delete last_tok; 371 | match_tok(read_token(), TOKEN_IDENTIFIER); 372 | identifier_token* id = (identifier_token*)last_tok; 373 | lexer_state->declare_id(id, GROUP_TYPE_VAR); 374 | match_tok(read_token(), TOKEN_SET); 375 | delete last_tok; 376 | read_token(); 377 | token* val_tok = tokenize_expression(); 378 | std::list modifiers; 379 | modifiers.push_back(id); 380 | return new set_token(new variable_access_token(modifiers), val_tok, true); 381 | } 382 | case TOKEN_BREAK: { 383 | token* tok = last_tok; 384 | read_token(); 385 | return tok; 386 | } 387 | case TOKEN_INCLUDE: { 388 | delete last_tok; 389 | match_tok(read_token(), TOKEN_CREATE_ARRAY); 390 | create_array_token* create_str = (create_array_token*)last_tok; 391 | char* buf = new char[create_str->values.size() + 1]; 392 | unsigned char size = 0; 393 | for (auto it = create_str->values.begin(); it != create_str->values.end(); ++it) { 394 | value_token* val_tok = (value_token*)*it; 395 | value* val = val_tok->get_value(); 396 | buf[size++] = *val->get_char(); 397 | delete val; 398 | } 399 | buf[size] = 0; 400 | delete create_str; 401 | read_token(); 402 | return new include_token(buf); 403 | } 404 | case TOKEN_IDENTIFIER: 405 | if (interactive_mode) { 406 | return print_encapsulate(tokenize_expression()); 407 | } 408 | return tokenize_expression(); 409 | case TOKEN_OPEN_PARAM: 410 | case TOKEN_OPEN_BRACKET: 411 | case TOKEN_CREATE_ARRAY: 412 | case TOKEN_CREATE_STRUCT: 413 | case TOKEN_BINARY_OP: 414 | case TOKEN_UNARY_OP: 415 | case TOKEN_VALUE: 416 | case OP_SUBTRACT: 417 | case OP_INVERT: 418 | if (interactive_mode) { 419 | return print_encapsulate(tokenize_expression()); 420 | } 421 | throw ERROR_UNEXPECTED_TOKEN; 422 | case TOKEN_RETURN: 423 | delete last_tok; 424 | read_token(); 425 | return new return_token(tokenize_expression()); 426 | case TOKEN_IF: { 427 | delete last_tok; 428 | read_token(); 429 | token* condition = tokenize_expression(); 430 | conditional_token* if_struct = new conditional_token(TOKEN_IF, condition, tokenize_body(), nullptr); 431 | conditional_token* current = if_struct; 432 | while (last_tok != nullptr) 433 | { 434 | if (last_tok->type == TOKEN_ELIF) { 435 | delete last_tok; 436 | read_token(); 437 | condition = tokenize_expression(); 438 | current->next = new conditional_token(TOKEN_ELIF, condition, tokenize_body(), nullptr); 439 | current = current->next; 440 | } 441 | else if (last_tok->type == TOKEN_ELSE) { 442 | delete last_tok; 443 | read_token(); 444 | current->next = new conditional_token(TOKEN_ELSE, nullptr, tokenize_body(), nullptr); 445 | break; 446 | } 447 | else 448 | break; 449 | } 450 | return if_struct; 451 | } 452 | case TOKEN_WHILE: { 453 | delete last_tok; 454 | read_token(); 455 | token* condition = tokenize_expression(); 456 | return new conditional_token(TOKEN_WHILE, condition, tokenize_body(), nullptr); 457 | } 458 | case TOKEN_FOR: { 459 | delete last_tok; 460 | match_tok(read_token(), TOKEN_IDENTIFIER); 461 | identifier_token* id = (identifier_token*)last_tok; 462 | lexer_state->declare_id(id, GROUP_TYPE_VAR); 463 | match_tok(read_token(), TOKEN_IN); 464 | delete last_tok; 465 | read_token(); 466 | token* collection = tokenize_value(); 467 | return new for_token(id, collection, tokenize_body()); 468 | } 469 | case TOKEN_STRUCT_PROTO: { 470 | delete last_tok; 471 | match_tok(read_token(), TOKEN_IDENTIFIER); 472 | identifier_token* proto_id = (identifier_token*)last_tok; 473 | lexer_state->declare_id(proto_id, GROUP_TYPE_STRUCT); 474 | match_tok(read_token(), TOKEN_OPEN_BRACE); 475 | delete last_tok; 476 | std::list properties; 477 | while (last_tok != nullptr && read_token()->type != TOKEN_CLOSE_BRACE) 478 | { 479 | match_tok(last_tok, TOKEN_IDENTIFIER); 480 | properties.push_back((identifier_token*)last_tok); 481 | } 482 | if (last_tok == nullptr) 483 | throw ERROR_UNEXPECTED_END; 484 | delete last_tok; 485 | read_token(); 486 | return new structure_prototype(proto_id, properties); 487 | } 488 | case TOKEN_FUNC_PROTO: { 489 | delete last_tok; 490 | match_tok(read_token(), TOKEN_IDENTIFIER); 491 | identifier_token* proto_id = (identifier_token*)last_tok; 492 | lexer_state->declare_id(proto_id, GROUP_TYPE_FUNC); 493 | match_tok(read_token(), TOKEN_OPEN_PARAM); 494 | delete last_tok; 495 | std::list params; 496 | bool params_mode = false; 497 | while (last_tok != nullptr && read_token()->type != TOKEN_CLOSE_PARAM) 498 | { 499 | if (last_tok->type == TOKEN_COMMA) { 500 | delete last_tok; 501 | read_token(); 502 | } 503 | else if (last_tok->type == TOKEN_PARAMS) { 504 | delete last_tok; 505 | params_mode = true; 506 | read_token(); 507 | } 508 | match_tok(last_tok, TOKEN_IDENTIFIER); 509 | params.push_back((identifier_token*)last_tok); 510 | lexer_state->declare_id((identifier_token*)last_tok, GROUP_TYPE_VAR); 511 | } 512 | if (params_mode && params.size() != 1) 513 | throw ERROR_UNEXPECTED_ARGUMENT_SIZE; 514 | if (last_tok == nullptr) 515 | throw ERROR_UNEXPECTED_TOKEN; 516 | delete last_tok; 517 | read_token(); 518 | return new function_prototype(proto_id, params, tokenize_body(), params_mode); 519 | } 520 | } 521 | throw ERROR_UNEXPECTED_TOKEN; 522 | } 523 | 524 | std::list lexer::tokenize_body() { 525 | if (last_tok == nullptr) 526 | throw ERROR_UNEXPECTED_END; 527 | else if (last_tok->type == TOKEN_OPEN_BRACE) { 528 | delete last_tok; 529 | read_token(); 530 | std::list body = tokenize(false); 531 | delete last_tok; 532 | read_token(); 533 | return body; 534 | } 535 | else if (last_tok->type == TOKEN_QUICK_BODY) { 536 | delete last_tok; 537 | read_token(); 538 | std::list body; 539 | if (last_tok == nullptr) 540 | throw ERROR_UNEXPECTED_END; 541 | body.push_back(tokenize_statement(false)); 542 | return body; 543 | } 544 | else { 545 | throw ERROR_UNEXPECTED_TOKEN; 546 | } 547 | } 548 | 549 | variable_access_token* lexer::tokenize_var_access() { 550 | match_tok(last_tok, TOKEN_IDENTIFIER); 551 | identifier_token* identifier = (identifier_token*)last_tok; 552 | read_token(); 553 | return tokenize_var_access(identifier); 554 | } 555 | 556 | variable_access_token* lexer::tokenize_var_access(identifier_token* identifier) { 557 | std::list toks; 558 | toks.push_back(identifier); 559 | while (last_tok != nullptr) 560 | { 561 | if (last_tok->type == TOKEN_PERIOD) { 562 | delete last_tok; 563 | match_tok(read_token(), TOKEN_IDENTIFIER); 564 | toks.push_back(last_tok); 565 | } 566 | else if (last_tok->type == TOKEN_OPEN_BRACKET) { 567 | delete last_tok; 568 | read_token(); 569 | toks.push_back(new index_token(tokenize_expression())); 570 | match_tok(last_tok, TOKEN_CLOSE_BRACKET); 571 | delete last_tok; 572 | } 573 | else { 574 | break; 575 | } 576 | read_token(); 577 | } 578 | return new variable_access_token(toks); 579 | } 580 | token* lexer::tokenize_value() { 581 | if (last_tok == nullptr) 582 | throw ERROR_UNEXPECTED_END; 583 | else if (last_tok->type == TOKEN_IDENTIFIER) { 584 | identifier_token* identifier = (identifier_token*)last_tok; 585 | read_token(); 586 | //tokenize function call 587 | if (last_tok != nullptr && last_tok->type == TOKEN_OPEN_PARAM) { 588 | delete last_tok; 589 | std::list arguments; 590 | while (last_tok != nullptr) 591 | { 592 | read_token(); 593 | if (last_tok->type == TOKEN_CLOSE_PARAM) 594 | break; 595 | arguments.push_back(tokenize_expression()); 596 | if (last_tok->type != TOKEN_COMMA) { 597 | break; 598 | } 599 | delete last_tok; 600 | } 601 | match_tok(last_tok, TOKEN_CLOSE_PARAM); 602 | delete last_tok; 603 | read_token(); 604 | this->lexer_state->reference_id(identifier, GROUP_TYPE_FUNC); 605 | return new function_call_token(identifier, arguments); 606 | } 607 | else 608 | { 609 | if (lexer_state->constants.count(identifier->id_hash)) { 610 | unsigned long hash = identifier->id_hash; 611 | delete identifier; 612 | return new value_token(lexer_state->constants[hash]->get_value()); 613 | } 614 | 615 | if (last_tok != nullptr && last_tok->type == TOKEN_SET) 616 | this->lexer_state->declare_id(identifier, GROUP_TYPE_VAR); 617 | else 618 | this->lexer_state->reference_id(identifier, GROUP_TYPE_VAR); 619 | 620 | variable_access_token* var_access = tokenize_var_access(identifier); 621 | 622 | if (last_tok == nullptr) 623 | return var_access; 624 | else if (last_tok->type == OP_INCRIMENT) { 625 | delete last_tok; 626 | read_token(); 627 | return new unary_operator_token(var_access, OP_INCRIMENT); 628 | } 629 | else if (last_tok->type == OP_DECRIMENT) { 630 | delete last_tok; 631 | read_token(); 632 | return new unary_operator_token(var_access, OP_DECRIMENT); 633 | } 634 | else if (last_tok->type == TOKEN_SET) { 635 | delete last_tok; 636 | read_token(); 637 | token* to_set = tokenize_expression(); 638 | return new set_token(var_access, to_set, false); 639 | } 640 | 641 | return var_access; 642 | } 643 | } 644 | else if (last_tok->type == TOKEN_GET_REFERENCE) { 645 | delete last_tok; 646 | match_tok(read_token(), TOKEN_IDENTIFIER); 647 | lexer_state->reference_id((identifier_token*)last_tok, GROUP_TYPE_VAR); 648 | get_reference_token* get_ref_tok = new get_reference_token(tokenize_var_access()); 649 | return get_ref_tok; 650 | } 651 | else if (last_tok->type == TOKEN_OPEN_PARAM) { 652 | delete last_tok; 653 | read_token(); 654 | token* val_tok = tokenize_expression(); 655 | match_tok(last_tok, TOKEN_CLOSE_PARAM); 656 | delete last_tok; 657 | read_token(); 658 | return val_tok; 659 | } 660 | else if (last_tok->type == TOKEN_OPEN_BRACKET) { 661 | delete last_tok; 662 | std::list values; 663 | 664 | while (last_tok != nullptr) 665 | { 666 | read_token(); 667 | 668 | if (last_tok->type == TOKEN_CLOSE_BRACKET) 669 | break; 670 | 671 | values.push_back(tokenize_expression()); 672 | if (last_tok->type != TOKEN_COMMA) { 673 | break; 674 | } 675 | delete last_tok; 676 | } 677 | match_tok(last_tok, TOKEN_CLOSE_BRACKET); 678 | delete last_tok; 679 | read_token(); 680 | return new create_array_token(values); 681 | } 682 | else if (last_tok->type == TOKEN_CREATE_STRUCT) { 683 | delete last_tok; 684 | match_tok(read_token(), TOKEN_IDENTIFIER); 685 | create_struct_token* new_struct = new create_struct_token((identifier_token*)last_tok); 686 | this->lexer_state->reference_id(new_struct->identifier, GROUP_TYPE_STRUCT); 687 | read_token(); 688 | return new_struct; 689 | } 690 | else if (last_tok->type == TOKEN_VALUE || last_tok->type == TOKEN_CREATE_ARRAY) { 691 | token* val_tok = last_tok; 692 | read_token(); 693 | return val_tok; 694 | } 695 | else if (last_tok->type == OP_INVERT || last_tok->type == OP_SUBTRACT) { 696 | unsigned char type = last_tok->type; 697 | if (type == OP_SUBTRACT) 698 | type = OP_NEGATE; 699 | delete last_tok; 700 | read_token(); 701 | return new unary_operator_token(tokenize_value(), type); 702 | } 703 | throw ERROR_UNEXPECTED_TOKEN; 704 | } 705 | 706 | token* lexer::tokenize_expression(unsigned char min) { 707 | //utilizes shunting-yard 708 | token* lhs = tokenize_value(); 709 | while (last_tok != nullptr && is_op_tok(last_tok->type) && get_operator_precedence(last_tok->type) >= min) { 710 | unsigned char op = last_tok->type; 711 | unsigned char prec = get_operator_precedence(op); 712 | const unsigned char assoc = 0; //operator assosiativity 0 = left, 1 = right 713 | unsigned char nextmin = assoc == 0 ? prec : prec + 1; 714 | delete last_tok; 715 | read_token(); 716 | token* rhs = tokenize_expression(nextmin); 717 | lhs = new binary_operator_token(lhs, rhs, op); 718 | } 719 | return lhs; 720 | } 721 | } 722 | } -------------------------------------------------------------------------------- /src/lexer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef LEXER_H 4 | #define LEXER_H 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "builtins.h" 11 | #include "tokens.h" 12 | 13 | #define GROUP_TYPE_STRUCT 0 14 | #define GROUP_TYPE_FUNC 1 15 | #define GROUP_TYPE_VAR 2 16 | 17 | namespace fastcode { 18 | namespace parsing { 19 | class lexer { 20 | public: 21 | struct lexer_state { 22 | private: 23 | struct identifier_system 24 | { 25 | std::unordered_set declerations; 26 | std::list references; 27 | }; 28 | 29 | struct group { 30 | private: 31 | identifier_system id_systems[3]; 32 | identifier_token* identifier; 33 | 34 | inline void proc_id(identifier_token* id) { 35 | std::list chars; 36 | 37 | for (int i = 0; i < strlen(id->get_identifier()); i++) 38 | chars.push_back(id->get_identifier()[i]); 39 | 40 | group* current = this; 41 | while (current != nullptr) 42 | { 43 | chars.push_back('@'); 44 | for (int i = 0; i < strlen(current->identifier->get_identifier()); i++) 45 | chars.push_back(current->identifier->get_identifier()[i]); 46 | current = current->parent; 47 | } 48 | 49 | char* new_buf = new char[chars.size() + 1]; 50 | unsigned long in = 0; 51 | for (auto i = chars.begin(); i != chars.end(); ++i) { 52 | new_buf[in++] = *i; 53 | } 54 | new_buf[in] = 0; 55 | 56 | id->set_c_str(new_buf); 57 | } 58 | public: 59 | group* parent; 60 | 61 | group(struct identifier_token* identifier, group* parent = nullptr) { 62 | this->identifier = identifier; 63 | this->parent = parent; 64 | } 65 | 66 | inline void proc_decleration(identifier_token* id, unsigned char type) { 67 | id_systems[type].declerations.insert(id->id_hash); 68 | proc_id(id); 69 | } 70 | 71 | inline void proc_reference(identifier_token* id, unsigned char type) { 72 | id_systems[type].references.push_back(id); 73 | } 74 | 75 | inline void group_references(bool remall = false) { 76 | for (unsigned char type = 0; type < 3; type++) 77 | { 78 | std::list::iterator> to_remove; 79 | for (auto i = id_systems[type].references.begin(); i != id_systems[type].references.end(); ++i) { 80 | if (id_systems[type].declerations.count((*i)->id_hash)) { 81 | proc_id(*i); 82 | if (!remall) 83 | to_remove.push_back(i); 84 | } 85 | } 86 | if (remall) 87 | id_systems[type].references.clear(); 88 | else 89 | for (auto i = to_remove.begin(); i != to_remove.end(); ++i) { 90 | id_systems[type].references.erase(*i); 91 | } 92 | } 93 | } 94 | 95 | ~group() { 96 | group_references(true); 97 | delete identifier; 98 | } 99 | }; 100 | 101 | group* top_group = nullptr; 102 | public: 103 | std::unordered_map constants; 104 | 105 | ~lexer_state() { 106 | for (auto it = this->constants.begin(); it != this->constants.end(); ++it) 107 | delete (*it).second; 108 | while (top_group != nullptr) 109 | pop_group(); 110 | } 111 | 112 | inline void declare_id(identifier_token* id, unsigned char type) { 113 | if (top_group != nullptr) 114 | top_group->proc_decleration(id, type); 115 | } 116 | 117 | inline void reference_id(identifier_token* id, unsigned char type) { 118 | if (top_group != nullptr) 119 | top_group->proc_reference(id, type); 120 | } 121 | 122 | inline group* current_group() { 123 | return this->top_group; 124 | } 125 | 126 | inline void new_group(identifier_token* identifier) { 127 | top_group = new group(identifier, top_group); 128 | } 129 | 130 | inline void pop_group() { 131 | group* to_delete = top_group; 132 | top_group = top_group->parent; 133 | delete to_delete; 134 | } 135 | 136 | inline void group_all_references() { 137 | group* current = top_group; 138 | while (current != nullptr) { 139 | current->group_references(); 140 | current = current->parent; 141 | } 142 | } 143 | }; 144 | 145 | lexer(const char* source, unsigned long source_length, lexer_state* lexer_state); 146 | 147 | inline unsigned int get_pos() { 148 | return this->position; 149 | } 150 | 151 | std::list tokenize(bool interactive_mode); 152 | private: 153 | const char* source; 154 | unsigned long position; 155 | unsigned long source_length; 156 | char last_char; 157 | token* last_tok; 158 | 159 | lexer_state* lexer_state; 160 | 161 | //reads the next availible character. 162 | char read_char(); 163 | 164 | char read_data_char(); 165 | 166 | //reads the next top-level token 167 | token* read_token(); 168 | token* tokenize_statement(bool interactive_mode); 169 | std::list tokenize_body(); 170 | variable_access_token* tokenize_var_access(); 171 | variable_access_token* tokenize_var_access(identifier_token* identifier); 172 | token* tokenize_expression(unsigned char min = 0); 173 | token* tokenize_value(); 174 | }; 175 | } 176 | } 177 | 178 | #endif // !LEXER_H -------------------------------------------------------------------------------- /src/linq.cpp: -------------------------------------------------------------------------------- 1 | #include "builtins.h" 2 | #include "collection.h" 3 | #include "linq.h" 4 | 5 | namespace fastcode { 6 | namespace builtins { 7 | runtime::reference_apartment* allocate_array(const std::vector arguments, runtime::garbage_collector* gc) { 8 | match_arg_len(arguments, 1); 9 | match_arg_type(arguments[0], VALUE_TYPE_NUMERICAL); 10 | 11 | if (*arguments[0]->get_numerical() < 0) { 12 | throw ERROR_INVALID_VALUE_TYPE; 13 | } 14 | 15 | unsigned long req_size = (unsigned long)*arguments[0]->get_numerical(); 16 | 17 | runtime::collection* allocated_array = new runtime::collection(req_size, gc); 18 | return allocated_array->get_parent_ref(); 19 | } 20 | 21 | runtime::reference_apartment* get_length(const std::vector arguments, runtime::garbage_collector* gc) { 22 | match_arg_len(arguments, 1); 23 | match_arg_type(arguments[0], VALUE_TYPE_COLLECTION); 24 | 25 | runtime::collection* collection = (runtime::collection*)arguments[0]->ptr; 26 | return gc->new_apartment(new value(VALUE_TYPE_NUMERICAL, new long double((long double)collection->size))); 27 | } 28 | 29 | runtime::reference_apartment* count_instances(const std::vector arguments, runtime::garbage_collector* gc) { 30 | match_arg_len(arguments, 2); 31 | match_arg_type(arguments[0], VALUE_TYPE_COLLECTION); 32 | 33 | runtime::collection* collection = (runtime::collection*)arguments[0]->ptr; 34 | unsigned int instances = 0; 35 | 36 | for (unsigned int i = 0; i < collection->size; i++) 37 | { 38 | if (collection->get_value(i)->compare(arguments[1]) == 0) 39 | instances++; 40 | } 41 | return gc->new_apartment(new value(VALUE_TYPE_NUMERICAL, new long double((long double)instances))); 42 | } 43 | 44 | runtime::reference_apartment* get_range(const std::vector arguments, runtime::garbage_collector* gc) { 45 | long double start = 0; 46 | long double step = 1; 47 | long double stop; 48 | if (arguments.size() == 1) 49 | stop = *arguments[0]->get_numerical(); 50 | else if (arguments.size() == 2) { 51 | start = *arguments[0]->get_numerical(); 52 | stop = *arguments[1]->get_numerical(); 53 | } 54 | else if (arguments.size() == 3) { 55 | start = *arguments[0]->get_numerical(); 56 | stop = *arguments[1]->get_numerical(); 57 | step = *arguments[2]->get_numerical(); 58 | } 59 | else 60 | throw ERROR_UNEXPECTED_ARGUMENT_SIZE; 61 | 62 | unsigned long size = abs(stop - start) / abs(step); 63 | 64 | runtime::collection* range = new runtime::collection(size, gc); 65 | 66 | unsigned int j = 0; 67 | 68 | for (long double i = start; step > 0 ? i < stop : i > stop; i += step) 69 | range->set_value(j++, new value(VALUE_TYPE_NUMERICAL, new long double(i))); 70 | 71 | return range->get_parent_ref(); 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /src/linq.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef LINQ_H 4 | #define LINQ_H 5 | 6 | #include 7 | #include "value.h" 8 | #include "references.h" 9 | #include "garbage.h" 10 | 11 | namespace fastcode { 12 | namespace builtins { 13 | runtime::reference_apartment* allocate_array(const std::vector arguments, runtime::garbage_collector* gc); 14 | runtime::reference_apartment* get_length(const std::vector arguments, runtime::garbage_collector* gc); 15 | runtime::reference_apartment* count_instances(const std::vector arguments, runtime::garbage_collector* gc); 16 | runtime::reference_apartment* get_range(const std::vector arguments, runtime::garbage_collector* gc); 17 | } 18 | } 19 | 20 | #endif // !LINQ_H -------------------------------------------------------------------------------- /src/math.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "builtins.h" 4 | #include "collection.h" 5 | #include "math.h" 6 | 7 | namespace fastcode { 8 | namespace builtins { 9 | namespace math { 10 | runtime::reference_apartment* numabs(const std::vector arguments, runtime::garbage_collector* gc) { 11 | match_arg_len(arguments, 1); 12 | match_arg_type(arguments[0], VALUE_TYPE_NUMERICAL); 13 | return gc->new_apartment(new value(VALUE_TYPE_NUMERICAL, new long double(std::abs(*arguments[0]->get_numerical())))); 14 | } 15 | 16 | runtime::reference_apartment* random(const std::vector arguments, runtime::garbage_collector* gc) { 17 | match_arg_len(arguments, 1); 18 | match_arg_type(arguments[0], VALUE_TYPE_COLLECTION); 19 | runtime::collection* collection = (runtime::collection*)arguments[0]->ptr; 20 | unsigned long index = abs(rand()) % collection->size; 21 | return collection->get_reference(index); 22 | } 23 | 24 | runtime::reference_apartment* sin(const std::vector arguments, runtime::garbage_collector* gc) { 25 | match_arg_len(arguments, 1); 26 | match_arg_type(arguments[0], VALUE_TYPE_NUMERICAL); 27 | return gc->new_apartment(new value(VALUE_TYPE_NUMERICAL, new long double(::sin(*arguments[0]->get_numerical())))); 28 | } 29 | 30 | runtime::reference_apartment* cos(const std::vector arguments, runtime::garbage_collector* gc) { 31 | match_arg_len(arguments, 1); 32 | match_arg_type(arguments[0], VALUE_TYPE_NUMERICAL); 33 | return gc->new_apartment(new value(VALUE_TYPE_NUMERICAL, new long double(::cos(*arguments[0]->get_numerical())))); 34 | } 35 | 36 | runtime::reference_apartment* tan(const std::vector arguments, runtime::garbage_collector* gc) { 37 | match_arg_len(arguments, 1); 38 | match_arg_type(arguments[0], VALUE_TYPE_NUMERICAL); 39 | return gc->new_apartment(new value(VALUE_TYPE_NUMERICAL, new long double(::tan(*arguments[0]->get_numerical())))); 40 | } 41 | 42 | runtime::reference_apartment* asin(const std::vector arguments, runtime::garbage_collector* gc) { 43 | match_arg_len(arguments, 1); 44 | match_arg_type(arguments[0], VALUE_TYPE_NUMERICAL); 45 | return gc->new_apartment(new value(VALUE_TYPE_NUMERICAL, new long double(::asin(*arguments[0]->get_numerical())))); 46 | } 47 | 48 | runtime::reference_apartment* acos(const std::vector arguments, runtime::garbage_collector* gc) { 49 | match_arg_len(arguments, 1); 50 | match_arg_type(arguments[0], VALUE_TYPE_NUMERICAL); 51 | return gc->new_apartment(new value(VALUE_TYPE_NUMERICAL, new long double(::acos(*arguments[0]->get_numerical())))); 52 | } 53 | 54 | runtime::reference_apartment* atan(const std::vector arguments, runtime::garbage_collector* gc) { 55 | match_arg_len(arguments, 1); 56 | match_arg_type(arguments[0], VALUE_TYPE_NUMERICAL); 57 | return gc->new_apartment(new value(VALUE_TYPE_NUMERICAL, new long double(::atan(*arguments[0]->get_numerical())))); 58 | } 59 | 60 | runtime::reference_apartment* log(const std::vector arguments, runtime::garbage_collector* gc) { 61 | match_arg_len(arguments, 1); 62 | match_arg_type(arguments[0], VALUE_TYPE_NUMERICAL); 63 | return gc->new_apartment(new value(VALUE_TYPE_NUMERICAL, new long double(::log(*arguments[0]->get_numerical())))); 64 | } 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /src/math.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef MATH_H 4 | #define MATH_H 5 | 6 | #include 7 | #include "garbage.h" 8 | #include "references.h" 9 | #include "value.h" 10 | 11 | namespace fastcode { 12 | namespace builtins { 13 | namespace math { 14 | runtime::reference_apartment* numabs(const std::vector arguments, runtime::garbage_collector* gc); 15 | runtime::reference_apartment* random(const std::vector arguments, runtime::garbage_collector* gc); 16 | 17 | //trigonometry 18 | runtime::reference_apartment* sin(const std::vector arguments, runtime::garbage_collector* gc); 19 | runtime::reference_apartment* cos(const std::vector arguments, runtime::garbage_collector* gc); 20 | runtime::reference_apartment* tan(const std::vector arguments, runtime::garbage_collector* gc); 21 | runtime::reference_apartment* asin(const std::vector arguments, runtime::garbage_collector* gc); 22 | runtime::reference_apartment* acos(const std::vector arguments, runtime::garbage_collector* gc); 23 | runtime::reference_apartment* atan(const std::vector arguments, runtime::garbage_collector* gc); 24 | 25 | //exponential 26 | runtime::reference_apartment* log(const std::vector arguments, runtime::garbage_collector* gc); 27 | } 28 | } 29 | } 30 | #endif // !MATH_H -------------------------------------------------------------------------------- /src/objects.cpp: -------------------------------------------------------------------------------- 1 | #include "hash.h" 2 | #include "structure.h" 3 | #include "collection.h" 4 | #include "references.h" 5 | #include "garbage.h" 6 | 7 | namespace fastcode { 8 | namespace parsing { 9 | structure_prototype::structure_prototype(const char* identifier, const char* properties[], unsigned int property_count) : token(TOKEN_STRUCT_PROTO) { 10 | this->identifier = new identifier_token(identifier); 11 | this->property_count = property_count; 12 | for (unsigned int i = 0; i < property_count; i++) { 13 | identifier_token* prop = new identifier_token(identifier); 14 | this->property_indicies[prop->id_hash] = i; 15 | this->properties.push_back(prop); 16 | } 17 | } 18 | 19 | structure_prototype::structure_prototype(identifier_token* identifier, std::list properties) : token(TOKEN_STRUCT_PROTO) { 20 | this->identifier = identifier; 21 | this->property_count = properties.size(); 22 | unsigned int index = 0; 23 | for (auto i = properties.begin(); i != properties.end(); ++i) 24 | this->property_indicies[(*i)->id_hash] = index++; 25 | this->properties = properties; 26 | } 27 | 28 | structure_prototype::~structure_prototype() { 29 | for (auto i = properties.begin(); i != properties.end(); ++i) 30 | delete* i; 31 | delete identifier; 32 | } 33 | } 34 | 35 | namespace runtime { 36 | structure::structure(parsing::structure_prototype* prototype, garbage_collector* gc) : structure(prototype, gc->new_apartment(new value(VALUE_TYPE_STRUCT, this))) { 37 | for (unsigned int i = 0; i < prototype->property_count; i++) 38 | this->properties[i] = gc->new_apartment(new value(VALUE_TYPE_NULL, nullptr)); 39 | } 40 | 41 | structure::structure(parsing::structure_prototype* prototype, reference_apartment* parent_reference) { 42 | this->parent_reference = parent_reference; 43 | this->prototype = prototype; 44 | this->properties = new reference_apartment * [prototype->property_count]; 45 | } 46 | 47 | structure::~structure() { 48 | delete[] this->properties; 49 | } 50 | 51 | void structure::set_reference(unsigned long id_hash, reference_apartment* reference) { 52 | unsigned int index = prototype->get_index(id_hash); 53 | this->properties[index]->remove_parent_references(parent_reference); 54 | reference->add_parent_references(parent_reference); 55 | this->properties[index] = reference; 56 | } 57 | 58 | void structure::set_reference_at(unsigned int index, reference_apartment* reference) { 59 | this->properties[index]->remove_parent_references(parent_reference); 60 | reference->add_parent_references(parent_reference); 61 | this->properties[index] = reference; 62 | } 63 | 64 | structure* structure::clone(reference_apartment* parent_reference) { 65 | structure* kopy = new structure(this->prototype, parent_reference); 66 | for (unsigned int i = 0; i < this->prototype->property_count; i++) 67 | { 68 | kopy->properties[i] = this->properties[i]; 69 | this->properties[i]->add_parent_references(parent_reference); 70 | } 71 | return kopy; 72 | } 73 | 74 | int structure::hash() { 75 | int hash = 2187; //magic num for structs 76 | for (unsigned int i = 0; i < this->prototype->property_count; i++) 77 | { 78 | hash = combine_hash(hash, this->properties[i]->value->hash()); 79 | } 80 | return hash; 81 | } 82 | 83 | collection::collection(unsigned long size, garbage_collector* gc) : collection(size, gc->new_apartment(new value(VALUE_TYPE_COLLECTION, this))) { 84 | for (unsigned long i = 0; i < size; i++) 85 | { 86 | this->inner_collection[i] = gc->new_apartment(new value(VALUE_TYPE_NULL, nullptr)); 87 | } 88 | } 89 | 90 | collection::collection(collection* a, collection* b, reference_apartment* parent_reference) : collection(a->size + b->size, parent_reference) { 91 | for (unsigned int i = 0; i < a->size; i++) 92 | { 93 | this->inner_collection[i] = a->inner_collection[i]; 94 | this->inner_collection[i]->add_parent_references(parent_reference); 95 | } 96 | for (unsigned int i = 0; i < b->size; i++) 97 | { 98 | this->inner_collection[a->size + i] = b->inner_collection[i]; 99 | b->inner_collection[i]->add_parent_references(parent_reference); 100 | } 101 | } 102 | 103 | collection::collection(unsigned long size, reference_apartment* parent_reference) { 104 | this->size = size; 105 | this->parent_reference = parent_reference; 106 | this->inner_collection = new reference_apartment * [size]; 107 | } 108 | 109 | collection::~collection() { 110 | delete this->inner_collection; 111 | } 112 | 113 | collection* collection::clone(reference_apartment* new_parent_apptr) { 114 | collection* copy = new collection(this->size, new_parent_apptr); 115 | for (unsigned long i = 0; i < size; i++) 116 | copy->inner_collection[i] = this->inner_collection[i]; 117 | return copy; 118 | } 119 | 120 | int collection::hash() { 121 | int hash = 66; //magic number for collection hahses 122 | for (unsigned int i = 0; i < this->size; i++) 123 | hash = combine_hash(hash, this->inner_collection[i]->value->hash()); 124 | return hash; 125 | } 126 | 127 | //returns the inner refrence_apartment array. DO NOT DELETE 128 | reference_apartment** reference_apartment::get_children(unsigned int* children_size) { 129 | if (value->type == VALUE_TYPE_COLLECTION) { 130 | collection* collection = (class collection*)value->ptr; 131 | *children_size = collection->size; 132 | return collection->get_children(); 133 | } 134 | else if (value->type == VALUE_TYPE_STRUCT) { 135 | structure* structure = (class structure*)value->ptr; 136 | *children_size = structure->get_size(); 137 | return structure->get_children(); 138 | } 139 | return nullptr; 140 | } 141 | } 142 | 143 | value::~value() { 144 | switch (this->type) 145 | { 146 | case VALUE_TYPE_NULL: 147 | break; 148 | case VALUE_TYPE_CHAR: 149 | delete (char*)this->ptr; 150 | break; 151 | case VALUE_TYPE_NUMERICAL: 152 | delete (long double*)this->ptr; 153 | break; 154 | case VALUE_TYPE_COLLECTION: 155 | delete (runtime::collection*)this->ptr; 156 | break; 157 | case VALUE_TYPE_STRUCT: 158 | delete (runtime::structure*)this->ptr; 159 | break; 160 | case VALUE_TYPE_HANDLE: 161 | break; 162 | default: 163 | throw ERROR_INVALID_VALUE_TYPE; 164 | } 165 | } 166 | 167 | int value::hash() { 168 | switch (this->type) 169 | { 170 | case VALUE_TYPE_CHAR: 171 | return int(*(char*)this->ptr); 172 | case VALUE_TYPE_NUMERICAL: 173 | return int(*(long double*)this->ptr); 174 | case VALUE_TYPE_COLLECTION: 175 | return ((runtime::collection*)this->ptr)->hash(); 176 | case VALUE_TYPE_STRUCT: 177 | return ((runtime::structure*)this->ptr)->hash(); 178 | case VALUE_TYPE_HANDLE: 179 | return int(this->ptr); 180 | default: 181 | throw ERROR_INVALID_VALUE_TYPE; 182 | } 183 | } 184 | } -------------------------------------------------------------------------------- /src/operators.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "errors.h" 3 | #include "operators.h" 4 | #include "collection.h" 5 | 6 | namespace fastcode { 7 | namespace parsing { 8 | binary_operator_token::binary_operator_token(token* left, token* right, unsigned char op) : token(TOKEN_BINARY_OP) { 9 | if (!is_binary_operator(op)) 10 | throw ERROR_INVALID_BINARY_OPERATOR; 11 | if (!is_value_tok(right) || !is_value_tok(left)) 12 | throw ERROR_UNEXPECTED_TOKEN; 13 | this->left = left; 14 | this->right = right; 15 | this->op = op; 16 | } 17 | 18 | binary_operator_token::~binary_operator_token() { 19 | destroy_value_tok(this->left); 20 | destroy_value_tok(this->right); 21 | } 22 | 23 | unary_operator_token::unary_operator_token(token* value, unsigned char op) : token(TOKEN_UNARY_OP) { 24 | if (!is_unary_operator(op)) 25 | throw ERROR_INVALID_unary_OPERATOR; 26 | if (!is_value_tok(value)) 27 | throw ERROR_UNEXPECTED_TOKEN; 28 | this->value = value; 29 | this->op = op; 30 | } 31 | 32 | unary_operator_token::~unary_operator_token() { 33 | destroy_value_tok(this->value); 34 | } 35 | } 36 | 37 | namespace runtime { 38 | value* evaluate_binary_op(unsigned char op, value* a, value* b) { 39 | if (parsing::is_unary_operator(op)) 40 | throw ERROR_UNEXPECTED_TOKEN; 41 | switch (op) 42 | { 43 | case OP_EQUALS: 44 | return new value(VALUE_TYPE_NUMERICAL, new long double(a->compare(b) == 0 ? 1 : 0)); 45 | case OP_NOT_EQUAL: 46 | return new value(VALUE_TYPE_NUMERICAL, new long double(a->compare(b) == 0 ? 0 : 1)); 47 | case OP_MORE: 48 | return new value(VALUE_TYPE_NUMERICAL, new long double(a->compare(b) > 0 ? 1 : 0)); 49 | case OP_LESS: 50 | return new value(VALUE_TYPE_NUMERICAL, new long double(a->compare(b) < 0 ? 1 : 0)); 51 | case OP_MORE_EQUAL: 52 | return new value(VALUE_TYPE_NUMERICAL, new long double(a->compare(b) >= 0 ? 1 : 0)); 53 | case OP_LESS_EQUAL: 54 | return new value(VALUE_TYPE_NUMERICAL, new long double(a->compare(b) <= 0 ? 1 : 0)); 55 | case OP_AND: 56 | if (a->type != b->type) 57 | throw ERROR_OP_NOT_IMPLEMENTED; 58 | if (a->type != VALUE_TYPE_NUMERICAL) 59 | throw ERROR_MUST_HAVE_NUM_TYPE; 60 | return new value(VALUE_TYPE_NUMERICAL, new long double(*a->get_numerical() != 0 && *b->get_numerical() != 0 ? 1 : 0)); 61 | case OP_OR: 62 | if (a->type != b->type) 63 | throw ERROR_OP_NOT_IMPLEMENTED; 64 | if (a->type != VALUE_TYPE_NUMERICAL) 65 | throw ERROR_MUST_HAVE_NUM_TYPE; 66 | return new value(VALUE_TYPE_NUMERICAL, new long double(*a->get_numerical() != 0 || *b->get_numerical() != 0 ? 1 : 0)); 67 | case OP_ADD: 68 | if (a->type != b->type) 69 | throw ERROR_OP_NOT_IMPLEMENTED; 70 | if (a->type == VALUE_TYPE_NUMERICAL) 71 | return new value(VALUE_TYPE_NUMERICAL, new long double(*a->get_numerical() + *b->get_numerical())); 72 | /*else if (a->type == VALUE_TYPE_COLLECTION) 73 | return new value(VALUE_TYPE_COLLECTION, new collection((collection*)a->ptr, (collection*)b->ptr));*/ 74 | throw ERROR_OP_NOT_IMPLEMENTED; 75 | case OP_SUBTRACT: 76 | if (a->type != b->type) 77 | throw ERROR_OP_NOT_IMPLEMENTED; 78 | if (a->type != VALUE_TYPE_NUMERICAL) 79 | throw ERROR_MUST_HAVE_NUM_TYPE; 80 | return new value(VALUE_TYPE_NUMERICAL, new long double(*a->get_numerical() - *b->get_numerical())); 81 | case OP_MULTIPLY: 82 | if (a->type != b->type) 83 | throw ERROR_OP_NOT_IMPLEMENTED; 84 | if (a->type != VALUE_TYPE_NUMERICAL) 85 | throw ERROR_MUST_HAVE_NUM_TYPE; 86 | return new value(VALUE_TYPE_NUMERICAL, new long double(*a->get_numerical() * *b->get_numerical())); 87 | case OP_DIVIDE: 88 | if (a->type != b->type) 89 | throw ERROR_OP_NOT_IMPLEMENTED; 90 | if (a->type != VALUE_TYPE_NUMERICAL) 91 | throw ERROR_MUST_HAVE_NUM_TYPE; 92 | if (*b->get_numerical() == 0) 93 | throw ERROR_DIVIDE_BY_ZERO; 94 | return new value(VALUE_TYPE_NUMERICAL, new long double(*a->get_numerical() / *b->get_numerical())); 95 | case OP_MODULOUS: 96 | if (a->type != b->type) 97 | throw ERROR_OP_NOT_IMPLEMENTED; 98 | if (a->type != VALUE_TYPE_NUMERICAL) 99 | throw ERROR_MUST_HAVE_NUM_TYPE; 100 | return new value(VALUE_TYPE_NUMERICAL, new long double(fmod(*a->get_numerical(), *b->get_numerical()))); 101 | case OP_POWER: 102 | if (a->type != b->type) 103 | throw ERROR_OP_NOT_IMPLEMENTED; 104 | if (a->type != VALUE_TYPE_NUMERICAL) 105 | throw ERROR_MUST_HAVE_NUM_TYPE; 106 | return new value(VALUE_TYPE_NUMERICAL, new long double(pow(*a->get_numerical(), *b->get_numerical()))); 107 | default: 108 | throw ERROR_OP_NOT_IMPLEMENTED; 109 | } 110 | } 111 | 112 | value* evaluate_unary_op(unsigned char op, value* a) { 113 | if (!parsing::is_unary_operator(op)) 114 | throw ERROR_UNEXPECTED_TOKEN; 115 | switch (op) { 116 | case OP_INVERT: 117 | /*if (a->type != VALUE_TYPE_NUMERICAL) 118 | throw ERROR_MUST_HAVE_NUM_TYPE;*/ 119 | return new value(VALUE_TYPE_NUMERICAL, new long double(a->hash() == 0 ? 1 : 0)); 120 | case OP_NEGATE: 121 | if (a->type != VALUE_TYPE_NUMERICAL) 122 | throw ERROR_MUST_HAVE_NUM_TYPE; 123 | return new value(VALUE_TYPE_NUMERICAL, new long double(-*a->get_numerical())); 124 | case OP_INCRIMENT: { 125 | if (a->type != VALUE_TYPE_NUMERICAL) 126 | throw ERROR_MUST_HAVE_NUM_TYPE; 127 | long double* old_ptr = a->get_numerical(); 128 | long double* new_ptr = new long double(*old_ptr + 1); 129 | a->ptr = new_ptr; 130 | return new value(VALUE_TYPE_NUMERICAL, old_ptr); 131 | } 132 | case OP_DECRIMENT: { 133 | if (a->type != VALUE_TYPE_NUMERICAL) 134 | throw ERROR_MUST_HAVE_NUM_TYPE; 135 | long double* old_ptr = a->get_numerical(); 136 | long double* new_ptr = new long double(*old_ptr - 1); 137 | a->ptr = new_ptr; 138 | return new value(VALUE_TYPE_NUMERICAL, old_ptr); 139 | } 140 | default: 141 | throw ERROR_OP_NOT_IMPLEMENTED; 142 | } 143 | } 144 | } 145 | } -------------------------------------------------------------------------------- /src/operators.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef OPERATORS_H 4 | #define OPERATORS_H 5 | 6 | #include "tokens.h" 7 | #include "value.h" 8 | #include "references.h" 9 | 10 | //operator types 11 | #define OP_AND 0 + STD_OP_TOK_OFFSET 12 | #define OP_OR 1 + STD_OP_TOK_OFFSET 13 | 14 | #define OP_EQUALS 10 + STD_OP_TOK_OFFSET 15 | #define OP_NOT_EQUAL 11 + STD_OP_TOK_OFFSET 16 | #define OP_LESS 12 + STD_OP_TOK_OFFSET 17 | #define OP_MORE 13 + STD_OP_TOK_OFFSET 18 | #define OP_LESS_EQUAL 14 + STD_OP_TOK_OFFSET 19 | #define OP_MORE_EQUAL 15 + STD_OP_TOK_OFFSET 20 | 21 | #define OP_ADD 20 + STD_OP_TOK_OFFSET 22 | #define OP_SUBTRACT 21 + STD_OP_TOK_OFFSET 23 | 24 | #define OP_MULTIPLY 30 + STD_OP_TOK_OFFSET 25 | #define OP_DIVIDE 31 + STD_OP_TOK_OFFSET 26 | #define OP_MODULOUS 32 + STD_OP_TOK_OFFSET 27 | 28 | #define OP_POWER 40 + STD_OP_TOK_OFFSET 29 | 30 | #define OP_INVERT 50 + STD_OP_TOK_OFFSET 31 | #define OP_NEGATE 51 + STD_OP_TOK_OFFSET 32 | #define OP_INCRIMENT 52 + STD_OP_TOK_OFFSET 33 | #define OP_DECRIMENT 53 + STD_OP_TOK_OFFSET 34 | 35 | namespace fastcode { 36 | namespace parsing { 37 | struct binary_operator_token : token { 38 | token* left; 39 | token* right; 40 | unsigned char op; 41 | binary_operator_token(token* left, token* right, unsigned char op); 42 | ~binary_operator_token(); 43 | 44 | void print(); 45 | }; 46 | 47 | struct unary_operator_token : token { 48 | token* value; 49 | unsigned char op; 50 | 51 | unary_operator_token(token* value, unsigned char op); 52 | ~unary_operator_token(); 53 | 54 | void print(); 55 | }; 56 | 57 | inline unsigned char get_operator_precedence(char op_type) { 58 | return (op_type - STD_OP_TOK_OFFSET) / 10; 59 | } 60 | 61 | inline bool is_op_tok(unsigned char op_type) { 62 | return op_type >= STD_OP_TOK_OFFSET && op_type <= STD_OP_TOK_OFFSET + 53; 63 | } 64 | 65 | inline bool is_binary_operator(unsigned char op_type) { 66 | return (op_type - STD_OP_TOK_OFFSET) < 50; 67 | } 68 | 69 | inline bool is_unary_operator(unsigned char op_type) { 70 | return (op_type - STD_OP_TOK_OFFSET) >= 50; 71 | } 72 | } 73 | 74 | namespace runtime { 75 | value* evaluate_binary_op(unsigned char op, value* a, value* b); 76 | value* evaluate_unary_op(unsigned char op, value* a); 77 | } 78 | } 79 | #endif // !OPERATORS_H -------------------------------------------------------------------------------- /src/references.cpp: -------------------------------------------------------------------------------- 1 | #include "errors.h" 2 | #include "references.h" 3 | 4 | namespace fastcode { 5 | namespace runtime { 6 | reference_apartment::reference_apartment(class value* value, reference_apartment* next_apartmen) 7 | { 8 | this->value = value; 9 | this->next_apartment = next_apartment; 10 | this->references = 0; 11 | } 12 | 13 | reference_apartment::~reference_apartment() { 14 | delete value; 15 | } 16 | 17 | void reference_apartment::add_reference() { 18 | this->references++; 19 | unsigned int children_count = 0; 20 | reference_apartment** children = get_children(&children_count); 21 | if (children != nullptr) { 22 | for (unsigned int i = 0; i < children_count; i++) 23 | { 24 | children[i]->add_reference(); 25 | } 26 | } 27 | } 28 | 29 | void reference_apartment::add_parent_references(reference_apartment* parent) { 30 | this->references += parent->references; 31 | unsigned int children_count = 0; 32 | reference_apartment** children = get_children(&children_count); 33 | if (children != nullptr) { 34 | for (unsigned int i = 0; i < children_count; i++) 35 | { 36 | children[i]->add_parent_references(parent); 37 | } 38 | } 39 | } 40 | 41 | void reference_apartment::remove_reference() { 42 | if (this->references == 0) 43 | throw ERROR_CANNOT_DEREFERENCE; 44 | this->references--; 45 | unsigned int children_count = 0; 46 | reference_apartment** children = get_children(&children_count); 47 | if (children != nullptr) { 48 | for (unsigned int i = 0; i < children_count; i++) 49 | { 50 | children[i]->remove_reference(); 51 | } 52 | } 53 | } 54 | 55 | void reference_apartment::remove_parent_references(reference_apartment* parent) { 56 | if (parent->references > this->references) 57 | throw ERROR_CANNOT_DEREFERENCE; 58 | this->references -= parent->references; 59 | unsigned int children_count = 0; 60 | reference_apartment** children = get_children(&children_count); 61 | if (children != nullptr) { 62 | for (unsigned int i = 0; i < children_count; i++) 63 | { 64 | children[i]->remove_parent_references(parent); 65 | } 66 | } 67 | } 68 | 69 | void reference_apartment::set_value(class value* value) { 70 | unsigned int children_count = 0; 71 | reference_apartment** children = get_children(&children_count); 72 | if (children != nullptr) { 73 | for (unsigned int i = 0; i < children_count; i++) 74 | { 75 | if (this->references > 0) 76 | children[i]->remove_parent_references(this); 77 | } 78 | } 79 | delete this->value; 80 | this->value = value; 81 | children = get_children(&children_count); 82 | if (children != nullptr) { 83 | for (unsigned int i = 0; i < children_count; i++) 84 | { 85 | if (this->references > 0) 86 | children[i]->add_parent_references(this); 87 | } 88 | } 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /src/references.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef REFERENCE_H 4 | #define REFERENCE_H 5 | 6 | #include "value.h" 7 | 8 | namespace fastcode { 9 | namespace runtime { 10 | class reference_apartment { 11 | private: 12 | unsigned int references; 13 | reference_apartment* next_apartment; 14 | 15 | //gets the TOP level children, does NOT get it's childrens children 16 | reference_apartment** get_children(unsigned int* children_size); 17 | 18 | friend class garbage_collector; 19 | public: 20 | value* value; 21 | 22 | reference_apartment(class value* value, reference_apartment* next_apartment = nullptr); 23 | ~reference_apartment(); 24 | 25 | //adds a reference to it and all it's child reference apartments 26 | void add_reference(); 27 | 28 | //adds a reference to it and all it's child reference apartments, adds the amount of parent references 29 | void add_parent_references(reference_apartment* parent); 30 | 31 | //adds a reference to it and all it's child reference apartments 32 | void remove_reference(); 33 | 34 | //adds a reference to it and all it's child reference apartments, removes the amount of parent references 35 | void remove_parent_references(reference_apartment* parent); 36 | 37 | //checks whether the reference apartment can be deleted 38 | inline bool can_delete() { 39 | return this->references == 0; 40 | } 41 | 42 | //sets the reference apartments value 43 | void set_value(class value* value); 44 | }; 45 | } 46 | } 47 | 48 | #endif // !REFERENCE_H 49 | -------------------------------------------------------------------------------- /src/runtime.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "collection.h" 3 | #include "operators.h" 4 | #include "garbage.h" 5 | #include "runtime.h" 6 | #include "hash.h" 7 | 8 | //built in top-level functions 9 | #include "types.h" 10 | #include "io.h" 11 | #include "linq.h" 12 | 13 | namespace fastcode { 14 | namespace runtime { 15 | interpreter::call_frame::call_frame(parsing::function_prototype* prototype, class garbage_collector* garbage_collector) { 16 | this->prototype = prototype; 17 | this->garbage_collector = garbage_collector; 18 | garbage_collector->new_frame(); 19 | this->manager = new variable_manager(garbage_collector); 20 | } 21 | 22 | interpreter::call_frame::~call_frame() { 23 | delete manager; 24 | garbage_collector->sweep(true); 25 | } 26 | 27 | interpreter::interpreter(bool multi_sweep) { 28 | this->multi_sweep = false; 29 | this->break_mode = false; 30 | static_var_manager = new variable_manager(&garbage_collector); 31 | call_stack.push(new call_frame(nullptr, &garbage_collector)); 32 | new_constant("true", new value(VALUE_TYPE_NUMERICAL, new long double(1))); 33 | new_constant("false", new value(VALUE_TYPE_NUMERICAL, new long double(0))); 34 | new_constant("null", new value(VALUE_TYPE_NULL, nullptr)); 35 | new_constant("numtype", new value(VALUE_TYPE_CHAR, new char(VALUE_TYPE_NUMERICAL))); 36 | new_constant("chartype", new value(VALUE_TYPE_CHAR, new char(VALUE_TYPE_CHAR))); 37 | new_constant("coltype", new value(VALUE_TYPE_CHAR, new char(VALUE_TYPE_COLLECTION))); 38 | new_constant("structtype", new value(VALUE_TYPE_CHAR, new char(VALUE_TYPE_STRUCT))); 39 | import_func("typeof", builtins::get_type); 40 | import_func("num", builtins::to_numerical); 41 | import_func("str", builtins::to_string); 42 | import_func("char", builtins::to_char); 43 | import_func("print", builtins::print); 44 | import_func("printl", builtins::print_line); 45 | import_func("input", builtins::get_input); 46 | import_func("array", builtins::allocate_array); 47 | import_func("len", builtins::get_length); 48 | import_func("range", builtins::get_range); 49 | import_func("handle", builtins::get_handle); 50 | import_func("setprop", builtins::set_struct_property); 51 | import_func("hash", builtins::get_hash); 52 | import_func("abort", builtins::abort_program); 53 | import_func("system", builtins::system_call); 54 | import_func("read@file", builtins::file_read_text); 55 | import_func("write@file", builtins::file_write_text); 56 | import_func("count@linq", builtins::count_instances); 57 | } 58 | 59 | interpreter::~interpreter() { 60 | delete call_stack.top(); 61 | call_stack.pop(); 62 | delete static_var_manager; 63 | 64 | for (auto it = this->function_definitions.begin(); it != this->function_definitions.end(); ++it) { 65 | delete (*it).second; 66 | } 67 | 68 | for (auto it = this->struct_definitions.begin(); it != this->struct_definitions.end(); ++it) { 69 | delete (*it).second; 70 | } 71 | } 72 | 73 | long double interpreter::run(const char* source, bool interactive_mode) { 74 | parsing::lexer* lexer = nullptr; 75 | std::list to_execute; 76 | try { 77 | lexer = new parsing::lexer(source, std::strlen(source), &lexer_state); 78 | to_execute = lexer->tokenize(interactive_mode); 79 | delete lexer; 80 | } 81 | catch (int syntax_err) { 82 | //handle syntax error 83 | last_error = syntax_err; 84 | handle_syntax_err(syntax_err, lexer == nullptr ? 0 : lexer->get_pos(), source); 85 | 86 | delete lexer; 87 | return -1; 88 | } 89 | 90 | this->break_mode = false; 91 | 92 | value_eval* ret_val; 93 | bool err = false; 94 | try { 95 | ret_val = execute_block(to_execute); 96 | if (break_mode) { 97 | throw ERROR_UNEXPECTED_BREAK; 98 | } 99 | } 100 | catch (int runtime_error) { 101 | last_error = runtime_error; 102 | 103 | std::stack toprint; 104 | //cleanup 105 | while (call_stack.size() > 1) 106 | { 107 | toprint.push(call_stack.top()->prototype); 108 | delete call_stack.top(); 109 | call_stack.pop(); 110 | } 111 | 112 | print_call_stack(toprint); 113 | handle_runtime_err(runtime_error, err_tok); 114 | 115 | ret_val = nullptr; 116 | err = true; 117 | } 118 | 119 | if(multi_sweep) 120 | garbage_collector.sweep(false); 121 | 122 | for (auto it = to_execute.begin(); it != to_execute.end(); ++it) 123 | if(!tok_internalized(*it)) 124 | delete* it; 125 | 126 | if (err) 127 | return -abs(last_error); 128 | if (ret_val == nullptr) 129 | return 0; 130 | long double exit_code = *ret_val->get_value()->get_numerical(); 131 | delete ret_val; 132 | return exit_code; 133 | } 134 | 135 | void interpreter::include(const char* file_path) { 136 | unsigned long path_hash = insecure_hash(file_path); 137 | if (included_files.count(path_hash)) { 138 | return; 139 | } 140 | included_files.insert(path_hash); 141 | 142 | std::ifstream infile(file_path, std::ifstream::binary); 143 | if (!infile.is_open()) { 144 | included_files.erase(path_hash); 145 | throw ERROR_CANNOT_INCLUDE_FILE; 146 | } 147 | infile.seekg(0, std::ios::end); 148 | int buffer_length = infile.tellg(); 149 | infile.seekg(0, std::ios::beg); 150 | char* buffer = new char[buffer_length + 1]; 151 | infile.read(buffer, buffer_length); 152 | infile.close(); 153 | buffer[buffer_length] = 0; 154 | long rc = run(buffer, false); 155 | delete[] buffer; 156 | included_files.erase(path_hash); 157 | if (rc != 0) 158 | throw ERROR_CANNOT_INCLUDE_FILE; 159 | } 160 | 161 | void interpreter::set_ref(parsing::variable_access_token* access, reference_apartment* reference) { 162 | if (access->modifiers.size() == 1) { 163 | if (static_var_manager->has_var(access->get_identifier())) 164 | static_var_manager->set_var_reference(access->get_identifier(), reference); 165 | else if (call_stack.top()->manager->has_var(access->get_identifier())) 166 | call_stack.top()->manager->set_var_reference(access->get_identifier(), reference); 167 | else 168 | throw ERROR_UNRECOGNIZED_VARIABLE; 169 | } 170 | else { 171 | reference_apartment* current; 172 | if (static_var_manager->has_var(access->get_identifier())) 173 | current = static_var_manager->get_var_reference(access->get_identifier()); 174 | else if (call_stack.top()->manager->has_var(access->get_identifier())) 175 | current = call_stack.top()->manager->get_var_reference(access->get_identifier()); 176 | else 177 | throw ERROR_UNRECOGNIZED_VARIABLE; 178 | for (auto i = ++access->modifiers.begin(); i != access->modifiers.end(); ++i) { 179 | if (i == --access->modifiers.end()) { 180 | if ((*i)->type == TOKEN_IDENTIFIER) { 181 | parsing::identifier_token* prop = (parsing::identifier_token*)(*i); 182 | if (current->value->type != VALUE_TYPE_STRUCT) 183 | throw ERROR_MUST_HAVE_STRUCT_TYPE; 184 | structure* parent = (structure*)current->value->ptr; 185 | parent->set_reference(prop, reference); 186 | } 187 | else if ((*i)->type == TOKEN_INDEX) { 188 | parsing::index_token* index = (parsing::index_token*)(*i); 189 | if (current->value->type != VALUE_TYPE_COLLECTION) 190 | throw ERROR_MUST_HAVE_COLLECTION_TYPE; 191 | collection* parent = (collection*)current->value->ptr; 192 | value_eval* index_eval = evaluate(index->value, false); 193 | if (index_eval->get_value()->type != VALUE_TYPE_NUMERICAL) 194 | throw ERROR_MUST_HAVE_NUM_TYPE; 195 | unsigned long index_ul = (unsigned long)*(long double*)index_eval->get_value()->ptr; 196 | parent->set_reference(index_ul, reference); 197 | delete index_eval; 198 | } 199 | } 200 | else { 201 | if ((*i)->type == TOKEN_IDENTIFIER) { 202 | parsing::identifier_token* prop = (parsing::identifier_token*)(*i); 203 | if (current->value->type != VALUE_TYPE_STRUCT) 204 | throw ERROR_MUST_HAVE_STRUCT_TYPE; 205 | structure* parent = (structure*)current->value->ptr; 206 | current = parent->get_reference(prop); 207 | } 208 | else if ((*i)->type == TOKEN_INDEX) { 209 | parsing::index_token* index = (parsing::index_token*)(*i); 210 | if (current->value->type != VALUE_TYPE_COLLECTION) 211 | throw ERROR_MUST_HAVE_COLLECTION_TYPE; 212 | collection* parent = (collection*)current->value->ptr; 213 | value_eval* index_eval = evaluate(index->value, false); 214 | if (index_eval->get_value()->type != VALUE_TYPE_NUMERICAL) 215 | throw ERROR_MUST_HAVE_NUM_TYPE; 216 | unsigned long index_ul = (unsigned long)*(long double*)index_eval->get_value()->ptr; 217 | current = parent->get_reference(index_ul); 218 | delete index_eval; 219 | } 220 | } 221 | } 222 | } 223 | } 224 | 225 | reference_apartment* interpreter::get_ref(parsing::variable_access_token* access) { 226 | reference_apartment* current; 227 | if (static_var_manager->has_var(access->get_identifier())) 228 | current = static_var_manager->get_var_reference(access->get_identifier()); 229 | else if (call_stack.top()->manager->has_var(access->get_identifier())) 230 | current = call_stack.top()->manager->get_var_reference(access->get_identifier()); 231 | else 232 | throw ERROR_UNRECOGNIZED_VARIABLE; 233 | for (auto i = ++access->modifiers.begin(); i != access->modifiers.end(); ++i) 234 | { 235 | if ((*i)->type == TOKEN_IDENTIFIER) { 236 | parsing::identifier_token* prop = (parsing::identifier_token*)(*i); 237 | if (current->value->type != VALUE_TYPE_STRUCT) 238 | throw ERROR_MUST_HAVE_STRUCT_TYPE; 239 | structure* parent = (structure*)current->value->ptr; 240 | current = parent->get_reference(prop); 241 | } 242 | else if ((*i)->type == TOKEN_INDEX) { 243 | parsing::index_token* index = (parsing::index_token*)(*i); 244 | if (current->value->type != VALUE_TYPE_COLLECTION) 245 | throw ERROR_MUST_HAVE_COLLECTION_TYPE; 246 | collection* parent = (collection*)current->value->ptr; 247 | value_eval* index_eval = evaluate(index->value, false); 248 | if (index_eval->get_value()->type != VALUE_TYPE_NUMERICAL) 249 | throw ERROR_MUST_HAVE_NUM_TYPE; 250 | unsigned long index_ul = (unsigned long)*(long double*)index_eval->get_value()->ptr; 251 | if (index_ul >= parent->size || index < 0) 252 | throw ERROR_INDEX_OUT_OF_RANGE; 253 | current = parent->get_reference(index_ul); 254 | delete index_eval; 255 | } 256 | } 257 | return current; 258 | } 259 | 260 | interpreter::value_eval* interpreter::evaluate(parsing::token* eval_tok, bool force_reference) { 261 | switch (eval_tok->type) 262 | { 263 | case TOKEN_VAR_ACCESS: { 264 | parsing::variable_access_token* access = (parsing::variable_access_token*)eval_tok; 265 | reference_apartment* ref = get_ref(access); 266 | if (force_reference || !ref->value->is_primitive()) { 267 | return new value_eval(ref); 268 | } 269 | return new value_eval(ref->value->clone()); 270 | } 271 | case TOKEN_GET_REFERENCE: { 272 | parsing::get_reference_token* get_ref_tok = (parsing::get_reference_token*)eval_tok; 273 | return new value_eval(get_ref(get_ref_tok->var_access)); 274 | } 275 | case TOKEN_VALUE: { 276 | parsing::value_token* val_tok = (parsing::value_token*)eval_tok; 277 | return new value_eval(val_tok->get_value()); 278 | } 279 | case TOKEN_CREATE_STRUCT: { 280 | parsing::create_struct_token* create_struct = (parsing::create_struct_token*)eval_tok; 281 | if (!struct_definitions.count(create_struct->identifier->id_hash)) 282 | throw ERROR_STRUCT_PROTO_NOT_DEFINED; 283 | structure* created_struct = new structure(struct_definitions[create_struct->identifier->id_hash], &garbage_collector); 284 | return new value_eval(created_struct->get_parent_ref()); 285 | } 286 | case TOKEN_CREATE_ARRAY: { 287 | parsing::create_array_token* create_array = (parsing::create_array_token*)eval_tok; 288 | collection* col = new collection(create_array->values.size(), &garbage_collector); 289 | unsigned int i = 0; 290 | for (auto it = create_array->values.begin(); it != create_array->values.end(); ++it) 291 | { 292 | value_eval* item_eval = evaluate(*it, false); 293 | if (item_eval->type == VALUE_EVAL_TYPE_REF) 294 | col->set_reference(i++, item_eval->get_reference()); 295 | else { 296 | item_eval->keep(); 297 | col->set_value(i++, item_eval->get_value()); 298 | } 299 | delete item_eval; 300 | } 301 | return new value_eval(col->get_parent_ref()); 302 | } 303 | case TOKEN_SET: { 304 | parsing::set_token* set_tok = (parsing::set_token*)eval_tok; 305 | value_eval* eval = evaluate(set_tok->value, false); 306 | if (set_tok->destination->modifiers.size() == 1) { 307 | if (set_tok->create_static) { 308 | if (static_var_manager->has_var(set_tok->destination->get_identifier())) 309 | throw ERROR_UNEXPECTED_TOKEN; 310 | if (eval->type == VALUE_EVAL_TYPE_REF) 311 | static_var_manager->declare_var(set_tok->destination->get_identifier(), eval->get_reference()); 312 | else { 313 | eval->keep(); 314 | static_var_manager->declare_var(set_tok->destination->get_identifier(), eval->get_value()); 315 | } 316 | } 317 | else { 318 | if (call_stack.top()->manager->has_var(set_tok->destination->get_identifier())) { 319 | if (eval->type == VALUE_EVAL_TYPE_REF) 320 | call_stack.top()->manager->set_var_reference(set_tok->destination->get_identifier(), eval->get_reference()); 321 | else { 322 | eval->keep(); 323 | call_stack.top()->manager->set_var_value(set_tok->destination->get_identifier(), eval->get_value()); 324 | } 325 | } 326 | else if (static_var_manager->has_var(set_tok->destination->get_identifier())) { 327 | if (eval->type == VALUE_EVAL_TYPE_REF) 328 | static_var_manager->set_var_reference(set_tok->destination->get_identifier(), eval->get_reference()); 329 | else { 330 | eval->keep(); 331 | static_var_manager->set_var_value(set_tok->destination->get_identifier(), eval->get_value()); 332 | } 333 | } 334 | else { 335 | if (eval->type == VALUE_EVAL_TYPE_REF) { 336 | call_stack.top()->manager->declare_var(set_tok->destination->get_identifier(), eval->get_reference()); 337 | } 338 | else { 339 | eval->keep(); 340 | call_stack.top()->manager->declare_var(set_tok->destination->get_identifier(), eval->get_value()); 341 | } 342 | } 343 | } 344 | } 345 | else { 346 | if (eval->type == VALUE_EVAL_TYPE_REF) { 347 | set_ref(set_tok->destination, eval->get_reference()); 348 | } 349 | else { 350 | eval->keep(); 351 | set_val(set_tok->destination, eval->get_value()); 352 | } 353 | } 354 | if (eval->type == VALUE_EVAL_TYPE_VAL) { 355 | value* eval_clone = eval->get_value()->clone(); 356 | delete eval; 357 | return new value_eval(eval_clone); 358 | } 359 | else { 360 | return eval; 361 | } 362 | } 363 | case TOKEN_BINARY_OP: { 364 | parsing::binary_operator_token* binop = (parsing::binary_operator_token*)eval_tok; 365 | value_eval* a_eval = evaluate(binop->left, false); 366 | if (binop->type == OP_AND) { 367 | if (*a_eval->get_value()->get_numerical() == 0) { 368 | delete a_eval; 369 | return new value_eval(new value(VALUE_TYPE_NUMERICAL, new long double(1))); 370 | } 371 | else { 372 | delete a_eval; 373 | return new value_eval(new value(VALUE_TYPE_NUMERICAL, new long double(0))); 374 | } 375 | } 376 | else if (binop->type == OP_OR) { 377 | if (*a_eval->get_value()->get_numerical() != 0) 378 | delete a_eval; 379 | return new value_eval(new value(VALUE_TYPE_NUMERICAL, new long double(1))); 380 | } 381 | value_eval* b_eval = evaluate(binop->right, false); 382 | value_eval* result; 383 | if (a_eval->get_value()->type == VALUE_TYPE_COLLECTION && b_eval->get_value()->type == VALUE_TYPE_COLLECTION && binop->op == OP_ADD) { 384 | reference_apartment* appartment = garbage_collector.new_apartment(nullptr); 385 | collection* c = new collection((collection*)a_eval->get_value()->ptr, (collection*)b_eval->get_value()->ptr, appartment); 386 | appartment->value = new value(VALUE_TYPE_COLLECTION, c); 387 | return new value_eval(appartment); 388 | } 389 | else { 390 | result = new value_eval(evaluate_binary_op(binop->op, a_eval->get_value(), b_eval->get_value())); 391 | } 392 | delete a_eval; 393 | delete b_eval; 394 | return result; 395 | } 396 | case TOKEN_UNARY_OP: { 397 | parsing::unary_operator_token* uniop = (parsing::unary_operator_token*)eval_tok; 398 | value_eval* a_eval = evaluate(uniop->value, true); 399 | value_eval* result = new value_eval(evaluate_unary_op(uniop->op, a_eval->get_value())); 400 | delete a_eval; 401 | return result; 402 | } 403 | case TOKEN_FUNCTION_CALL: { 404 | parsing::token* old_err_tok = err_tok; 405 | parsing::function_call_token* func_call = (parsing::function_call_token*)eval_tok; 406 | if (function_definitions.count(func_call->identifier->id_hash)) { 407 | parsing::function_prototype* to_execute = function_definitions[func_call->identifier->id_hash]; 408 | call_frame* new_frame = new call_frame(to_execute, &garbage_collector); 409 | if (to_execute->params_mode) { 410 | unsigned int i = 0; 411 | collection* param_args = new collection(func_call->arguments.size(), &garbage_collector); 412 | for (auto arg_val_it = func_call->arguments.begin(); arg_val_it != func_call->arguments.end(); ++arg_val_it) { 413 | value_eval* arg_eval = evaluate(*arg_val_it, true); 414 | if (arg_eval->type == VALUE_EVAL_TYPE_REF) 415 | param_args->set_reference(i++, arg_eval->get_reference()); 416 | else { 417 | arg_eval->keep(); 418 | param_args->set_value(i++, arg_eval->get_value()); 419 | } 420 | delete arg_eval; 421 | } 422 | new_frame->manager->declare_var(to_execute->argument_identifiers.front()->id_hash, param_args->get_parent_ref()); 423 | } 424 | else { 425 | if (func_call->arguments.size() != to_execute->argument_identifiers.size()) 426 | throw ERROR_UNEXPECTED_ARGUMENT_SIZE; 427 | auto arg_id_it = to_execute->argument_identifiers.begin(); 428 | for (auto arg_val_it = func_call->arguments.begin(); arg_val_it != func_call->arguments.end(); ++arg_val_it) { 429 | value_eval* arg_eval = evaluate(*arg_val_it, true); 430 | if (arg_eval->type == VALUE_EVAL_TYPE_REF) 431 | new_frame->manager->declare_var(*arg_id_it, arg_eval->get_reference()); 432 | else { 433 | arg_eval->keep(); 434 | new_frame->manager->declare_var(*arg_id_it, arg_eval->get_value()); 435 | } 436 | delete arg_eval; 437 | arg_id_it++; 438 | } 439 | } 440 | call_stack.push(new_frame); 441 | value_eval* ret_val = execute_block(to_execute->tokens); 442 | err_tok = old_err_tok; 443 | if (ret_val == nullptr) { 444 | if (break_mode) 445 | throw ERROR_UNEXPECTED_BREAK; 446 | ret_val = new value_eval(new value(VALUE_TYPE_NULL, nullptr)); 447 | } 448 | if (ret_val->type == VALUE_EVAL_TYPE_REF) 449 | ret_val->get_reference()->add_reference(); //add and incrememnt before garbage collection to preserve defer a references deletion to the callee call frame for further use 450 | delete call_stack.top(); 451 | call_stack.pop(); 452 | if (ret_val->type == VALUE_EVAL_TYPE_REF) 453 | ret_val->get_reference()->remove_reference(); 454 | return ret_val; 455 | } 456 | else if (built_in_functions.count(func_call->identifier->id_hash)) { 457 | std::vector arguments; 458 | std::list can_delete; 459 | for (auto it = func_call->arguments.begin(); it != func_call->arguments.end(); ++it) { 460 | value_eval* arg_eval = evaluate(*it, false); 461 | arguments.push_back(arg_eval->get_value()); 462 | can_delete.push_back(arg_eval->type != VALUE_EVAL_TYPE_REF); 463 | if (arg_eval->type == VALUE_EVAL_TYPE_VAL) 464 | arg_eval->keep(); 465 | delete arg_eval; 466 | } 467 | reference_apartment* val = built_in_functions[func_call->identifier->id_hash](arguments, &garbage_collector); 468 | auto del = can_delete.begin(); 469 | for (auto it = arguments.begin(); it != arguments.end(); ++it) { 470 | if (*del) 471 | delete* it; 472 | ++del; 473 | } 474 | return new value_eval(val); 475 | } 476 | throw ERROR_FUNCTION_PROTO_NOT_DEFINED; 477 | } 478 | } 479 | throw ERROR_UNEXPECTED_TOKEN; 480 | } 481 | 482 | interpreter::value_eval* interpreter::execute_block(std::list tokens) { 483 | for (auto it = tokens.begin(); it != tokens.end(); ++it) { 484 | err_tok = *it; 485 | switch ((*it)->type) 486 | { 487 | case TOKEN_BREAK: 488 | this->break_mode = true; 489 | break; 490 | case TOKEN_INCLUDE: { 491 | parsing::include_token* include_tok = (parsing::include_token*)*it; 492 | include(include_tok->get_file_path()); 493 | break; 494 | } 495 | case TOKEN_IF: 496 | case TOKEN_WHILE: { 497 | parsing::conditional_token* current = (parsing::conditional_token*)*it; 498 | while (current != nullptr) { 499 | err_tok = current; 500 | if (current->condition == nullptr) { 501 | value_eval* eval = execute_block(current->instructions); 502 | if (eval != nullptr) { 503 | if (break_mode) 504 | return nullptr; 505 | return eval; 506 | } 507 | break; 508 | } 509 | value_eval* cond_eval = evaluate(current->condition, false); 510 | if (*cond_eval->get_value()->get_numerical() == 0) 511 | current = current->get_next_conditional(false); 512 | else { 513 | value_eval* ret_eval = execute_block(current->instructions); 514 | if (ret_eval != nullptr) { 515 | delete cond_eval; 516 | return ret_eval; 517 | } 518 | else if (break_mode) { 519 | delete cond_eval; 520 | if (current->type == TOKEN_WHILE) { 521 | break_mode = false; 522 | break; 523 | } 524 | return nullptr; 525 | } 526 | current = current->get_next_conditional(true); 527 | } 528 | if(multi_sweep) 529 | garbage_collector.sweep(false); 530 | delete cond_eval; 531 | } 532 | break; 533 | } 534 | case TOKEN_FOR: { 535 | parsing::for_token* for_tok = (parsing::for_token*)*it; 536 | value_eval* to_iterate_eval = evaluate(for_tok->collection, true); 537 | if (to_iterate_eval->get_value()->type != VALUE_TYPE_COLLECTION) 538 | throw ERROR_MUST_HAVE_COLLECTION_TYPE; 539 | collection* to_iterate = (collection*)to_iterate_eval->get_value()->ptr; 540 | delete to_iterate_eval; 541 | 542 | if(!call_stack.top()->manager->has_var(for_tok->identifier)) 543 | call_stack.top()->manager->declare_var(for_tok->identifier, new value(VALUE_TYPE_NULL, nullptr)); 544 | 545 | for (unsigned int i = 0; i < to_iterate->size; i++) 546 | { 547 | call_stack.top()->manager->set_var_reference(for_tok->identifier, to_iterate->get_reference(i)); 548 | value_eval* eval = execute_block(for_tok->instructions); 549 | if (eval != nullptr) { 550 | return eval; 551 | } 552 | else if (break_mode) { 553 | break_mode = false; 554 | return nullptr; 555 | } 556 | } 557 | call_stack.top()->manager->remove_var(for_tok->identifier); 558 | break; 559 | } 560 | case TOKEN_UNARY_OP: 561 | case TOKEN_FUNCTION_CALL: 562 | case TOKEN_SET: 563 | delete evaluate(*it, false); 564 | break; 565 | case TOKEN_RETURN: { 566 | parsing::return_token* ret_tok = (parsing::return_token*)*it; 567 | return evaluate(ret_tok->value, false); 568 | } 569 | case TOKEN_FUNC_PROTO: { 570 | parsing::function_prototype* proto = (parsing::function_prototype*)*it; 571 | if (function_definitions.count(proto->identifier->id_hash)) 572 | throw ERROR_FUNCTION_PROTO_ALREADY_DEFINED; 573 | function_definitions[proto->identifier->id_hash] = proto; 574 | break; 575 | } 576 | case TOKEN_STRUCT_PROTO: { 577 | parsing::structure_prototype* proto = (parsing::structure_prototype*)*it; 578 | if (struct_definitions.count(proto->identifier->id_hash)) 579 | throw ERROR_STRUCT_PROTO_ALREADY_DEFINED; 580 | struct_definitions[proto->identifier->id_hash] = proto; 581 | break; 582 | } 583 | default: 584 | throw ERROR_UNEXPECTED_TOKEN; 585 | } 586 | } 587 | return nullptr; 588 | } 589 | } 590 | } -------------------------------------------------------------------------------- /src/runtime.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef RUNTIME_H 4 | #define RUNTIME_H 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "errors.h" 11 | #include "value.h" 12 | #include "references.h" 13 | #include "tokens.h" 14 | #include "variables.h" 15 | #include "garbage.h" 16 | #include "builtins.h" 17 | #include "structure.h" 18 | #include "lexer.h" 19 | #include "hash.h" 20 | 21 | #define VALUE_EVAL_TYPE_REF 0 22 | #define VALUE_EVAL_TYPE_VAL 1 23 | 24 | namespace fastcode { 25 | namespace runtime { 26 | class interpreter { 27 | private: 28 | class call_frame { 29 | private: 30 | garbage_collector* garbage_collector; 31 | 32 | public: 33 | variable_manager* manager; 34 | parsing::function_prototype* prototype; 35 | 36 | call_frame(parsing::function_prototype* prototype, class garbage_collector* garbage_collector); 37 | ~call_frame(); 38 | }; 39 | 40 | struct value_eval { 41 | private: 42 | bool keep_val; 43 | void* ptr; 44 | 45 | value_eval(void* ret_obj, unsigned char type, bool keep_val) { 46 | this->type = type; 47 | this->ptr = ret_obj; 48 | this->keep_val = keep_val; 49 | } 50 | 51 | public: 52 | unsigned char type; 53 | 54 | explicit value_eval(reference_apartment* reference) : value_eval(reference, VALUE_EVAL_TYPE_REF, true) {} 55 | explicit value_eval(value* value) : value_eval(value, VALUE_EVAL_TYPE_VAL, false) {} 56 | 57 | ~value_eval() { 58 | if (this->type == VALUE_EVAL_TYPE_VAL && !keep_val) 59 | delete (value*)this->ptr; 60 | } 61 | 62 | inline void keep() { 63 | this->keep_val = true; 64 | } 65 | 66 | inline reference_apartment* get_reference() { 67 | return (reference_apartment*)this->ptr; 68 | } 69 | 70 | inline value* get_value() { 71 | if (this->type == VALUE_EVAL_TYPE_REF) { 72 | return get_reference()->value; 73 | } 74 | return (value*)this->ptr; 75 | } 76 | }; 77 | 78 | variable_manager* static_var_manager; 79 | garbage_collector garbage_collector; 80 | std::stack call_stack; 81 | 82 | std::unordered_map struct_definitions; 83 | std::unordered_map function_definitions; 84 | std::unordered_map built_in_functions; 85 | 86 | std::unordered_set included_files; 87 | 88 | struct parsing::lexer::lexer_state lexer_state; 89 | 90 | bool break_mode; 91 | 92 | void set_ref(parsing::variable_access_token* access, reference_apartment* reference); 93 | reference_apartment* get_ref(parsing::variable_access_token* access); 94 | 95 | inline value* get_val(parsing::variable_access_token* access) { 96 | return get_ref(access)->value; 97 | } 98 | 99 | inline void set_val(parsing::variable_access_token* access, value* val) { 100 | get_ref(access)->set_value(val); 101 | } 102 | 103 | /* 104 | * IMPORTANT NOTE: 105 | Value eval must be returned as a pointer because C++ often messes around with when we return it. Using a pointer just keeps one singular version. 106 | */ 107 | 108 | 109 | //evaluates a reference or value 110 | value_eval* evaluate(parsing::token* token, bool force_reference); 111 | 112 | //executes a block of tokens 113 | value_eval* execute_block(std::list tokens); 114 | 115 | bool multi_sweep; 116 | 117 | inline bool tok_internalized(parsing::token* tok) { 118 | if (tok->type == TOKEN_STRUCT_PROTO) { 119 | parsing::structure_prototype* proto = (parsing::structure_prototype*)tok; 120 | return this->struct_definitions[proto->identifier->id_hash] == proto; 121 | } 122 | else if (tok->type == TOKEN_FUNC_PROTO) { 123 | parsing::function_prototype* proto = (parsing::function_prototype*)tok; 124 | return this->function_definitions[proto->identifier->id_hash] == proto; 125 | } 126 | return false; 127 | } 128 | public: 129 | int last_error; 130 | 131 | //last token, often is error token 132 | parsing::token* err_tok; 133 | 134 | interpreter(bool multi_sweep); 135 | ~interpreter(); 136 | 137 | long double run(const char* source, bool interactive_mode); 138 | 139 | void include(const char* file_path); 140 | 141 | inline void import_func(const char* identifier, builtins::built_in_function function) { 142 | unsigned long id_hash = insecure_hash(identifier); 143 | if (built_in_functions.count(id_hash)) 144 | throw ERROR_FUNCTION_PROTO_ALREADY_DEFINED; 145 | built_in_functions[id_hash] = function; 146 | } 147 | 148 | inline void import_struct(parsing::structure_prototype* struct_proto) { 149 | if (struct_definitions.count(struct_proto->identifier->id_hash)) 150 | throw ERROR_STRUCT_PROTO_ALREADY_DEFINED; 151 | struct_definitions[struct_proto->identifier->id_hash] = struct_proto; 152 | } 153 | 154 | inline void new_constant(const char* identifier, value* val) { 155 | this->lexer_state.constants[insecure_hash(identifier)] = new parsing::value_token(val); 156 | } 157 | }; 158 | } 159 | } 160 | #endif // !RUNTIME_H -------------------------------------------------------------------------------- /src/stdlib.cpp: -------------------------------------------------------------------------------- 1 | #include "structure.h" 2 | #include "collection.h" 3 | #include "runtime.h" 4 | #include "builtins.h" 5 | #include "errors.h" 6 | 7 | #include 8 | 9 | namespace fastcode { 10 | namespace builtins { 11 | runtime::reference_apartment* get_handle(const std::vector arguments, runtime::garbage_collector* gc) { 12 | match_arg_len(arguments, 1); 13 | if (arguments[0]->type == VALUE_TYPE_STRUCT) { 14 | return gc->new_apartment(new value(VALUE_TYPE_HANDLE, ((runtime::structure*)arguments[0]->ptr)->get_parent_ref())); 15 | } 16 | else if (arguments[0]->type == VALUE_TYPE_COLLECTION) { 17 | return gc->new_apartment(new value(VALUE_TYPE_HANDLE, ((runtime::collection*)arguments[0]->ptr)->get_parent_ref())); 18 | } 19 | throw ERROR_INVALID_VALUE_TYPE; 20 | } 21 | 22 | runtime::reference_apartment* set_struct_property(const std::vector arguments, runtime::garbage_collector* gc) { 23 | match_arg_len(arguments, 3); 24 | match_arg_type(arguments[0], VALUE_TYPE_STRUCT); 25 | match_arg_type(arguments[1], VALUE_TYPE_NUMERICAL); 26 | runtime::structure* structure = (runtime::structure*)arguments[0]->ptr; 27 | unsigned int property_index = unsigned int(*arguments[1]->get_numerical()); 28 | 29 | if (property_index >= structure->get_size()) 30 | throw ERROR_INVALID_VALUE_TYPE; 31 | 32 | if (arguments[2]->type == VALUE_TYPE_COLLECTION) { 33 | runtime::collection* col = (runtime::collection*)arguments[2]->ptr; 34 | structure->set_reference_at(property_index, col->get_parent_ref()); 35 | } 36 | else if (arguments[2]->type == VALUE_TYPE_STRUCT) { 37 | runtime::structure* s = (runtime::structure*)arguments[2]->ptr; 38 | structure->set_reference_at(property_index, s->get_parent_ref()); 39 | } 40 | else { 41 | structure->set_value_at(property_index, arguments[2]->clone()); 42 | } 43 | return gc->new_apartment(new value(VALUE_TYPE_NULL, nullptr)); 44 | } 45 | 46 | runtime::reference_apartment* abort_program(const std::vector arguments, runtime::garbage_collector* gc) { 47 | if (arguments.size() > 0) { 48 | std::cout << "The program was aborted with the following message:" << std::endl; 49 | for (auto i = arguments.begin(); i != arguments.end(); ++i) { 50 | if ((*i)->type == VALUE_TYPE_COLLECTION) { 51 | char* msg = to_c_str(*i); 52 | std::cout << msg; 53 | delete[] msg; 54 | } 55 | else if ((*i)->type == VALUE_TYPE_CHAR) 56 | std::cout << *(*i)->get_char(); 57 | else if ((*i)->type == VALUE_TYPE_NUMERICAL) 58 | std::cout << *(*i)->get_numerical(); 59 | } 60 | std::cout << std::endl; 61 | } 62 | throw ERROR_ABORTED; 63 | } 64 | 65 | runtime::reference_apartment* get_hash(const std::vector arguments, runtime::garbage_collector* gc) { 66 | match_arg_len(arguments, 1); 67 | return gc->new_apartment(new value(VALUE_TYPE_NUMERICAL, new double((unsigned int)arguments[0]->hash()))); 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/structure.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef STRUCT_H 4 | #define STRUCT_H 5 | 6 | #include 7 | #include 8 | #include "tokens.h" 9 | #include "references.h" 10 | #include "garbage.h" 11 | 12 | namespace fastcode { 13 | namespace parsing { 14 | class structure_prototype : public token { 15 | private: 16 | std::unordered_map property_indicies; 17 | std::list properties; 18 | public: 19 | 20 | identifier_token* identifier; 21 | 22 | unsigned int property_count; 23 | 24 | structure_prototype(const char* identifier, const char* properties[], unsigned int property_count); 25 | structure_prototype(identifier_token* identifier, std::list properties); 26 | ~structure_prototype(); 27 | 28 | //gets the index of a property 29 | inline unsigned int get_index(unsigned long id_hash) { 30 | if (!this->property_indicies.count(id_hash)) 31 | throw ERROR_PROPERTY_NOT_FOUND; 32 | return this->property_indicies[id_hash]; 33 | } 34 | 35 | //gets the index of a property 36 | inline unsigned int get_index(identifier_token* identifier) { 37 | return get_index(identifier->id_hash); 38 | } 39 | 40 | inline std::list get_properties() { 41 | return this->properties; 42 | } 43 | 44 | void print(); 45 | }; 46 | } 47 | 48 | namespace runtime { 49 | class structure { 50 | private: 51 | reference_apartment* parent_reference; 52 | parsing::structure_prototype* prototype; 53 | reference_apartment** properties; 54 | structure(parsing::structure_prototype* prototype, reference_apartment* parent_reference); 55 | 56 | public: 57 | structure(parsing::structure_prototype* prototype, garbage_collector* gc); 58 | ~structure(); 59 | 60 | //sets the reference of a property 61 | void set_reference(unsigned long id_hash, reference_apartment* reference); 62 | 63 | //sets a reference at a property's index 64 | void set_reference_at(unsigned int index, reference_apartment* reference); 65 | 66 | structure* clone(reference_apartment* new_parent_appt); 67 | 68 | int hash(); 69 | 70 | inline parsing::identifier_token* get_identifier() { 71 | return this->prototype->identifier; 72 | } 73 | 74 | //sets the reference of a property 75 | inline void set_reference(parsing::identifier_token* identifier, reference_apartment* reference) { 76 | set_reference(identifier->id_hash, reference); 77 | } 78 | 79 | //sets the value of a property 80 | inline void set_value(unsigned long id_hash, value* value) { 81 | this->properties[this->prototype->get_index(id_hash)]->set_value(value); 82 | } 83 | 84 | inline void set_value_at(unsigned int index, value* value) { 85 | this->properties[index]->set_value(value); 86 | } 87 | 88 | //sets the value of a property 89 | inline void set_value(parsing::identifier_token* identifier, value* value) { 90 | set_value(identifier->id_hash, value); 91 | } 92 | 93 | //gets the reference of a property 94 | inline reference_apartment* get_reference(unsigned long id_hash) { 95 | return this->properties[this->prototype->get_index(id_hash)]; 96 | } 97 | 98 | //gets the reference of a property 99 | inline reference_apartment* get_reference(parsing::identifier_token* identifier) { 100 | return get_reference(identifier->id_hash); 101 | } 102 | 103 | //gets the value of a property 104 | inline value* get_value(unsigned long id_hash) { 105 | return get_reference(id_hash)->value; 106 | } 107 | 108 | //gets the value of a property 109 | inline value* get_value(parsing::identifier_token* identifier) { 110 | return get_value(identifier->id_hash); 111 | } 112 | 113 | inline unsigned int get_size() { 114 | return this->prototype->property_count; 115 | } 116 | 117 | inline reference_apartment** get_children() { 118 | return this->properties; 119 | } 120 | 121 | inline reference_apartment* get_parent_ref() { 122 | return this->parent_reference; 123 | } 124 | 125 | inline parsing::structure_prototype* get_proto() { 126 | return this->prototype; 127 | } 128 | }; 129 | } 130 | } 131 | #endif // !STRUCT_H -------------------------------------------------------------------------------- /src/tokens.cpp: -------------------------------------------------------------------------------- 1 | #include "errors.h" 2 | #include "hash.h" 3 | #include "tokens.h" 4 | #include "operators.h" 5 | 6 | namespace fastcode { 7 | namespace parsing { 8 | inline bool is_control_tok(unsigned char type) { 9 | return type >= 65 && type < 70; 10 | } 11 | 12 | inline bool is_control_tok(token* control_structure) { 13 | return is_control_tok(control_structure->type); 14 | } 15 | 16 | void destroy_value_tok(token* val_tok) { 17 | switch (val_tok->type) 18 | { 19 | case TOKEN_VALUE: 20 | delete (value_token*)val_tok; 21 | break; 22 | case TOKEN_VAR_ACCESS: 23 | delete (variable_access_token*)val_tok; 24 | break; 25 | case TOKEN_FUNCTION_CALL: 26 | delete (function_call_token*)val_tok; 27 | break; 28 | case TOKEN_BINARY_OP: 29 | delete (binary_operator_token*)val_tok; 30 | break; 31 | case TOKEN_UNARY_OP: 32 | delete (unary_operator_token*)val_tok; 33 | break; 34 | case TOKEN_GET_REFERENCE: 35 | delete (get_reference_token*)val_tok; 36 | break; 37 | case TOKEN_CREATE_ARRAY: 38 | delete (create_array_token*)val_tok; 39 | break; 40 | case TOKEN_CREATE_STRUCT: 41 | delete (create_struct_token*)val_tok; 42 | break; 43 | case TOKEN_SET: 44 | delete (set_token*)val_tok; 45 | break; 46 | default: 47 | throw ERROR_UNEXPECTED_TOKEN; 48 | } 49 | } 50 | 51 | void destroy_top_lvl_tok(token* token) { 52 | switch (token->type) 53 | { 54 | case TOKEN_INCLUDE: 55 | delete (include_token*)token; 56 | break; 57 | case TOKEN_SET: 58 | delete (set_token*)token; 59 | break; 60 | case TOKEN_FUNCTION_CALL: 61 | delete (function_call_token*)token; 62 | break; 63 | case TOKEN_RETURN: 64 | delete (return_token*)token; 65 | break; 66 | case TOKEN_UNARY_OP: 67 | delete (unary_operator_token*)token; 68 | break; 69 | case TOKEN_IF: 70 | case TOKEN_ELIF: 71 | case TOKEN_ELSE: 72 | case TOKEN_WHILE: 73 | delete (conditional_token*)token; 74 | break; 75 | case TOKEN_FOR: 76 | delete (for_token*)token; 77 | break; 78 | case TOKEN_BREAK: 79 | delete token; 80 | break; 81 | default: 82 | throw ERROR_UNEXPECTED_TOKEN; 83 | } 84 | } 85 | 86 | token::token(unsigned char type) { 87 | this->type = type; 88 | } 89 | 90 | value_token::value_token(class value* value) : token(TOKEN_VALUE) { 91 | this->inner_value_ptr = value; 92 | } 93 | 94 | value_token::~value_token() { 95 | delete inner_value_ptr; 96 | } 97 | 98 | identifier_token::identifier_token(const char* identifier) : identifier_token((char*)identifier, insecure_hash(identifier), false) { 99 | 100 | } 101 | 102 | identifier_token::identifier_token(char* identifier, unsigned long id_hash, bool delete_id) : token(TOKEN_IDENTIFIER) { 103 | this->id_str_ptr = identifier; 104 | this->id_hash = id_hash; 105 | this->delete_id = delete_id; 106 | } 107 | 108 | identifier_token::~identifier_token() { 109 | if (delete_id) 110 | delete[] id_str_ptr; 111 | } 112 | 113 | variable_access_token::variable_access_token(const std::list modifiers) : token(TOKEN_VAR_ACCESS) { 114 | this->modifiers = modifiers; 115 | if (this->modifiers.size() < 1) 116 | throw ERROR_INVALID_ACCESSOR_MODIFIERS; 117 | if (this->modifiers.front()->type != TOKEN_IDENTIFIER) 118 | throw ERROR_INVALID_ACCESSOR_MODIFIERS; 119 | for (auto i = this->modifiers.begin(); i != this->modifiers.end(); ++i) { 120 | if ((*i)->type != TOKEN_IDENTIFIER && (*i)->type != TOKEN_INDEX) 121 | throw ERROR_UNEXPECTED_TOKEN; 122 | } 123 | 124 | } 125 | 126 | variable_access_token::~variable_access_token() { 127 | for (auto i = this->modifiers.begin(); i != this->modifiers.end(); ++i) 128 | { 129 | token* to_delete = *i; 130 | if (to_delete->type == TOKEN_IDENTIFIER) 131 | delete (identifier_token*)to_delete; 132 | else if (to_delete->type == TOKEN_INDEX) 133 | delete (index_token*)to_delete; 134 | else 135 | throw ERROR_UNEXPECTED_TOKEN; 136 | } 137 | } 138 | 139 | index_token::index_token(token* value) : token(TOKEN_INDEX) { 140 | if (!is_value_tok(value)) 141 | throw ERROR_UNEXPECTED_TOKEN; 142 | this->value = value; 143 | } 144 | 145 | index_token::~index_token() { 146 | destroy_value_tok(this->value); 147 | } 148 | 149 | get_reference_token::get_reference_token(variable_access_token* var_access) :token(TOKEN_GET_REFERENCE) { 150 | this->var_access = var_access; 151 | } 152 | 153 | get_reference_token::~get_reference_token() { 154 | delete var_access; 155 | } 156 | 157 | set_token::set_token(variable_access_token* destination, token* value, bool create_static) : token(TOKEN_SET) { 158 | this->destination = destination; 159 | this->create_static = create_static; 160 | if (is_value_tok(value)) 161 | this->value = value; 162 | else 163 | throw ERROR_UNEXPECTED_TOKEN; 164 | } 165 | 166 | set_token::~set_token() { 167 | delete destination; 168 | destroy_value_tok(this->value); 169 | } 170 | 171 | function_call_token::function_call_token(identifier_token* identifier, const std::list arguments) : token(TOKEN_FUNCTION_CALL) { 172 | this->identifier = identifier; 173 | this->arguments = arguments; 174 | for (auto i = this->arguments.begin(); i != this->arguments.end(); ++i) 175 | if (!is_value_tok(*i)) 176 | throw ERROR_UNEXPECTED_TOKEN; 177 | } 178 | 179 | function_call_token::~function_call_token() { 180 | delete identifier; 181 | for (auto i = this->arguments.begin(); i != this->arguments.end(); ++i) 182 | destroy_value_tok(*i); 183 | } 184 | 185 | return_token::return_token(token* value) : token(TOKEN_RETURN) { 186 | if (!is_value_tok(value)) 187 | throw ERROR_UNEXPECTED_TOKEN; 188 | this->value = value; 189 | } 190 | 191 | return_token::~return_token() { 192 | destroy_value_tok(this->value); 193 | } 194 | 195 | conditional_token::conditional_token(unsigned char type, token* condition, const std::list instructions, conditional_token* next) : token(type) { 196 | if (!is_control_tok(type) || (type != TOKEN_ELSE && !is_value_tok(condition))) 197 | throw ERROR_UNEXPECTED_TOKEN; 198 | this->condition = condition; 199 | for (auto i = instructions.begin(); i != instructions.end(); ++i) 200 | if (!is_top_level_tok(*i)) 201 | throw ERROR_UNEXPECTED_TOKEN; 202 | this->instructions = instructions; 203 | if (next != nullptr && type == TOKEN_ELSE) 204 | throw ERROR_UNEXPECTED_TOKEN; 205 | this->next = next; 206 | } 207 | 208 | conditional_token::~conditional_token() { 209 | if (this->condition != nullptr) 210 | destroy_value_tok(this->condition); 211 | if (this->next != nullptr) 212 | delete this->next; 213 | for (auto i = this->instructions.begin(); i != this->instructions.end(); ++i) 214 | destroy_top_lvl_tok(*i); 215 | } 216 | 217 | conditional_token* conditional_token::get_next_conditional(bool condition_success) { 218 | if (this->condition == nullptr) 219 | return nullptr; 220 | switch (this->type) 221 | { 222 | case TOKEN_IF: 223 | case TOKEN_ELIF: 224 | return condition_success ? nullptr : this->next; 225 | case TOKEN_WHILE: 226 | return condition_success ? this : nullptr; 227 | } 228 | throw ERROR_UNRECOGNIZED_TOKEN; 229 | } 230 | 231 | for_token::for_token(identifier_token* identifier, token* collection, const std::list instructions) : token(TOKEN_FOR){ 232 | this->collection = collection; 233 | this->identifier = identifier; 234 | this->instructions = instructions; 235 | } 236 | 237 | for_token::~for_token() { 238 | destroy_value_tok(this->collection); 239 | delete this->identifier; 240 | for (auto i = instructions.begin(); i != instructions.end(); ++i) 241 | destroy_top_lvl_tok(*i); 242 | } 243 | 244 | create_array_token::create_array_token(const std::list values) : token(TOKEN_CREATE_ARRAY) { 245 | for (auto i = values.begin(); i != values.end(); ++i) 246 | if (!is_value_tok(*i)) 247 | throw ERROR_UNEXPECTED_TOKEN; 248 | this->values = values; 249 | } 250 | 251 | create_array_token::~create_array_token() { 252 | for (auto i = this->values.begin(); i != this->values.end(); ++i) 253 | destroy_value_tok(*i); 254 | } 255 | 256 | create_struct_token::create_struct_token(identifier_token* identifier) : token(TOKEN_CREATE_STRUCT) { 257 | this->identifier = identifier; 258 | } 259 | 260 | create_struct_token::~create_struct_token() { 261 | delete this->identifier; 262 | } 263 | 264 | function_prototype::function_prototype(identifier_token* identifier, const std::list argument_identifiers, const std::list tokens, bool params_mode) : token(TOKEN_FUNC_PROTO) { 265 | this->identifier = identifier; 266 | this->argument_identifiers = argument_identifiers; 267 | this->tokens = tokens; 268 | this->params_mode = params_mode; 269 | for (auto i = this->tokens.begin(); i != this->tokens.end(); ++i) 270 | if (!is_top_level_tok(*i)) 271 | throw ERROR_UNEXPECTED_TOKEN; 272 | 273 | } 274 | 275 | function_prototype::~function_prototype() { 276 | delete identifier; 277 | for (auto i = this->argument_identifiers.begin(); i != this->argument_identifiers.end(); ++i) 278 | delete* i; 279 | for (auto i = this->tokens.begin(); i != this->tokens.end(); ++i) 280 | destroy_top_lvl_tok(*i); 281 | } 282 | 283 | include_token::include_token(char* file_path) : token(TOKEN_INCLUDE) { 284 | this->file_path = file_path; 285 | } 286 | 287 | include_token::~include_token() { 288 | delete this->file_path; 289 | } 290 | } 291 | } -------------------------------------------------------------------------------- /src/tokens.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef TOKENS_H 4 | #define TOKENS_H 5 | 6 | #include 7 | #include "errors.h" 8 | #include "value.h" 9 | #include "hash.h" 10 | 11 | //value and accessor tokens 0-4 12 | #define TOKEN_VALUE 0 13 | #define TOKEN_IDENTIFIER 1 14 | 15 | #define TOKEN_VAR_ACCESS 2 16 | #define TOKEN_INDEX 3 17 | 18 | #define TOKEN_GET_REFERENCE 4 19 | 20 | //operator tokens 5 - 59 21 | #define TOKEN_BINARY_OP 5 22 | #define TOKEN_UNARY_OP 6 23 | 24 | #define STD_OP_TOK_OFFSET 7 //offset for individual operator tokens/ids 25 | 26 | //top-level instruction tokens 60-69 27 | //instruction tokens 61-64 28 | #define TOKEN_SET 61 29 | #define TOKEN_FUNCTION_CALL 62 30 | #define TOKEN_RETURN 63 31 | #define TOKEN_BREAK 64 32 | 33 | //control structure tokens 65-69 34 | #define TOKEN_IF 65 35 | #define TOKEN_ELIF 66 36 | #define TOKEN_ELSE 67 37 | #define TOKEN_WHILE 68 38 | #define TOKEN_FOR 69 39 | 40 | //hardcoded create tokens 41 | #define TOKEN_CREATE_ARRAY 70 42 | #define TOKEN_CREATE_STRUCT 71 43 | 44 | #define TOKEN_STRUCT_PROTO 72 45 | #define TOKEN_FUNC_PROTO 73 46 | 47 | #define TOKEN_INCLUDE 74 48 | 49 | #define MAX_TOKEN_LIMIT 75 50 | 51 | namespace fastcode { 52 | namespace parsing { 53 | struct token { 54 | unsigned char type; 55 | explicit token(unsigned char type); 56 | }; 57 | 58 | struct value_token :token { 59 | public: 60 | value_token(class value* value); 61 | ~value_token(); 62 | 63 | inline value* get_value() { 64 | return this->inner_value_ptr->clone(); 65 | } 66 | 67 | void print(); 68 | private: 69 | value* inner_value_ptr; 70 | }; 71 | 72 | struct identifier_token : token { 73 | public: 74 | unsigned long id_hash; 75 | 76 | explicit identifier_token(const char* identifier); 77 | identifier_token(char* identifier, unsigned long id_hash, bool delete_id = true); 78 | ~identifier_token(); 79 | 80 | inline void no_delete() { 81 | this->delete_id = false; 82 | } 83 | 84 | inline const char* get_identifier() { 85 | return (const char*)this->id_str_ptr; 86 | } 87 | 88 | inline void set_c_str(char* id_str) { 89 | if (delete_id) 90 | delete[] this->id_str_ptr; 91 | delete_id = true; 92 | this->id_str_ptr = id_str; 93 | this->id_hash = insecure_hash(id_str); 94 | } 95 | 96 | void print(); 97 | private: 98 | char* id_str_ptr; 99 | bool delete_id; 100 | }; 101 | 102 | struct variable_access_token : token { 103 | std::list modifiers; 104 | 105 | variable_access_token(const std::list modifiers); 106 | ~variable_access_token(); 107 | 108 | inline identifier_token* get_identifier() { 109 | return (identifier_token*)modifiers.front(); 110 | } 111 | 112 | void print(); 113 | }; 114 | 115 | struct index_token : token { 116 | token* value; 117 | 118 | explicit index_token(token* value); 119 | ~index_token(); 120 | 121 | void print(); 122 | }; 123 | 124 | struct get_reference_token :token { 125 | variable_access_token* var_access; 126 | 127 | explicit get_reference_token(variable_access_token* var_access); 128 | ~get_reference_token(); 129 | 130 | void print(); 131 | }; 132 | 133 | struct set_token :token { 134 | bool create_static; 135 | variable_access_token* destination; 136 | token* value; 137 | 138 | set_token(variable_access_token* destination, token* value, bool create_static); 139 | ~set_token(); 140 | 141 | void print(); 142 | }; 143 | 144 | struct function_call_token :token { 145 | identifier_token* identifier; 146 | std::list arguments; 147 | 148 | function_call_token(identifier_token* identifier, const std::list arguments); 149 | ~function_call_token(); 150 | 151 | void print(); 152 | }; 153 | 154 | struct return_token :token { 155 | token* value; 156 | 157 | explicit return_token(token* value); 158 | ~return_token(); 159 | 160 | void print(); 161 | }; 162 | 163 | struct conditional_token :token { 164 | token* condition; 165 | std::list instructions; 166 | conditional_token* next; 167 | 168 | conditional_token(unsigned char type, token* condition, const std::list instructions, conditional_token* next); 169 | ~conditional_token(); 170 | 171 | conditional_token* get_next_conditional(bool condition_val); 172 | void print(int indent = 0); 173 | }; 174 | 175 | struct for_token : token { 176 | token* collection; 177 | identifier_token* identifier; 178 | std::list instructions; 179 | 180 | for_token(identifier_token* identifier, token* collection, const std::list instructions); 181 | ~for_token(); 182 | 183 | void print(int indent = 0); 184 | }; 185 | 186 | struct create_array_token :token { 187 | std::list values; 188 | 189 | create_array_token(const std::list values); 190 | ~create_array_token(); 191 | 192 | void print(); 193 | }; 194 | 195 | struct create_struct_token :token { 196 | identifier_token* identifier; 197 | 198 | explicit create_struct_token(identifier_token* identifier); 199 | ~create_struct_token(); 200 | 201 | void print(); 202 | }; 203 | 204 | struct function_prototype :token { 205 | identifier_token* identifier; 206 | std::list argument_identifiers; 207 | std::list tokens; 208 | bool params_mode; 209 | 210 | function_prototype(identifier_token* identifier, const std::list argument_identifiers, const std::list tokens, bool params_mode); 211 | ~function_prototype(); 212 | 213 | void print(); 214 | }; 215 | 216 | struct include_token :token { 217 | public: 218 | explicit include_token(char* file_path); 219 | ~include_token(); 220 | 221 | inline const char* get_file_path() { 222 | return this->file_path; 223 | } 224 | 225 | void print(); 226 | private: 227 | char* file_path; 228 | }; 229 | 230 | inline bool is_top_level_tok(token* token) { 231 | return (token->type > 60 && token->type < 70) || token->type == TOKEN_UNARY_OP || token->type == TOKEN_INCLUDE; 232 | } 233 | 234 | void destroy_top_lvl_tok(token* token); 235 | void destroy_value_tok(token* val_tok); 236 | 237 | inline bool is_value_tok(token* value) { 238 | return value->type == TOKEN_VALUE || value->type == TOKEN_VAR_ACCESS || value->type == TOKEN_FUNCTION_CALL || value->type == TOKEN_UNARY_OP || value->type == TOKEN_BINARY_OP || value->type == TOKEN_GET_REFERENCE || value->type == TOKEN_CREATE_ARRAY || value->type == TOKEN_CREATE_STRUCT || value->type == TOKEN_SET || value->type == TOKEN_RETURN; 239 | } 240 | } 241 | } 242 | 243 | #endif // !TOKENS_H -------------------------------------------------------------------------------- /src/types.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "collection.h" 3 | #include "builtins.h" 4 | #include "types.h" 5 | 6 | namespace fastcode { 7 | namespace builtins { 8 | runtime::reference_apartment* get_type(const std::vector arguments, runtime::garbage_collector* gc) { 9 | match_arg_len(arguments, 1); 10 | return gc->new_apartment(new value(VALUE_TYPE_CHAR, new char(arguments.front()->type))); 11 | } 12 | 13 | runtime::reference_apartment* to_string(const std::vector arguments, runtime::garbage_collector* gc) { 14 | match_arg_len(arguments, 1); 15 | match_arg_type(arguments[0], VALUE_TYPE_NUMERICAL); 16 | long double num = *arguments[0]->get_numerical(); 17 | 18 | std::string str = std::to_string(num); 19 | 20 | runtime::collection* strcol = from_c_str(str.c_str(), gc); 21 | return strcol->get_parent_ref(); 22 | } 23 | 24 | runtime::reference_apartment* to_numerical(const std::vector arguments, runtime::garbage_collector* gc) { 25 | match_arg_len(arguments, 1); 26 | if (arguments[0]->type == VALUE_TYPE_COLLECTION) { 27 | runtime::collection* strcol = (runtime::collection*)arguments[0]->ptr; 28 | char* str = to_c_str(arguments[0]); 29 | 30 | long double num = std::strtold(str, NULL); 31 | delete[] str; 32 | 33 | return gc->new_apartment(new value(VALUE_TYPE_NUMERICAL, new long double(num))); 34 | } 35 | else if (arguments[0]->type == VALUE_TYPE_CHAR) { 36 | return gc->new_apartment(new value(VALUE_TYPE_NUMERICAL, new long double(*arguments[0]->get_char()))); 37 | } 38 | throw ERROR_INVALID_VALUE_TYPE; 39 | } 40 | 41 | runtime::reference_apartment* to_char(const std::vector arguments, runtime::garbage_collector* gc) { 42 | match_arg_len(arguments, 1); 43 | match_arg_type(arguments[0], VALUE_TYPE_NUMERICAL); 44 | return gc->new_apartment(new value(VALUE_TYPE_CHAR, new char(*arguments[0]->get_numerical()))); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef TYPES_H 4 | #define TYPES_H 5 | 6 | #include 7 | #include "garbage.h" 8 | #include "references.h" 9 | #include "value.h" 10 | 11 | namespace fastcode { 12 | namespace builtins { 13 | runtime::reference_apartment* get_type(const std::vector arguments, runtime::garbage_collector* gc); 14 | runtime::reference_apartment* to_string(const std::vector arguments, runtime::garbage_collector* gc); 15 | runtime::reference_apartment* to_numerical(const std::vector arguments, runtime::garbage_collector* gc); 16 | runtime::reference_apartment* to_char(const std::vector arguments, runtime::garbage_collector* gc); 17 | } 18 | } 19 | 20 | #endif // !TYPES_H -------------------------------------------------------------------------------- /src/value.cpp: -------------------------------------------------------------------------------- 1 | #include "errors.h" 2 | #include "value.h" 3 | 4 | #define MAX_VALUE_TYPE 5 5 | 6 | namespace fastcode { 7 | value::value(char type, void* ptr) { 8 | if (type > MAX_VALUE_TYPE) { 9 | throw ERROR_INVALID_VALUE_TYPE; 10 | } 11 | this->type = type; 12 | this->ptr = ptr; 13 | } 14 | 15 | value* value::clone() { 16 | value* clone = new value(this->type, nullptr); 17 | switch (this->type) 18 | { 19 | case VALUE_TYPE_NULL: 20 | break; 21 | case VALUE_TYPE_CHAR: 22 | clone->ptr = new char(*(char*)this->ptr); 23 | break; 24 | case VALUE_TYPE_NUMERICAL: 25 | clone->ptr = new long double(*(long double*)this->ptr); 26 | break; 27 | case VALUE_TYPE_HANDLE: 28 | clone->ptr = this->ptr; 29 | break; 30 | default: 31 | throw ERROR_INVALID_VALUE_TYPE; 32 | } 33 | return clone; 34 | } 35 | } -------------------------------------------------------------------------------- /src/value.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef VALUE_H 4 | #define VALUE_H 5 | 6 | #define VALUE_TYPE_NULL 0 7 | #define VALUE_TYPE_CHAR 1 8 | #define VALUE_TYPE_NUMERICAL 2 9 | #define VALUE_TYPE_HANDLE 3 10 | #define VALUE_TYPE_COLLECTION 4 11 | #define VALUE_TYPE_STRUCT 5 12 | 13 | namespace fastcode { 14 | class value { 15 | public: 16 | char type; 17 | void* ptr; 18 | value(char type, void* ptr); 19 | ~value(); 20 | 21 | //do not call unless value is primitive 22 | value* clone(); 23 | 24 | int hash(); 25 | 26 | inline int compare(value* b) { 27 | if (this->type == VALUE_TYPE_NULL) 28 | return b->type == VALUE_TYPE_NULL ? 0 : 1; 29 | else if (b->type == VALUE_TYPE_NULL) 30 | return this->type == VALUE_TYPE_NULL ? 0 : 1; 31 | return this->hash() - b->hash(); 32 | } 33 | 34 | //gets the ptr as numerical if ptr is a numerical 35 | inline long double* get_numerical() { 36 | return (long double*)ptr; 37 | } 38 | 39 | inline bool is_primitive() { 40 | return this->type < VALUE_TYPE_COLLECTION; 41 | } 42 | 43 | //gets the ptr as char if ptr is a char 44 | inline char* get_char() { 45 | return (char*)ptr; 46 | } 47 | }; 48 | } 49 | 50 | #endif // !VALUE_H -------------------------------------------------------------------------------- /src/variables.cpp: -------------------------------------------------------------------------------- 1 | #include "errors.h" 2 | #include "hash.h" 3 | #include "variables.h" 4 | 5 | namespace fastcode { 6 | namespace runtime { 7 | variable_manager::variable_bucket::variable_bucket(unsigned long id_hash, reference_apartment* apartment, variable_bucket* next_bucket) { 8 | this->id_hash = id_hash; 9 | this->apartment = apartment; 10 | this->next_bucket = next_bucket; 11 | } 12 | 13 | variable_manager::variable_manager(class garbage_collector* garbage_collector) { 14 | this->size = 0; 15 | this->garbage_collector = garbage_collector; 16 | for (unsigned int i = 0; i < VARIABLE_HASH_BUCKET_SIZE; i++) 17 | { 18 | hash_buckets[i] = nullptr; 19 | } 20 | } 21 | 22 | variable_manager::~variable_manager() { 23 | for (unsigned int i = 0; i < VARIABLE_HASH_BUCKET_SIZE; i++) 24 | { 25 | if (hash_buckets[i] != nullptr) { 26 | variable_bucket* current = hash_buckets[i]; 27 | while (current != nullptr) 28 | { 29 | variable_bucket* to_delete = current; 30 | to_delete->apartment->remove_reference(); 31 | current = current->next_bucket; 32 | delete to_delete; 33 | } 34 | } 35 | } 36 | } 37 | 38 | reference_apartment* variable_manager::declare_var(unsigned long id_hash, reference_apartment* reference) { 39 | variable_bucket* bucket = hash_buckets[id_hash % VARIABLE_HASH_BUCKET_SIZE]; 40 | variable_bucket* parent = nullptr; 41 | while (bucket != nullptr) { 42 | if (bucket->id_hash == id_hash) 43 | throw ERROR_VARIABLE_ALREADY_DEFINED; 44 | parent = bucket; 45 | bucket = bucket->next_bucket; 46 | } 47 | reference->add_reference(); 48 | if (parent == nullptr) 49 | hash_buckets[id_hash % VARIABLE_HASH_BUCKET_SIZE] = new variable_bucket(id_hash, reference); 50 | else 51 | parent->next_bucket = new variable_bucket(id_hash, reference); 52 | size++; 53 | return reference; 54 | } 55 | 56 | void variable_manager::remove_var(unsigned long id_hash) { 57 | variable_bucket* bucket = hash_buckets[id_hash % VARIABLE_HASH_BUCKET_SIZE]; 58 | variable_bucket* parent = nullptr; 59 | while (bucket != nullptr) 60 | { 61 | if (bucket->id_hash == id_hash) { 62 | break; 63 | } 64 | parent = bucket; 65 | bucket = bucket->next_bucket; 66 | } 67 | if (bucket == nullptr) 68 | throw ERROR_VARIABLE_NOT_DEFINED; 69 | if (parent == nullptr) 70 | hash_buckets[id_hash % VARIABLE_HASH_BUCKET_SIZE] = bucket->next_bucket; 71 | else 72 | parent->next_bucket = bucket->next_bucket; 73 | bucket->apartment->remove_reference(); 74 | delete bucket; 75 | size--; 76 | } 77 | 78 | bool variable_manager::has_var(unsigned long id_hash) 79 | { 80 | variable_bucket* bucket = hash_buckets[id_hash % VARIABLE_HASH_BUCKET_SIZE]; 81 | while (bucket != nullptr) { 82 | if (bucket->id_hash == id_hash) 83 | return true; 84 | bucket = bucket->next_bucket; 85 | } 86 | return false; 87 | } 88 | 89 | void variable_manager::set_var_reference(unsigned long id_hash, reference_apartment* reference) { 90 | variable_bucket* bucket = hash_buckets[id_hash % VARIABLE_HASH_BUCKET_SIZE]; 91 | while (bucket != nullptr) { 92 | if (bucket->id_hash == id_hash) { 93 | bucket->apartment->remove_reference(); 94 | reference->add_reference(); 95 | bucket->apartment = reference; 96 | return; 97 | } 98 | bucket = bucket->next_bucket; 99 | } 100 | throw ERROR_VARIABLE_NOT_DEFINED; 101 | } 102 | 103 | reference_apartment* variable_manager::get_var_reference(unsigned long id_hash) { 104 | variable_bucket* bucket = hash_buckets[id_hash % VARIABLE_HASH_BUCKET_SIZE]; 105 | while (bucket != nullptr) { 106 | if (bucket->id_hash == id_hash) { 107 | return bucket->apartment; 108 | } 109 | bucket = bucket->next_bucket; 110 | } 111 | throw ERROR_VARIABLE_NOT_DEFINED; 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /src/variables.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef VARIABLE_H 4 | #define VARIABLE_H 5 | 6 | #include "tokens.h" 7 | #include "errors.h" 8 | #include "value.h" 9 | #include "references.h" 10 | #include "garbage.h" 11 | 12 | #define VARIABLE_HASH_BUCKET_SIZE 100 13 | 14 | namespace fastcode { 15 | namespace runtime { 16 | class variable_manager { 17 | private: 18 | class variable_bucket { 19 | public: 20 | unsigned long id_hash; 21 | reference_apartment* apartment; 22 | variable_bucket(unsigned long id_hash, reference_apartment* apartment, variable_bucket* next_bucket = nullptr); 23 | variable_bucket* next_bucket; 24 | }; 25 | 26 | unsigned int size; 27 | garbage_collector* garbage_collector; 28 | variable_bucket* hash_buckets[VARIABLE_HASH_BUCKET_SIZE]; 29 | 30 | public: 31 | variable_manager(class garbage_collector* garbage_collector); 32 | ~variable_manager(); 33 | 34 | //declares a variable with a value 35 | inline reference_apartment* declare_var(unsigned long id_hash, value* value) { 36 | return declare_var(id_hash, garbage_collector->new_apartment(value)); 37 | } 38 | 39 | //declares a variable with a value 40 | inline reference_apartment* declare_var(parsing::identifier_token* identifier, value* value) { 41 | return declare_var(identifier->id_hash, value); 42 | } 43 | 44 | //declares a variable with a reference 45 | reference_apartment* declare_var(unsigned long id_hash, reference_apartment* reference); 46 | 47 | //declares a variable with a reference 48 | inline reference_apartment* declare_var(parsing::identifier_token* identifier, reference_apartment* reference) { 49 | return declare_var(identifier->id_hash, reference); 50 | } 51 | 52 | //removes a variable 53 | void remove_var(unsigned long id_hash); 54 | 55 | //removes a variable 56 | inline void remove_var(parsing::identifier_token* identifier) { 57 | remove_var(identifier->id_hash); 58 | } 59 | 60 | //checks whether a variable exists 61 | bool has_var(unsigned long id_hash); 62 | 63 | //checks whether a variable exists 64 | inline bool has_var(parsing::identifier_token* identifier) { 65 | return has_var(identifier->id_hash); 66 | } 67 | 68 | //sets a variable to a reference 69 | void set_var_reference(unsigned long id_hash, reference_apartment* reference); 70 | 71 | //sets a variable to a reference 72 | inline void set_var_reference(parsing::identifier_token* identifier, reference_apartment* reference) { 73 | set_var_reference(identifier->id_hash, reference); 74 | } 75 | 76 | //sets a variable to a value 77 | inline void set_var_value(unsigned long id_hash, value* value) { 78 | get_var_reference(id_hash)->set_value(value); 79 | } 80 | 81 | //sets a variable to a value 82 | inline void set_var_value(parsing::identifier_token* identifier, value* value) { 83 | get_var_reference(identifier->id_hash)->set_value(value); 84 | } 85 | 86 | //gets a variable's reference 87 | reference_apartment* get_var_reference(unsigned long id_hash); 88 | 89 | //gets a variable's reference 90 | inline reference_apartment* get_var_reference(parsing::identifier_token* identifier) { 91 | return get_var_reference(identifier->id_hash); 92 | } 93 | 94 | //gets a variable's value 95 | inline value* get_var_value(unsigned long id_hash) { 96 | return get_var_reference(id_hash)->value; 97 | } 98 | 99 | //gets a variable's value 100 | inline value* get_var_value(parsing::identifier_token* identifier) { 101 | return get_var_reference(identifier->id_hash)->value; 102 | } 103 | }; 104 | } 105 | } 106 | 107 | #endif // !VARIABLE_H -------------------------------------------------------------------------------- /stl/char.txt: -------------------------------------------------------------------------------- 1 | group char 2 | 3 | static alpha_chars_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 4 | static alpha_chars_lower = "abcdefghijklmnopqrstuvwxyz" 5 | static alpha_chars = alpha_chars_lower + alpha_chars_upper 6 | static num_chars = "0123456789" 7 | 8 | proc char_to_upper(char) { 9 | i = len(alpha_chars_lower) 10 | while i-- { 11 | if alpha_chars_lower[i] == char => break 12 | } 13 | if i == -1 => return 0 14 | return alpha_chars_upper[i] 15 | } 16 | 17 | proc char_to_lower(char) { 18 | i = len(alpha_chars_upper) 19 | while i-- { 20 | if alpha_chars_upper[i] == char => break 21 | } 22 | if i == -1 => return 0 23 | return alpha_chars_lower[i] 24 | } 25 | 26 | proc to_upper(str) { 27 | for char in str { 28 | upper = char_to_upper(char) 29 | if upper => char = upper 30 | } 31 | return str 32 | } 33 | 34 | proc to_lower(str) { 35 | for char in str { 36 | lower = char_to_lower(char) 37 | if lower => char = lower 38 | } 39 | return str 40 | } 41 | 42 | proc is_alpha(char) { 43 | for check_char in alpha_chars { 44 | if check_char == char => return true 45 | } 46 | return false 47 | } 48 | 49 | proc is_num(char) { 50 | for check_char in num_chars { 51 | if check_char == char => return true 52 | } 53 | return false 54 | } 55 | 56 | proc is_alpha_num(char) => return is_alpha(char) or is_num(char) 57 | 58 | endgroup -------------------------------------------------------------------------------- /stl/csv.txt: -------------------------------------------------------------------------------- 1 | include "strlib.txt" 2 | include "char.txt" 3 | 4 | group csv 5 | 6 | group read 7 | 8 | proc read_num(lexer) => 9 | return num(read_while@strlib(lexer, 10 | num_chars@char + ['.'] 11 | )) 12 | 13 | proc read_quoted(lexer) { 14 | if lexer.last_char == '\"' => 15 | read_char@strlib(lexer) 16 | else => 17 | abort("Expected a open-quotation") 18 | quoted_str = read_till_c@strlib(lexer, ['\"']) 19 | read_char@strlib(lexer) 20 | return quoted_str 21 | } 22 | 23 | endgroup 24 | 25 | group write 26 | 27 | proc write_num(builder, num) => 28 | append@strlib(builder, str(num)) 29 | 30 | proc write_quoted(str){ 31 | append_c@strlib(builder, '\"') 32 | append@strlib(builder, str) 33 | append_c@strlib(builder, '\"') 34 | } 35 | 36 | endgroup 37 | 38 | proc from_str(str) { 39 | lexer = lexer@strlib(str) 40 | 41 | rows = [] 42 | current_col = [] 43 | 44 | while !eos@strlib(lexer) { 45 | read_while@strlib(lexer, [' ', '\t']) 46 | if is_alpha@char(lexer.last_char) => 47 | current_col = current_col + [read_till@strlib(lexer, [',', '\n'])] 48 | elif is_num@char(lexer.last_char) => 49 | current_col = current_col + [read_num@read@csv(lexer)] 50 | elif lexer.last_char == '\"' => 51 | current_col = current_col + [read_quoted@read@csv(lexer)] 52 | read_till_c@strlib(lexer, [',','\n']) 53 | if lexer.last_char == '\r' => 54 | read_char@strlib(lexer) 55 | if lexer.last_char == '\n' { 56 | rows = rows + [current_col] 57 | current_col = [] 58 | } 59 | elif eos@strlib(lexer) => 60 | break 61 | elif lexer.last_char != ',' => 62 | abort("Expected a comma. Got ", lexer.last_char, " instead.") 63 | read_char@strlib(lexer) 64 | } 65 | rows = rows + [current_col] 66 | return rows 67 | } 68 | 69 | proc to_str(table) { 70 | builder = builder@strlib() 71 | comma = false 72 | newline = false 73 | for row in table { 74 | if newline => 75 | append_c@strlib(builder, '\n') 76 | else => 77 | newline = true 78 | for col in row { 79 | if comma => 80 | append_c@strlib(builder, ',') 81 | else => 82 | comma = true 83 | if typeof(col) == coltype => 84 | if count@linq(col, ',') or count@linq(col, ' ') or count@linq(col, '\t') => 85 | write_quoted@write@csv(builder, col) 86 | else => 87 | append@strlib(builder, col) 88 | elif typeof(col) == numtype => 89 | write_num@write@csv(builder, col) 90 | else => 91 | abort("Expected num or string type, got ", typeof(col), " instead.") 92 | } 93 | comma = false 94 | } 95 | return build@strlib(builder) 96 | } 97 | 98 | endgroup -------------------------------------------------------------------------------- /stl/fcon.txt: -------------------------------------------------------------------------------- 1 | rem FastCode Object Notation 2 | rem Written by Michael Wang, 2020-2021 3 | 4 | rem FCON converts FCON strings to FastCode objects. 5 | 6 | include "strlib.txt" 7 | 8 | group fcon 9 | 10 | proc read_obj(lexer) { 11 | type = read_tok@strlib(lexer, whitespace@strlib) 12 | if type == "NUM" => return num(read_tok@strlib(lexer, whitespace@strlib)) 13 | elif type == "CHAR" { 14 | char_tok = read_tok@strlib(lexer, whitespace@strlib) 15 | if char_tok == "!SPACE" => return ' ' 16 | elif char_tok == "!NEWLINE" => return '\n' 17 | elif char_tok == "!TAB" => return '\t' 18 | else => return char_tok[0] 19 | } 20 | elif type == "COL" { 21 | len = num(read_tok@strlib(lexer, whitespace@strlib)) 22 | col = array(len) 23 | i = 0 24 | while i < len => col[i++] = read_obj(lexer) 25 | return col 26 | } 27 | } 28 | 29 | proc write_obj(builder, obj) { 30 | if typeof(obj) == numtype { 31 | append@strlib(builder, "NUM") 32 | append_char@strlib(builder, ' ') 33 | append@strlib(builder, str(obj)) 34 | } 35 | elif typeof(obj) == chartype { 36 | append@strlib(builder, "CHAR") 37 | append_char@strlib(builder, ' ') 38 | if obj == ' ' => append@strlib(builder, "!SPACE") 39 | elif obj == '\n' => append@strlib(builder, "!NEWLINE") 40 | elif obj == '\t' => append@strlib(builder, "!TAB") 41 | else => append_char@strlib(builder, obj) 42 | } 43 | elif typeof(obj) == coltype { 44 | append@strlib(builder, "COL") 45 | append_char@strlib(builder, ' ') 46 | len = len(obj) 47 | append@strlib(builder, str(len)) 48 | i = 0 49 | while i < len { 50 | append_char@strlib(builder, ' ') 51 | write_obj(builder, obj[i++]) 52 | } 53 | } 54 | } 55 | 56 | proc obj(str) { 57 | lexer = lexer@strlib(str) 58 | return read_obj(lexer) 59 | } 60 | 61 | endgroup 62 | 63 | proc fcon(obj) { 64 | builder = builder@strlib() 65 | write_obj@fcon(builder, obj) 66 | return build@strlib(builder) 67 | } -------------------------------------------------------------------------------- /stl/list.txt: -------------------------------------------------------------------------------- 1 | rem FastCode, written by Michael Wang 2020-2021 2 | 3 | struct list { 4 | head 5 | tail 6 | size 7 | } 8 | 9 | proc list() { 10 | list = new list 11 | list.size = 0 12 | return list 13 | } 14 | 15 | group list 16 | 17 | struct node { 18 | value 19 | next 20 | } 21 | 22 | proc node(value, next) { 23 | node = new node 24 | node.value = value 25 | node.next = next 26 | return node 27 | } 28 | 29 | proc push_front(list, value) { 30 | if list.size == 0 => 31 | list.head = list.tail = node(value, null) 32 | else => 33 | list.head = node(value, list.head) 34 | list.size++ 35 | } 36 | 37 | proc push_back(list, value) { 38 | if list.size == 0 => 39 | list.head = list.tail = node(value, null) 40 | else { 41 | new_node = node(value, null) 42 | list.tail.next = new_node 43 | list.tail = new_node 44 | } 45 | list.size++ 46 | } 47 | 48 | proc pop_front(list) { 49 | value = ref list.head.value 50 | if list.size <= 1 => 51 | list.head = list.tail = null 52 | else => 53 | list.head = list.head.next 54 | size-- 55 | return value 56 | } 57 | 58 | proc iterate(list) { 59 | to_iterate = array(list.size) 60 | current = list.head 61 | i = 0 62 | while current != null { 63 | to_iterate[i++] = ref current.value 64 | current = ref current.next 65 | } 66 | return to_iterate 67 | } 68 | 69 | endgroup -------------------------------------------------------------------------------- /stl/map.txt: -------------------------------------------------------------------------------- 1 | group map 2 | 3 | endgroup -------------------------------------------------------------------------------- /stl/pattern.txt: -------------------------------------------------------------------------------- 1 | group pattern 2 | 3 | rem checks if a pattern matches at a specified index 4 | proc match(base, i, pat) { 5 | l = len(pat) 6 | if i + l > len(base) => return false 7 | while l-- { 8 | if base[i + l] != pat[l] => return false 9 | } 10 | return true 11 | } 12 | 13 | rem checks if a collection contains a specified sub-pattern 14 | proc contains(base, pattern) { 15 | i = len(base) - len(pattern) 16 | while i-- { 17 | if match(base, i, pattern) => return true 18 | } 19 | return false 20 | } 21 | 22 | rem checks if a collection has a begins with a specified pattern 23 | proc begins(base, pattern) => return match(base, 0, pattern) 24 | 25 | rem checks if a collection ends with a specified pattern 26 | proc ends(base, pattern) { 27 | if len(pattern) > len(base) => return false 28 | return match(base, len(base) - len(pattern), pattern) 29 | } 30 | 31 | endgroup -------------------------------------------------------------------------------- /stl/set.txt: -------------------------------------------------------------------------------- 1 | rem FastCode hashset implementation 2 | 3 | const hashsize = 1000 4 | 5 | struct set { 6 | buckets 7 | } 8 | 9 | proc set() { 10 | set = new set 11 | set.buckets = array(hashsize) 12 | return set 13 | } 14 | 15 | group set 16 | 17 | struct bucket { 18 | next 19 | key 20 | } 21 | 22 | proc insert(set, key) { 23 | i = hash(key) % hashsize 24 | new_bucket = new bucket 25 | new_bucket.key = key 26 | new_bucket.next = set.buckets[i] 27 | set.buckets[i] = new_bucket 28 | } 29 | 30 | proc find(set, key) { 31 | current = set.buckets[hash(key) % hashsize] 32 | while current != null { 33 | if current.key == key => 34 | return true 35 | current = ref current.next 36 | } 37 | return false 38 | } 39 | 40 | proc remove(set, key) { 41 | i = hash(key) % hashsize 42 | current = set.buckets[i] 43 | parent = null 44 | while current != null { 45 | if current.key == key => 46 | break 47 | parent = current 48 | current = ref current.next 49 | } 50 | if current == null => 51 | return false 52 | if parent == null => 53 | set.buckets[i] = current.next 54 | else => 55 | parent.next = current.next 56 | return true 57 | } 58 | 59 | endgroup -------------------------------------------------------------------------------- /stl/strlib.txt: -------------------------------------------------------------------------------- 1 | rem strlib 2 | rem Written by Michael Wang, 2020-2021 3 | 4 | rem A library that supports string building and tokenization whilst keeping away from arrays. 5 | 6 | include "pattern.txt" 7 | 8 | group strlib 9 | 10 | static whitespace = " \t\r\n" 11 | 12 | struct _char_node { 13 | char 14 | next 15 | } 16 | 17 | struct builder { 18 | _head 19 | size 20 | } 21 | 22 | struct lexer { 23 | _str 24 | _index 25 | last_char 26 | excluded_chars 27 | size 28 | } 29 | 30 | proc builder() { 31 | builder = new builder 32 | builder.size = 0 33 | return builder 34 | } 35 | 36 | proc append_c(builder, char) { 37 | new_node = new _char_node 38 | new_node.char = char 39 | new_node.next = builder._head 40 | builder._head = new_node 41 | builder.size++ 42 | } 43 | 44 | proc append(builder, str) { 45 | i = 0 46 | len = len(str) 47 | while i < len => append_c(builder, str[i++]) 48 | } 49 | 50 | proc build(builder) { 51 | new_str = array(builder.size) 52 | i = builder.size - 1 53 | current = builder._head 54 | while current != null { 55 | new_str[i--] = current.char 56 | current = ref current.next 57 | } 58 | return new_str 59 | } 60 | 61 | proc lexer(str) { 62 | lexer = new lexer 63 | lexer._str = str 64 | lexer._index = 0 65 | lexer.size = len(str) 66 | lexer._index = 1 67 | lexer.last_char = str[0] 68 | lexer.excluded_chars = [] 69 | return lexer 70 | } 71 | 72 | proc eos(lexer) => return lexer.last_char == 0 73 | 74 | proc last_char(lexer) { 75 | return lexer.last_char 76 | } 77 | 78 | proc read_char(lexer) { 79 | if lexer._index == lexer.size { 80 | return lexer.last_char = 0 81 | } 82 | rchar = (lexer.last_char = lexer._str[lexer._index++]) 83 | if count@linq(lexer.excluded_chars, rchar) => 84 | return read_char(lexer) 85 | else => 86 | return rchar 87 | } 88 | 89 | proc read_till_c(lexer, stopchars) { 90 | builder = builder() 91 | if eos(lexer) => return null 92 | while !eos(lexer) and !count@linq(stopchars, lexer.last_char) { 93 | append_c(builder, lexer.last_char) 94 | read_char(lexer) 95 | } 96 | return build(builder) 97 | } 98 | 99 | proc read_while(lexer, stopchars) { 100 | builder = builder() 101 | if eos(lexer) => return null 102 | while !eos(lexer) and count@linq(stopchars, lexer.last_char) { 103 | append_c(builder, lexer.last_char) 104 | read_char(lexer) 105 | } 106 | return build(builder) 107 | } 108 | 109 | proc read_till(lexer, pattern) { 110 | builder = builder() 111 | if eos(lexer) => return null 112 | while !eos(lexer) and !match@pattern(lexer._str, lexer._index - 1, pattern) { 113 | append_c(builder, lexer.last_char) 114 | read_char(lexer) 115 | } 116 | return build(builder) 117 | } 118 | 119 | proc read_end(lexer) { 120 | builder = builder() 121 | while !eos(lexer) { 122 | append_c(builder, lexer.last_char) 123 | read_char(lexer) 124 | } 125 | return build(builder) 126 | } 127 | 128 | proc read_tok(lexer, stopchars) { 129 | read_while(lexer, stopchars) 130 | return read_till_c(lexer, stopchars) 131 | } 132 | 133 | endgroup 134 | 135 | group str 136 | 137 | proc split_c(str, split_chars) { 138 | lexer = lexer@strlib(str) 139 | split_toks = [] 140 | while !eos@strlib(lexer) { 141 | split_toks = split_toks + [read_till_c@strlib(lexer, split_chars)] 142 | read_while@strlib(lexer, split_chars) 143 | } 144 | return split_toks 145 | } 146 | 147 | proc split(str, pattern) { 148 | lexer = lexer@strlib(str) 149 | split_toks = [] 150 | while !eos@strlib(lexer) { 151 | split_toks = split_toks + [read_till@strlib(lexer, pattern)] 152 | for i in range(len(pattern)) => read_char@strlib(lexer) 153 | } 154 | return split_toks 155 | } 156 | 157 | proc is_whitespace(str) { 158 | if len(str) == 0 => return true 159 | for char in str { 160 | if char != ' ' and char != '\t' and char != '\r' => return false 161 | } 162 | return true 163 | } 164 | 165 | proc replace(str, to_replace, replace_with) { 166 | builder = builder@strlib() 167 | i = 0 168 | while i < len(str) { 169 | if match@pattern(str, i, to_replace) { 170 | append@strlib(builder, replace_with) 171 | i = i + len(to_replace) - 1 172 | } 173 | else => 174 | append_c@strlib(builder, str[i]) 175 | i++ 176 | } 177 | return build@strlib(builder) 178 | } 179 | 180 | proc cat_col(items) { 181 | builder = builder@strlib() 182 | for item in items { 183 | if typeof(item) == numtype => 184 | append@strlib(builder, str(item)) 185 | elif typeof(item) == chartype => 186 | append_c@strlib(builder, item) 187 | elif typeof(item) == coltype => 188 | append@strlib(builder, item) 189 | else => 190 | abort("Did not expect type ", typeof(item), ".") 191 | } 192 | return build@strlib(builder) 193 | } 194 | 195 | proc cat(params args) => return cat_col(args) 196 | 197 | proc substr(str, substr) => return contains@pattern(str, substr) 198 | 199 | proc starts_with(str, pat) => return begins@pattern(str, pat) 200 | 201 | proc ends_with(str, pat) => return ends@pattern(str, pat) 202 | 203 | endgroup -------------------------------------------------------------------------------- /stl/windows.txt: -------------------------------------------------------------------------------- 1 | include "strlib.txt" 2 | 3 | rem generic windows/systems operations 4 | group windows 5 | 6 | struct user { 7 | user_name 8 | full_name 9 | comment 10 | user_comment 11 | region_code 12 | account_active 13 | account_expires 14 | 15 | password_last_set 16 | password_expires 17 | password_changeable 18 | password_required 19 | user_many_change_password 20 | 21 | workstations_allowed 22 | logon_script 23 | user_profile 24 | home_directory 25 | last_logon 26 | 27 | logon_hours_allowed 28 | 29 | local_group_memberships 30 | global_group_memberships 31 | } 32 | 33 | proc cmd(params commands) { 34 | system("fsutil file createnew _cmd_temp 0 > nul") 35 | system(cat_col@str(commands + [" > _cmd_temp 2>&1"])) 36 | output = read@file("_cmd_temp") 37 | system("del /f _cmd_temp > nul") 38 | return output 39 | } 40 | 41 | static users = null 42 | 43 | group load_users 44 | 45 | proc get_user_info(user_name) { 46 | lexer = lexer@strlib(cmd@windows("net user "+user_name)) 47 | lines = [] 48 | while !starts_with@str(line = read_tok@strlib(lexer, ['\n']), "The command completed successfully.") { 49 | lines = lines + [line] 50 | } 51 | user = new user@windows 52 | prop = 0 53 | for line in lines { 54 | line_lexer = lexer@strlib(line) 55 | line_lexer.excluded_chars = ['\r', '\t'] 56 | if !is_whitespace@str(read_till@strlib(line_lexer, " ")) { 57 | read_while@strlib(line_lexer, [' ']) 58 | data = read_end@strlib(line_lexer) 59 | 60 | if is_whitespace@str(data) => 61 | setprop(user, prop++, null) 62 | else => 63 | setprop(user, prop++, data) 64 | } 65 | } 66 | if user.local_group_memberships != null => 67 | user.local_group_memberships = split_c@str(user.local_group_memberships, whitespace@strlib) 68 | if user.global_group_memberships != null => 69 | user.global_group_memberships = split_c@str(user.global_group_memberships, whitespace@strlib) 70 | return user 71 | } 72 | 73 | user_names = [] 74 | 75 | lexer = lexer@strlib(cmd@windows("net users")) 76 | read_till_c@strlib(lexer, ['-']) 77 | read_while@strlib(lexer, ['-']) 78 | 79 | while !starts_with@str(user_name = read_tok@strlib(lexer, whitespace@strlib), "The") { 80 | user_names = user_names + [user_name] 81 | } 82 | 83 | users@windows = array(len(user_names)) 84 | i = len(user_names) 85 | while i-- => 86 | users@windows[i] = get_user_info(user_names[i]) 87 | 88 | endgroup 89 | 90 | endgroup 91 | 92 | 93 | rem file operations 94 | group file 95 | 96 | proc exists(fpath) => 97 | return starts_with@str(cmd@windows(cat@str("if exist ",fpath, " (echo yes)")),"yes") 98 | 99 | proc mk(fname) => 100 | system(cat@str("type nul >", fname)) 101 | 102 | endgroup 103 | 104 | 105 | rem directory operations 106 | group dir 107 | 108 | proc exists(fpath) => 109 | return starts_with@str(cmd@windows(cat@str("if exist ",fpath, "\ (echo yes)")),"yes") 110 | 111 | endgroup --------------------------------------------------------------------------------