├── .codecov.yml ├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── src ├── main │ ├── antlr │ │ └── Lang.g4 │ └── kotlin │ │ └── xyz │ │ └── jadonfowler │ │ └── compiler │ │ ├── Compiler.kt │ │ ├── ast │ │ ├── ast.kt │ │ └── type.kt │ │ ├── backend │ │ ├── Backend.kt │ │ ├── JVMBackend.kt │ │ └── LLVMBackend.kt │ │ ├── pass │ │ ├── ConstantFoldPass.kt │ │ ├── Pass.kt │ │ ├── PrintPass.kt │ │ ├── SemanticAnalysis.kt │ │ └── TypePass.kt │ │ └── visitor │ │ ├── ASTBuilder.kt │ │ └── Visitor.kt └── test │ └── kotlin │ └── xyz │ └── jadonfowler │ └── compiler │ ├── ASTTest.kt │ ├── CompilerTestSuite.kt │ ├── ConstantFoldTest.kt │ ├── JVMTest.kt │ ├── LLVMTest.kt │ ├── ParallelSuite.kt │ ├── SemanticsTest.kt │ └── TypeCheckingTest.kt ├── std ├── io.l ├── llvm │ ├── fpmath.ll │ ├── io.ll │ └── main.ll └── math │ └── fp.l └── test ├── allocationInIf.l ├── allocationInLoop.l ├── allocationPutInField.l ├── classDeclaration.l ├── classInClass.l ├── copyClass.l ├── differentFloatTypes.l ├── differentIntTypes.l ├── elifBranching.l ├── genComplexExpressions.l ├── genComplexExpressionsInWhileLoop.l ├── genExecutable.l ├── genExternalFunction.l ├── genFunction.l ├── genGlobalConstant.l ├── genGlobalsInFunctions.l ├── genIfStatement.l ├── genImportExternalFunction.l ├── genInfixFunctionCall.l ├── genOperators.l ├── genRecursiveCall.l ├── genRecursiveInput1.l ├── genRecursiveInput2.l ├── genVariableDeclaration.l ├── genVariableReassignment.l ├── genWhileLoop.l ├── importClass1.l ├── importClass2.l ├── lotsOfAllocations.l ├── out └── llvm │ ├── allocationInIf.ll │ ├── allocationInLoop.ll │ ├── allocationPutInField.ll │ ├── classDeclaration.ll │ ├── classInClass.ll │ ├── copyClass.ll │ ├── differentFloatTypes.ll │ ├── differentIntTypes.ll │ ├── elifBranching.ll │ ├── genComplexExpressions.ll │ ├── genComplexExpressionsInWhileLoop.ll │ ├── genExecutable.ll │ ├── genExternalFunction.ll │ ├── genFunction.ll │ ├── genGlobalConstant.ll │ ├── genGlobalsInFunctions.ll │ ├── genIfStatement.ll │ ├── genImportExternalFunction.ll │ ├── genInfixFunctionCall.ll │ ├── genOperators.ll │ ├── genRecursiveCall.ll │ ├── genRecursiveInput1.ll │ ├── genRecursiveInput2.ll │ ├── genVariableDeclaration.ll │ ├── genVariableReassignment.ll │ ├── genWhileLoop.ll │ ├── importClass1.ll │ ├── importClass2.ll │ ├── lotsOfAllocations.ll │ └── outputFactorial.ll └── outputFactorial.l /.codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | require_ci_to_pass: yes 4 | 5 | coverage: 6 | precision: 2 7 | round: down 8 | range: "70...100" 9 | 10 | status: 11 | project: yes 12 | patch: yes 13 | changes: no 14 | 15 | parsers: 16 | gcov: 17 | branch_detection: 18 | conditional: yes 19 | loop: yes 20 | method: no 21 | macro: no 22 | 23 | comment: 24 | layout: "header, diff" 25 | behavior: default 26 | require_changes: no -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore generated sources from ANTLR 2 | src/main/java/xyz/jadonfowler/compiler/parser/ 3 | 4 | # IntelliJ 5 | *.ipr 6 | *.iws 7 | .idea/ 8 | 9 | # Gradle 10 | .gradle/ 11 | build/ 12 | 13 | # Output from LLVMBackend 14 | bin/ 15 | 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | dist: trusty 3 | sudo: required 4 | 5 | jdk: 6 | - oraclejdk8 7 | 8 | addons: 9 | apt: 10 | packages: 11 | - llvm-3.8 12 | - clang-3.8 13 | 14 | script: 15 | - ./gradlew check --stacktrace --info 16 | - ./gradlew jacocoTestReport 17 | 18 | after_success: 19 | - bash <(curl -s https://codecov.io/bash) 20 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mars compiler 2 | 3 | [![Build Status](https://travis-ci.org/phase/lang-kotlin-antlr-compiler.svg?branch=master)](https://travis-ci.org/phase/lang-kotlin-antlr-compiler) 4 | 5 | [![codecov](https://codecov.io/gh/phase/lang-kotlin-antlr-compiler/branch/master/graph/badge.svg)](https://codecov.io/gh/phase/lang-kotlin-antlr-compiler) 6 | 7 | This is a compiler written in Kotlin using an ANTLR parser. The language includes: Global variables, Functions, Classes, Methods, Automatic Memory Management, Type Inference, Calling External Functions, Modules, etc. 8 | 9 | Here's some example syntax: 10 | 11 | ```rust 12 | genComplexExpressionsInWhileLoop (a : Int, z : Int, y : Int, x : Int, w : Int) 13 | var i = 0, 14 | var sum = 0, 15 | while i < a 16 | var v = 42 + x, 17 | let u = 45 + v * 67 + 124 - (w * 4) / 5, 18 | v = v * 2 - z, 19 | var t = 1, 20 | if z < 10 21 | t = v * z 22 | else 23 | t = v - z 24 | ; 25 | let l = 74 * 3 - v + z * x - w, 26 | i = 5 + u * z * v + t * 2 * l 27 | ; 28 | let r = sum * i, 29 | r 30 | ``` 31 | 32 | This will be parsed into the AST and printed out as: 33 | 34 | ```javascript 35 | ; Module: genComplexExpressionsInWhileLoop 36 | 37 | ; genComplexExpressionsInWhileLoop has 4 statements and a return expression. 38 | function genComplexExpressionsInWhileLoop (a: Int, z: Int, y: Int, x: Int, w: Int) -> Int { 39 | ; VariableDeclarationStatement 40 | variable i: Int = 0 41 | ; VariableDeclarationStatement 42 | variable sum: Int = 0 43 | ; WhileStatement 44 | while (i < a) { 45 | ; VariableDeclarationStatement 46 | variable v: Int = (42 + x) 47 | ; VariableDeclarationStatement 48 | constant u: Int = (((45 + (v * 67)) + 124) - ((w * 4) / 5)) 49 | ; VariableReassignmentStatement 50 | v = ((v * 2) - z) 51 | ; VariableDeclarationStatement 52 | variable t: Int = 1 53 | ; IfStatement 54 | if (z < 10) { 55 | ; VariableReassignmentStatement 56 | t = (v * z) 57 | } 58 | else if (true) { 59 | ; VariableReassignmentStatement 60 | t = (v - z) 61 | } 62 | ; VariableDeclarationStatement 63 | constant l: Int = ((((74 * 3) - v) + (z * x)) - w) 64 | ; VariableReassignmentStatement 65 | i = ((5 + ((u * z) * v)) + ((t * 2) * l)) 66 | } 67 | ; VariableDeclarationStatement 68 | constant r: Int = (sum * i) 69 | return r 70 | } 71 | ``` 72 | 73 | (Notice the type inference: variables and return types are inferred and 74 | checked in the TypePass.) 75 | 76 | The LLVM backend will produce something like: 77 | 78 | ```LLVM 79 | ; ModuleID = 'genComplexExpressionsInWhileLoop' 80 | 81 | define i32 @genComplexExpressionsInWhileLoop(i32, i32, i32, i32, i32) { 82 | entry: 83 | br label %"while.c (i < a)" 84 | 85 | "while.c (i < a)": ; preds = %"if.o (z < 10)", %entry 86 | %i.0 = phi i32 [ 0, %entry ], [ %"((5 + ((u * z) * v)) + ((t * 2) * l))", %"if.o (z < 10)" ] 87 | %"(i < a)" = icmp slt i32 %i.0, %0 88 | br i1 %"(i < a)", label %"while.b (i < a)", label %"while.o (i < a)" 89 | 90 | "while.b (i < a)": ; preds = %"while.c (i < a)" 91 | %v = alloca i32, align 4 92 | %"(42 + x)" = add i32 %3, 42 93 | store i32 %"(42 + x)", i32* %v, align 4 94 | %"(v * 67)" = mul i32 %"(42 + x)", 67 95 | %"((45 + (v * 67)) + 124)" = add i32 %"(v * 67)", 169 96 | %"(w * 4)" = shl i32 %4, 2 97 | %"((w * 4) / 5)" = sdiv i32 %"(w * 4)", 5 98 | %"(((45 + (v * 67)) + 124) - ((w * 4) / 5))" = sub i32 %"((45 + (v * 67)) + 124)", %"((w * 4) / 5)" 99 | %"(v * 2)" = shl i32 %"(42 + x)", 1 100 | %"((v * 2) - z)" = sub i32 %"(v * 2)", %1 101 | store i32 %"((v * 2) - z)", i32* %v, align 4 102 | %t = alloca i32, align 4 103 | store i32 1, i32* %t, align 4 104 | %"(z < 10)" = icmp slt i32 %1, 10 105 | br i1 %"(z < 10)", label %"if.t (z < 10)", label %"if.t true" 106 | 107 | "while.o (i < a)": ; preds = %"while.c (i < a)" 108 | ret i32 0 109 | 110 | "if.t (z < 10)": ; preds = %"while.b (i < a)" 111 | %"(v * z)" = mul i32 %"((v * 2) - z)", %1 112 | store i32 %"(v * z)", i32* %t, align 4 113 | br label %"if.o (z < 10)" 114 | 115 | "if.o (z < 10)": ; preds = %"if.t true", %"if.t (z < 10)" 116 | %t8 = phi i32 [ %"(v - z)", %"if.t true" ], [ %"(v * z)", %"if.t (z < 10)" ] 117 | %"((74 * 3) - v)" = sub i32 222, %"((v * 2) - z)" 118 | %"(z * x)" = mul i32 %1, %3 119 | %"(((74 * 3) - v) + (z * x))" = add i32 %"((74 * 3) - v)", %"(z * x)" 120 | %"((((74 * 3) - v) + (z * x)) - w)" = sub i32 %"(((74 * 3) - v) + (z * x))", %4 121 | %"(u * z)" = mul i32 %"(((45 + (v * 67)) + 124) - ((w * 4) / 5))", %1 122 | %"((u * z) * v)" = mul i32 %"(u * z)", %"((v * 2) - z)" 123 | %"(5 + ((u * z) * v))" = add i32 %"((u * z) * v)", 5 124 | %"(t * 2)" = shl i32 %t8, 1 125 | %"((t * 2) * l)" = mul i32 %"(t * 2)", %"((((74 * 3) - v) + (z * x)) - w)" 126 | %"((5 + ((u * z) * v)) + ((t * 2) * l))" = add i32 %"(5 + ((u * z) * v))", %"((t * 2) * l)" 127 | br label %"while.c (i < a)" 128 | 129 | "if.t true": ; preds = %"while.b (i < a)" 130 | %"(v - z)" = sub i32 %"((v * 2) - z)", %1 131 | store i32 %"(v - z)", i32* %t, align 4 132 | br label %"if.o (z < 10)" 133 | } 134 | ``` 135 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | group "xyz.jadonfowler" 2 | version "0.0.0-SNAPSHOT" 3 | 4 | buildscript { 5 | ext.kotlin_version = "1.0.6" 6 | 7 | repositories { 8 | mavenCentral() 9 | } 10 | dependencies { 11 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 12 | } 13 | } 14 | 15 | apply plugin: "antlr" 16 | apply plugin: "java" 17 | apply plugin: "kotlin" 18 | apply plugin: "application" 19 | apply plugin: "jacoco" 20 | 21 | mainClassName = "xyz.jadonfowler.compiler.CompilerKt" 22 | 23 | task fatJar(type: Jar) { 24 | manifest { 25 | attributes 'Implementation-Title': 'Compiler', 26 | 'Implementation-Version': version, 27 | 'Main-Class': 'xyz.jadonfowler.compiler.CompilerKt' 28 | } 29 | baseName = project.name + '-all' 30 | from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } 31 | with jar 32 | } 33 | 34 | repositories { 35 | mavenCentral() 36 | } 37 | 38 | // https://github.com/bytedeco/javacpp-presets/wiki/Issues-with-Build-Tools#gradle 39 | configurations { 40 | all*.exclude group: "org.bytedeco", module: "javacpp-presets" 41 | } 42 | 43 | dependencies { 44 | antlr "org.antlr:antlr4:4.5" // use ANTLR version 4 45 | compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 46 | compile "org.bytedeco:javacpp:1.3" 47 | compile "org.bytedeco.javacpp-presets:llvm:3.9.0-1.3" 48 | compile "org.bytedeco.javacpp-presets:llvm:3.9.0-1.3:linux-x86_64" 49 | compile "org.ow2.asm:asm:5.0.3" 50 | 51 | testCompile "junit:junit:4.11" 52 | testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version" 53 | } 54 | 55 | jacoco { 56 | toolVersion = "0.7.4+" 57 | } 58 | 59 | jacocoTestReport { 60 | reports { 61 | html.enabled = true 62 | xml.enabled = true 63 | csv.enabled = false 64 | } 65 | } 66 | 67 | generateGrammarSource { 68 | maxHeapSize = "64m" 69 | arguments += ["-visitor", "-package", "xyz.jadonfowler.compiler.parser"] 70 | // Output generated sources from ANTLR into xyz.jadonfowler.compiler.parser 71 | outputDirectory = new File("src//main/java/xyz/jadonfowler/compiler/parser/".toString()) 72 | } 73 | 74 | compileKotlin.dependsOn generateGrammarSource 75 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.incremental=true 2 | 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phase/mars/7ce52caedd80d96504d9598c975e5c754cffe9a9/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Feb 23 19:57:25 PST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.4-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'compiler' 2 | 3 | -------------------------------------------------------------------------------- /src/main/antlr/Lang.g4: -------------------------------------------------------------------------------- 1 | grammar Lang; 2 | 3 | program: importDeclaration* externalDeclaration* EOF; 4 | 5 | id_p: ID ('.' ID)*; 6 | 7 | importDeclaration 8 | : 'import' id_p (',' id_p)* 9 | ; 10 | 11 | externalDeclaration 12 | : functionDeclaration 13 | | variableDeclaration 14 | | classDeclaration 15 | | traitDeclaration 16 | ; 17 | 18 | functionPrototype: attributeList ID '(' argumentList ')' typeAnnotation?; 19 | functionDeclaration: functionPrototype statementList (expression | statement | blockStatement); 20 | variableDeclaration: variableModifier variableSignature ('=' expression)?; 21 | 22 | functionCall: ID '(' expressionList ')'; 23 | methodCall: ID '.' ID '(' expressionList ')'; 24 | 25 | variableModifier 26 | : 'let' 27 | | 'var' 28 | ; 29 | 30 | attributeList 31 | : 32 | | attribute attributeList 33 | ; 34 | 35 | attribute: '@' ID ('(' ID (',' ID)* ')')?; 36 | 37 | variableReassignment: ID '=' expression; 38 | 39 | statement 40 | : fieldSetter 41 | | variableDeclaration 42 | | variableReassignment 43 | | methodCall 44 | | functionCall 45 | | 'continue' 46 | | 'break' 47 | | 'return' expression 48 | ; 49 | 50 | blockStatement 51 | : 'if' expression statementList elif* elseStatement? ';' 52 | | 'for' ID 'in' expression statementList ';' 53 | | 'while' expression statementList ';' 54 | ; 55 | 56 | elif: 'elif' expression statementList; 57 | elseStatement: 'else' statementList; 58 | 59 | statementList 60 | : 61 | | statement 62 | | blockStatement 63 | | blockStatement statementList 64 | | statement ',' statementList 65 | ; 66 | 67 | classDeclaration 68 | : 'class' ID (':' ID (',' ID)*)? externalDeclaration* ';' 69 | ; 70 | 71 | traitDeclaration 72 | : 'trait' ID functionPrototype* ';' 73 | ; 74 | 75 | expressionList 76 | : 77 | | expression 78 | | expression ',' expressionList 79 | ; 80 | 81 | expression 82 | : ('-'|'+') expression 83 | | expression '.' ID // field getter 84 | | expression ('*'|'/'|'*.'|'/.') expression 85 | | expression ('+'|'-'|'-.'|'+.') expression 86 | | expression ('>'|'>='|'<'|'<='|'=='|'!=') expression 87 | | '!' expression 88 | | expression ('&'|'&&') expression 89 | | expression ('|'|'||') expression 90 | | '~' expression 91 | | '(' expression ')' 92 | | expression '`' ID '`' expression // infix function 93 | | methodCall 94 | | classInitializer 95 | | functionCall 96 | | ID 97 | | STRING 98 | | HEX 99 | | INT 100 | | FLOAT 101 | | 'true' 102 | | 'false' 103 | ; 104 | 105 | fieldSetter: expression '.' ID '=' expression; 106 | 107 | classInitializer: 'new' ID '(' expressionList ')'; 108 | 109 | argumentList: argument (',' argument)*|; 110 | 111 | argument 112 | : variableSignature 113 | | variableSignature '=' expression 114 | ; 115 | 116 | typeAnnotation: ':' ID; 117 | variableSignature: ID typeAnnotation?; 118 | 119 | HEX: '0' ('x'|'X') HEXDIGIT+ [Ll]?; 120 | 121 | INT: DIGIT+ [Ll]?; 122 | 123 | fragment HEXDIGIT: ('0'..'9'|'a'..'f'|'A'..'F'); 124 | 125 | FLOAT 126 | : DIGIT+ '.' DIGIT* EXP? [dq]? 127 | | DIGIT+ EXP? [dq]? 128 | | '.' DIGIT+ EXP? [dq]? 129 | ; 130 | 131 | fragment DIGIT: '0'..'9'; 132 | fragment EXP: ('E' | 'e') ('+' | '-')? INT; 133 | 134 | STRING 135 | : '"' ( ESC | ~[\\"] )*? '"' 136 | | '\'' ( ESC | ~[\\'] )*? '\'' 137 | ; 138 | 139 | fragment ESC 140 | : '\\' [abtnfrv"'\\] 141 | | UNICODE_ESCAPE 142 | | HEX_ESCAPE 143 | | OCTAL_ESCAPE 144 | ; 145 | 146 | fragment UNICODE_ESCAPE 147 | : '\\' 'u' HEXDIGIT HEXDIGIT HEXDIGIT HEXDIGIT 148 | | '\\' 'u' '{' HEXDIGIT HEXDIGIT HEXDIGIT HEXDIGIT '}' 149 | ; 150 | 151 | fragment OCTAL_ESCAPE 152 | : '\\' [0-3] [0-7] [0-7] 153 | | '\\' [0-7] [0-7] 154 | | '\\' [0-7] 155 | ; 156 | 157 | fragment HEX_ESCAPE 158 | : '\\' HEXDIGIT HEXDIGIT? 159 | ; 160 | 161 | ID: LETTER (LETTER|DIGIT|'_')*; 162 | 163 | fragment LETTER: [a-zA-Z]; 164 | 165 | COMMENT: '#' .*? '\r'? '\n' -> type(WS); 166 | 167 | WS: [ \r\n\t\u000C]+ -> skip; 168 | -------------------------------------------------------------------------------- /src/main/kotlin/xyz/jadonfowler/compiler/Compiler.kt: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.compiler 2 | 3 | import org.antlr.v4.runtime.ANTLRInputStream 4 | import org.antlr.v4.runtime.CommonTokenStream 5 | import org.antlr.v4.runtime.ParserRuleContext 6 | import org.antlr.v4.runtime.RuleContext 7 | import xyz.jadonfowler.compiler.ast.Module 8 | import xyz.jadonfowler.compiler.backend.JVMBackend 9 | import xyz.jadonfowler.compiler.backend.LLVMBackend 10 | import xyz.jadonfowler.compiler.parser.LangLexer 11 | import xyz.jadonfowler.compiler.parser.LangParser 12 | import xyz.jadonfowler.compiler.pass.ConstantFoldPass 13 | import xyz.jadonfowler.compiler.pass.PrintPass 14 | import xyz.jadonfowler.compiler.pass.SemanticAnalysis 15 | import xyz.jadonfowler.compiler.pass.TypePass 16 | import xyz.jadonfowler.compiler.visitor.ASTBuilder 17 | import java.io.File 18 | 19 | val globalModules = mutableListOf() 20 | 21 | val EXTENSION = "l" 22 | 23 | fun main(args: Array) { 24 | val files: MutableList = mutableListOf() 25 | val options: MutableList = mutableListOf() 26 | 27 | args.forEach { 28 | if (it.startsWith("--")) // This argument is an option 29 | options.add(it.substring(2, it.length)) 30 | else // This argument is a file 31 | files.add(it) 32 | } 33 | 34 | if (files.isEmpty()) { 35 | println("No input files found!") 36 | System.exit(2) 37 | } 38 | 39 | // 40 | val modulesToCompile: MutableMap = mutableMapOf() 41 | 42 | val nonStdFiles = files.size 43 | 44 | // Standard Lib 45 | val stdDirectory = File("std") 46 | if (stdDirectory.exists() && stdDirectory.isDirectory) { 47 | val stdFiles = stdDirectory.listFiles() 48 | fun addFiles(it: File) { 49 | if (it.isFile && it.extension == EXTENSION) 50 | files.add(it.canonicalPath) 51 | else if (it.isDirectory) 52 | it.listFiles().forEach(::addFiles) 53 | } 54 | stdFiles.forEach(::addFiles) 55 | } 56 | 57 | // Read files that we need to compile 58 | files.forEach { 59 | val file = File(it) 60 | if (file.exists() && !file.isDirectory) { 61 | var moduleName = file.parentFile.name + "." + file.nameWithoutExtension 62 | 63 | // Get package name 64 | var parent = file.parentFile.parentFile 65 | while (parent != null && parent.isDirectory) { 66 | val filesInParent = parent.listFiles().filter { it.extension == EXTENSION } 67 | if (filesInParent.isNotEmpty()) { 68 | moduleName = parent.name + "." + moduleName 69 | } else break 70 | parent = parent.parentFile 71 | } 72 | 73 | modulesToCompile.put(moduleName, file.readLines().joinToString("\n")) 74 | } else { 75 | println("Can't find file '$it'.") 76 | System.exit(1) 77 | } 78 | } 79 | 80 | // Go over every module and parse it 81 | val modules: List = modulesToCompile.map { compileString(it.key, it.value) } 82 | 83 | var failed = false 84 | 85 | // Default passes 86 | modules.forEach { 87 | SemanticAnalysis(it) 88 | TypePass(it) 89 | ConstantFoldPass(it) 90 | 91 | if (it.errors.size > 0) { 92 | println("Found ${it.errors.size} errors in ${it.name}:") 93 | it.errors.forEach(::println) 94 | failed = true 95 | } 96 | } 97 | 98 | if (failed) System.exit(1) 99 | 100 | // The output should be the same for no matter the order of arguments 101 | options.sort() 102 | 103 | options.forEach { 104 | when (it.toLowerCase()) { 105 | "ast" -> { 106 | // Print the AST for each input module 107 | (0..nonStdFiles - 1).forEach { println(PrintPass(modules[it]).output) } 108 | } 109 | "llvm" -> { 110 | // Output native code through LLVM Backend 111 | val bin = File("bin") 112 | if (!bin.exists()) 113 | bin.mkdirs() 114 | modules.forEach { 115 | LLVMBackend(it).output(File("bin/${it.name}")) 116 | } 117 | 118 | // Compile LLVM specific Std Modules 119 | val runtime = Runtime.getRuntime() 120 | 121 | val stdLlvmDir = File("std/llvm/") 122 | if (!stdLlvmDir.exists() || !stdLlvmDir.isDirectory) 123 | stdLlvmDir.mkdirs() 124 | val stdLlvmBin = File("bin/llvm") 125 | if (!stdLlvmBin.exists()) 126 | stdLlvmBin.mkdirs() 127 | 128 | val stdLlvmFiles = stdLlvmDir.listFiles().map { it.canonicalPath } 129 | stdLlvmFiles.forEach { 130 | val llcProcess = runtime.exec("llc $it -filetype=obj -o bin/llvm/$it.o") 131 | llcProcess.waitFor() 132 | } 133 | // Link object files together 134 | val linkCommand = modules.map { "bin/${it.name}.o" }.toMutableList() 135 | stdLlvmFiles.forEach { 136 | linkCommand.add(it) 137 | } 138 | linkCommand.add(0, "clang") 139 | linkCommand.add("-lm") 140 | linkCommand.add("-o") 141 | linkCommand.add("bin/exec/${modules[0].name}") 142 | 143 | val execDir = File("bin/exec") 144 | if (!execDir.exists()) 145 | execDir.mkdirs() 146 | val clangProcess = runtime.exec(linkCommand.joinToString(separator = " ")) 147 | println(clangProcess.waitFor()) 148 | println("Compiled Executable " + String(clangProcess.inputStream.readBytes())) 149 | println(String(clangProcess.errorStream.readBytes())) 150 | } 151 | "jvm" -> { 152 | val bin = File("bin") 153 | if (!bin.exists()) 154 | bin.mkdirs() 155 | modules.forEach { 156 | JVMBackend(it).output(File("bin/${it.name}.class")) 157 | } 158 | } 159 | } 160 | } 161 | } 162 | 163 | fun compileString(moduleName: String, code: String, explore: Boolean = false): Module { 164 | val stream = ANTLRInputStream(code) 165 | val lexer = LangLexer(stream) 166 | val tokens = CommonTokenStream(lexer) 167 | val parser = LangParser(tokens) 168 | val result = parser.program() 169 | val astBuilder = ASTBuilder(moduleName, code) 170 | if (explore) explore(result, 0) 171 | return astBuilder.visitProgram(result) 172 | } 173 | 174 | fun explore(ctx: RuleContext, indentation: Int = 0) { 175 | val ignore = ctx.childCount === 1 && ctx.getChild(0) is ParserRuleContext 176 | if (!ignore) { 177 | val ruleName = LangParser.ruleNames[ctx.ruleIndex] 178 | println(" ".repeat(indentation) 179 | + ctx.javaClass.name.split(".").last() 180 | + " " + ruleName + ":\n" 181 | + " ".repeat(indentation) 182 | + ctx.text 183 | + "\n") 184 | } 185 | 186 | (0..ctx.childCount - 1).forEach { 187 | val element = ctx.getChild(it) 188 | if (element is RuleContext) 189 | explore(element, indentation + (if (ignore) 0 else 1)) 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/main/kotlin/xyz/jadonfowler/compiler/ast/ast.kt: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.compiler.ast 2 | 3 | import org.antlr.v4.runtime.ParserRuleContext 4 | import xyz.jadonfowler.compiler.globalModules 5 | import java.util.* 6 | 7 | interface Node 8 | 9 | /** 10 | * Used in ASTBuilder for areas where we need to return something. 11 | */ 12 | class EmptyNode : Node 13 | 14 | /** 15 | * Modules are compilation units that contain global variables, functions, and classes. 16 | */ 17 | class Module(val name: String, val imports: List, val globalVariables: List, 18 | val globalFunctions: List, val globalClasses: List, 19 | val globalTraits: List, val source: String) : Node { 20 | 21 | val errors: MutableList = mutableListOf() 22 | 23 | // TODO: I thought this was needed but I never used it. 24 | // fun containsReference(reference: Reference): Boolean { 25 | // return globalVariables.map { it.name }.contains(reference.name) 26 | // || globalFunctions.map { it.name }.contains(reference.name) 27 | // || globalClasses.map { it.name }.contains(reference.name) 28 | // } 29 | 30 | fun getFunctionFromReference(reference: Reference): Function? { 31 | val moduleName = name 32 | val name = reference.name 33 | val thingsWithName: List = globalFunctions.filter { it.name == name } 34 | if (thingsWithName.isNotEmpty()) 35 | return thingsWithName.last() 36 | 37 | // Go through imports 38 | val imports = imports.map { it.reference.name }.filter { it != moduleName } 39 | globalModules.forEach { 40 | if (imports.contains(it.name)) { 41 | val function = it.getFunctionFromReference(reference) 42 | if (function != null) 43 | return function 44 | } 45 | } 46 | 47 | return null 48 | } 49 | 50 | fun getClazzFromReference(reference: Reference): Clazz? { 51 | val moduleName = name 52 | val name = reference.name 53 | val thingsWithName: List = globalClasses.filter { it.name == name } 54 | if (thingsWithName.isNotEmpty()) 55 | return thingsWithName.last() 56 | 57 | // Go through imports 58 | val imports = imports.map { it.reference.name }.filter { it != moduleName } 59 | globalModules.forEach { 60 | if (imports.contains(it.name)) { 61 | val clazz = it.getClazzFromReference(reference) 62 | if (clazz != null) 63 | return clazz 64 | } 65 | } 66 | 67 | return null 68 | } 69 | 70 | fun getNodeFromReference(reference: Reference, localVariables: MutableMap?): Node? { 71 | val name = reference.name 72 | val thingsWithName: MutableList = mutableListOf() 73 | 74 | thingsWithName.addAll(globalClasses.filter { it.name == name }) 75 | thingsWithName.addAll(globalTraits.filter { it.name == name }) 76 | thingsWithName.addAll(globalFunctions.filter { it.name == name }) 77 | thingsWithName.addAll(globalVariables.filter { it.name == name }) 78 | localVariables?.forEach { if (it.key == name) thingsWithName.add(it.value) } 79 | 80 | if (thingsWithName.isNotEmpty()) { 81 | val lastThingWithName = thingsWithName.last() 82 | when (lastThingWithName) { 83 | is Clazz -> return lastThingWithName 84 | is Trait -> return lastThingWithName 85 | is Function -> return lastThingWithName 86 | is Variable -> return lastThingWithName 87 | is Formal -> return lastThingWithName 88 | } 89 | } 90 | 91 | // Go through imports 92 | val imports = imports.map { it.reference.name } 93 | globalModules.filter { imports.contains(it.name) }.forEach { 94 | val node = it.getNodeFromReference(reference, null) 95 | if (node != null) return node 96 | } 97 | return null 98 | } 99 | 100 | } 101 | 102 | /** 103 | * Container for name of module to import 104 | */ 105 | class Import(val reference: Reference, val context: ParserRuleContext) : Node 106 | 107 | /** 108 | * Attributes can be put on various declarations 109 | */ 110 | class Attribute(val name: String, val values: List, val context: ParserRuleContext) : Node { 111 | override fun toString(): String = "@$name" 112 | override fun hashCode(): Int = Objects.hash(name, values) 113 | override fun equals(other: Any?): Boolean = 114 | other is Attribute && other.name == name && other.values == values && other.hashCode() == hashCode() 115 | } 116 | 117 | /** 118 | * Declaration that can be accessed outside the Module 119 | */ 120 | interface Global : Node 121 | 122 | /** 123 | * Functions have a return type, which is the type of the value that is returned; 124 | * a list of Formals (aka Arguments), which are used inside the Function; 125 | * a list of Statements, which are executed in order and can use any formals or global objects; 126 | * and an optional last expression. The last expression is used as the return value for the function. 127 | * If there is no last expression, the function returns "void" (aka nothing). 128 | */ 129 | class Function(val prototype: Prototype, val statements: List, var expression: Expression? = null, 130 | val context: ParserRuleContext) : Global, Type { 131 | 132 | constructor(attributes: List, returnType: Type, name: String, formals: List, 133 | statements: List, expression: Expression? = null, context: ParserRuleContext) 134 | : this(Prototype(attributes, returnType, name, formals), statements, expression, context) 135 | 136 | override fun toString(): String { 137 | val formals = formals.joinToString(separator = " -> ") { it.type.toString() } 138 | return "($formals -> $returnType)" 139 | } 140 | 141 | override fun hashCode(): Int = Objects.hash(attributes, returnType, name, formals, statements, expression) 142 | 143 | override fun equals(other: Any?): Boolean = 144 | other is Function && other.hashCode() == hashCode() && other.attributes == attributes 145 | && other.returnType == returnType && other.name == name && other.formals == formals 146 | 147 | fun copy() = Function(attributes, returnType, name, formals, statements, expression, context) 148 | 149 | override fun isCopyable(): Boolean = false 150 | 151 | class Prototype(val attributes: List, var returnType: Type, var name: String, var formals: List) { 152 | override fun hashCode(): Int = Objects.hash(attributes, returnType, name, formals) 153 | override fun equals(other: Any?): Boolean = other is Prototype 154 | && other.attributes == attributes && other.returnType == returnType 155 | && other.name == name && other.formals == formals 156 | } 157 | 158 | // Prototype Boilerplate 159 | 160 | val attributes: List 161 | get() = prototype.attributes 162 | 163 | var returnType: Type 164 | get() = prototype.returnType 165 | set(value) { 166 | prototype.returnType = value 167 | } 168 | 169 | var name: String 170 | get() = prototype.name 171 | set(value) { 172 | prototype.name = value 173 | } 174 | 175 | var formals: List 176 | get() = prototype.formals 177 | set(value) { 178 | prototype.formals = value 179 | } 180 | 181 | } 182 | 183 | /** 184 | * Formals are the arguments for Functions. 185 | */ 186 | class Formal(type: Type, name: String, context: ParserRuleContext) : Variable(type, name, null, true, context) 187 | 188 | /** 189 | * Variables have a type, name, and can have an initial expression. 190 | * 191 | * ``` 192 | * let a = 7 193 | * ``` 194 | * 195 | * The type will be inferred to 'int' and the initial expression will be an IntegerLiteral(7). 196 | * 197 | * If 'constant' is true, the value of this variable can't be changed. 198 | */ 199 | open class Variable(val reference: Reference, var initialExpression: Expression? = null, 200 | val constant: Boolean = false, val context: ParserRuleContext) : Global { 201 | 202 | constructor(type: Type, name: String, initialExpression: Expression? = null, constant: Boolean = false, 203 | context: ParserRuleContext) : this(Reference(name, type), initialExpression, constant, context) 204 | 205 | val name: String 206 | get() = reference.name 207 | 208 | var type: Type 209 | get() = reference.type 210 | set(value) { 211 | reference.type = value 212 | } 213 | 214 | override fun toString(): String = "${if (constant) "let" else "var"} $name : $type${if (initialExpression != null) " = $initialExpression" else ""}" 215 | 216 | override fun hashCode(): Int = type.hashCode() + name.hashCode() + constant.hashCode() 217 | override fun equals(other: Any?): Boolean = 218 | other is Variable && other.name == name && other.type == type && other.constant == constant && other.hashCode() == hashCode() 219 | } 220 | 221 | /** 222 | * Classes (Clazz because Java contains a class named Class) are normal OOP classes, and can contain fields and methods. 223 | */ 224 | class Clazz(val name: String, val fields: List, val methods: List, val constructor: Function?, 225 | val traits: List, val context: ParserRuleContext) : Global, Type { 226 | 227 | override fun toString(): String = "$name(${fields.map { it.type }.joinToString()})" 228 | 229 | override fun hashCode(): Int = Objects.hash(name, fields, methods, constructor) 230 | 231 | override fun equals(other: Any?): Boolean = 232 | other is Clazz && other.hashCode() == hashCode() && other.name == name 233 | && other.fields == fields && other.methods == methods && other.constructor == constructor 234 | 235 | override fun isCopyable(): Boolean = traits.filter { it.name == "Copy" }.isNotEmpty() 236 | } 237 | 238 | open class Trait(val name: String, val functions: List = listOf()) : Node, Type { 239 | override fun toString(): String = name 240 | override fun isCopyable(): Boolean = name == "Copy" // This seems ghetto 241 | } 242 | 243 | object CopyTrait : Trait("Copy", listOf()) 244 | 245 | val defaultTraits: List = listOf(CopyTrait) 246 | 247 | /** 248 | * A Reference to a declaration. 249 | */ 250 | class Reference(val name: String, var type: Type = T_UNDEF) { 251 | override fun toString(): String = name 252 | override fun hashCode(): Int = Objects.hash(name, type) 253 | override fun equals(other: Any?): Boolean = name == other 254 | } 255 | 256 | /** 257 | * Stores the references and arguments for calling a Function. 258 | */ 259 | class FunctionCall(val functionReference: Reference, val arguments: List = listOf()) : Node { 260 | override fun toString(): String = 261 | functionReference.name + "(" + arguments.map { it.toString() }.joinToString() + ")" 262 | 263 | override fun hashCode(): Int = Objects.hash(functionReference, arguments) 264 | override fun equals(other: Any?): Boolean = 265 | other is FunctionCall && other.functionReference == functionReference && other.arguments == arguments 266 | } 267 | 268 | /** 269 | * Stores the references and arguments for a calling a Method. 270 | */ 271 | class MethodCall(val variableReference: Reference, val methodReference: Reference, val arguments: List = listOf()) : Node { 272 | override fun toString(): String = 273 | variableReference.name + "." + methodReference.name + "(" + arguments.map { it.toString() }.joinToString() + ")" 274 | 275 | override fun hashCode(): Int = Objects.hash(variableReference, methodReference, arguments) 276 | override fun equals(other: Any?): Boolean = 277 | other is MethodCall && other.variableReference == variableReference 278 | && other.methodReference == methodReference && other.arguments == arguments 279 | } 280 | 281 | // --------------------------------------------------------------------------------------------------------------------- 282 | // STATEMENTS 283 | // --------------------------------------------------------------------------------------------------------------------- 284 | 285 | /** 286 | * Statements are commands that Functions can run to do certain actions. These actions consist of function calls, 287 | * variable declarations, control flow, etc. 288 | */ 289 | abstract class Statement : Node { 290 | override fun equals(other: Any?): Boolean = other is Statement && other.hashCode() == hashCode() 291 | } 292 | 293 | /** 294 | * Blocks are Statements that contain Statements than can be run. 295 | */ 296 | open class Block(val statements: List) : Statement() { 297 | override fun hashCode(): Int = Objects.hash(statements) 298 | override fun equals(other: Any?): Boolean = 299 | other is Block && other.statements == statements && other.hashCode() == hashCode() 300 | } 301 | 302 | /** 303 | * CheckedBlocks are Blocks with an expression to be evaluated before the Statements run. 304 | * 305 | *
306 |  *     (expr) {
307 |  *          statements;
308 |  *     }
309 |  * 
310 | * 311 | * @param expression Expression to test 312 | * @param statements Statements to be run 313 | */ 314 | open class CheckedBlock(var expression: Expression, statements: List) : Block(statements) 315 | 316 | /** 317 | * If Statement 318 | * The statement list runs if the expression evaluates to true. 319 | * If the expression is false, control goes to elseStatement. This statement is an If Statement that has its own 320 | * expression to evaluate. There is no notion of an "Else Statement", they are If Statements with the expression set to 321 | * true. 322 | * 323 | *
324 |  *     if (eA) {
325 |  *         sA
326 |  *     }
327 |  *     else if (eB) {
328 |  *         sB
329 |  *     }
330 |  *     else if (eC) {
331 |  *         sC
332 |  *     }
333 |  *     else {
334 |  *         sD
335 |  *     }
336 |  * 
337 | * 338 | * This is represented in the tree as: 339 | * 340 | *
341 |  *     IfStatement
342 |  *     - eA
343 |  *     - sA
344 |  *     - IfStatement
345 |  *       - eB
346 |  *       - sB
347 |  *       - IfStatement
348 |  *         - eC
349 |  *         - sC
350 |  *         - IfStatement
351 |  *           - true
352 |  *           - sD
353 |  * 
354 | */ 355 | class IfStatement(expression: Expression, statements: List, var elseStatement: IfStatement?, 356 | val context: ParserRuleContext) : CheckedBlock(expression, statements) 357 | 358 | /** 359 | * elseStatement returns an IfStatement with the expression set to a TrueExpression 360 | * @param statements Statements to run 361 | */ 362 | fun elseStatement(statements: List, context: ParserRuleContext): IfStatement { 363 | return IfStatement(TrueExpression(context), statements, null, context) 364 | } 365 | 366 | /** 367 | * WhileStatements are Blocks that will run over and over as long as their expression is true. 368 | */ 369 | class WhileStatement(expression: Expression, statements: List, val context: ParserRuleContext) : CheckedBlock(expression, statements) 370 | 371 | /** 372 | * VariableDeclarationStatements add a Variable to the local variable pool that other Statements can access. 373 | */ 374 | class VariableDeclarationStatement(val variable: Variable, val context: ParserRuleContext) : Statement() 375 | 376 | /** 377 | * 378 | */ 379 | class VariableReassignmentStatement(val reference: Reference, var expression: Expression, val context: ParserRuleContext) : Statement() { 380 | override fun toString(): String = "$reference = $expression (${reference.hashCode()}, ${expression.hashCode()})" 381 | override fun hashCode(): Int = Objects.hash(reference, expression) 382 | override fun equals(other: Any?): Boolean = 383 | other is VariableReassignmentStatement && other.reference == reference && other.expression == expression 384 | } 385 | 386 | /** 387 | * Statement wrapper for FunctionCalls 388 | */ 389 | class FunctionCallStatement(val functionCall: FunctionCall, val context: ParserRuleContext) : Statement() 390 | 391 | /** 392 | * Statement wrapper for MethodCalls 393 | */ 394 | class MethodCallStatement(val methodCall: MethodCall, val context: ParserRuleContext) : Statement() { 395 | override fun toString(): String = methodCall.toString() 396 | } 397 | 398 | /** 399 | * Set field of a Class 400 | */ 401 | class FieldSetterStatement(val variable: Expression, val fieldReference: Reference, val expression: Expression, val context: ParserRuleContext) : Statement() { 402 | override fun toString(): String = "$variable.$fieldReference = $expression" 403 | } 404 | 405 | // --------------------------------------------------------------------------------------------------------------------- 406 | // EXPRESSIONS 407 | // --------------------------------------------------------------------------------------------------------------------- 408 | 409 | /** 410 | * Expressions evaluate to a value. 411 | */ 412 | abstract class Expression(val child: List = listOf()) : Node 413 | 414 | abstract class BooleanExpression(val value: Boolean, val context: ParserRuleContext) : Expression() 415 | 416 | /** 417 | * TrueExpressions are booleans that are only "true". 418 | */ 419 | class TrueExpression(context: ParserRuleContext) : BooleanExpression(true, context) { 420 | override fun toString(): String = "true" 421 | } 422 | 423 | /** 424 | * FalseExpressions are booleans that are only "false". 425 | */ 426 | class FalseExpression(context: ParserRuleContext) : BooleanExpression(false, context) { 427 | override fun toString(): String = "false" 428 | } 429 | 430 | /** 431 | * IntegerLiterals are an Expression wrapper for Ints. 432 | */ 433 | class IntegerLiteral(val value: Int, val context: ParserRuleContext) : Expression() { 434 | override fun toString(): String = value.toString() 435 | } 436 | 437 | class FloatLiteral(val value: Double, val type: FloatType, val context: ParserRuleContext) : Expression() { 438 | override fun toString(): String = value.toString() 439 | } 440 | 441 | /** 442 | * StringLiterals are an Expression wrapper for Strings. 443 | */ 444 | class StringLiteral(val value: String, val context: ParserRuleContext) : Expression() { 445 | override fun toString(): String = value 446 | } 447 | 448 | /** 449 | * Expression wrapper for References 450 | */ 451 | class ReferenceExpression(val reference: Reference, val context: ParserRuleContext) : Expression() { 452 | override fun toString(): String = reference.name 453 | override fun hashCode(): Int = reference.hashCode() 454 | override fun equals(other: Any?): Boolean = reference == other 455 | } 456 | 457 | /** 458 | * Expression wrapper for FunctionCalls 459 | */ 460 | class FunctionCallExpression(val functionCall: FunctionCall, val context: ParserRuleContext) : Expression(functionCall.arguments) { 461 | override fun toString(): String = functionCall.toString() 462 | } 463 | 464 | /** 465 | * Expression wrapper for MethodCalls 466 | */ 467 | class MethodCallExpression(val methodCall: MethodCall, val context: ParserRuleContext) : Expression(methodCall.arguments) { 468 | override fun toString(): String = methodCall.toString() 469 | } 470 | 471 | /** 472 | * Get field of a Class 473 | */ 474 | class FieldGetterExpression(val variable: Expression, val fieldReference: Reference, val context: ParserRuleContext) : Expression() { 475 | override fun toString(): String = "$variable.$fieldReference" 476 | } 477 | 478 | class ClazzInitializerExpression(val classReference: Reference, val arguments: List, val context: ParserRuleContext) : Expression(arguments) { 479 | override fun toString(): String = 480 | "new ${classReference.name}(" + arguments.map { it.toString() }.joinToString() + ")" 481 | } 482 | 483 | /** 484 | * Operators are constructs that behave like Functions, but differ syntactically. 485 | */ 486 | enum class Operator(val string: String, val aType: Type = T_UNDEF, val bType: Type = T_UNDEF, val returnType: Type = T_UNDEF) { 487 | // Maths 488 | PLUS_INT("+", aType = T_INT32, bType = T_INT32, returnType = T_INT32), 489 | MINUS_INT("-", aType = T_INT32, bType = T_INT32, returnType = T_INT32), 490 | MULTIPLY_INT("*", aType = T_INT32, bType = T_INT32, returnType = T_INT32), 491 | DIVIDE_INT("/", aType = T_INT32, bType = T_INT32, returnType = T_INT32), 492 | 493 | PLUS_FLOAT("+.", aType = T_FLOAT32, bType = T_FLOAT32, returnType = T_FLOAT32), 494 | MINUS_FLOAT("-.", aType = T_FLOAT32, bType = T_FLOAT32, returnType = T_FLOAT32), 495 | MULTIPLY_FLOAT("*.", aType = T_FLOAT32, bType = T_FLOAT32, returnType = T_FLOAT32), 496 | DIVIDE_FLOAT("/.", aType = T_FLOAT32, bType = T_FLOAT32, returnType = T_FLOAT32), 497 | 498 | // Comparisons 499 | GREATER_THAN(">", returnType = T_BOOL), 500 | LESS_THAN("<", returnType = T_BOOL), 501 | EQUALS("==", returnType = T_BOOL), 502 | GREATER_THAN_EQUAL(">=", returnType = T_BOOL), 503 | LESS_THAN_EQUAL("<=", returnType = T_BOOL), 504 | NOT_EQUAL("!=", returnType = T_BOOL), 505 | AND("&&", returnType = T_BOOL), 506 | OR("||", returnType = T_BOOL); 507 | 508 | override fun toString(): String = string 509 | } 510 | 511 | /** 512 | * Get an Operator through its String representation 513 | */ 514 | fun getOperator(s: String): Operator? { 515 | try { 516 | return Operator.values().filter { it.string == s }[0] 517 | } catch (e: IndexOutOfBoundsException) { 518 | return null 519 | } 520 | } 521 | 522 | /** 523 | * BinaryOperators are Expressions that contain two sub-Expressions and an Operator that operates on them. 524 | */ 525 | class BinaryOperator(var expressionA: Expression, val operator: Operator, var expressionB: Expression, val context: ParserRuleContext) : Expression(listOf(expressionA, expressionB)) { 526 | override fun toString(): String = "($expressionA $operator $expressionB)" 527 | override fun hashCode(): Int = Objects.hash(expressionA, operator, expressionB) 528 | override fun equals(other: Any?): Boolean { 529 | return other is BinaryOperator && other.expressionA == expressionA && other.operator == operator && other.expressionB == expressionB 530 | } 531 | } 532 | -------------------------------------------------------------------------------- /src/main/kotlin/xyz/jadonfowler/compiler/ast/type.kt: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.compiler.ast 2 | 3 | interface Type { 4 | fun isCopyable(): Boolean 5 | } 6 | 7 | interface NumericType : Type { 8 | override fun isCopyable(): Boolean = true 9 | } 10 | 11 | interface IntType : NumericType { 12 | fun getBits(): Int 13 | } 14 | 15 | interface FloatType : NumericType { 16 | fun getBits(): Int 17 | } 18 | 19 | private fun makeType(name: String, copyable: Boolean): Type { 20 | return object : Type { 21 | override fun toString(): String = name 22 | override fun isCopyable(): Boolean = copyable 23 | } 24 | } 25 | 26 | private fun makeIntType(bits: Int): IntType { 27 | return object : IntType { 28 | override fun toString(): String = "Int$bits" 29 | override fun getBits() = bits 30 | } 31 | } 32 | 33 | private fun makeFloatType(bits: Int): FloatType { 34 | return object : FloatType { 35 | override fun toString(): String = "Float$bits" 36 | override fun getBits(): Int = bits 37 | } 38 | } 39 | 40 | /** 41 | * This isn't like JavaScript's "undefined". 42 | * This type is used internally to tag objects that don't have a type *yet*. 43 | */ 44 | object T_UNDEF : Type { 45 | override fun toString(): String = "Undefined" 46 | override fun isCopyable(): Boolean = false 47 | } 48 | 49 | /** 50 | * Void is only used on Functions. 51 | */ 52 | val T_VOID = makeType("Void", false) 53 | 54 | val T_INT8 = makeIntType(8) 55 | val T_INT16 = makeIntType(16) 56 | val T_INT32 = makeIntType(32) 57 | val T_INT64 = makeIntType(64) 58 | val T_INT128 = makeIntType(128) 59 | 60 | val T_FLOAT32 = makeFloatType(32) 61 | val T_FLOAT64 = makeFloatType(64) 62 | val T_FLOAT128 = makeFloatType(128) 63 | 64 | val T_BOOL = makeType("Bool", true) 65 | 66 | val T_STRING = makeType("String", false) 67 | 68 | fun getType(name: String, classes: List): Type { 69 | return when (name) { 70 | "Int8" -> T_INT8 71 | "Int16" -> T_INT16 72 | "Int" -> T_INT32 73 | "Int32" -> T_INT32 74 | "Int64" -> T_INT64 75 | "Int128" -> T_INT128 76 | "Float" -> T_FLOAT32 77 | "Float32" -> T_FLOAT32 78 | "Float64" -> T_FLOAT64 79 | "Float128" -> T_FLOAT128 80 | "Bool" -> T_BOOL 81 | "String" -> T_STRING 82 | "Void" -> T_VOID 83 | else -> { 84 | val possibleClasses = classes.filter { it.name == name } 85 | if (possibleClasses.isNotEmpty()) 86 | possibleClasses.last() 87 | else 88 | T_UNDEF 89 | } 90 | } 91 | } 92 | 93 | fun getBiggestInt(a: IntType, b: IntType): IntType { 94 | return if (a.getBits() > b.getBits()) a else b 95 | } 96 | 97 | fun getBiggestFloat(a: FloatType, b: FloatType): FloatType { 98 | return if (a.getBits() > b.getBits()) a else b 99 | } 100 | -------------------------------------------------------------------------------- /src/main/kotlin/xyz/jadonfowler/compiler/backend/Backend.kt: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.compiler.backend 2 | 3 | import xyz.jadonfowler.compiler.ast.Module 4 | import xyz.jadonfowler.compiler.pass.Pass 5 | import java.io.File 6 | 7 | abstract class Backend(module: Module) : Pass(module) { 8 | abstract fun output(file: File?) 9 | } 10 | -------------------------------------------------------------------------------- /src/main/kotlin/xyz/jadonfowler/compiler/backend/JVMBackend.kt: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.compiler.backend 2 | 3 | import org.objectweb.asm.ClassWriter 4 | // import org.objectweb.asm.MethodVisitor 5 | import org.objectweb.asm.Opcodes.* 6 | import xyz.jadonfowler.compiler.ast.* 7 | import xyz.jadonfowler.compiler.ast.Function 8 | import java.io.File 9 | 10 | class JVMBackend(module: Module) : Backend(module) { 11 | 12 | val cw = ClassWriter(ClassWriter.COMPUTE_MAXS or ClassWriter.COMPUTE_FRAMES) 13 | 14 | init { 15 | cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER + ACC_FINAL, module.name, null, "java/lang/Object", null) 16 | 17 | val localVariables: MutableMap = mutableMapOf() 18 | 19 | module.globalVariables.forEach { 20 | val value = getValue(it.initialExpression, localVariables) 21 | localVariables.put(it.name, value) 22 | val fv = cw.visitField(ACC_PUBLIC + ACC_STATIC + ACC_FINAL, it.name, getJVMType(it.type), null, value) 23 | fv.visitEnd() 24 | } 25 | val mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null) 26 | mv.visitCode() 27 | mv.visitInsn(RETURN) 28 | mv.visitEnd() 29 | 30 | module.globalFunctions.forEach { visit(it) } 31 | module.globalClasses.forEach { visit(it) } 32 | } 33 | 34 | override fun output(file: File?) { 35 | cw.visitEnd() 36 | } 37 | 38 | fun getValue(expression: Expression?, localVariables: MutableMap): Any? { 39 | return if (expression == null) null 40 | else when (expression) { 41 | is IntegerLiteral -> expression.value 42 | is StringLiteral -> expression.value 43 | is BooleanExpression -> if (expression.value) 1 else 0 44 | is ReferenceExpression -> localVariables[expression.reference.name] 45 | else -> null 46 | } 47 | } 48 | 49 | override fun visit(function: Function) { 50 | // val mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, function.name, getJVMType(function), null, null) 51 | } 52 | 53 | override fun visit(clazz: Clazz) { 54 | // cw.visitInnerClass() 55 | } 56 | 57 | companion object { 58 | 59 | fun getJVMType(type: Type): String { 60 | return when (type) { 61 | T_VOID -> "V" 62 | xyz.jadonfowler.compiler.ast.T_INT32 -> "I" 63 | T_BOOL -> "Z" 64 | T_STRING -> "Ljava/lang/String;" 65 | is Function -> { 66 | "(" + type.formals.map { getJVMType(it.type) }.joinToString("") + ")" + getJVMType(type.returnType) 67 | } 68 | else -> "Ljava/lang/Object;" 69 | } 70 | } 71 | 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/kotlin/xyz/jadonfowler/compiler/pass/ConstantFoldPass.kt: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.compiler.pass 2 | 3 | import xyz.jadonfowler.compiler.ast.* 4 | import xyz.jadonfowler.compiler.ast.Function 5 | 6 | class ConstantFoldPass(module: Module) : Pass(module) { 7 | 8 | fun foldIntegerLiterals(originalExpression: Expression, a: IntegerLiteral, b: IntegerLiteral, operator: Operator): Expression { 9 | return when (operator) { 10 | Operator.PLUS_INT -> IntegerLiteral(a.value + b.value, a.context) 11 | Operator.MINUS_INT -> IntegerLiteral(a.value - b.value, a.context) 12 | Operator.MULTIPLY_INT -> IntegerLiteral(a.value * b.value, a.context) 13 | Operator.DIVIDE_INT -> IntegerLiteral(a.value / b.value, a.context) 14 | else -> originalExpression 15 | } 16 | } 17 | 18 | fun foldExpression(expression: Expression): Expression { 19 | return when (expression) { 20 | is BinaryOperator -> { 21 | val a = foldExpression(expression.expressionA) 22 | expression.expressionA = a 23 | val b = foldExpression(expression.expressionB) 24 | expression.expressionB = b 25 | var newExpression = expression 26 | if (a is IntegerLiteral && b is IntegerLiteral) 27 | newExpression = foldIntegerLiterals(expression, a, b, expression.operator) 28 | else if (a is BinaryOperator && b is IntegerLiteral) { 29 | val aa = a.expressionA 30 | val ab = a.expressionB 31 | if (a.operator == expression.operator) { 32 | if (aa is IntegerLiteral) 33 | newExpression = BinaryOperator(foldIntegerLiterals(a, aa, b, expression.operator), a.operator, ab, aa.context) 34 | else if (ab is IntegerLiteral) 35 | newExpression = BinaryOperator(aa, a.operator, foldIntegerLiterals(a, ab, b, expression.operator), ab.context) 36 | } 37 | } else if (a is IntegerLiteral && b is BinaryOperator) { 38 | val ba = b.expressionA 39 | val bb = b.expressionB 40 | if (b.operator == expression.operator) { 41 | if (ba is IntegerLiteral) 42 | newExpression = BinaryOperator(foldIntegerLiterals(b, ba, a, expression.operator), b.operator, bb, ba.context) 43 | else if (bb is IntegerLiteral) 44 | newExpression = BinaryOperator(ba, b.operator, foldIntegerLiterals(b, bb, a, expression.operator), bb.context) 45 | } 46 | } 47 | newExpression 48 | } 49 | else -> expression 50 | } 51 | } 52 | 53 | init { 54 | module.globalVariables.forEach { visit(it) } 55 | module.globalFunctions.forEach { visit(it) } 56 | module.globalClasses.forEach { visit(it) } 57 | } 58 | 59 | override fun visit(clazz: Clazz) { 60 | clazz.fields.forEach { visit(it) } 61 | clazz.methods.forEach { visit(it) } 62 | } 63 | 64 | override fun visit(variable: Variable) { 65 | if (variable.initialExpression != null) 66 | variable.initialExpression = foldExpression(variable.initialExpression!!) 67 | } 68 | 69 | override fun visit(statement: Statement) { 70 | when (statement) { 71 | is VariableDeclarationStatement -> visit(statement.variable) 72 | is VariableReassignmentStatement -> statement.expression = foldExpression(statement.expression) 73 | is IfStatement -> { 74 | statement.expression = foldExpression(statement.expression) 75 | statement.statements.forEach { visit(it) } 76 | if (statement.elseStatement != null) visit(statement.elseStatement!!) 77 | } 78 | is WhileStatement -> { 79 | statement.expression = foldExpression(statement.expression) 80 | statement.statements.forEach { visit(it) } 81 | } 82 | } 83 | } 84 | 85 | override fun visit(function: Function) { 86 | function.statements.forEach { visit(it) } 87 | 88 | if (function.expression != null) 89 | function.expression = foldExpression(function.expression!!) 90 | } 91 | 92 | } -------------------------------------------------------------------------------- /src/main/kotlin/xyz/jadonfowler/compiler/pass/Pass.kt: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.compiler.pass 2 | 3 | import org.antlr.v4.runtime.ParserRuleContext 4 | import xyz.jadonfowler.compiler.ast.* 5 | import xyz.jadonfowler.compiler.ast.Function 6 | import xyz.jadonfowler.compiler.visitor.Visitor 7 | 8 | open class Pass(module: Module) : Visitor(module) { 9 | 10 | fun reportError(problem: String, context: ParserRuleContext) { 11 | val line = context.start.line - 1 12 | val lines = module.source.split("\n") 13 | 14 | val before = if (line > 0) "$line ${lines[line - 1]}\n" else "" 15 | val errorLine = "${line + 1} ${lines[line]}\n" 16 | val after = if (line + 1 < lines.size) "${line + 2} ${lines[line + 1]}\n" else "" 17 | 18 | val column = context.start.charPositionInLine 19 | val arrow = " ".repeat(line.toString().length + 1) + "~".repeat(column) + "^" 20 | module.errors.add("$before$errorLine$arrow\n$after$problem\n") 21 | } 22 | 23 | override fun visit(variable: Variable) { 24 | } 25 | 26 | override fun visit(variableDeclarationStatement: VariableDeclarationStatement) { 27 | } 28 | 29 | override fun visit(variableReassignmentStatement: VariableReassignmentStatement) { 30 | } 31 | 32 | override fun visit(binaryOperator: BinaryOperator) { 33 | } 34 | 35 | override fun visit(block: Block) { 36 | } 37 | 38 | override fun visit(clazz: Clazz) { 39 | } 40 | 41 | override fun visit(falseExpression: FalseExpression) { 42 | } 43 | 44 | override fun visit(formal: Formal) { 45 | } 46 | 47 | override fun visit(function: Function) { 48 | } 49 | 50 | override fun visit(functionCallExpression: FunctionCallExpression) { 51 | } 52 | 53 | override fun visit(functionCallStatement: FunctionCallStatement) { 54 | } 55 | 56 | override fun visit(methodCallStatement: MethodCallStatement) { 57 | } 58 | 59 | override fun visit(methodCallExpression: MethodCallExpression) { 60 | } 61 | 62 | override fun visit(fieldGetterExpression: FieldGetterExpression) { 63 | } 64 | 65 | override fun visit(fieldSetterStatement: FieldSetterStatement) { 66 | } 67 | 68 | override fun visit(ifStatement: IfStatement) { 69 | } 70 | 71 | override fun visit(integerLiteral: IntegerLiteral) { 72 | } 73 | 74 | override fun visit(floatLiteral: FloatLiteral) { 75 | } 76 | 77 | override fun visit(referenceExpression: ReferenceExpression) { 78 | } 79 | 80 | override fun visit(stringLiteral: StringLiteral) { 81 | } 82 | 83 | override fun visit(trueExpression: TrueExpression) { 84 | } 85 | 86 | override fun visit(whileStatement: WhileStatement) { 87 | } 88 | 89 | override fun visit(clazzInitializerExpression: ClazzInitializerExpression) { 90 | } 91 | } -------------------------------------------------------------------------------- /src/main/kotlin/xyz/jadonfowler/compiler/pass/PrintPass.kt: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.compiler.pass 2 | 3 | import xyz.jadonfowler.compiler.ast.* 4 | import xyz.jadonfowler.compiler.ast.Function 5 | 6 | /** 7 | * This pass is used for debugging the AST. 8 | */ 9 | class PrintPass(module: Module) : Pass(module) { 10 | 11 | val tab = " " 12 | var tabIndent = 0 13 | 14 | var output = "" 15 | 16 | fun printI(any: Any) { 17 | output += any.toString() 18 | } 19 | 20 | fun print(any: Any) { 21 | output += tab.repeat(tabIndent) + any.toString() 22 | } 23 | 24 | fun println(any: Any) { 25 | output += tab.repeat(tabIndent) + any.toString() + "\n" 26 | } 27 | 28 | fun println() { 29 | println("") 30 | } 31 | 32 | init { 33 | println("; Module: ${module.name}") 34 | module.globalVariables.map { visit(it) } 35 | println() 36 | module.globalFunctions.map { visit(it) } 37 | println() 38 | module.globalClasses.map { visit(it) } 39 | } 40 | 41 | override fun visit(function: Function) { 42 | println("; ${function.name} has ${function.statements.size} statements${if (function.expression != null) " and a return expression" else ""}.") 43 | print("function ${function.name} (") 44 | function.formals.map { visit(it); if (function.formals.last() != it) print(", ") } 45 | printI(") -> ${function.returnType} {\n") 46 | 47 | tabIndent++ 48 | function.statements.map { println("; ${it.javaClass.simpleName}"); visit(it) } 49 | if (function.expression != null) { 50 | print("return ") 51 | visit(function.expression!!) 52 | println() 53 | } 54 | tabIndent-- 55 | 56 | println("}\n") 57 | } 58 | 59 | override fun visit(formal: Formal) { 60 | printI("${formal.name}: ${formal.type}") 61 | } 62 | 63 | override fun visit(variable: Variable) { 64 | print("${if (variable.constant) "constant" else "variable"} ${variable.name}: ${variable.type}") 65 | if (variable.initialExpression != null) { 66 | printI(" = ") 67 | visit(variable.initialExpression!!) 68 | } 69 | printI("\n") 70 | } 71 | 72 | override fun visit(clazz: Clazz) { 73 | println("class ${clazz.name} {") 74 | tabIndent++ 75 | clazz.fields.map { visit(it) } 76 | println() 77 | if (clazz.constructor != null) 78 | visit(clazz.constructor) 79 | clazz.methods.map { visit(it) } 80 | tabIndent-- 81 | println("}") 82 | } 83 | 84 | override fun visit(block: Block) { 85 | println("{") 86 | tabIndent++ 87 | block.statements.map { println("; ${it.javaClass.simpleName}"); visit(it) } 88 | tabIndent-- 89 | println("}") 90 | } 91 | 92 | override fun visit(ifStatement: IfStatement) { 93 | print("if ") 94 | visit(ifStatement.expression) 95 | printI(" {\n") 96 | tabIndent++ 97 | ifStatement.statements.map { println("; ${it.javaClass.simpleName}"); visit(it) } 98 | tabIndent-- 99 | println("}") 100 | 101 | var child: IfStatement? = ifStatement.elseStatement 102 | while (child != null) { 103 | print("else if (") 104 | visit(child.expression) 105 | printI(") {\n") 106 | tabIndent++ 107 | child.statements.map { println("; ${it.javaClass.simpleName}"); visit(it) } 108 | tabIndent-- 109 | println("}") 110 | child = child.elseStatement 111 | } 112 | } 113 | 114 | override fun visit(whileStatement: WhileStatement) { 115 | print("while ") 116 | visit(whileStatement.expression) 117 | printI(" {\n") 118 | tabIndent++ 119 | whileStatement.statements.map { println("; ${it.javaClass.simpleName}"); visit(it) } 120 | tabIndent-- 121 | println("}") 122 | } 123 | 124 | override fun visit(variableDeclarationStatement: VariableDeclarationStatement) { 125 | visit(variableDeclarationStatement.variable) 126 | } 127 | 128 | override fun visit(variableReassignmentStatement: VariableReassignmentStatement) { 129 | print("${variableReassignmentStatement.reference.name} = ") 130 | visit(variableReassignmentStatement.expression) 131 | printI("\n") 132 | } 133 | 134 | override fun visit(functionCallStatement: FunctionCallStatement) { 135 | print("${functionCallStatement.functionCall.functionReference.name}(") 136 | functionCallStatement.functionCall.arguments.map { visit(it); printI(", ") } 137 | printI(")\n") 138 | } 139 | 140 | override fun visit(trueExpression: TrueExpression) { 141 | printI("true") 142 | } 143 | 144 | override fun visit(falseExpression: FalseExpression) { 145 | printI("false") 146 | } 147 | 148 | override fun visit(fieldGetterExpression: FieldGetterExpression) { 149 | visit(fieldGetterExpression.variable) 150 | printI("." + fieldGetterExpression.fieldReference.name) 151 | } 152 | 153 | override fun visit(fieldSetterStatement: FieldSetterStatement) { 154 | print("") 155 | visit(fieldSetterStatement.variable) 156 | printI("." + fieldSetterStatement.fieldReference.name + " = ") 157 | visit(fieldSetterStatement.expression) 158 | println() 159 | } 160 | 161 | override fun visit(methodCallExpression: MethodCallExpression) { 162 | printI(methodCallExpression.methodCall.variableReference.name + "." 163 | + methodCallExpression.methodCall.methodReference.name + "(") 164 | methodCallExpression.methodCall.arguments.forEach { visit(it); printI(", ") } 165 | printI(")") 166 | } 167 | 168 | override fun visit(methodCallStatement: MethodCallStatement) { 169 | print(methodCallStatement.methodCall.variableReference.name + "." 170 | + methodCallStatement.methodCall.methodReference.name + "(") 171 | methodCallStatement.methodCall.arguments.forEach { visit(it); printI(", ") } 172 | printI(")\n") 173 | } 174 | 175 | override fun visit(integerLiteral: IntegerLiteral) { 176 | printI(integerLiteral.value) 177 | } 178 | 179 | override fun visit(floatLiteral: FloatLiteral) { 180 | printI(floatLiteral.value) 181 | } 182 | 183 | override fun visit(stringLiteral: StringLiteral) { 184 | printI("\"${stringLiteral.value}\"") 185 | } 186 | 187 | override fun visit(referenceExpression: ReferenceExpression) { 188 | printI(referenceExpression.reference.name) 189 | } 190 | 191 | override fun visit(functionCallExpression: FunctionCallExpression) { 192 | printI("${functionCallExpression.functionCall.functionReference.name}(") 193 | functionCallExpression.functionCall.arguments.forEach { visit(it); printI(", ") } 194 | printI(")") 195 | } 196 | 197 | override fun visit(clazzInitializerExpression: ClazzInitializerExpression) { 198 | printI("new ${clazzInitializerExpression.classReference.name}(") 199 | clazzInitializerExpression.arguments.forEach { visit(it); printI(", ") } 200 | printI(")") 201 | } 202 | 203 | override fun visit(binaryOperator: BinaryOperator) { 204 | printI("(") 205 | visit(binaryOperator.expressionA) 206 | printI(" ${binaryOperator.operator.string} ") 207 | visit(binaryOperator.expressionB) 208 | printI(")") 209 | } 210 | 211 | } 212 | -------------------------------------------------------------------------------- /src/main/kotlin/xyz/jadonfowler/compiler/pass/SemanticAnalysis.kt: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.compiler.pass 2 | 3 | import xyz.jadonfowler.compiler.ast.* 4 | import xyz.jadonfowler.compiler.ast.Function 5 | 6 | class SemanticAnalysis(module: Module) : Pass(module) { 7 | 8 | fun getReferences(expression: Expression): List { 9 | val list = mutableListOf() 10 | when (expression) { 11 | is ReferenceExpression -> { 12 | if (!expression.reference.type.isCopyable()) { 13 | list.add(expression.reference.name) 14 | } 15 | } 16 | is BinaryOperator -> { 17 | list.addAll(getReferences(expression.expressionA)) 18 | list.addAll(getReferences(expression.expressionB)) 19 | } 20 | is FieldGetterExpression -> { 21 | list.addAll(getReferences(expression.variable)) 22 | } 23 | } 24 | return list 25 | } 26 | 27 | init { 28 | module.globalVariables.forEach { visit(it, true) } 29 | module.globalFunctions.forEach { visit(it) } 30 | module.globalClasses.forEach { visit(it) } 31 | } 32 | 33 | override fun visit(variable: Variable) { 34 | visit(variable, false) 35 | } 36 | 37 | fun visit(variable: Variable, global: Boolean) { 38 | if (global) { 39 | if (!variable.constant) 40 | reportError("Global variable '${variable.name}' needs to be a constant.", variable.context) 41 | 42 | if (variable.initialExpression == null) 43 | reportError("Global variable '${variable.name}' needs to be assigned an expression.", variable.context) 44 | } 45 | } 46 | 47 | override fun visit(function: Function) { 48 | val unusedReferences = mutableListOf() 49 | val usedReferences = mutableListOf() 50 | 51 | function.statements.forEach { 52 | when (it) { 53 | is VariableDeclarationStatement -> { 54 | val context = it.context 55 | it.variable.initialExpression?.let { 56 | val refs = getReferences(it) 57 | refs.forEach { 58 | if (usedReferences.contains(it)) { 59 | reportError("Reference to '$it' has already been used.", context) 60 | } else { 61 | if (unusedReferences.contains(it)) 62 | unusedReferences.remove(it) 63 | usedReferences.add(it) 64 | } 65 | } 66 | } 67 | unusedReferences.add(it.variable.name) 68 | } 69 | else -> visit(it) 70 | } 71 | } 72 | } 73 | 74 | override fun visit(fieldSetterStatement: FieldSetterStatement) { 75 | super.visit(fieldSetterStatement) 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/kotlin/xyz/jadonfowler/compiler/pass/TypePass.kt: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.compiler.pass 2 | 3 | import xyz.jadonfowler.compiler.ast.* 4 | import xyz.jadonfowler.compiler.ast.Function 5 | 6 | class TypePass(module: Module) : Pass(module) { 7 | 8 | /** 9 | * Get an Expression's Type 10 | */ 11 | fun getType(expression: Expression, localVariables: MutableMap?): Type { 12 | return when (expression) { 13 | is IntegerLiteral -> T_INT32 14 | is FloatLiteral -> expression.type 15 | is BinaryOperator -> { 16 | val returnType = expression.operator.returnType 17 | 18 | val expA = expression.expressionA 19 | val expB = expression.expressionB 20 | 21 | val checkType = { expA: Expression, expB: Expression, expectedTypeOfA: Type, expectedTypeOfB: Type -> 22 | if (expectedTypeOfA != T_UNDEF) { 23 | if (expA is ReferenceExpression) { 24 | val node = module.getNodeFromReference(expA.reference, localVariables) 25 | when (node) { 26 | is Variable -> { 27 | if (node.type == T_UNDEF) { 28 | node.type = expectedTypeOfA 29 | expA.reference.type = expectedTypeOfA 30 | } else if (node.type != expectedTypeOfA && (node.type !is NumericType && expectedTypeOfA !is NumericType)) { 31 | reportError("${node.name} has the type ${node.type} but was " + 32 | "expected to have the type $expectedTypeOfA.", node.context) 33 | } 34 | } 35 | } 36 | } else if (expA is FunctionCallExpression) { 37 | val node = module.getNodeFromReference(expA.functionCall.functionReference, localVariables) 38 | when (node) { 39 | is Function -> { 40 | if (node.returnType == T_UNDEF) { 41 | node.returnType = expectedTypeOfA 42 | } else if (node.returnType != expectedTypeOfA && (node.returnType !is NumericType && expectedTypeOfA !is NumericType)) { 43 | reportError("${node.name} has the return type ${node.returnType} but was" + 44 | "expected to have the type $expectedTypeOfA.", node.context) 45 | } 46 | } 47 | } 48 | } 49 | } else if (expectedTypeOfB == T_UNDEF) { 50 | if (expA is ReferenceExpression) { 51 | val node = module.getNodeFromReference(expA.reference, localVariables) 52 | when (node) { 53 | is Variable -> { 54 | if (node.type == T_UNDEF) { 55 | val type = getType(expB, localVariables) 56 | node.type = type 57 | expA.reference.type = type 58 | } 59 | } 60 | } 61 | } else if (expA is FunctionCallExpression) { 62 | val node = module.getNodeFromReference(expA.functionCall.functionReference, localVariables) 63 | when (node) { 64 | is Function -> { 65 | if (node.returnType == T_UNDEF) { 66 | node.returnType = getType(expB, localVariables) 67 | } 68 | } 69 | } 70 | } 71 | } 72 | } 73 | checkType(expA, expB, expression.operator.aType, expression.operator.bType) 74 | checkType(expB, expA, expression.operator.bType, expression.operator.aType) 75 | 76 | if (returnType is IntType) { 77 | getBiggestInt(getType(expA, localVariables) as IntType, getType(expB, localVariables) as IntType) 78 | } else if (returnType is FloatType) { 79 | getBiggestFloat(getType(expA, localVariables) as FloatType, getType(expB, localVariables) as FloatType) 80 | } else 81 | returnType 82 | } 83 | is TrueExpression, is FalseExpression -> { 84 | T_BOOL 85 | } 86 | is StringLiteral -> { 87 | T_STRING 88 | } 89 | is FunctionCallExpression -> { 90 | val function = module.getNodeFromReference(expression.functionCall.functionReference, null) 91 | if (function is Function) 92 | function.returnType 93 | else { 94 | reportError("Can't find return type for '$expression'.", expression.context) 95 | T_UNDEF 96 | } 97 | } 98 | is ClazzInitializerExpression -> { 99 | val node = module.getNodeFromReference(expression.classReference, null) 100 | node as? Clazz ?: T_UNDEF 101 | } 102 | is FieldGetterExpression -> { 103 | val varType = getType(expression.variable, localVariables) 104 | if (varType is Clazz) { 105 | val fieldName = expression.fieldReference.name 106 | val possibleFields = varType.fields.filter { it.name == fieldName } 107 | val field = possibleFields.last() 108 | field.type 109 | } else T_UNDEF 110 | } 111 | is ReferenceExpression -> { 112 | val node = module.getNodeFromReference(expression.reference, localVariables) 113 | val type = if (node != null) { 114 | when (node) { 115 | is Clazz -> node 116 | is Function -> node 117 | is Variable -> node.type 118 | is Formal -> node.type 119 | else -> T_UNDEF 120 | } 121 | } else { 122 | reportError("Can't find type for '$expression'.", expression.context) 123 | T_UNDEF 124 | } 125 | expression.reference.type = type 126 | type 127 | } 128 | else -> T_UNDEF 129 | } 130 | } 131 | 132 | init { 133 | module.globalVariables.forEach { visit(it) } 134 | module.globalClasses.forEach { visit(it) } 135 | module.globalFunctions.forEach { visit(it) } 136 | } 137 | 138 | override fun visit(function: Function) { 139 | visit(function, null) 140 | } 141 | 142 | fun visit(function: Function, clazz: Clazz?) { 143 | val localVariables: MutableMap = mutableMapOf() 144 | function.formals.forEach { localVariables.put(it.name, it) } 145 | 146 | if (clazz != null) { 147 | clazz.fields.forEach { localVariables.put(it.name, it) } 148 | } 149 | 150 | function.statements.forEach { visit(it, localVariables) } 151 | if (function.expression != null) { 152 | // Function should return the type of the return expression. 153 | val lastExpressionType = getType(function.expression!!, localVariables) 154 | if (function.returnType != lastExpressionType && function.returnType != T_UNDEF) 155 | reportError("Function '${function.name}' is marked with the type " + 156 | "'${function.returnType}' but its last expression is of the type '$lastExpressionType'.", function.context) 157 | function.returnType = lastExpressionType 158 | } else { 159 | // Function should return void if there is no return expression. 160 | if (function.returnType != T_UNDEF && function.returnType != T_VOID) 161 | reportError("Function '${function.name}' is marked with the type " + 162 | "'${function.returnType}' but does not contain a final expression that returns " + 163 | "that type.", function.context) 164 | function.returnType = T_VOID 165 | } 166 | } 167 | 168 | fun visit(variable: Variable, localVariables: MutableMap?) { 169 | if (variable.initialExpression != null) { 170 | visit(variable.initialExpression!!, localVariables) 171 | 172 | // Variable should have the same type as their initial expression. 173 | val expressionType = getType(variable.initialExpression!!, localVariables) 174 | 175 | if (variable.type != T_UNDEF && variable.type != expressionType) 176 | reportError("Variable '${variable.name}' is marked with the type '${variable.type}' " + 177 | "but its initial expression is of type '$expressionType'.", variable.context) 178 | 179 | variable.type = expressionType 180 | } 181 | } 182 | 183 | override fun visit(variable: Variable) { 184 | visit(variable, null) 185 | } 186 | 187 | override fun visit(clazz: Clazz) { 188 | clazz.fields.forEach { visit(it) } 189 | if (clazz.constructor != null) 190 | visit(clazz.constructor, clazz) 191 | clazz.methods.forEach { visit(it, clazz) } 192 | } 193 | 194 | fun visit(expression: Expression, localVariables: MutableMap?) { 195 | when (expression) { 196 | is FunctionCallExpression -> visit(expression, localVariables) 197 | } 198 | } 199 | 200 | fun visit(functionCallExpression: FunctionCallExpression, localVariables: MutableMap?) { 201 | val function = module.globalFunctions.filter { it.name == functionCallExpression.functionCall.functionReference.name }.last() 202 | val formalTypes = function.formals.map { it.type } 203 | val argTypes = functionCallExpression.functionCall.arguments.map { getType(it, localVariables) } 204 | if (formalTypes != argTypes) 205 | reportError("Function call to '${functionCallExpression.functionCall.functionReference.name}' expected" + 206 | " '$formalTypes' but was given '$argTypes'.", functionCallExpression.context) 207 | } 208 | 209 | fun visit(statement: Statement, localVariables: MutableMap?) { 210 | when (statement) { 211 | is VariableDeclarationStatement -> visit(statement, localVariables) 212 | is VariableReassignmentStatement -> visit(statement, localVariables) 213 | is FieldSetterStatement -> visit(statement, localVariables) 214 | is IfStatement -> { 215 | visit(statement.expression, localVariables) 216 | if (getType(statement.expression, localVariables) != T_BOOL) { 217 | reportError("The type of '${statement.expression}' is not a boolean.'", statement.context) 218 | } 219 | statement.statements.forEach { visit(it, localVariables) } 220 | if (statement.elseStatement != null) 221 | visit(statement.elseStatement!!, localVariables) 222 | } 223 | is WhileStatement -> { 224 | visit(statement.expression, localVariables) 225 | if (getType(statement.expression, localVariables) != T_BOOL) { 226 | reportError("The type of '${statement.expression}' is not a boolean.", statement.context) 227 | } 228 | statement.statements.forEach { visit(it, localVariables) } 229 | } 230 | else -> visit(statement) 231 | } 232 | } 233 | 234 | fun visit(fieldSetterStatement: FieldSetterStatement, localVariables: MutableMap?) { 235 | val varType = getType(fieldSetterStatement.variable, localVariables) 236 | val fieldName = fieldSetterStatement.fieldReference.name 237 | if (varType is Clazz) { 238 | val possibleFields = varType.fields.filter { it.name == fieldName } 239 | val fieldType = possibleFields.last().type 240 | val expressionType = getType(fieldSetterStatement.expression, localVariables) 241 | if (fieldType != expressionType) 242 | reportError("Can't set '$fieldName' to '${fieldSetterStatement.expression}' because it is of type" + 243 | " '$expressionType' and it needs to be of type '$fieldType'.", fieldSetterStatement.context) 244 | } else reportError("Can't set field of '${fieldSetterStatement.variable}' because it is of type '$varType'.", fieldSetterStatement.context) 245 | } 246 | 247 | fun visit(variableDeclarationStatement: VariableDeclarationStatement, localVariables: MutableMap?) { 248 | localVariables?.put(variableDeclarationStatement.variable.name, variableDeclarationStatement.variable) 249 | visit(variableDeclarationStatement.variable, localVariables) 250 | } 251 | 252 | fun visit(variableReassignmentStatement: VariableReassignmentStatement, localVariables: MutableMap?) { 253 | val name = variableReassignmentStatement.reference.name 254 | val thingsWithName: MutableList = mutableListOf() 255 | thingsWithName.addAll(module.globalVariables.filter { it.name == name }) 256 | localVariables?.forEach { if (it.key == name) thingsWithName.add(it.value) } 257 | 258 | if (thingsWithName.isNotEmpty()) { 259 | val variable = thingsWithName.last() 260 | val expression = variableReassignmentStatement.expression 261 | visit(expression, localVariables) 262 | val expressionType = getType(expression, localVariables) 263 | if (expressionType == T_UNDEF && expression is ReferenceExpression) { 264 | val node = module.getNodeFromReference(expression.reference, localVariables) 265 | if (node is Variable) { 266 | node.type = variable.type 267 | return 268 | } 269 | } 270 | if (variable.type != expressionType) 271 | reportError("Variable '${variable.name}' is marked with the type '${variable.type}' but the type" + 272 | " of '$expression' is '$expressionType'.", variableReassignmentStatement.context) 273 | } 274 | } 275 | 276 | } 277 | -------------------------------------------------------------------------------- /src/main/kotlin/xyz/jadonfowler/compiler/visitor/ASTBuilder.kt: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.compiler.visitor 2 | 3 | import xyz.jadonfowler.compiler.ast.* 4 | import xyz.jadonfowler.compiler.ast.Function 5 | import xyz.jadonfowler.compiler.globalModules 6 | import xyz.jadonfowler.compiler.parser.LangBaseVisitor 7 | import xyz.jadonfowler.compiler.parser.LangParser 8 | 9 | /** 10 | * ASTBuilder transforms a ContextTree into an AST 11 | */ 12 | class ASTBuilder(val moduleName: String, val source: String) : LangBaseVisitor() { 13 | 14 | val imports: MutableList = mutableListOf() 15 | val globalVariables: MutableList = mutableListOf() 16 | val globalFunctions: MutableList = mutableListOf() 17 | val globalClasses: MutableList = mutableListOf() 18 | val globalTraits: MutableList = mutableListOf() 19 | 20 | override fun visitProgram(ctx: LangParser.ProgramContext?): Module { 21 | val externalDeclarations = ctx?.externalDeclaration() 22 | 23 | imports.addAll(ctx?.importDeclaration()?.map { import(it) }?.flatten()!!) 24 | 25 | globalVariables.addAll(externalDeclarations?.filter { it.variableDeclaration() != null }?.map { 26 | visitVariableDeclaration(it.variableDeclaration()) 27 | }.orEmpty()) 28 | 29 | externalDeclarations?.filter { it.traitDeclaration() != null }?.forEach { 30 | globalTraits.add(visitTraitDeclaration(it.traitDeclaration())) 31 | } 32 | 33 | externalDeclarations?.filter { it.classDeclaration() != null }?.forEach { 34 | globalClasses.add(visitClassDeclaration(it.classDeclaration())) 35 | } 36 | 37 | globalFunctions.addAll(externalDeclarations?.filter { it.functionDeclaration() != null }?.map { 38 | visitFunctionDeclaration(it.functionDeclaration()) 39 | }.orEmpty()) 40 | 41 | val module = Module(moduleName, imports, globalVariables, globalFunctions, globalClasses, globalTraits, source) 42 | globalModules.add(module) 43 | return module 44 | } 45 | 46 | fun import(ctx: LangParser.ImportDeclarationContext?): List { 47 | return ctx?.id_p()?.map { Import(Reference(it?.text ?: ""), ctx) }.orEmpty() 48 | } 49 | 50 | override fun visitExternalDeclaration(ctx: LangParser.ExternalDeclarationContext?): Node { 51 | ctx?.variableDeclaration()?.let { return visitVariableDeclaration(it) } 52 | ctx?.functionDeclaration()?.let { return visitFunctionDeclaration(it) } 53 | ctx?.classDeclaration()?.let { return visitClassDeclaration(it) } 54 | // Should be unreachable 55 | return EmptyNode() 56 | } 57 | 58 | fun getFunctionPrototype(ctx: LangParser.FunctionPrototypeContext?): Function.Prototype { 59 | var identifier = ctx?.ID()?.symbol?.text.orEmpty() 60 | 61 | // replace `main` with `real_main` for wrapping 62 | if (identifier == "main") 63 | identifier = "real_main" 64 | 65 | val returnType = getType(ctx?.typeAnnotation()?.ID()?.symbol?.text.orEmpty(), globalClasses) 66 | val formals = ctx?.argumentList()?.argument()?.map { 67 | Formal(getType(it.variableSignature()?.typeAnnotation()?.ID()?.symbol?.text.orEmpty(), globalClasses), 68 | it.variableSignature()?.ID()?.symbol?.text.orEmpty(), ctx) 69 | }.orEmpty() 70 | 71 | val attributes = attributeListFromAttributeListContext(ctx?.attributeList()) 72 | 73 | return Function.Prototype(attributes, returnType, identifier, formals) 74 | } 75 | 76 | override fun visitFunctionDeclaration(ctx: LangParser.FunctionDeclarationContext?): Function { 77 | 78 | val statements = statementListFromStatementListContext(ctx?.statementList()).toMutableList() 79 | 80 | ctx?.statement()?.let { 81 | val statementNode = visitStatement(it) 82 | if (statementNode is Statement) 83 | statements.add(statementNode) 84 | } 85 | 86 | ctx?.blockStatement()?.let { 87 | val blockStatementNode = visitBlockStatement(it) 88 | if (blockStatementNode is Block) 89 | statements.add(blockStatementNode) 90 | } 91 | 92 | var expression: Expression? = null 93 | ctx?.expression()?.let { expression = visitExpression(it) } 94 | 95 | 96 | return Function(getFunctionPrototype(ctx?.functionPrototype()), statements, expression, ctx!!) 97 | } 98 | 99 | fun attributeListFromAttributeListContext(attributeListContext: LangParser.AttributeListContext?): List { 100 | var context = attributeListContext 101 | val attributes: MutableList = mutableListOf() 102 | while (context != null && context.attribute() != null) { 103 | attributes.add(visitAttribute(context.attribute())) 104 | context = context.attributeList() 105 | } 106 | return attributes 107 | } 108 | 109 | override fun visitAttribute(ctx: LangParser.AttributeContext?): Attribute { 110 | val name = ctx?.ID(0)?.symbol?.text.orEmpty() 111 | val values: MutableList = mutableListOf() 112 | (1..ctx?.ID()?.size!! - 1).forEach { 113 | values.add(ctx?.ID(it)?.symbol?.text.orEmpty()) 114 | } 115 | return Attribute(name, values, ctx!!) 116 | } 117 | 118 | fun statementListFromStatementListContext(statementListContext: LangParser.StatementListContext?): List { 119 | var context = statementListContext 120 | val statements: MutableList = mutableListOf() 121 | while (context != null) { 122 | var statement: Statement? = null 123 | val blockStatementContext = context.blockStatement() 124 | val statementContext = context.statement() 125 | // There is either a blockStatement or a normal statement, so the order here doesn't matter. 126 | blockStatementContext?.let { 127 | val blockStatementNode = visitBlockStatement(it) 128 | if (blockStatementNode is Statement) statement = blockStatementNode 129 | } 130 | statementContext?.let { 131 | val statementNode = visitStatement(it) 132 | if (statementNode is Statement) statement = statementNode 133 | } 134 | statement?.let { 135 | statements.add(it) 136 | } 137 | // Set current context as child 138 | context = context.statementList() 139 | } 140 | return statements 141 | } 142 | 143 | override fun visitVariableDeclaration(ctx: LangParser.VariableDeclarationContext?): Variable { 144 | val constant: Boolean = ctx?.variableModifier()?.text.equals("let") 145 | val identifier = ctx?.variableSignature()?.ID()?.symbol?.text.orEmpty() 146 | val type = getType(ctx?.variableSignature()?.typeAnnotation()?.ID()?.symbol?.text.orEmpty(), globalClasses) 147 | var expression: Expression? = null 148 | ctx?.expression()?.let { 149 | expression = visitExpression(it) 150 | } 151 | return Variable(type, identifier, expression, constant, ctx!!) 152 | } 153 | 154 | override fun visitClassDeclaration(ctx: LangParser.ClassDeclarationContext?): Clazz { 155 | val identifier = ctx?.ID(0)?.symbol?.text.orEmpty() 156 | val declarations = ctx?.externalDeclaration()?.map { visitExternalDeclaration(it) }.orEmpty() 157 | val fields = declarations.filterIsInstance() 158 | val methods = declarations.filterIsInstance().toMutableList() 159 | val constructors = methods.filter { it.name == "init" } 160 | val constructor: Function? = if (constructors.isNotEmpty()) constructors.last() else null 161 | constructor?.let { 162 | methods.remove(constructor) 163 | } 164 | 165 | val ids = ctx?.ID().orEmpty() 166 | val traits = if (ids.size > 1) { 167 | ctx?.ID()?.subList(1, ctx.ID().lastIndex + 1)?.map { 168 | val traitName = it.symbol.text 169 | val traits = defaultTraits.toMutableList() 170 | traits.addAll(globalTraits) 171 | traits.filter { it.name == traitName }.last() 172 | }.orEmpty() 173 | } else listOf() 174 | 175 | return Clazz(identifier, fields, methods, constructor, traits, ctx!!) 176 | } 177 | 178 | override fun visitTraitDeclaration(ctx: LangParser.TraitDeclarationContext?): Trait { 179 | val name = ctx?.ID()?.symbol?.text.orEmpty() 180 | val prototypes = ctx?.functionPrototype()?.map { getFunctionPrototype(it) }.orEmpty() 181 | return Trait(name, prototypes) 182 | } 183 | 184 | override fun visitFunctionCall(ctx: LangParser.FunctionCallContext?): FunctionCall { 185 | val functionName = ctx?.ID()?.symbol?.text.orEmpty() 186 | val expressions = expressionListFromContext(ctx?.expressionList()) 187 | return FunctionCall(Reference(functionName), expressions) 188 | } 189 | 190 | override fun visitMethodCall(ctx: LangParser.MethodCallContext?): MethodCall { 191 | val variableName = ctx?.ID(0)?.symbol?.text.orEmpty() 192 | val methodName = ctx?.ID(1)?.symbol?.text.orEmpty() 193 | val expressions = expressionListFromContext(ctx?.expressionList()) 194 | return MethodCall(Reference(variableName), Reference(methodName), expressions) 195 | } 196 | 197 | override fun visitFieldSetter(ctx: LangParser.FieldSetterContext?): FieldSetterStatement { 198 | val variable = visitExpression(ctx?.expression(0)) 199 | val fieldReference = Reference(ctx?.ID()?.symbol?.text.orEmpty()) 200 | val expression = visitExpression(ctx?.expression(1)) 201 | return FieldSetterStatement(variable, fieldReference, expression, ctx!!) 202 | } 203 | 204 | fun expressionListFromContext(expressionListContext: LangParser.ExpressionListContext?): List { 205 | val expressions: MutableList = if (expressionListContext?.expression() != null) 206 | mutableListOf(visitExpression(expressionListContext?.expression())) 207 | else mutableListOf() 208 | 209 | var expressionListContextChild: LangParser.ExpressionListContext? = expressionListContext?.expressionList() 210 | while (expressionListContextChild != null) { 211 | val expressionListContextChildExpression = visitExpression(expressionListContextChild.expression()) 212 | expressions.add(expressionListContextChildExpression) 213 | expressionListContextChild = expressionListContextChild.expressionList() 214 | } 215 | 216 | return expressions 217 | } 218 | 219 | override fun visitVariableReassignment(ctx: LangParser.VariableReassignmentContext?): VariableReassignmentStatement { 220 | return VariableReassignmentStatement(Reference(ctx?.ID()?.symbol?.text.orEmpty()), visitExpression(ctx?.expression()), ctx!!) 221 | } 222 | 223 | override fun visitStatement(ctx: LangParser.StatementContext?): Node /*TODO: return Statement */ { 224 | ctx?.fieldSetter()?.let { 225 | return visitFieldSetter(it) 226 | } 227 | ctx?.variableDeclaration()?.let { 228 | return VariableDeclarationStatement(visitVariableDeclaration(it), ctx) 229 | } 230 | ctx?.variableReassignment()?.let { 231 | return visitVariableReassignment(it) 232 | } 233 | ctx?.methodCall()?.let { 234 | return MethodCallStatement(visitMethodCall(it), ctx) 235 | } 236 | ctx?.functionCall()?.let { 237 | return FunctionCallStatement(visitFunctionCall(it), ctx) 238 | } 239 | return EmptyNode() 240 | } 241 | 242 | override fun visitBlockStatement(ctx: LangParser.BlockStatementContext?): Node { 243 | val id: String = ctx?.getChild(0)?.text.orEmpty() 244 | return when (id) { 245 | "if" -> { 246 | val expression = visitExpression(ctx?.expression()) 247 | val statements = statementListFromStatementListContext(ctx?.statementList()) 248 | 249 | val parentIf: IfStatement = IfStatement(expression, statements, null, ctx!!) 250 | var currentIf: IfStatement = parentIf 251 | 252 | ctx.elif()?.forEach { 253 | val elifExpression = visitExpression(it.expression()) 254 | val elifStatements = statementListFromStatementListContext(it.statementList()) 255 | val elif = IfStatement(elifExpression, elifStatements, null, ctx) 256 | currentIf.elseStatement = elif 257 | currentIf = elif 258 | } 259 | 260 | ctx.elseStatement()?.let { 261 | val elseStatements = statementListFromStatementListContext(it.statementList()) 262 | currentIf.elseStatement = elseStatement(elseStatements, ctx) 263 | } 264 | 265 | parentIf 266 | } 267 | "while" -> { 268 | val expression = visitExpression(ctx?.expression()) 269 | val statements = statementListFromStatementListContext(ctx?.statementList()) 270 | WhileStatement(expression, statements, ctx!!) 271 | } 272 | else -> EmptyNode() 273 | } 274 | } 275 | 276 | override fun visitExpression(ctx: LangParser.ExpressionContext?): Expression { 277 | val firstSymbol = ctx?.getChild(0)?.text.orEmpty() 278 | val secondSymbol = ctx?.getChild(1)?.text.orEmpty() 279 | when (firstSymbol) { 280 | "(" -> { 281 | // | '(' expression ')' 282 | return visitExpression(ctx?.getChild(1) as LangParser.ExpressionContext?) 283 | } 284 | "true" -> return TrueExpression(ctx!!) 285 | "false" -> return FalseExpression(ctx!!) 286 | } 287 | when (secondSymbol) { 288 | "`" -> { 289 | // Inline Function Call 290 | val firstExpression = visitExpression(ctx?.getChild(0) as LangParser.ExpressionContext?) 291 | val secondExpression = visitExpression(ctx?.getChild(4) as LangParser.ExpressionContext?) 292 | val functionName = ctx?.getChild(2)?.text.orEmpty() 293 | return FunctionCallExpression(FunctionCall(Reference(functionName), listOf(firstExpression, secondExpression)), ctx!!) 294 | } 295 | "." -> { 296 | // Field Getter 297 | val variable = visitExpression(ctx?.expression(0)) 298 | val fieldReference = Reference(ctx?.ID()?.symbol?.text.orEmpty()) 299 | return FieldGetterExpression(variable, fieldReference, ctx!!) 300 | } 301 | } 302 | if (ctx?.getChild(0) is LangParser.ExpressionContext && ctx?.getChild(2) is LangParser.ExpressionContext) { 303 | val expressionA = visitExpression(ctx?.getChild(0) as LangParser.ExpressionContext?) 304 | val expressionB = visitExpression(ctx?.getChild(2) as LangParser.ExpressionContext?) 305 | val between = ctx?.getChild(1)?.text.orEmpty() 306 | val operator = getOperator(between) 307 | operator?.let { 308 | return BinaryOperator(expressionA, it, expressionB, ctx!!) 309 | } 310 | } 311 | ctx?.methodCall()?.let { 312 | return MethodCallExpression(visitMethodCall(it), ctx) 313 | } 314 | ctx?.functionCall()?.let { 315 | return FunctionCallExpression(visitFunctionCall(it), ctx) 316 | } 317 | ctx?.classInitializer()?.let { 318 | return visitClassInitializer(it) 319 | } 320 | ctx?.INT()?.let { 321 | return IntegerLiteral(it.text?.toInt()!!, ctx) 322 | } 323 | ctx?.FLOAT()?.let { 324 | val text = it.text.orEmpty() 325 | if (text.endsWith("d")) 326 | // Remove double suffix 327 | return FloatLiteral(text.substring(0..text.length - 2).toDouble(), T_FLOAT64, ctx) 328 | else if (text.endsWith("q")) 329 | return FloatLiteral(text.substring(0..text.length - 2).toDouble(), T_FLOAT128, ctx) 330 | else 331 | return FloatLiteral(text.toDouble(), T_FLOAT32, ctx) 332 | } 333 | ctx?.ID()?.let { 334 | val id = it.symbol?.text.orEmpty() 335 | return ReferenceExpression(Reference(id), ctx) 336 | } 337 | ctx?.STRING()?.let { 338 | val value = it.symbol?.text.orEmpty() 339 | // Remove quotes around string 340 | return StringLiteral(value.substring(1..value.length - 2), ctx) 341 | } 342 | throw Exception("${ctx?.text} can't be handled yet") 343 | } 344 | 345 | override fun visitClassInitializer(ctx: LangParser.ClassInitializerContext?): ClazzInitializerExpression { 346 | val className = ctx?.ID()?.symbol?.text.orEmpty() 347 | val expressions = expressionListFromContext(ctx?.expressionList()) 348 | return ClazzInitializerExpression(Reference(className), expressions, ctx!!) 349 | } 350 | 351 | override fun visitArgumentList(ctx: LangParser.ArgumentListContext?): Node { 352 | return EmptyNode() 353 | } 354 | 355 | override fun visitArgument(ctx: LangParser.ArgumentContext?): Node { 356 | return EmptyNode() 357 | } 358 | 359 | override fun visitTypeAnnotation(ctx: LangParser.TypeAnnotationContext?): Node { 360 | return EmptyNode() 361 | } 362 | 363 | override fun visitVariableSignature(ctx: LangParser.VariableSignatureContext?): Node { 364 | return EmptyNode() 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /src/main/kotlin/xyz/jadonfowler/compiler/visitor/Visitor.kt: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.compiler.visitor 2 | 3 | import xyz.jadonfowler.compiler.ast.* 4 | import xyz.jadonfowler.compiler.ast.Function 5 | 6 | abstract class Visitor(val module: Module) { 7 | // Module Structure 8 | 9 | abstract fun visit(function: Function) 10 | abstract fun visit(formal: Formal) 11 | abstract fun visit(variable: Variable) 12 | abstract fun visit(clazz: Clazz) 13 | 14 | // Statements 15 | open fun visit(statement: Statement) { 16 | when (statement) { 17 | is Block -> visit(statement) 18 | is IfStatement -> visit(statement) 19 | is WhileStatement -> visit(statement) 20 | is VariableDeclarationStatement -> visit(statement) 21 | is VariableReassignmentStatement -> visit(statement) 22 | is FunctionCallStatement -> visit(statement) 23 | is MethodCallStatement -> visit(statement) 24 | is FieldSetterStatement -> visit(statement) 25 | } 26 | } 27 | 28 | abstract fun visit(block: Block) 29 | abstract fun visit(ifStatement: IfStatement) 30 | abstract fun visit(whileStatement: WhileStatement) 31 | abstract fun visit(variableDeclarationStatement: VariableDeclarationStatement) 32 | abstract fun visit(variableReassignmentStatement: VariableReassignmentStatement) 33 | abstract fun visit(functionCallStatement: FunctionCallStatement) 34 | abstract fun visit(methodCallStatement: MethodCallStatement) 35 | abstract fun visit(fieldSetterStatement: FieldSetterStatement) 36 | 37 | // Expressions 38 | open fun visit(expression: Expression) { 39 | when (expression) { 40 | is TrueExpression -> visit(expression) 41 | is FalseExpression -> visit(expression) 42 | is IntegerLiteral -> visit(expression) 43 | is FloatLiteral -> visit(expression) 44 | is StringLiteral -> visit(expression) 45 | is ReferenceExpression -> visit(expression) 46 | is FunctionCallExpression -> visit(expression) 47 | is MethodCallExpression -> visit(expression) 48 | is FieldGetterExpression -> visit(expression) 49 | is BinaryOperator -> visit(expression) 50 | is ClazzInitializerExpression -> visit(expression) 51 | } 52 | } 53 | 54 | abstract fun visit(trueExpression: TrueExpression) 55 | abstract fun visit(falseExpression: FalseExpression) 56 | abstract fun visit(integerLiteral: IntegerLiteral) 57 | abstract fun visit(floatLiteral: FloatLiteral) 58 | abstract fun visit(stringLiteral: StringLiteral) 59 | abstract fun visit(referenceExpression: ReferenceExpression) 60 | abstract fun visit(functionCallExpression: FunctionCallExpression) 61 | abstract fun visit(methodCallExpression: MethodCallExpression) 62 | abstract fun visit(fieldGetterExpression: FieldGetterExpression) 63 | abstract fun visit(binaryOperator: BinaryOperator) 64 | abstract fun visit(clazzInitializerExpression: ClazzInitializerExpression) 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/test/kotlin/xyz/jadonfowler/compiler/ASTTest.kt: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.compiler 2 | 3 | import org.junit.Test 4 | import xyz.jadonfowler.compiler.ast.* 5 | import xyz.jadonfowler.compiler.pass.PrintPass 6 | import kotlin.test.assertEquals 7 | import kotlin.test.assertNotNull 8 | import kotlin.test.assertTrue 9 | 10 | class ASTTest { 11 | 12 | @Test fun functionDeclarationParsing() { 13 | val code = """ 14 | testFunction0 (argument0 : Int, argument1 : String, argument2 : Bool) : Int 15 | 0 16 | """ 17 | val module = compileString("functionDeclarationParsing", code, true) 18 | 19 | assertEquals("functionDeclarationParsing", module.name, "The Module's name is incorrect.") 20 | assertEquals(1, module.globalFunctions.size, "One Function should exist in the Module.") 21 | 22 | val testFunction0 = module.globalFunctions[0] 23 | assertEquals("testFunction0", testFunction0.name, "The name of the first Function isn't testFunction0.") 24 | 25 | assertEquals(0, testFunction0.statements.size, "testFunction0 shouldn't have any statements, but it has" + 26 | "${testFunction0.statements.size}.") 27 | assertNotNull(testFunction0.expression, "testFunction0 should have a return expression of 0.") 28 | 29 | assertEquals(3, testFunction0.formals.size, "testFunction0 has ${testFunction0.formals.size} formals instead" + 30 | " of 3.") 31 | 32 | assertEquals(T_BOOL, testFunction0.formals[2].type, "The third formal of testFunction0 should have a type of" + 33 | " Bool.") 34 | 35 | assertEquals("(Int32 -> String -> Bool -> Int32)", testFunction0.toString()) 36 | 37 | println(PrintPass(module).output) 38 | } 39 | 40 | @Test fun functionVariableDeclarationStatementParsing() { 41 | val code = """ 42 | testFunction1 (argument0 : Int) : Int 43 | let local_variable0 = argument1 * 32, 44 | local_variable0 - 8 45 | """ 46 | val module = compileString("functionVariableDeclarationStatementParsing", code, true) 47 | 48 | val testFunction1 = module.globalFunctions[0] 49 | assertEquals(1, testFunction1.statements.size, "testFunction1 should only have 1 statement.") 50 | assertTrue(testFunction1.statements[0] is VariableDeclarationStatement, "The first statement should be a" + 51 | " VariableDeclarationStatement.") 52 | val statement = testFunction1.statements[0] as VariableDeclarationStatement 53 | assertEquals("local_variable0", statement.variable.name, "The variable's name in the statement isn't" + 54 | " local_variable0.") 55 | assertTrue(statement.variable.constant, "The variable is not a constant.") 56 | assertTrue(statement.variable.initialExpression is BinaryOperator, "The variable's initial expression is not" + 57 | " a Binary Operator.") 58 | val op = statement.variable.initialExpression as BinaryOperator 59 | assertTrue(op.expressionA is ReferenceExpression, "The variable's initial expression's left argument isn't a" + 60 | " ReferenceExpression.") 61 | assertTrue(op.expressionB is IntegerLiteral, "The variable's initial expression's right argument is not an" + 62 | " IntegerLiteral.") 63 | 64 | println(PrintPass(module).output) 65 | } 66 | 67 | @Test fun functionIfStatementParsing() { 68 | val code = """ 69 | testFunction2 (a : Int) : Int 70 | var r = 1, 71 | if a < (100 + 12) 72 | r = a 73 | else 74 | r = a + 100 75 | ; 76 | r = r * 2, 77 | r 78 | """ 79 | val module = compileString("functionIfStatementParsing", code, true) 80 | val testFunction2 = module.globalFunctions[0] 81 | 82 | assertTrue(testFunction2.statements[1] is IfStatement, "The second statement isn't an IfStatement.") 83 | val ifStatement = testFunction2.statements[1] as IfStatement 84 | assertNotNull(ifStatement.elseStatement, "There should be an ElseStatement in the IfStatement.") 85 | assertEquals(1, ifStatement.statements.size) 86 | assertEquals(1, ifStatement.elseStatement?.statements?.size) 87 | assertTrue(((ifStatement.expression as BinaryOperator).expressionB as BinaryOperator).expressionA is IntegerLiteral) 88 | assertTrue(testFunction2.statements[2] is VariableReassignmentStatement) 89 | 90 | println(PrintPass(module).output) 91 | } 92 | 93 | @Test fun functionWhileStatementParsing() { 94 | val code = """ 95 | test (a : Int) : Int 96 | var i : Int = 0, 97 | var sum : Int = 0, 98 | while i < a 99 | i = i + 1, 100 | sum = sum + a 101 | ; 102 | sum 103 | """ 104 | val module = compileString("functionWhileStatementParsing", code, true) 105 | val function = module.globalFunctions[0] 106 | 107 | assertEquals(3, function.statements.size) 108 | assertTrue(function.statements[2] is WhileStatement) 109 | 110 | val statement = function.statements[2] as WhileStatement 111 | assertTrue(statement.expression is BinaryOperator) 112 | assertEquals("<", (statement.expression as BinaryOperator).operator.toString()) 113 | assertEquals(2, statement.statements.size) 114 | assertTrue(statement.statements[1] is VariableReassignmentStatement) 115 | 116 | println(PrintPass(module).output) 117 | } 118 | 119 | @Test fun functionCalling() { 120 | val code = """ 121 | bland () : Int 122 | 0 123 | 124 | exciting () : Int 125 | let b = bland(), 126 | bland(), 127 | b + 1 128 | """ 129 | val module = compileString("functionCalling", code, true) 130 | 131 | assertEquals(2, module.globalFunctions.size) 132 | val excitingFunction = module.globalFunctions[1] 133 | assertTrue(excitingFunction.statements[0] is VariableDeclarationStatement) 134 | val v = excitingFunction.statements[0] as VariableDeclarationStatement 135 | assertTrue(v.variable.initialExpression is FunctionCallExpression) 136 | assertTrue(excitingFunction.statements[1] is FunctionCallStatement) 137 | 138 | assertNotNull(excitingFunction.expression) 139 | assertTrue(excitingFunction.expression is BinaryOperator) 140 | val lastOperator = excitingFunction.expression as BinaryOperator 141 | assertEquals("b", (lastOperator.expressionA as ReferenceExpression).toString(), "ReferenceExpression should" + 142 | " reference 'b'.") 143 | assertEquals("+", lastOperator.operator.toString(), "Operator should be '+'.") 144 | assertEquals("1", (lastOperator.expressionB as IntegerLiteral).toString(), "IntegerLiteral should be '1'.") 145 | assertEquals("(b + 1)", lastOperator.toString()) 146 | 147 | println(PrintPass(module).output) 148 | } 149 | 150 | @Test fun globalVariableParsing() { 151 | val code = """ 152 | let a : Int = 7 153 | let b : Int = 8 154 | let c : Int = 9 155 | let d : String = "test" 156 | let e : Bool = false 157 | """ 158 | val module = compileString("globalVariableParsing", code, true) 159 | 160 | assertEquals(5, module.globalVariables.size) 161 | assertTrue(module.globalVariables[2].initialExpression is IntegerLiteral) 162 | assertEquals(T_STRING, module.globalVariables[3].type) 163 | assertTrue(module.globalVariables[4].initialExpression is FalseExpression) 164 | 165 | println(PrintPass(module).output) 166 | } 167 | 168 | @Test fun classParsing() { 169 | val code = """ 170 | class Test 171 | let field : Int = 8 172 | 173 | method (a : Int) : Int 174 | field = a, 175 | a 176 | ; 177 | """ 178 | val module = compileString("classParsing", code, true) 179 | 180 | assertEquals(1, module.globalClasses.size) 181 | val clazz = module.globalClasses[0] 182 | assertEquals(1, clazz.fields.size) 183 | assertTrue(clazz.fields[0].initialExpression is IntegerLiteral) 184 | assertEquals(1, clazz.methods.size) 185 | 186 | println(PrintPass(module).output) 187 | } 188 | 189 | @Test fun functionCallParsing() { 190 | val code = """ 191 | funA (a : Int) : Int 192 | a + 1 193 | 194 | funB (a : Int) : Int 195 | let b = funA(a), 196 | funA(b), 197 | b * 2 198 | """ 199 | val module = compileString("functionCallParsing", code, true) 200 | 201 | assertEquals(2, module.globalFunctions.size, "There should be two functions") 202 | assertTrue(module.globalFunctions[1].statements[0] is VariableDeclarationStatement) 203 | val dec = module.globalFunctions[1].statements[0] as VariableDeclarationStatement 204 | 205 | assertTrue(dec.variable.initialExpression is FunctionCallExpression) 206 | val functionCallExpression = dec.variable.initialExpression as FunctionCallExpression 207 | assertEquals("funA", functionCallExpression.functionCall.functionReference.name) 208 | assertEquals(1, functionCallExpression.functionCall.arguments.size) 209 | 210 | assertTrue(module.globalFunctions[1].statements[1] is FunctionCallStatement) 211 | val fcs = module.globalFunctions[1].statements[1] as FunctionCallStatement 212 | assertEquals("funA", fcs.functionCall.functionReference.name) 213 | 214 | assertTrue(functionCallExpression.functionCall.arguments[0] is ReferenceExpression) 215 | val argRef = functionCallExpression.functionCall.arguments[0] as ReferenceExpression 216 | assertEquals("a", argRef.reference.name) 217 | 218 | println(PrintPass(module).output) 219 | } 220 | 221 | @Test fun methodCallParsing() { 222 | val code = """ 223 | funA (a : SomeClass) 224 | let a = a.method(1, 2), 225 | a.statement(2), 226 | a.expression(1) 227 | """ 228 | val module = compileString("methodCallParsing", code, true) 229 | 230 | assertTrue((module.globalFunctions[0].statements[0] as VariableDeclarationStatement).variable.initialExpression is MethodCallExpression) 231 | val methodExpression = (module.globalFunctions[0].statements[0] as VariableDeclarationStatement).variable.initialExpression as MethodCallExpression 232 | assertTrue(methodExpression.methodCall.arguments[0] is IntegerLiteral) 233 | 234 | assertTrue(module.globalFunctions[0].statements[1] is MethodCallStatement) 235 | assertTrue((module.globalFunctions[0].statements[1] as MethodCallStatement).methodCall.arguments[0] is IntegerLiteral) 236 | 237 | assertTrue(module.globalFunctions[0].expression is MethodCallExpression) 238 | assertTrue((module.globalFunctions[0].expression as MethodCallExpression).methodCall.arguments[0] is IntegerLiteral) 239 | 240 | println(PrintPass(module).output) 241 | } 242 | 243 | @Test fun fieldGetterParsing() { 244 | val code = """ 245 | test (a : SomeClass) 246 | a.thing 247 | """ 248 | val module = compileString("fieldGetterParsing", code, true) 249 | 250 | val returnExpression = module.globalFunctions[0].expression 251 | assertTrue(returnExpression is FieldGetterExpression) 252 | val field = returnExpression as FieldGetterExpression 253 | assertEquals("a", field.variable.toString()) 254 | assertEquals("thing", field.fieldReference.name) 255 | 256 | println(PrintPass(module).output) 257 | } 258 | 259 | @Test fun fieldSetterParsing() { 260 | val code = """ 261 | test (a : SomeClass) 262 | a.thing = 8 263 | """ 264 | val module = compileString("fieldSetterParsing", code, true) 265 | 266 | val statement = module.globalFunctions[0].statements[0] 267 | assertTrue(statement is FieldSetterStatement) 268 | val field = statement as FieldSetterStatement 269 | assertEquals("a", field.variable.toString()) 270 | assertEquals("thing", field.fieldReference.name) 271 | assertEquals(8, (field.expression as IntegerLiteral).value) 272 | 273 | println(PrintPass(module).output) 274 | } 275 | 276 | @Test fun infixFunction() { 277 | val code = """ 278 | add (a : Int, b : Int) : Int 279 | a + b 280 | 281 | thing () : Int 282 | 1 `add` 1 283 | """ 284 | val module = compileString("infixFunction", code, true) 285 | 286 | val thing = module.globalFunctions[1].expression 287 | assertTrue(thing is FunctionCallExpression) 288 | val fce = thing as FunctionCallExpression 289 | assertEquals("add", fce.functionCall.functionReference.name) 290 | 291 | println(PrintPass(module).output) 292 | } 293 | 294 | @Test fun traitParsing() { 295 | val code = """ 296 | trait Test 297 | a (a : Int32) : Int32 298 | b (b : Int32) : Int32 299 | ; 300 | 301 | class TestImpl : Test 302 | a (a : Int32) : Int32 303 | a + 12 304 | 305 | b (b : Int32) : Int32 306 | b + 13 307 | ; 308 | """ 309 | val module = compileString("traitParsing", code) 310 | 311 | val trait = module.globalTraits[0] 312 | assertEquals(trait.name, "Test") 313 | val clazz = module.globalClasses[0] 314 | assertEquals(clazz.traits[0], trait) 315 | clazz.methods.forEachIndexed { i, function -> 316 | assertEquals(function.prototype, trait.functions[i]) 317 | } 318 | } 319 | 320 | } 321 | -------------------------------------------------------------------------------- /src/test/kotlin/xyz/jadonfowler/compiler/CompilerTestSuite.kt: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.compiler 2 | 3 | import org.junit.runner.RunWith 4 | import org.junit.runners.Suite 5 | 6 | @RunWith(ParallelSuite::class) 7 | @Suite.SuiteClasses( 8 | ASTTest::class, 9 | TypeCheckingTest::class, 10 | ConstantFoldTest::class, 11 | JVMTest::class, 12 | LLVMTest::class, 13 | SemanticsTest::class 14 | ) 15 | class CompilerTestSuite 16 | -------------------------------------------------------------------------------- /src/test/kotlin/xyz/jadonfowler/compiler/ConstantFoldTest.kt: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.compiler 2 | 3 | import org.junit.Test 4 | import xyz.jadonfowler.compiler.ast.IntegerLiteral 5 | import xyz.jadonfowler.compiler.pass.ConstantFoldPass 6 | import xyz.jadonfowler.compiler.pass.PrintPass 7 | import xyz.jadonfowler.compiler.pass.TypePass 8 | import kotlin.test.assertEquals 9 | 10 | class ConstantFoldTest { 11 | 12 | @Test fun additionConstantFolding() { 13 | val code = """ 14 | let a = 10 + 24 15 | """ 16 | val module = compileString("additionConstantFolding", code) 17 | TypePass(module) 18 | ConstantFoldPass(module) 19 | 20 | assertEquals(34, (module.globalVariables[0].initialExpression as IntegerLiteral).value) 21 | 22 | println(PrintPass(module).output) 23 | } 24 | 25 | @Test fun complexConstantFolding() { 26 | val code = """ 27 | let a = 1234 * 12 + 8767 + 3453 - 57347 + 73457 + 7457 28 | """ 29 | val module = compileString("complexConstantFolding", code) 30 | TypePass(module) 31 | ConstantFoldPass(module) 32 | 33 | assertEquals(50595, (module.globalVariables[0].initialExpression as IntegerLiteral).value) 34 | 35 | println(PrintPass(module).output) 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/test/kotlin/xyz/jadonfowler/compiler/JVMTest.kt: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.compiler 2 | 3 | import org.junit.Test 4 | import org.objectweb.asm.ClassReader 5 | import xyz.jadonfowler.compiler.backend.JVMBackend 6 | import xyz.jadonfowler.compiler.pass.ConstantFoldPass 7 | import xyz.jadonfowler.compiler.pass.TypePass 8 | import java.io.File 9 | import kotlin.test.assertEquals 10 | 11 | class JVMTest { 12 | 13 | @Test fun genFields() { 14 | val code = """ 15 | let a = 7 16 | """ 17 | val module = compileString("genFields", code) 18 | TypePass(module) 19 | ConstantFoldPass(module) 20 | 21 | val j = JVMBackend(module) 22 | j.output(File("/dev/null")) 23 | val reader = ClassReader(j.cw.toByteArray()) 24 | 25 | assertEquals("genFields", reader.className) 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/test/kotlin/xyz/jadonfowler/compiler/LLVMTest.kt: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.compiler 2 | 3 | import org.junit.Test 4 | import java.io.File 5 | import kotlin.test.assertTrue 6 | 7 | class LLVMTest { 8 | 9 | fun testIR(vararg testName: String) { 10 | val code = testName.map { "test/$it.l" } 11 | val files = code.map(::File) 12 | files.forEach { assertTrue(it.exists(), "Can't find file '${it.name}'.") } 13 | 14 | val expectedIRFiles = testName.map { File("test/out/llvm/$it.ll") } 15 | expectedIRFiles.forEach { assertTrue(it.exists(), "Output file doesn't exist!") } 16 | val expectedIR = expectedIRFiles.map { it.readLines().joinToString("\n") } 17 | 18 | main(arrayOf("--llvm", "--ast", *code.toTypedArray())) 19 | 20 | val actualIR = testName.map { File("bin/test.$it.ll").readLines().joinToString("\n") } 21 | actualIR.forEach(::println) 22 | 23 | // contains is used to ignore header information, which is different on every platform 24 | actualIR.forEachIndexed { i, s -> 25 | assertTrue(s.contains(expectedIR[i]), "Wrong IR emitted!\n\nExpected:\n\n${expectedIR[i]}" + 26 | "\n\n(Note: Header information is ignored.)\n") 27 | } 28 | } 29 | 30 | @Test fun genGlobalConstant() { 31 | testIR("genGlobalConstant") 32 | } 33 | 34 | @Test fun genFunction() { 35 | testIR("genFunction") 36 | } 37 | 38 | @Test fun genVariableDeclaration() { 39 | testIR("genVariableDeclaration") 40 | } 41 | 42 | @Test fun genComplexExpressions() { 43 | testIR("genComplexExpressions") 44 | } 45 | 46 | @Test fun genVariableReassignment() { 47 | testIR("genVariableReassignment") 48 | } 49 | 50 | @Test fun genIfStatement() { 51 | testIR("genIfStatement") 52 | } 53 | 54 | @Test fun genGlobalsInFunctions() { 55 | testIR("genGlobalsInFunctions") 56 | } 57 | 58 | @Test fun genOperators() { 59 | testIR("genOperators") 60 | } 61 | 62 | @Test fun genWhileLoop() { 63 | testIR("genWhileLoop") 64 | } 65 | 66 | @Test fun genComplexExpressionsInWhileLoop() { 67 | testIR("genComplexExpressionsInWhileLoop") 68 | } 69 | 70 | @Test fun genInfixFunctionCall() { 71 | testIR("genInfixFunctionCall") 72 | } 73 | 74 | @Test fun genRecursiveCall() { 75 | testIR("genRecursiveCall") 76 | } 77 | 78 | @Test fun genRecursiveImport() { 79 | testIR("genRecursiveInput1", "genRecursiveInput2") 80 | } 81 | 82 | @Test fun genExternalFunction() { 83 | testIR("genExternalFunction") 84 | } 85 | 86 | @Test fun genImportExternalFunction() { 87 | testIR("genExternalFunction", "genImportExternalFunction") 88 | } 89 | 90 | @Test fun genExecutable() { 91 | testIR("genExecutable") 92 | } 93 | 94 | @Test fun outputFactorial() { 95 | testIR("outputFactorial") 96 | } 97 | 98 | @Test fun classDeclaration() { 99 | testIR("classDeclaration") 100 | } 101 | 102 | @Test fun differentIntTypes() { 103 | testIR("differentIntTypes") 104 | } 105 | 106 | @Test fun lotsOfAllocations() { 107 | testIR("lotsOfAllocations") 108 | } 109 | 110 | @Test fun differentFloatTypes() { 111 | testIR("differentFloatTypes") 112 | } 113 | 114 | @Test fun elifBranching() { 115 | testIR("elifBranching") 116 | } 117 | 118 | @Test fun importClass() { 119 | testIR("importClass1", "importClass2") 120 | } 121 | 122 | @Test fun classInClass() { 123 | testIR("classInClass") 124 | } 125 | 126 | @Test fun allocationInLoop() { 127 | testIR("allocationInLoop") 128 | } 129 | 130 | @Test fun allocationInIf() { 131 | testIR("allocationInIf") 132 | } 133 | 134 | @Test fun allocationPutInField() { 135 | testIR("allocationPutInField") 136 | } 137 | 138 | @Test fun copyClass() { 139 | testIR("copyClass") 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /src/test/kotlin/xyz/jadonfowler/compiler/ParallelSuite.kt: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.compiler 2 | 3 | import org.junit.runners.Suite 4 | import org.junit.runners.model.RunnerBuilder 5 | import org.junit.runners.model.RunnerScheduler 6 | import java.util.concurrent.Executors 7 | import java.util.concurrent.TimeUnit 8 | 9 | class ParallelSuite(klass: Class<*>, builder: RunnerBuilder) : Suite(klass, builder) { 10 | 11 | init { 12 | setScheduler(object : RunnerScheduler { 13 | val service = Executors.newFixedThreadPool(16) 14 | 15 | override fun schedule(childStatement: Runnable) { 16 | service.submit(childStatement) 17 | } 18 | 19 | override fun finished() { 20 | try { 21 | service.shutdown() 22 | service.awaitTermination(java.lang.Long.MAX_VALUE, TimeUnit.NANOSECONDS) 23 | } catch (e: InterruptedException) { 24 | e.printStackTrace() 25 | } 26 | 27 | } 28 | }) 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/test/kotlin/xyz/jadonfowler/compiler/SemanticsTest.kt: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.compiler 2 | 3 | import org.junit.Test 4 | import xyz.jadonfowler.compiler.pass.SemanticAnalysis 5 | import xyz.jadonfowler.compiler.pass.TypePass 6 | import kotlin.test.assertTrue 7 | 8 | class SemanticsTest { 9 | 10 | @Test fun mutableGlobalVariable() { 11 | val code = """ 12 | var a = 7 13 | var b = 8 14 | let c = 9 15 | """ 16 | val module = compileString("mutableGlobalVariable", code) 17 | 18 | SemanticAnalysis(module) 19 | module.errors.forEach(::println) 20 | assertTrue(module.errors.size > 0) 21 | } 22 | 23 | @Test fun primitiveCopying() { 24 | val code = """ 25 | f () : Int 26 | let a = 7, 27 | let b = a, 28 | let c = a + b, 29 | 0 30 | """ 31 | val module = compileString("primitiveCopying", code) 32 | 33 | TypePass(module) 34 | SemanticAnalysis(module) 35 | module.errors.forEach(::println) 36 | assertTrue(module.errors.size == 0) 37 | } 38 | 39 | @Test fun referenceUsing() { 40 | val code = """ 41 | class P 42 | let x : Int 43 | 44 | init (v : Int) 45 | x = v 46 | ; 47 | 48 | main () : Int 49 | let a = new P(7), 50 | let b = a, 51 | let c = a.x + b.x, 52 | 0 53 | """ 54 | val module = compileString("referenceUsing", code) 55 | 56 | TypePass(module) 57 | SemanticAnalysis(module) 58 | module.errors.forEach(::println) 59 | assertTrue(module.errors.size > 0) 60 | } 61 | 62 | @Test fun copyableClass() { 63 | val code = """ 64 | class P : Copy 65 | let x : Int 66 | 67 | init (v : Int) 68 | x = v 69 | ; 70 | 71 | main () : Int 72 | let a = new P(7), 73 | let b = a, 74 | let c = a.x + b.x, 75 | 0 76 | """ 77 | 78 | val module = compileString("copyableClass", code) 79 | 80 | TypePass(module) 81 | SemanticAnalysis(module) 82 | module.errors.forEach(::println) 83 | assertTrue(module.errors.size == 0) 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/test/kotlin/xyz/jadonfowler/compiler/TypeCheckingTest.kt: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.compiler 2 | 3 | import org.junit.Test 4 | import xyz.jadonfowler.compiler.ast.* 5 | import xyz.jadonfowler.compiler.pass.PrintPass 6 | import xyz.jadonfowler.compiler.pass.TypePass 7 | import kotlin.test.assertEquals 8 | import kotlin.test.assertFails 9 | import kotlin.test.assertTrue 10 | 11 | class TypeCheckingTest { 12 | 13 | @Test fun inferGlobalVariableTypes() { 14 | val code = """ 15 | let a = 7 16 | """ 17 | val module = compileString("inferGlobalVariableTypes", code) 18 | TypePass(module) 19 | 20 | assertEquals(T_INT32, module.globalVariables[0].type) 21 | 22 | println(PrintPass(module).output) 23 | } 24 | 25 | @Test fun inferLocalVariableTypes() { 26 | val code = """ 27 | test (a, b, c : Int) : Int 28 | let d = a + b, 29 | let e = "test", 30 | let f = true, 31 | 7 + c 32 | """ 33 | val module = compileString("inferLocalVariableTypes", code) 34 | TypePass(module) 35 | 36 | assertEquals(3, module.globalFunctions[0].statements.size) 37 | val statements = module.globalFunctions[0].statements 38 | assertEquals(T_INT32, (statements[0] as VariableDeclarationStatement).variable.type) 39 | assertEquals(T_STRING, (statements[1] as VariableDeclarationStatement).variable.type) 40 | assertEquals(T_BOOL, (statements[2] as VariableDeclarationStatement).variable.type) 41 | 42 | println(PrintPass(module).output) 43 | } 44 | 45 | @Test fun inferFunctionReturnType() { 46 | val code = """ 47 | test (a, b, c : Int) 48 | let d = a + b, 49 | let e = b * d - a + b, 50 | let f = e / b * d - a + b + c * c, 51 | 7 + f 52 | """ 53 | val module = compileString("inferFunctionReturnType", code) 54 | TypePass(module) 55 | 56 | assertEquals(T_INT32, module.globalFunctions[0].returnType) 57 | 58 | println(PrintPass(module).output) 59 | } 60 | 61 | @Test fun incorrectTypeSigOnGlobalVariable() { 62 | val code = """ 63 | let a : String = 3 64 | """ 65 | val module = compileString("incorrectTypeSigOnGlobalVariable", code) 66 | 67 | TypePass(module) 68 | assertTrue(module.errors.size > 0) 69 | module.errors.forEach(::println) 70 | 71 | println(PrintPass(module).output) 72 | } 73 | 74 | @Test fun incorrectTypeSigOnLocalVariable() { 75 | val code = """ 76 | test () : Int 77 | let a : Void = "test", 78 | 6 79 | """ 80 | val module = compileString("incorrectTypeSigOnLocalVariable", code) 81 | 82 | TypePass(module) 83 | assertTrue(module.errors.size > 0) 84 | module.errors.forEach(::println) 85 | 86 | println(PrintPass(module).output) 87 | } 88 | 89 | @Test fun incorrectReassignmentType() { 90 | val code = """ 91 | test () : Int 92 | let a = 7, 93 | a = "test", 94 | 6 95 | """ 96 | val module = compileString("incorrectReassignmentType", code) 97 | 98 | TypePass(module) 99 | assertTrue(module.errors.size > 0) 100 | module.errors.forEach(::println) 101 | 102 | println(PrintPass(module).output) 103 | } 104 | 105 | @Test fun correctTypeMarkingAndInference() { 106 | val code = """ 107 | let d = 3 + 2 108 | let e = 0 109 | let f : Int 110 | let h = 6 111 | let i : Int = 7 112 | let j : Int = 8 113 | let str = "test" 114 | 115 | llvm (z, y, x, w : Int) 116 | var v = 42 + x, 117 | let u = 45 + v * 67 + 124 - (w * 4) / 5, 118 | v = v * 2 - z, 119 | 5 + u * z * v 120 | 121 | foo (t, s : Int) 122 | let a = 7, 123 | let b = 9, 124 | let r : Int = 90128, 125 | if 1 != (2 + 2) 126 | var q = s, 127 | if 2 != 14 * 7 - 5 128 | q = t 129 | else 130 | if 4 >= 2 131 | q = 7, 132 | if s >= t || s <= 8 133 | print(t, s) 134 | ; 135 | ; 136 | ; 137 | ; 138 | thing(a + b, a - b * g), 139 | a + b + 1 140 | 141 | class Object 142 | 143 | let field : Int = 0 144 | 145 | method (arg : Int) 146 | let local : Int = arg + 7, 147 | let thing : Int = local * 5, 148 | local / thing 149 | ; 150 | 151 | let variable_defined_after_class : Int = 0 152 | """ 153 | val module = compileString("correctTypeMarkingAndInference", code) 154 | TypePass(module) 155 | 156 | assertEquals(T_INT32, module.globalClasses[0].methods[0].returnType) 157 | assertEquals(T_INT32, module.globalFunctions[0].returnType) 158 | assertEquals(T_INT32, ((module.globalFunctions[1].statements[3] as IfStatement).statements[0] 159 | as VariableDeclarationStatement).variable.type) 160 | 161 | println(PrintPass(module).output) 162 | } 163 | 164 | @Test fun incorrectConditionTypeInIfStatement() { 165 | val code = """ 166 | test () : Int 167 | let a = 7, 168 | if a 169 | let b = 7 170 | ; 171 | 6 172 | """ 173 | val module = compileString("incorrectConditionTypeInIfStatement", code) 174 | 175 | TypePass(module) 176 | assertTrue(module.errors.size > 0) 177 | module.errors.forEach(::println) 178 | 179 | println(PrintPass(module).output) 180 | } 181 | 182 | @Test fun incorrectConditionTypeInWhileStatement() { 183 | val code = """ 184 | test () : Int 185 | let a = 7, 186 | while a 187 | let b = 7 188 | ; 189 | 6 190 | """ 191 | val module = compileString("incorrectConditionTypeInWhileStatement", code) 192 | 193 | TypePass(module) 194 | assertTrue(module.errors.size > 0) 195 | module.errors.forEach(::println) 196 | 197 | println(PrintPass(module).output) 198 | } 199 | 200 | @Test fun inferVoidReturnType() { 201 | val code = """ 202 | test1 () 203 | let a = 7 204 | 205 | test2 (a : Int) 206 | if a < 10 207 | doWap() 208 | else 209 | damn() 210 | ; 211 | """ 212 | val module = compileString("inferVoidReturnType", code) 213 | TypePass(module) 214 | 215 | assertEquals(T_VOID, module.globalFunctions[0].returnType) 216 | assertEquals(T_VOID, module.globalFunctions[1].returnType) 217 | 218 | println(PrintPass(module).output) 219 | } 220 | 221 | @Test fun incorrectTypeSigForVoidFunction() { 222 | val code = """ 223 | test () : Int 224 | let a = 7, 225 | while a 226 | let b = 7 227 | ; 228 | """ 229 | val module = compileString("incorrectTypeSigForVoidFunction", code) 230 | 231 | TypePass(module) 232 | assertTrue(module.errors.size > 0) 233 | module.errors.forEach(::println) 234 | 235 | println(PrintPass(module).output) 236 | } 237 | 238 | @Test fun inferReturnTypeForFunctions() { 239 | val code = """ 240 | funA (a : Int) : Int 241 | a + 1 242 | 243 | funB (a : Int) : Int 244 | let b = funA(a), 245 | b * 2 246 | """ 247 | val module = compileString("inferReturnTypeForFunctions", code) 248 | TypePass(module) 249 | 250 | assertEquals(T_INT32, (module.globalFunctions[1].statements[0] as VariableDeclarationStatement).variable.type) 251 | 252 | println(PrintPass(module).output) 253 | } 254 | 255 | @Test fun incorrectFunctionCallArguments() { 256 | val code = """ 257 | funA (a : Int) : Int 258 | a + 1 259 | 260 | funB (a : Int) : Int 261 | let b = funA(true), 262 | b * 2 263 | """ 264 | val module = compileString("incorrectFunctionCallArguments", code) 265 | 266 | TypePass(module) 267 | assertTrue(module.errors.size > 0) 268 | module.errors.forEach(::println) 269 | 270 | println(PrintPass(module).output) 271 | } 272 | 273 | @Test fun inferClassTypes() { 274 | val code = """ 275 | class Test 276 | let a : Int = 7 277 | let b : Bool = true 278 | ; 279 | 280 | test (a : Test) : Test 281 | a 282 | """ 283 | val module = compileString("inferClassTypes", code) 284 | TypePass(module) 285 | 286 | assertTrue(module.globalFunctions[0].returnType is Clazz, "${module.globalFunctions[0].returnType} is not a Class!") 287 | assertEquals("Test", (module.globalFunctions[0].returnType as Clazz).name) 288 | 289 | println(PrintPass(module).output) 290 | } 291 | 292 | @Test fun inferFieldTypes() { 293 | val code = """ 294 | class Test 295 | let a : Int = 7 296 | let b : Bool = true 297 | ; 298 | 299 | test (thing : Test) 300 | thing.a 301 | """ 302 | val module = compileString("inferFieldTypes", code) 303 | TypePass(module) 304 | 305 | assertEquals(T_INT32, module.globalFunctions[0].returnType, "${module.globalFunctions[0].returnType} is not an Int!") 306 | 307 | println(PrintPass(module).output) 308 | } 309 | 310 | @Test fun incorrectFieldSetterType() { 311 | val code = """ 312 | class Test 313 | let a : Int = 7 314 | let b : Bool = true 315 | ; 316 | 317 | test (thing : Test) 318 | thing.a = thing 319 | """ 320 | val module = compileString("incorrectFieldSetterType", code) 321 | 322 | TypePass(module) 323 | assertTrue(module.errors.size > 0) 324 | module.errors.forEach(::println) 325 | 326 | println(PrintPass(module).output) 327 | } 328 | 329 | @Test fun propagateFormalTypes() { 330 | val code = """ 331 | add (a, b : Int) 332 | a + b 333 | """ 334 | val module = compileString("propagateFormalTypes", code) 335 | TypePass(module) 336 | 337 | assertEquals(T_INT32, module.globalFunctions[0].returnType, "${module.globalFunctions[0].returnType} is not an Int!") 338 | 339 | println(PrintPass(module).output) 340 | } 341 | 342 | @Test fun inferFormalTypes() { 343 | val code = """ 344 | add (a, b) 345 | a + b 346 | """ 347 | val module = compileString("inferFormalTypes", code) 348 | TypePass(module) 349 | 350 | assertEquals(T_INT32, module.globalFunctions[0].formals[0].type, "${module.globalFunctions[0].formals[0].name} is not an Int!") 351 | assertEquals(T_INT32, module.globalFunctions[0].formals[1].type, "${module.globalFunctions[0].formals[1].name} is not an Int!") 352 | assertEquals(T_INT32, module.globalFunctions[0].returnType, "${module.globalFunctions[0].returnType} is not an Int!") 353 | 354 | println(PrintPass(module).output) 355 | } 356 | 357 | @Test fun floatTypes() { 358 | val code = """ 359 | add (a, b) 360 | a +. b 361 | """ 362 | val module = compileString("floatTypes", code) 363 | TypePass(module) 364 | 365 | assertEquals(T_FLOAT32, module.globalFunctions[0].formals[0].type, "${module.globalFunctions[0].formals[0].name} is not a Float32!") 366 | assertEquals(T_FLOAT32, module.globalFunctions[0].formals[1].type, "${module.globalFunctions[0].formals[1].name} is not a Float32!") 367 | assertEquals(T_FLOAT32, module.globalFunctions[0].returnType, "${module.globalFunctions[0].returnType} is not a Float32!") 368 | 369 | println(PrintPass(module).output) 370 | } 371 | 372 | @Test fun biggerFloatTypes() { 373 | val code = """ 374 | add (a : Float64, b : Float128) 375 | a +. b 376 | """ 377 | val module = compileString("biggerFloatTypes", code) 378 | TypePass(module) 379 | 380 | assertEquals(T_FLOAT64, module.globalFunctions[0].formals[0].type, "${module.globalFunctions[0].formals[0].name} is not a Float32!") 381 | assertEquals(T_FLOAT128, module.globalFunctions[0].formals[1].type, "${module.globalFunctions[0].formals[1].name} is not a Float32!") 382 | assertEquals(T_FLOAT128, module.globalFunctions[0].returnType, "${module.globalFunctions[0].returnType} is not a Float32!") 383 | 384 | println(PrintPass(module).output) 385 | } 386 | 387 | @Test fun inferFloatLiterals() { 388 | val code = """ 389 | thing () 390 | 52.76d +. 215.0 391 | """ 392 | val module = compileString("inferFloatLiterals", code) 393 | TypePass(module) 394 | 395 | assertEquals(T_FLOAT64, module.globalFunctions[0].returnType, "${module.globalFunctions[0].returnType} is not a Float32!") 396 | 397 | println(PrintPass(module).output) 398 | } 399 | 400 | @Test fun incorrectFloatLiteral() { 401 | val code = "let a : Float32 = 7.0d" 402 | val module = compileString("incorrectFloatLiteral", code) 403 | 404 | TypePass(module) 405 | assertTrue(module.errors.size > 0) 406 | module.errors.forEach(::println) 407 | 408 | println(PrintPass(module).output) 409 | } 410 | 411 | } 412 | -------------------------------------------------------------------------------- /std/io.l: -------------------------------------------------------------------------------- 1 | @extern printInt (a : Int32) : Int32 0 2 | @extern(castDownAndPrintInt64) printInt64 (a : Int64) : Int32 0 3 | 4 | @extern printFloat32 (a : Float32) : Int32 0 5 | @extern printFloat64 (a : Float64) : Int32 0 6 | @extern(castDownAndPrintFloat128) printFloat128 (a : Float128) : Int32 0 7 | -------------------------------------------------------------------------------- /std/llvm/fpmath.ll: -------------------------------------------------------------------------------- 1 | define float @sqrtF32(float) { 2 | %ret = call float @llvm.sqrt.f32(float %0) 3 | ret float %ret 4 | } 5 | 6 | define double @sqrtF64(double) { 7 | %ret = call double @llvm.sqrt.f64(double %0) 8 | ret double %ret 9 | } 10 | 11 | define fp128 @sqrtF128(fp128) { 12 | %ret = call fp128 @llvm.sqrt.f128(fp128 %0) 13 | ret fp128 %ret 14 | } 15 | 16 | declare float @llvm.sqrt.f32(float %Val) 17 | declare double @llvm.sqrt.f64(double %Val) 18 | declare fp128 @llvm.sqrt.f128(fp128 %Val) 19 | -------------------------------------------------------------------------------- /std/llvm/io.ll: -------------------------------------------------------------------------------- 1 | declare i32 @printf(i8*, ...) 2 | 3 | @int_format = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1 4 | 5 | @f32_format = private unnamed_addr constant [6 x i8] c"%.9g\0A\00", align 1 6 | @f64_format = private unnamed_addr constant [7 x i8] c"%.17g\0A\00", align 1 7 | 8 | define i32 @printInt(i32 %i) nounwind uwtable alwaysinline optsize { 9 | %1 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @int_format, i64 0, i64 0), i32 %i) 10 | ret i32 %1 11 | } 12 | 13 | define i32 @castDownAndPrintInt64(i64 %i) nounwind uwtable alwaysinline optsize { 14 | %castDown = trunc i64 %i to i32 15 | %1 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @int_format, i64 0, i64 0), i32 %castDown) #0 16 | ret i32 %1 17 | } 18 | 19 | define i32 @printFloat32(float %a) { 20 | %1 = fpext float %a to double 21 | %2 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @f32_format, i64 0, i64 0), double %1) 22 | ret i32 0 23 | } 24 | 25 | define i32 @printFloat64(double %a) { 26 | %1 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @f64_format, i64 0, i64 0), double %a) 27 | ret i32 0 28 | } 29 | 30 | ; Function Attrs: nounwind uwtable 31 | define i32 @castDownAndPrintFloat128(fp128 %a) #0 { 32 | %down = fptrunc fp128 %a to double 33 | %1 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @f64_format, i64 0, i64 0), double %down) 34 | ret i32 0 35 | } 36 | -------------------------------------------------------------------------------- /std/llvm/main.ll: -------------------------------------------------------------------------------- 1 | declare i32 @real_main() 2 | 3 | declare void @srand(i32) 4 | 5 | declare i64 @time(i64*) 6 | 7 | define i32 @main() { 8 | ; set random seed to current time 9 | %time = tail call i64 @time(i64* null) 10 | %seed = trunc i64 %time to i32 11 | tail call void @srand(i32 %seed) 12 | 13 | ; call main from user program 14 | %1 = tail call i32 @real_main() 15 | ret i32 %1 16 | } 17 | -------------------------------------------------------------------------------- /std/math/fp.l: -------------------------------------------------------------------------------- 1 | @extern sqrtF32 (a : Float32) : Float32 0.0 2 | @extern sqrtF64 (a : Float64) : Float64 0.0d 3 | @extern sqrtF128 (a : Float128) : Float128 0.0q 4 | -------------------------------------------------------------------------------- /test/allocationInIf.l: -------------------------------------------------------------------------------- 1 | import std.io 2 | 3 | class Box 4 | let value : Int32 5 | 6 | init (v : Int32) 7 | value = v 8 | ; 9 | 10 | allocate_thing(b : Bool) 11 | var r = 0, 12 | if b 13 | let b = new Box(7), 14 | r = b.value 15 | else 16 | r = 0 17 | ; 18 | r 19 | 20 | 21 | main () 22 | printInt(allocate_thing(true)), 23 | 0 24 | -------------------------------------------------------------------------------- /test/allocationInLoop.l: -------------------------------------------------------------------------------- 1 | class Box 2 | let value : Int32 3 | 4 | init (v : Int32) 5 | value = v 6 | ; 7 | 8 | main () 9 | while true 10 | let b = new Box(7) 11 | ; 12 | 0 13 | -------------------------------------------------------------------------------- /test/allocationPutInField.l: -------------------------------------------------------------------------------- 1 | import std.io 2 | 3 | class B 4 | let v = 7 5 | ; 6 | 7 | class A 8 | var b : B 9 | 10 | init (l : B) 11 | b = l 12 | ; 13 | 14 | t () : A 15 | let b = new B(), 16 | let a = new A(b), 17 | a.b = b, 18 | a 19 | 20 | main () 21 | let a = t(), 22 | printInt(a.b.v), 23 | 0 24 | -------------------------------------------------------------------------------- /test/classDeclaration.l: -------------------------------------------------------------------------------- 1 | import std.io 2 | 3 | class Account 4 | var id : Int32 5 | var amountOfCoins : Int128 6 | 7 | incrementId() : Int32 8 | let previousId = id, 9 | id = id + 1, 10 | previousId 11 | 12 | isAccount(i) : Bool 13 | id == i 14 | 15 | setId(i : Int32) 16 | id = i, 17 | id 18 | ; 19 | 20 | newAccount () : Account 21 | let a = new Account(), 22 | a.id = 7, 23 | a 24 | 25 | getId (a : Account) : Int32 26 | a.id 27 | 28 | main () 29 | let a = newAccount(), 30 | let previousId = a.incrementId(), 31 | var i = 0, 32 | while i < 10 33 | printInt(a.incrementId()), 34 | let b = 7, 35 | a.setId(a.id + 7), 36 | i = i + 1 37 | ; 38 | printInt(previousId), 39 | printInt(getId(a)), 40 | 0 41 | -------------------------------------------------------------------------------- /test/classInClass.l: -------------------------------------------------------------------------------- 1 | import std.io 2 | 3 | class Accumulator 4 | let a = 0 5 | 6 | init (initialValue : Int32) 7 | a = initialValue 8 | ; 9 | 10 | class CPU 11 | 12 | let acc = new Accumulator(1) 13 | let otherAcc = new Accumulator(2) 14 | 15 | za () 16 | acc.a = 0 17 | 18 | ia () 19 | acc.a = acc.a + 1 20 | 21 | ga () 22 | acc.a 23 | 24 | ; 25 | 26 | main () 27 | let cpu = new CPU(), 28 | cpu.ia(), 29 | cpu.ia(), 30 | cpu.acc.a = 7 - cpu.acc.a, 31 | printInt(cpu.ga()), 32 | 0 33 | -------------------------------------------------------------------------------- /test/copyClass.l: -------------------------------------------------------------------------------- 1 | import std.io 2 | 3 | class P : Copy 4 | let x : Int = 4 5 | let y : Int = 7 6 | ; 7 | 8 | main () 9 | let a = new P(), 10 | let b = a, 11 | b.y = 5, 12 | printInt(a.x), 13 | printInt(a.y), 14 | printInt(b.x), 15 | printInt(b.y), 16 | 0 17 | -------------------------------------------------------------------------------- /test/differentFloatTypes.l: -------------------------------------------------------------------------------- 1 | import std.math.fp, std.io 2 | 3 | addFloat32To64 (a : Float32, b : Float64) 4 | a +. b 5 | 6 | addFloat64To128 (a : Float64, b : Float128) 7 | a +. b 8 | 9 | addFloat32To128 (a : Float32, b : Float128) 10 | a +. b 11 | 12 | main () 13 | printFloat32(sqrtF32(3.0)), 14 | printFloat64(sqrtF64(5.0d)), 15 | printFloat64(sqrtF64(99.99d)), 16 | 0 17 | -------------------------------------------------------------------------------- /test/differentIntTypes.l: -------------------------------------------------------------------------------- 1 | import std.io 2 | 3 | woah (a : Int128, b : Int64) : Int128 4 | a + b 5 | 6 | add64s (a : Int64, b : Int64) : Int64 7 | a + b 8 | 9 | addAndPrint64 (a : Int64, b : Int64) : Int32 10 | printInt64(add64s(a, b)) -------------------------------------------------------------------------------- /test/elifBranching.l: -------------------------------------------------------------------------------- 1 | import std.io 2 | 3 | @extern rand() : Int32 0 4 | 5 | main () 6 | let i = rand(), 7 | printInt(i), 8 | if i == 1 9 | printInt(1) 10 | elif i == 2 11 | printInt(2) 12 | else 13 | printInt(3) 14 | ; 15 | 0 -------------------------------------------------------------------------------- /test/genComplexExpressions.l: -------------------------------------------------------------------------------- 1 | genComplexExpressions (z, y, x, w : Int32) 2 | var v = 42 + x, 3 | let u = 45 + v * 67 + 124 - (w * 4) / 5, 4 | let d = y * 2 - z, 5 | 5 + u * z * v + d 6 | -------------------------------------------------------------------------------- /test/genComplexExpressionsInWhileLoop.l: -------------------------------------------------------------------------------- 1 | genComplexExpressionsInWhileLoop (a, z, y, x, w : Int32) 2 | var i = 0, 3 | var sum = 0, 4 | while i < a 5 | var v = 42 + x, 6 | let u = 45 * 7 - 16 + 11 + v * 67 + 124 - (w * 4) / 5, 7 | v = v * 2 - z, 8 | var t = 1, 9 | if z < 10 10 | t = v * z + 24 - 15 + y 11 | else 12 | t = v - 7 + 8 + 8 - z 13 | ; 14 | let l = 74 * 3 - v + z * x - w, 15 | i = 5 + u * z * v + t * 2 * l 16 | ; 17 | let r = sum * i, 18 | r 19 | -------------------------------------------------------------------------------- /test/genExecutable.l: -------------------------------------------------------------------------------- 1 | import std.io 2 | 3 | main () : Int32 4 | printInt(7), 5 | 0 6 | -------------------------------------------------------------------------------- /test/genExternalFunction.l: -------------------------------------------------------------------------------- 1 | @extern printInt(a : Int32) : Int32 0 2 | -------------------------------------------------------------------------------- /test/genFunction.l: -------------------------------------------------------------------------------- 1 | genFunction (a : Int32, b : Int32, c : Int32) 2 | 0 3 | -------------------------------------------------------------------------------- /test/genGlobalConstant.l: -------------------------------------------------------------------------------- 1 | let a = 7 2 | -------------------------------------------------------------------------------- /test/genGlobalsInFunctions.l: -------------------------------------------------------------------------------- 1 | let G0 = 1234 + 4321 2 | let G1 = 1 + 2 - 3 * 4 + 6 / 6 3 | let G2 = 4 4 | 5 | genGlobalsInFunctions (z, y, x, w : Int32) 6 | var v = 42 + x - y, 7 | let u = 45 + v * 67 + 124 - (w * 4) / 5, 8 | v = v * 2 - z, 9 | var t = 1, 10 | if z < 10 11 | t = v * z 12 | else 13 | t = v - z 14 | ; 15 | let l = 74 * 3 - u + z, 16 | 5 + u * z * v + t * G2 - G0 * G1 + 2 * l 17 | 18 | returnGlobal () 19 | G0 20 | -------------------------------------------------------------------------------- /test/genIfStatement.l: -------------------------------------------------------------------------------- 1 | genIfStatement (a, b, c : Int32) 2 | var r = 0, 3 | if a == 10 4 | r = b 5 | else 6 | r = c 7 | ; 8 | r 9 | -------------------------------------------------------------------------------- /test/genImportExternalFunction.l: -------------------------------------------------------------------------------- 1 | import test.genExternalFunction 2 | 3 | main () : Int32 4 | printInt(7), 5 | 0 6 | -------------------------------------------------------------------------------- /test/genInfixFunctionCall.l: -------------------------------------------------------------------------------- 1 | add (a, b : Int32) 2 | a + b 3 | 4 | multiply (x, y : Int32) 5 | var mul = 0, 6 | var i = 0, 7 | while i < x 8 | i = i + 1, 9 | mul = mul `add` y 10 | ; 11 | mul 12 | -------------------------------------------------------------------------------- /test/genOperators.l: -------------------------------------------------------------------------------- 1 | genOperators (a, b : Int32) : Int32 2 | var r = 0, 3 | let c : Bool = a != b, 4 | if c 5 | r = r + 10 6 | else 7 | if a > b 8 | r = r + 11 9 | else 10 | if a <= b 11 | r = r + 12 12 | ; 13 | ; 14 | ; 15 | r 16 | -------------------------------------------------------------------------------- /test/genRecursiveCall.l: -------------------------------------------------------------------------------- 1 | fac (n) 2 | var r = 0, 3 | if n <=1 4 | r = 1 5 | else 6 | r = n * fac(n - 1) 7 | ; 8 | r 9 | -------------------------------------------------------------------------------- /test/genRecursiveInput1.l: -------------------------------------------------------------------------------- 1 | import test.genRecursiveInput2 2 | 3 | test1 () : Int32 4 | test4() + 1 5 | 6 | test3 () : Int32 7 | test2(test1()) 8 | -------------------------------------------------------------------------------- /test/genRecursiveInput2.l: -------------------------------------------------------------------------------- 1 | import test.genRecursiveInput1 2 | 3 | test2 (a : Int32) : Int32 4 | test3() + a 5 | 6 | test4 () : Int32 7 | 9 8 | -------------------------------------------------------------------------------- /test/genVariableDeclaration.l: -------------------------------------------------------------------------------- 1 | genVariableDeclaration () 2 | let a = 5, 3 | 0 4 | -------------------------------------------------------------------------------- /test/genVariableReassignment.l: -------------------------------------------------------------------------------- 1 | genVariableReassignment () 2 | var a = 0, 3 | a = 1, 4 | a = 2, 5 | a = 3, 6 | a 7 | -------------------------------------------------------------------------------- /test/genWhileLoop.l: -------------------------------------------------------------------------------- 1 | genWhileLoop (a : Int32) 2 | var i = 0, 3 | var sum = 0, 4 | while i < a 5 | i = i + 1, 6 | sum = sum + a 7 | ; 8 | let r = sum * i, 9 | r 10 | -------------------------------------------------------------------------------- /test/importClass1.l: -------------------------------------------------------------------------------- 1 | let INITIAL_ACCUMULATOR_VALUE = 1234 2 | 3 | class Accumulator 4 | 5 | let a : Int32 6 | 7 | add (b : Int32) 8 | a = a + b 9 | 10 | sub (b : Int32) 11 | a = a - b 12 | 13 | mul (b : Int32) 14 | a = a * b 15 | 16 | div (b : Int32) 17 | a = a / b 18 | 19 | ; 20 | -------------------------------------------------------------------------------- /test/importClass2.l: -------------------------------------------------------------------------------- 1 | import std.io 2 | import test.importClass1 3 | 4 | main () 5 | let a = new Accumulator(), 6 | a.a = INITIAL_ACCUMULATOR_VALUE, 7 | a.add(1), 8 | a.mul(7), 9 | printInt(a.a), 10 | 0 -------------------------------------------------------------------------------- /test/lotsOfAllocations.l: -------------------------------------------------------------------------------- 1 | import std.io 2 | 3 | class Account 4 | var id : Int32 5 | var amountOfCoins : Int128 6 | 7 | incrementId() : Int32 8 | let previousId = id, 9 | id = id + 1, 10 | previousId 11 | ; 12 | 13 | main () 14 | let a = new Account(), 15 | a.id = 1, 16 | let b = new Account(), 17 | b.id = 2, 18 | let c = new Account(), 19 | c.id = 3, 20 | let d = new Account(), 21 | d.id = 4, 22 | let e = new Account(), 23 | e.id = 5, 24 | let f = new Account(), 25 | f.id = 6, 26 | let g = new Account(), 27 | g.id = 7, 28 | let ret = a.id + b.id + c.id + d.id + e.id + f.id + g.id, 29 | printInt(ret), 30 | 0 31 | -------------------------------------------------------------------------------- /test/out/llvm/allocationInIf.ll: -------------------------------------------------------------------------------- 1 | define void @Box_init(%Box*, i32) { 2 | entry: 3 | %value = getelementptr inbounds %Box, %Box* %0, i64 0, i32 0 4 | store i32 %1, i32* %value, align 4 5 | ret void 6 | } 7 | 8 | define i32 @allocate_thing(i1) { 9 | entry: 10 | br i1 %0, label %"if.t b", label %"if.o b" 11 | 12 | "if.t b": ; preds = %entry 13 | %"malloc(4) for Box" = call i8* @malloc(i64 4) 14 | %castToBox = bitcast i8* %"malloc(4) for Box" to %Box* 15 | call void @Box_init(%Box* %castToBox, i32 7) 16 | %b = bitcast i8* %"malloc(4) for Box" to i32* 17 | %b.value = load i32, i32* %b, align 4 18 | call void @free(i8* %"malloc(4) for Box") 19 | br label %"if.o b" 20 | 21 | "if.o b": ; preds = %entry, %"if.t b" 22 | %r.0 = phi i32 [ %b.value, %"if.t b" ], [ 0, %entry ] 23 | ret i32 %r.0 24 | } 25 | 26 | define i32 @real_main() { 27 | entry: 28 | %"allocate_thing(true)" = call i32 @allocate_thing(i1 true) 29 | %"printInt(allocate_thing(true))" = call i32 @printInt(i32 %"allocate_thing(true)") 30 | ret i32 0 31 | } -------------------------------------------------------------------------------- /test/out/llvm/allocationInLoop.ll: -------------------------------------------------------------------------------- 1 | define i32 @real_main() { 2 | entry: 3 | br label %"while.c true" 4 | 5 | "while.c true": ; preds = %"while.c true", %entry 6 | %"malloc(4) for Box" = call i8* @malloc(i64 4) 7 | %castToBox = bitcast i8* %"malloc(4) for Box" to %Box* 8 | call void @Box_init(%Box* %castToBox, i32 7) 9 | call void @free(i8* %"malloc(4) for Box") 10 | br label %"while.c true" 11 | } -------------------------------------------------------------------------------- /test/out/llvm/allocationPutInField.ll: -------------------------------------------------------------------------------- 1 | define void @A_init(%A*, %B*) { 2 | entry: 3 | %b = getelementptr inbounds %A, %A* %0, i64 0, i32 0 4 | store %B* %1, %B** %b, align 8 5 | ret void 6 | } 7 | 8 | define %A* @t() { 9 | entry: 10 | %"malloc(4) for B" = call i8* @malloc(i64 4) 11 | %castToB = bitcast i8* %"malloc(4) for B" to %B* 12 | %v = bitcast i8* %"malloc(4) for B" to i32* 13 | store i32 7, i32* %v, align 4 14 | %"malloc(4) for A" = call i8* @malloc(i64 4) 15 | %castToA = bitcast i8* %"malloc(4) for A" to %A* 16 | call void @A_init(%A* %castToA, %B* %castToB) 17 | %0 = bitcast i8* %"malloc(4) for A" to i8** 18 | store i8* %"malloc(4) for B", i8** %0, align 8 19 | ret %A* %castToA 20 | } 21 | 22 | define i32 @real_main() { 23 | entry: 24 | %"t()" = call %A* @t() 25 | %a = getelementptr inbounds %A, %A* %"t()", i64 0, i32 0 26 | %a.b = load %B*, %B** %a, align 8 27 | %a.b1 = getelementptr inbounds %B, %B* %a.b, i64 0, i32 0 28 | %a.b.v = load i32, i32* %a.b1, align 4 29 | %"printInt(a.b.v)" = call i32 @printInt(i32 %a.b.v) 30 | %0 = bitcast %A* %"t()" to i8** 31 | %load.b2 = load i8*, i8** %0, align 8 32 | call void @free(i8* %load.b2) 33 | %bitPointerToa = bitcast %A* %"t()" to i8* 34 | call void @free(i8* %bitPointerToa) 35 | ret i32 0 36 | } -------------------------------------------------------------------------------- /test/out/llvm/classDeclaration.ll: -------------------------------------------------------------------------------- 1 | define i32 @Account_incrementId(%Account*) { 2 | entry: 3 | %id = getelementptr inbounds %Account, %Account* %0, i64 0, i32 0 4 | %id1 = load i32, i32* %id, align 4 5 | %"(id + 1)" = add i32 %id1, 1 6 | store i32 %"(id + 1)", i32* %id, align 4 7 | ret i32 %id1 8 | } 9 | 10 | define i1 @Account_isAccount(%Account*, i32) { 11 | entry: 12 | %id = getelementptr inbounds %Account, %Account* %0, i64 0, i32 0 13 | %id1 = load i32, i32* %id, align 4 14 | %"(id == i)" = icmp eq i32 %id1, %1 15 | ret i1 %"(id == i)" 16 | } 17 | 18 | define i32 @Account_setId(%Account*, i32) { 19 | entry: 20 | %id = getelementptr inbounds %Account, %Account* %0, i64 0, i32 0 21 | store i32 %1, i32* %id, align 4 22 | ret i32 %1 23 | } 24 | 25 | define %Account* @newAccount() { 26 | entry: 27 | %"malloc(20) for Account" = call i8* @malloc(i64 20) 28 | %castToAccount = bitcast i8* %"malloc(20) for Account" to %Account* 29 | %"a.id = 7" = bitcast i8* %"malloc(20) for Account" to i32* 30 | store i32 7, i32* %"a.id = 7", align 4 31 | ret %Account* %castToAccount 32 | } 33 | 34 | define i32 @getId(%Account*) { 35 | entry: 36 | %a = getelementptr inbounds %Account, %Account* %0, i64 0, i32 0 37 | %a.id = load i32, i32* %a, align 4 38 | ret i32 %a.id 39 | } 40 | 41 | define i32 @real_main() { 42 | entry: 43 | %"newAccount()" = call %Account* @newAccount() 44 | %"a.incrementId()" = call i32 @Account_incrementId(%Account* %"newAccount()") 45 | br label %"while.c (i < 10)" 46 | 47 | "while.c (i < 10)": ; preds = %"while.b (i < 10)", %entry 48 | %storemerge = phi i32 [ %"(i + 1)", %"while.b (i < 10)" ], [ 0, %entry ] 49 | %"(i < 10)" = icmp slt i32 %storemerge, 10 50 | br i1 %"(i < 10)", label %"while.b (i < 10)", label %"while.o (i < 10)" 51 | 52 | "while.b (i < 10)": ; preds = %"while.c (i < 10)" 53 | %"a.incrementId()2" = call i32 @Account_incrementId(%Account* %"newAccount()") 54 | %"printInt(a.incrementId())" = call i32 @printInt(i32 %"a.incrementId()2") 55 | %a = getelementptr inbounds %Account, %Account* %"newAccount()", i64 0, i32 0 56 | %a.id = load i32, i32* %a, align 4 57 | %"(a.id + 7)" = add i32 %a.id, 7 58 | %"a.setId((a.id + 7))" = call i32 @Account_setId(%Account* %"newAccount()", i32 %"(a.id + 7)") 59 | %"(i + 1)" = add i32 %storemerge, 1 60 | br label %"while.c (i < 10)" 61 | 62 | "while.o (i < 10)": ; preds = %"while.c (i < 10)" 63 | %"printInt(previousId)" = call i32 @printInt(i32 %"a.incrementId()") 64 | %"getId(a)" = call i32 @getId(%Account* %"newAccount()") 65 | %"printInt(getId(a))" = call i32 @printInt(i32 %"getId(a)") 66 | %bitPointerToa = bitcast %Account* %"newAccount()" to i8* 67 | call void @free(i8* %bitPointerToa) 68 | ret i32 0 69 | } -------------------------------------------------------------------------------- /test/out/llvm/classInClass.ll: -------------------------------------------------------------------------------- 1 | define void @Accumulator_init(%Accumulator*, i32) { 2 | entry: 3 | %a = getelementptr inbounds %Accumulator, %Accumulator* %0, i64 0, i32 0 4 | store i32 %1, i32* %a, align 4 5 | ret void 6 | } 7 | 8 | define void @CPU_za(%CPU*) { 9 | entry: 10 | %acc = getelementptr inbounds %CPU, %CPU* %0, i64 0, i32 0 11 | %acc1 = load %Accumulator*, %Accumulator** %acc, align 8 12 | %"acc.a = 0" = getelementptr inbounds %Accumulator, %Accumulator* %acc1, i64 0, i32 0 13 | store i32 0, i32* %"acc.a = 0", align 4 14 | ret void 15 | } 16 | 17 | define void @CPU_ia(%CPU*) { 18 | entry: 19 | %acc = getelementptr inbounds %CPU, %CPU* %0, i64 0, i32 0 20 | %acc1 = load %Accumulator*, %Accumulator** %acc, align 8 21 | %acc4 = getelementptr inbounds %Accumulator, %Accumulator* %acc1, i64 0, i32 0 22 | %acc.a = load i32, i32* %acc4, align 4 23 | %"(acc.a + 1)" = add i32 %acc.a, 1 24 | store i32 %"(acc.a + 1)", i32* %acc4, align 4 25 | ret void 26 | } 27 | 28 | define i32 @CPU_ga(%CPU*) { 29 | entry: 30 | %acc = getelementptr inbounds %CPU, %CPU* %0, i64 0, i32 0 31 | %acc1 = load %Accumulator*, %Accumulator** %acc, align 8 32 | %acc2 = getelementptr inbounds %Accumulator, %Accumulator* %acc1, i64 0, i32 0 33 | %acc.a = load i32, i32* %acc2, align 4 34 | ret i32 %acc.a 35 | } 36 | 37 | define i32 @real_main() { 38 | entry: 39 | %"malloc(8) for CPU" = call i8* @malloc(i64 8) 40 | %castToCPU = bitcast i8* %"malloc(8) for CPU" to %CPU* 41 | %"malloc(4) for Accumulator" = call i8* @malloc(i64 4) 42 | %castToAccumulator = bitcast i8* %"malloc(4) for Accumulator" to %Accumulator* 43 | %a = bitcast i8* %"malloc(4) for Accumulator" to i32* 44 | store i32 0, i32* %a, align 4 45 | call void @Accumulator_init(%Accumulator* %castToAccumulator, i32 1) 46 | %0 = bitcast i8* %"malloc(8) for CPU" to i8** 47 | store i8* %"malloc(4) for Accumulator", i8** %0, align 8 48 | %otherAcc = getelementptr inbounds i8, i8* %"malloc(8) for CPU", i64 8 49 | %"malloc(4) for Accumulator1" = call i8* @malloc(i64 4) 50 | %castToAccumulator2 = bitcast i8* %"malloc(4) for Accumulator1" to %Accumulator* 51 | %a3 = bitcast i8* %"malloc(4) for Accumulator1" to i32* 52 | store i32 0, i32* %a3, align 4 53 | call void @Accumulator_init(%Accumulator* %castToAccumulator2, i32 2) 54 | %1 = bitcast i8* %otherAcc to i8** 55 | store i8* %"malloc(4) for Accumulator1", i8** %1, align 8 56 | call void @CPU_ia(%CPU* %castToCPU) 57 | call void @CPU_ia(%CPU* %castToCPU) 58 | %cpu = bitcast i8* %"malloc(8) for CPU" to %Accumulator** 59 | %cpu.acc = load %Accumulator*, %Accumulator** %cpu, align 8 60 | %cpu.acc6 = getelementptr inbounds %Accumulator, %Accumulator* %cpu.acc, i64 0, i32 0 61 | %cpu.acc.a = load i32, i32* %cpu.acc6, align 4 62 | %"(7 - cpu.acc.a)" = sub i32 7, %cpu.acc.a 63 | store i32 %"(7 - cpu.acc.a)", i32* %cpu.acc6, align 4 64 | %"cpu.ga()" = call i32 @CPU_ga(%CPU* %castToCPU) 65 | %"printInt(cpu.ga())" = call i32 @printInt(i32 %"cpu.ga()") 66 | %load.acc9 = load i8*, i8** %0, align 8 67 | %load.otherAcc10 = load i8*, i8** %1, align 8 68 | call void @free(i8* %load.acc9) 69 | call void @free(i8* %load.otherAcc10) 70 | call void @free(i8* %"malloc(8) for CPU") 71 | ret i32 0 72 | } -------------------------------------------------------------------------------- /test/out/llvm/copyClass.ll: -------------------------------------------------------------------------------- 1 | define i32 @real_main() { 2 | entry: 3 | %"malloc(8) for P" = call i8* @malloc(i64 8) 4 | %x = bitcast i8* %"malloc(8) for P" to i32* 5 | store i32 4, i32* %x, align 4 6 | %y = getelementptr inbounds i8, i8* %"malloc(8) for P", i64 4 7 | %0 = bitcast i8* %y to i32* 8 | store i32 7, i32* %0, align 4 9 | %"malloc(8) for P1" = call i8* @malloc(i64 8) 10 | %copying.a.x.p = bitcast i8* %"malloc(8) for P1" to i32* 11 | %copying.a.x3 = load i32, i32* %x, align 4 12 | store i32 %copying.a.x3, i32* %copying.a.x.p, align 4 13 | %copying.a.y.p = getelementptr inbounds i8, i8* %"malloc(8) for P1", i64 4 14 | %1 = bitcast i8* %copying.a.y.p to i32* 15 | %copying.a.y4 = load i32, i32* %0, align 4 16 | store i32 %copying.a.y4, i32* %1, align 4 17 | store i32 5, i32* %1, align 4 18 | %a.x = load i32, i32* %x, align 4 19 | %"printInt(a.x)" = call i32 @printInt(i32 %a.x) 20 | %a.y = load i32, i32* %0, align 4 21 | %"printInt(a.y)" = call i32 @printInt(i32 %a.y) 22 | %b.x = load i32, i32* %copying.a.x.p, align 4 23 | %"printInt(b.x)" = call i32 @printInt(i32 %b.x) 24 | %b.y = load i32, i32* %1, align 4 25 | %"printInt(b.y)" = call i32 @printInt(i32 %b.y) 26 | call void @free(i8* %"malloc(8) for P") 27 | ret i32 0 28 | } 29 | -------------------------------------------------------------------------------- /test/out/llvm/differentFloatTypes.ll: -------------------------------------------------------------------------------- 1 | define double @addFloat32To64(float, double) { 2 | entry: 3 | %"a extended" = fpext float %0 to double 4 | %"(a +. b)" = fadd double %"a extended", %1 5 | ret double %"(a +. b)" 6 | } 7 | 8 | define fp128 @addFloat64To128(double, fp128) { 9 | entry: 10 | %"a extended" = fpext double %0 to fp128 11 | %"(a +. b)" = fadd fp128 %"a extended", %1 12 | ret fp128 %"(a +. b)" 13 | } 14 | 15 | define fp128 @addFloat32To128(float, fp128) { 16 | entry: 17 | %"a extended" = fpext float %0 to fp128 18 | %"(a +. b)" = fadd fp128 %"a extended", %1 19 | ret fp128 %"(a +. b)" 20 | } 21 | 22 | define i32 @real_main() { 23 | entry: 24 | %"sqrtF32(3.0)" = call float @sqrtF32(float 3.000000e+00) 25 | %"printFloat32(sqrtF32(3.0))" = call i32 @printFloat32(float %"sqrtF32(3.0)") 26 | %"sqrtF64(5.0)" = call double @sqrtF64(double 5.000000e+00) 27 | %"printFloat64(sqrtF64(5.0))" = call i32 @printFloat64(double %"sqrtF64(5.0)") 28 | %"sqrtF64(99.99)" = call double @sqrtF64(double 9.999000e+01) 29 | %"printFloat64(sqrtF64(99.99))" = call i32 @printFloat64(double %"sqrtF64(99.99)") 30 | ret i32 0 31 | } -------------------------------------------------------------------------------- /test/out/llvm/differentIntTypes.ll: -------------------------------------------------------------------------------- 1 | define i128 @woah(i128, i64) { 2 | entry: 3 | %"b extended" = zext i64 %1 to i128 4 | %"(a + b)" = add i128 %"b extended", %0 5 | ret i128 %"(a + b)" 6 | } 7 | 8 | define i64 @add64s(i64, i64) { 9 | entry: 10 | %"(a + b)" = add i64 %0, %1 11 | ret i64 %"(a + b)" 12 | } 13 | 14 | define i32 @addAndPrint64(i64, i64) { 15 | entry: 16 | %"add64s(a, b)" = call i64 @add64s(i64 %0, i64 %1) 17 | %"printInt64(add64s(a, b))" = call i32 @castDownAndPrintInt64(i64 %"add64s(a, b)") 18 | ret i32 %"printInt64(add64s(a, b))" 19 | } -------------------------------------------------------------------------------- /test/out/llvm/elifBranching.ll: -------------------------------------------------------------------------------- 1 | define i32 @real_main() { 2 | entry: 3 | %"rand()" = call i32 @rand() 4 | %"printInt(i)" = call i32 @printInt(i32 %"rand()") 5 | switch i32 %"rand()", label %"if.t true" [ 6 | i32 1, label %"if.t (i == 1)" 7 | i32 2, label %"if.t (i == 2)" 8 | ] 9 | 10 | "if.t (i == 1)": ; preds = %entry 11 | %"printInt(1)" = call i32 @printInt(i32 1) 12 | br label %"if.o (i == 1)" 13 | 14 | "if.o (i == 1)": ; preds = %"if.t (i == 2)", %"if.t true", %"if.t (i == 1)" 15 | ret i32 0 16 | 17 | "if.t (i == 2)": ; preds = %entry 18 | %"printInt(2)" = call i32 @printInt(i32 2) 19 | br label %"if.o (i == 1)" 20 | 21 | "if.t true": ; preds = %entry 22 | %"printInt(3)" = call i32 @printInt(i32 3) 23 | br label %"if.o (i == 1)" 24 | } -------------------------------------------------------------------------------- /test/out/llvm/genComplexExpressions.ll: -------------------------------------------------------------------------------- 1 | define i32 @genComplexExpressions(i32, i32, i32, i32) { 2 | entry: 3 | %"(42 + x)" = add i32 %2, 42 4 | %"(v * 67)" = mul i32 %"(42 + x)", 67 5 | %"(169 + (v * 67))" = add i32 %"(v * 67)", 169 6 | %"(w * 4)" = shl i32 %3, 2 7 | %"((w * 4) / 5)" = sdiv i32 %"(w * 4)", 5 8 | %"((169 + (v * 67)) - ((w * 4) / 5))" = sub i32 %"(169 + (v * 67))", %"((w * 4) / 5)" 9 | %"(y * 2)" = shl i32 %1, 1 10 | %"((y * 2) - z)" = sub i32 %"(y * 2)", %0 11 | %"(u * z)" = mul i32 %"((169 + (v * 67)) - ((w * 4) / 5))", %0 12 | %"((u * z) * v)" = mul i32 %"(u * z)", %"(42 + x)" 13 | %"(5 + ((u * z) * v))" = add i32 %"((u * z) * v)", 5 14 | %"((5 + ((u * z) * v)) + d)" = add i32 %"(5 + ((u * z) * v))", %"((y * 2) - z)" 15 | ret i32 %"((5 + ((u * z) * v)) + d)" 16 | } -------------------------------------------------------------------------------- /test/out/llvm/genComplexExpressionsInWhileLoop.ll: -------------------------------------------------------------------------------- 1 | define i32 @genComplexExpressionsInWhileLoop(i32, i32, i32, i32, i32) { 2 | entry: 3 | br label %"while.c (i < a)" 4 | 5 | "while.c (i < a)": ; preds = %"if.o (z < 10)", %entry 6 | %i.0 = phi i32 [ 0, %entry ], [ %"((5 + ((u * z) * v)) + ((t * 2) * l))", %"if.o (z < 10)" ] 7 | %"(i < a)" = icmp slt i32 %i.0, %0 8 | br i1 %"(i < a)", label %"while.b (i < a)", label %"while.o (i < a)" 9 | 10 | "while.b (i < a)": ; preds = %"while.c (i < a)" 11 | %v = alloca i32, align 4 12 | %"(42 + x)" = add i32 %3, 42 13 | store i32 %"(42 + x)", i32* %v, align 4 14 | %"(v * 67)" = mul i32 %"(42 + x)", 67 15 | %"(434 + (v * 67))" = add i32 %"(v * 67)", 434 16 | %"(w * 4)" = shl i32 %4, 2 17 | %"((w * 4) / 5)" = sdiv i32 %"(w * 4)", 5 18 | %"((434 + (v * 67)) - ((w * 4) / 5))" = sub i32 %"(434 + (v * 67))", %"((w * 4) / 5)" 19 | %"(v * 2)" = shl i32 %"(42 + x)", 1 20 | %"((v * 2) - z)" = sub i32 %"(v * 2)", %1 21 | store i32 %"((v * 2) - z)", i32* %v, align 4 22 | %t = alloca i32, align 4 23 | store i32 1, i32* %t, align 4 24 | %"(z < 10)" = icmp slt i32 %1, 10 25 | br i1 %"(z < 10)", label %"if.t (z < 10)", label %"if.t true" 26 | 27 | "while.o (i < a)": ; preds = %"while.c (i < a)" 28 | ret i32 0 29 | 30 | "if.t (z < 10)": ; preds = %"while.b (i < a)" 31 | %"(v * z)" = mul i32 %"((v * 2) - z)", %1 32 | %"(((v * z) + 24) - 15)" = add i32 %"(v * z)", 9 33 | %"((((v * z) + 24) - 15) + y)" = add i32 %"(((v * z) + 24) - 15)", %2 34 | store i32 %"((((v * z) + 24) - 15) + y)", i32* %t, align 4 35 | br label %"if.o (z < 10)" 36 | 37 | "if.o (z < 10)": ; preds = %"if.t true", %"if.t (z < 10)" 38 | %t8 = phi i32 [ %"((((v - 7) + 8) + 8) - z)", %"if.t true" ], [ %"((((v * z) + 24) - 15) + y)", %"if.t (z < 10)" ] 39 | %"(222 - v)" = sub i32 222, %"((v * 2) - z)" 40 | %"(z * x)" = mul i32 %1, %3 41 | %"((222 - v) + (z * x))" = add i32 %"(222 - v)", %"(z * x)" 42 | %"(((222 - v) + (z * x)) - w)" = sub i32 %"((222 - v) + (z * x))", %4 43 | %"(u * z)" = mul i32 %"((434 + (v * 67)) - ((w * 4) / 5))", %1 44 | %"((u * z) * v)" = mul i32 %"(u * z)", %"((v * 2) - z)" 45 | %"(5 + ((u * z) * v))" = add i32 %"((u * z) * v)", 5 46 | %"(t * 2)" = shl i32 %t8, 1 47 | %"((t * 2) * l)" = mul i32 %"(t * 2)", %"(((222 - v) + (z * x)) - w)" 48 | %"((5 + ((u * z) * v)) + ((t * 2) * l))" = add i32 %"(5 + ((u * z) * v))", %"((t * 2) * l)" 49 | br label %"while.c (i < a)" 50 | 51 | "if.t true": ; preds = %"while.b (i < a)" 52 | %"(((v - 7) + 8) + 8)" = add i32 %"((v * 2) - z)", 9 53 | %"((((v - 7) + 8) + 8) - z)" = sub i32 %"(((v - 7) + 8) + 8)", %1 54 | store i32 %"((((v - 7) + 8) + 8) - z)", i32* %t, align 4 55 | br label %"if.o (z < 10)" 56 | } 57 | -------------------------------------------------------------------------------- /test/out/llvm/genExecutable.ll: -------------------------------------------------------------------------------- 1 | define i32 @real_main() { 2 | entry: 3 | %"printInt(7)" = call i32 @printInt(i32 7) 4 | ret i32 0 5 | } -------------------------------------------------------------------------------- /test/out/llvm/genExternalFunction.ll: -------------------------------------------------------------------------------- 1 | declare i32 @printInt(i32) -------------------------------------------------------------------------------- /test/out/llvm/genFunction.ll: -------------------------------------------------------------------------------- 1 | define i32 @genFunction(i32, i32, i32) { 2 | entry: 3 | ret i32 0 4 | } -------------------------------------------------------------------------------- /test/out/llvm/genGlobalConstant.ll: -------------------------------------------------------------------------------- 1 | @a = constant i32 7 2 | 3 | declare i8* @malloc(i64) 4 | 5 | declare void @free(i8*) -------------------------------------------------------------------------------- /test/out/llvm/genGlobalsInFunctions.ll: -------------------------------------------------------------------------------- 1 | @G0 = constant i32 5555 2 | @G1 = constant i32 -8 3 | @G2 = constant i32 4 4 | 5 | declare i8* @malloc(i64) 6 | 7 | declare void @free(i8*) 8 | 9 | define i32 @genGlobalsInFunctions(i32, i32, i32, i32) { 10 | entry: 11 | %"(42 + x)" = add i32 %2, 42 12 | %"((42 + x) - y)" = sub i32 %"(42 + x)", %1 13 | %"(v * 67)" = mul i32 %"((42 + x) - y)", 67 14 | %"(169 + (v * 67))" = add i32 %"(v * 67)", 169 15 | %"(w * 4)" = shl i32 %3, 2 16 | %"((w * 4) / 5)" = sdiv i32 %"(w * 4)", 5 17 | %"((169 + (v * 67)) - ((w * 4) / 5))" = sub i32 %"(169 + (v * 67))", %"((w * 4) / 5)" 18 | %"(v * 2)" = shl i32 %"((42 + x) - y)", 1 19 | %"((v * 2) - z)" = sub i32 %"(v * 2)", %0 20 | %"(z < 10)" = icmp slt i32 %0, 10 21 | %"(v * z)" = mul i32 %"((v * 2) - z)", %0 22 | %"(v - z)" = sub i32 %"((v * 2) - z)", %0 23 | %t.0 = select i1 %"(z < 10)", i32 %"(v * z)", i32 %"(v - z)" 24 | %"(222 - u)" = sub i32 222, %"((169 + (v * 67)) - ((w * 4) / 5))" 25 | %"((222 - u) + z)" = add i32 %"(222 - u)", %0 26 | %"(u * z)" = mul i32 %"((169 + (v * 67)) - ((w * 4) / 5))", %0 27 | %"((u * z) * v)" = mul i32 %"(u * z)", %"((v * 2) - z)" 28 | %"(5 + ((u * z) * v))" = add i32 %"((u * z) * v)", 5 29 | %"(t * G2)" = shl i32 %t.0, 2 30 | %"((5 + ((u * z) * v)) + (t * G2))" = add i32 %"(5 + ((u * z) * v))", %"(t * G2)" 31 | %"(((5 + ((u * z) * v)) + (t * G2)) - (G0 * G1))" = add i32 %"((5 + ((u * z) * v)) + (t * G2))", 44440 32 | %"(2 * l)" = shl i32 %"((222 - u) + z)", 1 33 | %"((((5 + ((u * z) * v)) + (t * G2)) - (G0 * G1)) + (2 * l))" = add i32 %"(((5 + ((u * z) * v)) + (t * G2)) - (G0 * G1))", %"(2 * l)" 34 | ret i32 %"((((5 + ((u * z) * v)) + (t * G2)) - (G0 * G1)) + (2 * l))" 35 | } 36 | 37 | define i32 @returnGlobal() { 38 | entry: 39 | ret i32 5555 40 | } 41 | -------------------------------------------------------------------------------- /test/out/llvm/genIfStatement.ll: -------------------------------------------------------------------------------- 1 | define i32 @genIfStatement(i32, i32, i32) { 2 | entry: 3 | %"(a == 10)" = icmp eq i32 %0, 10 4 | %. = select i1 %"(a == 10)", i32 %1, i32 %2 5 | ret i32 %. 6 | } -------------------------------------------------------------------------------- /test/out/llvm/genImportExternalFunction.ll: -------------------------------------------------------------------------------- 1 | declare i32 @printInt(i32) 2 | 3 | define i32 @real_main() { 4 | entry: 5 | %"printInt(7)" = call i32 @printInt(i32 7) 6 | ret i32 0 7 | } -------------------------------------------------------------------------------- /test/out/llvm/genInfixFunctionCall.ll: -------------------------------------------------------------------------------- 1 | define i32 @add(i32, i32) { 2 | entry: 3 | %"(a + b)" = add i32 %0, %1 4 | ret i32 %"(a + b)" 5 | } 6 | 7 | define i32 @multiply(i32, i32) { 8 | entry: 9 | br label %"while.c (i < x)" 10 | 11 | "while.c (i < x)": ; preds = %"while.b (i < x)", %entry 12 | %i.0 = phi i32 [ 0, %entry ], [ %"(i + 1)", %"while.b (i < x)" ] 13 | %mul.0 = phi i32 [ 0, %entry ], [ %"add(mul, y)", %"while.b (i < x)" ] 14 | %"(i < x)" = icmp slt i32 %i.0, %0 15 | br i1 %"(i < x)", label %"while.b (i < x)", label %"while.o (i < x)" 16 | 17 | "while.b (i < x)": ; preds = %"while.c (i < x)" 18 | %"(i + 1)" = add i32 %i.0, 1 19 | %"add(mul, y)" = call i32 @add(i32 %mul.0, i32 %1) 20 | br label %"while.c (i < x)" 21 | 22 | "while.o (i < x)": ; preds = %"while.c (i < x)" 23 | ret i32 %mul.0 24 | } -------------------------------------------------------------------------------- /test/out/llvm/genOperators.ll: -------------------------------------------------------------------------------- 1 | define i32 @genOperators(i32, i32) { 2 | entry: 3 | %"(a != b)" = icmp eq i32 %0, %1 4 | %. = select i1 %"(a != b)", i32 12, i32 10 5 | ret i32 %. 6 | } -------------------------------------------------------------------------------- /test/out/llvm/genRecursiveCall.ll: -------------------------------------------------------------------------------- 1 | define i32 @fac(i32) { 2 | entry: 3 | %"(n <= 1)" = icmp slt i32 %0, 2 4 | br i1 %"(n <= 1)", label %"if.o (n <= 1)", label %"if.t true" 5 | 6 | "if.o (n <= 1)": ; preds = %entry, %"if.t true" 7 | %r.0 = phi i32 [ %"(n * fac((n - 1)))", %"if.t true" ], [ 1, %entry ] 8 | ret i32 %r.0 9 | 10 | "if.t true": ; preds = %entry 11 | %"(n - 1)" = add i32 %0, -1 12 | %"fac((n - 1))" = call i32 @fac(i32 %"(n - 1)") 13 | %"(n * fac((n - 1)))" = mul i32 %"fac((n - 1))", %0 14 | br label %"if.o (n <= 1)" 15 | } -------------------------------------------------------------------------------- /test/out/llvm/genRecursiveInput1.ll: -------------------------------------------------------------------------------- 1 | declare i32 @test2(i32) 2 | 3 | declare i32 @test4() 4 | 5 | define i32 @test1() { 6 | entry: 7 | %"test4()" = call i32 @test4() 8 | %"(test4() + 1)" = add i32 %"test4()", 1 9 | ret i32 %"(test4() + 1)" 10 | } 11 | 12 | define i32 @test3() { 13 | entry: 14 | %"test1()" = call i32 @test1() 15 | %"test2(test1())" = call i32 @test2(i32 %"test1()") 16 | ret i32 %"test2(test1())" 17 | } -------------------------------------------------------------------------------- /test/out/llvm/genRecursiveInput2.ll: -------------------------------------------------------------------------------- 1 | declare i32 @test1() 2 | 3 | declare i32 @test3() 4 | 5 | define i32 @test2(i32) { 6 | entry: 7 | %"test3()" = call i32 @test3() 8 | %"(test3() + a)" = add i32 %"test3()", %0 9 | ret i32 %"(test3() + a)" 10 | } 11 | 12 | define i32 @test4() { 13 | entry: 14 | ret i32 9 15 | } -------------------------------------------------------------------------------- /test/out/llvm/genVariableDeclaration.ll: -------------------------------------------------------------------------------- 1 | define i32 @genVariableDeclaration() { 2 | entry: 3 | ret i32 0 4 | } -------------------------------------------------------------------------------- /test/out/llvm/genVariableReassignment.ll: -------------------------------------------------------------------------------- 1 | define i32 @genVariableReassignment() { 2 | entry: 3 | ret i32 3 4 | } -------------------------------------------------------------------------------- /test/out/llvm/genWhileLoop.ll: -------------------------------------------------------------------------------- 1 | define i32 @genWhileLoop(i32) { 2 | entry: 3 | br label %"while.c (i < a)" 4 | 5 | "while.c (i < a)": ; preds = %"while.b (i < a)", %entry 6 | %i.0 = phi i32 [ 0, %entry ], [ %"(i + 1)", %"while.b (i < a)" ] 7 | %storemerge = phi i32 [ %"(sum + a)", %"while.b (i < a)" ], [ 0, %entry ] 8 | %"(i < a)" = icmp slt i32 %i.0, %0 9 | br i1 %"(i < a)", label %"while.b (i < a)", label %"while.o (i < a)" 10 | 11 | "while.b (i < a)": ; preds = %"while.c (i < a)" 12 | %"(i + 1)" = add i32 %i.0, 1 13 | %"(sum + a)" = add i32 %storemerge, %0 14 | br label %"while.c (i < a)" 15 | 16 | "while.o (i < a)": ; preds = %"while.c (i < a)" 17 | %"(sum * i)" = mul i32 %storemerge, %i.0 18 | ret i32 %"(sum * i)" 19 | } -------------------------------------------------------------------------------- /test/out/llvm/importClass1.ll: -------------------------------------------------------------------------------- 1 | %Accumulator = type { i32 } 2 | 3 | @INITIAL_ACCUMULATOR_VALUE = constant i32 1234 4 | 5 | declare i8* @malloc(i64) 6 | 7 | declare void @free(i8*) 8 | 9 | define void @Accumulator_add(%Accumulator*, i32) { 10 | entry: 11 | %a = getelementptr inbounds %Accumulator, %Accumulator* %0, i64 0, i32 0 12 | %a1 = load i32, i32* %a, align 4 13 | %"(a + b)" = add i32 %a1, %1 14 | store i32 %"(a + b)", i32* %a, align 4 15 | ret void 16 | } 17 | 18 | define void @Accumulator_sub(%Accumulator*, i32) { 19 | entry: 20 | %a = getelementptr inbounds %Accumulator, %Accumulator* %0, i64 0, i32 0 21 | %a1 = load i32, i32* %a, align 4 22 | %"(a - b)" = sub i32 %a1, %1 23 | store i32 %"(a - b)", i32* %a, align 4 24 | ret void 25 | } 26 | 27 | define void @Accumulator_mul(%Accumulator*, i32) { 28 | entry: 29 | %a = getelementptr inbounds %Accumulator, %Accumulator* %0, i64 0, i32 0 30 | %a1 = load i32, i32* %a, align 4 31 | %"(a * b)" = mul i32 %a1, %1 32 | store i32 %"(a * b)", i32* %a, align 4 33 | ret void 34 | } 35 | 36 | define void @Accumulator_div(%Accumulator*, i32) { 37 | entry: 38 | %a = getelementptr inbounds %Accumulator, %Accumulator* %0, i64 0, i32 0 39 | %a1 = load i32, i32* %a, align 4 40 | %"(a / b)" = sdiv i32 %a1, %1 41 | store i32 %"(a / b)", i32* %a, align 4 42 | ret void 43 | } -------------------------------------------------------------------------------- /test/out/llvm/importClass2.ll: -------------------------------------------------------------------------------- 1 | declare void @Accumulator_add(%Accumulator*, i32) 2 | 3 | declare void @Accumulator_sub(%Accumulator*, i32) 4 | 5 | declare void @Accumulator_mul(%Accumulator*, i32) 6 | 7 | declare void @Accumulator_div(%Accumulator*, i32) 8 | 9 | define i32 @real_main() { 10 | entry: 11 | %"malloc(4) for Accumulator" = call i8* @malloc(i64 4) 12 | %castToAccumulator = bitcast i8* %"malloc(4) for Accumulator" to %Accumulator* 13 | %INITIAL_ACCUMULATOR_VALUE = load i32, i32* @INITIAL_ACCUMULATOR_VALUE, align 4 14 | %"a.a = INITIAL_ACCUMULATOR_VALUE" = bitcast i8* %"malloc(4) for Accumulator" to i32* 15 | store i32 %INITIAL_ACCUMULATOR_VALUE, i32* %"a.a = INITIAL_ACCUMULATOR_VALUE", align 4 16 | call void @Accumulator_add(%Accumulator* %castToAccumulator, i32 1) 17 | call void @Accumulator_mul(%Accumulator* %castToAccumulator, i32 7) 18 | %a.a = load i32, i32* %"a.a = INITIAL_ACCUMULATOR_VALUE", align 4 19 | %"printInt(a.a)" = call i32 @printInt(i32 %a.a) 20 | call void @free(i8* %"malloc(4) for Accumulator") 21 | ret i32 0 22 | } -------------------------------------------------------------------------------- /test/out/llvm/lotsOfAllocations.ll: -------------------------------------------------------------------------------- 1 | define i32 @Account_incrementId(%Account*) { 2 | entry: 3 | %id = getelementptr inbounds %Account, %Account* %0, i64 0, i32 0 4 | %id1 = load i32, i32* %id, align 4 5 | %"(id + 1)" = add i32 %id1, 1 6 | store i32 %"(id + 1)", i32* %id, align 4 7 | ret i32 %id1 8 | } 9 | 10 | define i32 @real_main() { 11 | entry: 12 | %"malloc(20) for Account" = call i8* @malloc(i64 20) 13 | %"a.id = 1" = bitcast i8* %"malloc(20) for Account" to i32* 14 | store i32 1, i32* %"a.id = 1", align 4 15 | %"malloc(20) for Account1" = call i8* @malloc(i64 20) 16 | %"b.id = 2" = bitcast i8* %"malloc(20) for Account1" to i32* 17 | store i32 2, i32* %"b.id = 2", align 4 18 | %"malloc(20) for Account3" = call i8* @malloc(i64 20) 19 | %"c.id = 3" = bitcast i8* %"malloc(20) for Account3" to i32* 20 | store i32 3, i32* %"c.id = 3", align 4 21 | %"malloc(20) for Account5" = call i8* @malloc(i64 20) 22 | %"d.id = 4" = bitcast i8* %"malloc(20) for Account5" to i32* 23 | store i32 4, i32* %"d.id = 4", align 4 24 | %"malloc(20) for Account7" = call i8* @malloc(i64 20) 25 | %"e.id = 5" = bitcast i8* %"malloc(20) for Account7" to i32* 26 | store i32 5, i32* %"e.id = 5", align 4 27 | %"malloc(20) for Account9" = call i8* @malloc(i64 20) 28 | %"f.id = 6" = bitcast i8* %"malloc(20) for Account9" to i32* 29 | store i32 6, i32* %"f.id = 6", align 4 30 | %"malloc(20) for Account11" = call i8* @malloc(i64 20) 31 | %"g.id = 7" = bitcast i8* %"malloc(20) for Account11" to i32* 32 | store i32 7, i32* %"g.id = 7", align 4 33 | %a.id = load i32, i32* %"a.id = 1", align 4 34 | %b.id = load i32, i32* %"b.id = 2", align 4 35 | %"(a.id + b.id)" = add i32 %a.id, %b.id 36 | %c.id = load i32, i32* %"c.id = 3", align 4 37 | %"((a.id + b.id) + c.id)" = add i32 %"(a.id + b.id)", %c.id 38 | %d.id = load i32, i32* %"d.id = 4", align 4 39 | %"(((a.id + b.id) + c.id) + d.id)" = add i32 %"((a.id + b.id) + c.id)", %d.id 40 | %e.id = load i32, i32* %"e.id = 5", align 4 41 | %"((((a.id + b.id) + c.id) + d.id) + e.id)" = add i32 %"(((a.id + b.id) + c.id) + d.id)", %e.id 42 | %f.id = load i32, i32* %"f.id = 6", align 4 43 | %"(((((a.id + b.id) + c.id) + d.id) + e.id) + f.id)" = add i32 %"((((a.id + b.id) + c.id) + d.id) + e.id)", %f.id 44 | %"((((((a.id + b.id) + c.id) + d.id) + e.id) + f.id) + g.id)" = add i32 %"(((((a.id + b.id) + c.id) + d.id) + e.id) + f.id)", 7 45 | %"printInt(ret)" = call i32 @printInt(i32 %"((((((a.id + b.id) + c.id) + d.id) + e.id) + f.id) + g.id)") 46 | call void @free(i8* %"malloc(20) for Account") 47 | call void @free(i8* %"malloc(20) for Account1") 48 | call void @free(i8* %"malloc(20) for Account3") 49 | call void @free(i8* %"malloc(20) for Account5") 50 | call void @free(i8* %"malloc(20) for Account7") 51 | call void @free(i8* %"malloc(20) for Account9") 52 | call void @free(i8* %"malloc(20) for Account11") 53 | ret i32 0 54 | } -------------------------------------------------------------------------------- /test/out/llvm/outputFactorial.ll: -------------------------------------------------------------------------------- 1 | define i32 @fac(i32) { 2 | entry: 3 | %"(n <= 1)" = icmp slt i32 %0, 2 4 | br i1 %"(n <= 1)", label %"if.o (n <= 1)", label %"if.t true" 5 | 6 | "if.o (n <= 1)": ; preds = %entry, %"if.t true" 7 | %r.0 = phi i32 [ %"(n * fac((n - 1)))", %"if.t true" ], [ 1, %entry ] 8 | ret i32 %r.0 9 | 10 | "if.t true": ; preds = %entry 11 | %"(n - 1)" = add i32 %0, -1 12 | %"fac((n - 1))" = call i32 @fac(i32 %"(n - 1)") 13 | %"(n * fac((n - 1)))" = mul i32 %"fac((n - 1))", %0 14 | br label %"if.o (n <= 1)" 15 | } 16 | 17 | define i32 @inc(i32) { 18 | entry: 19 | %"(a + 1)" = add i32 %0, 1 20 | ret i32 %"(a + 1)" 21 | } 22 | 23 | define i32 @real_main() { 24 | entry: 25 | br label %"while.c (i < 11)" 26 | 27 | "while.c (i < 11)": ; preds = %"while.b (i < 11)", %entry 28 | %storemerge = phi i32 [ %"inc(i)", %"while.b (i < 11)" ], [ 1, %entry ] 29 | %"(i < 11)" = icmp slt i32 %storemerge, 11 30 | br i1 %"(i < 11)", label %"while.b (i < 11)", label %"while.o (i < 11)" 31 | 32 | "while.b (i < 11)": ; preds = %"while.c (i < 11)" 33 | %"fac(i)" = call i32 @fac(i32 %storemerge) 34 | %"printInt(fac(i))" = call i32 @printInt(i32 %"fac(i)") 35 | %"inc(i)" = call i32 @inc(i32 %storemerge) 36 | br label %"while.c (i < 11)" 37 | 38 | "while.o (i < 11)": ; preds = %"while.c (i < 11)" 39 | ret i32 0 40 | } -------------------------------------------------------------------------------- /test/outputFactorial.l: -------------------------------------------------------------------------------- 1 | import std.io 2 | 3 | fac (n) 4 | var r = 0, 5 | if n <=1 6 | r = 1 7 | else 8 | r = n * fac(n - 1) 9 | ; 10 | r 11 | 12 | inc (a) 13 | a + 1 14 | 15 | main () 16 | var i = 1, 17 | while (i < 11) 18 | printInt(fac(i)), 19 | i = inc(i) 20 | ; 21 | 0 22 | --------------------------------------------------------------------------------