├── .github └── workflows │ ├── main.yml │ └── push.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── src ├── hashmap.h ├── tasm │ ├── tasm.c │ ├── tasm.h │ ├── tasmlexer.c │ ├── tasmlexer.h │ ├── tasmparser.c │ └── tasmparser.h ├── tim.c ├── tim.h ├── tipp.h ├── tire │ ├── tire.c │ └── tire.h └── view.h ├── tests ├── escape_character.t1 ├── escape_character.tasm ├── exit.tasm ├── fibiter.tasm ├── fibrec.tasm ├── fizzbuzz.tasm ├── helloworld.tasm ├── inttostr.tasm ├── label.tasm ├── linuxsyscalls.tash ├── macrotest.tasm ├── negative.tasm ├── open.tasm ├── power.tasm ├── printfloat.tasm ├── printint.tasm ├── random.tasm ├── read.tasm ├── std.tash ├── stddefs.tash ├── stringcmp.tasm ├── strlen.tasm └── strrev.tasm ├── todolist.txt └── tools └── titasm.vim /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: build and test on pr 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - run: make 13 | -------------------------------------------------------------------------------- /.github/workflows/push.yml: -------------------------------------------------------------------------------- 1 | name: build and test on push 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - run: make 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.tim 3 | build/ 4 | canoscript/build/ 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [2024] [CobbCoding] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-Wall -Wextra -pedantic -ggdb2 3 | DEFINES= 4 | INCLUDES=-Isrc 5 | LIBS= 6 | 7 | SRCDIR=src 8 | BUILDDIR=build 9 | 10 | ifeq ($(BUILD_TYPE), DEBUG) 11 | CFLAGS += -g -ggdb2 12 | endif 13 | 14 | SRC=$(wildcard $(SRCDIR)/*.c) 15 | OBJ=$(patsubst $(SRCDIR)/%.c, $(BUILDDIR)/%.o, $(SRC)) 16 | 17 | TIREDIR=$(BUILDDIR)/tire 18 | TIRESRC=$(wildcard $(SRCDIR)/tire/*.c) 19 | TIREOBJ=$(patsubst $(SRCDIR)/tire/%.c, $(TIREDIR)/%.o, $(TIRESRC)) 20 | 21 | TIRENAME=tire 22 | TIRE=$(TIREDIR)/$(TIRENAME) 23 | 24 | TASMDIR=$(BUILDDIR)/tasm 25 | TASMSRC=$(wildcard $(SRCDIR)/tasm/*.c) 26 | TASMOBJ=$(patsubst $(SRCDIR)/tasm/%.c, $(TASMDIR)/%.o, $(TASMSRC)) 27 | 28 | TASMNAME=tasm 29 | TASM=$(TASMDIR)/$(TASMNAME) 30 | 31 | .PHONY: all clean destroy test 32 | 33 | all: $(OBJ) $(TIRE) $(TASM) 34 | 35 | $(BUILDDIR)/%.o: $(SRCDIR)/%.c 36 | @ mkdir -p $(dir $@) 37 | $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -c $< -o $@ 38 | 39 | $(TIRE): $(BUILDDIR)/$(TIREOBJ) 40 | $(CC) $(CFLAGS) $(INCLUDES) $(TIREOBJ) $(OBJ) -o $(TIRE) $(LIBS) 41 | 42 | $(BUILDDIR)/tire/%.o: $(SRCDIR)/tire/%.c 43 | @ mkdir -p $(dir $@) 44 | $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -c $< -o $@ 45 | 46 | $(TASM): $(BUILDDIR)/$(TASMOBJ) 47 | $(CC) $(CFLAGS) $(INCLUDES) $(TASMOBJ) $(OBJ) -o $(TASM) $(LIBS) 48 | 49 | $(BUILDDIR)/tasm/%.o: $(SRCDIR)/tasm/%.c 50 | @ mkdir -p $(dir $@) 51 | $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -c $< -o $@ 52 | 53 | clean: 54 | rm -rf $(TIRE) 55 | rm -rf $(TIREOBJ) 56 | rm -rf $(TASM) 57 | rm -rf $(TASMOBJ) 58 | rm -rf $(OBJ) 59 | 60 | destroy: 61 | rm -rf $(BUILDDIR) 62 | 63 | test: 64 | $(foreach file, $(wildcard tests/*.tasm), ./build/tasm/tasm $(file);) 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TIM - Titanium Virtual Machine 2 | 3 | CanoScript is now in its own [repo](https://www.github.com/CobbCoding1/canoscript) 4 | 5 | Implementation of a virtual machine in C. 6 | 7 | VM currently has 45 instructions as well as a some native functions. List can be found in tim.h. 8 | There is also a working assembly which contains support for all instructions in the VM. 9 | 10 | Quick Start: 11 | ```bash 12 | make 13 | ./tasm 14 | ./tire 15 | ``` 16 | 17 | Example of hello world in assembly: 18 | ```asm 19 | @imp "stddefs.tash" 20 | 21 | push_str "Hello, world!\n" 22 | get_str 0 ; Index of the string on the data stack 23 | push STDOUT 24 | write ; length is inferred because the string is null-terminated 25 | ``` 26 | 27 | -------------------------------------------------------------------------------- /src/hashmap.h: -------------------------------------------------------------------------------- 1 | /* 2 | The latest version of this library is available on GitHub; 3 | https://github.com/sheredom/hashmap.h 4 | */ 5 | 6 | /* 7 | This is free and unencumbered software released into the public domain. 8 | 9 | Anyone is free to copy, modify, publish, use, compile, sell, or 10 | distribute this software, either in source code form or as a compiled 11 | binary, for any purpose, commercial or non-commercial, and by any 12 | means. 13 | 14 | In jurisdictions that recognize copyright laws, the author or authors 15 | of this software dedicate any and all copyright interest in the 16 | software to the public domain. We make this dedication for the benefit 17 | of the public at large and to the detriment of our heirs and 18 | successors. We intend this dedication to be an overt act of 19 | relinquishment in perpetuity of all present and future rights to this 20 | software under copyright law. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 25 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 26 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 27 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 28 | OTHER DEALINGS IN THE SOFTWARE. 29 | 30 | For more information, please refer to 31 | */ 32 | #ifndef SHEREDOM_HASHMAP_H_INCLUDED 33 | #define SHEREDOM_HASHMAP_H_INCLUDED 34 | 35 | #if defined(_MSC_VER) 36 | // Workaround a bug in the MSVC runtime where it uses __cplusplus when not 37 | // defined. 38 | #pragma warning(push, 0) 39 | #pragma warning(disable : 4668) 40 | #endif 41 | 42 | #include 43 | #include 44 | 45 | #if (defined(_MSC_VER) && defined(__AVX__)) || \ 46 | (!defined(_MSC_VER) && defined(__SSE4_2__)) 47 | #define HASHMAP_X86_SSE42 48 | #endif 49 | 50 | #if defined(HASHMAP_X86_SSE42) 51 | #include 52 | #endif 53 | 54 | #if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) 55 | #define HASHMAP_ARM_CRC32 56 | #endif 57 | 58 | #if defined(HASHMAP_ARM_CRC32) 59 | #include 60 | #endif 61 | 62 | #if defined(_MSC_VER) 63 | #include 64 | #endif 65 | 66 | #if defined(_MSC_VER) 67 | #pragma warning(pop) 68 | #endif 69 | 70 | #if defined(_MSC_VER) 71 | #pragma warning(push) 72 | /* Stop MSVC complaining about unreferenced functions */ 73 | #pragma warning(disable : 4505) 74 | /* Stop MSVC complaining about not inlining functions */ 75 | #pragma warning(disable : 4710) 76 | /* Stop MSVC complaining about inlining functions! */ 77 | #pragma warning(disable : 4711) 78 | #endif 79 | 80 | #if defined(__clang__) 81 | #pragma clang diagnostic push 82 | #pragma clang diagnostic ignored "-Wunused-function" 83 | #pragma clang diagnostic ignored "-Wstatic-in-inline" 84 | #endif 85 | 86 | #if defined(_MSC_VER) 87 | #define HASHMAP_WEAK __inline 88 | #elif defined(__clang__) || defined(__GNUC__) 89 | #define HASHMAP_WEAK __attribute__((weak)) 90 | #else 91 | #error Non clang, non gcc, non MSVC compiler found! 92 | #endif 93 | 94 | #if defined(_MSC_VER) 95 | #define HASHMAP_ALWAYS_INLINE __forceinline 96 | #elif (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ 97 | defined(__cplusplus) 98 | #define HASHMAP_ALWAYS_INLINE __attribute__((always_inline)) inline 99 | #else 100 | /* If we cannot use inline, its not safe to use always_inline, so we mark the 101 | * function weak. */ 102 | #define HASHMAP_ALWAYS_INLINE HASHMAP_WEAK 103 | #endif 104 | 105 | #if defined(_MSC_VER) && (_MSC_VER < 1920) 106 | typedef unsigned __int8 hashmap_uint8_t; 107 | typedef unsigned __int32 hashmap_uint32_t; 108 | typedef unsigned __int64 hashmap_uint64_t; 109 | #else 110 | #include 111 | typedef uint8_t hashmap_uint8_t; 112 | typedef uint32_t hashmap_uint32_t; 113 | typedef uint64_t hashmap_uint64_t; 114 | #endif 115 | 116 | typedef struct hashmap_element_s { 117 | const void *key; 118 | hashmap_uint32_t key_len; 119 | int in_use; 120 | void *data; 121 | } hashmap_element_t; 122 | 123 | typedef hashmap_uint32_t (*hashmap_hasher_t)(hashmap_uint32_t seed, 124 | const void *key, 125 | hashmap_uint32_t key_len); 126 | typedef int (*hashmap_comparer_t)(const void *a, hashmap_uint32_t a_len, 127 | const void *b, hashmap_uint32_t b_len); 128 | 129 | typedef struct hashmap_s { 130 | hashmap_uint32_t log2_capacity; 131 | hashmap_uint32_t size; 132 | hashmap_hasher_t hasher; 133 | hashmap_comparer_t comparer; 134 | struct hashmap_element_s *data; 135 | } hashmap_t; 136 | 137 | #define HASHMAP_LINEAR_PROBE_LENGTH (8) 138 | 139 | typedef struct hashmap_create_options_s { 140 | hashmap_hasher_t hasher; 141 | hashmap_comparer_t comparer; 142 | hashmap_uint32_t initial_capacity; 143 | hashmap_uint32_t _; 144 | } hashmap_create_options_t; 145 | 146 | #if defined(__cplusplus) 147 | extern "C" { 148 | #endif 149 | 150 | /// @brief Create a hashmap. 151 | /// @param initial_capacity The initial capacity of the hashmap. 152 | /// @param out_hashmap The storage for the created hashmap. 153 | /// @return On success 0 is returned. 154 | HASHMAP_WEAK int hashmap_create(const hashmap_uint32_t initial_capacity, 155 | struct hashmap_s *const out_hashmap); 156 | 157 | /// @brief Create a hashmap. 158 | /// @param options The options to create the hashmap with. 159 | /// @param out_hashmap The storage for the created hashmap. 160 | /// @return On success 0 is returned. 161 | /// 162 | /// The options members work as follows: 163 | /// - initial_capacity The initial capacity of the hashmap. 164 | /// - hasher Which hashing function to use with the hashmap (by default the 165 | // crc32 with Robert Jenkins' mix is used). 166 | HASHMAP_WEAK int hashmap_create_ex(struct hashmap_create_options_s options, 167 | struct hashmap_s *const out_hashmap); 168 | 169 | /// @brief Put an element into the hashmap. 170 | /// @param hashmap The hashmap to insert into. 171 | /// @param key The string key to use. 172 | /// @param len The length of the string key. 173 | /// @param value The value to insert. 174 | /// @return On success 0 is returned. 175 | /// 176 | /// The key string slice is not copied when creating the hashmap entry, and thus 177 | /// must remain a valid pointer until the hashmap entry is removed or the 178 | /// hashmap is destroyed. 179 | HASHMAP_WEAK int hashmap_put(struct hashmap_s *const hashmap, 180 | const void *const key, const hashmap_uint32_t len, 181 | void *const value); 182 | 183 | /// @brief Get an element from the hashmap. 184 | /// @param hashmap The hashmap to get from. 185 | /// @param key The string key to use. 186 | /// @param len The length of the string key. 187 | /// @return The previously set element, or NULL if none exists. 188 | HASHMAP_WEAK void *hashmap_get(const struct hashmap_s *const hashmap, 189 | const void *const key, 190 | const hashmap_uint32_t len); 191 | 192 | /// @brief Remove an element from the hashmap. 193 | /// @param hashmap The hashmap to remove from. 194 | /// @param key The string key to use. 195 | /// @param len The length of the string key. 196 | /// @return On success 0 is returned. 197 | HASHMAP_WEAK int hashmap_remove(struct hashmap_s *const hashmap, 198 | const void *const key, 199 | const hashmap_uint32_t len); 200 | 201 | /// @brief Remove an element from the hashmap. 202 | /// @param hashmap The hashmap to remove from. 203 | /// @param key The string key to use. 204 | /// @param len The length of the string key. 205 | /// @return On success the original stored key pointer is returned, on failure 206 | /// NULL is returned. 207 | HASHMAP_WEAK const void * 208 | hashmap_remove_and_return_key(struct hashmap_s *const hashmap, 209 | const void *const key, 210 | const hashmap_uint32_t len); 211 | 212 | /// @brief Iterate over all the elements in a hashmap. 213 | /// @param hashmap The hashmap to iterate over. 214 | /// @param iterator The function pointer to call on each element. 215 | /// @param context The context to pass as the first argument to f. 216 | /// @return If the entire hashmap was iterated then 0 is returned. Otherwise if 217 | /// the callback function f returned non-zero then non-zero is returned. 218 | HASHMAP_WEAK int hashmap_iterate(const struct hashmap_s *const hashmap, 219 | int (*iterator)(void *const context, 220 | void *const value), 221 | void *const context); 222 | 223 | /// @brief Iterate over all the elements in a hashmap. 224 | /// @param hashmap The hashmap to iterate over. 225 | /// @param iterator The function pointer to call on each element. 226 | /// @param context The context to pass as the first argument to f. 227 | /// @return If the entire hashmap was iterated then 0 is returned. 228 | /// Otherwise if the callback function f returned positive then the positive 229 | /// value is returned. If the callback function returns -1, the current item 230 | /// is removed and iteration continues. 231 | HASHMAP_WEAK int hashmap_iterate_pairs( 232 | struct hashmap_s *const hashmap, 233 | int (*iterator)(void *const, struct hashmap_element_s *const), 234 | void *const context); 235 | 236 | /// @brief Get the size of the hashmap. 237 | /// @param hashmap The hashmap to get the size of. 238 | /// @return The size of the hashmap. 239 | HASHMAP_ALWAYS_INLINE hashmap_uint32_t 240 | hashmap_num_entries(const struct hashmap_s *const hashmap); 241 | 242 | /// @brief Get the capacity of the hashmap. 243 | /// @param hashmap The hashmap to get the size of. 244 | /// @return The capacity of the hashmap. 245 | HASHMAP_ALWAYS_INLINE hashmap_uint32_t 246 | hashmap_capacity(const struct hashmap_s *const hashmap); 247 | 248 | /// @brief Destroy the hashmap. 249 | /// @param hashmap The hashmap to destroy. 250 | HASHMAP_WEAK void hashmap_destroy(struct hashmap_s *const hashmap); 251 | 252 | static hashmap_uint32_t hashmap_crc32_hasher(const hashmap_uint32_t seed, 253 | const void *const s, 254 | const hashmap_uint32_t len); 255 | static int hashmap_memcmp_comparer(const void *const a, 256 | const hashmap_uint32_t a_len, 257 | const void *const b, 258 | const hashmap_uint32_t b_len); 259 | HASHMAP_ALWAYS_INLINE hashmap_uint32_t hashmap_hash_helper_int_helper( 260 | const struct hashmap_s *const m, const void *const key, 261 | const hashmap_uint32_t len); 262 | HASHMAP_ALWAYS_INLINE int 263 | hashmap_hash_helper(const struct hashmap_s *const m, const void *const key, 264 | const hashmap_uint32_t len, 265 | hashmap_uint32_t *const out_index); 266 | HASHMAP_WEAK int hashmap_rehash_iterator(void *const new_hash, 267 | struct hashmap_element_s *const e); 268 | HASHMAP_ALWAYS_INLINE int hashmap_rehash_helper(struct hashmap_s *const m); 269 | HASHMAP_ALWAYS_INLINE hashmap_uint32_t hashmap_clz(const hashmap_uint32_t x); 270 | 271 | #if defined(__cplusplus) 272 | } 273 | #endif 274 | 275 | #if defined(__cplusplus) 276 | #define HASHMAP_CAST(type, x) static_cast(x) 277 | #define HASHMAP_PTR_CAST(type, x) reinterpret_cast(x) 278 | #define HASHMAP_NULL NULL 279 | #else 280 | #define HASHMAP_CAST(type, x) ((type)(x)) 281 | #define HASHMAP_PTR_CAST(type, x) ((type)(x)) 282 | #define HASHMAP_NULL 0 283 | #endif 284 | 285 | int hashmap_create(const hashmap_uint32_t initial_capacity, 286 | struct hashmap_s *const out_hashmap) { 287 | struct hashmap_create_options_s options; 288 | memset(&options, 0, sizeof(options)); 289 | options.initial_capacity = initial_capacity; 290 | 291 | return hashmap_create_ex(options, out_hashmap); 292 | } 293 | 294 | int hashmap_create_ex(struct hashmap_create_options_s options, 295 | struct hashmap_s *const out_hashmap) { 296 | if (2 > options.initial_capacity) { 297 | options.initial_capacity = 2; 298 | } else if (0 != (options.initial_capacity & (options.initial_capacity - 1))) { 299 | options.initial_capacity = 1u 300 | << (32 - hashmap_clz(options.initial_capacity)); 301 | } 302 | 303 | if (HASHMAP_NULL == options.hasher) { 304 | options.hasher = &hashmap_crc32_hasher; 305 | } 306 | 307 | if (HASHMAP_NULL == options.comparer) { 308 | options.comparer = &hashmap_memcmp_comparer; 309 | } 310 | 311 | out_hashmap->data = HASHMAP_CAST( 312 | struct hashmap_element_s *, 313 | calloc(options.initial_capacity + HASHMAP_LINEAR_PROBE_LENGTH, 314 | sizeof(struct hashmap_element_s))); 315 | 316 | out_hashmap->log2_capacity = 31 - hashmap_clz(options.initial_capacity); 317 | out_hashmap->size = 0; 318 | out_hashmap->hasher = options.hasher; 319 | out_hashmap->comparer = options.comparer; 320 | 321 | return 0; 322 | } 323 | 324 | int hashmap_put(struct hashmap_s *const m, const void *const key, 325 | const hashmap_uint32_t len, void *const value) { 326 | hashmap_uint32_t index; 327 | 328 | if ((HASHMAP_NULL == key) || (0 == len)) { 329 | return 1; 330 | } 331 | 332 | /* Find a place to put our value. */ 333 | while (!hashmap_hash_helper(m, key, len, &index)) { 334 | if (hashmap_rehash_helper(m)) { 335 | return 1; 336 | } 337 | } 338 | 339 | /* Set the data. */ 340 | m->data[index].data = value; 341 | m->data[index].key = key; 342 | m->data[index].key_len = len; 343 | 344 | /* If the hashmap element was not already in use, set that it is being used 345 | * and bump our size. */ 346 | if (0 == m->data[index].in_use) { 347 | m->data[index].in_use = 1; 348 | m->size++; 349 | } 350 | 351 | return 0; 352 | } 353 | 354 | void *hashmap_get(const struct hashmap_s *const m, const void *const key, 355 | const hashmap_uint32_t len) { 356 | hashmap_uint32_t i, curr; 357 | 358 | if ((HASHMAP_NULL == key) || (0 == len)) { 359 | return HASHMAP_NULL; 360 | } 361 | 362 | curr = hashmap_hash_helper_int_helper(m, key, len); 363 | 364 | /* Linear probing, if necessary */ 365 | for (i = 0; i < HASHMAP_LINEAR_PROBE_LENGTH; i++) { 366 | const hashmap_uint32_t index = curr + i; 367 | 368 | if (m->data[index].in_use) { 369 | if (m->comparer(m->data[index].key, m->data[index].key_len, key, len)) { 370 | return m->data[index].data; 371 | } 372 | } 373 | } 374 | 375 | /* Not found */ 376 | return HASHMAP_NULL; 377 | } 378 | 379 | int hashmap_remove(struct hashmap_s *const m, const void *const key, 380 | const hashmap_uint32_t len) { 381 | hashmap_uint32_t i, curr; 382 | 383 | if ((HASHMAP_NULL == key) || (0 == len)) { 384 | return 1; 385 | } 386 | 387 | curr = hashmap_hash_helper_int_helper(m, key, len); 388 | 389 | /* Linear probing, if necessary */ 390 | for (i = 0; i < HASHMAP_LINEAR_PROBE_LENGTH; i++) { 391 | const hashmap_uint32_t index = curr + i; 392 | 393 | if (m->data[index].in_use) { 394 | if (m->comparer(m->data[index].key, m->data[index].key_len, key, len)) { 395 | /* Blank out the fields including in_use */ 396 | memset(&m->data[index], 0, sizeof(struct hashmap_element_s)); 397 | 398 | /* Reduce the size */ 399 | m->size--; 400 | 401 | return 0; 402 | } 403 | } 404 | } 405 | 406 | return 1; 407 | } 408 | 409 | const void *hashmap_remove_and_return_key(struct hashmap_s *const m, 410 | const void *const key, 411 | const hashmap_uint32_t len) { 412 | hashmap_uint32_t i, curr; 413 | 414 | if ((HASHMAP_NULL == key) || (0 == len)) { 415 | return HASHMAP_NULL; 416 | } 417 | 418 | curr = hashmap_hash_helper_int_helper(m, key, len); 419 | 420 | /* Linear probing, if necessary */ 421 | for (i = 0; i < HASHMAP_LINEAR_PROBE_LENGTH; i++) { 422 | const hashmap_uint32_t index = curr + i; 423 | 424 | if (m->data[index].in_use) { 425 | if (m->comparer(m->data[index].key, m->data[index].key_len, key, len)) { 426 | const void *const stored_key = m->data[index].key; 427 | 428 | /* Blank out the fields */ 429 | memset(&m->data[index], 0, sizeof(struct hashmap_element_s)); 430 | 431 | /* Reduce the size */ 432 | m->size--; 433 | 434 | return stored_key; 435 | } 436 | } 437 | } 438 | 439 | return HASHMAP_NULL; 440 | } 441 | 442 | int hashmap_iterate(const struct hashmap_s *const m, 443 | int (*f)(void *const, void *const), void *const context) { 444 | hashmap_uint32_t i; 445 | 446 | for (i = 0; i < (hashmap_capacity(m) + HASHMAP_LINEAR_PROBE_LENGTH); i++) { 447 | if (m->data[i].in_use) { 448 | if (!f(context, m->data[i].data)) { 449 | return 1; 450 | } 451 | } 452 | } 453 | 454 | return 0; 455 | } 456 | 457 | int hashmap_iterate_pairs(struct hashmap_s *const m, 458 | int (*f)(void *const, 459 | struct hashmap_element_s *const), 460 | void *const context) { 461 | hashmap_uint32_t i; 462 | struct hashmap_element_s *p; 463 | int r; 464 | 465 | for (i = 0; i < (hashmap_capacity(m) + HASHMAP_LINEAR_PROBE_LENGTH); i++) { 466 | p = &m->data[i]; 467 | if (p->in_use) { 468 | r = f(context, p); 469 | switch (r) { 470 | case -1: /* remove item */ 471 | memset(p, 0, sizeof(struct hashmap_element_s)); 472 | m->size--; 473 | break; 474 | case 0: /* continue iterating */ 475 | break; 476 | default: /* early exit */ 477 | return 1; 478 | } 479 | } 480 | } 481 | return 0; 482 | } 483 | 484 | void hashmap_destroy(struct hashmap_s *const m) { 485 | free(m->data); 486 | memset(m, 0, sizeof(struct hashmap_s)); 487 | } 488 | 489 | HASHMAP_ALWAYS_INLINE hashmap_uint32_t 490 | hashmap_num_entries(const struct hashmap_s *const m) { 491 | return m->size; 492 | } 493 | 494 | HASHMAP_ALWAYS_INLINE hashmap_uint32_t 495 | hashmap_capacity(const struct hashmap_s *const m) { 496 | return 1u << m->log2_capacity; 497 | } 498 | 499 | hashmap_uint32_t hashmap_crc32_hasher(const hashmap_uint32_t seed, 500 | const void *const k, 501 | const hashmap_uint32_t len) { 502 | hashmap_uint32_t i = 0; 503 | hashmap_uint32_t crc32val = seed; 504 | const hashmap_uint8_t *const s = HASHMAP_PTR_CAST(const hashmap_uint8_t *, k); 505 | 506 | #if defined(HASHMAP_X86_SSE42) 507 | for (; (i + sizeof(hashmap_uint32_t)) < len; i += sizeof(hashmap_uint32_t)) { 508 | hashmap_uint32_t next; 509 | memcpy(&next, &s[i], sizeof(next)); 510 | crc32val = _mm_crc32_u32(crc32val, next); 511 | } 512 | 513 | for (; i < len; i++) { 514 | crc32val = _mm_crc32_u8(crc32val, s[i]); 515 | } 516 | #elif defined(HASHMAP_ARM_CRC32) 517 | for (; (i + sizeof(hashmap_uint64_t)) < len; i += sizeof(hashmap_uint64_t)) { 518 | hashmap_uint64_t next; 519 | memcpy(&next, &s[i], sizeof(next)); 520 | crc32val = __crc32d(crc32val, next); 521 | } 522 | 523 | for (; i < len; i++) { 524 | crc32val = __crc32b(crc32val, s[i]); 525 | } 526 | #else 527 | // Using polynomial 0x11EDC6F41 to match SSE 4.2's crc function. 528 | static const hashmap_uint32_t crc32_tab[] = { 529 | 0x00000000U, 0xF26B8303U, 0xE13B70F7U, 0x1350F3F4U, 0xC79A971FU, 530 | 0x35F1141CU, 0x26A1E7E8U, 0xD4CA64EBU, 0x8AD958CFU, 0x78B2DBCCU, 531 | 0x6BE22838U, 0x9989AB3BU, 0x4D43CFD0U, 0xBF284CD3U, 0xAC78BF27U, 532 | 0x5E133C24U, 0x105EC76FU, 0xE235446CU, 0xF165B798U, 0x030E349BU, 533 | 0xD7C45070U, 0x25AFD373U, 0x36FF2087U, 0xC494A384U, 0x9A879FA0U, 534 | 0x68EC1CA3U, 0x7BBCEF57U, 0x89D76C54U, 0x5D1D08BFU, 0xAF768BBCU, 535 | 0xBC267848U, 0x4E4DFB4BU, 0x20BD8EDEU, 0xD2D60DDDU, 0xC186FE29U, 536 | 0x33ED7D2AU, 0xE72719C1U, 0x154C9AC2U, 0x061C6936U, 0xF477EA35U, 537 | 0xAA64D611U, 0x580F5512U, 0x4B5FA6E6U, 0xB93425E5U, 0x6DFE410EU, 538 | 0x9F95C20DU, 0x8CC531F9U, 0x7EAEB2FAU, 0x30E349B1U, 0xC288CAB2U, 539 | 0xD1D83946U, 0x23B3BA45U, 0xF779DEAEU, 0x05125DADU, 0x1642AE59U, 540 | 0xE4292D5AU, 0xBA3A117EU, 0x4851927DU, 0x5B016189U, 0xA96AE28AU, 541 | 0x7DA08661U, 0x8FCB0562U, 0x9C9BF696U, 0x6EF07595U, 0x417B1DBCU, 542 | 0xB3109EBFU, 0xA0406D4BU, 0x522BEE48U, 0x86E18AA3U, 0x748A09A0U, 543 | 0x67DAFA54U, 0x95B17957U, 0xCBA24573U, 0x39C9C670U, 0x2A993584U, 544 | 0xD8F2B687U, 0x0C38D26CU, 0xFE53516FU, 0xED03A29BU, 0x1F682198U, 545 | 0x5125DAD3U, 0xA34E59D0U, 0xB01EAA24U, 0x42752927U, 0x96BF4DCCU, 546 | 0x64D4CECFU, 0x77843D3BU, 0x85EFBE38U, 0xDBFC821CU, 0x2997011FU, 547 | 0x3AC7F2EBU, 0xC8AC71E8U, 0x1C661503U, 0xEE0D9600U, 0xFD5D65F4U, 548 | 0x0F36E6F7U, 0x61C69362U, 0x93AD1061U, 0x80FDE395U, 0x72966096U, 549 | 0xA65C047DU, 0x5437877EU, 0x4767748AU, 0xB50CF789U, 0xEB1FCBADU, 550 | 0x197448AEU, 0x0A24BB5AU, 0xF84F3859U, 0x2C855CB2U, 0xDEEEDFB1U, 551 | 0xCDBE2C45U, 0x3FD5AF46U, 0x7198540DU, 0x83F3D70EU, 0x90A324FAU, 552 | 0x62C8A7F9U, 0xB602C312U, 0x44694011U, 0x5739B3E5U, 0xA55230E6U, 553 | 0xFB410CC2U, 0x092A8FC1U, 0x1A7A7C35U, 0xE811FF36U, 0x3CDB9BDDU, 554 | 0xCEB018DEU, 0xDDE0EB2AU, 0x2F8B6829U, 0x82F63B78U, 0x709DB87BU, 555 | 0x63CD4B8FU, 0x91A6C88CU, 0x456CAC67U, 0xB7072F64U, 0xA457DC90U, 556 | 0x563C5F93U, 0x082F63B7U, 0xFA44E0B4U, 0xE9141340U, 0x1B7F9043U, 557 | 0xCFB5F4A8U, 0x3DDE77ABU, 0x2E8E845FU, 0xDCE5075CU, 0x92A8FC17U, 558 | 0x60C37F14U, 0x73938CE0U, 0x81F80FE3U, 0x55326B08U, 0xA759E80BU, 559 | 0xB4091BFFU, 0x466298FCU, 0x1871A4D8U, 0xEA1A27DBU, 0xF94AD42FU, 560 | 0x0B21572CU, 0xDFEB33C7U, 0x2D80B0C4U, 0x3ED04330U, 0xCCBBC033U, 561 | 0xA24BB5A6U, 0x502036A5U, 0x4370C551U, 0xB11B4652U, 0x65D122B9U, 562 | 0x97BAA1BAU, 0x84EA524EU, 0x7681D14DU, 0x2892ED69U, 0xDAF96E6AU, 563 | 0xC9A99D9EU, 0x3BC21E9DU, 0xEF087A76U, 0x1D63F975U, 0x0E330A81U, 564 | 0xFC588982U, 0xB21572C9U, 0x407EF1CAU, 0x532E023EU, 0xA145813DU, 565 | 0x758FE5D6U, 0x87E466D5U, 0x94B49521U, 0x66DF1622U, 0x38CC2A06U, 566 | 0xCAA7A905U, 0xD9F75AF1U, 0x2B9CD9F2U, 0xFF56BD19U, 0x0D3D3E1AU, 567 | 0x1E6DCDEEU, 0xEC064EEDU, 0xC38D26C4U, 0x31E6A5C7U, 0x22B65633U, 568 | 0xD0DDD530U, 0x0417B1DBU, 0xF67C32D8U, 0xE52CC12CU, 0x1747422FU, 569 | 0x49547E0BU, 0xBB3FFD08U, 0xA86F0EFCU, 0x5A048DFFU, 0x8ECEE914U, 570 | 0x7CA56A17U, 0x6FF599E3U, 0x9D9E1AE0U, 0xD3D3E1ABU, 0x21B862A8U, 571 | 0x32E8915CU, 0xC083125FU, 0x144976B4U, 0xE622F5B7U, 0xF5720643U, 572 | 0x07198540U, 0x590AB964U, 0xAB613A67U, 0xB831C993U, 0x4A5A4A90U, 573 | 0x9E902E7BU, 0x6CFBAD78U, 0x7FAB5E8CU, 0x8DC0DD8FU, 0xE330A81AU, 574 | 0x115B2B19U, 0x020BD8EDU, 0xF0605BEEU, 0x24AA3F05U, 0xD6C1BC06U, 575 | 0xC5914FF2U, 0x37FACCF1U, 0x69E9F0D5U, 0x9B8273D6U, 0x88D28022U, 576 | 0x7AB90321U, 0xAE7367CAU, 0x5C18E4C9U, 0x4F48173DU, 0xBD23943EU, 577 | 0xF36E6F75U, 0x0105EC76U, 0x12551F82U, 0xE03E9C81U, 0x34F4F86AU, 578 | 0xC69F7B69U, 0xD5CF889DU, 0x27A40B9EU, 0x79B737BAU, 0x8BDCB4B9U, 579 | 0x988C474DU, 0x6AE7C44EU, 0xBE2DA0A5U, 0x4C4623A6U, 0x5F16D052U, 580 | 0xAD7D5351U}; 581 | 582 | for (; i < len; i++) { 583 | crc32val = crc32_tab[(HASHMAP_CAST(hashmap_uint8_t, crc32val) ^ s[i])] ^ 584 | (crc32val >> 8); 585 | } 586 | #endif 587 | 588 | // Use the mix function from murmur3. 589 | crc32val ^= len; 590 | 591 | crc32val ^= crc32val >> 16; 592 | crc32val *= 0x85ebca6b; 593 | crc32val ^= crc32val >> 13; 594 | crc32val *= 0xc2b2ae35; 595 | crc32val ^= crc32val >> 16; 596 | 597 | return crc32val; 598 | } 599 | 600 | int hashmap_memcmp_comparer(const void *const a, const hashmap_uint32_t a_len, 601 | const void *const b, const hashmap_uint32_t b_len) { 602 | return (a_len == b_len) && (0 == memcmp(a, b, a_len)); 603 | } 604 | 605 | HASHMAP_ALWAYS_INLINE hashmap_uint32_t 606 | hashmap_hash_helper_int_helper(const struct hashmap_s *const m, 607 | const void *const k, const hashmap_uint32_t l) { 608 | return (m->hasher(~0u, k, l) * 2654435769u) >> (32u - m->log2_capacity); 609 | } 610 | 611 | HASHMAP_ALWAYS_INLINE int 612 | hashmap_hash_helper(const struct hashmap_s *const m, const void *const key, 613 | const hashmap_uint32_t len, 614 | hashmap_uint32_t *const out_index) { 615 | hashmap_uint32_t curr; 616 | hashmap_uint32_t i; 617 | hashmap_uint32_t first_free; 618 | 619 | /* If full, return immediately */ 620 | if (hashmap_num_entries(m) == hashmap_capacity(m)) { 621 | return 0; 622 | } 623 | 624 | /* Find the best index */ 625 | curr = hashmap_hash_helper_int_helper(m, key, len); 626 | first_free = ~0u; 627 | 628 | for (i = 0; i < HASHMAP_LINEAR_PROBE_LENGTH; i++) { 629 | const hashmap_uint32_t index = curr + i; 630 | 631 | if (!m->data[index].in_use) { 632 | first_free = (first_free < index) ? first_free : index; 633 | } else if (m->comparer(m->data[index].key, m->data[index].key_len, key, 634 | len)) { 635 | *out_index = index; 636 | return 1; 637 | } 638 | } 639 | 640 | // Couldn't find a free element in the linear probe. 641 | if (~0u == first_free) { 642 | return 0; 643 | } 644 | 645 | *out_index = first_free; 646 | return 1; 647 | } 648 | 649 | int hashmap_rehash_iterator(void *const new_hash, 650 | struct hashmap_element_s *const e) { 651 | int temp = hashmap_put(HASHMAP_PTR_CAST(struct hashmap_s *, new_hash), e->key, 652 | e->key_len, e->data); 653 | 654 | if (0 < temp) { 655 | return 1; 656 | } 657 | 658 | /* clear old value to avoid stale pointers */ 659 | return -1; 660 | } 661 | 662 | /* 663 | * Doubles the size of the hashmap, and rehashes all the elements 664 | */ 665 | HASHMAP_ALWAYS_INLINE int hashmap_rehash_helper(struct hashmap_s *const m) { 666 | struct hashmap_create_options_s options; 667 | struct hashmap_s new_m; 668 | int flag; 669 | 670 | memset(&options, 0, sizeof(options)); 671 | options.initial_capacity = hashmap_capacity(m) * 2; 672 | options.hasher = m->hasher; 673 | 674 | if (0 == options.initial_capacity) { 675 | return 1; 676 | } 677 | 678 | flag = hashmap_create_ex(options, &new_m); 679 | 680 | if (0 != flag) { 681 | return flag; 682 | } 683 | 684 | /* copy the old elements to the new table */ 685 | flag = hashmap_iterate_pairs(m, hashmap_rehash_iterator, 686 | HASHMAP_PTR_CAST(void *, &new_m)); 687 | 688 | if (0 != flag) { 689 | return flag; 690 | } 691 | 692 | hashmap_destroy(m); 693 | 694 | /* put new hash into old hash structure by copying */ 695 | memcpy(m, &new_m, sizeof(struct hashmap_s)); 696 | 697 | return 0; 698 | } 699 | 700 | HASHMAP_ALWAYS_INLINE hashmap_uint32_t hashmap_clz(const hashmap_uint32_t x) { 701 | #if defined(_MSC_VER) 702 | unsigned long result; 703 | _BitScanReverse(&result, x); 704 | return 31 - HASHMAP_CAST(hashmap_uint32_t, result); 705 | #else 706 | return HASHMAP_CAST(hashmap_uint32_t, __builtin_clz(x)); 707 | #endif 708 | } 709 | 710 | #if defined(_MSC_VER) 711 | #pragma warning(pop) 712 | #endif 713 | 714 | #if defined(__clang__) 715 | #pragma clang diagnostic pop 716 | #endif 717 | 718 | #endif 719 | -------------------------------------------------------------------------------- /src/tasm/tasm.c: -------------------------------------------------------------------------------- 1 | #include "tasm.h" 2 | 3 | #define MAX_PROGRAM_SIZE 1024 4 | 5 | void push_program(Inst program[], int *program_size, Inst value){ 6 | assert(*program_size < MAX_PROGRAM_SIZE && "Program size cannot exceed max program size\n"); 7 | program[(*program_size)++] = value; 8 | } 9 | 10 | Inst pop_program(Inst program[], int program_size){ 11 | assert(program_size > 0 && "STACK UNDERFLOW\n"); 12 | return program[--program_size]; 13 | } 14 | 15 | size_t length_of_list(ParseList *head){ 16 | ParseList *tmp = head; 17 | size_t result = 1; 18 | while(tmp->next != NULL){ 19 | result += 1; 20 | tmp = tmp->next; 21 | } 22 | return result; 23 | } 24 | 25 | char *remove_first_character(char *str){ 26 | int length = strlen(str); 27 | char *result = malloc(sizeof(char) * length); 28 | for(int i = 0; i < length - 1; i++){ 29 | result[i] = str[i + 1]; 30 | } 31 | result[length - 1] = '\0'; 32 | return result; 33 | } 34 | 35 | size_t get_register_index(ParseList *head){ 36 | size_t result = (size_t)atoi(remove_first_character(head->value.text)); 37 | if(result >= AMOUNT_OF_REGISTERS){ 38 | fprintf(stderr, "error: register index is too great\n"); 39 | exit(1); 40 | } 41 | return result; 42 | } 43 | 44 | Inst *generate_instructions(ParseList *head, int *program_size, String_View str_stack[MAX_STACK_SIZE], size_t *entrypoint, bool *has_entrypoint){ 45 | Inst *program = malloc(sizeof(Inst) * length_of_list(head)); 46 | Inst_Set insts[INST_COUNT + 1] = { 47 | INST_NOP, INST_PUSH, INST_PUSH_STR, INST_MOV, INST_REF, INST_DEREF, 48 | INST_ALLOC, INST_DEALLOC, INST_WRITE, INST_READ, INST_POP, INST_DUP, INST_INDUP, INST_SWAP, INST_INSWAP, 49 | INST_ADD, INST_SUB, INST_MUL, INST_DIV, INST_MOD, INST_ADD_F, INST_SUB_F, 50 | INST_MUL_F, INST_DIV_F, INST_MOD_F, INST_CMPE, 51 | INST_CMPNE, INST_CMPG, INST_CMPL, INST_CMPGE, INST_CMPLE, INST_ITOF, INST_FTOI, INST_ITOC, 52 | INST_TOI, INST_TOF, INST_TOC, INST_TOVP, INST_CALL, INST_RET, 53 | INST_JMP, INST_ZJMP, INST_NZJMP, INST_PRINT, INST_NATIVE, INST_ENTRYPOINT, INST_SS, 54 | INST_HALT, INST_COUNT 55 | }; 56 | 57 | while(head != NULL){ 58 | assert(head->value.type != TYPE_NONE && "Value should not be none\n"); 59 | assert(head->value.type < (TokenType)INST_COUNT && "Incorrect value\n"); 60 | Inst *instruction = malloc(sizeof(Inst)); 61 | instruction->type = insts[head->value.type]; 62 | if( 63 | head->value.type == TYPE_CALL || head->value.type == TYPE_NATIVE || 64 | head->value.type == TYPE_JMP || head->value.type == TYPE_ZJMP || 65 | head->value.type == TYPE_NZJMP 66 | ){ 67 | head = head->next; 68 | instruction->value.as_int = atoi(head->value.text); 69 | instruction->data_type = INT_TYPE; 70 | } 71 | 72 | if(head->value.type == TYPE_ENTRYPOINT){ 73 | instruction->type = INST_NOP; 74 | head = head->next; 75 | if(!*has_entrypoint){ 76 | *entrypoint = (size_t)atoi(head->value.text); 77 | *has_entrypoint = true; 78 | } else { 79 | fprintf(stderr, "error: cannot define entrypoint more than once\n"); 80 | exit(1); 81 | } 82 | } 83 | 84 | 85 | if(head->value.type == TYPE_PUSH){ 86 | head = head->next; 87 | if(head->value.type == TYPE_INT){ 88 | instruction->value.as_int = atoi(head->value.text); 89 | instruction->data_type = INT_TYPE; 90 | } else if(head->value.type == TYPE_FLOAT){ 91 | instruction->value.as_float = atof(head->value.text); 92 | instruction->data_type = FLOAT_TYPE; 93 | } else if(head->value.type == TYPE_CHAR){ 94 | instruction->value.as_char = head->value.text[0]; 95 | instruction->data_type = CHAR_TYPE; 96 | } else if(check_if_register(head->value.type)){ 97 | instruction->register_index = get_register_index(head); 98 | instruction->data_type = REGISTER_TYPE; 99 | } else { 100 | assert(false && "you should not be here\n"); 101 | } 102 | } 103 | 104 | if(head->value.type == TYPE_MOV){ 105 | head = head->next; 106 | instruction->register_index = get_register_index(head); 107 | head = head->next; 108 | if(head->value.type == TYPE_INT){ 109 | instruction->value.as_int = atoi(head->value.text); 110 | instruction->data_type = INT_TYPE; 111 | } else if(head->value.type == TYPE_FLOAT){ 112 | instruction->value.as_float = atof(head->value.text); 113 | instruction->data_type = FLOAT_TYPE; 114 | } else if(head->value.type == TYPE_CHAR){ 115 | instruction->value.as_char = head->value.text[0]; 116 | instruction->data_type = CHAR_TYPE; 117 | } else if(head->value.type == TYPE_TOP){ 118 | instruction->data_type = TOP_TYPE; 119 | } else { 120 | assert(false && "you should not be here\n"); 121 | } 122 | } 123 | 124 | if(head->value.type == TYPE_PUSH_STR){ 125 | head = head->next; 126 | if(head->value.type != TYPE_STRING){ 127 | assert(false && "why are you here\n"); 128 | } 129 | instruction->type = INST_PUSH_STR; 130 | instruction->value.as_int = str_stack_size; 131 | instruction->data_type = INT_TYPE; 132 | size_t str_s = strlen(head->value.text)+1; 133 | str_stack[str_stack_size].len = str_s; 134 | str_stack[str_stack_size].data = malloc(sizeof(char)*str_s); 135 | strncpy(str_stack[str_stack_size++].data, head->value.text, str_s); 136 | } 137 | 138 | push_program(program, program_size, *instruction); 139 | free(instruction); 140 | head = head->next; 141 | } 142 | return program; 143 | } 144 | 145 | char *chop_file_by_dot(char *file_name){ 146 | int index; 147 | char *result = malloc(sizeof(char) * 64); 148 | for(index = 0; file_name[index] != '.' && file_name[index] != '\0'; index++){ 149 | result[index] = file_name[index]; 150 | } 151 | snprintf(result + index, 5, ".tim"); 152 | result = realloc(result, sizeof(char) * index); 153 | return result; 154 | } 155 | 156 | int main(int argc, char *argv[]){ 157 | if(argc < 2){ 158 | fprintf(stderr, "Usage: %s \n", argv[0]); 159 | exit(1); 160 | } 161 | 162 | char *file_name = argv[1]; 163 | char *output_file = chop_file_by_dot(file_name); 164 | Lexer lex = lexer(file_name); 165 | ParseList list = parser(lex); 166 | //print_list(&list); 167 | int program_size = 0; 168 | Machine machine; 169 | size_t entrypoint = 0; 170 | bool has_entrypoint = false; 171 | Inst *program = generate_instructions(&list, &program_size, machine.str_stack, &entrypoint, &has_entrypoint); 172 | machine.instructions = program; 173 | machine.program_size = program_size; 174 | machine.entrypoint = entrypoint; 175 | machine.has_entrypoint = has_entrypoint; 176 | machine.str_stack_size = str_stack_size; 177 | write_program_to_file(&machine, output_file); 178 | return 0; 179 | } 180 | -------------------------------------------------------------------------------- /src/tasm/tasm.h: -------------------------------------------------------------------------------- 1 | #ifndef TASM_H 2 | #define TASM_H 3 | 4 | #include 5 | #include 6 | 7 | #include "tasmlexer.h" 8 | #include "tasmparser.h" 9 | #include "tim.h" 10 | 11 | int str_stack_size = 0; 12 | 13 | void push_program(Inst program[], int *program_size, Inst value); 14 | Inst pop_program(Inst program[], int program_size); 15 | size_t length_of_list(ParseList *head); 16 | Inst *generate_instructions(ParseList *head, int *program_size, String_View str_stack[MAX_STACK_SIZE], size_t *entrypoint, bool *has_entrypoint); 17 | char *chop_file_by_dot(char *file_name); 18 | int main(int argc, char *argv[]); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/tasm/tasmlexer.c: -------------------------------------------------------------------------------- 1 | #include "tasmlexer.h" 2 | 3 | #define TIPP_IMPLEMENTATION 4 | #include "tipp.h" 5 | 6 | int is_name(char character){ 7 | if(isalpha(character) || character == '_'){ 8 | return 1; 9 | } 10 | return 0; 11 | } 12 | 13 | char *open_file(char *file_path, int *length){ 14 | FILE *file = fopen(file_path, "r"); 15 | if(!file){ 16 | fprintf(stderr, "error: could not find file: %s\n", file_path); 17 | exit(1); 18 | } 19 | char *current = {0}; 20 | 21 | fseek(file, 0, SEEK_END); 22 | *length = ftell(file); 23 | fseek(file, 0, SEEK_SET); 24 | 25 | current = malloc(sizeof(char) * *length); 26 | fread(current, 1, *length, file); 27 | if(!current){ 28 | fprintf(stderr, "error: could not read from file: %s\n", file_path); 29 | exit(1); 30 | } 31 | 32 | fclose(file); 33 | return current; 34 | } 35 | 36 | void push_token(Lexer *lexer, Token value){ 37 | assert(lexer->stack_size < MAX_TOKEN_STACK_SIZE && "stack overflow\n"); 38 | lexer->token_stack[lexer->stack_size++] = value; 39 | } 40 | 41 | Token pop_token(Lexer *lexer){ 42 | assert(lexer->stack_size > 0 && "stack underflow\n"); 43 | return lexer->token_stack[lexer->stack_size]; 44 | } 45 | 46 | char *token_type_text[TYPE_COUNT + 1] = { 47 | "nop","push","push_str","mov","ref","deref", 48 | "alloc", "dealloc", "write", "read", "pop","dup", 49 | "indup","swap","inswap","add","sub", "mul","div", "mod","add_f","sub_f","mul_f","div_f", 50 | "mod_f","cmpe","cmpne","cmpg","cmpl","cmpge","cmple","itof","ftoi","itoc","toi","tof","toc","tovp","call","ret","jmp","zjmp","nzjmp","print", 51 | "native","entrypoint","ss","halt","int","float","char","string","NULL","register","label_def","label","top","count" 52 | }; 53 | 54 | char *pretty_token(Token token){ 55 | if(token.type > TYPE_COUNT){ 56 | return "none"; 57 | } 58 | return(token_type_text[token.type]); 59 | } 60 | 61 | void print_token(Token token){ 62 | assert(&token != NULL && "error: token cannot be NULL\n"); 63 | printf("%s\n", pretty_token(token)); 64 | printf("text: %s, line: %d, character: %d\n", token.text, token.line, token.character); 65 | } 66 | 67 | Token init_token(TokenType type, char *text, int line, int character, char *file_name){ 68 | Token token = {.type = type, .text = text, .line = line, .character = character, .file_name = file_name}; 69 | return token; 70 | } 71 | 72 | TokenType check_builtin_keywords(char *name){ 73 | for(int i = 0; i < TYPE_COUNT; i++){ 74 | if(strcmp(name, token_type_text[i]) == 0){ 75 | return (TokenType)i; 76 | } 77 | } 78 | return TYPE_NONE; 79 | } 80 | 81 | TokenType check_label_type(char *current, int *current_index){ 82 | if(current[*current_index] == ':'){ 83 | return TYPE_LABEL_DEF; 84 | } 85 | return TYPE_LABEL; 86 | } 87 | 88 | TokenType check_if_register_type(char *name){ 89 | if(name[0] == 'r' && isdigit(name[1])){ 90 | return TYPE_REGISTER; 91 | } 92 | return TYPE_NONE; 93 | } 94 | 95 | Token generate_keyword(char *current, int *current_index, int *line, int *character, Lexer lex){ 96 | char *keyword_name = malloc(sizeof(char) * 1024); 97 | int keyword_length = 0; 98 | while(is_name(current[*current_index]) || isdigit(current[*current_index])){ 99 | keyword_name[keyword_length] = current[*current_index]; 100 | *current_index += 1; 101 | keyword_length++; 102 | } 103 | keyword_name[keyword_length] = '\0'; 104 | TokenType type = check_if_register_type(keyword_name); 105 | if(type == TYPE_NONE){ 106 | type = check_builtin_keywords(keyword_name); 107 | } 108 | 109 | if(type == TYPE_NONE){ 110 | type = check_label_type(current, current_index); 111 | } 112 | 113 | Token token = init_token(type, keyword_name, *line, *character, lex.file_name); 114 | // Increment character by lenth of keyword, also subtract one because iteration occurs in lexer function as well 115 | *character += keyword_length - 1; 116 | keyword_name = realloc(keyword_name, sizeof(char) * keyword_length); 117 | return token; 118 | } 119 | 120 | Token generate_num(char *current, int *current_index, int line, int *character, Lexer lex){ 121 | char *num_name = malloc(sizeof(char) * 16); 122 | int num_length = 0; 123 | 124 | if(current[*current_index] == '-'){ 125 | num_name[num_length] = '-'; 126 | *current_index += 1; 127 | num_length++; 128 | } 129 | 130 | while(isdigit(current[*current_index])){ 131 | num_name[num_length] = current[*current_index]; 132 | *current_index += 1; 133 | num_length++; 134 | } 135 | if(current[*current_index] != '.'){ 136 | num_name[num_length] = '\0'; 137 | TokenType type = TYPE_INT; 138 | Token token = init_token(type, num_name, line, *character, lex.file_name); 139 | return token; 140 | } 141 | num_name[num_length] = current[*current_index]; 142 | *current_index += 1; 143 | num_length++; 144 | while(isdigit(current[*current_index])){ 145 | num_name[num_length] = current[*current_index]; 146 | *current_index += 1; 147 | num_length++; 148 | } 149 | num_name[num_length] = '\0'; 150 | TokenType type = TYPE_FLOAT; 151 | Token token = init_token(type, num_name, line, *character, lex.file_name); 152 | 153 | // Increment character by lenth of number, also subtract one because iteration occurs in lexer function as well 154 | *character += num_length - 1; 155 | num_name = realloc(num_name, sizeof(char) * num_length); 156 | return token; 157 | } 158 | 159 | char valid_escape_character(char *current, int *current_index){ 160 | switch(current[*current_index]){ 161 | case 'n': 162 | return '\n'; 163 | case 't': 164 | return '\t'; 165 | case 'v': 166 | return '\v'; 167 | case 'b': 168 | return '\b'; 169 | case 'r': 170 | return '\r'; 171 | case 'f': 172 | return '\f'; 173 | case 'a': 174 | return '\a'; 175 | case '\\': 176 | return '\\'; 177 | case '?': 178 | return '\?'; 179 | case '\'': 180 | return '\''; 181 | case '\"': 182 | return '\"'; 183 | case '0': 184 | return '\0'; 185 | default: 186 | return ' '; 187 | } 188 | } 189 | 190 | Token generate_char(char *file_name, char *current, int *current_index, int line, int *character, Lexer lex){ 191 | char *character_name = malloc(sizeof(char) * 2); 192 | *current_index += 1; 193 | character_name[0] = current[*current_index]; 194 | if(current[*current_index] == '\\'){ 195 | *current_index += 1; 196 | char escape_character = valid_escape_character(current, current_index); 197 | if(escape_character == ' '){ 198 | fprintf(stderr, "%s:%d:%d error: invalid escape character\n", file_name, line, *character); 199 | exit(1); 200 | } 201 | character_name[0] = escape_character; 202 | } 203 | *current_index += 1; 204 | if(current[*current_index] != '\''){ 205 | fprintf(stderr, "%s:%d:%d error: expected close single quote\n", file_name, line, *character); 206 | exit(1); 207 | } 208 | 209 | character_name[1] = '\0'; 210 | 211 | TokenType type = TYPE_CHAR; 212 | Token token = init_token(type, character_name, line, *character, lex.file_name); 213 | // Increment character by 3 because of the character length, also subtract one because iteration occurs in lexer function as well 214 | *character += (3) - 1; 215 | return token; 216 | } 217 | 218 | Token generate_string(char *file_name, char *current, int *current_index, int line, int *character, Lexer lex){ 219 | char *string_name = malloc(sizeof(char) * 64); 220 | int string_index = 0; 221 | *current_index += 1; 222 | if(current[*current_index] == '\0'){ 223 | fprintf(stderr, "%s:%d:%d error: invalid string\n", file_name, line, *character); 224 | exit(1); 225 | } 226 | 227 | while(current[*current_index] != '"' && current[*current_index] != '\0'){ 228 | if(current[*current_index] == '\\'){ 229 | *current_index += 1; 230 | char escape_character = valid_escape_character(current, current_index); 231 | if(escape_character == ' '){ 232 | fprintf(stderr, "%s:%d:%d error: invalid escape character\n", file_name, line, *character); 233 | exit(1); 234 | } 235 | current[*current_index] = escape_character; 236 | } 237 | string_name[string_index] = current[*current_index]; 238 | *current_index += 1; 239 | string_index++; 240 | } 241 | 242 | if(current[*current_index] != '"'){ 243 | fprintf(stderr, "%s:%d:%d error: expected close quote\n", file_name, line, *character); 244 | exit(1); 245 | } 246 | 247 | string_name[string_index] = '\0'; 248 | 249 | 250 | TokenType type = TYPE_STRING; 251 | Token token = init_token(type, string_name, line, *character, lex.file_name); 252 | // Increment character by 3 because of the character length, also subtract one because iteration occurs in lexer function as well 253 | *character += (string_index) - 1; 254 | string_name = realloc(string_name, sizeof(char) * string_index); 255 | return token; 256 | } 257 | 258 | Lexer lexer(char *file_name){ 259 | int length = 0; 260 | char *current = prepro(file_name, &length, 0); 261 | //printf("%s\n", current); 262 | //printf("%d\n", length); 263 | 264 | int current_index = 0; 265 | int line = 1; 266 | int character = 1; 267 | 268 | Lexer lex = {.stack_size = 0, .file_name = file_name}; 269 | 270 | while(current_index < length){ 271 | if(current[current_index] == '\n'){ 272 | line++; 273 | character = 0; 274 | } 275 | 276 | if(is_name(current[current_index])){ 277 | Token token = generate_keyword(current, ¤t_index, &line, &character, lex); 278 | push_token(&lex, token); 279 | current_index--; 280 | } else if(isdigit(current[current_index])){ 281 | Token token = generate_num(current, ¤t_index, line, &character, lex); 282 | push_token(&lex, token); 283 | current_index--; 284 | } else if(current[current_index] == '-'){ 285 | Token token = generate_num(current, ¤t_index, line, &character, lex); 286 | push_token(&lex, token); 287 | current_index--; 288 | } else if(current[current_index] == '\''){ 289 | Token token = generate_char(file_name, current, ¤t_index, line, &character, lex); 290 | push_token(&lex, token); 291 | } else if(current[current_index] == '"'){ 292 | Token token = generate_string(file_name, current, ¤t_index, line, &character, lex); 293 | push_token(&lex, token); 294 | } else if(current[current_index] == ';'){ 295 | while(current[current_index] != '\n'){ 296 | current_index++; 297 | } 298 | line++; 299 | } else if(current[current_index] == '@'){ 300 | current_index++; 301 | if(current[current_index] != '"' || current_index > length){ 302 | fprintf(stderr, "error: expected open paren\n"); 303 | exit(1); 304 | } 305 | current_index++; 306 | if(current_index > length){ 307 | fprintf(stderr, "error: expected file path\n"); 308 | exit(1); 309 | } 310 | char *defined_file_path = get_filename(current, ¤t_index, length); 311 | lex.file_name = defined_file_path; 312 | 313 | current_index++; 314 | if(current_index > length){ 315 | fprintf(stderr, "error: expected file path\n"); 316 | exit(1); 317 | } 318 | current_index++; 319 | if(!isdigit(current[current_index]) || current_index > length){ 320 | fprintf(stderr, "error: expected file path\n"); 321 | exit(1); 322 | } 323 | Token num = generate_num(current, ¤t_index, line, &character, lex); 324 | current_index--; 325 | line = atoi(num.text) - 1; 326 | character = 0; 327 | } 328 | character++; 329 | current_index++; 330 | } 331 | 332 | for(int i = 0; i < lex.stack_size; i++){ 333 | //print_token(lex.token_stack[i]); 334 | } 335 | return lex; 336 | } 337 | -------------------------------------------------------------------------------- /src/tasm/tasmlexer.h: -------------------------------------------------------------------------------- 1 | #ifndef LEXER_H 2 | #define LEXER_H 3 | 4 | #define MAX_TOKEN_STACK_SIZE 2048 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | typedef enum { 14 | TYPE_NONE = -1, 15 | TYPE_NOP = 0, 16 | TYPE_PUSH, 17 | TYPE_PUSH_STR, 18 | TYPE_MOV, 19 | TYPE_REF, 20 | TYPE_DEREF, 21 | TYPE_ALLOC, 22 | TYPE_DEALLOC, 23 | TYPE_WRITE, 24 | TYPE_READ, 25 | TYPE_POP, 26 | TYPE_DUP, 27 | TYPE_INDUP, 28 | TYPE_SWAP, 29 | TYPE_INSWAP, 30 | TYPE_ADD, 31 | TYPE_SUB, 32 | TYPE_MUL, 33 | TYPE_DIV, 34 | TYPE_MOD, 35 | TYPE_ADD_F, 36 | TYPE_SUB_F, 37 | TYPE_MUL_F, 38 | TYPE_DIV_F, 39 | TYPE_MOD_F, 40 | TYPE_CMPE, 41 | TYPE_CMPNE, 42 | TYPE_CMPG, 43 | TYPE_CMPL, 44 | TYPE_CMPGE, 45 | TYPE_CMPLE, 46 | TYPE_ITOF, 47 | TYPE_FTOI, 48 | TYPE_ITOC, 49 | TYPE_TOI, 50 | TYPE_TOF, 51 | TYPE_TOC, 52 | TYPE_TOVP, 53 | TYPE_CALL, 54 | TYPE_RET, 55 | TYPE_JMP, 56 | TYPE_ZJMP, 57 | TYPE_NZJMP, 58 | TYPE_PRINT, 59 | TYPE_NATIVE, 60 | TYPE_ENTRYPOINT, 61 | TYPE_SS, 62 | TYPE_HALT, 63 | TYPE_INT, 64 | TYPE_FLOAT, 65 | TYPE_CHAR, 66 | TYPE_STRING, 67 | TYPE_NULL, 68 | TYPE_REGISTER, 69 | TYPE_LABEL_DEF, 70 | TYPE_LABEL, 71 | TYPE_TOP, 72 | TYPE_COUNT, 73 | } TokenType; 74 | 75 | typedef struct { 76 | TokenType type; 77 | char *text; 78 | int line; 79 | int character; 80 | char *file_name; 81 | } Token; 82 | 83 | typedef struct { 84 | Token token_stack[MAX_TOKEN_STACK_SIZE]; 85 | int stack_size; 86 | char *file_name; 87 | } Lexer; 88 | 89 | int is_name(char character); 90 | char *open_file(char *file_path, int *length); 91 | void push_token(Lexer *lexer, Token value); 92 | Token pop_token(Lexer *lexer); 93 | char *pretty_token(Token token); 94 | void print_token(Token token); 95 | Token init_token(TokenType type, char *text, int line, int character, char *file_name); 96 | TokenType check_builtin_keywords(char *name); 97 | TokenType check_label_type(char *current, int *current_index); 98 | Token generate_keyword(char *current, int *current_index, int *line, int *character, Lexer lex); 99 | Token generate_num(char *current, int *current_index, int line, int *character, Lexer lex); 100 | char valid_escape_character(char *current, int *current_index); 101 | Token generate_char(char *file_name, char *current, int *current_index, int line, int *character, Lexer lex); 102 | Token generate_string(char *file_name, char *current, int *current_index, int line, int *character, Lexer lex); 103 | Lexer lexer(); 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /src/tasm/tasmparser.c: -------------------------------------------------------------------------------- 1 | #include "tasmparser.h" 2 | 3 | void append(ParseList *head, Token value){ 4 | ParseList *new = malloc(sizeof(ParseList)); new->value = value; 5 | new->next = NULL; 6 | 7 | if(head == NULL){ 8 | head = new; 9 | return; 10 | } 11 | 12 | ParseList *tmp = head; 13 | while(tmp->next != NULL){ 14 | tmp = tmp->next; 15 | } 16 | tmp->next = new; 17 | } 18 | 19 | void print_list(ParseList *head){ 20 | while(head != NULL){ 21 | print_token(head->value); 22 | head = head->next; 23 | } 24 | } 25 | 26 | void handle_token_def(Lexer *lexer, Token token, int index, int line_num, struct hashmap_s *hashmap){ 27 | if (hashmap_remove(hashmap, lexer->token_stack[index].text, strlen(lexer->token_stack[index].text)) == 0) { 28 | fprintf(stderr, "%s:%d:%d: error: label '%s' already declared\n", token.file_name, lexer->token_stack[index].line, lexer->token_stack[index].character, lexer->token_stack[index].text); 29 | exit(1); 30 | } 31 | int *in = malloc(sizeof(int)); 32 | *in = line_num; 33 | 34 | int hashmap_error = hashmap_put(hashmap, lexer->token_stack[index].text, strlen(lexer->token_stack[index].text), in); 35 | assert(hashmap_error == 0 && "could not place item in hashmap\n"); 36 | 37 | lexer->token_stack[index].type = TYPE_NOP; 38 | } 39 | 40 | void print_syntax_error(Token *current_token, char *type_of_error, char *expected){ 41 | fprintf(stderr, "%s:%d:%d %s error: Expected %s but found %s\n", current_token->file_name, current_token->line, current_token->character, 42 | type_of_error, expected, pretty_token(*current_token)); 43 | exit(1); 44 | } 45 | 46 | int expect_token(Lexer *lexer, int index, int count, ...){ 47 | assert(index < lexer->stack_size); 48 | int result = 0; 49 | va_list list; 50 | va_start(list, count); 51 | for(int i = 0; i < count; i++){ 52 | if(lexer->token_stack[index].type == va_arg(list, int)){ 53 | result = 1; 54 | } 55 | } 56 | va_end(list); 57 | return result; 58 | } 59 | 60 | int check_if_register(TokenType type){ 61 | if(type != TYPE_REGISTER){ 62 | return 0; 63 | } 64 | return 1; 65 | } 66 | 67 | void generate_list(ParseList *root, Lexer *lexer, struct hashmap_s *hashmap){ 68 | int line_num = 0; 69 | for(int index = 0; index < lexer->stack_size; index++){ 70 | assert(lexer->token_stack[index].type != TYPE_NONE && "Should not be none\n"); 71 | Token current_token = lexer->token_stack[index]; 72 | 73 | if(expect_token(lexer, index, 6, TYPE_INT, TYPE_FLOAT, TYPE_CHAR, TYPE_LABEL, TYPE_STRING, TYPE_NULL)){ 74 | print_syntax_error(¤t_token, "unexpected token", "non-type"); 75 | } 76 | if(lexer->token_stack[index].type == TYPE_INT && lexer->token_stack[index].type == TYPE_FLOAT && 77 | lexer->token_stack[index].type == TYPE_CHAR && lexer->token_stack[index].type == TYPE_LABEL){ 78 | index -= 2; 79 | continue; 80 | } 81 | 82 | if(expect_token(lexer, index, 1, TYPE_LABEL_DEF)){ 83 | handle_token_def(lexer, current_token, index, line_num, hashmap); 84 | } 85 | 86 | append(root, lexer->token_stack[index]); 87 | 88 | if(expect_token(lexer, index, 5, TYPE_CALL, TYPE_JMP, TYPE_ZJMP, TYPE_NZJMP, TYPE_ENTRYPOINT)){ 89 | index++; 90 | current_token = lexer->token_stack[index]; 91 | if(lexer->token_stack[index].type != TYPE_INT && lexer->token_stack[index].type != TYPE_LABEL){ 92 | print_syntax_error(¤t_token, "syntax", "type of int or type of label"); 93 | } 94 | append(root, lexer->token_stack[index]); 95 | } 96 | 97 | if(expect_token(lexer, index, 1, TYPE_PUSH)){ 98 | index++; 99 | current_token = lexer->token_stack[index]; 100 | if(lexer->token_stack[index].type != TYPE_INT && lexer->token_stack[index].type != TYPE_NULL && 101 | lexer->token_stack[index].type != TYPE_CHAR && lexer->token_stack[index].type != TYPE_FLOAT && 102 | !check_if_register(lexer->token_stack[index].type) 103 | ){ 104 | print_syntax_error(¤t_token, "syntax", "type of int or type of float or type of char"); 105 | } 106 | append(root, lexer->token_stack[index]); 107 | } 108 | 109 | if(expect_token(lexer, index, 1, TYPE_PUSH_STR)){ 110 | index++; 111 | current_token = lexer->token_stack[index]; 112 | if(lexer->token_stack[index].type != TYPE_STRING){ 113 | print_syntax_error(¤t_token, "syntax", "type of int or type of float or type of char or type of string"); 114 | } 115 | append(root, lexer->token_stack[index]); 116 | } 117 | 118 | if(expect_token(lexer, index, 1, TYPE_NATIVE)){ 119 | index++; 120 | current_token = lexer->token_stack[index]; 121 | if(!expect_token(lexer, index, 1, TYPE_INT)){ 122 | print_syntax_error(¤t_token, "syntax", "type of int"); 123 | } 124 | append(root, lexer->token_stack[index]); 125 | } 126 | 127 | if(expect_token(lexer, index, 1, TYPE_MOV)){ 128 | index++; 129 | current_token = lexer->token_stack[index]; 130 | if(!check_if_register(current_token.type)){ 131 | print_syntax_error(¤t_token, "syntax", "a register"); 132 | } 133 | append(root, lexer->token_stack[index]); 134 | index++; 135 | current_token = lexer->token_stack[index]; 136 | if(lexer->token_stack[index].type != TYPE_INT && lexer->token_stack[index].type != TYPE_NULL && 137 | lexer->token_stack[index].type != TYPE_CHAR && lexer->token_stack[index].type != TYPE_FLOAT && lexer->token_stack[index].type != TYPE_TOP){ 138 | print_syntax_error(¤t_token, "syntax", "type of int or type of float or type of char, or top of stack"); 139 | } 140 | append(root, lexer->token_stack[index]); 141 | } 142 | line_num++; 143 | } 144 | } 145 | 146 | void check_labels(ParseList *head, Lexer *lexer, struct hashmap_s *hashmap){ 147 | while(head != NULL){ 148 | if(head->value.type == TYPE_LABEL){ 149 | void *const label_index = hashmap_get(hashmap, head->value.text, strlen(head->value.text)); 150 | if(label_index == NULL){ 151 | fprintf(stderr, "%s:%d:%d: error: undeclared label: '%s'\n", lexer->file_name, head->value.line, head->value.character, head->value.text); 152 | exit(1); 153 | } 154 | head->value.type = TYPE_INT; 155 | sprintf(head->value.text, "%d", *(int*)label_index); 156 | } 157 | head = head->next; 158 | } 159 | } 160 | 161 | ParseList parser(Lexer lexer){ 162 | const unsigned initial_size = 2; 163 | struct hashmap_s label_map; 164 | 165 | int hashmap_error = hashmap_create(initial_size, &label_map); 166 | assert(hashmap_error == 0 && "could not initialize hashmap\n"); 167 | 168 | ParseList root = {0}; 169 | generate_list(&root, &lexer, &label_map); 170 | root = *root.next; 171 | check_labels(&root, &lexer, &label_map); 172 | //print_list(&root); 173 | 174 | return root; 175 | } 176 | -------------------------------------------------------------------------------- /src/tasm/tasmparser.h: -------------------------------------------------------------------------------- 1 | #ifndef PARSER_H 2 | #define PARSER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "tasmlexer.h" 11 | 12 | #include "hashmap.h" 13 | 14 | typedef struct ParseList { 15 | Token value; 16 | struct ParseList *next; 17 | } ParseList; 18 | 19 | void append(ParseList *head, Token value); 20 | void print_list(ParseList *head); 21 | void handle_token_def(Lexer *lexer, Token token, int index, int line_num, struct hashmap_s *hashmap); 22 | void print_syntax_error(Token *current_token, char *type_of_error, char *expected); 23 | int expect_token(Lexer *lexer, int index, int count, ...); 24 | int check_if_register(TokenType type); 25 | void generate_list(ParseList *root, Lexer *lexer, struct hashmap_s *hashmap); 26 | void check_labels(ParseList *head, Lexer *lexer, struct hashmap_s *hashmap); 27 | ParseList parser(Lexer lexer); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/tim.c: -------------------------------------------------------------------------------- 1 | #include "tim.h" 2 | #define VIEW_IMPLEMENTATION 3 | #include "view.h" 4 | 5 | char *str_types[] = {"int", "float", "char", "ptr", "reg", "top"}; 6 | 7 | char *instructions[INST_COUNT] = { 8 | "nop", 9 | "push", 10 | "push_str", 11 | "mov", 12 | "ref", 13 | "deref", 14 | "alloc", 15 | "dealloc", 16 | "write", 17 | "read", 18 | "pop", 19 | "dup", 20 | "indup", 21 | "swap", 22 | "inswap", 23 | "add", 24 | "sub", 25 | "mul", 26 | "div", 27 | "mod", 28 | "and", 29 | "or", 30 | "add_f", 31 | "sub_f", 32 | "mul_f", 33 | "div_f", 34 | "mod_f", 35 | "cmpe", 36 | "cmpne", 37 | "cmpg", 38 | "cmpl", 39 | "cmpge", 40 | "cmple", 41 | "itof", 42 | "ftoi", 43 | "itoc", 44 | "toi", 45 | "tof", 46 | "toc", 47 | "tovp", 48 | "call", 49 | "ret", 50 | "jmp", 51 | "zjmp", 52 | "nzjmp", 53 | "print", 54 | "native", 55 | "entrypoint", 56 | "load_lib", 57 | "ss", 58 | "halt", 59 | }; 60 | 61 | bool has_operand[INST_COUNT] = { 62 | false, // "nop", 63 | true, // "push", 64 | true, // "push_str", 65 | true, // "mov", 66 | false, // "ref", 67 | false, // "deref", 68 | false, // "alloc", 69 | false, // "dealloc", 70 | false, // "write", 71 | false, // "read", 72 | false, // "pop", 73 | false, // "dup", 74 | false, // "indup", 75 | false, // "swap", 76 | false, // "inswap", 77 | false, // "add", 78 | false, // "sub", 79 | false, // "mul", 80 | false, // "div", 81 | false, // "mod", 82 | false, // "and", 83 | false, // "or", 84 | false, // "add_f", 85 | false, // "sub_f", 86 | false, // "mul_f", 87 | false, // "div_f", 88 | false, // "mod_f", 89 | false, // "cmpe", 90 | false, // "cmpne", 91 | false, // "cmpg", 92 | false, // "cmpl", 93 | false, // "cmpge", 94 | false, // "cmple", 95 | false, // "itof", 96 | false, // "ftoi", 97 | false, // "itoc", 98 | false, // "toi", 99 | false, // "tof", 100 | false, // "toc", 101 | false, // "tovp", 102 | true, // "call", 103 | false, // "ret", 104 | true, // "jmp", 105 | true, // "zjmp", 106 | true, // "nzjmp", 107 | false, // "print", 108 | true, // "native", 109 | true, // "entrypoint", 110 | false, // "load_lib", 111 | false, // "ss", 112 | false, // "halt", 113 | }; 114 | 115 | void free_cell(Memory **cell) { 116 | free((*cell)->cell.data); 117 | free(*cell); 118 | } 119 | 120 | void free_memory(Machine *machine, void *ptr) { 121 | Memory *cur = machine->memory; 122 | if(cur == NULL) goto defer; 123 | if(cur->cell.data == ptr) { 124 | machine->memory = cur->next; 125 | free_cell(&cur); 126 | return; 127 | } 128 | while(cur->next != NULL) { 129 | if(cur->next->cell.data == ptr) { 130 | Memory *cell = cur->next; 131 | cur->next = cur->next->next; 132 | free_cell(&cell); 133 | return; 134 | } 135 | cur = cur->next; 136 | } 137 | defer: 138 | TIM_ERROR("could not free pointer\n"); 139 | } 140 | 141 | void insert_memory(Machine *machine, size_t size) { 142 | Memory *new = malloc(sizeof(Memory)); 143 | memset(new, 0, sizeof(Memory)); 144 | new->cell.data = malloc(sizeof(*new->cell.data)*size); 145 | memset(new->cell.data, 0, sizeof(*new->cell.data)*size); 146 | new->next = machine->memory; 147 | machine->memory = new; 148 | } 149 | 150 | int64_t my_trunc(double num){ 151 | return (int64_t)num; 152 | } 153 | 154 | int64_t my_pow(int64_t base, int64_t num2){ 155 | int64_t result = 1; 156 | for(size_t i = 0; i < (size_t)num2; i++){ 157 | result *= base; 158 | } 159 | return result; 160 | } 161 | 162 | double my_fmod(double num1, double num2){ 163 | return num1 - my_trunc(num1 / num2) * num2; 164 | } 165 | 166 | char *reverse_string(char *str){ 167 | int length = strlen(str); 168 | int start = 0; 169 | int end = length - 1; 170 | char temp; 171 | 172 | while (start < end) { 173 | temp = str[start]; 174 | str[start] = str[end]; 175 | str[end] = temp; 176 | start++; 177 | end--; 178 | } 179 | return str; 180 | } 181 | 182 | int int_to_str(char *str, int *str_index, int64_t x){ 183 | if(x < 0){ 184 | str[*str_index] = '-'; 185 | *str_index += 1; 186 | x = -x; 187 | } 188 | if(x > 9){ 189 | int64_t new = x / 10; 190 | int_to_str(str, str_index, new); 191 | } 192 | x = (x % 10) + '0'; 193 | str[*str_index] = (char)(x); 194 | *str_index += 1; 195 | return 0; 196 | } 197 | 198 | int float_to_str(char *str, int *str_index, double x, int afterpoint){ 199 | int ipart = (int64_t)x; 200 | float fpart = x - (float)ipart; 201 | 202 | int_to_str(str, str_index, ipart); 203 | if(afterpoint != 0){ 204 | str[*str_index] = '.'; 205 | *str_index += 1; 206 | 207 | fpart = fpart * my_pow(10, afterpoint); 208 | if(fpart < 0){ 209 | fpart = -fpart; 210 | } 211 | int_to_str(str, str_index, (int64_t)fpart); 212 | } 213 | return 0; 214 | } 215 | 216 | void *get_stream(Word stream){ 217 | switch(stream.as_int){ 218 | case 0: 219 | return stdin; 220 | break; 221 | case 1: 222 | return stdout; 223 | break; 224 | case 2: 225 | return stderr; 226 | break; 227 | default: 228 | return stream.as_pointer; 229 | break; 230 | } 231 | } 232 | 233 | // native functions 234 | 235 | #define MODES_LENGTH 7 236 | char *open_modes[MODES_LENGTH] = {"r", "w", "wr", "a", "rb", "wb", "ab"}; 237 | 238 | void native_open(Machine *machine){ 239 | Word flag_mode = pop(machine).word; 240 | if(flag_mode.as_int > MODES_LENGTH - 1){ 241 | fprintf(stderr, "error: mode %ld out of bounds\n", flag_mode.as_int); 242 | exit(1); 243 | } 244 | Word path = pop(machine).word; 245 | char *mode = open_modes[flag_mode.as_int]; 246 | void *fd = fopen(path.as_pointer, mode); 247 | Word w = {.as_pointer=fd}; 248 | push(machine, w, PTR_TYPE); 249 | } 250 | 251 | void native_write(Machine *machine){ 252 | Word stream = pop(machine).word; 253 | char *str = (char*)pop(machine).word.as_pointer; 254 | stream.as_pointer = get_stream(stream); 255 | int length = strlen(str); 256 | fwrite(str, 1, length, stream.as_pointer); 257 | } 258 | 259 | void native_read(Machine *machine){ 260 | Word ptr = pop(machine).word; 261 | ptr.as_pointer = get_stream(ptr); 262 | int length = pop(machine).word.as_int; 263 | char *buffer = pop(machine).word.as_pointer; 264 | fread(buffer, 1, length, ptr.as_pointer); 265 | buffer[length] = '\0'; 266 | push_ptr(machine, (Word*)buffer); 267 | } 268 | 269 | void native_close(Machine *machine){ 270 | void *stream = pop(machine).word.as_pointer; 271 | fclose(stream); 272 | } 273 | 274 | void native_malloc(Machine *machine){ 275 | int num_of_bytes = pop(machine).word.as_int; 276 | void *ptr = malloc(sizeof(char) * num_of_bytes); 277 | push_ptr(machine, ptr); 278 | } 279 | 280 | void native_realloc(Machine *machine){ 281 | void *ptr = pop(machine).word.as_pointer; 282 | int num_of_bytes = pop(machine).word.as_int; 283 | void *result = realloc(ptr, sizeof(char) * num_of_bytes); 284 | push_ptr(machine, result); 285 | } 286 | 287 | void native_free(Machine *machine){ 288 | Word ptr = pop(machine).word; 289 | free(ptr.as_pointer); 290 | } 291 | 292 | void native_scanf(Machine *machine){ 293 | char *buffer = pop(machine).word.as_pointer; 294 | scanf("%s", buffer); 295 | push_ptr(machine, (Word*)buffer); 296 | } 297 | 298 | void native_pow(Machine *machine){ 299 | int64_t power = pop(machine).word.as_int; 300 | int64_t num = pop(machine).word.as_int; 301 | int64_t result = my_pow(power, num); 302 | Word w = {.as_int=result}; 303 | push(machine, w, INT_TYPE); 304 | } 305 | 306 | void native_time(Machine *machine){ 307 | time_t seconds; 308 | seconds = time(NULL); 309 | Word w = {.as_int=seconds}; 310 | push(machine, w, INT_TYPE); 311 | } 312 | 313 | void native_exit(Machine *machine){ 314 | int64_t code = pop(machine).word.as_int; 315 | exit(code); 316 | } 317 | 318 | // end native functions 319 | 320 | void push(Machine *machine, Word value, DataType type){ 321 | if(machine->stack_size >= MAX_STACK_SIZE){ 322 | TIM_ERROR("error: stack overflow\n"); 323 | } 324 | Data data; 325 | data.word = value; 326 | data.type = type; 327 | machine->stack[machine->stack_size++] = data; 328 | } 329 | 330 | void push_ptr(Machine *machine, Word *value){ 331 | if(machine->stack_size >= MAX_STACK_SIZE){ 332 | TIM_ERROR("error: stack overflow\n"); 333 | } 334 | machine->stack[machine->stack_size].type = PTR_TYPE; 335 | machine->stack[machine->stack_size++].word.as_pointer = value; 336 | } 337 | 338 | Data pop(Machine *machine){ 339 | if(machine->stack_size <= 0){ 340 | TIM_ERROR("error: stack underflow\n"); 341 | } 342 | machine->stack_size--; 343 | return machine->stack[machine->stack_size]; 344 | } 345 | 346 | void index_swap(Machine *machine, int64_t index){ 347 | if(index > machine->stack_size || index < 0){ 348 | TIM_ERROR("error: index out of range\n"); 349 | } 350 | Data temp_value = machine->stack[index]; 351 | machine->stack[index] = machine->stack[machine->stack_size - 1]; 352 | machine->stack[machine->stack_size - 1] = temp_value; 353 | } 354 | 355 | void index_dup(Machine *machine, int64_t index){ 356 | if(machine->stack_size <= 0){ 357 | TIM_ERROR("error: stack underflow\n"); 358 | } 359 | if(index > machine->stack_size || index < 0){ 360 | TIM_ERROR("error: index out of range\n"); 361 | } 362 | push(machine, machine->stack[index].word, machine->stack[index].type); 363 | } 364 | 365 | void print_stack(Machine *machine){ 366 | printf("------ STACK\n"); 367 | for(int i = machine->stack_size - 1; i >= 0; i--){ 368 | printf("as int: %ld, as float: %f, as char: %c, as pointer: %p\n", machine->stack[i].word.as_int, 369 | machine->stack[i].word.as_float, machine->stack[i].word.as_char, machine->stack[i].word.as_pointer); 370 | } 371 | printf("------ END OF STACK\n"); 372 | } 373 | 374 | int cmp_types(Data a){ 375 | switch(a.type){ 376 | case INT_TYPE: 377 | return 0; 378 | case FLOAT_TYPE: 379 | return 1; 380 | case CHAR_TYPE: 381 | return 2; 382 | case PTR_TYPE: 383 | return 3; 384 | case REGISTER_TYPE: 385 | case TOP_TYPE: 386 | default: 387 | return -1; 388 | } 389 | } 390 | 391 | void write_program_to_file(Machine *machine, char *file_path){ 392 | FILE *file = fopen(file_path, "wb"); 393 | if(file == NULL){ 394 | TIM_ERROR("error: could not write to file\n"); 395 | } 396 | fwrite(&machine->str_stack.count, sizeof(size_t), 1, file); 397 | for(int i = 0; i < (int)machine->str_stack.count; i++){ 398 | String_View str = machine->str_stack.data[i]; 399 | fwrite(&str.len, sizeof(size_t), 1, file); 400 | fwrite(str.data, sizeof(char), str.len, file); 401 | } 402 | 403 | fwrite(&machine->entrypoint, sizeof(size_t), 1, file); 404 | fwrite(machine->instructions.data, sizeof(machine->instructions.data[0]), machine->program_size, file); 405 | 406 | fclose(file); 407 | } 408 | 409 | Machine *read_program_from_file(Machine *machine, char *file_path){ 410 | FILE *file = fopen(file_path, "rb"); 411 | 412 | if(file == NULL){ 413 | TIM_ERROR("error: could not read from file\n"); 414 | } 415 | 416 | int index = 0; 417 | size_t length; 418 | fread(&machine->str_stack.count, 1, sizeof(size_t), file); 419 | machine->str_stack.data = malloc(sizeof(String_View)*machine->str_stack.count); 420 | for(size_t i = 0; i < machine->str_stack.count; i++) { 421 | size_t len = 0; 422 | fread(&len, 1, sizeof(size_t), file); 423 | char *str = malloc(sizeof(char)*len); 424 | fread(str, sizeof(char), len, file); 425 | machine->str_stack.data[i] = view_create(str, len); 426 | } 427 | index = ftell(file); 428 | 429 | 430 | fseek(file, 0, SEEK_END); 431 | length = ftell(file); 432 | fseek(file, index, SEEK_SET); 433 | length = length - ftell(file); 434 | fread(&machine->entrypoint, sizeof(size_t), 1, file); 435 | Inst *instructions = malloc(sizeof(Inst) * length); 436 | length = fread(instructions, sizeof(*instructions), length, file); 437 | 438 | machine->program_size = length; 439 | machine->instructions.data = instructions; 440 | 441 | fclose(file); 442 | return machine; 443 | } 444 | 445 | void handle_char_print(char c) { 446 | switch(c) { 447 | case '\n': 448 | putc('\\', stdout); 449 | putc('n', stdout); 450 | break; 451 | case '\t': 452 | putc('\\', stdout); 453 | putc('t', stdout); 454 | break; 455 | case '\v': 456 | putc('\v', stdout); 457 | putc('n', stdout); 458 | break; 459 | case '\b': 460 | putc('\b', stdout); 461 | putc('n', stdout); 462 | break; 463 | case '\r': 464 | putc('\\', stdout); 465 | putc('r', stdout); 466 | break; 467 | case '\f': 468 | putc('\\', stdout); 469 | putc('f', stdout); 470 | break; 471 | case '\a': 472 | putc('\\', stdout); 473 | putc('a', stdout); 474 | break; 475 | case '\\': 476 | putc('\\', stdout); 477 | putc('\\', stdout); 478 | break; 479 | case '\?': 480 | putc('\\', stdout); 481 | putc('?', stdout); 482 | break; 483 | case '\'': 484 | putc('\\', stdout); 485 | putc('\'', stdout); 486 | break; 487 | case '\"': 488 | putc('\\', stdout); 489 | putc('"', stdout); 490 | break; 491 | case '\0': 492 | putc('\\', stdout); 493 | putc('0', stdout); 494 | break; 495 | default: 496 | putc(c, stdout); 497 | } 498 | } 499 | 500 | void machine_disasm(Machine *machine) { 501 | for(size_t i = machine->entrypoint; i < machine->program_size; i++) { 502 | printf("%s", instructions[machine->instructions.data[i].type]); 503 | if(has_operand[machine->instructions.data[i].type]) { 504 | putc(' ', stdout); 505 | switch(machine->instructions.data[i].data_type) { 506 | case U64_TYPE: 507 | case U32_TYPE: 508 | case U16_TYPE: 509 | case U8_TYPE: 510 | case INT_TYPE: { 511 | int64_t value = machine->instructions.data[i].value.as_int; 512 | if(machine->instructions.data[i].type == INST_PUSH_STR) { 513 | String_View string = machine->str_stack.data[value]; 514 | putc('"', stdout); 515 | for(size_t j = 0; j < string.len-1; j++) { 516 | handle_char_print(string.data[j]); 517 | } 518 | putc('"', stdout); 519 | break; 520 | } 521 | printf("%ld", value); 522 | } break; 523 | case DOUBLE_TYPE: 524 | case FLOAT_TYPE: { 525 | printf("%f", machine->instructions.data[i].value.as_double); 526 | } break; 527 | case CHAR_TYPE: { 528 | putc('\'', stdout); 529 | handle_char_print(machine->instructions.data[i].value.as_char); 530 | putc('\'', stdout); 531 | } break; 532 | case PTR_TYPE: { 533 | printf("%p", machine->instructions.data[i].value.as_pointer); 534 | } break; 535 | default: 536 | assert(false && "UNReaCHABLE"); 537 | } 538 | } 539 | printf("\n"); 540 | } 541 | } 542 | 543 | void machine_free(Machine *machine) { 544 | Memory *cur = machine->memory; 545 | while(cur != NULL) { 546 | Memory *old = cur; 547 | cur = cur->next; 548 | free_cell(&old); 549 | } 550 | free(machine->instructions.data); 551 | free(machine->str_stack.data); 552 | } 553 | 554 | void machine_load_native(Machine *machine, native ptr) { 555 | ASSERT(ptr != NULL, "function pointer cannot be null: %s", dlerror()); 556 | machine->native_ptrs[machine->native_ptrs_s++] = ptr; 557 | } 558 | 559 | void run_instructions(Machine *machine) { 560 | machine_load_native(machine, native_write); 561 | machine_load_native(machine, native_exit); 562 | Data a, b; 563 | for(size_t ip = machine->entrypoint; ip < machine->program_size; ip++){ 564 | //print_stack(machine); 565 | switch(machine->instructions.data[ip].type){ 566 | case INST_NOP: 567 | continue; 568 | break; 569 | case INST_PUSH: 570 | if(machine->instructions.data[ip].data_type == REGISTER_TYPE){ 571 | push(machine, machine->registers[machine->instructions.data[ip].register_index].data, 572 | machine->registers[machine->instructions.data[ip].register_index].data_type); 573 | } else { 574 | push(machine, machine->instructions.data[ip].value, machine->instructions.data[ip].data_type); 575 | } 576 | break; 577 | case INST_PUSH_STR: { 578 | size_t index = machine->instructions.data[ip].value.as_int; 579 | String_View str = machine->str_stack.data[index]; 580 | insert_memory(machine, str.len+1); 581 | for(size_t i = 0; i < str.len; i++) { 582 | machine->memory->cell.data[i] = str.data[i]; 583 | } 584 | machine->memory->cell.data[str.len] = '\0'; 585 | Word word; 586 | word.as_pointer = machine->memory->cell.data; 587 | push(machine, word, PTR_TYPE); 588 | } break; 589 | case INST_MOV: 590 | if(machine->instructions.data[ip].data_type == TOP_TYPE){ 591 | machine->registers[machine->instructions.data[ip].register_index].data = machine->stack[machine->stack_size - 1].word; 592 | machine->registers[machine->instructions.data[ip].register_index].data_type = machine->stack[machine->stack_size - 1].type; 593 | } else { 594 | machine->registers[machine->instructions.data[ip].register_index].data = machine->instructions.data[ip].value; 595 | machine->registers[machine->instructions.data[ip].register_index].data_type = machine->instructions.data[ip].data_type; 596 | } 597 | break; 598 | case INST_REF: { 599 | Word *ptr = &machine->stack[machine->stack_size - 1].word; 600 | push_ptr(machine, ptr); 601 | } break; 602 | case INST_DEREF: { 603 | Word *ptr = machine->stack[machine->stack_size - 1].word.as_pointer; 604 | Data *ref = (Data*)ptr; 605 | push(machine, ref->word, ref->type); 606 | break; 607 | } 608 | case INST_ALLOC: { 609 | a = pop(machine); 610 | if(a.type != INT_TYPE) { 611 | TIM_ERROR("error: expected int"); 612 | } 613 | uint64_t val = a.word.as_int; 614 | insert_memory(machine, val); 615 | Word word; 616 | word.as_pointer = machine->memory->cell.data; 617 | push(machine, word, PTR_TYPE); 618 | } break; 619 | case INST_DEALLOC: { 620 | Data ptr = pop(machine); 621 | if(ptr.type != PTR_TYPE) { 622 | TIM_ERROR("error: expected ptr"); 623 | } 624 | free_memory(machine, ptr.word.as_pointer); 625 | } break; 626 | case INST_WRITE: { 627 | Data size = pop(machine); 628 | Data data = pop(machine); 629 | if(size.type != INT_TYPE) { 630 | TIM_ERROR("error: expected int"); 631 | } 632 | if(size.word.as_int < 0) { 633 | TIM_ERROR("error: size cannot be negative"); 634 | } 635 | Data ptr_data = pop(machine); 636 | if(ptr_data.type != PTR_TYPE) { 637 | TIM_ERROR("error: expected ptr"); 638 | } 639 | uint64_t index = size.word.as_int; 640 | void *ptr = ptr_data.word.as_pointer; 641 | memcpy(ptr, &data.word, index); 642 | } break; 643 | case INST_READ: { 644 | Data size = pop(machine); 645 | if(size.type != INT_TYPE) { 646 | TIM_ERROR("error: expected int"); 647 | } 648 | if(size.word.as_int < 0) { 649 | TIM_ERROR("error: size cannot be negative"); 650 | } 651 | Data ptr_data = pop(machine); 652 | if(ptr_data.type != PTR_TYPE) { 653 | TIM_ERROR("error: expected pointer"); 654 | } 655 | uint64_t index = size.word.as_int; 656 | void *ptr = ptr_data.word.as_pointer; 657 | Data data = {0}; 658 | data.type = INT_TYPE; 659 | memcpy(&data.word, ptr, index); 660 | push(machine, data.word, data.type); 661 | } break; 662 | case INST_POP: 663 | pop(machine); 664 | break; 665 | case INST_DUP: 666 | a = machine->stack[machine->stack_size - 1]; 667 | push(machine, a.word, a.type); 668 | break; 669 | case INST_INDUP: { 670 | Data index = pop(machine); 671 | if(index.type != INT_TYPE) { 672 | TIM_ERROR("error: expected int"); 673 | } 674 | index_dup(machine, machine->stack_size-index.word.as_int-1); 675 | } break; 676 | case INST_SWAP: { 677 | Data temp = machine->stack[machine->stack_size - 1]; 678 | machine->stack[machine->stack_size - 1] = machine->stack[machine->stack_size - 2]; 679 | machine->stack[machine->stack_size - 2] = temp; 680 | } break; 681 | case INST_INSWAP: { 682 | Data index = pop(machine); 683 | if(index.type != INT_TYPE) { 684 | TIM_ERROR("error: expected int"); 685 | } 686 | index_swap(machine, machine->stack_size-index.word.as_int-1); 687 | } break; 688 | case INST_ADD: 689 | if(machine->stack_size < 1) TIM_ERROR("error: stack underflow\n"); 690 | a = machine->stack[machine->stack_size - 1]; 691 | b = machine->stack[machine->stack_size - 2]; 692 | machine->stack_size -= 2; 693 | switch(a.type) { 694 | case PTR_TYPE: 695 | case U64_TYPE: 696 | TYPE_OP(as_u64, U64_TYPE, +); 697 | break; 698 | case CHAR_TYPE: 699 | case U8_TYPE: 700 | TYPE_OP(as_u8, U8_TYPE, +); 701 | break; 702 | case U16_TYPE: 703 | TYPE_OP(as_u16, U16_TYPE, +); 704 | break; 705 | case U32_TYPE: 706 | TYPE_OP(as_u32, U32_TYPE, +); 707 | break; 708 | case INT_TYPE: 709 | TYPE_OP(as_int, INT_TYPE, +); 710 | break; 711 | case FLOAT_TYPE: 712 | TYPE_OP(as_float, FLOAT_TYPE, +); 713 | break; 714 | case DOUBLE_TYPE: 715 | TYPE_OP(as_double, DOUBLE_TYPE, +); 716 | break; 717 | default: 718 | TIM_ERROR("error: not right...\n"); 719 | } 720 | break; 721 | case INST_SUB: 722 | if(machine->stack_size < 1) TIM_ERROR("error: stack underflow\n"); 723 | b = machine->stack[machine->stack_size - 1]; 724 | a = machine->stack[machine->stack_size - 2]; 725 | machine->stack_size -= 2; 726 | switch(a.type) { 727 | case PTR_TYPE: 728 | case U64_TYPE: 729 | TYPE_OP(as_u64, U64_TYPE, -); 730 | break; 731 | case CHAR_TYPE: 732 | case U8_TYPE: 733 | TYPE_OP(as_u8, U8_TYPE, -); 734 | break; 735 | case U16_TYPE: 736 | TYPE_OP(as_u16, U16_TYPE, -); 737 | break; 738 | case U32_TYPE: 739 | TYPE_OP(as_u32, U32_TYPE, -); 740 | break; 741 | case INT_TYPE: 742 | TYPE_OP(as_int, INT_TYPE, -); 743 | break; 744 | case FLOAT_TYPE: 745 | TYPE_OP(as_float, FLOAT_TYPE, -); 746 | break; 747 | case DOUBLE_TYPE: 748 | TYPE_OP(as_double, DOUBLE_TYPE, -); 749 | break; 750 | default: 751 | TIM_ERROR("error: not right...\n"); 752 | } 753 | break; 754 | case INST_MUL: 755 | if(machine->stack_size < 1) TIM_ERROR("error: stack underflow\n"); 756 | b = machine->stack[machine->stack_size - 1]; 757 | a = machine->stack[machine->stack_size - 2]; 758 | machine->stack_size -= 2; 759 | switch(a.type) { 760 | case PTR_TYPE: 761 | case U64_TYPE: 762 | TYPE_OP(as_u64, U64_TYPE, *); 763 | break; 764 | case CHAR_TYPE: 765 | case U8_TYPE: 766 | TYPE_OP(as_u8, U8_TYPE, *); 767 | break; 768 | case U16_TYPE: 769 | TYPE_OP(as_u16, U16_TYPE, *); 770 | break; 771 | case U32_TYPE: 772 | TYPE_OP(as_u32, U32_TYPE, *); 773 | break; 774 | case INT_TYPE: 775 | TYPE_OP(as_int, INT_TYPE, *); 776 | break; 777 | case FLOAT_TYPE: 778 | TYPE_OP(as_float, FLOAT_TYPE, *); 779 | break; 780 | case DOUBLE_TYPE: 781 | TYPE_OP(as_double, DOUBLE_TYPE, *); 782 | break; 783 | default: 784 | TIM_ERROR("error: not right...\n"); 785 | } 786 | break; 787 | case INST_DIV: 788 | if(machine->stack_size < 1) TIM_ERROR("error: stack underflow\n"); 789 | if(machine->stack[machine->stack_size - 1].word.as_int == 0) TIM_ERROR("error: cannot divide by 0\n"); 790 | b = machine->stack[machine->stack_size - 1]; 791 | a = machine->stack[machine->stack_size - 2]; 792 | machine->stack_size -= 2; 793 | switch(a.type) { 794 | case PTR_TYPE: 795 | case U64_TYPE: 796 | TYPE_OP(as_u64, U64_TYPE, /); 797 | break; 798 | case CHAR_TYPE: 799 | case U8_TYPE: 800 | TYPE_OP(as_u8, U8_TYPE, /); 801 | break; 802 | case U16_TYPE: 803 | TYPE_OP(as_u16, U16_TYPE, /); 804 | break; 805 | case U32_TYPE: 806 | TYPE_OP(as_u32, U32_TYPE, /); 807 | break; 808 | case INT_TYPE: 809 | TYPE_OP(as_int, INT_TYPE, /); 810 | break; 811 | case FLOAT_TYPE: 812 | TYPE_OP(as_float, FLOAT_TYPE, /); 813 | break; 814 | case DOUBLE_TYPE: 815 | TYPE_OP(as_double, DOUBLE_TYPE, /); 816 | break; 817 | default: 818 | TIM_ERROR("error: not right...\n"); 819 | } 820 | break; 821 | case INST_MOD: 822 | if(machine->stack_size < 1) TIM_ERROR("error: stack underflow\n"); 823 | if(machine->stack[machine->stack_size - 1].word.as_int == 0) TIM_ERROR("error: cannot divide by 0\n"); 824 | MATH_OP(as_int, %, INT_TYPE); 825 | break; 826 | case INST_AND: 827 | if(machine->stack_size < 1) TIM_ERROR("error: stack underflow\n"); 828 | MATH_OP(as_int, &&, INT_TYPE); 829 | break; 830 | case INST_OR: 831 | if(machine->stack_size < 1) TIM_ERROR("error: stack underflow\n"); 832 | MATH_OP(as_int, ||, INT_TYPE); 833 | break; 834 | case INST_ADD_F: 835 | MATH_OP(as_float, +, FLOAT_TYPE); 836 | break; 837 | case INST_SUB_F: 838 | MATH_OP(as_float, -, FLOAT_TYPE); 839 | break; 840 | case INST_MUL_F: 841 | MATH_OP(as_float, *, FLOAT_TYPE); 842 | break; 843 | case INST_DIV_F: 844 | if(machine->stack[machine->stack_size - 1].word.as_float == 0.0){ 845 | TIM_ERROR("error: cannot divide by 0\n"); 846 | } 847 | MATH_OP(as_float, /, FLOAT_TYPE); 848 | break; 849 | case INST_MOD_F: 850 | if(machine->stack[machine->stack_size - 1].word.as_float == 0.0){ 851 | TIM_ERROR("error: cannot divide by 0\n"); 852 | } 853 | b = pop(machine); 854 | a = pop(machine); 855 | Word c = {.as_float=my_fmod(a.word.as_float, b.word.as_float)}; 856 | push(machine, c, FLOAT_TYPE); 857 | break; 858 | case INST_CMPE: { 859 | b = machine->stack[machine->stack_size - 1]; 860 | a = machine->stack[machine->stack_size - 2]; 861 | machine->stack_size -= 2; 862 | TYPE_OP(as_u8, U8_TYPE, ==); 863 | } break; 864 | case INST_CMPNE: { 865 | b = machine->stack[machine->stack_size - 1]; 866 | a = machine->stack[machine->stack_size - 2]; 867 | machine->stack_size -= 2; 868 | TYPE_OP(as_u8, U8_TYPE, !=); 869 | } break; 870 | case INST_CMPG: { 871 | b = machine->stack[machine->stack_size - 1]; 872 | a = machine->stack[machine->stack_size - 2]; 873 | machine->stack_size -= 2; 874 | TYPE_OP(as_u8, U8_TYPE, >); 875 | } break; 876 | case INST_CMPL: { 877 | b = machine->stack[machine->stack_size - 1]; 878 | a = machine->stack[machine->stack_size - 2]; 879 | machine->stack_size -= 2; 880 | TYPE_OP(as_u8, U8_TYPE, <); 881 | } break; 882 | case INST_CMPGE: { 883 | b = machine->stack[machine->stack_size - 1]; 884 | a = machine->stack[machine->stack_size - 2]; 885 | machine->stack_size -= 2; 886 | TYPE_OP(as_u8, U8_TYPE, >=); 887 | } break; 888 | case INST_CMPLE: { 889 | b = machine->stack[machine->stack_size - 1]; 890 | a = machine->stack[machine->stack_size - 2]; 891 | machine->stack_size -= 2; 892 | TYPE_OP(as_u8, U8_TYPE, <=); 893 | } break; 894 | case INST_ITOF: 895 | a = pop(machine); 896 | a.word.as_float = (double)a.word.as_int; 897 | push(machine, a.word, FLOAT_TYPE); 898 | break; 899 | case INST_FTOI: 900 | a = pop(machine); 901 | a.word.as_int = (int64_t)a.word.as_float; 902 | push(machine, a.word, INT_TYPE); 903 | break; 904 | case INST_ITOC: 905 | a = pop(machine); 906 | a.word.as_char = (char)a.word.as_int; 907 | push(machine, a.word, CHAR_TYPE); 908 | break; 909 | case INST_TOI: 910 | machine->stack[machine->stack_size-1].type = INT_TYPE; 911 | break; 912 | case INST_TOF: 913 | machine->stack[machine->stack_size-1].type = FLOAT_TYPE; 914 | break; 915 | case INST_TOC: 916 | machine->stack[machine->stack_size-1].type = CHAR_TYPE; 917 | break; 918 | case INST_TOVP: 919 | machine->stack[machine->stack_size-1].type = PTR_TYPE; 920 | break; 921 | case INST_CALL: 922 | machine->return_stack[machine->return_stack_size++] = ip; 923 | ip = machine->instructions.data[ip].value.as_int - 1; 924 | break; 925 | case INST_RET: 926 | ip = machine->return_stack[--machine->return_stack_size]; 927 | break; 928 | case INST_JMP: 929 | if(machine->instructions.data[ip].value.as_int == 0) TIM_ERROR("error: cannot jump to 0\n"); 930 | ip = machine->instructions.data[ip].value.as_int - 1; 931 | if(ip + 1 >= machine->program_size){ 932 | TIM_ERROR("error: cannot jmp out of bounds to: %ld\n", machine->instructions.data[ip].value.as_int); 933 | } 934 | break; 935 | case INST_ZJMP: 936 | if(machine->instructions.data[ip].value.as_int == 0) TIM_ERROR("error: cannot jump to 0\n"); 937 | if(pop(machine).word.as_int == 0){ 938 | ip = machine->instructions.data[ip].value.as_int - 1; 939 | if(ip + 1 >= machine->program_size){ 940 | TIM_ERROR("error: cannot zjmp out of bounds to: %ld\n", machine->instructions.data[ip].value.as_int); 941 | } 942 | } else { 943 | break; 944 | } 945 | break; 946 | case INST_NZJMP: 947 | if(machine->instructions.data[ip].value.as_int == 0) TIM_ERROR("error: cannot jump to 0\n"); 948 | if(pop(machine).word.as_int != 0){ 949 | ip = machine->instructions.data[ip].value.as_int - 1; 950 | if(ip + 1 >= machine->program_size){ 951 | TIM_ERROR("error: cannot nzjmp out of bounds to: %ld\n", machine->instructions.data[ip].value.as_int); 952 | } 953 | } else { 954 | break; 955 | } 956 | break; 957 | case INST_PRINT: 958 | a = pop(machine); 959 | printf("as float: %f, as int: %ld, as char: %c, as pointer: %p, type: %s\n", 960 | a.word.as_float, a.word.as_int, a.word.as_char, a.word.as_pointer, str_types[a.type]); 961 | break; 962 | case INST_SS: 963 | push(machine, (Word){.as_int=machine->stack_size}, INT_TYPE); 964 | break; 965 | case INST_NATIVE: { 966 | machine->native_ptrs[machine->instructions.data[ip].value.as_int](machine); 967 | } break; 968 | case INST_ENTRYPOINT: 969 | assert(false); 970 | break; 971 | case INST_HALT: 972 | ip = machine->program_size; 973 | break; 974 | case INST_LOAD_LIBRARY: { 975 | char *lib_name = (char*)pop(machine).word.as_pointer; 976 | char *func_name = (char*)pop(machine).word.as_pointer; 977 | void *lib = dlopen(lib_name, RTLD_LAZY); 978 | if(!lib) { 979 | fprintf(stderr, "error loading lib: %s\n", dlerror()); 980 | exit(1); 981 | } 982 | native func; 983 | *(void**)(&func) = dlsym(lib, func_name); 984 | machine_load_native(machine, func); 985 | // WIP FIX THIS 986 | /* 987 | while(func_name[0] != '\0') { 988 | native func; 989 | *(void**)(&func) = dlsym(lib, func_name); 990 | if(!func) { 991 | fprintf(stderr, "error loading function: %s\n", dlerror()); 992 | exit(1); 993 | } 994 | machine_load_native(machine, func); 995 | func_name = (char*)pop(machine).word.as_pointer; 996 | } 997 | */ 998 | } break; 999 | case INST_COUNT: 1000 | assert(false); 1001 | } 1002 | } 1003 | for(size_t i = 2; i < machine->native_ptrs_s; i++) { 1004 | dlclose(machine->native_ptrs); 1005 | } 1006 | } 1007 | -------------------------------------------------------------------------------- /src/tim.h: -------------------------------------------------------------------------------- 1 | #ifndef TIM_H 2 | #define TIM_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | #include "view.h" 19 | 20 | #define MAX_STACK_SIZE 1024 21 | #define DATA_START_CAPACITY 16 22 | 23 | #define DA_APPEND(da, item) do { \ 24 | if ((da)->count >= (da)->capacity) { \ 25 | (da)->capacity = (da)->capacity == 0 ? DATA_START_CAPACITY : (da)->capacity*2; \ 26 | (da)->data = custom_realloc((da)->data, (da)->capacity*sizeof(*(da)->data)); \ 27 | ASSERT((da)->data != NULL, "outta ram"); \ 28 | } \ 29 | (da)->data[(da)->count++] = (item); \ 30 | } while (0) 31 | 32 | typedef enum { 33 | INST_NOP = 0, 34 | INST_PUSH, 35 | INST_PUSH_STR, 36 | INST_MOV, 37 | INST_REF, 38 | INST_DEREF, 39 | INST_ALLOC, 40 | INST_DEALLOC, 41 | INST_WRITE, 42 | INST_READ, 43 | INST_POP, 44 | INST_DUP, 45 | INST_INDUP, 46 | INST_SWAP, 47 | INST_INSWAP, 48 | INST_ADD, 49 | INST_SUB, 50 | INST_MUL, 51 | INST_DIV, 52 | INST_MOD, 53 | INST_AND, 54 | INST_OR, 55 | // TODO GET RID OF _F OPERATIONS AND REPLACE WITH SWITCH OVER DATA_TYPE 56 | INST_ADD_F, 57 | INST_SUB_F, 58 | INST_MUL_F, 59 | INST_DIV_F, 60 | INST_MOD_F, 61 | INST_CMPE, 62 | INST_CMPNE, 63 | INST_CMPG, 64 | INST_CMPL, 65 | INST_CMPGE, 66 | INST_CMPLE, 67 | INST_ITOF, 68 | INST_FTOI, 69 | INST_ITOC, 70 | INST_TOI, 71 | INST_TOF, 72 | INST_TOC, 73 | INST_TOVP, 74 | INST_CALL, 75 | INST_RET, 76 | INST_JMP, 77 | INST_ZJMP, 78 | INST_NZJMP, 79 | INST_PRINT, 80 | INST_NATIVE, 81 | INST_ENTRYPOINT, 82 | INST_LOAD_LIBRARY, 83 | INST_SS, 84 | INST_HALT, 85 | INST_COUNT, 86 | } Inst_Set; 87 | 88 | typedef enum { 89 | INT_TYPE = 0, 90 | U8_TYPE, 91 | U16_TYPE, 92 | U32_TYPE, 93 | U64_TYPE, 94 | FLOAT_TYPE, 95 | DOUBLE_TYPE, 96 | CHAR_TYPE, 97 | PTR_TYPE, 98 | REGISTER_TYPE, 99 | TOP_TYPE, 100 | } DataType; 101 | 102 | typedef union { 103 | int64_t as_int; 104 | uint8_t as_u8; 105 | uint16_t as_u16; 106 | uint32_t as_u32; 107 | uint64_t as_u64; 108 | float as_float; 109 | double as_double; 110 | char as_char; 111 | void *as_pointer; 112 | } Word; 113 | 114 | typedef struct { 115 | Word word; 116 | DataType type; 117 | } Data; 118 | 119 | typedef struct { 120 | Inst_Set type; 121 | Word value; 122 | DataType data_type; 123 | size_t register_index; 124 | } Inst; 125 | 126 | #define CMP_AS_TYPE(type, op) \ 127 | do{ \ 128 | if(b.word.type op a.word.type){ \ 129 | push(machine, yes, INT_TYPE); \ 130 | } else { \ 131 | push(machine, no, INT_TYPE); \ 132 | } \ 133 | } while(0) 134 | 135 | #define MATH_OP(as_type, op, data_type) \ 136 | do { \ 137 | b = pop(machine); \ 138 | a = pop(machine); \ 139 | Word c = {.as_type = a.word.as_type op b.word.as_type}; \ 140 | push(machine, c, data_type); \ 141 | } while(0) 142 | 143 | #define ASSERT(cond, ...) \ 144 | do { \ 145 | if (!(cond)) { \ 146 | fprintf(stderr, "%s:%d: ASSERTION FAILED: ", __FILE__, __LINE__); \ 147 | fprintf(stderr, __VA_ARGS__); \ 148 | fprintf(stderr, "\n"); \ 149 | exit(1); \ 150 | } \ 151 | } while (0) 152 | 153 | #define GET_TYPE(var, val) \ 154 | do { \ 155 | switch((var).type) { \ 156 | case INT_TYPE: (val) = (var).word.as_int; break; \ 157 | case U8_TYPE: (val) = (var).word.as_u8; break;\ 158 | case U16_TYPE: (val) = (var).word.as_u16; break; \ 159 | case U32_TYPE: (val) = (var).word.as_u32; break; \ 160 | case U64_TYPE: (val) = (var).word.as_u64; break; \ 161 | case FLOAT_TYPE: (val) = (var).word.as_float; break; \ 162 | case DOUBLE_TYPE: (val) = (var).word.as_double; break; \ 163 | case CHAR_TYPE: (val) = (var).word.as_char; break; \ 164 | case PTR_TYPE: (val) = (uint64_t)(var).word.as_pointer; break; \ 165 | default: ASSERT(false, "Unknown type"); \ 166 | } \ 167 | } while(0) 168 | 169 | #define TYPE_OP(as_type, return_type, op) \ 170 | do { \ 171 | switch(a.type) {\ 172 | case CHAR_TYPE:\ 173 | case PTR_TYPE:\ 174 | case U8_TYPE:\ 175 | case U16_TYPE:\ 176 | case U32_TYPE:\ 177 | case U64_TYPE: {\ 178 | uint64_t a_val;\ 179 | uint64_t b_val; \ 180 | GET_TYPE(a, a_val); \ 181 | GET_TYPE(b, b_val);\ 182 | push(machine, (Word){.as_type=(a_val op b_val)}, return_type);\ 183 | } break;\ 184 | case INT_TYPE: {\ 185 | int64_t a_val;\ 186 | int64_t b_val; \ 187 | GET_TYPE(a, a_val); \ 188 | GET_TYPE(b, b_val);\ 189 | push(machine, (Word){.as_type=(a_val op b_val)}, return_type);\ 190 | } break;\ 191 | case FLOAT_TYPE: {\ 192 | float a_val;\ 193 | float b_val; \ 194 | GET_TYPE(a, a_val); \ 195 | GET_TYPE(b, b_val);\ 196 | push(machine, (Word){.as_type=(a_val op b_val)}, return_type);\ 197 | } break;\ 198 | case DOUBLE_TYPE: {\ 199 | double a_val;\ 200 | double b_val; \ 201 | GET_TYPE(a, a_val); \ 202 | GET_TYPE(b, b_val);\ 203 | push(machine, (Word){.as_type=(a_val op b_val)}, return_type);\ 204 | } break;\ 205 | default:\ 206 | ASSERT(false, "Unknown type");\ 207 | } \ 208 | } while(0) 209 | 210 | #define TIM_ERROR(...) do { \ 211 | fprintf(stderr, __VA_ARGS__); exit(1); \ 212 | } while (0) 213 | 214 | 215 | #define AMOUNT_OF_REGISTERS 16 216 | #define MAX_STRING_SIZE 256 217 | 218 | typedef struct { 219 | Word data; 220 | DataType data_type; 221 | } Register; 222 | 223 | typedef struct { 224 | int8_t *data; 225 | size_t count; 226 | size_t capacity; 227 | } Memory_Cell; 228 | 229 | typedef struct Memory { 230 | struct Memory *next; 231 | Memory_Cell cell; 232 | } Memory; 233 | 234 | typedef struct { 235 | Inst *data; 236 | size_t count; 237 | size_t capacity; 238 | } Insts; 239 | 240 | typedef struct { 241 | String_View *data; 242 | size_t count; 243 | size_t capacity; 244 | } Str_Stack; 245 | 246 | struct Machine; 247 | 248 | typedef void (*native)(struct Machine*); 249 | 250 | typedef struct Machine { 251 | Data stack[MAX_STACK_SIZE]; 252 | int stack_size; 253 | Str_Stack str_stack; 254 | size_t return_stack[MAX_STACK_SIZE]; 255 | int return_stack_size; 256 | size_t program_size; 257 | 258 | Memory *memory; 259 | 260 | size_t entrypoint; 261 | bool has_entrypoint; 262 | 263 | Register registers[AMOUNT_OF_REGISTERS]; 264 | 265 | native native_ptrs[100]; 266 | size_t native_ptrs_s; 267 | 268 | Insts instructions; 269 | } Machine; 270 | 271 | // helper functions 272 | 273 | char *reverse_string(char *str); 274 | int int_to_str(char *str, int *str_index, int64_t x); 275 | void *get_stream(Word stream); 276 | 277 | // natives 278 | 279 | void native_open(Machine *machine); 280 | void native_write(Machine *machine); 281 | void native_read(Machine *machine); 282 | void native_close(Machine *machine); 283 | void native_malloc(Machine *machine); 284 | void native_free(Machine *machine); 285 | void native_exit(Machine *machine); 286 | void native_itoa(Machine *machine); 287 | 288 | // reverse_string 289 | 290 | void push_ptr(Machine *machine, Word *value); 291 | void push(Machine *machine, Word value, DataType type); 292 | void push_str(Machine *machine, char *value); 293 | Data pop(Machine *machine); 294 | void index_swap(Machine *machine, int64_t index); 295 | void index_dup(Machine *machine, int64_t index); 296 | void print_stack(Machine *machine); 297 | void write_program_to_file(Machine *machine, char *file_path); 298 | Machine *read_program_from_file(Machine *machine, char *file_path); 299 | void machine_disasm(Machine *machine); 300 | void machine_free(Machine *machine); 301 | void machine_load_native(Machine *machine, native ptr); 302 | void run_instructions(Machine *machine); 303 | 304 | 305 | #endif // TIM_H 306 | 307 | -------------------------------------------------------------------------------- /src/tipp.h: -------------------------------------------------------------------------------- 1 | #ifndef TIPP_H 2 | #define TIPP_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "hashmap.h" 11 | 12 | char *read_file_to_buff(char *file_name, int *length); 13 | char *get_word(char *buffer, int *index, int length); 14 | char *get_filename(char *buffer, int *index, int length); 15 | char *get_value(char *buffer, int *index, int length); 16 | void eof_error(char *buffer, int index, int length); 17 | char *prepro(char *file_name, int *length, int depth); 18 | void append_to_output(char *output, int *output_index, char *value, int value_length); 19 | char *pass(char *buffer, int length, int depth, char *file_name); 20 | 21 | #endif // TIPP_H 22 | 23 | #ifdef TIPP_IMPLEMENTATION 24 | #define TIPP_IMPLEMENTATION 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "hashmap.h" 33 | 34 | char *read_file_to_buff(char *file_name, int *length); 35 | char *get_word(char *buffer, int *index, int length); 36 | char *get_filename(char *buffer, int *index, int length); 37 | char *get_value(char *buffer, int *index, int length); 38 | void eof_error(char *buffer, int index, int length); 39 | char *prepro(char *file_name, int *length, int depth); 40 | void append_to_output(char *output, int *output_index, char *value, int value_length); 41 | char *pass(char *buffer, int length, int depth, char *file_name); 42 | 43 | const unsigned initial_size = 1; 44 | struct hashmap_s hashmap; 45 | int hashmap_initted = 0; 46 | 47 | char *read_file_to_buff(char *file_name, int *length){ 48 | FILE *file = fopen(file_name, "r"); 49 | if(file == NULL){ 50 | fprintf(stderr, "error: file not found: %s\n", file_name); 51 | exit(1); 52 | } 53 | 54 | char *current = {0}; 55 | fseek(file, 0, SEEK_END); 56 | *length = ftell(file); 57 | fseek(file, 0, SEEK_SET); 58 | 59 | current = malloc((sizeof(char)) * (*length)); 60 | fread(current, 1, *length, file); 61 | if(!current){ 62 | fprintf(stderr, "error: could not read from file: %s\n", file_name); 63 | fclose(file); 64 | exit(1); 65 | } 66 | 67 | fclose(file); 68 | return current; 69 | } 70 | 71 | char *get_word(char *buffer, int *index, int length){ 72 | char *word = malloc(sizeof(char) * length+1); 73 | int word_size = 0; 74 | while((isalpha(buffer[*index]) || buffer[*index] == '_') && *index < length){ 75 | word[word_size] = buffer[*index]; 76 | word_size++; 77 | *index += 1; 78 | } 79 | word[word_size] = '\0'; 80 | return word; 81 | } 82 | 83 | char *get_filename(char *buffer, int *index, int length){ 84 | char *word = malloc(sizeof(char) * 32); 85 | int word_size = 0; 86 | while((isalpha(buffer[*index]) || buffer[*index] == '.' || isdigit(buffer[*index])) && *index < length){ 87 | word[word_size] = buffer[*index]; 88 | word_size++; 89 | *index += 1; 90 | } 91 | word[word_size] = '\0'; 92 | return word; 93 | } 94 | 95 | char *get_value(char *buffer, int *index, int length){ 96 | char *value = malloc(sizeof(char) * 4); 97 | int value_size = 0; 98 | while(buffer[*index] != '\n' && *index < length){ 99 | value[value_size] = buffer[*index]; 100 | value_size++; 101 | *index += 1; 102 | } 103 | value[value_size] = '\0'; 104 | return value; 105 | } 106 | 107 | void eof_error(char *buffer, int index, int length){ 108 | if(buffer[index] == '\0' || buffer == NULL || index > length){ 109 | fprintf(stderr, "error: reached end of file\n"); 110 | exit(1); 111 | } 112 | } 113 | 114 | char *prepro(char *file_name, int *length, int depth){ 115 | if(!hashmap_initted){ 116 | int hashmap_error = hashmap_create(initial_size, &hashmap); 117 | assert(hashmap_error == 0 && "COULD NOT INITIALIZE HASHMAP\n"); 118 | hashmap_initted = 1; 119 | } 120 | char *buffer = read_file_to_buff(file_name, length); 121 | if(!buffer){ 122 | fprintf(stderr, "error reading file\n"); 123 | exit(1); 124 | } 125 | char *result = pass(buffer, *length, depth, file_name); 126 | *length = strlen(result) + 1; 127 | 128 | return(result); 129 | } 130 | 131 | void append_to_output(char *output, int *output_index, char *value, int value_length){ 132 | for(int i = 0; i < value_length; i++){ 133 | output[*output_index] = value[i]; 134 | *output_index += 1; 135 | } 136 | } 137 | 138 | char *pass(char *buffer, int length, int depth, char *file_name){ 139 | if(depth > 500){ 140 | fprintf(stderr, "error: recursive import detected\n"); 141 | exit(1); 142 | } 143 | int index = 0; 144 | int line = 1; 145 | char *output = calloc(length+1, sizeof(char)); 146 | int output_index = 0; 147 | while(index < length){ 148 | if(buffer[index] == '\n'){ 149 | line++; 150 | } 151 | if(buffer[index] == ';'){ 152 | while(buffer[index] != '\n' && buffer[index] != '\0'){ 153 | index++; 154 | } 155 | line++; 156 | } 157 | if(buffer[index] == '@'){ 158 | index++; 159 | eof_error(buffer, index, length); 160 | char *word = get_word(buffer, &index, length); 161 | if(strcmp(word, "def") == 0){ 162 | index++; 163 | eof_error(buffer, index, length); 164 | char *def = get_word(buffer, &index, length); 165 | index++; 166 | eof_error(buffer, index, length); 167 | char *value = get_value(buffer, &index, length); 168 | line++; 169 | int put_error = hashmap_put(&hashmap, def, strlen(def), value); 170 | assert(put_error == 0 && "COULD NOT PLACE INTO HASHMAP\n"); 171 | } else if(strcmp(word, "imp") == 0){ 172 | index++; 173 | eof_error(buffer, index, length); 174 | if(buffer[index] != '"'){ 175 | fprintf(stderr, "error: expected close paren"); 176 | exit(1); 177 | } 178 | index++; 179 | eof_error(buffer, index, length); 180 | char *imported_file = get_filename(buffer, &index, length); 181 | eof_error(buffer, index, length); 182 | if(buffer[index] != '"'){ 183 | fprintf(stderr, "error: expected close paren"); 184 | exit(1); 185 | } 186 | index++; 187 | eof_error(buffer, index, length); 188 | int imported_length = 0; 189 | char *imported_buffer = prepro(imported_file, &imported_length, depth + 1); 190 | imported_length = strlen(imported_buffer); 191 | char *file_info = malloc(sizeof(char) * 64); 192 | sprintf(file_info, "\n@\"%s\" %d\n", imported_file, 1); 193 | append_to_output(output, &output_index, file_info, strlen(file_info)); 194 | 195 | append_to_output(output, &output_index, imported_buffer, imported_length); 196 | 197 | sprintf(file_info, "\n@\"%s\" %d\n", file_name, line); 198 | line++; 199 | 200 | append_to_output(output, &output_index, file_info, strlen(file_info)); 201 | } else { 202 | fprintf(stderr, "Unexpected keyword: %s\n", word); 203 | exit(1); 204 | } 205 | } else if(isalpha(buffer[index])){ 206 | // set a temp index because we dont want to iterate index here 207 | int temp_index = index; 208 | char *word = get_word(buffer, &temp_index, length); 209 | char* const element = hashmap_get(&hashmap, word, strlen(word)); 210 | if(element){ 211 | // if found in hashmap, set the element value instead of word value 212 | for(size_t i = 0; i < strlen(element); i++){ 213 | output[output_index] = element[i]; 214 | output_index++; 215 | } 216 | index = temp_index; 217 | } else { 218 | // if not found in hashmap, set the word value instead of element value 219 | for(size_t i = 0; i < strlen(word); i++){ 220 | output[output_index] = word[i]; 221 | output_index++; 222 | } 223 | index = temp_index; 224 | } 225 | } 226 | 227 | output[output_index++] = buffer[index++]; 228 | } 229 | 230 | output[output_index-1] = '\0'; 231 | return(output); 232 | } 233 | 234 | #endif 235 | -------------------------------------------------------------------------------- /src/tire/tire.c: -------------------------------------------------------------------------------- 1 | #include "tire.h" 2 | 3 | void shift(int *argc, char ***argv) { 4 | *argc += 1; 5 | *argv += 1; 6 | } 7 | 8 | void print_usage(char *program) { 9 | fprintf(stderr, "Usage: %s -- \n", program); 10 | fprintf(stderr, "Flags: disasm\n"); 11 | exit(1); 12 | } 13 | 14 | int main(int argc, char **argv){ 15 | int disasm = 0; 16 | char *program = *argv; 17 | shift(&argc, &argv); 18 | char *flag = *argv; 19 | shift(&argc, &argv); 20 | if(flag == NULL) { 21 | print_usage(program); 22 | } 23 | 24 | char *filename = NULL; 25 | 26 | if(strncmp(flag, "--dis", 5) == 0) { 27 | disasm = 1; 28 | filename = *argv; 29 | if(filename == NULL) print_usage(program); 30 | } else { 31 | filename = flag; 32 | } 33 | 34 | assert(filename); 35 | 36 | Machine *machine = calloc(1, sizeof(Machine)); 37 | machine = read_program_from_file(machine, filename); 38 | if(disasm) { 39 | machine_disasm(machine); 40 | return 0; 41 | } 42 | run_instructions(machine); 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /src/tire/tire.h: -------------------------------------------------------------------------------- 1 | #ifndef _TIME_H 2 | #define _TIME_H 3 | 4 | #include 5 | #include 6 | 7 | #include "tim.h" 8 | 9 | int main(); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/view.h: -------------------------------------------------------------------------------- 1 | #ifndef VIEW_H 2 | #define VIEW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifndef VIEW_MALLOC 11 | #define VIEW_MALLOC malloc 12 | #endif 13 | 14 | typedef struct { 15 | const char *data; 16 | size_t len; 17 | } String_View; 18 | 19 | #define View_Print "%.*s" 20 | #define View_Arg(view) (int)view.len, view.data 21 | #define LITERAL_CREATE(lit) view_create(lit, sizeof(lit)-1) 22 | 23 | String_View view_create(const char *str, size_t len); 24 | char *view_to_cstr(String_View view); 25 | String_View view_trim_left(String_View view); 26 | String_View view_trim_right(String_View view); 27 | int view_cmp(String_View a, String_View b); 28 | int view_starts_with_c(String_View view, char c); 29 | int view_starts_with_s(String_View a, String_View b); 30 | int view_ends_with_c(String_View view, char c); 31 | int view_ends_with_s(String_View a, String_View b); 32 | int view_contains(String_View haystack, String_View needle); 33 | size_t view_first_of(String_View view, char target); 34 | size_t view_last_of(String_View view, char target); 35 | size_t view_split(String_View view, char c, String_View *arr, size_t arr_s); 36 | String_View view_chop(String_View view, char c); 37 | String_View view_chop_left(String_View view); 38 | size_t view_find(String_View haystack, String_View needle); 39 | int view_to_int(String_View view); 40 | float view_to_float(String_View view); 41 | #endif 42 | 43 | #ifdef VIEW_IMPLEMENTATION 44 | 45 | float power(float base, float exponent) { 46 | float result = 1; 47 | for(size_t i = 0; i < exponent; i++) { 48 | result *= base; 49 | } 50 | return result; 51 | } 52 | 53 | String_View view_create(const char *str, size_t len) { 54 | String_View view = { 55 | .data = str, 56 | .len = len, 57 | }; 58 | return view; 59 | } 60 | 61 | char *view_to_cstr(String_View view) { 62 | char *str = VIEW_MALLOC(sizeof(char) * view.len+1); 63 | strncpy(str, view.data, view.len); 64 | str[view.len] = '\0'; 65 | return str; 66 | } 67 | 68 | String_View view_trim_left(String_View view) { 69 | size_t i = 0; 70 | while(i < view.len && isspace(view.data[i])) { 71 | i++; 72 | } 73 | return (String_View){ 74 | .data = view.data + i, 75 | .len = view.len - i, 76 | }; 77 | } 78 | 79 | String_View view_trim_right(String_View view) { 80 | size_t i = view.len - 1; 81 | while(i < view.len && isspace(view.data[i])) { 82 | i--; 83 | } 84 | return (String_View){ 85 | .data = view.data, 86 | .len = i+1, 87 | }; 88 | } 89 | 90 | int view_cmp(String_View a, String_View b) { 91 | if(a.len != b.len) return 0; 92 | return memcmp(a.data, b.data, a.len) == 0; 93 | } 94 | 95 | int view_starts_with_c(String_View view, char c) { 96 | return view.data[0] == c; 97 | } 98 | 99 | int view_starts_with_s(String_View a, String_View b) { 100 | String_View compare = view_create(a.data, b.len); 101 | return view_cmp(compare, b); 102 | } 103 | 104 | int view_ends_with_c(String_View view, char c) { 105 | return view.data[view.len-1] == c; 106 | } 107 | 108 | int view_ends_with_s(String_View a, String_View b) { 109 | String_View compare = view_create(a.data + a.len - b.len, b.len); 110 | return view_cmp(compare, b); 111 | } 112 | 113 | int view_contains(String_View haystack, String_View needle) { 114 | if(needle.len > haystack.len) return 0; 115 | String_View compare = view_create(haystack.data, needle.len); 116 | for(size_t i = 0; i < haystack.len; i++) { 117 | compare.data = haystack.data + i; 118 | if(view_cmp(needle, compare)) { 119 | return 1; 120 | } 121 | } 122 | return 0; 123 | } 124 | 125 | size_t view_first_of(String_View view, char target) { 126 | for(size_t i = 0; i < view.len; i++) { 127 | if(view.data[i] == target) { 128 | return i; 129 | } 130 | } 131 | return 0; 132 | } 133 | 134 | size_t view_last_of(String_View view, char target) { 135 | for(size_t i = view.len-1; i > 0; i--) { 136 | if(view.data[i] == target) { 137 | return i; 138 | } 139 | } 140 | return 0; 141 | } 142 | 143 | // needs work 144 | size_t view_split(String_View view, char c, String_View *arr, size_t arr_s) { 145 | const char *cur = view.data; 146 | size_t arr_index = 0; 147 | size_t i; 148 | for(i = 0; i < view.len; i++) { 149 | if(view.data[i] == c) { 150 | if(arr_index < arr_s-2) { 151 | String_View new = {.data = cur, .len = view.data + i - cur}; 152 | arr[arr_index++] = new; 153 | cur = view.data + i + 1; 154 | } else { 155 | String_View new = {.data = view.data + i+1, .len = view.len - i-1}; 156 | arr[arr_index++] = new; 157 | return arr_index; 158 | } 159 | } 160 | } 161 | String_View new = {.data = cur, .len = view.data + i - cur}; 162 | arr[arr_index++] = new; 163 | return arr_index; 164 | } 165 | 166 | String_View view_chop(String_View view, char c) { 167 | size_t i = 0; 168 | while(view.data[i] != c && i != view.len) i++; 169 | if(i < view.len) { 170 | i++; 171 | } 172 | return (String_View) { 173 | .data = view.data + i, 174 | .len = view.len - i, 175 | }; 176 | } 177 | 178 | String_View view_chop_left(String_View view) { 179 | if(view.len > 0) { 180 | view.data++; 181 | view.len--; 182 | } 183 | return view; 184 | } 185 | 186 | size_t view_find(String_View haystack, String_View needle) { 187 | if(needle.len > haystack.len) return 0; 188 | String_View compare = view_create(haystack.data, needle.len); 189 | for(size_t i = 0; i < haystack.len; i++) { 190 | compare.data = haystack.data + i; 191 | if(view_cmp(needle, compare)) { 192 | return i; 193 | } 194 | } 195 | return 0; 196 | } 197 | 198 | int view_to_int(String_View view) { 199 | int result = 0; 200 | for(size_t i = 0; i < view.len; i++) { 201 | result = result * 10 + view.data[i] - '0'; 202 | } 203 | return result; 204 | } 205 | 206 | float view_to_float(String_View view) { 207 | float result = 0; 208 | size_t dotpos = 0; 209 | for(size_t i = 0; i < view.len; i++) { 210 | if (view.data[i] == '.') { 211 | dotpos = view.len - i - 1; 212 | } else { 213 | result = result * 10 + (view.data[i] - '0'); 214 | } 215 | } 216 | result /= power(10, dotpos); 217 | return result; 218 | } 219 | 220 | #endif 221 | -------------------------------------------------------------------------------- /tests/escape_character.t1: -------------------------------------------------------------------------------- 1 |  2 |     \?'+++++++++++ -------------------------------------------------------------------------------- /tests/escape_character.tasm: -------------------------------------------------------------------------------- 1 | push '\0' 2 | push '\n' 3 | push '\t' 4 | push '\v' 5 | push '\b' 6 | push '\r' 7 | push '\f' 8 | push '\a' 9 | push '\\' 10 | push '\?' 11 | push '\'' 12 | print 13 | print 14 | print 15 | print 16 | print 17 | print 18 | print 19 | print 20 | print 21 | print 22 | print 23 | -------------------------------------------------------------------------------- /tests/exit.tasm: -------------------------------------------------------------------------------- 1 | @imp "stddefs.tash" 2 | main: 3 | push_str "test\n" 4 | push STDOUT 5 | nwrite 6 | push 69 7 | exit 8 | 9 | entrypoint main 10 | -------------------------------------------------------------------------------- /tests/fibiter.tasm: -------------------------------------------------------------------------------- 1 | @imp "stddefs.tash" 2 | @def N 30 3 | 4 | //push_str "\n" 5 | //push_str "# of iterations " 6 | 7 | push N 8 | push 1 9 | push 1 10 | push 0 11 | 12 | loop: 13 | inswap 0 14 | dup 15 | push 0 16 | cmpe 17 | nzjmp end 18 | pop 19 | inswap 0 20 | 21 | indup 2 22 | inswap 1 23 | pop 24 | dup 25 | inswap 2 26 | pop 27 | indup 1 28 | indup 2 29 | add 30 | swap 31 | 32 | int_to_str 33 | push STDOUT 34 | nwrite 35 | 36 | inswap 0 37 | push 1 38 | sub 39 | inswap 0 40 | 41 | jmp loop 42 | 43 | end: 44 | 45 | push N 46 | int_to_str 47 | push STDOUT 48 | nwrite 49 | -------------------------------------------------------------------------------- /tests/fibrec.tasm: -------------------------------------------------------------------------------- 1 | @imp "stddefs.tash" 2 | @def N 30 3 | push_str "\n" 4 | push_str "# of iterations " 5 | entrypoint main 6 | fib: 7 | push 2 8 | swap 9 | cmpl 10 | zjmp else 11 | swap 12 | pop 13 | ret 14 | else: 15 | swap 16 | pop 17 | dup 18 | push 1 19 | sub 20 | call fib 21 | swap 22 | push 2 23 | sub 24 | call fib 25 | add 26 | endfib: 27 | ret 28 | 29 | main: 30 | push 0 31 | loop: 32 | push N 33 | cmpge 34 | zjmp end 35 | pop 36 | dup 37 | call fib 38 | int_to_str 39 | push STDOUT 40 | write 41 | get_str 0 42 | push STDOUT 43 | write 44 | push 1 45 | add 46 | jmp loop 47 | end: 48 | get_str 1 49 | push STDOUT 50 | write 51 | push N 52 | int_to_str 53 | push STDOUT 54 | write 55 | get_str 0 56 | push STDOUT 57 | write 58 | push 11 59 | exit 60 | -------------------------------------------------------------------------------- /tests/fizzbuzz.tasm: -------------------------------------------------------------------------------- 1 | @imp "stddefs.tash" 2 | @def N 100 3 | jmp main 4 | push_str "Fizz" 5 | push_str "Buzz" 6 | push_str "\n" 7 | 8 | handle_three: 9 | get_str 0 10 | push STDOUT 11 | write 12 | inswap 0 13 | push 1 14 | add 15 | inswap 0 16 | jmp three_continue 17 | 18 | handle_five: 19 | get_str 1 20 | push STDOUT 21 | write 22 | inswap 0 23 | push 1 24 | add 25 | inswap 0 26 | jmp five_continue 27 | 28 | handle_number: 29 | dup 30 | int_to_str 31 | push STDOUT 32 | write 33 | get_str 2 34 | push STDOUT 35 | write 36 | jmp number_continue 37 | 38 | main: 39 | push 1 40 | 41 | loop: 42 | push 0 43 | inswap 0 44 | 45 | dup 46 | push 3 47 | mod 48 | zjmp handle_three 49 | three_continue: 50 | 51 | dup 52 | push 5 53 | mod 54 | zjmp handle_five 55 | five_continue: 56 | 57 | 58 | 59 | inswap 0 60 | zjmp handle_number 61 | get_str 2 62 | push STDOUT 63 | write 64 | number_continue: 65 | 66 | push 1 67 | add 68 | 69 | push N 70 | cmpl 71 | nzjmp end 72 | pop 73 | 74 | 75 | jmp loop 76 | 77 | end: 78 | push 0 79 | exit 80 | 81 | -------------------------------------------------------------------------------- /tests/helloworld.tasm: -------------------------------------------------------------------------------- 1 | @imp "stddefs.tash" 2 | main: 3 | push_str "Hello, world!\n" 4 | push STDOUT 5 | nwrite 6 | 7 | entrypoint main 8 | -------------------------------------------------------------------------------- /tests/inttostr.tasm: -------------------------------------------------------------------------------- 1 | @imp "stddefs.tash" 2 | push_str "\n" 3 | push 420 ; number to convert to string 4 | int_to_str 5 | push STDOUT 6 | write ; write number 7 | get_str 0 8 | push STDOUT 9 | write ; write \n 10 | -------------------------------------------------------------------------------- /tests/label.tasm: -------------------------------------------------------------------------------- 1 | jmp main 2 | 3 | 4 | test: 5 | push 10 6 | push 4 7 | add 8 | print 9 | jmp end 10 | 11 | 12 | 13 | 14 | main: 15 | push 5 16 | push 7 17 | add 18 | print 19 | jmp test 20 | 21 | 22 | end: 23 | push 2 24 | push 5 25 | add 26 | print 27 | jmp realend 28 | 29 | 30 | realend: 31 | push 15 32 | print 33 | jmp theend 34 | 35 | 36 | theend: 37 | push 99 38 | print 39 | -------------------------------------------------------------------------------- /tests/linuxsyscalls.tash: -------------------------------------------------------------------------------- 1 | @def SYS_READ 0 2 | @def SYS_WRITE 1 3 | @def SYS_OPEN 2 4 | @def SYS_EXIT 60 5 | -------------------------------------------------------------------------------- /tests/macrotest.tasm: -------------------------------------------------------------------------------- 1 | @imp "stddefs.tash" 2 | @imp "linuxsyscalls.tash" 3 | @def N 100 4 | push N 5 | print 6 | 7 | push STDIN 8 | push STDOUT 9 | push STDERR 10 | print 11 | print 12 | print 13 | 14 | ; syscall numbers 15 | push SYS_READ 16 | push SYS_WRITE 17 | push SYS_OPEN 18 | push SYS_EXIT 19 | print 20 | print 21 | print 22 | print 23 | -------------------------------------------------------------------------------- /tests/negative.tasm: -------------------------------------------------------------------------------- 1 | @imp "stddefs.tash" 2 | push -35 3 | push -34 4 | add 5 | int_to_str 6 | push STDOUT 7 | nwrite 8 | push_str "\n" 9 | push STDOUT 10 | nwrite 11 | -------------------------------------------------------------------------------- /tests/open.tasm: -------------------------------------------------------------------------------- 1 | @imp "stddefs.tash" 2 | push 1 3 | push_str "push 10\nprint\n" 4 | push_str "Enter the file name: " 5 | get_str 1 6 | push STDOUT 7 | write 8 | push 4 9 | malloc 10 | push 8 11 | push STDIN 12 | read 13 | push 1 14 | open 15 | dup 16 | inswap 0 17 | pop 18 | get_str 0 19 | swap 20 | write 21 | close 22 | -------------------------------------------------------------------------------- /tests/power.tasm: -------------------------------------------------------------------------------- 1 | @imp "stddefs.tash" 2 | @imp "std.tash" 3 | 4 | main: 5 | mov r0 2 6 | mov r1 20 7 | call power 8 | call printint 9 | call print_newline 10 | 11 | entrypoint main 12 | -------------------------------------------------------------------------------- /tests/printfloat.tasm: -------------------------------------------------------------------------------- 1 | @imp "std.tash" 2 | @imp "stddefs.tash" 3 | main: 4 | push 69.420 5 | call printfloat 6 | call print_newline 7 | 8 | entrypoint main 9 | -------------------------------------------------------------------------------- /tests/printint.tasm: -------------------------------------------------------------------------------- 1 | @imp "std.tash" 2 | @imp "stddefs.tash" 3 | 4 | main: 5 | push 69420 6 | call printint 7 | push_str "\n" 8 | push STDOUT 9 | nwrite 10 | 11 | entrypoint main 12 | -------------------------------------------------------------------------------- /tests/random.tasm: -------------------------------------------------------------------------------- 1 | @imp "stddefs.tash" 2 | @imp "std.tash" 3 | 4 | main: 5 | native 10 6 | mov r0 top 7 | call random 8 | call print_newline 9 | 10 | entrypoint main 11 | -------------------------------------------------------------------------------- /tests/read.tasm: -------------------------------------------------------------------------------- 1 | @imp "stddefs.tash" 2 | push 14 3 | malloc 4 | push 14 5 | push STDIN 6 | read 7 | free 8 | -------------------------------------------------------------------------------- /tests/std.tash: -------------------------------------------------------------------------------- 1 | print_newline: 2 | push '\n' 3 | ref 4 | push 1 5 | native 1 6 | pop 7 | ret 8 | 9 | power: 10 | push r0 11 | _power_loop: 12 | push r1 13 | push 1 14 | cmpge 15 | swap 16 | pop 17 | swap 18 | pop 19 | nzjmp _power_end 20 | push r0 21 | mul 22 | push r1 23 | push 1 24 | sub 25 | mov r1 top 26 | pop 27 | jmp _power_loop 28 | _power_end: 29 | ret 30 | 31 | random: 32 | mov r1 502312312 ;a 33 | mov r2 121239838 ;b 34 | mov r3 9998912389;m 35 | push 8 ;n 36 | _rand_loop: 37 | push 0 38 | cmpl 39 | swap 40 | pop 41 | zjmp _rand_end 42 | push r1 43 | push r0 44 | mul 45 | push r2 46 | add 47 | push r3 48 | mod 49 | mov r0 top 50 | call printint 51 | push 1 52 | sub 53 | jmp _rand_loop 54 | _rand_end: 55 | pop 56 | push r0 57 | ret 58 | 59 | 60 | 61 | convert: 62 | push 0 63 | cmpg 64 | zjmp _not_neg 65 | push '-' 66 | ref 67 | push 1 68 | native 1 69 | pop 70 | swap 71 | push 0 72 | swap 73 | sub 74 | swap 75 | _not_neg: 76 | pop 77 | push 9 78 | cmpl 79 | zjmp _lessthannine 80 | pop 81 | dup 82 | push 10 83 | div 84 | call convert 85 | _lessthannine: 86 | pop 87 | push 10 88 | mod 89 | push 48 90 | add 91 | ref 92 | push 1 93 | native 1 94 | ret 95 | 96 | printint: 97 | call convert 98 | pop 99 | ret 100 | 101 | printfloat: 102 | dup 103 | ftoi 104 | dup 105 | inswap 0 106 | swap 107 | itof 108 | sub_f 109 | mov r0 top 110 | pop 111 | call printint 112 | push '.' 113 | ref 114 | push 1 115 | native 1 116 | push r0 117 | mov r0 10 118 | mov r1 8 119 | call power 120 | itof 121 | mul_f 122 | ftoi 123 | push 0 124 | cmpg 125 | zjmp _notneg 126 | pop 127 | dup 128 | push 2 129 | mul 130 | sub 131 | jmp _endneg 132 | _notneg: 133 | pop 134 | _endneg: 135 | 136 | call printint 137 | pop 138 | ret 139 | 140 | _strcmp_not_equal: 141 | push 1 142 | sub 143 | jmp _strcmp_end 144 | 145 | stringcmp: 146 | push 1 147 | _strcmp_loop: 148 | push r0 149 | deref 150 | swap 151 | push 1 152 | add 153 | mov r0 top 154 | pop 155 | push r1 156 | deref 157 | swap 158 | push 1 159 | add 160 | mov r1 top 161 | pop 162 | cmpe 163 | swap 164 | pop 165 | swap 166 | pop 167 | zjmp _strcmp_not_equal 168 | push '\0' 169 | push r0 170 | deref 171 | swap 172 | pop 173 | cmpe 174 | swap 175 | pop 176 | swap 177 | pop 178 | zjmp _strcmp_loop 179 | _strcmp_end: 180 | ret 181 | 182 | stringlen: 183 | push '\0' 184 | mov r0 top 185 | pop 186 | push 0 187 | swap 188 | _strlen_loop: 189 | deref 190 | push r0 191 | cmpe 192 | swap 193 | pop 194 | swap 195 | pop 196 | ;native 90 ;strcmp 197 | nzjmp _strlen_end 198 | push 1 199 | add 200 | swap 201 | push 1 202 | add 203 | swap 204 | jmp _strlen_loop 205 | _strlen_end: 206 | pop 207 | ret 208 | -------------------------------------------------------------------------------- /tests/stddefs.tash: -------------------------------------------------------------------------------- 1 | @def STDIN 0 2 | @def STDOUT 1 3 | @def STDERR 2 4 | 5 | @def RONLY 0 6 | @def WONLY 1 7 | @def RDWR 2 8 | @def CREAT 64 9 | @def EXCL 128 10 | 11 | @def open native 0 12 | @def nwrite native 1 13 | @def nread native 2 14 | @def close native 3 15 | @def malloc native 4 16 | @def realloc native 5 17 | @def free native 6 18 | @def scanf native 7 19 | @def pow native 8 20 | @def time native 10 21 | @def exit native 60 22 | @def strcmp native 90 23 | @def strcpy native 91 24 | @def memcpy native 92 25 | @def strcat native 93 26 | @def strlen native 94 27 | @def float_to_str native 98 28 | @def int_to_str native 99 29 | @def assert native 100 30 | 31 | -------------------------------------------------------------------------------- /tests/stringcmp.tasm: -------------------------------------------------------------------------------- 1 | push_str "abc" 2 | push_str "abc" 3 | @imp "stddefs.tash" 4 | @imp "std.tash" 5 | 6 | main: 7 | get_str 0 8 | get_str 1 9 | mov r0 top 10 | pop 11 | mov r1 top 12 | pop 13 | call stringcmp 14 | call printint 15 | call print_newline 16 | 17 | entrypoint main 18 | 19 | -------------------------------------------------------------------------------- /tests/strlen.tasm: -------------------------------------------------------------------------------- 1 | @imp "stddefs.tash" 2 | @imp "std.tash" 3 | push_str "Hello, world!\n" 4 | main: 5 | push 5123 6 | push 23 7 | get_str 0 8 | call stringlen 9 | call printint 10 | push '\n' 11 | ref 12 | push STDOUT 13 | write 14 | 15 | entrypoint main 16 | -------------------------------------------------------------------------------- /tests/strrev.tasm: -------------------------------------------------------------------------------- 1 | @imp "stddefs.tash" 2 | _strrev: 3 | push_str "hello" 4 | dup_str 5 | get_str 0 ;str 6 | dup ;str str 7 | strlen 8 | push 1 9 | sub 10 | mov r1 top 11 | add 12 | mov r0 top 13 | pop 14 | push -1 15 | 16 | loop: 17 | push 1 18 | add ; 0 19 | dup ; 0 0 str2 20 | get_str 1 21 | swap 22 | push r0 ; str2 0 0 "o" 23 | deref ; 0 str 0 'o' 24 | swap 25 | pop 26 | index ; 0 27 | 28 | push r0 29 | push 1 30 | sub 31 | 32 | mov r0 top 33 | pop 34 | pop 35 | 36 | push r1 37 | cmpl 38 | swap 39 | pop 40 | zjmp loop 41 | get_str 1 42 | push r1 43 | push 1 44 | add 45 | push '\0' 46 | index 47 | push r0 48 | push 1 49 | add 50 | mov r0 top 51 | jmp end 52 | 53 | 54 | 55 | end: 56 | get_str 1 57 | push 1 58 | write 59 | 60 | push '\n' 61 | ref 62 | push 1 63 | write 64 | 65 | get_str 0 66 | push 1 67 | write 68 | 69 | push '\n' 70 | ref 71 | push 1 72 | write 73 | 74 | halt 75 | 76 | -------------------------------------------------------------------------------- /todolist.txt: -------------------------------------------------------------------------------- 1 | [] fix preprocessor bugs 2 | 3 | [x] fix the windows assembler of TASM 4 | 5 | [] test turing completeness 6 | 7 | 8 | -------------------------------------------------------------------------------- /tools/titasm.vim: -------------------------------------------------------------------------------- 1 | " Vim syntax file 2 | " Language: TASM (Titanium assembly) 3 | 4 | " Usage Instructions 5 | " Put this file in .vim/syntax/titasm.vim or in 6 | " your $VIMSOURCE, which may be found in /usr/share/nvim/runtime/syntax 7 | " and in your .vimrc file add the following line: 8 | " autocmd BufRead,BufNewFile *.tasm,*.tash set filetype=tasm 9 | " or for neovim, add this command to your init.lua 10 | " vim.cmd[[ 11 | " augroup FileTypeSettings 12 | " autocmd! 13 | " autocmd BufRead,BufNewFile *.tasm,*.tash set filetype=tasm 14 | " augroup END 15 | " ]] 16 | 17 | 18 | if exists("b:current_syntax") 19 | finish 20 | endif 21 | 22 | syntax keyword tasmTodos TODO NOTE 23 | 24 | " Language keywords 25 | syntax keyword tasmKeywords nop push pop dup indup 26 | syntax keyword tasmKeywords add sub mul div mod 27 | syntax keyword tasmKeywords mul_f div_f mod_f 28 | syntax keyword tasmKeywords add_f sub_f 29 | syntax keyword tasmKeywords jmp zjmp nzjmp halt swap inswap 30 | syntax keyword tasmKeywords cmpe cmpl cmpg cmple cmpge cmpne 31 | syntax keyword tasmKeywords ret call native 32 | syntax keyword tasmKeywords itof ftoi push_ptr push_str get_str mov 33 | syntax keyword tasmKeywords mov_str ref deref pop_str 34 | syntax keyword tasmKeywords swap_str inswap_str index 35 | syntax keyword tasmKeywords entrypoint top print 36 | syntax match tasmRegister /r\%(3[0-1]\|[0-2]\?\d\)/ 37 | 38 | " Comments 39 | syntax region tasmCommentLine start=";" end="$" contains=tasmTodos 40 | syntax region tasmDirective start="@" end=" " 41 | 42 | syntax match tasmLabel "[a-z_][a-z0-9_]*:"he=e-1 43 | 44 | " Numbers 45 | syntax match tasmDecInt display "\<[0-9][0-9_]*" 46 | "syntax match tasmHexInt display "\<0[xX][0-9a-fA-F][0-9_a-fA-F]*" 47 | syntax match tasmFloat display "\<[0-9][0-9_]*\%(\.[0-9][0-9_]*\)" 48 | 49 | " Strings 50 | syntax region tasmString start=/\v"/ skip=/\v\\./ end=/\v"/ 51 | syntax region tasmString start=/\v'/ skip=/\v\\./ end=/\v'/ 52 | 53 | " Set highlights 54 | highlight default link tasmTodos Todo 55 | highlight default link tasmKeywords Identifier 56 | highlight default link tasmCommentLine Comment 57 | highlight default link tasmDirective PreProc 58 | highlight default link tasmLoopKeywords PreProc 59 | highlight default link tasmDecInt Number 60 | highlight default link tasmHexInt Number 61 | highlight default link tasmFloat Float 62 | highlight default link tasmString String 63 | highlight default link tasmLabel Label 64 | highlight default link tasmRegister StorageClass 65 | 66 | let b:current_syntax = "titasm" 67 | 68 | --------------------------------------------------------------------------------