├── .gitignore ├── CODE_OF_CONDUCT.md ├── LICENSE.md ├── README.md ├── __init__.py ├── compiler ├── __init__.py ├── builtin_functions.py ├── linked_list.py ├── my_compiler.py └── operations.py ├── example.my ├── llvm_ir ├── declares.ll ├── dynamic_array.ll ├── linked_list.ll └── prints.ll ├── my_ast.py ├── my_grammar.py ├── my_interpreter.py ├── my_lexer.py ├── my_parser.py ├── my_preprocessor.py ├── my_symbol_table.py ├── my_types.py ├── my_visitor.py └── test.my /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Linux template 3 | *~ 4 | 5 | # temporary files which can be created if a process still has a handle open of a deleted file 6 | .fuse_hidden* 7 | 8 | # KDE directory preferences 9 | .directory 10 | 11 | # Linux trash folder which might appear on any partition or disk 12 | .Trash-* 13 | ### NotepadPP template 14 | # Notepad++ backups # 15 | *.bak 16 | ### Python template 17 | # Byte-compiled / optimized / DLL files 18 | __pycache__/ 19 | *.py[cod] 20 | *$py.class 21 | 22 | # C extensions 23 | *.so 24 | 25 | # Distribution / packaging 26 | .Python 27 | env/ 28 | build/ 29 | develop-eggs/ 30 | dist/ 31 | downloads/ 32 | eggs/ 33 | .eggs/ 34 | lib/ 35 | lib64/ 36 | parts/ 37 | sdist/ 38 | var/ 39 | *.egg-info/ 40 | .installed.cfg 41 | *.egg 42 | 43 | # PyInstaller 44 | # Usually these files are written by a python script from a template 45 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 46 | *.manifest 47 | *.spec 48 | 49 | # Installer logs 50 | pip-log.txt 51 | pip-delete-this-directory.txt 52 | 53 | # Unit test / coverage reports 54 | htmlcov/ 55 | .tox/ 56 | .coverage 57 | .coverage.* 58 | .cache 59 | nosetests.xml 60 | coverage.xml 61 | *,cover 62 | .hypothesis/ 63 | 64 | # Translations 65 | *.mo 66 | *.pot 67 | 68 | # Django stuff: 69 | *.log 70 | local_settings.py 71 | 72 | # Flask stuff: 73 | instance/ 74 | .webassets-cache 75 | 76 | # Scrapy stuff: 77 | .scrapy 78 | 79 | # Sphinx documentation 80 | docs/_build/ 81 | 82 | # PyBuilder 83 | target/ 84 | 85 | # IPython Notebook 86 | .ipynb_checkpoints 87 | 88 | # pyenv 89 | .python-version 90 | 91 | # celery beat schedule file 92 | celerybeat-schedule 93 | 94 | # dotenv 95 | .env 96 | 97 | # virtualenv 98 | venv/ 99 | ENV/ 100 | 101 | # Spyder project settings 102 | .spyderproject 103 | 104 | # Rope project settings 105 | .ropeproject 106 | ### Windows template 107 | # Windows image file caches 108 | Thumbs.db 109 | ehthumbs.db 110 | 111 | # Folder config file 112 | Desktop.ini 113 | 114 | # Recycle Bin used on file shares 115 | $RECYCLE.BIN/ 116 | 117 | # Windows Installer files 118 | *.cab 119 | *.msi 120 | *.msm 121 | *.msp 122 | 123 | # Windows shortcuts 124 | *.lnk 125 | ### JetBrains template 126 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 127 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 128 | 129 | .idea/ 130 | 131 | # User-specific stuff: 132 | .idea/tasks.xml 133 | .idea/jsLibraryMappings.xml 134 | 135 | # Sensitive or high-churn files: 136 | .idea/dataSources.ids 137 | .idea/dataSources.xml 138 | .idea/dataSources.local.xml 139 | .idea/sqlDataSources.xml 140 | .idea/dynamic.xml 141 | .idea/uiDesigner.xml 142 | 143 | # Gradle: 144 | .idea/gradle.xml 145 | .idea/libraries 146 | 147 | # Mongo Explorer plugin: 148 | .idea/mongoSettings.xml 149 | 150 | ## File-based project format: 151 | *.iws 152 | 153 | ## Plugin-specific files: 154 | 155 | # IntelliJ 156 | /out/ 157 | 158 | # mpeltonen/sbt-idea plugin 159 | .idea_modules/ 160 | 161 | # JIRA plugin 162 | atlassian-ide-plugin.xml 163 | 164 | # Crashlytics plugin (for Android Studio and IntelliJ) 165 | com_crashlytics_export_strings.xml 166 | crashlytics.properties 167 | crashlytics-build.properties 168 | fabric.properties 169 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at postanthony3000@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mythril 2 | A new multi-paradigm programming language. Right now it is being coded in Python using llvmlite. 3 | 4 | This project is super early in development. 5 | 6 | Some examples of the code for Mythril can be seen in the [example.my file](https://github.com/Ayehavgunne/Mythril/blob/master/example.my). 7 | That is where I have been placing bits of test code as I work on various features. 8 | 9 | ## Goals: 10 | * Learn about compilers, LLVM, language design 11 | * Create a Python like syntax and mix in a whole lot of new language features and ideas 12 | * Use LLVM to make it more performant than Python but make sure it is just as easy to use 13 | * Focus on designs that will reduce possible errors 14 | * Choose defaults that are simple, easy and work despite possible performance overhead but make optimization easy. Example: use dynamic arrays by default but allow creation of fixed size arrays with a bit more notation 15 | 16 | ## Planned Features: 17 | * Type Infrencing 18 | * Pattern Matching 19 | * First Class Functions 20 | * Closures 21 | * Classes 22 | * Actors 23 | * Default to an accurate Decimal type and offer Floating Point as an option 24 | * Default parameter values to functions 25 | * Keyword arguments 26 | * Design by Contract 27 | * Builtin Testing 28 | * Builtin Documentation 29 | * Array programming 30 | * Generators 31 | * Comprehensions 32 | * Context Managers 33 | * Anonymous (multi statement) Functions 34 | * Decorators 35 | * Type Aliasing 36 | * Slicing 37 | * Multiple Dispatch 38 | 39 | ## TODO: 40 | - [ ] Refactor, refactor, refactor 41 | - [ ] Make a super cool logo 42 | - [x] Keyword arguments 43 | - [x] Parameter default values 44 | - [ ] Variable number of arguments (varargs) {Partialy done} 45 | - [ ] Variable number of keyword arguments 46 | - [ ] Signed Integers 47 | - [ ] Nested Functions 48 | - [x] Structs 49 | - [ ] Classes 50 | - [ ] Multiple Inheritance (for both classes and structs! Considering alternatives) 51 | - [ ] A more robust Type System would most likely be good to have 52 | - [ ] Enums 53 | - [ ] Actors 54 | - [ ] Single quotes for Interpolated strings and Double quotes for literal strings 55 | - [ ] Tests built in ('test') 56 | - [ ] Contracts built in ('require' and 'ensure') 57 | - [ ] Exceptions (Looking at alternatives) 58 | - [ ] Yielding 59 | - [ ] Context Manager ('with' and 'as') 60 | - [ ] Modules (importing with 'import' and 'from') 61 | - [ ] Closures 62 | - [ ] Anonymous (multi statement) functions 63 | - [x] Assigning functions to variables 64 | - [ ] Properties ('getter' and 'setter') 65 | - [ ] Decorators 66 | - [ ] Delete things ('del') 67 | - [ ] Type Aliasing 68 | - [ ] Bytes type 69 | - [ ] Binary operators 70 | - [ ] Complex number type 71 | - [ ] Arrays {working in basic form} 72 | - [ ] Lists 73 | - [ ] Slices 74 | - [ ] Iterator unpacking 75 | - [ ] Allow for double calling as in immediatly calling a function returned by a function ex: returns_function()() 76 | - [ ] More Collection types (set, hashmap, linked list, trees, etc.) 77 | - [ ] Pattern matching ('match') 78 | - [ ] Throw away variable using a single underscore character (be able to use it multiple times) 79 | - [ ] Call C and/or Python functions from within Mythril [Example](http://eli.thegreenplace.net/2015/calling-back-into-python-from-llvmlite-jited-code/) 80 | - [ ] Automatic integer(or number) promotion on overflow 81 | - [ ] Support Unicode (UTF-8) by default 82 | - [ ] Multiple dispatch 83 | - [ ] Ignore underscores in numbers (as separators to increase readability) 84 | - [ ] Add hexidecimal, octal, and binary literal representations of numbers 85 | - [ ] Javadocs like documentation built in 86 | - [x] DO NOT add a Null type 87 | - [ ] Use an Option type instead of Null for sentinal values 88 | - [ ] Implement Exhaustive Pattern Matching to help reduce potential errors 89 | 90 | ## Influences 91 | * Python 92 | * Javascript 93 | * Java 94 | * Julia 95 | * Go 96 | * Pony 97 | * F# 98 | * Cobra 99 | * SQL 100 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | if __name__ == '__main__': 2 | from my_lexer import Lexer 3 | from my_parser import Parser 4 | from my_preprocessor import Preprocessor 5 | from compiler.my_compiler import CodeGenerator 6 | file = 'test.my' 7 | code = open(file).read() 8 | lexer = Lexer(code, file) 9 | parser = Parser(lexer) 10 | t = parser.parse() 11 | symtab_builder = Preprocessor(parser.file_name) 12 | symtab_builder.check(t) 13 | if not symtab_builder.warnings: 14 | generator = CodeGenerator(parser.file_name) 15 | generator.generate_code(t) 16 | # generator.evaluate(True, True, False) 17 | # generator.evaluate(True, False, False) 18 | generator.evaluate(False, True, False) 19 | # generator.evaluate(False, False, False) 20 | # 21 | # generator.evaluate(True, True, True) 22 | # generator.evaluate(True, False, True) 23 | # generator.evaluate(False, True, True) 24 | # generator.evaluate(False, False, True) 25 | # 26 | # generator.compile(file[:-3], True, True) 27 | else: 28 | print('Did not run') 29 | -------------------------------------------------------------------------------- /compiler/__init__.py: -------------------------------------------------------------------------------- 1 | from llvmlite import ir 2 | from my_grammar import * 3 | 4 | 5 | RET_VAR = 'ret_var' 6 | 7 | NUM_TYPES = (ir.IntType, ir.DoubleType, ir.FloatType) 8 | 9 | # TODO: temorarily making Decimal a DoubleType till find (or make) a better representation 10 | type_map = { 11 | ANY: ir.VoidType(), 12 | BOOL: ir.IntType(1), 13 | INT: ir.IntType(64), 14 | INT8: ir.IntType(8), 15 | INT32: ir.IntType(32), 16 | INT64: ir.IntType(64), 17 | INT128: ir.IntType(128), 18 | DEC: ir.DoubleType(), 19 | FLOAT: ir.FloatType(), 20 | FUNC: ir.FunctionType, 21 | VOID: ir.VoidType(), 22 | STR: ir.IntType(8).as_pointer, 23 | } 24 | -------------------------------------------------------------------------------- /compiler/builtin_functions.py: -------------------------------------------------------------------------------- 1 | from llvmlite import ir 2 | from compiler import type_map 3 | from my_grammar import * 4 | 5 | ARRAY_INITIAL_CAPACITY = ir.Constant(type_map[INT], 100) 6 | 7 | zero = ir.Constant(type_map[INT], 0) 8 | one = ir.Constant(type_map[INT], 1) 9 | two = ir.Constant(type_map[INT], 2) 10 | eight = ir.Constant(type_map[INT], 8) 11 | ten = ir.Constant(type_map[INT], 10) 12 | zero_32 = ir.Constant(type_map[INT32], 0) 13 | one_32 = ir.Constant(type_map[INT32], 1) 14 | two_32 = ir.Constant(type_map[INT32], 2) 15 | 16 | 17 | def define_dynamic_array(compiler): 18 | # define a struct dynamic_array 19 | # 0: int size 20 | # 1: int capacity 21 | # 2: int *data TODO: maybe make this a void pointer to allow any kind of data 22 | dyn_array_struct = ir.LiteralStructType([type_map[INT], type_map[INT], type_map[INT].as_pointer()]) 23 | compiler.define('Dynamic_Array', dyn_array_struct) 24 | dyn_array_struct_ptr = dyn_array_struct.as_pointer() 25 | 26 | dynamic_array_init(compiler, dyn_array_struct_ptr) 27 | dynamic_array_double_if_full(compiler, dyn_array_struct_ptr) 28 | dynamic_array_append(compiler, dyn_array_struct_ptr) 29 | dynamic_array_get(compiler, dyn_array_struct_ptr) 30 | dynamic_array_set(compiler, dyn_array_struct_ptr) 31 | dynamic_array_length(compiler, dyn_array_struct_ptr) 32 | define_create_range(compiler, dyn_array_struct_ptr) 33 | define_int_to_str(compiler, dyn_array_struct_ptr) 34 | define_bool_to_str(compiler, dyn_array_struct_ptr) 35 | define_print(compiler, dyn_array_struct_ptr) 36 | 37 | 38 | def define_create_range(compiler, dyn_array_struct_ptr): 39 | create_range_type = ir.FunctionType(type_map[VOID], [dyn_array_struct_ptr, type_map[INT], type_map[INT]]) 40 | create_range = ir.Function(compiler.module, create_range_type, 'create_range') 41 | create_range_entry = create_range.append_basic_block('entry') 42 | builder = ir.IRBuilder(create_range_entry) 43 | compiler.builder = builder 44 | create_range_test = create_range.append_basic_block('test') 45 | create_range_body = create_range.append_basic_block('body') 46 | create_range_exit = create_range.append_basic_block('exit') 47 | 48 | builder.position_at_end(create_range_entry) 49 | array_ptr = builder.alloca(dyn_array_struct_ptr) 50 | builder.store(create_range.args[0], array_ptr) 51 | start_ptr = builder.alloca(type_map[INT]) 52 | builder.store(create_range.args[1], start_ptr) 53 | stop_ptr = builder.alloca(type_map[INT]) 54 | builder.store(create_range.args[2], stop_ptr) 55 | 56 | num_ptr = builder.alloca(type_map[INT]) 57 | builder.store(builder.load(start_ptr), num_ptr) 58 | builder.branch(create_range_test) 59 | 60 | builder.position_at_end(create_range_test) 61 | cond = builder.icmp_unsigned(LESS_THAN, builder.load(num_ptr), builder.load(stop_ptr)) 62 | builder.cbranch(cond, create_range_body, create_range_exit) 63 | 64 | builder.position_at_end(create_range_body) 65 | builder.call(compiler.module.get_global('dyn_array_append'), [builder.load(array_ptr), builder.load(num_ptr)]) 66 | builder.store(builder.add(one, builder.load(num_ptr)), num_ptr) 67 | 68 | builder.branch(create_range_test) 69 | 70 | builder.position_at_end(create_range_exit) 71 | builder.ret_void() 72 | 73 | 74 | def dynamic_array_init(compiler, dyn_array_struct_ptr): 75 | # START 76 | dyn_array_init_type = ir.FunctionType(type_map[VOID], [dyn_array_struct_ptr]) 77 | dyn_array_init = ir.Function(compiler.module, dyn_array_init_type, 'dyn_array_init') 78 | dyn_array_init_entry = dyn_array_init.append_basic_block('entry') 79 | builder = ir.IRBuilder(dyn_array_init_entry) 80 | compiler.builder = builder 81 | dyn_array_init_exit = dyn_array_init.append_basic_block('exit') 82 | builder.position_at_end(dyn_array_init_entry) 83 | array_ptr = builder.alloca(dyn_array_struct_ptr) 84 | builder.store(dyn_array_init.args[0], array_ptr) 85 | 86 | # BODY 87 | size_ptr = builder.gep(builder.load(array_ptr), [zero_32, zero_32], inbounds=True) 88 | builder.store(zero, size_ptr) 89 | 90 | capacity_ptr = builder.gep(builder.load(array_ptr), [zero_32, one_32], inbounds=True) 91 | builder.store(ARRAY_INITIAL_CAPACITY, capacity_ptr) 92 | 93 | data_ptr = builder.gep(builder.load(array_ptr), [zero_32, two_32], inbounds=True) 94 | size_of = builder.mul(builder.load(capacity_ptr), eight) 95 | mem_alloc = builder.call(compiler.module.get_global('malloc'), [size_of]) 96 | mem_alloc = builder.bitcast(mem_alloc, type_map[INT].as_pointer()) 97 | builder.store(mem_alloc, data_ptr) 98 | 99 | builder.branch(dyn_array_init_exit) 100 | 101 | # CLOSE 102 | builder.position_at_end(dyn_array_init_exit) 103 | builder.ret_void() 104 | 105 | 106 | def dynamic_array_double_if_full(compiler, dyn_array_struct_ptr): 107 | # START 108 | dyn_array_double_capacity_if_full_type = ir.FunctionType(type_map[VOID], [dyn_array_struct_ptr]) 109 | dyn_array_double_capacity_if_full = ir.Function(compiler.module, dyn_array_double_capacity_if_full_type, 'dyn_array_double_capacity_if_full') 110 | dyn_array_double_capacity_if_full_entry = dyn_array_double_capacity_if_full.append_basic_block('entry') 111 | builder = ir.IRBuilder(dyn_array_double_capacity_if_full_entry) 112 | compiler.builder = builder 113 | dyn_array_double_capacity_if_full_exit = dyn_array_double_capacity_if_full.append_basic_block('exit') 114 | dyn_array_double_capacity_block = dyn_array_double_capacity_if_full.append_basic_block('double_capacity') 115 | builder.position_at_end(dyn_array_double_capacity_if_full_entry) 116 | array_ptr = builder.alloca(dyn_array_struct_ptr) 117 | builder.store(dyn_array_double_capacity_if_full.args[0], array_ptr) 118 | 119 | # BODY 120 | size_ptr = builder.gep(builder.load(array_ptr), [zero_32, zero_32], inbounds=True) 121 | size_val = builder.load(size_ptr) 122 | 123 | capacity_ptr = builder.gep(builder.load(array_ptr), [zero_32, one_32], inbounds=True) 124 | capacity_val = builder.load(capacity_ptr) 125 | 126 | data_ptr = builder.gep(builder.load(array_ptr), [zero_32, two_32], inbounds=True) 127 | 128 | compare_size_to_capactiy = builder.icmp_unsigned(GREATER_THAN_OR_EQUAL_TO, size_val, capacity_val) 129 | 130 | builder.cbranch(compare_size_to_capactiy, dyn_array_double_capacity_block, dyn_array_double_capacity_if_full_exit) 131 | 132 | builder.position_at_end(dyn_array_double_capacity_block) 133 | 134 | capacity_val = builder.mul(capacity_val, two) 135 | builder.store(capacity_val, capacity_ptr) 136 | capacity_val = builder.load(capacity_ptr) 137 | size_of = builder.mul(capacity_val, eight) 138 | 139 | data_ptr_8 = builder.bitcast(builder.load(data_ptr), type_map[INT8].as_pointer()) 140 | re_alloc = builder.call(compiler.module.get_global('realloc'), [data_ptr_8, size_of]) 141 | re_alloc = builder.bitcast(re_alloc, type_map[INT].as_pointer()) 142 | builder.store(re_alloc, data_ptr) 143 | 144 | builder.branch(dyn_array_double_capacity_if_full_exit) 145 | 146 | # CLOSE 147 | builder.position_at_end(dyn_array_double_capacity_if_full_exit) 148 | builder.ret_void() 149 | 150 | 151 | def dynamic_array_append(compiler, dyn_array_struct_ptr): 152 | # START 153 | dyn_array_append_type = ir.FunctionType(type_map[VOID], [dyn_array_struct_ptr, type_map[INT]]) 154 | dyn_array_append = ir.Function(compiler.module, dyn_array_append_type, 'dyn_array_append') 155 | dyn_array_append_entry = dyn_array_append.append_basic_block('entry') 156 | builder = ir.IRBuilder(dyn_array_append_entry) 157 | compiler.builder = builder 158 | dyn_array_append_exit = dyn_array_append.append_basic_block('exit') 159 | builder.position_at_end(dyn_array_append_entry) 160 | array_ptr = builder.alloca(dyn_array_struct_ptr) 161 | builder.store(dyn_array_append.args[0], array_ptr) 162 | value_ptr = builder.alloca(type_map[INT]) 163 | builder.store(dyn_array_append.args[1], value_ptr) 164 | 165 | # BODY 166 | builder.call(compiler.module.get_global('dyn_array_double_capacity_if_full'), [builder.load(array_ptr)]) 167 | 168 | size_ptr = builder.gep(builder.load(array_ptr), [zero_32, zero_32], inbounds=True) 169 | size_val = builder.load(size_ptr) 170 | 171 | size_val = builder.add(size_val, one) 172 | builder.store(size_val, size_ptr) 173 | 174 | data_ptr = builder.gep(builder.load(array_ptr), [zero_32, two_32], inbounds=True) 175 | 176 | data_element_ptr = builder.gep(builder.load(data_ptr), [size_val], inbounds=True) 177 | 178 | builder.store(builder.load(value_ptr), data_element_ptr) 179 | 180 | builder.branch(dyn_array_append_exit) 181 | 182 | # CLOSE 183 | builder.position_at_end(dyn_array_append_exit) 184 | builder.ret_void() 185 | 186 | 187 | def dynamic_array_get(compiler, dyn_array_struct_ptr): 188 | # START 189 | dyn_array_get_type = ir.FunctionType(type_map[INT], [dyn_array_struct_ptr, type_map[INT]]) 190 | dyn_array_get = ir.Function(compiler.module, dyn_array_get_type, 'dyn_array_get') 191 | dyn_array_get_entry = dyn_array_get.append_basic_block('entry') 192 | builder = ir.IRBuilder(dyn_array_get_entry) 193 | compiler.builder = builder 194 | dyn_array_get_exit = dyn_array_get.append_basic_block('exit') 195 | dyn_array_get_index_out_of_bounds = dyn_array_get.append_basic_block('index_out_of_bounds') 196 | dyn_array_get_is_index_less_than_zero = dyn_array_get.append_basic_block('is_index_less_than_zero') 197 | dyn_array_get_negative_index = dyn_array_get.append_basic_block('negative_index') 198 | dyn_array_get_block = dyn_array_get.append_basic_block('get') 199 | builder.position_at_end(dyn_array_get_entry) 200 | array_ptr = builder.alloca(dyn_array_struct_ptr) 201 | builder.store(dyn_array_get.args[0], array_ptr) 202 | index_ptr = builder.alloca(type_map[INT]) 203 | builder.store(dyn_array_get.args[1], index_ptr) 204 | 205 | # BODY 206 | index_val = builder.load(index_ptr) 207 | size_ptr = builder.gep(builder.load(array_ptr), [zero_32, zero_32], inbounds=True) 208 | size_val = builder.load(size_ptr) 209 | 210 | compare_index_to_size = builder.icmp_unsigned(GREATER_THAN_OR_EQUAL_TO, index_val, size_val) 211 | 212 | builder.cbranch(compare_index_to_size, dyn_array_get_index_out_of_bounds, dyn_array_get_is_index_less_than_zero) 213 | 214 | builder.position_at_end(dyn_array_get_index_out_of_bounds) 215 | compiler.print_string('Array index out of bounds') 216 | builder.call(compiler.module.get_global('exit'), [one_32]) 217 | builder.unreachable() 218 | 219 | builder.position_at_end(dyn_array_get_is_index_less_than_zero) 220 | 221 | compare_index_to_zero = builder.icmp_unsigned(LESS_THAN, index_val, zero) 222 | 223 | builder.cbranch(compare_index_to_zero, dyn_array_get_negative_index, dyn_array_get_block) 224 | 225 | builder.position_at_end(dyn_array_get_negative_index) 226 | 227 | add = builder.add(size_val, index_val) 228 | builder.store(add, index_ptr) 229 | builder.branch(dyn_array_get_block) 230 | 231 | builder.position_at_end(dyn_array_get_block) 232 | 233 | data_ptr = builder.gep(builder.load(array_ptr), [zero_32, two_32], inbounds=True) 234 | 235 | add_1 = builder.add(one, index_val) 236 | builder.store(add_1, index_ptr) 237 | index_val = builder.load(index_ptr) 238 | data_element_ptr = builder.gep(builder.load(data_ptr), [index_val], inbounds=True) 239 | 240 | builder.branch(dyn_array_get_exit) 241 | 242 | # CLOSE 243 | builder.position_at_end(dyn_array_get_exit) 244 | builder.ret(builder.load(data_element_ptr)) 245 | 246 | 247 | def dynamic_array_set(compiler, dyn_array_struct_ptr): 248 | # START 249 | dyn_array_set_type = ir.FunctionType(type_map[VOID], [dyn_array_struct_ptr, type_map[INT], type_map[INT]]) 250 | dyn_array_set = ir.Function(compiler.module, dyn_array_set_type, 'dyn_array_set') 251 | dyn_array_set_entry = dyn_array_set.append_basic_block('entry') 252 | builder = ir.IRBuilder(dyn_array_set_entry) 253 | compiler.builder = builder 254 | dyn_array_set_exit = dyn_array_set.append_basic_block('exit') 255 | dyn_array_set_index_out_of_bounds = dyn_array_set.append_basic_block('index_out_of_bounds') 256 | dyn_array_set_is_index_less_than_zero = dyn_array_set.append_basic_block('is_index_less_than_zero') 257 | dyn_array_set_negative_index = dyn_array_set.append_basic_block('negative_index') 258 | dyn_array_set_block = dyn_array_set.append_basic_block('set') 259 | builder.position_at_end(dyn_array_set_entry) 260 | array_ptr = builder.alloca(dyn_array_struct_ptr) 261 | builder.store(dyn_array_set.args[0], array_ptr) 262 | index_ptr = builder.alloca(type_map[INT]) 263 | builder.store(dyn_array_set.args[1], index_ptr) 264 | value_ptr = builder.alloca(type_map[INT]) 265 | builder.store(dyn_array_set.args[2], value_ptr) 266 | 267 | # BODY 268 | index_val = builder.load(index_ptr) 269 | 270 | size_ptr = builder.gep(builder.load(array_ptr), [zero_32, zero_32], inbounds=True) 271 | size_val = builder.load(size_ptr) 272 | 273 | compare_index_to_size = builder.icmp_unsigned(GREATER_THAN_OR_EQUAL_TO, index_val, size_val) 274 | 275 | builder.cbranch(compare_index_to_size, dyn_array_set_index_out_of_bounds, dyn_array_set_is_index_less_than_zero) 276 | 277 | builder.position_at_end(dyn_array_set_index_out_of_bounds) 278 | compiler.print_string('Array index out of bounds') 279 | builder.call(compiler.module.get_global('exit'), [one_32]) 280 | builder.unreachable() 281 | 282 | builder.position_at_end(dyn_array_set_is_index_less_than_zero) 283 | 284 | compare_index_to_zero = builder.icmp_unsigned(LESS_THAN, index_val, zero) 285 | 286 | builder.cbranch(compare_index_to_zero, dyn_array_set_negative_index, dyn_array_set_block) 287 | 288 | builder.position_at_end(dyn_array_set_negative_index) 289 | 290 | add = builder.add(size_val, index_val) 291 | builder.store(add, index_ptr) 292 | builder.branch(dyn_array_set_block) 293 | 294 | builder.position_at_end(dyn_array_set_block) 295 | 296 | data_ptr = builder.gep(builder.load(array_ptr), [zero_32, two_32], inbounds=True) 297 | 298 | add_1 = builder.add(one, index_val) 299 | builder.store(add_1, index_ptr) 300 | index_val = builder.load(index_ptr) 301 | 302 | data_element_ptr = builder.gep(builder.load(data_ptr), [index_val], inbounds=True) 303 | 304 | builder.store(builder.load(value_ptr), data_element_ptr) 305 | 306 | builder.branch(dyn_array_set_exit) 307 | 308 | # CLOSE 309 | builder.position_at_end(dyn_array_set_exit) 310 | builder.ret_void() 311 | 312 | 313 | def dynamic_array_length(compiler, dyn_array_struct_ptr): 314 | # START 315 | dyn_array_length_type = ir.FunctionType(type_map[INT], [dyn_array_struct_ptr]) 316 | dyn_array_length = ir.Function(compiler.module, dyn_array_length_type, 'dyn_array_length') 317 | dyn_array_length_entry = dyn_array_length.append_basic_block('entry') 318 | builder = ir.IRBuilder(dyn_array_length_entry) 319 | compiler.builder = builder 320 | builder.position_at_end(dyn_array_length_entry) 321 | array_ptr = builder.alloca(dyn_array_struct_ptr) 322 | builder.store(dyn_array_length.args[0], array_ptr) 323 | 324 | size_ptr = builder.gep(builder.load(array_ptr), [zero_32, zero_32], inbounds=True) 325 | 326 | # CLOSE 327 | builder.ret(builder.load(size_ptr)) 328 | 329 | # TODO: add the following functions for dynamic array 330 | # extend(iterable) 331 | # insert(item, index) 332 | # remove(item) 333 | # pop([index]) 334 | # clear() 335 | # index(x[, start[, end]]) 336 | # count(item) 337 | # sort(key=None, reverse=False) 338 | # reverse() 339 | 340 | 341 | def define_print(compiler, dyn_array_struct_ptr): 342 | # START 343 | func_type = ir.FunctionType(type_map[VOID], [dyn_array_struct_ptr]) 344 | func = ir.Function(compiler.module, func_type, 'print') 345 | entry_block = func.append_basic_block('entry') 346 | builder = ir.IRBuilder(entry_block) 347 | compiler.builder = builder 348 | builder.position_at_end(entry_block) 349 | zero_length_check_block = func.append_basic_block('zero_length_check') 350 | non_zero_length_block = func.append_basic_block('non_zero_length') 351 | cond_block = func.append_basic_block('check_if_done') 352 | body_block = func.append_basic_block('print_it') 353 | exit_block = func.append_basic_block('exit') 354 | array_ptr = builder.alloca(dyn_array_struct_ptr) 355 | builder.store(func.args[0], array_ptr) 356 | 357 | # BODY 358 | builder.position_at_end(entry_block) 359 | length = builder.call(compiler.module.get_global('dyn_array_length'), [builder.load(array_ptr)]) 360 | builder.branch(zero_length_check_block) 361 | 362 | builder.position_at_end(zero_length_check_block) 363 | cond = builder.icmp_unsigned(LESS_THAN_OR_EQUAL_TO, zero, length) 364 | builder.cbranch(cond, non_zero_length_block, exit_block) 365 | 366 | builder.position_at_end(non_zero_length_block) 367 | position_ptr = builder.alloca(type_map[INT]) 368 | builder.store(zero, position_ptr) 369 | builder.branch(cond_block) 370 | 371 | builder.position_at_end(cond_block) 372 | cond = builder.icmp_unsigned(LESS_THAN, builder.load(position_ptr), length) 373 | builder.cbranch(cond, body_block, exit_block) 374 | 375 | builder.position_at_end(body_block) 376 | char = builder.call(compiler.module.get_global('dyn_array_get'), [builder.load(array_ptr), builder.load(position_ptr)]) 377 | builder.call(compiler.module.get_global('putchar'), [char]) 378 | add_one = builder.add(one, builder.load(position_ptr)) 379 | builder.store(add_one, position_ptr) 380 | builder.branch(cond_block) 381 | 382 | # CLOSE 383 | builder.position_at_end(exit_block) 384 | builder.call(compiler.module.get_global('putchar'), [ten]) 385 | builder.ret_void() 386 | 387 | 388 | def define_int_to_str(compiler, dyn_array_struct_ptr): 389 | # START 390 | func_type = ir.FunctionType(type_map[VOID], [dyn_array_struct_ptr, type_map[INT]]) 391 | func = ir.Function(compiler.module, func_type, 'int_to_str') 392 | entry_block = func.append_basic_block('entry') 393 | builder = ir.IRBuilder(entry_block) 394 | compiler.builder = builder 395 | builder.position_at_end(entry_block) 396 | exit_block = func.append_basic_block('exit') 397 | array_ptr = builder.alloca(dyn_array_struct_ptr) 398 | builder.store(func.args[0], array_ptr) 399 | n_addr = builder.alloca(type_map[INT]) 400 | builder.store(func.args[1], n_addr) 401 | x_addr = builder.alloca(type_map[INT]) 402 | 403 | # BODY 404 | fourtyeight = ir.Constant(type_map[INT], 48) 405 | 406 | div_ten = builder.udiv(builder.load(n_addr), ten) 407 | greater_than_zero = builder.icmp_unsigned(GREATER_THAN, div_ten, zero) 408 | mod_ten = builder.urem(builder.trunc(builder.load(n_addr), type_map[INT]), ten) 409 | builder.store(mod_ten, x_addr) 410 | 411 | with builder.if_then(greater_than_zero): 412 | builder.call(compiler.module.get_global('int_to_str'), [builder.load(array_ptr), div_ten]) 413 | 414 | char = builder.add(fourtyeight, builder.load(x_addr)) 415 | builder.call(compiler.module.get_global('dyn_array_append'), [builder.load(array_ptr), char]) 416 | builder.branch(exit_block) 417 | 418 | # CLOSE 419 | builder.position_at_end(exit_block) 420 | builder.ret_void() 421 | 422 | 423 | def define_bool_to_str(compiler, dyn_array_struct_ptr): 424 | # START 425 | func_type = ir.FunctionType(type_map[VOID], [dyn_array_struct_ptr, type_map[BOOL]]) 426 | func = ir.Function(compiler.module, func_type, 'bool_to_str') 427 | entry_block = func.append_basic_block('entry') 428 | builder = ir.IRBuilder(entry_block) 429 | compiler.builder = builder 430 | exit_block = func.append_basic_block('exit') 431 | array_ptr = builder.alloca(dyn_array_struct_ptr) 432 | builder.store(func.args[0], array_ptr) 433 | 434 | # BODY 435 | equalszero = builder.icmp_unsigned(EQUALS, func.args[1], ir.Constant(type_map[BOOL], 0)) 436 | dyn_array_append = compiler.module.get_global('dyn_array_append') 437 | 438 | with builder.if_else(equalszero) as (then, otherwise): 439 | with then: 440 | builder.call(dyn_array_append, [builder.load(array_ptr), ir.Constant(type_map[INT], 102)]) 441 | builder.call(dyn_array_append, [builder.load(array_ptr), ir.Constant(type_map[INT], 97)]) 442 | builder.call(dyn_array_append, [builder.load(array_ptr), ir.Constant(type_map[INT], 108)]) 443 | builder.call(dyn_array_append, [builder.load(array_ptr), ir.Constant(type_map[INT], 115)]) 444 | builder.call(dyn_array_append, [builder.load(array_ptr), ir.Constant(type_map[INT], 101)]) 445 | with otherwise: 446 | builder.call(dyn_array_append, [builder.load(array_ptr), ir.Constant(type_map[INT], 116)]) 447 | builder.call(dyn_array_append, [builder.load(array_ptr), ir.Constant(type_map[INT], 114)]) 448 | builder.call(dyn_array_append, [builder.load(array_ptr), ir.Constant(type_map[INT], 117)]) 449 | builder.call(dyn_array_append, [builder.load(array_ptr), ir.Constant(type_map[INT], 101)]) 450 | 451 | builder.branch(exit_block) 452 | 453 | # CLOSE 454 | builder.position_at_end(exit_block) 455 | builder.ret_void() -------------------------------------------------------------------------------- /compiler/linked_list.py: -------------------------------------------------------------------------------- 1 | from llvmlite import ir 2 | 3 | from compiler import type_map 4 | from my_grammar import * 5 | 6 | 7 | def define_vector(generator, data_type): 8 | vector_struct = generator.builder.LiteralStructType(type_map[INT], type_map[INT], data_type) 9 | vector_name = '{}vector'.format(data_type) 10 | generator.builder.alloc(vector_struct, name=vector_name) 11 | init_vector(generator, vector_struct) 12 | 13 | 14 | def init_vector(generator, vector_struct): 15 | generator.start_function('vector_init', type_map[VOID], [vector_struct]) -------------------------------------------------------------------------------- /compiler/my_compiler.py: -------------------------------------------------------------------------------- 1 | from ctypes import CFUNCTYPE 2 | from ctypes import c_void_p 3 | from decimal import Decimal 4 | from time import sleep 5 | from time import time 6 | import llvmlite.binding as llvm 7 | from llvmlite import ir 8 | from compiler import RET_VAR 9 | from compiler import type_map 10 | from compiler.builtin_functions import define_dynamic_array 11 | from compiler.operations import operations 12 | from my_ast import DotAccess 13 | from my_ast import Input 14 | from my_ast import StructLiteral 15 | from my_ast import CollectionAccess 16 | from my_ast import VarDecl 17 | from my_grammar import * 18 | from my_visitor import NodeVisitor 19 | 20 | 21 | class CodeGenerator(NodeVisitor): 22 | def __init__(self, file_name): 23 | super().__init__() 24 | self.file_name = file_name 25 | self.module = ir.Module() 26 | self.builder = None 27 | self._add_builtins() 28 | func_ty = ir.FunctionType(ir.VoidType(), []) 29 | func = ir.Function(self.module, func_ty, 'main') 30 | entry_block = func.append_basic_block('entry') 31 | exit_block = func.append_basic_block('exit') 32 | # self.func_table = {'main': func} 33 | self.current_function = func # TODO: investigate to see if these function attributes are redundant, they might not be... who knows 34 | self.function_stack = [func] 35 | self.builder = ir.IRBuilder(entry_block) 36 | self.exit_blocks = [exit_block] 37 | self.block_stack = [entry_block] 38 | self.loop_test_blocks = [] 39 | self.loop_end_blocks = [] 40 | self.is_break = False 41 | llvm.initialize() 42 | llvm.initialize_native_target() 43 | llvm.initialize_native_asmprinter() 44 | self.anon_counter = 0 45 | 46 | def __str__(self): 47 | return str(self.module) 48 | 49 | def visit_program(self, node): 50 | self.visit(node.block) 51 | self.branch(self.exit_blocks[0]) 52 | self.position_at_end(self.exit_blocks[0]) 53 | self.builder.ret_void() 54 | 55 | @staticmethod 56 | def visit_num(node): 57 | return ir.Constant(type_map[node.val_type], node.value) 58 | 59 | def visit_var(self, node): 60 | var = self.search_scopes(node.value) 61 | if isinstance(var, type_map[FUNC]): 62 | return var 63 | return self.load(node.value) 64 | 65 | def visit_binop(self, node): 66 | return operations(self, node) 67 | 68 | def visit_anonymousfunc(self, node): 69 | self.anon_counter += 1 70 | return self.funcdecl('anon{}'.format(self.anon_counter), node) 71 | 72 | def visit_funcdecl(self, node): 73 | self.funcdecl(node.name, node) 74 | 75 | def funcdecl(self, name, node): 76 | self.start_function(name, node.return_type, node.parameters, node.parameter_defaults, node.varargs) 77 | for i, arg in enumerate(self.current_function.args): 78 | arg.name = list(node.parameters.keys())[i] 79 | self.alloc_define_store(arg, arg.name, arg.type) 80 | if self.current_function.function_type.return_type != type_map[VOID]: 81 | self.alloc_and_define(RET_VAR, self.current_function.function_type.return_type) 82 | ret = self.visit(node.body) 83 | self.end_function(ret) 84 | 85 | def visit_return(self, node): 86 | val = self.visit(node.value) 87 | if val.type != ir.VoidType(): 88 | self.store(val, RET_VAR) 89 | self.branch(self.exit_blocks[-1]) 90 | return True 91 | 92 | def visit_funccall(self, node): 93 | func_type = self.search_scopes(node.name) 94 | # func_type = self.func_table[node.name] 95 | if isinstance(func_type, ir.Function): 96 | func_type = func_type.type.pointee 97 | name = self.search_scopes(node.name) 98 | name = name.name 99 | else: 100 | name = node.name 101 | if len(node.arguments) < len(func_type.args): 102 | args = [] 103 | args_supplied = [] 104 | for x, arg in enumerate(func_type.arg_order): 105 | if x < len(node.arguments): 106 | args.append(self.visit(node.arguments[x])) 107 | else: 108 | if node.named_arguments and arg in node.named_arguments: 109 | args.append(self.visit(node.named_arguments[arg])) 110 | else: 111 | if set(node.named_arguments.keys()) & set(args_supplied): 112 | raise TypeError('got multiple values for argument(s) {}'.format(set(node.named_arguments.keys()) & set(args_supplied))) 113 | args.append(self.visit(func_type.parameter_defaults[arg])) 114 | args_supplied.append(arg) 115 | elif len(node.arguments) + len(node.named_arguments) > len(func_type.args): 116 | raise SyntaxError('Unexpected arguments') 117 | else: 118 | args = [self.visit(arg) for arg in node.arguments] 119 | return self.call(name, args) 120 | 121 | def visit_compound(self, node): 122 | ret = None 123 | for child in node.children: 124 | temp = self.visit(child) 125 | if temp: 126 | ret = temp 127 | return ret 128 | 129 | def visit_structdeclaration(self, node): 130 | fields = [] 131 | for field in node.fields.values(): 132 | if field.value == STR: 133 | fields.append(str) 134 | else: 135 | fields.append(type_map[field.value]) 136 | struct = ir.LiteralStructType(fields) 137 | struct.fields = [field for field in node.fields.keys()] 138 | self.define(node.name, struct) 139 | 140 | def visit_typedeclaration(self, node): 141 | raise NotImplementedError 142 | 143 | def visit_vardecl(self, node): 144 | var_addr = self.allocate(type_map[node.type_node.value], name=node.var_node.value) 145 | self.define(node.var_node.value, var_addr) 146 | self.store(self.visit(node.var_node), node.var_node.value) 147 | 148 | @staticmethod 149 | def visit_type(node): 150 | return type_map[node.value] 151 | 152 | def visit_if(self, node): 153 | start_block = self.add_block('if.start') 154 | end_block = self.add_block('if.end') 155 | self.branch(start_block) 156 | self.position_at_end(start_block) 157 | for x, comp in enumerate(node.comps): 158 | if_true_block = self.add_block('if.true.{}'.format(x)) 159 | if x + 1 < len(node.comps): 160 | if_false_block = self.add_block('if.false.{}'.format(x)) 161 | else: 162 | if_false_block = end_block 163 | cond_val = self.visit(comp) 164 | self.cbranch(cond_val, if_true_block, if_false_block) 165 | self.position_at_end(if_true_block) 166 | ret = self.visit(node.blocks[x]) 167 | if not ret: 168 | self.branch(end_block) 169 | self.position_at_end(if_false_block) 170 | self.position_at_end(end_block) 171 | 172 | def visit_else(self, _): 173 | return self.builder.icmp_unsigned(EQUALS, self.const(1), self.const(1), 'cmptmp') 174 | 175 | def visit_while(self, node): 176 | cond_block = self.add_block('while.cond') 177 | body_block = self.add_block('while.body') 178 | end_block = self.add_block('while.end') 179 | self.loop_test_blocks.append(cond_block) 180 | self.loop_end_blocks.append(end_block) 181 | self.branch(cond_block) 182 | self.position_at_end(cond_block) 183 | cond = self.visit(node.comp) 184 | self.cbranch(cond, body_block, end_block) 185 | self.position_at_end(body_block) 186 | self.visit(node.block) 187 | if not self.is_break: 188 | self.branch(cond_block) 189 | else: 190 | self.is_break = False 191 | self.position_at_end(end_block) 192 | self.loop_test_blocks.pop() 193 | self.loop_end_blocks.pop() 194 | 195 | def visit_for(self, node): 196 | init_block = self.add_block('for.init') 197 | zero_length_block = self.add_block('for.zero_length') 198 | non_zero_length_block = self.add_block('for.non_zero_length') 199 | cond_block = self.add_block('for.cond') 200 | body_block = self.add_block('for.body') 201 | end_block = self.add_block('for.end') 202 | self.loop_test_blocks.append(cond_block) 203 | self.loop_end_blocks.append(end_block) 204 | self.branch(init_block) 205 | 206 | self.position_at_end(init_block) 207 | zero = self.const(0) 208 | one = self.const(1) 209 | if node.iterator.value == RANGE: 210 | iterator = self.visit(node.iterator) 211 | else: 212 | iterator = self.search_scopes(node.iterator.value) 213 | stop = self.call('dyn_array_length', [iterator]) 214 | self.branch(zero_length_block) 215 | 216 | self.position_at_end(zero_length_block) 217 | cond = self.builder.icmp_unsigned(LESS_THAN, zero, stop) 218 | self.cbranch(cond, non_zero_length_block, end_block) 219 | 220 | self.position_at_end(non_zero_length_block) 221 | varname = node.elements[0].value 222 | val = self.call('dyn_array_get', [iterator, zero]) 223 | self.alloc_define_store(val, varname, iterator.type.pointee.elements[2].pointee) 224 | position = self.alloc_define_store(zero, 'position', type_map[INT]) 225 | self.branch(cond_block) 226 | 227 | self.position_at_end(cond_block) 228 | cond = self.builder.icmp_unsigned(LESS_THAN, self.load(position), stop) 229 | self.cbranch(cond, body_block, end_block) 230 | 231 | self.position_at_end(body_block) 232 | self.store(self.call('dyn_array_get', [iterator, self.load(position)]), varname) 233 | self.store(self.builder.add(one, self.load(position)), position) 234 | self.visit(node.block) 235 | if not self.is_break: 236 | self.branch(cond_block) 237 | else: 238 | self.is_break = False 239 | 240 | self.position_at_end(end_block) 241 | self.loop_test_blocks.pop() 242 | self.loop_end_blocks.pop() 243 | 244 | def visit_loopblock(self, node): 245 | for child in node.children: 246 | temp = self.visit(child) 247 | if temp: 248 | return temp 249 | 250 | def visit_switch(self, node): 251 | default_exists = False 252 | switch_end_block = self.add_block('switch_end') 253 | default_block = self.add_block('default') 254 | switch = self.switch(self.visit(node.value), default_block) 255 | cases = [] 256 | for case in node.cases: 257 | if case.value == DEFAULT: 258 | cases.append(default_block) 259 | default_exists = True 260 | else: 261 | cases.append(self.add_block('case')) 262 | if not default_exists: 263 | self.position_at_end(default_block) 264 | self.branch(switch_end_block) 265 | for x, case in enumerate(node.cases): 266 | self.position_at_end(cases[x]) 267 | break_ = self.visit(case.block) 268 | if break_ == BREAK: 269 | self.branch(switch_end_block) 270 | else: 271 | if x == len(node.cases) - 1: 272 | self.branch(switch_end_block) 273 | else: 274 | self.branch(cases[x + 1]) 275 | if case.value != DEFAULT: 276 | switch.add_case(self.visit(case.value), cases[x]) 277 | self.position_at_end(switch_end_block) 278 | 279 | def visit_break(self, _): 280 | if 'case' in self.builder.block.name: 281 | return BREAK 282 | else: 283 | self.is_break = True 284 | return self.branch(self.loop_end_blocks[-1]) 285 | 286 | def visit_continue(self, _): 287 | return self.branch(self.loop_test_blocks[-1]) 288 | 289 | @staticmethod 290 | def visit_pass(_): 291 | return 292 | 293 | def visit_unaryop(self, node): 294 | op = node.op.value 295 | expr = self.visit(node.expr) 296 | if op == PLUS: 297 | return expr 298 | elif op == MINUS: 299 | return self.builder.neg(expr) 300 | elif op == NOT: 301 | return self.builder.not_(expr) 302 | 303 | def visit_range(self, node): 304 | start = self.visit(node.left) 305 | stop = self.visit(node.right) 306 | array_ptr = self.create_array() 307 | self.call('create_range', [array_ptr, start, stop]) 308 | return array_ptr 309 | 310 | def visit_assign(self, node): # TODO: Simplify this, it just keeps getting worse 311 | if isinstance(node.right, StructLiteral): 312 | self.struct_assign(node) 313 | elif hasattr(node.right, 'value') and isinstance(self.search_scopes(node.right.value), ir.Function): 314 | self.define(node.left.value, self.search_scopes(node.right.value)) 315 | else: 316 | if isinstance(node.right, Input): 317 | node.right.type = node.left.type_node.value 318 | var = self.visit(node.right) 319 | if not var: 320 | return 321 | if isinstance(node.left, VarDecl): 322 | var_name = node.left.value.value 323 | if node.left.type.value == FLOAT: 324 | node.right.value = float(node.right.value) 325 | self.alloc_define_store(var, var_name, var.type) 326 | elif isinstance(node.left, DotAccess): 327 | obj = self.search_scopes(node.left.obj) 328 | obj_type = self.search_scopes(obj.struct_name) 329 | new_obj = self.builder.insert_value(self.load(obj.name), self.visit(node.right), obj_type.fields.index(node.left.field)) 330 | struct_ptr = self.alloc_and_store(new_obj, obj_type, name=obj.name) 331 | struct_ptr.struct_name = obj.struct_name 332 | self.define(obj.name, struct_ptr) 333 | elif isinstance(node.left, CollectionAccess): 334 | right = self.visit(node.right) 335 | self.call('dyn_array_set', [self.search_scopes(node.left.collection.value), self.const(node.left.key.value), right]) 336 | else: 337 | var_name = node.left.value 338 | var_value = self.top_scope.get(var_name) 339 | if var_value: 340 | if isinstance(var_value, float): 341 | node.right.value = float(node.right.value) 342 | self.store(var, var_name) 343 | elif isinstance(var, ir.Function): 344 | self.define(var_name, var) 345 | else: 346 | self.alloc_define_store(var, var_name, var.type) 347 | 348 | def visit_fieldassignment(self, node): 349 | obj = self.search_scopes(node.obj) 350 | obj_type = self.search_scopes(obj.struct_name) 351 | return self.builder.extract_value(self.load(node.obj), obj_type.fields.index(node.field)) 352 | 353 | def struct_assign(self, node): 354 | struct_type = self.search_scopes(node.left.type.value) 355 | name = node.left.value.value 356 | fields = [] 357 | for field in node.right.fields.values(): 358 | fields.append(self.visit(field)) 359 | struct = struct_type(fields) 360 | struct_ptr = self.alloc_and_store(struct, struct_type, name=name) 361 | struct_ptr.struct_name = node.left.type.value 362 | self.define(name, struct_ptr) 363 | 364 | def visit_dotaccess(self, node): 365 | obj = self.search_scopes(node.obj) 366 | obj_type = self.search_scopes(obj.struct_name) 367 | return self.builder.extract_value(self.load(node.obj), obj_type.fields.index(node.field)) 368 | 369 | def visit_opassign(self, node): 370 | right = self.visit(node.right) 371 | collection_access = None 372 | key = None 373 | if isinstance(node.left, CollectionAccess): 374 | collection_access = True 375 | var_name = self.search_scopes(node.left.collection.value) 376 | key = self.const(node.left.key.value) 377 | var = self.call('dyn_array_get', [var_name, key]) 378 | pointee = var.type 379 | else: 380 | var_name = node.left.value 381 | var = self.load(var_name) 382 | pointee = self.search_scopes(var_name).type.pointee 383 | op = node.op 384 | if isinstance(pointee, ir.IntType): 385 | if op == PLUS_ASSIGN: 386 | res = self.builder.add(var, right) 387 | elif op == MINUS_ASSIGN: 388 | res = self.builder.sub(var, right) 389 | elif op == MUL_ASSIGN: 390 | res = self.builder.mul(var, right) 391 | elif op == FLOORDIV_ASSIGN: 392 | res = self.builder.sdiv(var, right) 393 | elif op == DIV_ASSIGN: 394 | res = self.builder.fdiv(var, right) 395 | elif op == MOD_ASSIGN: 396 | res = self.builder.srem(var, right) 397 | elif op == POWER_ASSIGN: 398 | temp = self.alloc_and_store(var, type_map[INT]) 399 | for _ in range(node.right.value - 1): 400 | res = self.builder.mul(self.load(temp), var) 401 | self.store(res, temp) 402 | res = self.load(temp) 403 | else: 404 | raise NotImplementedError() 405 | else: 406 | if op == PLUS_ASSIGN: 407 | res = self.builder.fadd(var, right) 408 | elif op == MINUS_ASSIGN: 409 | res = self.builder.fsub(var, right) 410 | elif op == MUL_ASSIGN: 411 | res = self.builder.fmul(var, right) 412 | elif op == FLOORDIV_ASSIGN: 413 | res = self.builder.sdiv(self.builder.fptosi(var, ir.IntType(64)), self.builder.fptosi(right, ir.IntType(64))) 414 | elif op == DIV_ASSIGN: 415 | res = self.builder.fdiv(var, right) 416 | elif op == MOD_ASSIGN: 417 | res = self.builder.frem(var, right) 418 | elif op == POWER_ASSIGN: 419 | temp = self.alloc_and_store(var, type_map[DEC]) 420 | for _ in range(node.right.value - 1): 421 | res = self.builder.fmul(self.load(temp), var) 422 | self.store(res, temp) 423 | res = self.load(temp) 424 | else: 425 | raise NotImplementedError() 426 | if collection_access: 427 | self.call('dyn_array_set', [var_name, key, res]) 428 | else: 429 | self.store(res, var_name) 430 | 431 | def visit_constant(self, node): 432 | if node.value == TRUE: 433 | return self.const(1, BOOL) 434 | elif node.value == FALSE: 435 | return self.const(0, BOOL) 436 | else: 437 | raise NotImplementedError('file={} line={}'.format(self.file_name, node.line_num)) 438 | 439 | def visit_collection(self, node): 440 | elements = [] 441 | for item in node.items: 442 | elements.append(self.visit(item)) 443 | if node.type == ARRAY: 444 | return self.define_array(node, elements) 445 | elif node.type == LIST: 446 | return self.define_list(node, elements) 447 | else: 448 | raise NotImplementedError 449 | 450 | def define_array(self, _, elements): 451 | array_ptr = self.create_array() 452 | for element in elements: 453 | self.call('dyn_array_append', [array_ptr, element]) 454 | return self.load(array_ptr) 455 | 456 | def create_array(self): 457 | dyn_array_type = self.search_scopes('Dynamic_Array') 458 | array = dyn_array_type([self.const(0), self.const(0), self.const(0).inttoptr(type_map[INT].as_pointer())]) 459 | array = self.alloc_and_store(array, dyn_array_type) 460 | self.call('dyn_array_init', [array]) 461 | return array 462 | 463 | def define_list(self, node, elements): 464 | raise NotImplementedError 465 | 466 | def visit_hashmap(self, node): 467 | raise NotImplementedError 468 | 469 | def visit_collectionaccess(self, node): 470 | key = self.visit(node.key) 471 | collection = self.search_scopes(node.collection.value) 472 | if collection.type.pointee == self.search_scopes('Dynamic_Array'): 473 | return self.call('dyn_array_get', [collection, key]) 474 | else: 475 | return self.builder.extract_value(self.load(collection.name), [key]) 476 | 477 | def visit_str(self, node): 478 | array = self.create_array() 479 | string = node.value.encode('utf-8') 480 | for char in string: 481 | self.call('dyn_array_append', [array, self.const(char)]) 482 | return array 483 | 484 | def visit_print(self, node): 485 | if node.value: 486 | val = self.visit(node.value) 487 | else: 488 | self.call('putchar', [ir.Constant(type_map[INT32], 10)]) 489 | return 490 | if isinstance(val.type, ir.IntType): 491 | # noinspection PyUnresolvedReferences 492 | if val.type.width == 1: 493 | array = self.create_array() 494 | self.call('bool_to_str', [array, val]) 495 | val = array 496 | else: 497 | array = self.create_array() 498 | self.call('int_to_str', [array, val]) 499 | val = array 500 | elif isinstance(val.type, (ir.FloatType, ir.DoubleType)): 501 | percent_g = self.stringz('%g') 502 | percent_g = self.alloc_and_store(percent_g, ir.ArrayType(percent_g.type.element, percent_g.type.count)) 503 | percent_g = self.gep(percent_g, [self.const(0), self.const(0)]) 504 | percent_g = self.builder.bitcast(percent_g, type_map[INT8].as_pointer()) 505 | self.call('printf', [percent_g, val]) 506 | self.call('putchar', [ir.Constant(type_map[INT], 10)]) 507 | return 508 | self.call('print', [val]) 509 | 510 | def print_string(self, string): 511 | stringz = self.stringz(string) 512 | str_ptr = self.alloc_and_store(stringz, ir.ArrayType(stringz.type.element, stringz.type.count)) 513 | str_ptr = self.gep(str_ptr, [self.const(0), self.const(0)]) 514 | str_ptr = self.builder.bitcast(str_ptr, type_map[INT].as_pointer()) 515 | self.call('puts', [str_ptr]) 516 | 517 | def print_int(self, integer): 518 | array = self.create_array() 519 | self.call('int_to_str', [array, integer]) 520 | self.call('print', [array]) 521 | 522 | def visit_input(self, node): 523 | var_ptr = self.alloc_and_store(self.stringz(node.value.value), ir.ArrayType(type_map[INT8], len(node.value.value) + 1)) 524 | var_ptr_gep = self.gep(var_ptr, [self.const(0), self.const(0)]) 525 | self.call('puts', [var_ptr_gep]) 526 | percent_d = self.stringz('%d') 527 | percent_ptr = self.alloc_and_store(percent_d, ir.ArrayType(percent_d.type.element, percent_d.type.count)) 528 | percent_ptr_gep = self.gep(percent_ptr, [self.const(0), self.const(0)]) 529 | percent_ptr_gep = self.builder.bitcast(percent_ptr_gep, type_map[INT8].as_pointer()) 530 | return self.call('scanf', [percent_ptr_gep, self.allocate(type_map[INT])]) 531 | 532 | # noinspection PyUnusedLocal 533 | def start_function(self, name, return_type, parameters, parameter_defaults=None, varargs=None): 534 | self.function_stack.append(self.current_function) 535 | self.block_stack.append(self.builder.block) 536 | self.new_scope() 537 | ret_type = type_map[return_type.value] 538 | args = [type_map[param.value] for param in parameters.values()] 539 | arg_keys = parameters.keys() 540 | func_type = ir.FunctionType(ret_type, args) 541 | if parameter_defaults: 542 | func_type.parameter_defaults = parameter_defaults 543 | func_type.arg_order = arg_keys 544 | if hasattr(return_type, 'func_ret_type') and return_type.func_ret_type: 545 | func_type.return_type = func_type.return_type(type_map[return_type.func_ret_type.value], [return_type.func_ret_type.value]).as_pointer() 546 | func = ir.Function(self.module, func_type, name) 547 | self.define(name, func, 1) 548 | self.current_function = func 549 | entry = self.add_block('entry') 550 | self.exit_blocks.append(self.add_block('exit')) 551 | self.position_at_end(entry) 552 | 553 | def end_function(self, returned=False): 554 | if not returned: 555 | self.branch(self.exit_blocks[-1]) 556 | self.position_at_end(self.exit_blocks.pop()) 557 | if self.current_function.function_type.return_type != type_map[VOID]: 558 | retval = self.load(self.search_scopes(RET_VAR)) 559 | self.builder.ret(retval) 560 | else: 561 | self.builder.ret_void() 562 | back_block = self.block_stack.pop() 563 | self.position_at_end(back_block) 564 | last_function = self.function_stack.pop() 565 | self.current_function = last_function 566 | self.drop_top_scope() 567 | 568 | def new_builder(self, block): 569 | self.builder = ir.IRBuilder(block) 570 | return self.builder 571 | 572 | def add_block(self, name): 573 | return self.current_function.append_basic_block(name) 574 | 575 | def position_at_end(self, block): 576 | self.builder.position_at_end(block) 577 | 578 | def cbranch(self, cond, true_block, false_block): 579 | self.builder.cbranch(cond, true_block, false_block) 580 | 581 | def branch(self, block): 582 | self.builder.branch(block) 583 | 584 | def switch(self, value, default): 585 | return self.builder.switch(value, default) 586 | 587 | def const(self, val, width=None): 588 | if isinstance(val, int): 589 | if width: 590 | return ir.Constant(type_map[width], val) 591 | else: 592 | return ir.Constant(type_map[INT], val) 593 | elif isinstance(val, (float, Decimal)): 594 | return ir.Constant(type_map[DEC], val) 595 | elif isinstance(val, bool): 596 | return ir.Constant(type_map[BOOL], bool(val)) 597 | elif isinstance(val, str): 598 | return self.stringz(val) 599 | else: 600 | raise NotImplementedError 601 | 602 | def allocate(self, typ, name=''): 603 | saved_block = self.builder.block 604 | var_addr = self.create_entry_block_alloca(name, typ) 605 | self.builder.position_at_end(saved_block) 606 | return var_addr 607 | 608 | def alloc_and_store(self, val, typ, name=''): 609 | var_addr = self.builder.alloca(typ, name=name) 610 | self.builder.store(val, var_addr) 611 | return var_addr 612 | 613 | def alloc_and_define(self, name, typ): 614 | var_addr = self.builder.alloca(typ, name=name) 615 | self.define(name, var_addr) 616 | return var_addr 617 | 618 | def alloc_define_store(self, val, name, typ): 619 | saved_block = self.builder.block 620 | var_addr = self.builder.alloca(typ, name=name) 621 | self.define(name, var_addr) 622 | self.builder.position_at_end(saved_block) 623 | self.builder.store(val, var_addr) 624 | return var_addr 625 | 626 | def create_entry_block_alloca(self, name, typ): 627 | self.builder.position_at_start(self.builder.function.entry_basic_block) 628 | return self.builder.alloca(typ, size=None, name=name) 629 | 630 | def store(self, value, name): 631 | if isinstance(name, str): 632 | self.builder.store(value, self.search_scopes(name)) 633 | else: 634 | self.builder.store(value, name) 635 | 636 | def load(self, name): 637 | if isinstance(name, str): 638 | return self.builder.load(self.search_scopes(name)) 639 | return self.builder.load(name) 640 | 641 | def call(self, name, args): 642 | if isinstance(name, str): 643 | func = self.module.get_global(name) 644 | else: 645 | func = self.module.get_global(name.name) 646 | if func is None: 647 | raise TypeError('Calling non existant function') 648 | return self.builder.call(func, args) 649 | 650 | def gep(self, ptr, indices, inbounds=False, name=''): 651 | return self.builder.gep(ptr, indices, inbounds, name) 652 | 653 | def _add_builtins(self): 654 | malloc_ty = ir.FunctionType(type_map[INT8].as_pointer(), [type_map[INT]]) 655 | ir.Function(self.module, malloc_ty, 'malloc') 656 | 657 | realloc_ty = ir.FunctionType(type_map[INT8].as_pointer(), [type_map[INT8].as_pointer(), type_map[INT]]) 658 | ir.Function(self.module, realloc_ty, 'realloc') 659 | 660 | free_ty = ir.FunctionType(type_map[VOID], [type_map[INT8].as_pointer()]) 661 | ir.Function(self.module, free_ty, 'free') 662 | 663 | exit_ty = ir.FunctionType(type_map[VOID], [type_map[INT32]]) 664 | ir.Function(self.module, exit_ty, 'exit') 665 | 666 | putchar_ty = ir.FunctionType(type_map[INT], [type_map[INT]]) 667 | ir.Function(self.module, putchar_ty, 'putchar') 668 | 669 | printf_ty = ir.FunctionType(type_map[INT32], [type_map[INT8].as_pointer()], var_arg=True) 670 | ir.Function(self.module, printf_ty, 'printf') 671 | 672 | scanf_ty = ir.FunctionType(type_map[INT], [type_map[INT8].as_pointer(), type_map[INT].as_pointer()], var_arg=True) 673 | ir.Function(self.module, scanf_ty, 'scanf') 674 | 675 | getchar_ty = ir.FunctionType(ir.IntType(4), []) 676 | ir.Function(self.module, getchar_ty, 'getchar') 677 | 678 | puts_ty = ir.FunctionType(type_map[INT], [type_map[INT].as_pointer()]) 679 | ir.Function(self.module, puts_ty, 'puts') 680 | 681 | define_dynamic_array(self) 682 | 683 | @staticmethod 684 | def stringz(string): 685 | n = len(string) + 1 686 | buf = bytearray((' ' * n).encode('ascii')) 687 | buf[-1] = 0 688 | buf[:-1] = string.encode('utf-8') 689 | return ir.Constant(ir.ArrayType(type_map[INT8], n), buf) 690 | 691 | def generate_code(self, node): 692 | return self.visit(node) 693 | 694 | def evaluate(self, optimize=True, ir_dump=False, only_main=False): 695 | if ir_dump and not optimize: 696 | if only_main: 697 | print('define void @"main"(){}'.format(str(self.module).split('define void @"main"()')[1])) 698 | else: 699 | print(str(self.module)) 700 | llvmmod = llvm.parse_assembly(str(self.module)) 701 | if optimize: 702 | pmb = llvm.create_pass_manager_builder() 703 | pmb.opt_level = 2 704 | pm = llvm.create_module_pass_manager() 705 | pmb.populate(pm) 706 | pm.run(llvmmod) 707 | if ir_dump: 708 | print(str(llvmmod)) 709 | target_machine = llvm.Target.from_default_triple().create_target_machine() 710 | with llvm.create_mcjit_compiler(llvmmod, target_machine) as ee: 711 | ee.finalize_object() 712 | fptr = CFUNCTYPE(c_void_p)(ee.get_function_address('main')) 713 | start_time = time() 714 | fptr() 715 | end_time = time() 716 | print('\n{:f} sec'.format(end_time - start_time)) 717 | 718 | def compile(self, filename, optimize=True, run=False): 719 | import os 720 | import subprocess 721 | program_string = llvm.parse_assembly(str(self.module)) 722 | if optimize: 723 | pmb = llvm.create_pass_manager_builder() 724 | pmb.opt_level = 2 725 | pm = llvm.create_module_pass_manager() 726 | pmb.populate(pm) 727 | pm.run(program_string) 728 | cwd = os.getcwd() 729 | program_string = str(program_string).replace('source_filename = ""\n', '') 730 | program_string = program_string.replace('target triple = "unknown-unknown-unknown"\n', '') 731 | program_string = program_string.replace('local_unnamed_addr', '') 732 | program_string = program_string.replace('@llvm.memset.p0i8.i64(i8* nocapture writeonly', '@llvm.memset.p0i8.i64(i8* nocapture') 733 | with open(cwd + '/out/' + filename + '.ll', 'w') as output: 734 | output.write(program_string) 735 | if os.name != 'nt': 736 | os.popen('llc -filetype=obj out/{0}.ll -march=x86-64 -o out/{0}.o'.format(filename)) 737 | sleep(1) 738 | os.popen('gcc out/{0}.o -o out/{0}.bin'.format(filename)) 739 | if run: 740 | sleep(.1) 741 | start_time = time() 742 | output = subprocess.run('out/{}.bin'.format(filename), stdout=subprocess.PIPE) 743 | end_time = time() 744 | print(output.stdout.decode('utf-8')) 745 | print('{:f} sec'.format(end_time - start_time)) 746 | # os.remove('out/{}.bin'.format(filename)) 747 | # os.remove('out/{}.o'.format(filename)) 748 | -------------------------------------------------------------------------------- /compiler/operations.py: -------------------------------------------------------------------------------- 1 | from llvmlite import ir 2 | from compiler import type_map 3 | from compiler import NUM_TYPES 4 | from my_grammar import * 5 | 6 | I1 = 'i1' 7 | I8 = 'i8' 8 | I32 = 'i32' 9 | I64 = 'i64' 10 | I128 = 'i128' 11 | DOUBLE = 'double' 12 | FLOATINGPOINT = 'float' 13 | 14 | 15 | def operations(compiler, node): 16 | op = node.op 17 | left = compiler.visit(node.left) 18 | right = compiler.visit(node.right) 19 | if op == CAST: 20 | return cast_ops(compiler, left, right, node) 21 | elif isinstance(left.type, ir.IntType) and isinstance(right.type, ir.IntType): 22 | return int_ops(compiler, op, left, right, node) 23 | elif type(left.type) in NUM_TYPES and type(right.type) in NUM_TYPES: 24 | # if isinstance(left.type, ir.IntType): 25 | # left = compiler.builder.uitofp(left, type_map[DEC]) 26 | # if isinstance(right.type, ir.IntType): 27 | # right = compiler.builder.uitofp(right, type_map[DEC]) 28 | return float_ops(compiler, op, left, right, node) 29 | elif isinstance(left, (ir.LoadInstr, ir.GEPInstr)) and isinstance(right, (ir.LoadInstr, ir.GEPInstr)): 30 | new_left = compiler.search_scopes(node.left.value) 31 | new_right = compiler.search_scopes(node.right.value) 32 | return str_ops(compiler, op, new_left, new_right, node) 33 | 34 | 35 | def int_ops(compiler, op, left, right, node): 36 | # if left.type.width == 1: 37 | # left = compiler.builder.zext(left, type_map[INT]) 38 | # if right.type.width == 1: 39 | # right = compiler.builder.zext(right, type_map[INT]) 40 | if op == PLUS: 41 | return compiler.builder.add(left, right, 'addtmp') 42 | elif op == MINUS: 43 | return compiler.builder.sub(left, right, 'subtmp') 44 | elif op == MUL: 45 | return compiler.builder.mul(left, right, 'multmp') 46 | elif op == FLOORDIV: 47 | return compiler.builder.sdiv(left, right, 'divtmp') 48 | elif op == DIV: 49 | return compiler.builder.fdiv(compiler.builder.sitofp(left, type_map[DEC]), 50 | compiler.builder.sitofp(right, type_map[DEC]), 'fdivtmp') 51 | elif op == MOD: 52 | return compiler.builder.srem(left, right, 'modtmp') 53 | elif op == POWER: 54 | temp = compiler.builder.alloca(type_map[INT]) 55 | compiler.builder.store(left, temp) 56 | for _ in range(node.right.value - 1): 57 | res = compiler.builder.mul(compiler.builder.load(temp), left) 58 | compiler.builder.store(res, temp) 59 | return compiler.builder.load(temp) 60 | elif op == AND: 61 | return compiler.builder.and_(left, right) 62 | elif op == OR: 63 | return compiler.builder.or_(left, right) 64 | elif op == XOR: 65 | return compiler.builder.xor(left, right) 66 | elif op == ARITHMATIC_LEFT_SHIFT or op == BINARY_LEFT_SHIFT: 67 | return compiler.builder.shl(left, right) 68 | elif op == ARITHMATIC_RIGHT_SHIFT: 69 | return compiler.builder.ashr(left, right) 70 | elif op == BINARY_LEFT_SHIFT: 71 | return compiler.builder.lshr(left, right) 72 | elif op in (EQUALS, NOT_EQUALS, LESS_THAN, LESS_THAN_OR_EQUAL_TO, GREATER_THAN, GREATER_THAN_OR_EQUAL_TO): 73 | cmp = compiler.builder.icmp_signed(op, left, right, 'cmptmp') 74 | return compiler.builder.uitofp(cmp, type_map[BOOL], 'booltmp') 75 | else: 76 | raise SyntaxError('Unknown binary operator', node.op) 77 | 78 | 79 | def float_ops(compiler, op, left, right, node): 80 | if op == PLUS: 81 | return compiler.builder.fadd(left, right, 'faddtmp') 82 | elif op == MINUS: 83 | return compiler.builder.fsub(left, right, 'fsubtmp') 84 | elif op == MUL: 85 | return compiler.builder.fmul(left, right, 'fmultmp') 86 | elif op == FLOORDIV: 87 | return compiler.builder.udiv(compiler.builder.fptosi(left, ir.IntType(64)), 88 | compiler.builder.fptosi(right, ir.IntType(64)), 'ffloordivtmp') 89 | elif op == DIV: 90 | return compiler.builder.fdiv(left, right, 'fdivtmp') 91 | elif op == MOD: 92 | return compiler.builder.frem(left, right, 'fmodtmp') 93 | elif op == POWER: 94 | temp = compiler.builder.alloca(type_map[DEC]) 95 | compiler.builder.store(left, temp) 96 | for _ in range(node.right.value - 1): 97 | res = compiler.builder.fmul(compiler.builder.load(temp), left) 98 | compiler.builder.store(res, temp) 99 | return compiler.builder.load(temp) 100 | elif op in (EQUALS, NOT_EQUALS, LESS_THAN, LESS_THAN_OR_EQUAL_TO, GREATER_THAN, GREATER_THAN_OR_EQUAL_TO): 101 | cmp = compiler.builder.fcmp_ordered(op, left, right, 'cmptmp') 102 | return compiler.builder.sitofp(cmp, type_map[BOOL], 'booltmp') 103 | else: 104 | raise SyntaxError('Unknown binary operator', node.op) 105 | 106 | 107 | def str_ops(compiler, op, left, right, node): 108 | # TODO add strings together! 109 | # left_len = str_get_len(left, compiler) 110 | # right_len = str_get_len(right, compiler) 111 | # n = left_len + right_len 112 | return 113 | 114 | 115 | # def str_get_len(string, compiler): 116 | # if isinstance(string, ir.AllocaInstr): 117 | # str_gep = compiler.builder.gep(string, [compiler.const(1), compiler.const(1)]) 118 | # compiler.print_int(compiler.builder.ptrtoint(str_gep, type_map[INT])) 119 | # return str_gep 120 | # if isinstance(string, ir.GEPInstr): 121 | # return string.pointer.type.pointee.count 122 | 123 | 124 | def cast_ops(compiler, left, right, node): 125 | orig_type = str(left.type) 126 | cast_type = str(right) 127 | if cast_type == I64: 128 | if orig_type == DOUBLE: 129 | cast = compiler.builder.fptoui(left, type_map[INT]) 130 | return cast 131 | elif cast_type == DOUBLE: 132 | if orig_type == I64: 133 | cast = compiler.builder.uitofp(left, type_map[DEC]) 134 | return cast 135 | elif cast_type == FLOAT: 136 | raise NotImplementedError 137 | elif cast_type == COMPLEX: 138 | raise NotImplementedError 139 | elif cast_type == STR: 140 | raise NotImplementedError 141 | elif cast_type == BOOL: 142 | raise NotImplementedError 143 | elif cast_type == BYTES: 144 | raise NotImplementedError 145 | elif cast_type == LIST: 146 | raise NotImplementedError 147 | elif cast_type == DICT: 148 | raise NotImplementedError 149 | elif cast_type == ENUM: 150 | raise NotImplementedError 151 | elif cast_type in (ANY, FUNC): 152 | raise TypeError('file={} line={}: Cannot cast to type {}'.format( 153 | compiler.file_name, 154 | node.line_num, 155 | cast_type 156 | )) 157 | -------------------------------------------------------------------------------- /example.my: -------------------------------------------------------------------------------- 1 | # line comment 2 | 3 | # The syntax is still in flux be here is what I have so far 4 | 5 | # Hello World example 6 | print('Hello World') 7 | 8 | Int moogles # Initialize an Integer, must be assigned a value before it can be used 9 | 10 | number = 23 # Type Inference 11 | 12 | # Decimal will be used by default (as soon as a decimal standard is implimented) 13 | decimal_number=1.2+2.4 # Spaces are generally insignificant, but encouraged for readability 14 | # If a floating point number is desired then one could include the Float type identifier 15 | 16 | question = 'what\'s going on' # Escaping 17 | 18 | Str something # Initialize a String 19 | 20 | things = [1, 2, 3] # Array, homogeneous 21 | other_things = (1, 'Hello') # List, non-homogeneous 22 | stuff = {first_name: 'Samus', 'last_name': 'Aran'} # Dictionary, key strings don't have to be in quotes 23 | Int[] other_stuff = [] # Empty Array of Ints 24 | 25 | if number > 23 26 | print('greater than 23') 27 | else if number == 23 28 | print('equals 23') 29 | else 30 | print('less than 23') 31 | 32 | if something is not number \ # Continuing statement onto next line 33 | and true # Indentaion is with tabs only 34 | print('They are not the same') 35 | 36 | for x in 0..40 # For loop using a range 37 | print(x * 2) 38 | 39 | for item in things # Iterate over objects 40 | print(item) 41 | 42 | while number > 1 43 | number -= 1 44 | print(number) 45 | 46 | if 2 in things 47 | print('yes') 48 | 49 | if 2 not in things 50 | print('no') 51 | 52 | moogles = 1 53 | 54 | switch moogles # Switch soon to be replaced with Pattern Matching 55 | case 1 56 | print('One Moogle') 57 | case 2 58 | print('Two Moogles') 59 | break 60 | default 61 | print(moogles) 62 | print("Lots of Moogles") 63 | case 3 64 | print('Three Moogles') 65 | 66 | # Pattern matching 67 | s = 'hello world' 68 | match s.contains 69 | | 'hello' 70 | print('Greating') 71 | | 'goodby' 72 | print('Farewell') 73 | | _ 74 | print('Nothing') 75 | 76 | # Function Return notation 77 | def fib(Int n) -> Int 78 | a = 0 79 | b = 1 80 | for _ in 0..n 81 | prev_a = a 82 | a = b 83 | b = prev_a + b 84 | return a 85 | 86 | def fib_rec(Int n) -> Int 87 | if n == 0 88 | return 0 89 | if n == 1 90 | return 1 91 | return fib_rec(n - 1) + fib_rec(n - 2) 92 | 93 | def factorial(Int n) -> Int 94 | if n <= 1 95 | return 1 96 | return n * factorial(n - 1) 97 | 98 | # Assign anonymous function to a variable 99 | myfunc = def (Int x, Int y) -> Int 100 | if x > y 101 | return x + y 102 | else 103 | return x * y 104 | 105 | print(myfunc(2, 3)) 106 | 107 | def bar(Int x=4, Int y=5) -> Int 108 | return x * y 109 | 110 | baz = bar 111 | print(baz()) 112 | 113 | def foo(Int x) -> Int 114 | return baz(x) + x 115 | 116 | print(foo(2)) 117 | 118 | print('🍌') 119 | print('夜のコンサートは最高でした。') # I have no idea what this says 120 | 121 | # Type Aliasing 122 | type FInt = Func[Int] -> Int 123 | # All types (including structs and classes) should start with a capital 124 | 125 | def do_stuff(Int x, FInt callback) -> Int 126 | x **= 2 127 | x = callback(x) 128 | return x 129 | 130 | num = do_stuff(3, 131 | def (Int y) -> Int 132 | y += 7 133 | return y 134 | ) 135 | 136 | print( 137 | num 138 | ) 139 | 140 | # Closure 141 | def start_at(Int x) -> FInt 142 | def increment_by(Int y) -> Int 143 | return x + y 144 | return increment_by 145 | 146 | start_at_5 = start_at(5) 147 | start_at_27 = start_at(27) 148 | 149 | print(start_at_5(4)) 150 | print(start_at_27(15)) 151 | 152 | Int num = 1.0::Int # Casting a Decimal to Int 153 | 154 | print(num) 155 | 156 | # User input 157 | Int age = input('How old are you?') 158 | 159 | # String Interpolation 160 | print('Wow! You are {age} years old?!') 161 | 162 | Enum Colors( 163 | GREEN, 164 | RED, 165 | BLUE, 166 | YELLOW 167 | ) 168 | 169 | Struct Circle 170 | Int radius 171 | Int x 172 | Int y 173 | 174 | Circle cir = {radius=5, x=2, y=4} 175 | 176 | print(cir.radius) 177 | 178 | abstract class Vehicle 179 | # Constructor 180 | new(Str make, Int year, Str color) 181 | this.year = year 182 | this.make # Automtically assigns make from argument if they have the same name 183 | this._color = color 184 | Str this._fav_color 185 | 186 | # Inheritance 187 | class Car(Vehicle) 188 | new(Str make, Int year, color='green', hatchback=false) 189 | # Design by Contract 190 | require year < 10 191 | this.hatchback 192 | super.Vehicle(make, year, color) 193 | 194 | void print_year() 195 | print('This car was made in {this.year}') 196 | 197 | class Person 198 | new(Str name, Int age) 199 | this.name 200 | this.age 201 | 202 | frank = Person('Frank', 30) 203 | 204 | print(frank.age) 205 | 206 | garry = true 207 | larry = false 208 | num = 8 209 | 210 | if not num == 7 211 | num += 2 212 | else if garry 213 | num -= 1 214 | else 215 | num *= 4 216 | 217 | print(num) 218 | print(garry) 219 | print(larry) 220 | 221 | def is_less_than_5(Int x) -> Bool 222 | if x >= 5 223 | return false 224 | else 225 | return true 226 | 227 | print(is_less_than_5(3)) 228 | print(is_less_than_5(5)) 229 | print(is_less_than_5(7)) 230 | 231 | # Testing scope 232 | def do_stuff(Int y) -> void 233 | print(y) 234 | 235 | def square(Int x) -> Int 236 | do_stuff(x) 237 | return x ** 2 238 | 239 | print(square(5)) 240 | 241 | def greet_name(Str name) -> void 242 | print('Hello ' + name) 243 | 244 | greet_name('Anthony') 245 | 246 | x = 0 247 | while x < 10 248 | if x == 1 249 | break 250 | x += 1 251 | 252 | switch x 253 | case 2 254 | print('x is Two') 255 | break 256 | 257 | case 1 258 | print('x is One') 259 | 260 | default 261 | print("Sorry, wasn't paying attention") 262 | 263 | y = 10 264 | for x in 0..y + 1 265 | print(x::Str + ' ' + fib(x)::Str) 266 | 267 | things = [3, 2, 1] 268 | print(things[1 + 1]) 269 | 270 | Dec a 271 | b = 3 272 | 273 | def four() -> Int 274 | return 2 + 2 275 | 276 | Bool yes = true 277 | f = four() 278 | Float g = 7.5 279 | h = 5 280 | no = false 281 | 282 | if no 283 | g = 2.6 284 | if yes and no 285 | h += h 286 | else if yes 287 | g = 3.6 288 | else 289 | g = 7.6 290 | 291 | if yes 292 | g += 7 293 | first_name = 'Cid' 294 | last_name = 'Fabool' 295 | full_name = first_name + ' ' + last_name 296 | a = 2 * g 297 | d = 0 298 | c = a::Int ** b 299 | 300 | def kupo(Int first, Int second) -> Int 301 | # Nested function 302 | def mog() -> Int 303 | return 54 304 | if first < second 305 | return first + second 306 | else 307 | return mog() 308 | 309 | e = kupo(c, b) 310 | 311 | while b < 30 312 | b += c + 1 313 | if b >= 20 314 | b += 2 315 | else 316 | b += 1 317 | 318 | d += c + b 319 | 320 | print('a') 321 | print(a) 322 | print('b') 323 | print(b) 324 | print('c') 325 | print(c) 326 | print('d') 327 | print(d) 328 | print('e') 329 | print(e) 330 | print('f') 331 | print(f) 332 | print('g') 333 | print(g) 334 | print('h') 335 | print(h) 336 | print('yes') 337 | print(yes) 338 | print('no') 339 | print(no) 340 | print('first_name') 341 | print(first_name) 342 | print('last_name') 343 | print(last_name) 344 | print('full_name') 345 | print(full_name) 346 | -------------------------------------------------------------------------------- /llvm_ir/declares.ll: -------------------------------------------------------------------------------- 1 | ; ModuleID = declares 2 | 3 | declare i8* @"malloc"(i64 %".1") 4 | 5 | declare void @"free"(i8* %".1") 6 | 7 | declare i32 @"putchar"(i32 %".1") 8 | 9 | declare i32 @"printf"(i8* %".1", ...) 10 | 11 | declare i64 @"scanf"(i8* %".1", i64* %".2", ...) 12 | 13 | declare i4 @"getchar"() 14 | 15 | declare i32 @"puts"(i32* %".1") 16 | -------------------------------------------------------------------------------- /llvm_ir/dynamic_array.ll: -------------------------------------------------------------------------------- 1 | ; ModuleID = dynamic_array 2 | 3 | %struct.Vector = type { i32, i32, i32* } 4 | 5 | @.str = private unnamed_addr constant [46 x i8] c"Index %d out of bounds for vector of size %d\0A\00", align 1 6 | 7 | ; Function Attrs: nounwind uwtable 8 | define void @vector_init(%struct.Vector* %vector) { 9 | %1 = alloca %struct.Vector*, align 8 10 | store %struct.Vector* %vector, %struct.Vector** %1, align 8 11 | %2 = load %struct.Vector*, %struct.Vector** %1, align 8 12 | %3 = getelementptr inbounds %struct.Vector, %struct.Vector* %2, i32 0, i32 0 13 | store i32 0, i32* %3, align 8 14 | %4 = load %struct.Vector*, %struct.Vector** %1, align 8 15 | %5 = getelementptr inbounds %struct.Vector, %struct.Vector* %4, i32 0, i32 1 16 | store i32 100, i32* %5, align 4 17 | %6 = load %struct.Vector*, %struct.Vector** %1, align 8 18 | %7 = getelementptr inbounds %struct.Vector, %struct.Vector* %6, i32 0, i32 1 19 | %8 = load i32, i32* %7, align 4 20 | %9 = sext i32 %8 to i64 21 | %10 = mul i64 4, %9 22 | %11 = call noalias i8* @malloc(i64 %10) 23 | %12 = bitcast i8* %11 to i32* 24 | %13 = load %struct.Vector*, %struct.Vector** %1, align 8 25 | %14 = getelementptr inbounds %struct.Vector, %struct.Vector* %13, i32 0, i32 2 26 | store i32* %12, i32** %14, align 8 27 | ret void 28 | } 29 | 30 | ; Function Attrs: nounwind 31 | declare noalias i8* @malloc(i64) 32 | 33 | ; Function Attrs: nounwind uwtable 34 | define void @vector_append(%struct.Vector* %vector, i32 %value) { 35 | %1 = alloca %struct.Vector*, align 8 36 | %2 = alloca i32, align 4 37 | store %struct.Vector* %vector, %struct.Vector** %1, align 8 38 | store i32 %value, i32* %2, align 4 39 | %3 = load %struct.Vector*, %struct.Vector** %1, align 8 40 | call void @vector_double_capacity_if_full(%struct.Vector* %3) 41 | %4 = load i32, i32* %2, align 4 42 | %5 = load %struct.Vector*, %struct.Vector** %1, align 8 43 | %6 = getelementptr inbounds %struct.Vector, %struct.Vector* %5, i32 0, i32 0 44 | %7 = load i32, i32* %6, align 8 45 | %8 = add nsw i32 %7, 1 46 | store i32 %8, i32* %6, align 8 47 | %9 = sext i32 %7 to i64 48 | %10 = load %struct.Vector*, %struct.Vector** %1, align 8 49 | %11 = getelementptr inbounds %struct.Vector, %struct.Vector* %10, i32 0, i32 2 50 | %12 = load i32*, i32** %11, align 8 51 | %13 = getelementptr inbounds i32, i32* %12, i64 %9 52 | store i32 %4, i32* %13, align 4 53 | ret void 54 | } 55 | 56 | ; Function Attrs: nounwind uwtable 57 | define void @vector_double_capacity_if_full(%struct.Vector* %vector) { 58 | %1 = alloca %struct.Vector*, align 8 59 | store %struct.Vector* %vector, %struct.Vector** %1, align 8 60 | %2 = load %struct.Vector*, %struct.Vector** %1, align 8 61 | %3 = getelementptr inbounds %struct.Vector, %struct.Vector* %2, i32 0, i32 0 62 | %4 = load i32, i32* %3, align 8 63 | %5 = load %struct.Vector*, %struct.Vector** %1, align 8 64 | %6 = getelementptr inbounds %struct.Vector, %struct.Vector* %5, i32 0, i32 1 65 | %7 = load i32, i32* %6, align 4 66 | %8 = icmp sge i32 %4, %7 67 | br i1 %8, label %9, label %27 68 | 69 | ;