├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .idea ├── codeStyles │ └── codeStyleConfig.xml ├── enact.iml ├── misc.xml ├── modules.xml └── vcs.xml ├── .travis.yml ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── docs ├── img │ ├── enact-logo-text.png │ ├── enact_icon.png │ └── tmp └── implementation.md ├── include └── Enact.h ├── lib ├── AstSerialise.cpp ├── AstSerialise.h ├── CMakeLists.txt ├── InsertionOrderMap.h ├── Natives.cpp ├── Natives.h ├── analyser │ ├── Analyser.cpp │ ├── Analyser.h │ └── CMakeLists.txt ├── ast │ ├── AstVisitor.h │ ├── CMakeLists.txt │ ├── Expr.h │ ├── Pattern.h │ ├── Stmt.h │ └── generate.py ├── bytecode │ ├── CMakeLists.txt │ ├── Chunk.cpp │ └── Chunk.h ├── common.h ├── compiler │ ├── CMakeLists.txt │ ├── Compiler.cpp │ └── Compiler.h ├── context │ ├── CMakeLists.txt │ ├── CompileContext.cpp │ ├── CompileContext.h │ ├── Options.cpp │ └── Options.h ├── memory │ ├── CMakeLists.txt │ ├── GC.cpp │ └── GC.h ├── parser │ ├── CMakeLists.txt │ ├── Lexer.cpp │ ├── Lexer.h │ ├── Parser.cpp │ ├── Parser.h │ ├── Token.h │ ├── Typename.cpp │ └── Typename.h ├── sema │ ├── CMakeLists.txt │ ├── Sema.cpp │ ├── Sema.h │ ├── SemaDecls.cpp │ ├── SemaDecls.h │ ├── SemaDefs.cpp │ ├── SemaDefs.h │ └── VariableInfo.h ├── trivialStructs.h ├── type │ ├── CMakeLists.txt │ ├── Type.cpp │ └── Type.h ├── value │ ├── CMakeLists.txt │ ├── Object.cpp │ ├── Object.h │ ├── Value.cpp │ └── Value.h └── vm │ ├── CMakeLists.txt │ ├── VM.cpp │ └── VM.h └── src ├── CMakeLists.txt └── main.cpp /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | labels: 5 | 6 | --- 7 | 8 | **Describe the bug** 9 | A clear and concise description of what the bug is. 10 | 11 | **To Reproduce** 12 | - If you are setting any flags, list them here. 13 | - If the problem occurs when running a program, paste a link to a gist containing it. 14 | - If the problem occurs elsewhere, explain where it is happening and how to reproduce the behavior. 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Output** 20 | Paste output from Enact with the `-verbose` flag set. 21 | 22 | **Platform** 23 | - Enact version: [e.g. 0.1.1] 24 | - OS [e.g. Ubuntu Linux, Windows] 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | labels: 5 | 6 | --- 7 | 8 | **Is your feature request related to a problem? Please describe.** 9 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 10 | 11 | **Describe the solution you'd like** 12 | A clear and concise description of what you want to happen. 13 | 14 | **Describe alternatives you've considered** 15 | A clear and concise description of any alternative solutions or features you've considered. 16 | 17 | **Additional context** 18 | Add any other context or screenshots about the feature request here. 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ 2 | ## Prerequisites 3 | *.d 4 | 5 | ## Compiled Object files 6 | *.slo 7 | *.lo 8 | *.o 9 | *.obj 10 | 11 | ## Precompiled Headers 12 | *.gch 13 | *.pch 14 | 15 | ## Compiled Dynamic libraries 16 | *.so 17 | *.dylib 18 | *.dll 19 | 20 | ## Fortran module files 21 | *.mod 22 | *.smod 23 | 24 | ## Compiled Static libraries 25 | *.lai 26 | *.la 27 | *.a 28 | *.lib 29 | 30 | ## Executables 31 | *.exe 32 | *.out 33 | *.app 34 | 35 | # CLion 36 | ## User-specific stuff 37 | .idea/**/workspace.xml 38 | .idea/**/tasks.xml 39 | .idea/**/usage.statistics.xml 40 | .idea/**/dictionaries 41 | .idea/**/shelf 42 | 43 | ## Generated files 44 | .idea/**/contentModel.xml 45 | 46 | ## Sensitive or high-churn files 47 | .idea/**/dataSources/ 48 | .idea/**/dataSources.ids 49 | .idea/**/dataSources.local.xml 50 | .idea/**/sqlDataSources.xml 51 | .idea/**/dynamic.xml 52 | .idea/**/uiDesigner.xml 53 | .idea/**/dbnavigator.xml 54 | 55 | ## Gradle 56 | .idea/**/gradle.xml 57 | .idea/**/libraries 58 | 59 | ## Gradle and Maven with auto-import 60 | ### When using Gradle or Maven with auto-import, you should exclude module files, 61 | ### since they will be recreated, and may cause churn. Uncomment if using 62 | ### auto-import. 63 | #### .idea/modules.xml 64 | #### .idea/*.iml 65 | #### .idea/modules 66 | 67 | ## CMake 68 | cmake-build-*/ 69 | 70 | ## Mongo Explorer plugin 71 | .idea/**/mongoSettings.xml 72 | 73 | ## File-based project format 74 | *.iws 75 | 76 | ## IntelliJ 77 | out/ 78 | 79 | ## mpeltonen/sbt-idea plugin 80 | .idea_modules/ 81 | 82 | ## JIRA plugin 83 | atlassian-ide-plugin.xml 84 | 85 | ## Cursive Clojure plugin 86 | .idea/replstate.xml 87 | 88 | ## Crashlytics plugin (for Android Studio and IntelliJ) 89 | com_crashlytics_export_strings.xml 90 | crashlytics.properties 91 | crashlytics-build.properties 92 | fabric.properties 93 | 94 | ## Editor-based Rest Client 95 | .idea/httpRequests 96 | 97 | ## Android studio 3.1+ serialized cache file 98 | .idea/caches/build_file_checksums.ser 99 | .idea/workspace.xml 100 | 101 | # Xcode 102 | ## User settings 103 | xcuserdata/ 104 | 105 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 106 | *.xcscmblueprint 107 | *.xccheckout 108 | 109 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 110 | build/ 111 | DerivedData/ 112 | *.moved-aside 113 | *.pbxuser 114 | !default.pbxuser 115 | *.mode1v3 116 | !default.mode1v3 117 | *.mode2v3 118 | !default.mode2v3 119 | *.perspectivev3 120 | !default.perspectivev3 121 | 122 | ## Gcc Patch 123 | /*.gcno 124 | 125 | # macOS 126 | .DS_Store -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/enact.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: bionic 2 | 3 | language: cpp 4 | compiler: gcc 5 | 6 | addons: 7 | apt: 8 | sources: 9 | - ubuntu-toolchain-r-test 10 | packages: 11 | - gcc-8 12 | - g++-8 13 | - cmake 14 | 15 | script: 16 | - export CC=gcc-8 17 | - export CXX=g++-8 18 | - cmake ./ 19 | - cmake --build ./ --target enact-cli -- -j 2 20 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.9.2) 2 | project(enact CXX) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_compile_definitions(DEBUG) 7 | 8 | include_directories(include) 9 | add_subdirectory(lib) 10 | add_subdirectory(src) 11 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at abuse@matilda.dandigit.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Enact 2 | 3 | Welcome to the Enact project. It's wonderful to have you here to contribute! 4 | Before you get started helping out, have a quick read over this guide to know what to expect. 5 | 6 | ## Code of Conduct 7 | 8 | All participation in the Enact project is to be governed by the [Code of Conduct](https://github.com/Dandigit/matilda/blob/master/CODE_OF_CONDUCT.md). Any abusive behaviour may be reported to 9 | `enact@dandigit.com`. 10 | 11 | ## How can I contribute? 12 | 13 | ### Bug reports 14 | 15 | If you find a bug in Enact, open a **Bug Report** issue and fill out the template! Help us improve the experience by providing 16 | detailed reports with plenty of info. 17 | 18 | ### Feature requests 19 | 20 | Perhaps you use Enact on the daily and think there's a missing piece of the language that's really bugging you. Propose a new 21 | feature by opening a **Feature Request** issue. Keep in mind that all feature requests are purely proposals and may or may not 22 | become part of the language. 23 | 24 | ### Writing code 25 | 26 | If you're fluent in C++ you can be a huge help to the Enact project. Take a look at bug reports and see if there's any you'd 27 | like to fix, then submit a pull request fixing the bug and explaining how you fixed it. Make sure to include the original bug 28 | report in your PR. Approved feature requests waiting for implementation can also use your help. Submit a pull request with your 29 | implementation of the feature, including the original feature request issue. 30 | 31 | \ 32 | Hopefully you've been inspired to contribute to Enact in one way or another! I look forward to seeing you around! 33 | 34 | **- Dan** \ 35 | Project maintainer 36 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2018 Daniel Boulton and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Related issue:** (Link to the original issue here. If N/A, specify.) 2 | 3 | **Changes made** \ 4 | What changes does this PR make? Why are they necessary? 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Enact logo 3 |

4 |

5 | Build Status 6 | CodeFactor
7 |

8 |

9 | Enact is a new compiled general-purpose programming language that's not designed to be unique or groundbreaking. 10 | Instead, Enact aims to be familiar, taking advantage of already established techniques and paradigms and making them 11 | nicer. 12 |

13 | 14 | ## Example 15 | ``` 16 | // FizzBuzz in Enact 17 | 18 | func fizzBuzz(n int) { 19 | for i in 1...n { 20 | switch (i % 3 == 0, i % 5 == 0) { 21 | case (true, false) => print("Fizz"); 22 | case (false, true) => print("Buzz"); 23 | case (true, true) => print("FizzBuzz"); 24 | default => print(n); 25 | } 26 | } 27 | } 28 | ``` 29 | 30 | ## Features 31 | - Static types that help, not hinder 32 | - Efficient compile-time memory management 33 | - Easy-to-use generics, hygienic macros 34 | - Pattern matching and tail-calls 35 | - Clean and familiar syntax 36 | - Built-in build system and package management 37 | 38 | ## Goals 39 | - Easy to pick up from other compiled languages like C, C++, Rust and Go 40 | - More memory-safe than C, more approachable than Rust, more peformant than Go 41 | - Small standard library with a strong ecosystem of external packages -------------------------------------------------------------------------------- /docs/img/enact-logo-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enact-lang/enact/6315773acda3f9f4bb25d220a73581641801b68a/docs/img/enact-logo-text.png -------------------------------------------------------------------------------- /docs/img/enact_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enact-lang/enact/6315773acda3f9f4bb25d220a73581641801b68a/docs/img/enact_icon.png -------------------------------------------------------------------------------- /docs/img/tmp: -------------------------------------------------------------------------------- 1 | tmp 2 | -------------------------------------------------------------------------------- /docs/implementation.md: -------------------------------------------------------------------------------- 1 | # Enact 2 | ## Planned interpreter structure 3 | 4 | Mainly I'm just writing this down for myself, however potential contributors may find this interesting as well. 5 | Enact is first and foremost an interpreted language, with the possibility of a JIT compiler in the future. 6 | 7 | This repository contains source for a program that compiles Enact source code (`.en` files) down to a high level bytecode and runs 8 | this bytecode on a virtual machine. 9 | 10 | Enact has a planned 5 pass interpreter: 11 | - First, the source is parsed and converted into an AST. \[ done ✔️ \] 12 | - Next, the AST is walked to resolve variables and check types. \[ done ✔️ \] 13 | - Afterwards, the AST is walked again and compiled down to bytecode. \[ in progress 🚧 \] 14 | - This bytecode is optimized by yet another pass. \[ not implemented ❌ \] 15 | - Finally, the VM takes the bytecode and runs it. \[ in progress 🚧 \] 16 | 17 | Currently, the focus of development is implementing the VM abd compiler, and things that come along with that, like garbage collection. 18 | -------------------------------------------------------------------------------- /include/Enact.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_ENACT_H 2 | #define ENACT_ENACT_H 3 | 4 | #include "../lib/context/CompileContext.h" 5 | 6 | #endif //ENACT_ENACT_H 7 | -------------------------------------------------------------------------------- /lib/AstSerialise.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "AstSerialise.h" 4 | 5 | namespace enact { 6 | std::string AstSerialise::operator()(Stmt &stmt) { 7 | return visitStmt(stmt); 8 | } 9 | 10 | std::string AstSerialise::operator()(Expr &expr) { 11 | return visitExpr(expr); 12 | } 13 | 14 | std::string AstSerialise::visitBreakStmt(BreakStmt &stmt) { 15 | return m_ident + "(Stmt::Break)"; 16 | } 17 | 18 | std::string AstSerialise::visitContinueStmt(ContinueStmt &stmt) { 19 | return m_ident + "(Stmt::Continue)"; 20 | } 21 | 22 | std::string AstSerialise::visitEnumStmt(EnumStmt &stmt) { 23 | std::stringstream s; 24 | s << "(Stmt::Enum " << stmt.name.lexeme << " ("; 25 | 26 | s << ") (\n"; 27 | m_ident += " "; 28 | 29 | for (const EnumStmt::Variant& variant : stmt.variants) { 30 | s << m_ident << "(" << variant.name.lexeme << " " << variant.typename_->name() << ")\n"; 31 | } 32 | 33 | m_ident.erase(0, 4); 34 | s << ")"; 35 | 36 | return s.str(); 37 | } 38 | 39 | std::string AstSerialise::visitExpressionStmt(ExpressionStmt &stmt) { 40 | return m_ident + "(Stmt::Expression " + visitExpr(*stmt.expr) + ")"; 41 | } 42 | 43 | std::string AstSerialise::visitFunctionStmt(FunctionStmt &stmt) { 44 | std::stringstream s; 45 | 46 | s << "(Stmt::Function " << stmt.name.lexeme << " ("; 47 | 48 | std::string separator; 49 | for (auto ¶m : stmt.params) { 50 | s << separator << param.name.lexeme << ' ' << param.typename_->name(); 51 | separator = " "; 52 | } 53 | 54 | s << ") " << stmt.returnTypename->name() << " (\n"; 55 | m_ident += " "; 56 | 57 | s << visitExpr(*stmt.body); 58 | 59 | m_ident.erase(0, 4); 60 | s << ')'; 61 | 62 | return s.str(); 63 | } 64 | 65 | std::string AstSerialise::visitImplStmt(ImplStmt &stmt) { 66 | std::stringstream s; 67 | s << m_ident << "(Stmt::Impl " << stmt.typename_->name() << ' '; 68 | 69 | if (stmt.traitTypename != nullptr) { 70 | s << stmt.traitTypename->name() << ' '; 71 | } 72 | 73 | s << "(\n"; 74 | m_ident += " "; 75 | 76 | for (const std::unique_ptr& method : stmt.methods) { 77 | s << visitFunctionStmt(*method); 78 | } 79 | 80 | m_ident.erase(0, 4); 81 | s << ")"; 82 | 83 | return s.str(); 84 | } 85 | 86 | std::string AstSerialise::visitReturnStmt(ReturnStmt &stmt) { 87 | return "(Stmt::Return " + visitExpr(*stmt.value) + ")"; 88 | } 89 | 90 | std::string AstSerialise::visitStructStmt(StructStmt &stmt) { 91 | std::stringstream s; 92 | s << m_ident << "(Stmt::Struct " << stmt.name.lexeme << " ("; 93 | 94 | s << ") (\n"; 95 | m_ident += " "; 96 | 97 | for (auto &field : stmt.fields) { 98 | s << m_ident << "(" << field.name.lexeme << " " << field.typename_->name() << ")\n"; 99 | } 100 | 101 | m_ident.erase(0, 4); 102 | s << ")"; 103 | 104 | return s.str(); 105 | } 106 | 107 | std::string AstSerialise::visitTraitStmt(TraitStmt &stmt) { 108 | std::stringstream s; 109 | 110 | s << m_ident << "(Stmt::Trait " << stmt.name.lexeme << " (\n"; 111 | m_ident += " "; 112 | 113 | for (auto &method : stmt.methods) { 114 | s << m_ident << visitStmt(*method) << "\n"; 115 | } 116 | 117 | m_ident.erase(0, 4); 118 | s << ")"; 119 | 120 | return s.str(); 121 | } 122 | 123 | std::string AstSerialise::visitVariableStmt(VariableStmt &stmt) { 124 | std::stringstream s; 125 | 126 | s << m_ident << "(Stmt::Variable "; 127 | s << stmt.keyword.lexeme << ' '; 128 | s << stmt.typeName->name() << (stmt.typeName->name().empty() ? "" : " "); 129 | s << stmt.name.lexeme + " " + visitExpr(*stmt.initializer) << ")"; 130 | 131 | return s.str(); 132 | } 133 | 134 | std::string AstSerialise::visitAssignExpr(AssignExpr &expr) { 135 | return "(= " + visitExpr(*expr.target) + " " + visitExpr(*expr.value) + ")"; 136 | } 137 | 138 | std::string AstSerialise::visitBinaryExpr(BinaryExpr &expr) { 139 | return "(" + expr.oper.lexeme + " " + visitExpr(*expr.left) + " " + visitExpr(*expr.right) + ")"; 140 | } 141 | 142 | std::string AstSerialise::visitBlockExpr(BlockExpr &expr) { 143 | std::stringstream s; 144 | 145 | s << m_ident << "(Expr::Block (\n"; 146 | m_ident += " "; 147 | 148 | for (auto &statement : expr.stmts) { 149 | s << m_ident << visitStmt(*statement) << '\n'; 150 | } 151 | s << m_ident << visitExpr(*expr.expr) << ')'; 152 | 153 | m_ident.erase(0, 4); 154 | 155 | return s.str(); 156 | } 157 | 158 | std::string AstSerialise::visitBooleanExpr(BooleanExpr &expr) { 159 | return (expr.value ? "true" : "false"); 160 | } 161 | 162 | std::string AstSerialise::visitCallExpr(CallExpr &expr) { 163 | std::stringstream s; 164 | 165 | s << "(() " << visitExpr(*expr.callee); 166 | for (auto &arg : expr.args) { 167 | s << " " << visitExpr(*arg); 168 | } 169 | s << ")"; 170 | 171 | return s.str(); 172 | } 173 | 174 | std::string AstSerialise::visitCastExpr(CastExpr& expr) { 175 | return "(" + expr.oper.lexeme + " " + visitExpr(*expr.expr) + " " + expr.typename_->name() + ")"; 176 | } 177 | 178 | std::string AstSerialise::visitFloatExpr(FloatExpr &expr) { 179 | return std::to_string(expr.value); 180 | } 181 | 182 | std::string AstSerialise::visitForExpr(ForExpr& expr) { 183 | std::stringstream s; 184 | 185 | s << m_ident << "(Expr::For (" << expr.name.lexeme << " " << visitExpr(*expr.object) << ")\n"; 186 | m_ident += " "; 187 | 188 | s << visitExpr(*expr.body); 189 | 190 | m_ident.erase(0, 4); 191 | s << m_ident << ")"; 192 | 193 | return s.str(); 194 | } 195 | 196 | std::string AstSerialise::visitGetExpr(FieldExpr &expr) { 197 | return "(. " + visitExpr(*expr.object) + " " + expr.name.lexeme + ")"; 198 | } 199 | 200 | std::string AstSerialise::visitIfExpr(IfExpr& expr) { 201 | std::stringstream s; 202 | 203 | s << m_ident << "(Expr::If " << visitExpr(*expr.condition) << '\n'; 204 | m_ident += " "; 205 | 206 | s << visitExpr(*expr.thenBody) << '\n'; 207 | s << visitExpr(*expr.elseBody) << ')'; 208 | 209 | m_ident.erase(0, 4); 210 | 211 | return s.str(); 212 | } 213 | 214 | std::string AstSerialise::visitIntegerExpr(IntegerExpr &expr) { 215 | return std::to_string(expr.value); 216 | } 217 | 218 | std::string AstSerialise::visitInterpolationExpr(InterpolationExpr &expr) { 219 | return visitExpr(*expr.start) + "\\(" + visitExpr(*expr.interpolated) + ")" + visitExpr(*expr.end); 220 | } 221 | 222 | std::string AstSerialise::visitLogicalExpr(LogicalExpr &expr) { 223 | return "(" + expr.oper.lexeme + " " + visitExpr(*expr.left) + " " + visitExpr(*expr.right) + ")"; 224 | } 225 | 226 | std::string AstSerialise::visitReferenceExpr(ReferenceExpr &expr) { 227 | return "(&" + 228 | (expr.permission ? expr.permission->lexeme : "") + " " + 229 | (expr.region ? expr.region->lexeme : "") + " " + 230 | visitExpr(*expr.expr) + ")"; 231 | } 232 | 233 | std::string AstSerialise::visitStringExpr(StringExpr &expr) { 234 | std::stringstream s; 235 | s << "\"" << expr.value << "\""; 236 | return s.str(); 237 | } 238 | 239 | std::string AstSerialise::visitSwitchExpr(SwitchExpr& expr) { 240 | std::stringstream s; 241 | 242 | s << m_ident << "(Expr::Switch " << visitExpr(*expr.value) << " (\n"; 243 | m_ident += " "; 244 | 245 | for (auto &case_ : expr.cases) { 246 | s << m_ident << "(" << visitPattern(*case_.pattern) << " " << 247 | visitExpr(*case_.predicate) << " (\n"; 248 | m_ident += " "; 249 | 250 | s << visitExpr(*case_.body); 251 | 252 | m_ident.erase(0, 4); 253 | s << m_ident << ")\n"; 254 | } 255 | 256 | m_ident.erase(0, 4); 257 | s << m_ident << ")"; 258 | 259 | return s.str(); 260 | } 261 | 262 | std::string AstSerialise::visitSymbolExpr(SymbolExpr& expr) { 263 | return expr.name.lexeme; 264 | } 265 | 266 | std::string AstSerialise::visitTupleExpr(TupleExpr &expr) { 267 | std::ostringstream s; 268 | s << '('; 269 | 270 | std::string separator; 271 | for (const std::unique_ptr& elem : expr.elems) { 272 | s << separator << visitExpr(*elem); 273 | separator = ", "; 274 | } 275 | 276 | s << ')'; 277 | return s.str(); 278 | } 279 | 280 | std::string AstSerialise::visitUnaryExpr(UnaryExpr &expr) { 281 | return "(" + expr.oper.lexeme + " " + visitExpr(*expr.operand) + ")"; 282 | } 283 | 284 | std::string AstSerialise::visitUnitExpr(UnitExpr &expr) { 285 | return "()"; 286 | } 287 | 288 | std::string AstSerialise::visitWhileExpr(WhileExpr& expr) { 289 | std::stringstream s; 290 | 291 | s << m_ident << "(Expr::While " << visitExpr(*expr.condition) << '\n'; 292 | m_ident += " "; 293 | 294 | s << visitExpr(*expr.body); 295 | 296 | m_ident.erase(0, 4); 297 | s << m_ident << ")"; 298 | 299 | return s.str(); 300 | } 301 | 302 | std::string AstSerialise::visitValuePattern(ValuePattern &pattern) { 303 | return visitExpr(*pattern.value); 304 | } 305 | 306 | std::string AstSerialise::visitWildcardPattern(WildcardPattern &pattern) { 307 | return "_"; 308 | } 309 | } -------------------------------------------------------------------------------- /lib/AstSerialise.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_ASTSERIALISE_H 2 | #define ENACT_ASTSERIALISE_H 3 | 4 | #include "ast/AstVisitor.h" 5 | 6 | namespace enact { 7 | // A functor which takes an AST node (Stmt/Decl/Expr), serializes it, 8 | // and returns the output as an std::string. 9 | class AstSerialise : private AstVisitor { 10 | std::string m_ident = ""; 11 | 12 | std::string visitBreakStmt(BreakStmt& stmt) override; 13 | std::string visitContinueStmt(ContinueStmt& stmt) override; 14 | std::string visitEnumStmt(EnumStmt& stmt) override; 15 | std::string visitExpressionStmt(ExpressionStmt& stmt) override; 16 | std::string visitFunctionStmt(FunctionStmt& stmt) override; 17 | std::string visitImplStmt(ImplStmt& stmt) override; 18 | std::string visitReturnStmt(ReturnStmt& stmt) override; 19 | std::string visitStructStmt(StructStmt& stmt) override; 20 | std::string visitTraitStmt(TraitStmt& stmt) override; 21 | std::string visitVariableStmt(VariableStmt& stmt) override; 22 | 23 | std::string visitAssignExpr(AssignExpr& expr) override; 24 | std::string visitBinaryExpr(BinaryExpr& expr) override; 25 | std::string visitBlockExpr(BlockExpr& expr) override; 26 | std::string visitBooleanExpr(BooleanExpr& expr) override; 27 | std::string visitCallExpr(CallExpr& expr) override; 28 | std::string visitCastExpr(CastExpr& expr) override; 29 | std::string visitFloatExpr(FloatExpr& expr) override; 30 | std::string visitForExpr(ForExpr& expr) override; 31 | std::string visitGetExpr(FieldExpr& expr) override; 32 | std::string visitIfExpr(IfExpr& expr) override; 33 | std::string visitIntegerExpr(IntegerExpr& expr) override; 34 | std::string visitInterpolationExpr(InterpolationExpr& expr) override; 35 | std::string visitLogicalExpr(LogicalExpr& expr) override; 36 | std::string visitReferenceExpr(ReferenceExpr& expr) override; 37 | std::string visitStringExpr(StringExpr& expr) override; 38 | std::string visitSwitchExpr(SwitchExpr& expr) override; 39 | std::string visitSymbolExpr(SymbolExpr& expr) override; 40 | std::string visitTupleExpr(TupleExpr& expr) override; 41 | std::string visitUnaryExpr(UnaryExpr& expr) override; 42 | std::string visitUnitExpr(UnitExpr& expr) override; 43 | std::string visitWhileExpr(WhileExpr& expr) override; 44 | 45 | std::string visitValuePattern(ValuePattern& pattern) override; 46 | std::string visitWildcardPattern(WildcardPattern& pattern) override; 47 | 48 | public: 49 | std::string operator()(Stmt& stmt); 50 | std::string operator()(Expr& expr); 51 | }; 52 | } 53 | 54 | #endif //ENACT_ASTSERIALISE_H 55 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(ast) 2 | add_subdirectory(context) 3 | add_subdirectory(parser) 4 | 5 | set(ENACT_SRC 6 | ${AST_SRC} 7 | ${CONTEXT_SRC} 8 | ${PARSER_SRC} 9 | 10 | ${CMAKE_CURRENT_SOURCE_DIR}/AstSerialise.cpp 11 | ${CMAKE_CURRENT_SOURCE_DIR}/AstSerialise.h 12 | ${CMAKE_CURRENT_SOURCE_DIR}/common.h 13 | ${CMAKE_CURRENT_SOURCE_DIR}/InsertionOrderMap.h 14 | ${CMAKE_CURRENT_SOURCE_DIR}/trivialStructs.h) 15 | set(ENACT_SRC ${ENACT_SRC} PARENT_SCOPE) 16 | 17 | add_library(enact ${ENACT_SRC}) -------------------------------------------------------------------------------- /lib/InsertionOrderMap.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_INSERTIONORDERMAP_H 2 | #define ENACT_INSERTIONORDERMAP_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace enact { 11 | template 12 | class InsertionOrderMapIter; 13 | 14 | template 15 | class InsertionOrderMapConstIter; 16 | 17 | template 18 | class InsertionOrderMap { 19 | friend class InsertionOrderMapIter; 20 | 21 | friend class InsertionOrderMapConstIter; 22 | 23 | std::unordered_map m_map{}; 24 | std::vector m_insertionOrder{}; 25 | 26 | public: 27 | /* Constructors */ 28 | InsertionOrderMap() = default; 29 | 30 | InsertionOrderMap(std::initializer_list> values) : 31 | m_map{values}, 32 | m_insertionOrder{} { 33 | std::transform(begin(values), end(values), 34 | std::back_inserter(m_insertionOrder), 35 | [](const auto &pair) { return pair.first; }); 36 | }; 37 | 38 | /* Iterators */ 39 | using iterator = InsertionOrderMapIter; 40 | using const_iterator = InsertionOrderMapConstIter; 41 | 42 | iterator begin() { 43 | return iterator{*this, 0}; 44 | } 45 | 46 | iterator end() { 47 | return iterator{*this, length()}; 48 | } 49 | 50 | const_iterator begin() const { 51 | return const_iterator{*this, 0}; 52 | } 53 | 54 | const_iterator end() const { 55 | return const_iterator{*this, length()}; 56 | } 57 | 58 | const_iterator cbegin() const { 59 | return const_iterator{*this, 0}; 60 | } 61 | 62 | const_iterator cend() const { 63 | return const_iterator{*this, length()}; 64 | } 65 | 66 | /* Length */ 67 | bool empty() const { 68 | return length() == 0; 69 | } 70 | 71 | size_t length() const { 72 | return m_insertionOrder.size(); 73 | } 74 | 75 | /* Insertion and removal */ 76 | void insert(const std::pair &value) { 77 | m_map.insert(value); 78 | m_insertionOrder.push_back(value.first); 79 | } 80 | 81 | template 82 | void emplace(const KeyType& key, Args... constructorArgs) { 83 | m_map.emplace(key, constructorArgs...); 84 | m_insertionOrder.push_back(key); 85 | } 86 | 87 | void insertOrAssign(const std::pair &value) { 88 | if (contains(value.first)) { 89 | m_map[value.first] = value.second; 90 | m_insertionOrder.push_back(value.first); 91 | } else { 92 | insert(value); 93 | } 94 | } 95 | 96 | template 97 | void emplaceOrAssign(const KeyType& key, Args... constructorArgs) { 98 | if (contains(key)) { 99 | m_map[key] = ValueType{constructorArgs...}; 100 | m_insertionOrder.push_back(key); 101 | } else { 102 | emplace(key, constructorArgs...); 103 | } 104 | } 105 | 106 | void erase(size_t index) { 107 | m_map.erase(m_insertionOrder[index]); 108 | m_insertionOrder.erase(index); 109 | } 110 | 111 | void clear() { 112 | m_map.clear(); 113 | m_insertionOrder.clear(); 114 | } 115 | 116 | /* Element access */ 117 | ValueType &operator[](const KeyType &key) { 118 | return m_map[key]; 119 | } 120 | 121 | const ValueType &operator[](const KeyType &key) const { 122 | return m_map[key]; 123 | } 124 | 125 | std::optional> at(const KeyType &key) { 126 | if (contains(key)) return m_map.at(key); 127 | return {}; 128 | } 129 | 130 | std::optional> at(const KeyType &key) const { 131 | if (contains(key)) return m_map.at(key); 132 | return {}; 133 | } 134 | 135 | std::optional> atIndex(size_t index) { 136 | if (index < length()) return m_map[m_insertionOrder[index]]; 137 | return {}; 138 | } 139 | 140 | std::optional> atIndex(size_t index) const { 141 | if (index < length()) return m_map[m_insertionOrder[index]]; 142 | return {}; 143 | } 144 | 145 | std::optional find(const KeyType &key) const { 146 | size_t index = 0; 147 | for (const auto &myKeyType : m_insertionOrder) { 148 | if (myKeyType == key) return index; 149 | ++index; 150 | } 151 | 152 | return {}; 153 | } 154 | 155 | /* Checking */ 156 | size_t count(const KeyType &key) const { 157 | return m_map.count(key); 158 | } 159 | 160 | bool contains(const KeyType &key) const { 161 | return count(key) > 0; 162 | } 163 | 164 | /* Keys and values */ 165 | std::vector> keys() { 166 | std::vector> keys{}; 167 | std::transform( 168 | m_insertionOrder.begin(), m_insertionOrder.end(), 169 | std::back_inserter(keys), 170 | [](auto &key) { return std::ref(key.get()); }); 171 | return keys; 172 | } 173 | 174 | std::vector> keys() const { 175 | std::vector> keys{}; 176 | std::transform( 177 | m_insertionOrder.begin(), m_insertionOrder.end(), 178 | std::back_inserter(keys), 179 | [](const auto &key) { return std::cref(key); }); 180 | return keys; 181 | } 182 | 183 | std::vector> values() { 184 | std::vector> values; 185 | std::transform( 186 | m_insertionOrder.begin(), m_insertionOrder.end(), 187 | std::back_inserter(values), 188 | [this](auto &key) { return std::ref(m_map[key]); }); 189 | return values; 190 | } 191 | 192 | std::vector> values() const { 193 | std::vector> values; 194 | std::transform( 195 | m_insertionOrder.begin(), m_insertionOrder.end(), 196 | std::back_inserter(values), 197 | [this](const auto &key) { return std::cref(m_map.at(key)); }); 198 | return values; 199 | } 200 | }; 201 | 202 | template 203 | class InsertionOrderMapIter { 204 | InsertionOrderMap &m_map; 205 | size_t m_index; 206 | 207 | public: 208 | InsertionOrderMapIter(InsertionOrderMap &map, size_t index) : 209 | m_map{map}, 210 | m_index{index} { 211 | } 212 | 213 | std::pair &operator*() { 214 | return *m_map.m_map.find(m_map.m_insertionOrder[m_index]); 215 | } 216 | 217 | InsertionOrderMapIter &operator++() { 218 | ++m_index; 219 | return *this; 220 | } 221 | 222 | InsertionOrderMapIter operator++(int) { 223 | return InsertionOrderMapIter{m_map, m_index++}; 224 | } 225 | 226 | InsertionOrderMapIter &operator--() { 227 | --m_index; 228 | return *this; 229 | } 230 | 231 | InsertionOrderMapIter operator--(int) { 232 | return InsertionOrderMapIter{m_map, m_index--}; 233 | } 234 | 235 | bool operator==(const InsertionOrderMapIter &other) const { 236 | return &m_map == &other.m_map && m_index == other.m_index; 237 | } 238 | 239 | bool operator!=(const InsertionOrderMapIter &other) const { 240 | return &m_map != &other.m_map || m_index != other.m_index; 241 | } 242 | }; 243 | 244 | template 245 | class InsertionOrderMapConstIter { 246 | const InsertionOrderMap &m_map; 247 | size_t m_index; 248 | 249 | public: 250 | InsertionOrderMapConstIter(const InsertionOrderMap &map, size_t index) : 251 | m_map{map}, 252 | m_index{index} { 253 | } 254 | 255 | const std::pair &operator*() { 256 | return *m_map.m_map.find(m_map.m_insertionOrder[m_index]); 257 | }; 258 | 259 | InsertionOrderMapConstIter &operator++() { 260 | ++m_index; 261 | return *this; 262 | } 263 | 264 | InsertionOrderMapConstIter operator++(int) { 265 | return InsertionOrderMapIter{m_map, m_index++}; 266 | } 267 | 268 | InsertionOrderMapConstIter &operator--() { 269 | --m_index; 270 | return *this; 271 | } 272 | 273 | InsertionOrderMapConstIter operator--(int) { 274 | return InsertionOrderMapIter{m_map, m_index--}; 275 | } 276 | 277 | bool operator==(const InsertionOrderMapConstIter &other) const { 278 | return &m_map == &other.m_map && m_index == other.m_index; 279 | } 280 | 281 | bool operator!=(const InsertionOrderMapConstIter &other) const { 282 | return &m_map != &other.m_map || m_index != other.m_index; 283 | } 284 | }; 285 | } 286 | 287 | #endif //ENACT_INSERTIONORDERMAP_H 288 | -------------------------------------------------------------------------------- /lib/Natives.cpp: -------------------------------------------------------------------------------- 1 | #include "bytecode/Chunk.h" 2 | #include "value/Object.h" 3 | 4 | #include "Natives.h" 5 | 6 | namespace enact { 7 | Value Natives::print(uint8_t argCount, Value *args) { 8 | std::cout << args[0] << "\n"; 9 | return Value{}; 10 | } 11 | 12 | Value Natives::put(uint8_t argCount, Value *args) { 13 | std::cout << args[0]; 14 | return Value{}; 15 | } 16 | 17 | Value Natives::dis(uint8_t count, Value *args) { 18 | Chunk &chunk = args[0].asObject()->as()->getFunction()->getChunk(); 19 | return Value{new StringObject{chunk.disassemble()}}; 20 | } 21 | } -------------------------------------------------------------------------------- /lib/Natives.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_NATIVES_H 2 | #define ENACT_NATIVES_H 3 | 4 | #include "value/Value.h" 5 | 6 | namespace enact { 7 | namespace Natives { 8 | Value print(uint8_t argCount, Value *args); 9 | Value put(uint8_t argCount, Value *args); 10 | Value dis(uint8_t argCount, Value *args); 11 | } 12 | } 13 | 14 | #endif //ENACT_NATIVES_H 15 | -------------------------------------------------------------------------------- /lib/analyser/Analyser.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_ANALYSER_H 2 | #define ENACT_ANALYSER_H 3 | 4 | #include "../ast/Stmt.h" 5 | #include "../type/Type.h" 6 | 7 | namespace enact { 8 | class CompileContext; 9 | 10 | // Walks the AST and assigns a Type to each node. 11 | 12 | class Analyser : private StmtVisitor, private ExprVisitor { 13 | CompileContext &m_context; 14 | 15 | std::unordered_map m_types{ 16 | std::pair("int", INT_TYPE), 17 | std::pair("float", FLOAT_TYPE), 18 | std::pair("bool", BOOL_TYPE), 19 | std::pair("String", STRING_TYPE), 20 | std::pair("any", DYNAMIC_TYPE), 21 | std::pair("nothing", NOTHING_TYPE), 22 | }; 23 | 24 | struct Variable { 25 | Type type = nullptr; 26 | bool isConst = false; 27 | }; 28 | 29 | std::vector> m_scopes{}; 30 | 31 | // Keep track of whether we can use break/continue 32 | uint32_t m_loopCount = 0; 33 | 34 | // Keep track of the current function type to see if return statements are valid. Acts like a stack for nested 35 | // functions. If the stack is empty, then we are at the global scope. 36 | std::vector m_currentFunctions{}; 37 | 38 | // Keep track of functions that need to be analysed later 39 | std::vector> m_globalFunctions{}; 40 | 41 | bool m_hadError = false; 42 | 43 | void analyse(Stmt &stmt); 44 | void analyse(Expr &expr); 45 | 46 | void analyseFunctionBody(FunctionStmt &stmt); 47 | 48 | Type getFunctionType(const FunctionStmt &stmt, bool isMethod = false, bool isNative = false); 49 | 50 | Variable &lookUpVariable(const Token &name); 51 | void declareVariable(const std::string &name, const Variable &variable); 52 | 53 | void beginScope(); 54 | void endScope(); 55 | 56 | class AnalysisError : public std::runtime_error { 57 | public: 58 | AnalysisError() : std::runtime_error{ 59 | "Internal error, raising exception:\nUncaught AnalysisError! Private Analyser::analyse() was likely called by mistake where public Analyser::analyse() was supposed to be called instead."} {} 60 | }; 61 | 62 | AnalysisError errorAt(const Token &token, const std::string &message); 63 | 64 | void visitBlockStmt(BlockStmt &stmt) override; 65 | void visitBreakStmt(BreakStmt &stmt) override; 66 | void visitContinueStmt(ContinueStmt &stmt) override; 67 | void visitEachStmt(EachStmt &stmt) override; 68 | void visitExpressionStmt(ExpressionStmt &stmt) override; 69 | void visitForStmt(ForStmt &stmt) override; 70 | void visitFunctionStmt(FunctionStmt &stmt) override; 71 | void visitGivenStmt(GivenStmt &stmt) override; 72 | void visitIfStmt(IfStmt &stmt) override; 73 | void visitReturnStmt(ReturnStmt &stmt) override; 74 | void visitStructStmt(StructStmt &stmt) override; 75 | void visitTraitStmt(TraitStmt &stmt) override; 76 | void visitWhileStmt(WhileStmt &stmt) override; 77 | void visitVariableStmt(VariableStmt &stmt) override; 78 | void visitAllotExpr(AllotExpr &expr) override; 79 | void visitAnyExpr(AnyExpr &expr) override; 80 | void visitArrayExpr(ArrayExpr &expr) override; 81 | void visitAssignExpr(AssignExpr &expr) override; 82 | void visitBinaryExpr(BinaryExpr &expr) override; 83 | void visitBooleanExpr(BooleanExpr &expr) override; 84 | void visitCallExpr(CallExpr &expr) override; 85 | void visitFloatExpr(FloatExpr &expr) override; 86 | void visitGetExpr(GetExpr &expr) override; 87 | void visitIntegerExpr(IntegerExpr &expr) override; 88 | void visitLogicalExpr(LogicalExpr &expr) override; 89 | void visitNilExpr(NilExpr &expr) override; 90 | void visitSetExpr(SetExpr &expr) override; 91 | void visitStringExpr(StringExpr &expr) override; 92 | void visitSubscriptExpr(SubscriptExpr &expr) override; 93 | void visitTernaryExpr(TernaryExpr &expr) override; 94 | void visitUnaryExpr(UnaryExpr &expr) override; 95 | void visitVariableExpr(VariableExpr &expr) override; 96 | 97 | public: 98 | Analyser(CompileContext &context); 99 | ~Analyser() = default; 100 | 101 | std::vector> analyse(std::vector> ast); 102 | 103 | Type lookUpType(const Typename &name); 104 | 105 | bool hadError(); 106 | }; 107 | } 108 | 109 | #endif //ENACT_ANALYSER_H 110 | -------------------------------------------------------------------------------- /lib/analyser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(ANALYSER_SRC 2 | ${CMAKE_CURRENT_SOURCE_DIR}/Analyser.cpp 3 | ${CMAKE_CURRENT_SOURCE_DIR}/Analyser.h 4 | 5 | PARENT_SCOPE) -------------------------------------------------------------------------------- /lib/ast/AstVisitor.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_ASTVISITOR_H 2 | #define ENACT_ASTVISITOR_H 3 | 4 | #include "Stmt.h" 5 | 6 | namespace enact { 7 | template 8 | class AstVisitor : public StmtVisitor, public ExprVisitor, public PatternVisitor { 9 | }; 10 | } 11 | 12 | #endif //ENACT_ASTVISITOR_H 13 | -------------------------------------------------------------------------------- /lib/ast/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(AST_SRC 2 | ${CMAKE_CURRENT_SOURCE_DIR}/AstVisitor.h 3 | ${CMAKE_CURRENT_SOURCE_DIR}/Expr.h 4 | ${CMAKE_CURRENT_SOURCE_DIR}/Stmt.h 5 | 6 | PARENT_SCOPE) -------------------------------------------------------------------------------- /lib/ast/Expr.h: -------------------------------------------------------------------------------- 1 | // This file was automatically generated. 2 | // "See generate.py" for details. 3 | 4 | #ifndef ENACT_EXPR_H 5 | #define ENACT_EXPR_H 6 | 7 | #include 8 | #include 9 | 10 | #include "../parser/Typename.h" 11 | 12 | #include "Pattern.h" 13 | 14 | namespace enact { 15 | // From "Stmt.h": 16 | class Stmt; 17 | 18 | template 19 | class ExprVisitor; 20 | 21 | class Expr { 22 | public: 23 | //SemaInfo semaInfo{}; 24 | 25 | virtual ~Expr() = default; 26 | 27 | // We need to overload for every possible visitor return type here, as we cannot 28 | // have a templated virtual member function. 29 | virtual std::string accept(ExprVisitor *visitor) = 0; 30 | virtual void accept(ExprVisitor *visitor) = 0; 31 | }; 32 | 33 | class AssignExpr; 34 | class BinaryExpr; 35 | class BlockExpr; 36 | class BooleanExpr; 37 | class CallExpr; 38 | class CastExpr; 39 | class FloatExpr; 40 | class ForExpr; 41 | class FieldExpr; 42 | class IfExpr; 43 | class IntegerExpr; 44 | class InterpolationExpr; 45 | class LogicalExpr; 46 | class ReferenceExpr; 47 | class StringExpr; 48 | class SwitchExpr; 49 | class SymbolExpr; 50 | class TupleExpr; 51 | class UnaryExpr; 52 | class UnitExpr; 53 | class WhileExpr; 54 | 55 | template 56 | class ExprVisitor { 57 | public: 58 | R visitExpr(Expr& expr) { 59 | return expr.accept(this); 60 | } 61 | 62 | virtual R visitAssignExpr(AssignExpr& expr) = 0; 63 | virtual R visitBinaryExpr(BinaryExpr& expr) = 0; 64 | virtual R visitBlockExpr(BlockExpr& expr) = 0; 65 | virtual R visitBooleanExpr(BooleanExpr& expr) = 0; 66 | virtual R visitCallExpr(CallExpr& expr) = 0; 67 | virtual R visitCastExpr(CastExpr& expr) = 0; 68 | virtual R visitFloatExpr(FloatExpr& expr) = 0; 69 | virtual R visitForExpr(ForExpr& expr) = 0; 70 | virtual R visitGetExpr(FieldExpr& expr) = 0; 71 | virtual R visitIfExpr(IfExpr& expr) = 0; 72 | virtual R visitIntegerExpr(IntegerExpr& expr) = 0; 73 | virtual R visitInterpolationExpr(InterpolationExpr& expr) = 0; 74 | virtual R visitLogicalExpr(LogicalExpr& expr) = 0; 75 | virtual R visitReferenceExpr(ReferenceExpr& expr) = 0; 76 | virtual R visitStringExpr(StringExpr& expr) = 0; 77 | virtual R visitSwitchExpr(SwitchExpr& expr) = 0; 78 | virtual R visitSymbolExpr(SymbolExpr& expr) = 0; 79 | virtual R visitTupleExpr(TupleExpr& expr) = 0; 80 | virtual R visitUnaryExpr(UnaryExpr& expr) = 0; 81 | virtual R visitUnitExpr(UnitExpr& expr) = 0; 82 | virtual R visitWhileExpr(WhileExpr& expr) = 0; 83 | }; 84 | 85 | class AssignExpr : public Expr { 86 | public: 87 | std::unique_ptr target; 88 | std::unique_ptr value; 89 | Token oper; 90 | 91 | AssignExpr(std::unique_ptr target, std::unique_ptr value, Token oper) : 92 | target{std::move(target)}, 93 | value{std::move(value)}, 94 | oper{std::move(oper)} {} 95 | 96 | ~AssignExpr() override = default; 97 | 98 | std::string accept(ExprVisitor *visitor) override { 99 | return visitor->visitAssignExpr(*this); 100 | } 101 | 102 | void accept(ExprVisitor *visitor) override { 103 | return visitor->visitAssignExpr(*this); 104 | } 105 | }; 106 | 107 | class BinaryExpr : public Expr { 108 | public: 109 | std::unique_ptr left; 110 | std::unique_ptr right; 111 | Token oper; 112 | 113 | BinaryExpr(std::unique_ptr left, std::unique_ptr right, Token oper) : 114 | left{std::move(left)}, 115 | right{std::move(right)}, 116 | oper{std::move(oper)} {} 117 | 118 | ~BinaryExpr() override = default; 119 | 120 | std::string accept(ExprVisitor *visitor) override { 121 | return visitor->visitBinaryExpr(*this); 122 | } 123 | 124 | void accept(ExprVisitor *visitor) override { 125 | return visitor->visitBinaryExpr(*this); 126 | } 127 | }; 128 | 129 | class BlockExpr : public Expr { 130 | public: 131 | // Statements preceeding the final expression 132 | std::vector> stmts; 133 | 134 | // The final expression 135 | std::unique_ptr expr; 136 | 137 | BlockExpr(std::vector> stmts, std::unique_ptr expr) : 138 | stmts{std::move(stmts)}, 139 | expr{std::move(expr)} {} 140 | 141 | ~BlockExpr() override = default; 142 | 143 | std::string accept(ExprVisitor *visitor) override { 144 | return visitor->visitBlockExpr(*this); 145 | } 146 | 147 | void accept(ExprVisitor *visitor) override { 148 | return visitor->visitBlockExpr(*this); 149 | } 150 | }; 151 | 152 | class BooleanExpr : public Expr { 153 | public: 154 | bool value; 155 | 156 | explicit BooleanExpr(bool value) : 157 | value{value} {} 158 | 159 | ~BooleanExpr() override = default; 160 | 161 | std::string accept(ExprVisitor *visitor) override { 162 | return visitor->visitBooleanExpr(*this); 163 | } 164 | 165 | void accept(ExprVisitor *visitor) override { 166 | return visitor->visitBooleanExpr(*this); 167 | } 168 | }; 169 | 170 | class CallExpr : public Expr { 171 | public: 172 | std::unique_ptr callee; 173 | std::vector> args; 174 | Token paren; 175 | 176 | CallExpr(std::unique_ptr callee, std::vector> args, Token paren) : 177 | callee{std::move(callee)}, 178 | args{std::move(args)}, 179 | paren{std::move(paren)} {} 180 | 181 | ~CallExpr() override = default; 182 | 183 | std::string accept(ExprVisitor *visitor) override { 184 | return visitor->visitCallExpr(*this); 185 | } 186 | 187 | void accept(ExprVisitor *visitor) override { 188 | return visitor->visitCallExpr(*this); 189 | } 190 | }; 191 | 192 | class CastExpr : public Expr { 193 | public: 194 | public: 195 | std::unique_ptr expr; 196 | std::unique_ptr typename_; 197 | Token oper; 198 | 199 | CastExpr(std::unique_ptr expr, std::unique_ptr typename_, Token oper) : 200 | expr{std::move(expr)}, 201 | typename_{std::move(typename_)}, 202 | oper{std::move(oper)} {} 203 | 204 | ~CastExpr() override = default; 205 | 206 | std::string accept(ExprVisitor *visitor) override { 207 | return visitor->visitCastExpr(*this); 208 | } 209 | 210 | void accept(ExprVisitor *visitor) override { 211 | return visitor->visitCastExpr(*this); 212 | } 213 | }; 214 | 215 | class FloatExpr : public Expr { 216 | public: 217 | double value; 218 | 219 | explicit FloatExpr(double value) : 220 | value{value} {} 221 | 222 | ~FloatExpr() override = default; 223 | 224 | std::string accept(ExprVisitor *visitor) override { 225 | return visitor->visitFloatExpr(*this); 226 | } 227 | 228 | void accept(ExprVisitor *visitor) override { 229 | return visitor->visitFloatExpr(*this); 230 | } 231 | }; 232 | 233 | class ForExpr : public Expr { 234 | public: 235 | Token name; 236 | std::unique_ptr object; 237 | std::unique_ptr body; 238 | 239 | ForExpr(Token name, std::unique_ptr object, std::unique_ptr body) : 240 | name{std::move(name)}, 241 | object{std::move(object)}, 242 | body{std::move(body)} {} 243 | 244 | ~ForExpr() override = default; 245 | 246 | std::string accept(ExprVisitor *visitor) override { 247 | return visitor->visitForExpr(*this); 248 | } 249 | 250 | void accept(ExprVisitor *visitor) override { 251 | return visitor->visitForExpr(*this); 252 | } 253 | }; 254 | 255 | class FieldExpr : public Expr { 256 | public: 257 | std::unique_ptr object; 258 | Token name; 259 | Token oper; 260 | 261 | FieldExpr(std::unique_ptr object, Token name, Token oper) : 262 | object{std::move(object)}, 263 | name{std::move(name)}, 264 | oper{std::move(oper)} {} 265 | 266 | ~FieldExpr() override = default; 267 | 268 | std::string accept(ExprVisitor *visitor) override { 269 | return visitor->visitGetExpr(*this); 270 | } 271 | 272 | void accept(ExprVisitor *visitor) override { 273 | return visitor->visitGetExpr(*this); 274 | } 275 | }; 276 | 277 | class IfExpr : public Expr { 278 | public: 279 | std::unique_ptr condition; 280 | std::unique_ptr thenBody; 281 | std::unique_ptr elseBody; 282 | Token keyword; 283 | 284 | IfExpr(std::unique_ptr condition, 285 | std::unique_ptr thenBody, 286 | std::unique_ptr elseBody, 287 | Token keyword) : 288 | condition{std::move(condition)}, 289 | thenBody{std::move(thenBody)}, 290 | elseBody{std::move(elseBody)}, 291 | keyword{std::move(keyword)} {} 292 | 293 | ~IfExpr() override = default; 294 | 295 | std::string accept(ExprVisitor *visitor) override { 296 | return visitor->visitIfExpr(*this); 297 | } 298 | 299 | void accept(ExprVisitor *visitor) override { 300 | return visitor->visitIfExpr(*this); 301 | } 302 | }; 303 | 304 | class IntegerExpr : public Expr { 305 | public: 306 | int value; 307 | 308 | explicit IntegerExpr(int value) : 309 | value{value} {} 310 | 311 | ~IntegerExpr() override = default; 312 | 313 | std::string accept(ExprVisitor *visitor) override { 314 | return visitor->visitIntegerExpr(*this); 315 | } 316 | 317 | void accept(ExprVisitor *visitor) override { 318 | return visitor->visitIntegerExpr(*this); 319 | } 320 | }; 321 | 322 | class InterpolationExpr : public Expr { 323 | public: 324 | std::unique_ptr start; 325 | std::unique_ptr interpolated; 326 | std::unique_ptr end; 327 | Token token; 328 | 329 | InterpolationExpr( 330 | std::unique_ptr start, 331 | std::unique_ptr interpolated, 332 | std::unique_ptr end, 333 | Token token) : 334 | start{std::move(start)}, 335 | interpolated{std::move(interpolated)}, 336 | end{std::move(end)}, 337 | token{std::move(token)} {} 338 | 339 | ~InterpolationExpr() override = default; 340 | 341 | std::string accept(ExprVisitor *visitor) override { 342 | return visitor->visitInterpolationExpr(*this); 343 | } 344 | 345 | void accept(ExprVisitor *visitor) override { 346 | return visitor->visitInterpolationExpr(*this); 347 | } 348 | }; 349 | 350 | class LogicalExpr : public Expr { 351 | public: 352 | std::unique_ptr left; 353 | std::unique_ptr right; 354 | Token oper; 355 | 356 | LogicalExpr(std::unique_ptr left, std::unique_ptr right, Token oper) : 357 | left{std::move(left)}, 358 | right{std::move(right)}, 359 | oper{std::move(oper)} {} 360 | 361 | ~LogicalExpr() override = default; 362 | 363 | std::string accept(ExprVisitor *visitor) override { 364 | return visitor->visitLogicalExpr(*this); 365 | } 366 | 367 | void accept(ExprVisitor *visitor) override { 368 | return visitor->visitLogicalExpr(*this); 369 | } 370 | }; 371 | 372 | class ReferenceExpr : public Expr { 373 | public: 374 | std::unique_ptr expr; 375 | Token oper; 376 | std::optional permission; 377 | std::optional region; 378 | 379 | ReferenceExpr( 380 | std::unique_ptr expr, 381 | Token oper, 382 | std::optional permission, 383 | std::optional region) : 384 | expr{std::move(expr)}, 385 | oper{std::move(oper)}, 386 | permission{std::move(permission)}, 387 | region{std::move(region)} {} 388 | 389 | ~ReferenceExpr() override = default; 390 | 391 | std::string accept(ExprVisitor *visitor) override { 392 | return visitor->visitReferenceExpr(*this); 393 | } 394 | 395 | void accept(ExprVisitor *visitor) override { 396 | return visitor->visitReferenceExpr(*this); 397 | } 398 | }; 399 | 400 | class StringExpr : public Expr { 401 | public: 402 | std::string value; 403 | 404 | explicit StringExpr(std::string value) : 405 | value{std::move(value)} {} 406 | 407 | ~StringExpr() override = default; 408 | 409 | std::string accept(ExprVisitor *visitor) override { 410 | return visitor->visitStringExpr(*this); 411 | } 412 | 413 | void accept(ExprVisitor *visitor) override { 414 | return visitor->visitStringExpr(*this); 415 | } 416 | }; 417 | 418 | struct SwitchCase { 419 | std::unique_ptr pattern; 420 | std::unique_ptr predicate; 421 | std::unique_ptr body; 422 | Token keyword; 423 | }; 424 | 425 | class SwitchExpr : public Expr { 426 | public: 427 | std::unique_ptr value; 428 | std::vector cases; 429 | 430 | SwitchExpr(std::unique_ptr value, std::vector&& cases) : 431 | value{std::move(value)}, 432 | cases{std::move(cases)} {} 433 | 434 | ~SwitchExpr() override = default; 435 | 436 | std::string accept(ExprVisitor *visitor) override { 437 | return visitor->visitSwitchExpr(*this); 438 | } 439 | 440 | void accept(ExprVisitor *visitor) override { 441 | return visitor->visitSwitchExpr(*this); 442 | } 443 | }; 444 | 445 | class SymbolExpr : public Expr { 446 | public: 447 | Token name; 448 | 449 | explicit SymbolExpr(Token name) : 450 | name{std::move(name)} {} 451 | 452 | ~SymbolExpr() override = default; 453 | 454 | std::string accept(ExprVisitor *visitor) override { 455 | return visitor->visitSymbolExpr(*this); 456 | } 457 | 458 | void accept(ExprVisitor *visitor) override { 459 | return visitor->visitSymbolExpr(*this); 460 | } 461 | }; 462 | 463 | class TupleExpr : public Expr { 464 | public: 465 | std::vector> elems; 466 | Token paren; 467 | 468 | TupleExpr(std::vector> elems, Token paren) : 469 | elems{std::move(elems)}, 470 | paren{std::move(paren)} {} 471 | 472 | ~TupleExpr() override = default; 473 | 474 | std::string accept(ExprVisitor *visitor) override { 475 | return visitor->visitTupleExpr(*this); 476 | } 477 | 478 | void accept(ExprVisitor *visitor) override { 479 | return visitor->visitTupleExpr(*this); 480 | } 481 | }; 482 | 483 | class UnaryExpr : public Expr { 484 | public: 485 | std::unique_ptr operand; 486 | Token oper; 487 | 488 | UnaryExpr(std::unique_ptr operand, Token oper) : 489 | operand{std::move(operand)}, 490 | oper{std::move(oper)} {} 491 | 492 | ~UnaryExpr() override = default; 493 | 494 | std::string accept(ExprVisitor *visitor) override { 495 | return visitor->visitUnaryExpr(*this); 496 | } 497 | 498 | void accept(ExprVisitor *visitor) override { 499 | return visitor->visitUnaryExpr(*this); 500 | } 501 | }; 502 | 503 | class UnitExpr : public Expr { 504 | public: 505 | Token token; 506 | 507 | UnitExpr(Token token) : 508 | token{std::move(token)} {} 509 | 510 | ~UnitExpr() override = default; 511 | 512 | std::string accept(ExprVisitor *visitor) override { 513 | return visitor->visitUnitExpr(*this); 514 | } 515 | 516 | void accept(ExprVisitor *visitor) override { 517 | return visitor->visitUnitExpr(*this); 518 | } 519 | }; 520 | 521 | class WhileExpr : public Expr { 522 | public: 523 | std::unique_ptr condition; 524 | std::unique_ptr body; 525 | Token keyword; 526 | 527 | WhileExpr(std::unique_ptr condition, std::unique_ptr body, Token keyword) : 528 | condition{std::move(condition)}, 529 | body{std::move(body)}, 530 | keyword{std::move(keyword)} {} 531 | 532 | ~WhileExpr() override = default; 533 | 534 | std::string accept(ExprVisitor *visitor) override { 535 | return visitor->visitWhileExpr(*this); 536 | } 537 | 538 | void accept(ExprVisitor *visitor) override { 539 | return visitor->visitWhileExpr(*this); 540 | } 541 | }; 542 | } 543 | 544 | #endif // ENACT_EXPR_H 545 | -------------------------------------------------------------------------------- /lib/ast/Pattern.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_PATTERN_H 2 | #define ENACT_PATTERN_H 3 | 4 | namespace enact { 5 | // From "Expr.h": 6 | class Expr; 7 | 8 | template 9 | class PatternVisitor; 10 | 11 | class Pattern { 12 | public: 13 | virtual ~Pattern() = default; 14 | 15 | // We need to overload for every possible visitor return type here, as we cannot 16 | // have a templated virtual member function. 17 | virtual std::string accept(PatternVisitor *visitor) = 0; 18 | virtual void accept(PatternVisitor *visitor) = 0; 19 | }; 20 | 21 | class ValuePattern; 22 | class WildcardPattern; 23 | 24 | template 25 | class PatternVisitor { 26 | public: 27 | R visitPattern(Pattern &pattern) { 28 | return pattern.accept(this); 29 | } 30 | 31 | virtual R visitValuePattern(ValuePattern& pattern) = 0; 32 | virtual R visitWildcardPattern(WildcardPattern& pattern) = 0; 33 | // TODO: add other pattern types 34 | }; 35 | 36 | class ValuePattern : public Pattern { 37 | public: 38 | std::unique_ptr value; 39 | 40 | ValuePattern(std::unique_ptr value) : 41 | value{std::move(value)} {} 42 | 43 | ~ValuePattern() override = default; 44 | 45 | std::string accept(PatternVisitor* visitor) override { 46 | return visitor->visitValuePattern(*this); 47 | } 48 | 49 | void accept(PatternVisitor* visitor) override { 50 | return visitor->visitValuePattern(*this); 51 | } 52 | }; 53 | 54 | class WildcardPattern : public Pattern { 55 | public: 56 | Token keyword; 57 | 58 | WildcardPattern(Token keyword) : 59 | keyword{std::move(keyword)} {} 60 | 61 | ~WildcardPattern() override = default; 62 | 63 | std::string accept(PatternVisitor* visitor) override { 64 | return visitor->visitWildcardPattern(*this); 65 | } 66 | 67 | void accept(PatternVisitor* visitor) override { 68 | return visitor->visitWildcardPattern(*this); 69 | } 70 | }; 71 | } 72 | 73 | #endif //ENACT_PATTERN_H 74 | -------------------------------------------------------------------------------- /lib/ast/Stmt.h: -------------------------------------------------------------------------------- 1 | // This file was automatically generated. 2 | // "See generate.py" for details. 3 | 4 | #ifndef ENACT_STMT_H 5 | #define ENACT_STMT_H 6 | 7 | #include "../trivialStructs.h" 8 | 9 | #include "Expr.h" 10 | 11 | namespace enact { 12 | template 13 | class StmtVisitor; 14 | 15 | class Stmt { 16 | public: 17 | virtual ~Stmt() = default; 18 | 19 | // We need to overload for every possible visitor return type here, as we cannot 20 | // have a templated virtual member function. 21 | virtual std::string accept(StmtVisitor *visitor) = 0; 22 | virtual void accept(StmtVisitor *visitor) = 0; 23 | }; 24 | 25 | class BreakStmt; 26 | class ContinueStmt; 27 | class EnumStmt; 28 | class ExpressionStmt; 29 | class FunctionStmt; 30 | class ImplStmt; 31 | class ReturnStmt; 32 | class StructStmt; 33 | class TraitStmt; 34 | class VariableStmt; 35 | 36 | template 37 | class StmtVisitor { 38 | public: 39 | R visitStmt(Stmt& stmt) { 40 | return stmt.accept(this); 41 | }; 42 | 43 | virtual R visitBreakStmt(BreakStmt &stmt) = 0; 44 | virtual R visitContinueStmt(ContinueStmt &stmt) = 0; 45 | virtual R visitEnumStmt(EnumStmt& stmt) = 0; 46 | virtual R visitExpressionStmt(ExpressionStmt &stmt) = 0; 47 | virtual R visitFunctionStmt(FunctionStmt &stmt) = 0; 48 | virtual R visitImplStmt(ImplStmt& stmt) = 0; 49 | virtual R visitReturnStmt(ReturnStmt &stmt) = 0; 50 | virtual R visitStructStmt(StructStmt &stmt) = 0; 51 | virtual R visitTraitStmt(TraitStmt &stmt) = 0; 52 | virtual R visitVariableStmt(VariableStmt &stmt) = 0; 53 | }; 54 | 55 | class BreakStmt : public Stmt { 56 | public: 57 | Token keyword; 58 | std::unique_ptr value; 59 | 60 | BreakStmt(Token keyword, std::unique_ptr value) : 61 | keyword{std::move(keyword)}, 62 | value{std::move(value)} {} 63 | 64 | ~BreakStmt() override = default; 65 | 66 | std::string accept(StmtVisitor *visitor) override { 67 | return visitor->visitBreakStmt(*this); 68 | } 69 | 70 | void accept(StmtVisitor *visitor) override { 71 | return visitor->visitBreakStmt(*this); 72 | } 73 | }; 74 | 75 | class ContinueStmt : public Stmt { 76 | public: 77 | Token keyword; 78 | 79 | explicit ContinueStmt(Token keyword) : 80 | keyword{std::move(keyword)} {} 81 | 82 | ~ContinueStmt() override = default; 83 | 84 | std::string accept(StmtVisitor *visitor) override { 85 | return visitor->visitContinueStmt(*this); 86 | } 87 | 88 | void accept(StmtVisitor *visitor) override { 89 | return visitor->visitContinueStmt(*this); 90 | } 91 | }; 92 | 93 | class EnumStmt : public Stmt { 94 | public: 95 | struct Variant { 96 | Token name; 97 | std::unique_ptr typename_; 98 | }; 99 | 100 | Token name; 101 | std::vector variants; 102 | 103 | explicit EnumStmt(Token name, std::vector&& variants) : 104 | name{std::move(name)}, 105 | variants{std::move(variants)} {} 106 | 107 | ~EnumStmt() override = default; 108 | 109 | std::string accept(StmtVisitor *visitor) override { 110 | return visitor->visitEnumStmt(*this); 111 | } 112 | 113 | void accept(StmtVisitor *visitor) override { 114 | return visitor->visitEnumStmt(*this); 115 | } 116 | }; 117 | 118 | class ExpressionStmt : public Stmt { 119 | public: 120 | std::unique_ptr expr; 121 | 122 | explicit ExpressionStmt(std::unique_ptr expr) : 123 | expr{std::move(expr)} {} 124 | 125 | ~ExpressionStmt() override = default; 126 | 127 | std::string accept(StmtVisitor *visitor) override { 128 | return visitor->visitExpressionStmt(*this); 129 | } 130 | 131 | void accept(StmtVisitor *visitor) override { 132 | return visitor->visitExpressionStmt(*this); 133 | } 134 | }; 135 | 136 | class FunctionStmt : public Stmt { 137 | public: 138 | struct Param { 139 | Token name; 140 | std::unique_ptr typename_; 141 | }; 142 | 143 | Token name; 144 | std::unique_ptr returnTypename; 145 | std::vector params; 146 | std::unique_ptr body; 147 | 148 | FunctionStmt(Token name, std::unique_ptr returnTypename, std::vector &¶ms, 149 | std::unique_ptr body) : 150 | name{std::move(name)}, 151 | returnTypename{std::move(returnTypename)}, 152 | params{std::move(params)}, 153 | body{std::move(body)} {} 154 | 155 | ~FunctionStmt() override = default; 156 | 157 | std::string accept(StmtVisitor *visitor) override { 158 | return visitor->visitFunctionStmt(*this); 159 | } 160 | 161 | void accept(StmtVisitor *visitor) override { 162 | return visitor->visitFunctionStmt(*this); 163 | } 164 | }; 165 | 166 | class ImplStmt : public Stmt { 167 | public: 168 | std::unique_ptr typename_; 169 | std::unique_ptr traitTypename; 170 | std::vector> methods; 171 | 172 | ImplStmt(std::unique_ptr typename_, 173 | std::unique_ptr traitTypename, 174 | std::vector> methods) : 175 | typename_{std::move(typename_)}, 176 | traitTypename{std::move(traitTypename)}, 177 | methods{std::move(methods)} {} 178 | 179 | ~ImplStmt() override = default; 180 | 181 | std::string accept(StmtVisitor *visitor) override { 182 | return visitor->visitImplStmt(*this); 183 | } 184 | 185 | void accept(StmtVisitor *visitor) override { 186 | return visitor->visitImplStmt(*this); 187 | } 188 | }; 189 | 190 | class ReturnStmt : public Stmt { 191 | public: 192 | Token keyword; 193 | std::unique_ptr value; 194 | 195 | ReturnStmt(Token keyword, std::unique_ptr value) : 196 | keyword{std::move(keyword)}, 197 | value{std::move(value)} {} 198 | 199 | ~ReturnStmt() override = default; 200 | 201 | std::string accept(StmtVisitor *visitor) override { 202 | return visitor->visitReturnStmt(*this); 203 | } 204 | 205 | void accept(StmtVisitor *visitor) override { 206 | return visitor->visitReturnStmt(*this); 207 | } 208 | }; 209 | 210 | class StructStmt : public Stmt { 211 | public: 212 | struct Field { 213 | Token name; 214 | std::unique_ptr typename_; 215 | }; 216 | 217 | Token name; 218 | std::vector fields; 219 | 220 | StructStmt(Token name, std::vector&& fields) : 221 | name{std::move(name)}, 222 | fields{std::move(fields)} {} 223 | 224 | ~StructStmt() override = default; 225 | 226 | std::string accept(StmtVisitor *visitor) override { 227 | return visitor->visitStructStmt(*this); 228 | } 229 | 230 | void accept(StmtVisitor *visitor) override { 231 | return visitor->visitStructStmt(*this); 232 | } 233 | }; 234 | 235 | class TraitStmt : public Stmt { 236 | public: 237 | Token name; 238 | std::vector> methods; 239 | 240 | TraitStmt(Token name, std::vector> methods) : 241 | name{std::move(name)}, 242 | methods{std::move(methods)} {} 243 | 244 | ~TraitStmt() override = default; 245 | 246 | std::string accept(StmtVisitor *visitor) override { 247 | return visitor->visitTraitStmt(*this); 248 | } 249 | 250 | void accept(StmtVisitor *visitor) override { 251 | return visitor->visitTraitStmt(*this); 252 | } 253 | }; 254 | 255 | class VariableStmt : public Stmt { 256 | public: 257 | Token keyword; 258 | Token name; 259 | std::unique_ptr typeName; 260 | std::unique_ptr initializer; 261 | 262 | VariableStmt( 263 | Token keyword, 264 | Token name, 265 | std::unique_ptr typeName, 266 | std::unique_ptr initializer) : 267 | keyword{std::move(keyword)}, 268 | name{std::move(name)}, 269 | typeName{std::move(typeName)}, 270 | initializer{std::move(initializer)} {} 271 | 272 | ~VariableStmt() override = default; 273 | 274 | std::string accept(StmtVisitor *visitor) override { 275 | return visitor->visitVariableStmt(*this); 276 | } 277 | 278 | void accept(StmtVisitor *visitor) override { 279 | return visitor->visitVariableStmt(*this); 280 | } 281 | }; 282 | } 283 | 284 | #endif // ENACT_STMT_H 285 | -------------------------------------------------------------------------------- /lib/ast/generate.py: -------------------------------------------------------------------------------- 1 | # THIS SCRIPT IS DEPRECATED 2 | # It will NOT generate a correct AST 3 | 4 | # This script generates the AST classes found in Expr.h/cpp and Stmt.h/cpp. 5 | 6 | 7 | def uncapitalize(s): 8 | s[:1].lower() + s[1:] if s else '' 9 | 10 | 11 | def generate_ast_visitor(name, type_fields, visitor_types): 12 | ret = f"template \nclass {name}Visitor {{\npublic:\n" 13 | for key in type_fields: 14 | ret += f" virtual R visit{key}{name}({key}{name}& {name.lower()}) = 0;\n" 15 | ret += "};\n\n" 16 | return ret 17 | 18 | 19 | def generate_ast_class_visitors(name, type_fields, visitor_types): 20 | ret = "" 21 | for type in visitor_types: 22 | ret += f" virtual {type} accept({name}Visitor<{type}> *visitor) = 0;\n" 23 | return ret 24 | 25 | 26 | def generate_ast_subclasses(name, type_fields, visitor_types): 27 | ret = "" 28 | for key in type_fields: 29 | ret += f"class {key}{name} : public {name}Base {{\npublic:\n" 30 | if len(type_fields[key]) > 0: 31 | for field in type_fields[key]: 32 | ret += " " + field + ";\n" 33 | ret += f"\n {key}{name}(" 34 | for field in type_fields[key]: 35 | ret += field + "," 36 | ret = ret[:len(ret)-1] 37 | ret += ") : \n " 38 | for field in type_fields[key]: 39 | ret += f"{field.split(' ')[1]}{{{field.split(' ')[1]}}}," 40 | ret = ret[:len(ret)-1] 41 | ret += " {}\n" 42 | else: 43 | ret += f" {key}{name}() = default;\n" 44 | 45 | ret += f" ~{key}{name}() override = default;\n" 46 | 47 | for visitor_type in visitor_types: 48 | ret += f"\n {visitor_type} accept({name}Visitor<{visitor_type}> *visitor) override {{\n return visitor->visit{key+name}(*this);\n }}\n" 49 | ret += "};\n\n" 50 | return ret 51 | 52 | 53 | def generate_ast_subclass_decls(name, type_fields): 54 | ret = "" 55 | for type_field in type_fields: 56 | ret += f"class {type_field}{name};\n" 57 | ret += "\n" 58 | return ret 59 | 60 | 61 | def generate_ast_class_body(name, type_fields, visitor_types): 62 | ret = f" virtual ~{name}Base() = default;\n" 63 | ret += "\n" + generate_ast_class_visitors(name, type_fields, visitor_types) 64 | ret += "};\n\n" 65 | return ret 66 | 67 | 68 | def generate_includes(includes): 69 | ret = "" 70 | for include in includes: 71 | ret += f"#include {include}\n" 72 | return ret 73 | 74 | 75 | def generate_ast_class(name, type_fields, visitor_types, includes): 76 | type_field = "" 77 | type_getter = "" 78 | 79 | if name == "Expr": 80 | type_field = " Type m_type = nullptr;\n" 81 | type_getter = " virtual void setType(Type t) { m_type = t; }\n virtual const Type& getType() {\n ENACT_ASSERT(m_type != nullptr, \"Expr::getType(): Tried to get uninitialized type.\");\n return m_type;\n }\n" 82 | 83 | ret = ("// This file was automatically generated.\n" 84 | "// \"See generate.py\" for details.\n\n" 85 | f"#ifndef ENACT_{name.upper()}_H\n" 86 | f"#define ENACT_{name.upper()}_H\n\n" 87 | 88 | f"{generate_includes(includes)}\n" 89 | 90 | f"template \nclass {name}Visitor;\n\n" 91 | 92 | f"class {name}Base {{\n" 93 | 94 | f"{type_field}" 95 | 96 | "public:\n" 97 | 98 | f"{type_getter}" 99 | 100 | f"{generate_ast_class_body(name, type_fields, visitor_types)}" 101 | 102 | f"typedef std::shared_ptr<{name}Base> {name};\n\n" 103 | 104 | f"{generate_ast_subclass_decls(name, type_fields)}" 105 | f"{generate_ast_visitor(name, type_fields, visitor_types)}" 106 | 107 | f"{generate_ast_subclasses(name, type_fields, visitor_types)}" 108 | 109 | f"#endif // ENACT_{name.upper()}_H\n" 110 | ) 111 | return ret 112 | 113 | 114 | def generate_tree(name, type_fields, visitor_types, includes): 115 | with open(name + ".h", "w") as file: 116 | file.write(generate_ast_class(name, type_fields, visitor_types, includes)) 117 | 118 | 119 | generate_tree( 120 | "Expr", 121 | { 122 | "Allot": ["std::shared_ptr target", "Expr value", "Token oper"], 123 | "Any": [], 124 | "Array": ["std::vector value", "Token square", "std::unique_ptr typeName"], 125 | "Assign": ["std::shared_ptr target", "Expr value", "Token oper"], 126 | "Binary": ["Expr left", "Expr right", "Token oper"], 127 | "Boolean": ["bool value"], 128 | "Call": ["Expr callee", "std::vector arguments", "Token paren"], 129 | "Float": ["double value"], 130 | "Get": ["Expr object", "Token name", "Token oper"], 131 | "Integer": ["int value"], 132 | "Logical": ["Expr left", "Expr right", "Token oper"], 133 | "Nil": [], 134 | "String": ["std::string value"], 135 | "Subscript":["Expr object", "Expr index", "Token square"], 136 | "Ternary": ["Expr condition", "Expr thenExpr", "Expr elseExpr", "Token oper"], 137 | "Unary": ["Expr operand", "Token oper"], 138 | "Variable": ["Token name"] 139 | }, 140 | ["std::string", "void"], 141 | ['"../h/Type.h"', '"../h/Typename.h"', "", ""] 142 | ) 143 | 144 | generate_tree( 145 | "Stmt", 146 | { 147 | "Block": ["std::vector statements"], 148 | "Break": ["Token keyword"], 149 | "Continue": ["Token keyword"], 150 | "Each": ["Token name", "Expr object", "std::vector body"], 151 | "Expression": ["Expr expr"], 152 | "For": ["Stmt initializer", "Expr condition", "Expr increment", "std::vector body", "Token keyword"], 153 | "Function": ["Token name", "std::unique_ptr returnTypename", "std::vector> params", "std::vector body", "Type type"], 154 | "Given": ["Expr value", "std::vector cases"], 155 | "If": ["Expr condition", "std::vector thenBlock", "std::vector elseBlock", "Token keyword"], 156 | "Return": ["Token keyword", "Expr value"], 157 | "Struct": ["Token name", "std::vector traits", "std::vector> fields", "std::vector> methods", "std::vector> assocFunctions"], 158 | "Trait": ["Token name", "std::vector> methods"], 159 | "While": ["Expr condition", "std::vector body", "Token keyword"], 160 | "Variable": ["Token name", "Typename typeName", "Expr initializer", "bool isConst"], 161 | }, 162 | ["std::string", "void"], 163 | ['"Expr.h"', '"../h/trivialStructs.h"'] 164 | ) 165 | -------------------------------------------------------------------------------- /lib/bytecode/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(BYTECODE_SRC 2 | ${CMAKE_CURRENT_SOURCE_DIR}/Chunk.cpp 3 | ${CMAKE_CURRENT_SOURCE_DIR}/Chunk.h 4 | 5 | PARENT_SCOPE) -------------------------------------------------------------------------------- /lib/bytecode/Chunk.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_CHUNK_H 2 | #define ENACT_CHUNK_H 3 | 4 | #include 5 | 6 | #include "../common.h" 7 | #include "../value/Value.h" 8 | 9 | namespace enact { 10 | enum class OpCode : uint8_t { 11 | CONSTANT, 12 | CONSTANT_LONG, 13 | 14 | TRUE, 15 | FALSE, 16 | NIL, 17 | 18 | CHECK_INT, 19 | CHECK_NUMERIC, 20 | CHECK_BOOL, 21 | CHECK_REFERENCE, 22 | CHECK_INDEXABLE, 23 | CHECK_ALLOTABLE, 24 | CHECK_TYPE, 25 | CHECK_TYPE_LONG, 26 | 27 | NEGATE, 28 | NOT, 29 | COPY, 30 | 31 | ADD, 32 | SUBTRACT, 33 | MULTIPLY, 34 | DIVIDE, 35 | 36 | LESS, 37 | GREATER, 38 | EQUAL, 39 | 40 | ARRAY, 41 | ARRAY_LONG, 42 | 43 | GET_ARRAY_INDEX, 44 | SET_ARRAY_INDEX, 45 | 46 | POP, 47 | 48 | GET_LOCAL, 49 | GET_LOCAL_LONG, 50 | 51 | SET_LOCAL, 52 | SET_LOCAL_LONG, 53 | 54 | GET_UPVALUE, 55 | GET_UPVALUE_LONG, 56 | 57 | SET_UPVALUE, 58 | SET_UPVALUE_LONG, 59 | 60 | GET_FIELD, 61 | GET_FIELD_LONG, 62 | 63 | SET_FIELD, 64 | SET_FIELD_LONG, 65 | 66 | GET_METHOD, 67 | GET_METHOD_LONG, 68 | 69 | GET_ASSOC, 70 | GET_ASSOC_LONG, 71 | 72 | GET_PROPERTY_DYNAMIC, 73 | GET_PROPERTY_DYNAMIC_LONG, 74 | 75 | SET_PROPERTY_DYNAMIC, 76 | SET_PROPERTY_DYNAMIC_LONG, 77 | 78 | JUMP, 79 | JUMP_IF_TRUE, 80 | JUMP_IF_FALSE, 81 | 82 | LOOP, 83 | 84 | CALL_FUNCTION, 85 | CALL_BOUND_METHOD, 86 | CALL_CONSTRUCTOR, 87 | CALL_NATIVE, 88 | CALL_DYNAMIC, 89 | 90 | CLOSURE, 91 | CLOSURE_LONG, 92 | 93 | CLOSE_UPVALUE, 94 | 95 | RETURN, 96 | 97 | STRUCT, 98 | STRUCT_LONG, 99 | 100 | PAUSE, 101 | }; 102 | 103 | std::string opCodeToString(OpCode code); 104 | 105 | class Chunk { 106 | static constexpr size_t MAX_INSTRUCTION_NAME_LENGTH = 26; 107 | 108 | std::vector m_code; 109 | std::vector m_constants; 110 | 111 | std::unordered_map m_lines; 112 | 113 | std::pair disassembleSimple(size_t index) const; 114 | std::pair disassembleByte(size_t index) const; 115 | std::pair disassembleShort(size_t index) const; 116 | std::pair disassembleLong(size_t index) const; 117 | std::pair disassembleConstant(size_t index, size_t argCount = 1) const; 118 | std::pair disassembleLongConstant(size_t index, size_t argCount = 1) const; 119 | std::pair disassembleClosure(size_t index, bool isLong) const; 120 | std::pair disassembleStruct(size_t index, bool isLong) const; 121 | std::pair disassembleClosureArgs(size_t index, bool isLong) const; 122 | 123 | public: 124 | Chunk() = default; 125 | 126 | void write(uint8_t byte, line_t line); 127 | void write(OpCode byte, line_t line); 128 | void writeShort(uint32_t value, line_t line); 129 | void writeLong(uint32_t value, line_t line); 130 | void writeConstant(Value constant, line_t line); 131 | 132 | size_t addConstant(Value constant); 133 | 134 | void rewrite(size_t index, uint8_t byte); 135 | void rewrite(size_t index, OpCode byte); 136 | 137 | std::string disassemble() const; 138 | std::pair disassembleInstruction(size_t index) const; 139 | 140 | line_t getLine(size_t index) const; 141 | line_t getCurrentLine() const; 142 | 143 | const std::vector &getCode() const; 144 | const std::vector &getConstants() const; 145 | 146 | size_t getCount() const; 147 | }; 148 | } 149 | 150 | #endif //ENACT_CHUNK_H 151 | -------------------------------------------------------------------------------- /lib/common.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_COMMON_H 2 | #define ENACT_COMMON_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef DEBUG 11 | #define DEBUG_ASSERTIONS_ENABLED 12 | #endif 13 | 14 | #ifdef DEBUG_ASSERTIONS_ENABLED 15 | #define ENACT_ASSERT(expr, msg) \ 16 | _assert(expr, #expr, msg, __FILE__, __LINE__) 17 | #else 18 | #define ENACT_ASSERT(expr, msg) 19 | #endif 20 | 21 | #define ENACT_ABORT(msg) \ 22 | _abort(msg, __FILE__, __LINE__) 23 | 24 | #define ENACT_UNREACHABLE() \ 25 | ENACT_ABORT("Unreachable!") 26 | 27 | namespace enact { 28 | typedef int index_t; 29 | typedef uint32_t line_t; 30 | typedef uint16_t col_t; 31 | 32 | inline void _assert(bool expr, std::string exprString, std::string msg, std::string file, int line) { 33 | if (!expr) { 34 | std::cerr << "Assertion failed: " << msg << "\n" 35 | << "Expected: " << exprString << "\n" 36 | << "Source: " << file << ", line " << line << "\n"; 37 | abort(); 38 | } 39 | } 40 | 41 | inline void _abort(std::string msg, std::string file, int line) { 42 | std::cerr << "Aborted: " << msg << "\n" 43 | << "Source: " << file << ", line " << line << "\n"; 44 | abort(); 45 | } 46 | 47 | template 48 | inline std::unique_ptr static_unique_ptr_cast(std::unique_ptr ptr) { 49 | return std::unique_ptr(static_cast(ptr.release())); 50 | } 51 | 52 | template 53 | inline std::unique_ptr dynamic_unique_ptr_cast(std::unique_ptr ptr) { 54 | return std::unique_ptr(dynamic_cast(ptr.release())); 55 | } 56 | 57 | template 58 | inline std::vector> cloneAll(const std::vector>& cloneables) { 59 | std::vector> cloned; 60 | for (const std::unique_ptr& cloneable : cloneables) { 61 | cloned.push_back(cloneable->clone()); 62 | } 63 | return cloned; 64 | } 65 | } 66 | 67 | #endif //ENACT_COMMON_H 68 | -------------------------------------------------------------------------------- /lib/compiler/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(COMPILER_SRC 2 | ${CMAKE_CURRENT_SOURCE_DIR}/Compiler.cpp 3 | ${CMAKE_CURRENT_SOURCE_DIR}/Compiler.h 4 | 5 | PARENT_SCOPE) -------------------------------------------------------------------------------- /lib/compiler/Compiler.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_COMPILER_H 2 | #define ENACT_COMPILER_H 3 | 4 | #include "../ast/Stmt.h" 5 | #include "../bytecode/Chunk.h" 6 | #include "../value/Object.h" 7 | 8 | namespace enact { 9 | class CompileContext; 10 | 11 | struct Local { 12 | Token name; 13 | uint32_t depth; 14 | bool initialized; 15 | bool isCaptured; 16 | }; 17 | 18 | struct Upvalue { 19 | uint32_t index; 20 | bool isLocal; 21 | }; 22 | 23 | enum class FunctionKind { 24 | FUNCTION, 25 | SCRIPT 26 | }; 27 | 28 | class Compiler : private StmtVisitor, private ExprVisitor { 29 | friend class GC; 30 | 31 | CompileContext &m_context; 32 | 33 | Compiler *m_enclosing = nullptr; 34 | 35 | FunctionObject *m_currentFunction = nullptr; 36 | FunctionKind m_functionType; 37 | 38 | std::vector m_locals; 39 | uint32_t m_scopeDepth = 0; 40 | 41 | std::vector m_upvalues{}; 42 | 43 | bool m_hadError = false; 44 | 45 | void start(FunctionKind functionKind, Type functionType, const std::string &name); 46 | 47 | void startProgram(); 48 | 49 | void startFunction(FunctionStmt &function); 50 | 51 | void compile(std::vector> ast); 52 | 53 | void compile(Stmt &stmt); 54 | 55 | void compile(Expr &expr); 56 | 57 | FunctionObject *compileFunction(FunctionStmt &stmt); 58 | 59 | void endProgram(); 60 | 61 | void endPart(); 62 | 63 | void endFunction(); 64 | 65 | void beginScope(); 66 | 67 | void endScope(); 68 | 69 | void addLocal(const Token &name); 70 | 71 | uint32_t resolveLocal(const Token &name); 72 | 73 | void addUpvalue(uint32_t index, bool isLocal); 74 | 75 | uint32_t resolveUpvalue(const Token &name); 76 | 77 | void defineNative(std::string name, Type functionType, NativeFn function); 78 | 79 | void emitByte(uint8_t byte); 80 | 81 | void emitByte(OpCode byte); 82 | 83 | void emitShort(uint16_t value); 84 | 85 | void emitLong(uint32_t value); 86 | 87 | void emitConstant(Value constant); 88 | 89 | size_t emitJump(OpCode jump); 90 | 91 | void patchJump(size_t index, Token where); 92 | 93 | void emitLoop(size_t loopStartIndex, Token where); 94 | 95 | void emitFunction(FunctionStmt &stmt); 96 | 97 | // Exactly the same as emitFunction except it does not emit the CLOSURE(_LONG) instruction 98 | void emitAssoc(FunctionStmt &stmt); 99 | 100 | // Exactly the same as emitAssoc, except it emits the `self` variable 101 | void emitMethod(FunctionStmt &stmt); 102 | 103 | Chunk ¤tChunk(); 104 | 105 | class CompileError : public std::runtime_error { 106 | Token m_token; 107 | std::string m_message; 108 | 109 | public: 110 | CompileError(Token token, std::string message) : std::runtime_error{ 111 | "Internal error, raising exception:\nUncaught CompileError!"}, m_token{std::move(token)}, 112 | m_message{std::move(message)} {} 113 | 114 | const Token &getToken() const { return m_token; } 115 | 116 | const std::string &getMessage() const { return m_message; } 117 | }; 118 | 119 | CompileError errorAt(const Token &token, const std::string &message); 120 | 121 | void visitBlockStmt(BlockStmt &stmt) override; 122 | 123 | void visitBreakStmt(BreakStmt &stmt) override; 124 | 125 | void visitContinueStmt(ContinueStmt &stmt) override; 126 | 127 | void visitEachStmt(EachStmt &stmt) override; 128 | 129 | void visitExpressionStmt(ExpressionStmt &stmt) override; 130 | 131 | void visitForStmt(ForStmt &stmt) override; 132 | 133 | void visitFunctionStmt(FunctionStmt &stmt) override; 134 | 135 | void visitGivenStmt(GivenStmt &stmt) override; 136 | 137 | void visitIfStmt(IfStmt &stmt) override; 138 | 139 | void visitReturnStmt(ReturnStmt &stmt) override; 140 | 141 | void visitStructStmt(StructStmt &stmt) override; 142 | 143 | void visitTraitStmt(TraitStmt &stmt) override; 144 | 145 | void visitWhileStmt(WhileStmt &stmt) override; 146 | 147 | void visitVariableStmt(VariableStmt &stmt) override; 148 | 149 | void visitAllotExpr(AllotExpr &expr) override; 150 | 151 | void visitAnyExpr(AnyExpr &expr) override; 152 | 153 | void visitArrayExpr(ArrayExpr &expr) override; 154 | 155 | void visitAssignExpr(AssignExpr &expr) override; 156 | 157 | void visitBinaryExpr(BinaryExpr &expr) override; 158 | 159 | void visitBooleanExpr(BooleanExpr &expr) override; 160 | 161 | void visitCallExpr(CallExpr &expr) override; 162 | 163 | void visitFloatExpr(FloatExpr &expr) override; 164 | 165 | void visitGetExpr(GetExpr &expr) override; 166 | 167 | void visitIntegerExpr(IntegerExpr &expr) override; 168 | 169 | void visitLogicalExpr(LogicalExpr &expr) override; 170 | 171 | void visitNilExpr(NilExpr &expr) override; 172 | 173 | void visitSetExpr(SetExpr &expr) override; 174 | 175 | void visitStringExpr(StringExpr &expr) override; 176 | 177 | void visitSubscriptExpr(SubscriptExpr &expr) override; 178 | 179 | void visitTernaryExpr(TernaryExpr &expr) override; 180 | 181 | void visitUnaryExpr(UnaryExpr &expr) override; 182 | 183 | void visitVariableExpr(VariableExpr &expr) override; 184 | 185 | public: 186 | Compiler(CompileContext &context, Compiler *enclosing = nullptr); 187 | 188 | ~Compiler() = default; 189 | 190 | // Starts, compiles, and ends the program. 191 | FunctionObject *compileProgram(std::vector> ast); 192 | 193 | // Compiles a single part of the program which pauses the interpreter when it is executed. 194 | // Used for the REPL. Does not start or end the program, only a singular segment. 195 | FunctionObject *compilePart(std::vector> ast); 196 | 197 | void startRepl(); 198 | 199 | FunctionObject *endRepl(); 200 | 201 | bool hadError(); 202 | }; 203 | } 204 | 205 | #endif //ENACT_COMPILER_H 206 | -------------------------------------------------------------------------------- /lib/context/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(CONTEXT_SRC 2 | ${CMAKE_CURRENT_SOURCE_DIR}/CompileContext.cpp 3 | ${CMAKE_CURRENT_SOURCE_DIR}/CompileContext.h 4 | ${CMAKE_CURRENT_SOURCE_DIR}/Options.cpp 5 | ${CMAKE_CURRENT_SOURCE_DIR}/Options.h 6 | 7 | PARENT_SCOPE) -------------------------------------------------------------------------------- /lib/context/CompileContext.cpp: -------------------------------------------------------------------------------- 1 | #include "CompileContext.h" 2 | 3 | #include 4 | 5 | #include "../AstSerialise.h" 6 | 7 | namespace enact { 8 | CompileContext::CompileContext(Options options) : m_options{std::move(options)} { 9 | } 10 | 11 | CompileResult CompileContext::compile(std::string source) { 12 | m_source = std::move(source); 13 | 14 | std::vector> ast = m_parser.parse(); 15 | // TODO: serialise and print AST 16 | 17 | AstSerialise serialise{}; 18 | for (const std::unique_ptr& stmt : ast) { 19 | std::cout << serialise(*stmt) << '\n'; 20 | } 21 | } 22 | 23 | std::string CompileContext::getSourceLine(line_t line) { 24 | std::istringstream stream{m_source}; 25 | line_t lineNumber{1}; 26 | std::string lineContents; 27 | 28 | while (std::getline(stream, lineContents) && lineNumber < line) { 29 | ++lineNumber; 30 | } 31 | 32 | return lineContents; 33 | } 34 | 35 | void CompileContext::reportErrorAt(const Token &token, const std::string &msg) { 36 | std::cerr << "[line " << token.line << "] Error"; 37 | 38 | if (token.type == TokenType::END_OF_FILE) { 39 | std::cerr << " at end: " << msg << "\n\n"; 40 | } else { 41 | if (token.type == TokenType::ERROR) { 42 | std::cerr << ":\n"; 43 | } else { 44 | std::cerr << " at " << (token.lexeme == "\n" ? "newline" : "'" + token.lexeme + "'") << ":\n"; 45 | } 46 | 47 | std::cerr << " " << getSourceLine(token.lexeme == "\n" ? token.line - 1 : token.line) << "\n "; 48 | for (int i = 1; i <= token.col - token.lexeme.size(); ++i) { 49 | std::cerr << " "; 50 | } 51 | 52 | for (int i = 1; i <= token.lexeme.size(); ++i) { 53 | std::cerr << "^"; 54 | } 55 | std::cerr << "\n" << msg << "\n\n"; 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /lib/context/CompileContext.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_COMPILECONTEXT_H 2 | #define ENACT_COMPILECONTEXT_H 3 | 4 | #include "../parser/Parser.h" 5 | 6 | #include "Options.h" 7 | 8 | namespace enact { 9 | enum class CompileResult { 10 | OK = 0, 11 | PARSE_ERROR, 12 | ANALYSIS_ERROR, 13 | COMPILE_ERROR, 14 | RUNTIME_ERROR, 15 | }; 16 | 17 | class CompileContext { 18 | public: 19 | explicit CompileContext(Options options); 20 | ~CompileContext() = default; 21 | 22 | CompileResult compile(std::string source); 23 | 24 | const std::string& getSource() const { return m_source; } 25 | const Options& getOptions() const { return m_options; } 26 | 27 | std::string getSourceLine(line_t line); 28 | void reportErrorAt(const Token &token, const std::string &msg); 29 | 30 | private: 31 | std::string m_source; 32 | Options m_options; 33 | 34 | Parser m_parser{*this}; 35 | }; 36 | } 37 | 38 | #endif //ENACT_COMPILECONTEXT_H 39 | -------------------------------------------------------------------------------- /lib/context/Options.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Options.h" 4 | 5 | namespace enact { 6 | Options::Options(std::string filename, std::vector programArgs, std::unordered_set flags) : 7 | m_filename{std::move(filename)}, 8 | m_programArgs{std::move(programArgs)}, 9 | m_flags{std::move(flags)} { 10 | } 11 | 12 | Options::Options(int argc, char **argv) { 13 | if (argc == 0) return; 14 | std::vector args{argv + 1, argv + argc}; 15 | 16 | size_t current = 0; 17 | for (; current < args.size(); ++current) { 18 | std::string arg = args[current]; 19 | 20 | if (arg.size() >= 2 && arg[0] == '-' && arg[1] == '-') { 21 | parseString(arg); 22 | } else if (arg.size() >= 1 && arg[0] == '-') { 23 | for (char c : arg.substr(1)) { 24 | parseString(std::string{"-"} + c); 25 | } 26 | } else { 27 | break; 28 | } 29 | } 30 | 31 | if (current >= args.size()) { 32 | // There are no more arguments for us to parse. 33 | return; 34 | } 35 | 36 | m_filename = args[current++]; 37 | 38 | while (current < args.size()) { 39 | m_programArgs.push_back(args[current++]); 40 | } 41 | } 42 | 43 | void Options::parseString(const std::string &string) { 44 | if (m_parseTable.count(string) > 0) { 45 | m_parseTable[string](); 46 | } else { 47 | std::cerr << "[enact] Error:\n Unknown interpreter flag '" << string << 48 | "'.\nUsage: enact [interpreter flags] [filename] [program flags]\n\n"; 49 | throw FlagsError{}; 50 | } 51 | } 52 | 53 | void Options::parseStrings(const std::vector &strings) { 54 | for (const std::string &string : strings) { 55 | parseString(string); 56 | } 57 | } 58 | 59 | bool Options::flagEnabled(Flag flag) const { 60 | return m_flags.count(flag) > 0; 61 | } 62 | 63 | void Options::enableFlag(Flag flag) { 64 | m_flags.insert(flag); 65 | } 66 | 67 | void Options::enableFlags(std::vector flags) { 68 | for (Flag flag : flags) { 69 | enableFlag(flag); 70 | } 71 | } 72 | 73 | const std::string &Options::getFilename() { 74 | return m_filename; 75 | } 76 | 77 | const std::vector &Options::getProgramArgs() { 78 | return m_programArgs; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/context/Options.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_OPTIONS_H 2 | #define ENACT_OPTIONS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace enact { 11 | enum class Flag { 12 | DEBUG_PRINT_AST, 13 | DEBUG_DISASSEMBLE_CHUNK, 14 | DEBUG_TRACE_EXECUTION, 15 | DEBUG_STRESS_GC, 16 | DEBUG_LOG_GC 17 | }; 18 | 19 | class FlagsError : public std::runtime_error { 20 | public: 21 | FlagsError() : std::runtime_error{"Uncaught FlagsError!"} {} 22 | }; 23 | 24 | class Options { 25 | std::string m_filename{}; 26 | std::vector m_programArgs{}; 27 | std::unordered_set m_flags{}; 28 | 29 | public: 30 | Options(std::string filename, std::vector programArgs, std::unordered_set flags); 31 | 32 | Options(int argc, char **argv); 33 | 34 | void parseString(const std::string &string); 35 | 36 | void parseStrings(const std::vector &strings); 37 | 38 | bool flagEnabled(Flag flag) const; 39 | 40 | void enableFlag(Flag flag); 41 | 42 | void enableFlags(std::vector flags); 43 | 44 | const std::string &getFilename(); 45 | 46 | const std::vector &getProgramArgs(); 47 | 48 | private: 49 | std::unordered_map> m_parseTable{ 50 | {"--debug-print-ast", std::bind(&Options::enableFlag, this, Flag::DEBUG_PRINT_AST)}, 51 | {"--debug-disassemble-chunk", std::bind(&Options::enableFlag, this, Flag::DEBUG_DISASSEMBLE_CHUNK)}, 52 | {"--debug-trace-execution", std::bind(&Options::enableFlag, this, Flag::DEBUG_TRACE_EXECUTION)}, 53 | {"--debug-stress-gc", std::bind(&Options::enableFlag, this, Flag::DEBUG_STRESS_GC)}, 54 | {"--debug-log-gc", std::bind(&Options::enableFlag, this, Flag::DEBUG_LOG_GC)}, 55 | 56 | {"--debug", std::bind(&Options::enableFlags, this, std::vector{ 57 | Flag::DEBUG_PRINT_AST, 58 | Flag::DEBUG_DISASSEMBLE_CHUNK, 59 | Flag::DEBUG_TRACE_EXECUTION, 60 | Flag::DEBUG_STRESS_GC, 61 | Flag::DEBUG_LOG_GC 62 | })}, 63 | }; 64 | }; 65 | } 66 | 67 | #endif //ENACT_OPTIONS_H 68 | -------------------------------------------------------------------------------- /lib/memory/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(MEMORY_SRC 2 | ${CMAKE_CURRENT_SOURCE_DIR}/GC.cpp 3 | ${CMAKE_CURRENT_SOURCE_DIR}/GC.h 4 | 5 | PARENT_SCOPE) -------------------------------------------------------------------------------- /lib/memory/GC.cpp: -------------------------------------------------------------------------------- 1 | #include "../context/CompileContext.h" 2 | 3 | namespace enact { 4 | GC::GC(Context &context) : m_context{context} { 5 | } 6 | 7 | GC::~GC() { 8 | freeObjects(); 9 | } 10 | 11 | 12 | Object *GC::cloneObject(Object *object) { 13 | m_bytesAllocated += object->size(); 14 | if (m_bytesAllocated > m_nextRun || m_context.options.flagEnabled(Flag::DEBUG_STRESS_GC)) { 15 | collectGarbage(); 16 | } 17 | 18 | Object *cloned = object->clone(); 19 | m_objects.push_back(cloned); 20 | 21 | if (m_context.options.flagEnabled(Flag::DEBUG_LOG_GC)) { 22 | std::cout << static_cast(cloned) << ": allocated object of size " << cloned->size() << " and type " 23 | << 24 | static_cast(static_cast(cloned)->m_type) << ".\n"; 25 | } 26 | 27 | return cloned; 28 | } 29 | 30 | void GC::collectGarbage() { 31 | if (m_context.options.flagEnabled(Flag::DEBUG_LOG_GC)) { 32 | std::cout << "-- GC BEGIN\n"; 33 | } 34 | 35 | size_t before = m_bytesAllocated; 36 | 37 | markRoots(); 38 | traceReferences(); 39 | sweep(); 40 | 41 | m_nextRun = m_bytesAllocated * GC_HEAP_GROW_FACTOR; 42 | 43 | if (m_context.options.flagEnabled(Flag::DEBUG_LOG_GC)) { 44 | std::cout << "-- GC END: collected " << before - m_bytesAllocated << " bytes (from " << before << " to " << 45 | m_bytesAllocated << "), next GC at " << m_nextRun << ".\n"; 46 | } 47 | } 48 | 49 | void GC::markRoots() { 50 | markCompilerRoots(); 51 | markVMRoots(); 52 | } 53 | 54 | void GC::markCompilerRoots() { 55 | Compiler *compiler = &m_context.currentCompiler(); 56 | while (compiler != nullptr) { 57 | if (compiler->m_currentFunction) markObject(compiler->m_currentFunction); 58 | compiler = compiler->m_enclosing; 59 | } 60 | } 61 | 62 | void GC::markVMRoots() { 63 | for (Value &value : m_context.vm.m_stack) { 64 | markValue(value); 65 | } 66 | 67 | for (size_t i = 0; i < m_context.vm.m_frameCount; ++i) { 68 | markObject(m_context.vm.m_frames[i].closure); 69 | } 70 | 71 | for (UpvalueObject *upvalue = m_context.vm.m_openUpvalues; upvalue != nullptr; upvalue = upvalue->getNext()) { 72 | markObject(upvalue); 73 | } 74 | } 75 | 76 | void GC::markObject(Object *object) { 77 | if (!object || object->isMarked()) return; 78 | object->mark(); 79 | 80 | m_greyStack.push_back(object); 81 | 82 | if (m_context.options.flagEnabled(Flag::DEBUG_LOG_GC)) { 83 | std::cout << static_cast(object) << ": marked object [ " << *object << " ].\n"; 84 | } 85 | } 86 | 87 | void GC::markValue(Value value) { 88 | if (value.isObject()) { 89 | markObject(value.asObject()); 90 | } 91 | } 92 | 93 | void GC::markValues(const std::vector &values) { 94 | for (const Value &value : values) { 95 | markValue(value); 96 | } 97 | } 98 | 99 | void GC::traceReferences() { 100 | while (!m_greyStack.empty()) { 101 | Object *object = m_greyStack.back(); 102 | m_greyStack.pop_back(); 103 | blackenObject(object); 104 | } 105 | } 106 | 107 | void GC::blackenObject(Object *object) { 108 | if (m_context.options.flagEnabled(Flag::DEBUG_LOG_GC)) { 109 | std::cout << static_cast(object) << ": blackened object [ " << *object << " ].\n"; 110 | } 111 | 112 | switch (object->m_type) { 113 | case ObjectType::BOUND_METHOD: { 114 | auto *boundMethod = object->as(); 115 | markValue(boundMethod->receiver()); 116 | markObject(boundMethod->method()); 117 | break; 118 | } 119 | 120 | case ObjectType::INSTANCE: { 121 | auto *instance = object->as(); 122 | markObject(instance->getStruct()); 123 | for (Value field : instance->fields()) { 124 | markValue(field); 125 | } 126 | break; 127 | } 128 | 129 | case ObjectType::STRUCT: { 130 | auto *struct_ = object->as(); 131 | for (ClosureObject *method : struct_->methods()) { 132 | markObject(method); 133 | } 134 | for (Value assoc : struct_->assocs()) { 135 | markValue(assoc); 136 | } 137 | break; 138 | } 139 | 140 | case ObjectType::CLOSURE: { 141 | auto *closure = object->as(); 142 | markObject(closure->getFunction()); 143 | for (UpvalueObject *upvalue : closure->getUpvalues()) { 144 | markObject(upvalue); 145 | } 146 | break; 147 | } 148 | 149 | case ObjectType::FUNCTION: { 150 | auto function = object->as(); 151 | markValues(function->getChunk().getConstants()); 152 | break; 153 | } 154 | 155 | case ObjectType::UPVALUE: 156 | markValue(object->as()->getClosed()); 157 | break; 158 | 159 | default: 160 | break; 161 | } 162 | } 163 | 164 | void GC::sweep() { 165 | for (auto it = m_objects.begin(); it != m_objects.end();) { 166 | Object *object = *it; 167 | if (object->isMarked()) { 168 | object->unmark(); 169 | it++; 170 | } else { 171 | freeObject(object); 172 | it = m_objects.erase(it); 173 | } 174 | } 175 | } 176 | 177 | void GC::freeObject(Object *object) { 178 | if (m_context.options.flagEnabled(Flag::DEBUG_LOG_GC)) { 179 | std::cout << static_cast(object) << ": freed object of type " << 180 | static_cast(object->m_type) << ".\n"; 181 | } 182 | 183 | delete object; 184 | } 185 | 186 | void GC::freeObjects() { 187 | while (m_objects.begin() != m_objects.end()) { 188 | freeObject(*m_objects.begin()); 189 | m_objects.erase(m_objects.begin()); 190 | } 191 | } 192 | } -------------------------------------------------------------------------------- /lib/memory/GC.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_GC_H 2 | #define ENACT_GC_H 3 | 4 | #include 5 | 6 | #include "../value/Object.h" 7 | 8 | namespace enact { 9 | class CompileContext; 10 | 11 | constexpr size_t GC_HEAP_GROW_FACTOR = 2; 12 | 13 | class GC { 14 | CompileContext &m_context; 15 | 16 | size_t m_bytesAllocated = 0; 17 | size_t m_nextRun = 1024 * 1024; 18 | 19 | std::vector m_objects{}; 20 | std::vector m_greyStack{}; 21 | 22 | void markRoots(); 23 | 24 | void markCompilerRoots(); 25 | 26 | void markVMRoots(); 27 | 28 | void markObject(Object *object); 29 | 30 | void markValue(Value value); 31 | 32 | void markValues(const std::vector &values); 33 | 34 | void traceReferences(); 35 | 36 | void blackenObject(Object *object); 37 | 38 | void sweep(); 39 | 40 | public: 41 | explicit GC(CompileContext &context); 42 | 43 | ~GC(); 44 | 45 | template 46 | T *allocateObject(Args &&... args); 47 | 48 | Object *cloneObject(Object *object); 49 | 50 | void collectGarbage(); 51 | 52 | void freeObject(Object *object); 53 | 54 | void freeObjects(); 55 | }; 56 | } 57 | 58 | #endif //ENACT_GC_H 59 | -------------------------------------------------------------------------------- /lib/parser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(PARSER_SRC 2 | ${CMAKE_CURRENT_SOURCE_DIR}/Parser.cpp 3 | ${CMAKE_CURRENT_SOURCE_DIR}/Parser.h 4 | ${CMAKE_CURRENT_SOURCE_DIR}/Lexer.cpp 5 | ${CMAKE_CURRENT_SOURCE_DIR}/Lexer.h 6 | ${CMAKE_CURRENT_SOURCE_DIR}/Token.h 7 | ${CMAKE_CURRENT_SOURCE_DIR}/Typename.cpp 8 | ${CMAKE_CURRENT_SOURCE_DIR}/Typename.h 9 | 10 | PARENT_SCOPE) -------------------------------------------------------------------------------- /lib/parser/Lexer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Lexer.h" 4 | 5 | namespace enact { 6 | Lexer::Lexer(std::string source) : m_source{std::move(source)} {} 7 | 8 | Token Lexer::scanToken() { 9 | skipWhitespace(); 10 | m_start = m_current; 11 | 12 | if (isAtEnd()) return makeToken(TokenType::END_OF_FILE); 13 | 14 | char c = advance(); 15 | 16 | if (isDigit(c)) return number(); 17 | if (isIdentifierStart(c)) return identifier(); 18 | 19 | switch (c) { 20 | // Single character tokens. 21 | case '(': 22 | return makeToken(TokenType::LEFT_PAREN); 23 | case ')': 24 | if (m_currentInterpolations > 0) { 25 | return interpolationEnd(); 26 | } 27 | return makeToken(TokenType::RIGHT_PAREN); 28 | case '{': 29 | return makeToken(TokenType::LEFT_BRACE); 30 | case '}': 31 | return makeToken(TokenType::RIGHT_BRACE); 32 | case '[': 33 | return makeToken(TokenType::LEFT_SQUARE); 34 | case ']': 35 | return makeToken(TokenType::RIGHT_SQUARE); 36 | case '&': 37 | return makeToken(TokenType::AMPERSAND); 38 | case '\'': 39 | return makeToken(TokenType::APOSTROPHE); 40 | case '^': 41 | return makeToken(TokenType::CARAT); 42 | case ',': 43 | return makeToken(TokenType::COMMA); 44 | case '$': 45 | return makeToken(TokenType::DOLLAR); 46 | case '#': 47 | return makeToken(TokenType::HASH); 48 | case '-': 49 | return makeToken(TokenType::MINUS); 50 | case '|': 51 | return makeToken(TokenType::PIPE); 52 | case '+': 53 | return makeToken(TokenType::PLUS); 54 | case '?': 55 | return makeToken(TokenType::QUESTION); 56 | case ';': 57 | return makeToken(TokenType::SEMICOLON); 58 | case '/': 59 | return makeToken(TokenType::SLASH); 60 | case '*': 61 | return makeToken(TokenType::STAR); 62 | case '~': 63 | return makeToken(TokenType::TILDE); 64 | 65 | // 1 or 2 character tokens. 66 | case '!': 67 | return makeToken(match('=') ? TokenType::BANG_EQUAL : TokenType::BANG); 68 | case '=': 69 | if (match('=')) { 70 | return makeToken(TokenType::EQUAL_EQUAL); 71 | } 72 | if (match('>')) { 73 | return makeToken(TokenType::EQUAL_GREATER); 74 | } 75 | return makeToken(TokenType::EQUAL); 76 | case '>': 77 | if (match('=')) { 78 | return makeToken(TokenType::GREATER_EQUAL); 79 | } 80 | if (match('>')) { 81 | return makeToken(TokenType::GREATER_GREATER); 82 | } 83 | return makeToken(TokenType::GREATER); 84 | case '<': 85 | if (match('=')) { 86 | return makeToken(TokenType::LESS_EQUAL); 87 | } 88 | if (match('<')) { 89 | return makeToken(TokenType::LESS_LESS); 90 | } 91 | return makeToken(TokenType::LESS); 92 | 93 | // 1, 2 or 3 character tokens. 94 | case '.': 95 | if (match('.')) { 96 | if (match('.')) { 97 | return makeToken(TokenType::DOT_DOT_DOT); 98 | } 99 | return makeToken(TokenType::DOT_DOT); 100 | } 101 | return makeToken(TokenType::DOT); 102 | 103 | case '"': 104 | return string(); 105 | } 106 | 107 | std::string errorMessage = "Unrecognized character '"; 108 | errorMessage.push_back(c); 109 | errorMessage.append("'."); 110 | return errorToken(errorMessage); 111 | } 112 | 113 | void Lexer::skipWhitespace() { 114 | while (true) { 115 | char c = peek(); 116 | switch (c) { 117 | case '\n': 118 | ++m_line; 119 | // Fallthrough 120 | case ' ': 121 | case '\t': 122 | case '\r': 123 | advance(); 124 | break; 125 | 126 | case '/': 127 | if (peekNext() == '/') { 128 | while (peek() != '\n' && !isAtEnd()) advance(); 129 | } else { 130 | return; 131 | } 132 | 133 | default: 134 | return; 135 | } 136 | } 137 | } 138 | 139 | Token Lexer::number() { 140 | while (isDigit(peek())) advance(); 141 | 142 | TokenType type = TokenType::INTEGER; 143 | 144 | if (peek() == '.' && isDigit(peekNext())) { 145 | type = TokenType::FLOAT; 146 | advance(); 147 | while (isDigit(peek())) advance(); 148 | } 149 | 150 | return makeToken(type); 151 | } 152 | 153 | Token Lexer::identifier() { 154 | while (isIdentifier(peek())) advance(); 155 | return makeToken(getIdentifierType(m_source.substr(m_start, m_current - m_start))); 156 | } 157 | 158 | Token Lexer::string() { 159 | std::string value; 160 | bool inEscapeSequence = false; 161 | while (!isAtEnd()) { 162 | const char c = peek(); 163 | 164 | if (inEscapeSequence) { 165 | switch (c) { 166 | case 'n': 167 | value.push_back('\n'); 168 | break; 169 | case 'r': 170 | value.push_back('\r'); 171 | break; 172 | case 't': 173 | value.push_back('\t'); 174 | break; 175 | case '\\': 176 | value.push_back('\\'); 177 | break; 178 | case '"': 179 | value.push_back('"'); 180 | break; 181 | case '(': 182 | // Eat the '(' 183 | advance(); 184 | return interpolationStart(std::move(value)); 185 | default: 186 | advance(); 187 | return errorToken("Unrecognised escape sequence."); 188 | } 189 | 190 | inEscapeSequence = false; 191 | } else { 192 | if (c == '"') break; 193 | if (c == '\\') { 194 | inEscapeSequence = true; 195 | advance(); 196 | continue; 197 | } 198 | 199 | value.push_back(c); 200 | } 201 | 202 | advance(); 203 | } 204 | 205 | if (isAtEnd()) { 206 | return errorToken("Unterminated string."); 207 | } 208 | 209 | // Eat the close quote. 210 | advance(); 211 | 212 | return Token{TokenType::STRING, value, m_line, m_col}; 213 | } 214 | 215 | Token Lexer::interpolationStart(std::string value) { 216 | ++m_currentInterpolations; 217 | return Token{TokenType::INTERPOLATION, std::move(value), m_line, m_col}; 218 | } 219 | 220 | Token Lexer::interpolationEnd() { 221 | --m_currentInterpolations; 222 | return string(); 223 | } 224 | 225 | Token Lexer::makeToken(TokenType type) { 226 | std::string lexeme{m_source.substr(m_start, m_current - m_start)}; 227 | m_last = Token{type, lexeme, m_line, m_col}; 228 | return m_last; 229 | } 230 | 231 | Token Lexer::errorToken(const std::string &what) { 232 | return Token{TokenType::ERROR, what, m_line, m_col}; 233 | } 234 | 235 | TokenType Lexer::getIdentifierType(const std::string& candidate) { 236 | if (m_keywords.count(candidate) > 0) { 237 | return m_keywords.at(candidate); 238 | } 239 | 240 | return TokenType::IDENTIFIER; 241 | } 242 | 243 | bool Lexer::isAtEnd() { 244 | return m_current >= m_source.length(); 245 | } 246 | 247 | char Lexer::advance() { 248 | ++m_col; 249 | return m_source[m_current++]; 250 | } 251 | 252 | char Lexer::peek() { 253 | return m_source[m_current]; 254 | } 255 | 256 | char Lexer::peekNext() { 257 | return m_source[m_current + 1]; 258 | } 259 | 260 | char Lexer::previous() { 261 | return m_source[m_current - 1]; 262 | } 263 | 264 | bool Lexer::match(char expected) { 265 | if (peek() == expected) { 266 | advance(); 267 | return true; 268 | } 269 | return false; 270 | } 271 | 272 | bool Lexer::isDigit(char c) { 273 | return c >= '0' && c <= '9'; 274 | } 275 | 276 | bool Lexer::isIdentifierStart(char c) { 277 | return (c >= 'a' && c <= 'z') || 278 | (c >= 'A' && c <= 'Z') || 279 | c == '_'; 280 | } 281 | 282 | bool Lexer::isIdentifier(char c) { 283 | return isIdentifierStart(c) || isDigit(c); 284 | } 285 | } -------------------------------------------------------------------------------- /lib/parser/Lexer.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_LEXER_H 2 | #define ENACT_LEXER_H 3 | 4 | #include 5 | 6 | #include "Token.h" 7 | 8 | namespace enact { 9 | class Lexer { 10 | public: 11 | explicit Lexer(std::string source); 12 | ~Lexer() = default; 13 | 14 | Token scanToken(); 15 | 16 | private: 17 | Token number(); 18 | Token identifier(); 19 | Token string(); 20 | 21 | Token interpolationStart(std::string value); 22 | Token interpolationEnd(); 23 | 24 | TokenType getIdentifierType(const std::string& candidate); 25 | 26 | Token makeToken(TokenType type); 27 | Token errorToken(const std::string &what); 28 | 29 | void skipWhitespace(); 30 | bool isAtEnd(); 31 | 32 | char advance(); 33 | char peek(); 34 | char peekNext(); 35 | char previous(); 36 | 37 | bool match(char expected); 38 | 39 | bool isDigit(char c); 40 | bool isIdentifierStart(char c); 41 | bool isIdentifier(char c); 42 | 43 | std::string m_source; 44 | size_t m_start, m_current = 0; 45 | 46 | line_t m_line = 1; 47 | col_t m_col = 0; 48 | 49 | Token m_last; 50 | 51 | int m_currentInterpolations = 0; 52 | 53 | std::unordered_map m_keywords{ 54 | {"and", TokenType::AND}, 55 | {"as", TokenType::AS}, 56 | {"assoc", TokenType::ASSOC}, 57 | {"break", TokenType::BREAK}, 58 | {"case", TokenType::CASE}, 59 | {"continue", TokenType::CONTINUE}, 60 | {"default", TokenType::DEFAULT}, 61 | {"else", TokenType::ELSE}, 62 | {"enum", TokenType::ENUM}, 63 | {"false", TokenType::FALSE}, 64 | {"func", TokenType::FUNC}, 65 | {"for", TokenType::FOR}, 66 | {"gc", TokenType::GC}, 67 | {"if", TokenType::IF}, 68 | {"imm", TokenType::IMM}, 69 | {"impl", TokenType::IMPL}, 70 | {"in", TokenType::IN}, 71 | {"is", TokenType::IS}, 72 | {"mut", TokenType::MUT}, 73 | {"not", TokenType::NOT}, 74 | {"or", TokenType::OR}, 75 | {"pub", TokenType::PUB}, 76 | {"rc", TokenType::RC}, 77 | {"return", TokenType::RETURN}, 78 | {"so", TokenType::SO}, 79 | {"struct", TokenType::STRUCT}, 80 | {"switch", TokenType::SWITCH}, 81 | {"trait", TokenType::TRAIT}, 82 | {"true", TokenType::TRUE}, 83 | {"when", TokenType::WHEN}, 84 | {"while", TokenType::WHILE}, 85 | }; 86 | }; 87 | } 88 | 89 | 90 | #endif //ENACT_LEXER_H 91 | -------------------------------------------------------------------------------- /lib/parser/Parser.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_PARSER_H 2 | #define ENACT_PARSER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../ast/Stmt.h" 9 | #include "../bytecode/Chunk.h" 10 | 11 | #include "Lexer.h" 12 | #include "Token.h" 13 | #include "Typename.h" 14 | 15 | namespace enact { 16 | class CompileContext; 17 | 18 | enum class Precedence { 19 | NONE, 20 | ASSIGNMENT, // = 21 | LOGICAL_OR, // or 22 | LOGICAL_AND, // and 23 | EQUALITY, // == != 24 | COMPARISON, // < > <= >= 25 | CAST, // as is 26 | RANGE, // .. ... 27 | BITWISE_OR, // | 28 | BITWISE_XOR, // ^ 29 | BITWISE_AND, // & 30 | ADD, // + - 31 | MULTIPLY, // * / % 32 | BITWISE_SHIFT, // << >> 33 | UNARY, // - not ~ & * 34 | CALL, // () . 35 | PRIMARY, 36 | }; 37 | 38 | class Parser; 39 | 40 | // For our Pratt parse table 41 | typedef std::unique_ptr (Parser::*PrefixFn)(); 42 | typedef std::unique_ptr (Parser::*InfixFn)(std::unique_ptr); 43 | 44 | struct ParseRule { 45 | PrefixFn prefix; 46 | InfixFn infix; 47 | Precedence parsePrecedence; 48 | }; 49 | 50 | class ParseError : public std::runtime_error { 51 | public: 52 | ParseError() : std::runtime_error{"Uncaught ParseError: Internal"} {} 53 | }; 54 | 55 | class Parser { 56 | public: 57 | explicit Parser(CompileContext &context); 58 | ~Parser() = default; 59 | 60 | std::vector> parse(); 61 | 62 | bool hadError() const; 63 | 64 | private: 65 | std::unique_ptr parseStmt(); 66 | 67 | std::unique_ptr parseFunctionStmt(bool mustParseBody = true); 68 | std::unique_ptr parseStructStmt(); 69 | std::unique_ptr parseEnumStmt(); 70 | std::unique_ptr parseTraitStmt(); 71 | std::unique_ptr parseImplStmt(); 72 | std::unique_ptr parseVariableStmt(); 73 | std::unique_ptr parseReturnStmt(); 74 | std::unique_ptr parseBreakStmt(); 75 | std::unique_ptr parseContinueStmt(); 76 | std::unique_ptr parseExpressionStmt(); 77 | 78 | std::unique_ptr parseExpr(); 79 | 80 | std::unique_ptr parseBlockExpr(); 81 | std::unique_ptr parseIfExpr(); 82 | std::unique_ptr parseWhileExpr(); 83 | std::unique_ptr parseForExpr(); 84 | std::unique_ptr parseSwitchExpr(); 85 | 86 | std::unique_ptr parsePrecAssignment(); 87 | std::unique_ptr parsePrecLogicalOr(); 88 | std::unique_ptr parsePrecLogicalAnd(); 89 | std::unique_ptr parsePrecEquality(); 90 | std::unique_ptr parsePrecComparison(); 91 | std::unique_ptr parsePrecCast(); 92 | std::unique_ptr parsePrecRange(); 93 | std::unique_ptr parsePrecBitwiseOr(); 94 | std::unique_ptr parsePrecBitwiseXor(); 95 | std::unique_ptr parsePrecBitwiseAnd(); 96 | std::unique_ptr parsePrecAdd(); 97 | std::unique_ptr parsePrecMultiply(); 98 | std::unique_ptr parsePrecBitwiseShift(); 99 | std::unique_ptr parsePrecUnary(); 100 | std::unique_ptr parsePrecCall(); 101 | std::unique_ptr parsePrecPrimary(); 102 | 103 | std::unique_ptr parseInterpolationExpr(); 104 | 105 | std::unique_ptr expectTypename(const std::string& msg, bool emptyAllowed = false); 106 | 107 | std::unique_ptr expectTypenamePrecFunction(const std::string& msg, bool emptyAllowed = false); 108 | std::unique_ptr expectTypenamePrecUnary(const std::string& msg, bool emptyAllowed = false); 109 | std::unique_ptr expectTypenamePrecParametric(const std::string& msg, bool emptyAllowed = false); 110 | std::unique_ptr expectTypenamePrecPrimary(const std::string& msg, bool emptyAllowed = false); 111 | 112 | std::unique_ptr expectBlock(const std::string& msg); 113 | 114 | void advance(); 115 | bool check(TokenType expected); 116 | bool consume(TokenType expected); 117 | void expect(TokenType type, const std::string &message); 118 | bool isAtEnd(); 119 | 120 | ParseError errorAt(const Token &token, const std::string &message); 121 | ParseError errorAtCurrent(const std::string &message); 122 | ParseError error(const std::string &message); 123 | void synchronise(); 124 | 125 | CompileContext &m_context; 126 | 127 | Lexer m_scanner{""}; 128 | 129 | Token m_previous{}; 130 | Token m_current{}; 131 | 132 | bool m_hadError = false; 133 | }; 134 | } 135 | 136 | #endif //ENACT_COMPILER_H 137 | -------------------------------------------------------------------------------- /lib/parser/Token.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_TOKEN_H 2 | #define ENACT_TOKEN_H 3 | 4 | #include "../common.h" 5 | 6 | #include 7 | 8 | namespace enact { 9 | enum class TokenType { 10 | // Single character tokens. 11 | LEFT_PAREN, RIGHT_PAREN, 12 | LEFT_BRACE, RIGHT_BRACE, 13 | LEFT_SQUARE, RIGHT_SQUARE, 14 | AMPERSAND, APOSTROPHE, CARAT, COMMA, DOLLAR, 15 | HASH, MINUS, PIPE, PLUS, QUESTION, 16 | SEMICOLON, SLASH, STAR, TILDE, 17 | 18 | // 1 or 2 character tokens. 19 | BANG, BANG_EQUAL, 20 | EQUAL, EQUAL_EQUAL, EQUAL_GREATER, 21 | GREATER, GREATER_EQUAL, GREATER_GREATER, 22 | LESS, LESS_EQUAL, LESS_LESS, 23 | 24 | // 1, 2, or 3 character tokens. 25 | DOT, DOT_DOT, DOT_DOT_DOT, 26 | 27 | // Literals. 28 | IDENTIFIER, INTEGER, FLOAT, 29 | STRING, INTERPOLATION, 30 | 31 | // Reserved words. 32 | AND, AS, ASSOC, BREAK, CASE, 33 | CONTINUE, DEFAULT, ELSE, ENUM, FALSE, 34 | GC, FUNC, FOR, IF, IMM, 35 | IMPL, IN, IS, MUT, NOT, 36 | OR, PUB, RC, RETURN, SO, 37 | STRUCT, SWITCH, TRAIT, TRUE, WHEN, 38 | WHILE, 39 | 40 | ERROR, END_OF_FILE, ENUM_MAX, 41 | }; 42 | 43 | struct Token { 44 | TokenType type; 45 | std::string lexeme; 46 | line_t line; 47 | col_t col; 48 | }; 49 | } 50 | 51 | #endif //ENACT_TOKEN_H 52 | -------------------------------------------------------------------------------- /lib/parser/Typename.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Typename.h" 4 | 5 | namespace enact { 6 | BasicTypename::BasicTypename(Token name) : 7 | m_name{name.lexeme}, 8 | m_where{std::move(name)} {} 9 | 10 | BasicTypename::BasicTypename(std::string name, Token where) : 11 | m_name{std::move(name)}, 12 | m_where{std::move(where)} {} 13 | 14 | BasicTypename::BasicTypename(const BasicTypename& typeName) : 15 | BasicTypename{typeName.m_name, typeName.m_where} {} 16 | 17 | std::unique_ptr BasicTypename::clone() const { 18 | return std::make_unique(*this); 19 | } 20 | 21 | Typename::Kind BasicTypename::kind() const { 22 | return Kind::BASIC; 23 | } 24 | 25 | const std::string& BasicTypename::name() const { 26 | return m_name; 27 | } 28 | 29 | const Token& BasicTypename::where() const { 30 | return m_where; 31 | } 32 | 33 | VariableTypename::VariableTypename(Token identifier) : 34 | m_identifier{std::move(identifier)}, 35 | m_name{"$" + m_identifier.lexeme} { 36 | } 37 | 38 | VariableTypename::VariableTypename(const VariableTypename &typename_) : 39 | VariableTypename{typename_.m_identifier} { 40 | } 41 | 42 | std::unique_ptr VariableTypename::clone() const { 43 | return std::make_unique(*this); 44 | } 45 | 46 | Typename::Kind VariableTypename::kind() const { 47 | return Kind::VARIABLE; 48 | } 49 | 50 | const std::string& VariableTypename::name() const { 51 | return m_name; 52 | } 53 | 54 | const Token& VariableTypename::where() const { 55 | return m_identifier; 56 | } 57 | 58 | ParametricTypename::ParametricTypename( 59 | std::unique_ptr constructorTypename, 60 | std::vector > parameterTypenames) : 61 | m_constructorTypename{std::move(constructorTypename)}, 62 | m_parameterTypenames{std::move(parameterTypenames)}, 63 | m_name{} { 64 | std::ostringstream name; 65 | std::string separator; 66 | 67 | name << m_constructorTypename->name() << '['; 68 | for (const auto& parameterTypename : m_parameterTypenames) { 69 | name << separator << parameterTypename->name(); 70 | separator = ", "; 71 | } 72 | name << ']'; 73 | 74 | m_name = name.str(); 75 | } 76 | 77 | ParametricTypename::ParametricTypename(const ParametricTypename &typename_) : 78 | ParametricTypename{ 79 | typename_.m_constructorTypename->clone(), 80 | cloneAll(typename_.m_parameterTypenames)} { 81 | } 82 | 83 | std::unique_ptr ParametricTypename::clone() const { 84 | return std::make_unique(*this); 85 | } 86 | 87 | Typename::Kind ParametricTypename::kind() const { 88 | return Kind::PARAMETRIC; 89 | } 90 | 91 | const std::string& ParametricTypename::name() const { 92 | return m_name; 93 | } 94 | 95 | const Token& ParametricTypename::where() const { 96 | return m_constructorTypename->where(); 97 | } 98 | 99 | const Typename& ParametricTypename::constructorTypename() const { 100 | return *m_constructorTypename; 101 | } 102 | 103 | const std::vector>& ParametricTypename::parameterTypenames() const { 104 | return m_parameterTypenames; 105 | } 106 | 107 | TupleTypename::TupleTypename(std::vector> elementTypenames, Token paren) : 108 | m_elementTypenames{std::move(elementTypenames)}, 109 | m_paren{std::move(paren)} { 110 | std::ostringstream name; 111 | std::string separator; 112 | 113 | name << '('; 114 | for (const auto& elementTypename : m_elementTypenames) { 115 | name << separator << elementTypename->name(); 116 | separator = ", "; 117 | } 118 | name << ')'; 119 | 120 | m_name = name.str(); 121 | } 122 | 123 | TupleTypename::TupleTypename(const TupleTypename &typename_) : 124 | TupleTypename{ 125 | cloneAll(typename_.m_elementTypenames), 126 | typename_.m_paren} { 127 | } 128 | 129 | std::unique_ptr TupleTypename::clone() const { 130 | return std::make_unique(*this); 131 | } 132 | 133 | Typename::Kind TupleTypename::kind() const { 134 | return Kind::TUPLE; 135 | } 136 | 137 | const std::string& TupleTypename::name() const { 138 | return m_name; 139 | } 140 | 141 | const Token& TupleTypename::where() const { 142 | return m_paren; 143 | } 144 | 145 | const std::vector>& TupleTypename::elementTypenames() const { 146 | return m_elementTypenames; 147 | } 148 | 149 | FunctionTypename::FunctionTypename(std::unique_ptr returnTypename, 150 | std::vector> argumentTypenames) : 151 | m_returnTypename{std::move(returnTypename)}, 152 | m_argumentTypenames{std::move(argumentTypenames)} { 153 | std::ostringstream name{}; 154 | 155 | const bool wrap = argumentTypenames.size() != 1; 156 | 157 | if (wrap) name << '('; 158 | 159 | std::string separator{}; 160 | for (const auto& typename_ : m_argumentTypenames) { 161 | name << separator; 162 | name << typename_->name(); 163 | separator = ", "; 164 | } 165 | 166 | if (wrap) name << ')'; 167 | name << " => " << m_returnTypename->name(); 168 | 169 | m_name = name.str(); 170 | } 171 | 172 | FunctionTypename::FunctionTypename(const FunctionTypename& typename_) : 173 | FunctionTypename{ 174 | typename_.m_returnTypename->clone(), 175 | cloneAll(typename_.m_argumentTypenames)} { 176 | } 177 | 178 | std::unique_ptr FunctionTypename::clone() const { 179 | return std::make_unique(*this); 180 | } 181 | 182 | const Typename& FunctionTypename::returnTypename() const { 183 | return *m_returnTypename; 184 | } 185 | 186 | const std::vector>& FunctionTypename::argumentTypenames() const { 187 | return m_argumentTypenames; 188 | } 189 | 190 | Typename::Kind FunctionTypename::kind() const { 191 | return Kind::FUNCTION; 192 | } 193 | 194 | const std::string& FunctionTypename::name() const { 195 | return m_name; 196 | } 197 | 198 | const Token& FunctionTypename::where() const { 199 | return m_returnTypename->where(); 200 | } 201 | 202 | ReferenceTypename::ReferenceTypename( 203 | std::optional permission, 204 | std::optional region, 205 | std::unique_ptr referringTypename) : 206 | m_permission{std::move(permission)}, 207 | m_region{std::move(region)}, 208 | m_referringTypename{std::move(referringTypename)}, 209 | m_name{ 210 | "&" + 211 | (m_permission ? m_permission->lexeme : "") + " " + 212 | (m_region ? m_region->lexeme : "")} { 213 | } 214 | 215 | ReferenceTypename::ReferenceTypename(const ReferenceTypename& typename_) : 216 | ReferenceTypename{ 217 | typename_.m_permission, 218 | typename_.m_region, 219 | typename_.m_referringTypename->clone()} { 220 | } 221 | 222 | std::unique_ptr ReferenceTypename::clone() const { 223 | return std::make_unique(*this); 224 | } 225 | 226 | Typename::Kind ReferenceTypename::kind() const { 227 | return Kind::REFERENCE; 228 | } 229 | 230 | const std::string& ReferenceTypename::name() const { 231 | return m_name; 232 | } 233 | 234 | const Token& ReferenceTypename::where() const { 235 | return m_referringTypename->where(); 236 | } 237 | 238 | const std::optional& ReferenceTypename::permission() const { 239 | return m_permission; 240 | } 241 | 242 | const std::optional& ReferenceTypename::region() const { 243 | return m_region; 244 | } 245 | 246 | const std::unique_ptr& ReferenceTypename::referringTypename() const { 247 | return m_referringTypename; 248 | } 249 | 250 | OptionalTypename::OptionalTypename(std::unique_ptr wrappedTypename) : 251 | m_wrappedTypename{std::move(wrappedTypename)}, 252 | m_name{"?" + m_wrappedTypename->name()} { 253 | } 254 | 255 | OptionalTypename::OptionalTypename(const OptionalTypename &typename_) : 256 | OptionalTypename{typename_.m_wrappedTypename->clone()} { 257 | } 258 | 259 | std::unique_ptr OptionalTypename::clone() const { 260 | return std::make_unique(*this); 261 | } 262 | 263 | Typename::Kind OptionalTypename::kind() const { 264 | return Kind::OPTIONAL; 265 | } 266 | 267 | const std::string& OptionalTypename::name() const { 268 | return m_name; 269 | } 270 | 271 | const Token& OptionalTypename::where() const { 272 | return m_wrappedTypename->where(); 273 | } 274 | 275 | const Typename& OptionalTypename::wrappedTypename() const { 276 | return *m_wrappedTypename; 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /lib/parser/Typename.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_TYPENAME_H 2 | #define ENACT_TYPENAME_H 3 | 4 | #include 5 | #include 6 | 7 | #include "Token.h" 8 | 9 | namespace enact { 10 | class Typename { 11 | public: 12 | enum class Kind { 13 | BASIC, 14 | VARIABLE, 15 | PARAMETRIC, 16 | TUPLE, 17 | FUNCTION, 18 | REFERENCE, 19 | OPTIONAL 20 | }; 21 | 22 | virtual ~Typename() = default; 23 | 24 | virtual std::unique_ptr clone() const = 0; 25 | virtual Kind kind() const = 0; 26 | 27 | virtual const std::string& name() const = 0; 28 | virtual const Token& where() const = 0; 29 | }; 30 | 31 | class BasicTypename : public Typename { 32 | public: 33 | explicit BasicTypename(Token name); 34 | explicit BasicTypename(std::string name, Token where); 35 | BasicTypename(const BasicTypename& typename_); 36 | 37 | ~BasicTypename() override = default; 38 | 39 | std::unique_ptr clone() const override; 40 | Kind kind() const override; 41 | 42 | const std::string& name() const override; 43 | const Token& where() const override; 44 | 45 | private: 46 | std::string m_name; 47 | Token m_where; 48 | }; 49 | 50 | class VariableTypename : public Typename { 51 | public: 52 | explicit VariableTypename(Token identifier); 53 | VariableTypename(const VariableTypename& typename_); 54 | 55 | ~VariableTypename() override = default; 56 | 57 | std::unique_ptr clone() const override; 58 | Kind kind() const override; 59 | 60 | const std::string& name() const override; 61 | const Token& where() const override; 62 | 63 | private: 64 | Token m_identifier; 65 | 66 | std::string m_name; 67 | }; 68 | 69 | class ParametricTypename : public Typename { 70 | public: 71 | ParametricTypename( 72 | std::unique_ptr constructorTypename, 73 | std::vector> parameterTypenames); 74 | ParametricTypename(const ParametricTypename& typename_); 75 | 76 | ~ParametricTypename() override = default; 77 | 78 | std::unique_ptr clone() const override; 79 | Kind kind() const override; 80 | 81 | const std::string& name() const override; 82 | const Token& where() const override; 83 | 84 | const Typename& constructorTypename() const; 85 | const std::vector>& parameterTypenames() const; 86 | 87 | private: 88 | std::unique_ptr m_constructorTypename; 89 | std::vector> m_parameterTypenames; 90 | 91 | std::string m_name; 92 | }; 93 | 94 | class TupleTypename : public Typename { 95 | public: 96 | TupleTypename( 97 | std::vector> elementTypenames, 98 | Token paren); 99 | TupleTypename(const TupleTypename& typename_); 100 | 101 | ~TupleTypename() override = default; 102 | 103 | std::unique_ptr clone() const override; 104 | Kind kind() const override; 105 | 106 | const std::string& name() const override; 107 | const Token& where() const override; 108 | 109 | const std::vector>& elementTypenames() const; 110 | 111 | private: 112 | std::vector> m_elementTypenames; 113 | Token m_paren; 114 | 115 | std::string m_name; 116 | }; 117 | 118 | class FunctionTypename : public Typename { 119 | public: 120 | FunctionTypename(std::unique_ptr returnTypename, 121 | std::vector> argumentTypenames); 122 | FunctionTypename(const FunctionTypename& typename_); 123 | 124 | ~FunctionTypename() override = default; 125 | 126 | std::unique_ptr clone() const override; 127 | Kind kind() const override; 128 | 129 | const std::string& name() const override; 130 | const Token& where() const override; 131 | 132 | const Typename& returnTypename() const; 133 | const std::vector>& argumentTypenames() const; 134 | 135 | private: 136 | std::unique_ptr m_returnTypename; 137 | std::vector> m_argumentTypenames; 138 | std::string m_name; 139 | }; 140 | 141 | class ReferenceTypename : public Typename { 142 | public: 143 | ReferenceTypename( 144 | std::optional permission, 145 | std::optional region, 146 | std::unique_ptr referringTypename); 147 | ReferenceTypename(const ReferenceTypename& typename_); 148 | 149 | ~ReferenceTypename() override = default; 150 | 151 | std::unique_ptr clone() const override; 152 | Kind kind() const override; 153 | 154 | const std::string& name() const override; 155 | const Token& where() const override; 156 | 157 | const std::optional& permission() const; 158 | const std::optional& region() const; 159 | const std::unique_ptr& referringTypename() const; 160 | 161 | private: 162 | std::optional m_permission; 163 | std::optional m_region; 164 | std::unique_ptr m_referringTypename; 165 | std::string m_name; 166 | }; 167 | 168 | class OptionalTypename : public Typename { 169 | public: 170 | explicit OptionalTypename(std::unique_ptr wrappedTypename); 171 | OptionalTypename(const OptionalTypename& typename_); 172 | 173 | ~OptionalTypename() override = default; 174 | 175 | std::unique_ptr clone() const override; 176 | Kind kind() const override; 177 | 178 | const std::string& name() const override; 179 | const Token& where() const override; 180 | 181 | const Typename& wrappedTypename() const; 182 | 183 | private: 184 | std::unique_ptr m_wrappedTypename; 185 | 186 | std::string m_name; 187 | }; 188 | } 189 | 190 | #endif //ENACT_TYPENAME_H 191 | -------------------------------------------------------------------------------- /lib/sema/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SEMA_SRC 2 | ${CMAKE_CURRENT_SOURCE_DIR}/Sema.cpp 3 | ${CMAKE_CURRENT_SOURCE_DIR}/Sema.h 4 | ${CMAKE_CURRENT_SOURCE_DIR}/SemaDecls.cpp 5 | ${CMAKE_CURRENT_SOURCE_DIR}/SemaDecls.h 6 | 7 | ${CMAKE_CURRENT_SOURCE_DIR}/VariableInfo.h 8 | 9 | PARENT_SCOPE) -------------------------------------------------------------------------------- /lib/sema/Sema.cpp: -------------------------------------------------------------------------------- 1 | #include "Sema.h" 2 | 3 | namespace enact { 4 | Sema::Sema(Context& context) : m_context{context} { 5 | } 6 | 7 | void Sema::addDeclStmt(const Stmt& stmt) { 8 | m_decls.emplace_back(stmt); 9 | } 10 | 11 | void Sema::declareVariable(const std::string& name, const VariableInfo& info) { 12 | m_variables.emplaceOrAssign(name, info); 13 | } 14 | 15 | void Sema::declareType(const std::string& name, Type value) { 16 | m_types.emplaceOrAssign(name, value); 17 | } 18 | 19 | void Sema::defineVariable(const std::string &name, Type type) { 20 | VariableInfo& info = m_variables[name]; 21 | info.type = type; 22 | info.isInitialised = true; 23 | } 24 | 25 | void Sema::defineType(const std::string& name, Type value) { 26 | m_types[name] = type; 27 | } 28 | 29 | std::optional Sema::variableDeclared(const std::string& name) { 30 | return m_variables.at(name); 31 | } 32 | 33 | std::optional Sema::typeDeclared(const std::string& name) { 34 | return m_types.at(name); 35 | } 36 | } -------------------------------------------------------------------------------- /lib/sema/Sema.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_SEMA_H 2 | #define ENACT_SEMA_H 3 | 4 | namespace enact { 5 | // Forward declared from "../context/Context.h" 6 | class Context; 7 | 8 | class Sema { 9 | private: 10 | Context& m_context; 11 | 12 | // All global declarations. Assembled at parse time. We use reference 13 | // wrappers here relying on the assumption that the AST will be preserved 14 | // throughout the compilation process. 15 | std::vector> m_decls{}; 16 | 17 | // All global variables and their corresponding types and semantic info. 18 | // Populated (declared) by SemaDecls and resolved (defined) by SemaDefs. 19 | InsertionOrderMap m_variables{}; 20 | 21 | // All global types and their resolved values. Populated (declared) by 22 | // SemaDecls and resolved (defined) by SemaDefs. 23 | InsertionOrderMap m_types{}; 24 | 25 | // The two AST passes that we manage here. 26 | SemaDecls m_declarer{*this}; 27 | SemaDefs m_definer{*this}; 28 | 29 | public: 30 | explicit Sema(Context& context); 31 | 32 | // Parser uses this to add global declarations when it finds them. 33 | void addDeclStmt(const Stmt& stmt); 34 | 35 | // SemaDecl uses these to declare global names. 36 | void declareVariable(const std::string& name, const VariableInfo& info); 37 | void declareType(const std::string& name, Type value = nullptr); 38 | 39 | // SemaDef uses these to define global names. 40 | void defineVariable(const std::string& name, Type type); 41 | void defineType(const std::string& name, Type value); 42 | 43 | // Check if a symbol has been declared. Returns nullopt if the symbol 44 | // does not exist, otherwise returns the symbol in question. 45 | std::optional variableDeclared(const std::string& name); 46 | std::optional typeDeclared(const std::string& name); 47 | }; 48 | } 49 | 50 | #endif //ENACT_SEMA_H 51 | -------------------------------------------------------------------------------- /lib/sema/SemaDecls.cpp: -------------------------------------------------------------------------------- 1 | #include "SemaDecls.h" -------------------------------------------------------------------------------- /lib/sema/SemaDecls.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_SEMADECLS_H 2 | #define ENACT_SEMADECLS_H 3 | 4 | #include "../ast/Stmt.h" 5 | 6 | namespace enact { 7 | // Forward declared from "Sema.h" 8 | class Sema; 9 | 10 | // The first pass over the AST by Sema (semantic analysis). 11 | // We visit the top-level declarations provided to Sema by the parser, and declare 12 | // to Sema the variables and types that we come across - it's fine if they are 13 | // incomplete for now - we don't do any resolution until SemaDef, when everything 14 | // has been declared. 15 | class SemaDecls : private ExprVisitor, StmtVisitor { 16 | public: 17 | explicit SemaDecls(Sema& sema); 18 | 19 | void declareDeclStmts(); 20 | 21 | private: 22 | Sema& m_sema; 23 | 24 | void visit(Stmt& stmt); 25 | void visit(Expr& expr); 26 | 27 | void visitBlockStmt(BlockStmt &stmt) override; 28 | void visitBreakStmt(BreakStmt &stmt) override; 29 | void visitContinueStmt(ContinueStmt &stmt) override; 30 | void visitEachStmt(EachStmt &stmt) override; 31 | void visitExpressionStmt(ExpressionStmt &stmt) override; 32 | void visitForStmt(ForStmt &stmt) override; 33 | void visitFunctionStmt(FunctionStmt &stmt) override; 34 | void visitGivenStmt(GivenStmt &stmt) override; 35 | void visitIfStmt(IfStmt &stmt) override; 36 | void visitReturnStmt(ReturnStmt &stmt) override; 37 | void visitStructStmt(StructStmt &stmt) override; 38 | void visitTraitStmt(TraitStmt &stmt) override; 39 | void visitWhileStmt(WhileStmt &stmt) override; 40 | void visitVariableStmt(VariableStmt &stmt) override; 41 | void visitAllotExpr(AllotExpr &expr) override; 42 | void visitAnyExpr(AnyExpr &expr) override; 43 | void visitArrayExpr(ArrayExpr &expr) override; 44 | void visitAssignExpr(AssignExpr &expr) override; 45 | void visitBinaryExpr(BinaryExpr &expr) override; 46 | void visitBooleanExpr(BooleanExpr &expr) override; 47 | void visitCallExpr(CallExpr &expr) override; 48 | void visitFloatExpr(FloatExpr &expr) override; 49 | void visitGetExpr(GetExpr &expr) override; 50 | void visitIntegerExpr(IntegerExpr &expr) override; 51 | void visitLogicalExpr(LogicalExpr &expr) override; 52 | void visitNilExpr(NilExpr &expr) override; 53 | void visitSetExpr(SetExpr &expr) override; 54 | void visitStringExpr(StringExpr &expr) override; 55 | void visitSubscriptExpr(SubscriptExpr &expr) override; 56 | void visitTernaryExpr(TernaryExpr &expr) override; 57 | void visitUnaryExpr(UnaryExpr &expr) override; 58 | void visitVariableExpr(VariableExpr &expr) override; 59 | }; 60 | } 61 | 62 | #endif //ENACT_SEMADECLS_H 63 | -------------------------------------------------------------------------------- /lib/sema/SemaDefs.cpp: -------------------------------------------------------------------------------- 1 | #include "SemaDefs.h" 2 | 3 | namespace enact { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /lib/sema/SemaDefs.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_SEMADEFS_H 2 | #define ENACT_SEMADEFS_H 3 | 4 | namespace enact { 5 | // Forward declared from "Sema.h" 6 | class Sema; 7 | 8 | // The second and final pass over the AST conducted by Sema. 9 | // Using the type and variable declarations which SemaDecls so thoughtfully assembled 10 | // for us, we walk the AST again, hopefully giving a complete type to every declaration. 11 | 12 | // While doing this, we assign types to each node of the AST and perform typechecking, 13 | // including both declaration and definition for non-global scopes (we can do this all 14 | // here because local declarations must be in order). 15 | 16 | // If we come across a declaration that has not yet been defined, we first attempt to 17 | // define it, and if that is unsuccessful, we report an error. 18 | class SemaDefs { 19 | public: 20 | explicit SemaDefs(Sema& sema); 21 | 22 | std::vector> define(std::vector ast); 23 | 24 | private: 25 | Sema& m_sema; 26 | 27 | // All local variables and their corresponding types and semantic info. Each element 28 | // in the list, starting from the front, is one local scope, with the outermost global 29 | // scope stored in Sema. 30 | std::list> m_localVariables; 31 | 32 | // All local type declarations and their corresponding values. Again, each element in 33 | // the list, starting from the front, is one local scope, with the outermost global 34 | // scope stored in Sema. 35 | std::list> m_localTypes; 36 | 37 | // Keep track of the type of the current function to see if return statements are valid. 38 | std::vector> m_currentFunctions; 39 | 40 | // Push/pop a new local scope to both `m_localTypes` and `m_localVariables`. We start 41 | // in the global scope stored by Sema. 42 | void beginScope(); 43 | void endScope(); 44 | 45 | // Declare a local symbol in the scope specified by `depth`. Starts from the current scope, 46 | // i.e. the default, 0, is the innermost scope. 47 | void declareLocalVariable(const std::string& name, const VariableInfo& info, size_t depth = 0); 48 | void declareLocalType(const std::string& name, Type value = nullptr, size_t depth = 0); 49 | 50 | // Define a local symbol in the scope specified by `depth`. 51 | void defineLocalVariable(const std::string& name, Type type, size_t depth = 0); 52 | void defineLocalType(const std::string& name, Type value, size_t depth = 0); 53 | 54 | // Look up a symbol, starting in the innermost scope and searching all the way out, including 55 | // in the global scope stored by Sema. Returns `nullopt` if the symbol has not been declared, 56 | // otherwise returns the symbol in question. 57 | std::optional variableDeclared(const std::string& name); 58 | std::optional typeDeclared(const std::string& name); 59 | 60 | void visitBlockStmt(BlockStmt &stmt) override; 61 | void visitBreakStmt(BreakStmt &stmt) override; 62 | void visitContinueStmt(ContinueStmt &stmt) override; 63 | void visitEachStmt(EachStmt &stmt) override; 64 | void visitDeclarationStmt(DeclarationStmt &stmt) override; 65 | void visitForStmt(ForStmt &stmt) override; 66 | void visitGivenStmt(GivenStmt &stmt) override; 67 | void visitIfStmt(IfStmt &stmt) override; 68 | void visitReturnStmt(ReturnStmt &stmt) override; 69 | void visitWhileStmt(WhileStmt &stmt) override; 70 | 71 | void visitExpressionDecl(ExpressionDecl& decl) override; 72 | void visitFunctionDecl(FunctionDecl& decl) override; 73 | void visitStructDecl(StructDecl& decl) override; 74 | void visitTraitDecl(TraitDecl& decl) override; 75 | void visitVariableDecl(VariableDecl& decl) override; 76 | 77 | void visitAllotExpr(AllotExpr &expr) override; 78 | void visitAnyExpr(AnyExpr &expr) override; 79 | void visitArrayExpr(ArrayExpr &expr) override; 80 | void visitAssignExpr(AssignExpr &expr) override; 81 | void visitBinaryExpr(BinaryExpr &expr) override; 82 | void visitBooleanExpr(BooleanExpr &expr) override; 83 | void visitCallExpr(CallExpr &expr) override; 84 | void visitFloatExpr(FloatExpr &expr) override; 85 | void visitGetExpr(GetExpr &expr) override; 86 | void visitIntegerExpr(IntegerExpr &expr) override; 87 | void visitLogicalExpr(LogicalExpr &expr) override; 88 | void visitNilExpr(NilExpr &expr) override; 89 | void visitSetExpr(SetExpr &expr) override; 90 | void visitStringExpr(StringExpr &expr) override; 91 | void visitSubscriptExpr(SubscriptExpr &expr) override; 92 | void visitTernaryExpr(TernaryExpr &expr) override; 93 | void visitUnaryExpr(UnaryExpr &expr) override; 94 | void visitVariableExpr(VariableExpr &expr) override; 95 | }; 96 | } 97 | 98 | #endif //ENACT_SEMADEFS_H 99 | -------------------------------------------------------------------------------- /lib/sema/VariableInfo.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_VARIABLEINFO_H 2 | #define ENACT_VARIABLEINFO_H 3 | 4 | namespace enact { 5 | enum class Mutability { 6 | NONE, // 'val' 7 | BOXED, // 'let' 8 | FULL // 'var' 9 | }; 10 | 11 | struct VariableInfo { 12 | Type type; 13 | Mutability mutability; 14 | bool isInitialised; 15 | }; 16 | } 17 | 18 | #endif //ENACT_VARIABLEINFO_H 19 | -------------------------------------------------------------------------------- /lib/trivialStructs.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_GIVENCASE_H 2 | #define ENACT_GIVENCASE_H 3 | 4 | #include "parser/Typename.h" 5 | 6 | namespace enact { 7 | 8 | } 9 | 10 | #endif //ENACT_GIVENCASE_H 11 | -------------------------------------------------------------------------------- /lib/type/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(TYPE_SRC 2 | ${CMAKE_CURRENT_SOURCE_DIR}/Type.cpp 3 | ${CMAKE_CURRENT_SOURCE_DIR}/Type.h 4 | 5 | PARENT_SCOPE) -------------------------------------------------------------------------------- /lib/type/Type.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../ast/Stmt.h" 5 | 6 | #include "Type.h" 7 | 8 | namespace enact { 9 | const Type INT_TYPE = std::make_shared(PrimitiveKind::INT); 10 | const Type FLOAT_TYPE = std::make_shared(PrimitiveKind::FLOAT); 11 | const Type BOOL_TYPE = std::make_shared(PrimitiveKind::BOOL); 12 | const Type STRING_TYPE = std::make_shared(PrimitiveKind::STRING); 13 | const Type DYNAMIC_TYPE = std::make_shared(PrimitiveKind::DYNAMIC); 14 | const Type NOTHING_TYPE = std::make_shared(PrimitiveKind::NOTHING); 15 | 16 | TypeBase::TypeBase(TypeKind kind) : 17 | m_kind{kind} {} 18 | 19 | TypeKind TypeBase::getKind() const { 20 | return m_kind; 21 | } 22 | 23 | bool TypeBase::operator==(const TypeBase &type) const { 24 | if (type.getKind() != m_kind) return false; 25 | 26 | switch (m_kind) { 27 | case TypeKind::PRIMITIVE: { 28 | auto left = this->as(); 29 | auto right = type.as(); 30 | return left->getPrimitiveKind() == right->getPrimitiveKind(); 31 | } 32 | case TypeKind::ARRAY: { 33 | auto left = this->as(); 34 | auto right = type.as(); 35 | return *(left->getElementType()) == *(right->getElementType()); 36 | } 37 | case TypeKind::FUNCTION: { 38 | auto left = this->as(); 39 | auto right = type.as(); 40 | 41 | if (*(left->getReturnType()) != *(right->getReturnType())) return false; 42 | if (left->getArgumentTypes().size() != right->getArgumentTypes().size()) return false; 43 | 44 | for (int i = 0; i < left->getArgumentTypes().size(); ++i) { 45 | if (*(left->getArgumentTypes()[i]) != *(right->getArgumentTypes()[i])) return false; 46 | } 47 | 48 | return true; 49 | } 50 | case TypeKind::TRAIT: { 51 | auto left = this->as(); 52 | auto right = type.as(); 53 | 54 | return left->getName() == right->getName(); 55 | } 56 | case TypeKind::STRUCT: { 57 | auto left = this->as(); 58 | auto right = type.as(); 59 | 60 | return left->getName() == right->getName(); 61 | } 62 | case TypeKind::CONSTRUCTOR: { 63 | auto left = this->as(); 64 | auto right = type.as(); 65 | 66 | return *left->getStructType() == *right->getStructType(); 67 | } 68 | 69 | // Unreachable 70 | default: 71 | return false; 72 | } 73 | } 74 | 75 | bool TypeBase::operator!=(const TypeBase &type) const { 76 | return !(*this == type); 77 | } 78 | 79 | bool TypeBase::looselyEquals(const TypeBase &type) const { 80 | if (this->isArray() && type.isArray()) { 81 | return this->as()->getElementType()->looselyEquals(*type.as()->getElementType()); 82 | } 83 | if (this->isTrait() && type.isStruct()) { 84 | return type.as()->hasTrait(*this); 85 | } 86 | if (type.isTrait() && this->isStruct()) { 87 | return this->as()->hasTrait(*this); 88 | } 89 | 90 | return this->isDynamic() || type.isDynamic() || (this->isInt() && type.isNumeric()) || *this == type; 91 | } 92 | 93 | std::string TypeBase::toString() const { 94 | return toTypename()->name(); 95 | } 96 | 97 | bool TypeBase::isPrimitive() const { 98 | return m_kind == TypeKind::PRIMITIVE && !isDynamic(); 99 | } 100 | 101 | bool TypeBase::isInt() const { 102 | return m_kind == TypeKind::PRIMITIVE && 103 | this->as()->getPrimitiveKind() == PrimitiveKind::INT; 104 | } 105 | 106 | bool TypeBase::isFloat() const { 107 | return m_kind == TypeKind::PRIMITIVE && 108 | this->as()->getPrimitiveKind() == PrimitiveKind::FLOAT; 109 | } 110 | 111 | bool TypeBase::isNumeric() const { 112 | return isInt() || isFloat(); 113 | } 114 | 115 | bool TypeBase::isBool() const { 116 | return m_kind == TypeKind::PRIMITIVE && 117 | this->as()->getPrimitiveKind() == PrimitiveKind::BOOL; 118 | } 119 | 120 | bool TypeBase::isString() const { 121 | return m_kind == TypeKind::PRIMITIVE && 122 | this->as()->getPrimitiveKind() == PrimitiveKind::STRING; 123 | } 124 | 125 | bool TypeBase::isDynamic() const { 126 | return m_kind == TypeKind::PRIMITIVE && 127 | this->as()->getPrimitiveKind() == PrimitiveKind::DYNAMIC; 128 | } 129 | 130 | bool TypeBase::isNothing() const { 131 | return m_kind == TypeKind::PRIMITIVE && 132 | this->as()->getPrimitiveKind() == PrimitiveKind::NOTHING; 133 | } 134 | 135 | bool TypeBase::maybePrimitive() const { 136 | return m_kind == TypeKind::PRIMITIVE; 137 | } 138 | 139 | bool TypeBase::maybeInt() const { 140 | return isInt() || isDynamic(); 141 | } 142 | 143 | bool TypeBase::maybeFloat() const { 144 | return isFloat() || isDynamic(); 145 | } 146 | 147 | bool TypeBase::maybeNumeric() const { 148 | return isNumeric() || isDynamic(); 149 | } 150 | 151 | bool TypeBase::maybeBool() const { 152 | return isBool() || isDynamic(); 153 | } 154 | 155 | bool TypeBase::maybeString() const { 156 | return isString() || isDynamic(); 157 | } 158 | 159 | bool TypeBase::isArray() const { 160 | return m_kind == TypeKind::ARRAY; 161 | } 162 | 163 | bool TypeBase::isFunction() const { 164 | return m_kind == TypeKind::FUNCTION; 165 | } 166 | 167 | bool TypeBase::isTrait() const { 168 | return m_kind == TypeKind::TRAIT; 169 | } 170 | 171 | bool TypeBase::isStruct() const { 172 | return m_kind == TypeKind::STRUCT; 173 | } 174 | 175 | bool TypeBase::isConstructor() const { 176 | return m_kind == TypeKind::CONSTRUCTOR; 177 | } 178 | 179 | bool TypeBase::maybeArray() const { 180 | return isArray() || isDynamic(); 181 | } 182 | 183 | bool TypeBase::maybeFunction() const { 184 | return isFunction() || isDynamic(); 185 | } 186 | 187 | bool TypeBase::maybeTrait() const { 188 | return isTrait() || isDynamic(); 189 | } 190 | 191 | bool TypeBase::maybeStruct() const { 192 | return isStruct() || isDynamic(); 193 | } 194 | 195 | 196 | // Primitive types 197 | PrimitiveType::PrimitiveType(PrimitiveKind kind) : 198 | TypeBase{TypeKind::PRIMITIVE}, 199 | m_kind{kind} {} 200 | 201 | PrimitiveKind PrimitiveType::getPrimitiveKind() const { 202 | return m_kind; 203 | } 204 | 205 | std::unique_ptr PrimitiveType::toTypename() const { 206 | std::string name; 207 | switch (m_kind) { 208 | case PrimitiveKind::INT: 209 | name = "int"; 210 | break; 211 | case PrimitiveKind::FLOAT: 212 | name = "float"; 213 | break; 214 | case PrimitiveKind::BOOL: 215 | name = "bool"; 216 | break; 217 | case PrimitiveKind::STRING: 218 | name = "String"; 219 | break; 220 | case PrimitiveKind::DYNAMIC: 221 | name = "any"; 222 | break; 223 | case PrimitiveKind::NOTHING: 224 | name = "nothing"; 225 | break; 226 | } 227 | return std::make_unique(Token{TokenType::IDENTIFIER, name, 0, 0}); 228 | } 229 | 230 | ArrayType::ArrayType(Type elementType) : 231 | TypeBase{TypeKind::ARRAY}, 232 | m_elementType{elementType} {} 233 | 234 | const Type ArrayType::getElementType() const { 235 | return m_elementType; 236 | } 237 | 238 | std::unique_ptr ArrayType::toTypename() const { 239 | return std::make_unique(m_elementType->toTypename()); 240 | } 241 | 242 | FunctionType::FunctionType(Type returnType, std::vector argumentTypes, bool isMethod, bool isNative) : 243 | TypeBase{TypeKind::FUNCTION}, 244 | m_returnType{returnType}, 245 | m_argumentTypes{argumentTypes}, 246 | m_isMethod{isMethod}, 247 | m_isNative{isNative} {} 248 | 249 | const Type FunctionType::getReturnType() const { 250 | return m_returnType; 251 | } 252 | 253 | const std::vector &FunctionType::getArgumentTypes() const { 254 | return m_argumentTypes; 255 | } 256 | 257 | bool FunctionType::isMethod() const { 258 | return m_isMethod; 259 | } 260 | 261 | bool FunctionType::isNative() const { 262 | return m_isNative; 263 | } 264 | 265 | std::unique_ptr FunctionType::toTypename() const { 266 | std::vector> argumentTypenames; 267 | for (Type type : m_argumentTypes) { 268 | argumentTypenames.push_back(type->toTypename()); 269 | } 270 | return std::make_unique(m_returnType->toTypename(), std::move(argumentTypenames)); 271 | } 272 | 273 | TraitType::TraitType(std::string name, InsertionOrderMap methods) : 274 | TypeBase{TypeKind::TRAIT}, 275 | m_name{name}, 276 | m_methods{methods} {} 277 | 278 | const std::string &TraitType::getName() const { 279 | return m_name; 280 | } 281 | 282 | const InsertionOrderMap &TraitType::getMethods() const { 283 | return m_methods; 284 | } 285 | 286 | std::optional TraitType::getMethod(const std::string &name) const { 287 | return m_methods.at(name); 288 | } 289 | 290 | std::unique_ptr TraitType::toTypename() const { 291 | return std::make_unique(m_name, Token{TokenType::IDENTIFIER, m_name, 0, 0}); 292 | } 293 | 294 | StructType::StructType( 295 | std::string name, 296 | std::vector> traits, 297 | InsertionOrderMap fields, 298 | InsertionOrderMap methods) : 299 | TypeBase{TypeKind::STRUCT}, 300 | m_name{std::move(name)}, 301 | m_traits{std::move(traits)}, 302 | m_fields{std::move(fields)}, 303 | m_methods{std::move(methods)} { 304 | } 305 | 306 | const std::string &StructType::getName() const { 307 | return m_name; 308 | } 309 | 310 | const std::vector> &StructType::getTraits() const { 311 | return m_traits; 312 | } 313 | 314 | bool StructType::hasTrait(const TypeBase &trait) const { 315 | for (auto &myTrait : m_traits) { 316 | if (myTrait->looselyEquals(trait)) return true; 317 | } 318 | 319 | return false; 320 | } 321 | 322 | std::optional StructType::findTrait(const TypeBase &trait) const { 323 | size_t index = 0; 324 | for (auto &myTrait : m_traits) { 325 | if (myTrait->looselyEquals(trait)) return index; 326 | ++index; 327 | } 328 | 329 | return {}; 330 | } 331 | 332 | std::optional StructType::getProperty(const std::string &name) const { 333 | if (auto field = getField(name)) { 334 | return field; 335 | } 336 | 337 | return getMethod(name); 338 | } 339 | 340 | const InsertionOrderMap &StructType::getFields() const { 341 | return m_fields; 342 | } 343 | 344 | std::optional StructType::getField(const std::string &name) const { 345 | return m_fields.at(name); 346 | } 347 | 348 | std::optional StructType::findField(const std::string &name) const { 349 | return m_fields.find(name); 350 | } 351 | 352 | const InsertionOrderMap &StructType::getMethods() const { 353 | return m_methods; 354 | } 355 | 356 | std::optional StructType::getMethod(const std::string &name) const { 357 | return m_methods.at(name); 358 | } 359 | 360 | std::optional StructType::findMethod(const std::string &name) const { 361 | return m_methods.find(name); 362 | } 363 | 364 | std::unique_ptr StructType::toTypename() const { 365 | return std::make_unique(m_name, Token{TokenType::IDENTIFIER, m_name, 0, 0}); 366 | } 367 | 368 | ConstructorType::ConstructorType(std::shared_ptr structType, 369 | InsertionOrderMap assocProperties) : 370 | TypeBase{TypeKind::CONSTRUCTOR}, 371 | m_structType{structType}, 372 | m_assocProperties{assocProperties} { 373 | } 374 | 375 | std::shared_ptr ConstructorType::getStructType() const { 376 | return m_structType; 377 | } 378 | 379 | const InsertionOrderMap &ConstructorType::getAssocProperties() const { 380 | return m_assocProperties; 381 | } 382 | 383 | std::optional ConstructorType::getAssocProperty(const std::string &name) const { 384 | return m_assocProperties.at(name); 385 | } 386 | 387 | std::optional ConstructorType::findAssocProperty(const std::string &name) const { 388 | return m_assocProperties.find(name); 389 | } 390 | 391 | std::unique_ptr ConstructorType::toTypename() const { 392 | std::string name = m_structType->toString(); 393 | return std::make_unique( 394 | std::make_unique(name, Token{TokenType::IDENTIFIER, name, 0, 0})); 395 | } 396 | } -------------------------------------------------------------------------------- /lib/type/Type.h: -------------------------------------------------------------------------------- 1 | #ifndef ENACT_TYPE_H 2 | #define ENACT_TYPE_H 3 | 4 | #include 5 | #include 6 | 7 | #include "../InsertionOrderMap.h" 8 | #include "../parser/Token.h" 9 | #include "../parser/Typename.h" 10 | 11 | namespace enact { 12 | // Forward declarations from "../ast/Stmt.h": 13 | class FunctionStmt; 14 | 15 | // The type tree used to give expressions types. 16 | // Here's what it looks like: 17 | 18 | // Type 19 | // ├── Numeric 20 | // │ ├── Integral 21 | // │ └── Decimal 22 | // ├── String 23 | // ├── Struct 24 | // ├── Dynamic 25 | // └── Nothing 26 | 27 | class TypeBase; 28 | 29 | // Type is just a managed pointer to TypeBase that allows for polymorphism. 30 | typedef std::shared_ptr Type; 31 | 32 | extern const Type INT_TYPE; 33 | extern const Type FLOAT_TYPE; 34 | extern const Type BOOL_TYPE; 35 | extern const Type STRING_TYPE; 36 | extern const Type DYNAMIC_TYPE; 37 | extern const Type NOTHING_TYPE; 38 | 39 | enum class TypeKind { 40 | PRIMITIVE, 41 | ARRAY, 42 | FUNCTION, 43 | TRAIT, 44 | STRUCT, 45 | CONSTRUCTOR 46 | }; 47 | 48 | class TypeBase { 49 | private: 50 | TypeKind m_kind; 51 | public: 52 | TypeBase(TypeKind kind); 53 | 54 | virtual ~TypeBase() = default; 55 | 56 | virtual TypeKind getKind() const; 57 | 58 | // Strict equality comparison - the types must be exactly the same 59 | virtual bool operator==(const TypeBase &type) const; 60 | 61 | virtual bool operator!=(const TypeBase &type) const; 62 | 63 | // Loose equality comparison - the types must be exactly the same, 64 | // dynamic, or convertible to each other. 65 | virtual bool looselyEquals(const TypeBase &type) const; 66 | 67 | virtual std::unique_ptr toTypename() const = 0; 68 | 69 | virtual std::string toString() const; 70 | 71 | // Primitive type groups 72 | bool isPrimitive() const; 73 | 74 | bool isNumeric() const; 75 | 76 | bool isInt() const; 77 | 78 | bool isFloat() const; 79 | 80 | bool isBool() const; 81 | 82 | bool isString() const; 83 | 84 | bool isDynamic() const; 85 | 86 | bool isNothing() const; 87 | 88 | bool maybePrimitive() const; 89 | 90 | bool maybeNumeric() const; 91 | 92 | bool maybeInt() const; 93 | 94 | bool maybeFloat() const; 95 | 96 | bool maybeBool() const; 97 | 98 | bool maybeString() const; 99 | 100 | // Complex type groups 101 | bool isArray() const; 102 | 103 | bool isFunction() const; 104 | 105 | bool isTrait() const; 106 | 107 | bool isStruct() const; 108 | 109 | bool isConstructor() const; 110 | 111 | bool maybeArray() const; 112 | 113 | bool maybeFunction() const; 114 | 115 | bool maybeTrait() const; 116 | 117 | bool maybeStruct() const; 118 | 119 | template 120 | inline const T *as() const { 121 | static_assert(std::is_base_of_v, 122 | "TypeBase::as: T must derive from TypeBase."); 123 | return static_cast(this); 124 | } 125 | }; 126 | 127 | // Primitive types 128 | // - numbers/strings/dynamic/nothing 129 | enum class PrimitiveKind { 130 | INT, 131 | FLOAT, 132 | BOOL, 133 | STRING, 134 | DYNAMIC, 135 | NOTHING, 136 | }; 137 | 138 | class PrimitiveType : public TypeBase { 139 | private: 140 | PrimitiveKind m_kind; 141 | public: 142 | PrimitiveType(PrimitiveKind kind); 143 | 144 | ~PrimitiveType() override = default; 145 | 146 | PrimitiveKind getPrimitiveKind() const; 147 | 148 | std::unique_ptr toTypename() const override; 149 | }; 150 | 151 | // Array types 152 | class ArrayType : public TypeBase { 153 | Type m_elementType; 154 | public: 155 | ArrayType(Type elementType); 156 | 157 | ~ArrayType() override = default; 158 | 159 | const Type getElementType() const; 160 | 161 | std::unique_ptr toTypename() const override; 162 | }; 163 | 164 | // Function types 165 | class FunctionType : public TypeBase { 166 | Type m_returnType; 167 | std::vector m_argumentTypes; 168 | bool m_isMethod; 169 | bool m_isNative; 170 | public: 171 | FunctionType(Type returnType, std::vector argumentTypes, bool isMethod = false, bool isNative = false); 172 | 173 | ~FunctionType() override = default; 174 | 175 | const Type getReturnType() const; 176 | 177 | const std::vector &getArgumentTypes() const; 178 | 179 | bool isMethod() const; 180 | 181 | bool isNative() const; 182 | 183 | std::unique_ptr toTypename() const override; 184 | }; 185 | 186 | // Trait types 187 | class TraitType : public TypeBase { 188 | std::string m_name; 189 | InsertionOrderMap m_methods; 190 | public: 191 | TraitType(std::string name, InsertionOrderMap methods); 192 | 193 | ~TraitType() override = default; 194 | 195 | const std::string &getName() const; 196 | 197 | const InsertionOrderMap &getMethods() const; 198 | 199 | std::optional getMethod(const std::string &name) const; 200 | 201 | std::unique_ptr toTypename() const override; 202 | }; 203 | 204 | // Struct types 205 | class StructType : public TypeBase { 206 | std::string m_name; 207 | std::vector> m_traits; 208 | InsertionOrderMap m_fields; 209 | InsertionOrderMap m_methods; 210 | 211 | public: 212 | StructType( 213 | std::string name, 214 | std::vector> traits, 215 | InsertionOrderMap fields, 216 | InsertionOrderMap methods); 217 | 218 | ~StructType() override = default; 219 | 220 | const std::string &getName() const; 221 | 222 | const std::vector> &getTraits() const; 223 | 224 | bool hasTrait(const TypeBase &trait) const; 225 | 226 | std::optional findTrait(const TypeBase &trait) const; 227 | 228 | std::optional getProperty(const std::string &name) const; 229 | 230 | const InsertionOrderMap &getFields() const; 231 | 232 | std::optional getField(const std::string &name) const; 233 | 234 | std::optional findField(const std::string &name) const; 235 | 236 | const InsertionOrderMap &getMethods() const; 237 | 238 | std::optional getMethod(const std::string &name) const; 239 | 240 | std::optional findMethod(const std::string &name) const; 241 | 242 | std::unique_ptr toTypename() const override; 243 | }; 244 | 245 | // Struct constructor types 246 | class ConstructorType : public TypeBase { 247 | std::shared_ptr m_structType; 248 | InsertionOrderMap m_assocProperties; 249 | 250 | public: 251 | ConstructorType(std::shared_ptr structType, 252 | InsertionOrderMap assocProperties); 253 | 254 | ~ConstructorType() override = default; 255 | 256 | std::shared_ptr getStructType() const; 257 | 258 | const InsertionOrderMap &getAssocProperties() const; 259 | 260 | std::optional getAssocProperty(const std::string &name) const; 261 | 262 | std::optional findAssocProperty(const std::string &name) const; 263 | 264 | std::unique_ptr toTypename() const override; 265 | }; 266 | } 267 | 268 | #endif //ENACT_TYPE_H 269 | -------------------------------------------------------------------------------- /lib/value/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(VALUE_SRC 2 | ${CMAKE_CURRENT_SOURCE_DIR}/Object.cpp 3 | ${CMAKE_CURRENT_SOURCE_DIR}/Object.h 4 | ${CMAKE_CURRENT_SOURCE_DIR}/Value.cpp 5 | ${CMAKE_CURRENT_SOURCE_DIR}/Value.h 6 | 7 | PARENT_SCOPE) -------------------------------------------------------------------------------- /lib/value/Object.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../bytecode/Chunk.h" 4 | #include "../memory/GC.h" 5 | #include "../vm/VM.h" 6 | 7 | #include "Object.h" 8 | #include "Value.h" 9 | 10 | #ifdef DEBUG_LOG_GC 11 | #include 12 | #include "h/Chunk.h" 13 | #endif 14 | 15 | namespace enact { 16 | Object::Object(ObjectType type) : m_type{type} { 17 | } 18 | 19 | Object::~Object() { 20 | } 21 | 22 | bool Object::operator==(const Object &object) const { 23 | if (m_type != object.m_type) { 24 | return false; 25 | } 26 | 27 | switch (m_type) { 28 | case ObjectType::STRING: 29 | return this->as()->asStdString() == object.as()->asStdString(); 30 | case ObjectType::ARRAY: 31 | return this->as()->asVector() == object.as()->asVector(); 32 | } 33 | } 34 | 35 | void Object::mark() { 36 | m_isMarked = true; 37 | } 38 | 39 | void Object::unmark() { 40 | m_isMarked = false; 41 | } 42 | 43 | bool Object::isMarked() { 44 | return m_isMarked; 45 | } 46 | 47 | std::ostream &operator<<(std::ostream &stream, const Object &object) { 48 | stream << object.toString(); 49 | return stream; 50 | } 51 | 52 | StringObject::StringObject(std::string data) : Object{ObjectType::STRING}, m_data{std::move(data)} { 53 | 54 | } 55 | 56 | const std::string &StringObject::asStdString() const { 57 | return m_data; 58 | } 59 | 60 | std::string StringObject::toString() const { 61 | return asStdString(); 62 | } 63 | 64 | Type StringObject::getType() const { 65 | return STRING_TYPE; 66 | } 67 | 68 | StringObject *StringObject::clone() const { 69 | return new StringObject(*this); 70 | } 71 | 72 | size_t StringObject::size() const { 73 | return sizeof(StringObject); 74 | } 75 | 76 | ArrayObject::ArrayObject(Type type) : Object{ObjectType::ARRAY}, m_type{type}, m_vector{} { 77 | } 78 | 79 | ArrayObject::ArrayObject(size_t length, Type type) : Object{ObjectType::ARRAY}, m_vector{length}, m_type{type} { 80 | } 81 | 82 | ArrayObject::ArrayObject(std::vector vector, Type type) : Object{ObjectType::ARRAY}, 83 | m_vector{std::move(vector)}, m_type{type} { 84 | } 85 | 86 | size_t ArrayObject::length() const { 87 | return m_vector.size(); 88 | } 89 | 90 | Value &ArrayObject::at(size_t index) { 91 | return m_vector[index]; 92 | } 93 | 94 | const Value &ArrayObject::at(size_t index) const { 95 | return m_vector[index]; 96 | } 97 | 98 | void ArrayObject::append(Value value) { 99 | m_vector.push_back(value); 100 | } 101 | 102 | const std::vector &ArrayObject::asVector() const { 103 | return m_vector; 104 | } 105 | 106 | std::string ArrayObject::toString() const { 107 | std::stringstream output{}; 108 | std::string separator{}; 109 | 110 | output << "["; 111 | for (const Value &item : asVector()) { 112 | output << separator; 113 | output << item.toString(); 114 | separator = ", "; 115 | } 116 | output << "]"; 117 | 118 | return output.str(); 119 | } 120 | 121 | Type ArrayObject::getType() const { 122 | return m_type; 123 | } 124 | 125 | ArrayObject *ArrayObject::clone() const { 126 | return new ArrayObject(*this); 127 | } 128 | 129 | size_t ArrayObject::size() const { 130 | return sizeof(ArrayObject); 131 | } 132 | 133 | UpvalueObject::UpvalueObject(uint32_t location) : Object{ObjectType::UPVALUE}, m_location{location} { 134 | } 135 | 136 | uint32_t UpvalueObject::getLocation() { 137 | return m_location; 138 | } 139 | 140 | UpvalueObject *UpvalueObject::getNext() { 141 | return m_next; 142 | } 143 | 144 | void UpvalueObject::setNext(UpvalueObject *next) { 145 | m_next = next; 146 | } 147 | 148 | bool UpvalueObject::isClosed() const { 149 | return m_isClosed; 150 | } 151 | 152 | Value UpvalueObject::getClosed() const { 153 | return m_closed; 154 | } 155 | 156 | void UpvalueObject::setClosed(Value value) { 157 | m_isClosed = true; 158 | m_closed = value; 159 | } 160 | 161 | std::string UpvalueObject::toString() const { 162 | return "upvalue"; 163 | } 164 | 165 | Type UpvalueObject::getType() const { 166 | return NOTHING_TYPE; 167 | } 168 | 169 | UpvalueObject *UpvalueObject::clone() const { 170 | return new UpvalueObject(*this); 171 | } 172 | 173 | size_t UpvalueObject::size() const { 174 | return sizeof(UpvalueObject); 175 | } 176 | 177 | ClosureObject::ClosureObject(FunctionObject *function) : Object{ObjectType::CLOSURE}, m_function{function}, 178 | m_upvalues{function->getUpvalueCount()} { 179 | } 180 | 181 | FunctionObject *ClosureObject::getFunction() { 182 | return m_function; 183 | } 184 | 185 | std::vector &ClosureObject::getUpvalues() { 186 | return m_upvalues; 187 | } 188 | 189 | std::string ClosureObject::toString() const { 190 | return m_function->toString(); 191 | } 192 | 193 | Type ClosureObject::getType() const { 194 | return m_function->getType(); 195 | } 196 | 197 | ClosureObject *ClosureObject::clone() const { 198 | return new ClosureObject(*this); 199 | } 200 | 201 | size_t ClosureObject::size() const { 202 | return sizeof(ClosureObject); 203 | } 204 | 205 | StructObject::StructObject(std::shared_ptr constructorType, 206 | std::vector methods, std::vector assocs) : 207 | Object{ObjectType::STRUCT}, 208 | m_constructorType{std::move(constructorType)}, 209 | m_methods{std::move(methods)}, 210 | m_assocs{std::move(assocs)} { 211 | } 212 | 213 | const std::string &StructObject::getName() const { 214 | return m_constructorType->getStructType()->getName(); 215 | } 216 | 217 | std::vector &StructObject::methods() { 218 | return m_methods; 219 | } 220 | 221 | ClosureObject *StructObject::method(uint32_t index) { 222 | return m_methods[index]; 223 | } 224 | 225 | std::optional StructObject::methodNamed(const std::string &name) { 226 | if (auto index = m_constructorType->getStructType()->findMethod(name)) { 227 | return m_methods[*index]; 228 | } 229 | return {}; 230 | } 231 | 232 | std::vector &StructObject::assocs() { 233 | return m_assocs; 234 | } 235 | 236 | Value &StructObject::assoc(uint32_t index) { 237 | return m_assocs[index]; 238 | } 239 | 240 | std::optional> StructObject::assocNamed(const std::string &name) { 241 | if (auto index = m_constructorType->findAssocProperty(name)) { 242 | return m_assocs[*index]; 243 | } 244 | return {}; 245 | } 246 | 247 | std::string StructObject::toString() const { 248 | return "<" + m_constructorType->toString() + ">"; 249 | } 250 | 251 | Type StructObject::getType() const { 252 | return m_constructorType; 253 | } 254 | 255 | StructObject *StructObject::clone() const { 256 | return new StructObject(*this); 257 | } 258 | 259 | size_t StructObject::size() const { 260 | return sizeof(StructObject); 261 | } 262 | 263 | InstanceObject::InstanceObject(StructObject *struct_, std::vector fields) : 264 | Object{ObjectType::INSTANCE}, 265 | m_struct{struct_}, 266 | m_fields{std::move(fields)} { 267 | } 268 | 269 | StructObject *InstanceObject::getStruct() { 270 | return m_struct; 271 | } 272 | 273 | std::vector &InstanceObject::fields() { 274 | return m_fields; 275 | } 276 | 277 | Value &InstanceObject::field(uint32_t index) { 278 | return m_fields[index]; 279 | } 280 | 281 | std::optional> InstanceObject::fieldNamed(const std::string &name) { 282 | auto structType = getType()->as(); 283 | if (std::optional index = structType->findField(name)) { 284 | return m_fields[*index]; 285 | } 286 | 287 | return {}; 288 | } 289 | 290 | std::string InstanceObject::toString() const { 291 | return "<" + getType()->toString() + " instance>"; 292 | } 293 | 294 | Type InstanceObject::getType() const { 295 | return m_struct->getType()->as()->getStructType(); 296 | } 297 | 298 | StructObject *InstanceObject::clone() const { 299 | return nullptr; 300 | } 301 | 302 | size_t InstanceObject::size() const { 303 | return 0; 304 | } 305 | 306 | FunctionObject::FunctionObject(Type type, Chunk chunk, std::string name) : 307 | Object{ObjectType::FUNCTION}, m_type{type}, m_chunk{std::move(chunk)}, m_name{std::move(name)} { 308 | } 309 | 310 | Chunk &FunctionObject::getChunk() { 311 | return m_chunk; 312 | } 313 | 314 | const std::string &FunctionObject::getName() const { 315 | return m_name; 316 | } 317 | 318 | uint32_t &FunctionObject::getUpvalueCount() { 319 | return m_upvalueCount; 320 | } 321 | 322 | std::string FunctionObject::toString() const { 323 | // Check if this is the global function 324 | if (m_name.empty()) { 325 | return "