diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..7337f7d
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,3 @@
+*.aspl linguist-language=ASPL text=auto eol=lf
+*.v linguist-language=V text=auto eol=lf
+**/v.mod linguist-language=V text=auto eol=lf
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..5e7a35a
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,35 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: Bug report
+labels: bug, unreviewed
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Do '...'
+2. Click on '...'
+3. See '...'
+
+Or a minimal reproducible code example:
+```aspl
+// code here
+```
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Software (please complete the following information):**
+ - OS: [e.g. Windows]
+ - Version of ASPL [obtainable by running `aspl version`]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..8d408a6
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,7 @@
+contact_links:
+ - name: Discussion Q&A
+ url: https://github.com/aspl-lang/aspl/discussions/categories/q-a
+ about: You can ask and answer questions here
+ - name: Discord
+ url: https://discord.gg/UUNzAFrKU2
+ about: You can ask and answer questions here
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..144c9c7
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: Feature request
+labels: enhancement, unreviewed
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is.
+
+**Describe the solution you would like**
+A clear and concise description of what you want to be implemented.
+
+**Describe alternatives you have considered**
+A clear and concise description of any alternative solutions or features you have considered.
+
+**Additional context**
+Add any other context or screenshots about your feature request here.
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md
new file mode 100644
index 0000000..ef626a5
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/question.md
@@ -0,0 +1,14 @@
+---
+name: Question
+about: Ask a question about ASPL
+title: Question
+labels: help wanted, question, unreviewed
+assignees: ''
+
+---
+
+**Describe your question**
+I don't understand...
+
+**What kind answer do you expect?**
+I expect something like...
\ No newline at end of file
diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml
new file mode 100644
index 0000000..08d2ce7
--- /dev/null
+++ b/.github/workflows/ci_cd.yml
@@ -0,0 +1,303 @@
+name: CI/CD
+
+on:
+ - push
+ - workflow_dispatch
+
+jobs:
+ build-runtime-linux:
+ runs-on: ubuntu-latest
+ container:
+ image: debian:buster # use an older Debian version to support older glibc versions
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Install ASPL installer dependencies
+ run: |
+ apt-get update
+ apt-get install --quiet -y git jq curl unzip
+ - name: Install ASPL
+ run: |
+ cd $GITHUB_WORKSPACE
+ ./install_ci.sh
+ - name: Install runtime dependencies
+ run: |
+ apt-get update
+ apt-get install --quiet -y build-essential
+ apt-get install --quiet -y libglfw3-dev libxi-dev libxcursor-dev # for the graphics module
+ apt-get install --quiet -y gcc-arm-linux-gnueabi # for ARM32 cross-compilation
+ apt-get install --quiet -y lib32z1 # for ARM32 cross-compilation
+ - name: Build ASPL runtime templates
+ run: |
+ cd $GITHUB_WORKSPACE
+ aspl -os linux -cc gcc build-minimal-template
+ mv -f Template templates/linux/x86_64/minimal
+ aspl -os linux -cc gcc build-full-template
+ mv -f Template templates/linux/x86_64/full
+ # aspl -os linux -arch arm32 -cc arm-linux-gnueabi-gcc build-minimal-template
+ # mv -f Template templates/linux/arm32/minimal
+ # aspl -os linux -arch arm32 -cc arm-linux-gnueabi-gcc build-full-template
+ # mv -f Template templates/linux/arm32/full
+ - name: Upload template artifact (Linux x86_64 minimal)
+ uses: actions/upload-artifact@v3
+ with:
+ name: template_linux_x86_64_minimal
+ path: templates/linux/x86_64/minimal/Template
+ - name: Upload template artifact (Linux x86_64 full)
+ uses: actions/upload-artifact@v3
+ with:
+ name: template_linux_x86_64_full
+ path: templates/linux/x86_64/full/Template
+ build-runtime-windows:
+ runs-on: windows-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Install ASPL
+ run: |
+ cd $GITHUB_WORKSPACE
+ .\install.bat
+ shell: cmd
+ - name: Build ASPL runtime templates
+ run: |
+ cd $GITHUB_WORKSPACE
+ aspl -os windows -cc gcc build-minimal-template
+ mv -f Template.exe templates/windows/x86_64/minimal
+ aspl -os windows -cc gcc build-full-template
+ mv -f Template.exe templates/windows/x86_64/full/cli
+ shell: cmd
+ - name: Upload template artifact (Windows x86_64 minimal)
+ uses: actions/upload-artifact@v3
+ with:
+ name: template_windows_x86_64_minimal
+ path: templates/windows/x86_64/minimal/Template.exe
+ - name: Upload template artifact (Windows x86_64 full)
+ uses: actions/upload-artifact@v3
+ with:
+ name: template_windows_x86_64_full
+ path: templates/windows/x86_64/full/cli/Template.exe
+ build-runtime-macos:
+ runs-on: macos-13
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Install ASPL
+ run: |
+ cd $GITHUB_WORKSPACE
+ ./install_ci.sh
+ - name: Install runtime dependencies
+ run: |
+ brew install glfw # for the graphics module
+ - name: Build ASPL runtime templates
+ run: |
+ cd $GITHUB_WORKSPACE
+ aspl -os macos -cc gcc build-minimal-template
+ mv -f Template templates/macos/x86_64/minimal
+ aspl -os macos -cc gcc build-full-template
+ mv -f Template templates/macos/x86_64/full
+ - name: Upload template artifact (macOS x86_64 minimal)
+ uses: actions/upload-artifact@v3
+ with:
+ name: template_macos_x86_64_minimal
+ path: templates/macos/x86_64/minimal/Template
+ - name: Upload template artifact (macOS x86_64 full)
+ uses: actions/upload-artifact@v3
+ with:
+ name: template_macos_x86_64_full
+ path: templates/macos/x86_64/full/Template
+ build-compiler:
+ runs-on: ubuntu-latest
+ container:
+ image: debian:buster # use an older Debian version to support older glibc versions
+ needs: [build-runtime-linux, build-runtime-windows, build-runtime-macos]
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Install ASPL installer dependencies
+ run: |
+ apt-get update
+ apt-get install --quiet -y git jq curl unzip xz-utils build-essential
+ - name: Install ASPL
+ run: |
+ cd $GITHUB_WORKSPACE
+ ./install_ci.sh
+ # - name: Delete template (macOS x86_64)
+ # run: |
+ # rm templates/macos/x86_64/Template
+ - name: Download template artifact (macOS x86_64 full)
+ uses: actions/download-artifact@v3
+ with:
+ name: template_macos_x86_64_full
+ path: templates/macos/x86_64/full
+ - name: Setup Zig
+ uses: goto-bus-stop/setup-zig@v2
+ - name: Build ASPL compiler
+ run: |
+ cd $GITHUB_WORKSPACE
+ cd cli
+ ../aspl -prod -os linux -backend c -heapBased -useDynamicCTemplate -cc gcc -showcc compile . # use GCC as it tends to optimize better
+ mv cli ../aspl_linux_x86_64
+ # ../aspl -prod -os linux -arch arm32 -backend c -heapBased -useDynamicCTemplate compile .
+ # mv cli ../aspl_linux_arm32
+ ../aspl -prod -os windows -backend c -heapBased -useDynamicCTemplate -showcc compile .
+ mv cli.exe ../aspl_windows_x86_64.exe
+ ../aspl -prod -os macos -backend ail compile .
+ mv cli ../aspl_macos_x86_64
+ # TODO: Build for more platforms
+ - name: Upload ASPL compiler artifact (Linux x86_64)
+ uses: actions/upload-artifact@v3
+ with:
+ name: aspl_linux_x86_64
+ path: aspl_linux_x86_64
+ - name: Upload ASPL compiler artifact (Windows x86_64)
+ uses: actions/upload-artifact@v3
+ with:
+ name: aspl_windows_x86_64
+ path: aspl_windows_x86_64.exe
+ - name: Upload ASPL compiler artifact (macOS x86_64)
+ uses: actions/upload-artifact@v3
+ with:
+ name: aspl_macos_x86_64
+ path: aspl_macos_x86_64
+ publish:
+ runs-on: ubuntu-latest
+ needs: [build-runtime-linux, build-runtime-windows, build-runtime-macos, build-compiler]
+ steps:
+ - name: Delete template (Linux x86_64 minimal)
+ run: |
+ rm -rf templates/linux/x86_64/minimal/Template
+ - name: Download template artifact (Linux x86_64 minimal)
+ uses: actions/download-artifact@v3
+ with:
+ name: template_linux_x86_64_minimal
+ path: templates/linux/x86_64/minimal
+ - name: Delete template artifact (Linux x86_64 minimal)
+ uses: geekyeggo/delete-artifact@v2
+ with:
+ name: template_linux_x86_64_minimal
+ - name: Delete template (Linux x86_64 full)
+ run: |
+ rm -rf templates/linux/x86_64/full/Template
+ - name: Download template artifact (Linux x86_64 full)
+ uses: actions/download-artifact@v3
+ with:
+ name: template_linux_x86_64_full
+ path: templates/linux/x86_64/full
+ - name: Delete template artifact (Linux x86_64 full)
+ uses: geekyeggo/delete-artifact@v2
+ with:
+ name: template_linux_x86_64_full
+ - name: Delete template (Windows x86_64 minimal)
+ run: |
+ rm -rf templates/windows/x86_64/minimal/Template.exe
+ - name: Download template artifact (Windows x86_64 minimal)
+ uses: actions/download-artifact@v3
+ with:
+ name: template_windows_x86_64_minimal
+ path: templates/windows/x86_64/minimal
+ - name: Delete template artifact (Windows x86_64 minimal)
+ uses: geekyeggo/delete-artifact@v2
+ with:
+ name: template_windows_x86_64_minimal
+ - name: Delete template (Windows x86_64 full)
+ run: |
+ rm -rf templates/windows/x86_64/full/cli/Template.exe
+ - name: Download template artifact (Windows x86_64 full)
+ uses: actions/download-artifact@v3
+ with:
+ name: template_windows_x86_64_full
+ path: templates/windows/x86_64/full/cli
+ - name: Delete template artifact (Windows x86_64 full)
+ uses: geekyeggo/delete-artifact@v2
+ with:
+ name: template_windows_x86_64_full
+ - name: Delete template (macOS x86_64 minimal)
+ run: |
+ rm -rf templates/macos/x86_64/minimal/Template
+ - name: Download template artifact (macOS x86_64 minimal)
+ uses: actions/download-artifact@v3
+ with:
+ name: template_macos_x86_64_minimal
+ path: templates/macos/x86_64/minimal
+ - name: Delete template artifact (macOS x86_64 minimal)
+ uses: geekyeggo/delete-artifact@v2
+ with:
+ name: template_macos_x86_64_minimal
+ - name: Delete template (macOS x86_64 full)
+ run: |
+ rm -rf templates/macos/x86_64/full/Template
+ - name: Download template artifact (macOS x86_64 full)
+ uses: actions/download-artifact@v3
+ with:
+ name: template_macos_x86_64_full
+ path: templates/macos/x86_64/full
+ - name: Delete template artifact (macOS x86_64 full)
+ uses: geekyeggo/delete-artifact@v2
+ with:
+ name: template_macos_x86_64_full
+ - name: Download ASPL compiler artifact (Linux x86_64)
+ uses: actions/download-artifact@v3
+ with:
+ name: aspl_linux_x86_64
+ path: ./
+ - name: Delete ASPL compiler artifact (Linux x86_64)
+ uses: geekyeggo/delete-artifact@v2
+ with:
+ name: aspl_linux_x86_64
+ - name: Download ASPL compiler artifact (Windows x86_64)
+ uses: actions/download-artifact@v3
+ with:
+ name: aspl_windows_x86_64
+ path: ./
+ - name: Delete ASPL compiler artifact (Windows x86_64)
+ uses: geekyeggo/delete-artifact@v2
+ with:
+ name: aspl_windows_x86_64
+ - name: Download ASPL compiler artifact (macOS x86_64)
+ uses: actions/download-artifact@v3
+ with:
+ name: aspl_macos_x86_64
+ path: ./
+ - name: Delete ASPL compiler artifact (macOS x86_64)
+ uses: geekyeggo/delete-artifact@v2
+ with:
+ name: aspl_macos_x86_64
+ - name: Zip templates
+ run: |
+ cd $GITHUB_WORKSPACE
+ zip -r templates.zip templates -x '.gitignore' -x '*.md'
+ - name: Release
+ uses: softprops/action-gh-release@v1
+ with:
+ repository: aspl-lang/cd
+ tag_name: SHA-${{ github.sha }}
+ token: ${{ secrets.CDKEY }}
+ files: |
+ ${{ github.workspace }}/aspl_linux_x86_64
+ ${{ github.workspace }}/aspl_windows_x86_64.exe
+ ${{ github.workspace }}/aspl_macos_x86_64
+ ${{ github.workspace }}/templates.zip
+ # ${{ github.workspace }}/aspl_linux_arm32
+ - name: Update metadata file
+ run: |
+ curl \
+ -X PUT\
+ -H "Authorization: token ${{ secrets.CDKEY }}"\
+ -d '{"message": "Update latest.txt", "content": "'$(echo ${{ github.sha }} | base64)'", "sha": "'$(curl -s -H "Authorization: token ${{ secrets.CDKEY }}" https://api.github.com/repos/aspl-lang/cd/contents/latest.txt | jq -r .sha)'", "committer": {"name": "github-actions[bot]", "email": "github-actions[bot]@users.noreply.github.com"}}'\
+ https://api.github.com/repos/aspl-lang/cd/contents/latest.txt
+ ci:
+ runs-on: ubuntu-latest
+ needs: [publish]
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Install ASPL
+ run: |
+ $GITHUB_WORKSPACE/install_ci.sh
+ - name: Run tests
+ run: |
+ cd $GITHUB_WORKSPACE
+ echo "Testing the C backend..."
+ ./aspl -backend c -cc gcc -showcc test-all || exit $?
+ echo "Testing the AIL backend..."
+ ./aspl -backend ail -cc gcc -showcc test-all || exit $?
\ No newline at end of file
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 0000000..ccb7503
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,46 @@
+name: Docs
+
+on:
+ - push
+ - workflow_dispatch
+
+jobs:
+ generate-and-publish-docs:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Install ASPL
+ run: |
+ cd $GITHUB_WORKSPACE
+ ./install_ci.sh
+ - name: Generate docs
+ run: |
+ cd $GITHUB_WORKSPACE
+ mkdir docs
+ cd stdlib
+ FOLDERS=""
+ for d in * ; do
+ ../aspl -o ../docs/$d document $d
+ FOLDERS+="* [$d]($d.md)"$'\n'
+ done
+ echo "$FOLDERS" > ../docs/overview.md
+ - uses: actions/checkout@v3
+ with:
+ repository: aspl-lang/docs
+ path: docs_repo
+ token: ${{ secrets.CDKEY }}
+ - name: Copy docs
+ run: |
+ cd $GITHUB_WORKSPACE
+ cp -r docs/* docs_repo/
+ sed -i '/## Modules/,$ d' docs_repo/README.md && echo "## Modules" >> docs_repo/README.md && cat docs_repo/overview.md >> docs_repo/README.md
+ rm docs_repo/overview.md
+ - name: Commit and push
+ run: |
+ cd $GITHUB_WORKSPACE/docs_repo
+ git config --local user.email "github-actions[bot]@users.noreply.github.com"
+ git config --local user.name "github-actions[bot]"
+ git add .
+ git diff-index --quiet HEAD || git commit -m "Update docs"
+ git push
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3c851a8
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,13 @@
+*
+!*.*
+!*/
+!LICENSE
+*.exe
+*.exe.lib
+*.dll
+*.pdb
+/*.c
+*.def
+*.apk
+*.ail
+*.log
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..cf6ac09
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,16 @@
+# Contributing to ASPL
+## Creating something awesome
+One of the best ways to support ASPL is by creating a project in it. You may port an already exisiting tool, develop something innovative and new or even rewrite your own applications in ASPL.
+
+Only through such projects, we can find out how usable ASPL really is in different fields of computer science, catch and patch bugs, and implement new features. It also just massively helps in making ASPL more know and popular.
+
+## Actually contributing to the implementation
+You can of course also directly contribute to ASPLs sourcecode (and the stuff around it) here on Github.
+ The best way to get started is probably just looking around at the files and folders; here's a quick overview on how the code is structured:
+
+### Compiler and standard library
+The compiler and all the stuff around it is written in ASPL and part of the ASPL standard library, which is located in the `stdlib` folder.
+ Please read the REAMDE of the `stdlib/aspl` folder for more information.
+
+### Runtime and AIL interpreter
+The runtime and the AIL interpreter are written in C; they are currently located at `stdlib/aspl/compiler/backend/stringcode/c/template.c` and `runtime/ailinterpreter` respectively.
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..4ccd037
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) The ASPL contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e84e388
--- /dev/null
+++ b/README.md
@@ -0,0 +1,83 @@
+
+
+
The ASPL Programming Language
+
+
+
+[![CI/CD Test Suite](https://github.com/aspl-lang/aspl/actions/workflows/ci_cd.yml/badge.svg)](https://github.com/aspl-lang/aspl/actions/workflows/ci_cd.yml)
+![Commit activity](https://img.shields.io/github/commit-activity/m/aspl-lang/aspl)
+[![Discord](https://img.shields.io/discord/1053681798430859264?label=Discord&logo=discord&logoColor=white)](https://discord.gg/UUNzAFrKU2)
+
+
+ASPL is a modern programming language featuring beautiful simplicity, seamless cross-platform support, practical OOP and so much more.
+
+## Why ASPL?
+* Simple 😀
+ The syntax is clean and readable, error messages provide useful information, and installing ASPL is as easy as a few clicks.
+
+* Safe 🔒
+ High-level abstractions, automatic memory management, out-of-the-box TLS support, and a strong type system make ASPL one of the safest languages out there.
+
+* Cross platform 💻
+ Newcomers as well as experienced programmers can easily create powerful cross-platform applications with ASPL; seamless cross-compilation is one of ASPL's core design principles.
+
+* Huge standard library 🔥
+ Builtin JSON, graphics, and advanced networking support are just a few of the things that will drastically speed up your coding process.
+
+* Modular architecture 📚
+ ASPL has a great and simple modular library system that helps you reuse any kind of third-party code in your projects, including even the ASPL compiler itself.
+
+> [!IMPORTANT]
+> ASPL is still in early development and some features are missing or incomplete. Even though the language is already quite stable and very pratical, it is not yet recommended for production use. Contributions are always welcome!
+
+# Installing
+Have a look at the installation guide .
+
+# Introduction
+An introduction into ASPL can be found here .
+ It also documents most of the language and has a lot of examples.
+
+# Bugs and Suggestions
+ASPL is currently in early development and still has some bugs and missing features.
+ Please report bugs or suggest features by opening an issue or posting them in the ASPL discord server .
+
+# Star History
+[![Star History Chart](https://api.star-history.com/svg?repos=aspl-lang/aspl&type=Date)](https://star-history.com/#aspl-lang/aspl)
+
+# Code Examples
+-> means the program prints text to the console
+ <- means the user types something into the console
+## Hello World
+```aspl
+print("Hello World!") // print means "write into the console"
+```
+**Output:**
+ -> Hello World!
+
+## Favorite food
+```aspl
+var favoriteFood = input("What's your favorite food?") // prints "What's your favorite food" and waits until the user types something into the console
+if(favoriteFood == "Spaghetti"){ // checks whether the users input matches a certain string, here: "Spaghetti"; if it doesn't, the code between the braces will be skipped
+ print("Hm, yummy, that's my favorite food too!")
+}else{ // the following code is executed only if the condition in the if statement evaluated to false, here: the input was not "Spaghetti"
+ print("Sounds great!")
+}
+```
+**Example Output:**
+ -> What's your favorite food?
+ <- Spaghetti
+ -> Hm, yummy, that's my favorite food too!
+
+## Random number
+```aspl
+import rand
+
+print(rand.irange(1, 100)) // prints a random number between 1 and 100 into the console
+```
+**Example Output:**
+ -> 42
+
+> [!NOTE]
+> **More examples can be found here or in the ASPL introduction .**
+
+
\ No newline at end of file
diff --git a/cli/main.aspl b/cli/main.aspl
new file mode 100644
index 0000000..9fe4892
--- /dev/null
+++ b/cli/main.aspl
@@ -0,0 +1,572 @@
+import os
+import console
+import aspl.parser
+import aspl.compiler
+import aspl.compiler.utils
+import io
+import aspl.parser.ast
+import aspl.parser.ast.statements
+import aspl.parser.functions
+import aspl.parser.properties
+import aspl.parser.methods
+import aspl.parser.utils
+import cli.utils
+
+if(os.args().length < 2){
+ display_help()
+ exit(2)
+}
+
+var i = 1 // starting at 1 because 0 is the executable
+if(os.args()[1].startsWith("-")){
+ while(i < os.args().length && os.args()[i].startsWith("-")){
+ var option = os.args()[i]
+ if(option == "-define" || option == "-d"){
+ i++
+ if(i >= os.args().length){
+ print(console.red("aspl: `-define` option requires an argument"))
+ exit(2)
+ }
+ aspl.parser.Options:customConditionalCompilationSymbols.add(os.args()[i])
+ }elseif(option == "-keeptemp"){
+ Options:keepTemporary = true
+ }elseif(option == "-os"){
+ i++
+ if(i >= os.args().length){
+ print(console.red("aspl: `-os` option requires an argument"))
+ exit(2)
+ }
+ Options:targetOs = os.args()[i]
+ }elseif(option == "-architecture" || option == "-arch"){
+ i++
+ if(i >= os.args().length){
+ print(console.red("aspl: `-architecture` option requires an argument"))
+ exit(2)
+ }
+ if(os.architecture_from_string(os.args()[i]) == null){
+ print(console.red("aspl: unknown architecture: " + os.args()[i]))
+ exit(2)
+ }
+ Options:targetArchitecture = Architecture(os.architecture_from_string(os.args()[i]))
+ }elseif(option == "-output" || option == "-o"){
+ i++
+ if(i >= os.args().length){
+ print(console.red("aspl: `-output` option requires an argument"))
+ exit(2)
+ }
+ Options:outputFile = os.args()[i]
+ }elseif(option == "-production" || option == "-prod" || option == "-release"){
+ Options:production = true
+ }elseif(option == "-gui"){
+ Options:guiApp = true
+ }elseif(option == "-backend"){
+ i++
+ if(i >= os.args().length){
+ print(console.red("aspl: `-backend` option requires an argument"))
+ exit(2)
+ }
+ Options:backend = os.args()[i]
+ if(!["ail", "c"].contains(Options:backend)){
+ if(Options:backend == "twail"){
+ aspl.parser.utils.notice("The `twail` backend is deprecated and should only be used if it is actually needed.")
+ }else{
+ print(console.red("aspl: unknown backend `" + Options:backend + "`"))
+ exit(2)
+ }
+ }
+ }elseif(option == "-cc"){
+ i++
+ if(i >= os.args().length){
+ print(console.red("aspl: `-cc` option requires an argument"))
+ exit(2)
+ }
+ // TODO: Check if the compiler exists
+ Options:cCompiler = os.args()[i]
+ if(Options:cCompiler == "zigcc"){
+ Options:cCompiler = "zig cc"
+ }
+ }elseif(option == "-useDynamicCTemplate"){
+ Options:useDynamicCTemplate = true
+ }elseif(option == "-showcc"){
+ Options:showCCommand = true
+ }elseif(option == "-heapBased"){
+ Options:heapBased = true
+ }elseif(option == "-stacksize" || option == "-stack"){
+ i++
+ if(i >= os.args().length){
+ print(console.red("aspl: `-stacksize` option requires an argument"))
+ exit(2)
+ }
+ Options:stackSize = int(os.args()[i])
+ }elseif(option == "-ssl"){
+ Options:useSsl = true
+ }elseif(option == "-enableErrorHandling"){
+ Options:enableErrorHandling = true
+ }elseif(option == "-noCachedTemplate"){
+ Options:noCachedTemplate = true
+ }else{
+ print(console.red("aspl: unknown option `" + option + "`"))
+ exit(2)
+ }
+ i++
+ }
+ if(Options:useSsl){
+ if(Options:backend != "c"){
+ print(console.red("aspl: `-ssl` option can only be used with the C backend"))
+ exit(2)
+ }
+ if(Options:targetOs == "windows"){
+ print(console.red("aspl: `-ssl` option is not supported (and also not required) for Windows"))
+ exit(2)
+ }
+ if(Options:targetOs == "macos"){
+ print(console.red("aspl: `-ssl` option is not yet supported for macOS"))
+ exit(2)
+ }
+ }
+}
+
+if(os.args().length < i + 1){
+ display_help()
+ exit(2)
+}
+
+var subcommand = os.args()[i]
+if(subcommand == "compile" || subcommand == "build"){
+ i++
+ if(os.args().length < i + 1){
+ print(console.red("aspl compile: missing source file or directory"))
+ exit(2)
+ }
+ aspl.compiler.compile(os.args()[i])
+}elseif(subcommand == "run"){
+ i++
+ if(os.args().length < i + 1){
+ print(console.red("aspl run: missing source file or directory"))
+ exit(2)
+ }
+ Options:outputFile = ".aspl_run" // TODO: Use a random name
+ var outputFile = Options:outputFile
+ if(Options:targetOs == "windows"){
+ outputFile += ".exe"
+ }
+ outputFile = io.abs(outputFile)
+ aspl.compiler.compile(os.args()[i])
+ print(os.execute(outputFile).output, false)
+ io.delete_file(outputFile)
+}elseif(subcommand == "test-all"){
+ test_all()
+}elseif(subcommand == "format"){
+ print(console.yellow("Coming soon..."))
+}elseif(subcommand == "document" || subcommand == "doc"){
+ i++
+ if(os.args().length < i + 1){
+ print(console.red("aspl document: missing source file or directory"))
+ exit(2)
+ }
+ var main = io.abs(os.args()[i])
+ var string mainDirectory = ""
+ if(io.exists_file(main)){
+ mainDirectory = io.full_directory_path(main)
+ }elseif(io.exists_directory(main)){
+ mainDirectory = main
+ }else{
+ aspl.parser.utils.fatal_error("Main is neither a valid file nor a valid directory: " + main)
+ }
+ Module:init(new Module(io.directory_name(mainDirectory), mainDirectory))
+ var result = aspl.parser.parse()
+ var outputMode = "markdown" // TODO: Add option to change this
+ var string output = ""
+ if(outputMode == "markdown"){
+ output += "# " + Module:mainModule.name + "\n"
+ }
+ foreach(result.nodes as node){
+ if(node oftype ClassDeclareStatement){
+ if(ClassDeclareStatement(node).c.type.toString().toLower().startsWith(Module:mainModule.id) && ClassDeclareStatement(node).c.isPublic){
+ if(outputMode == "markdown"){
+ output += "## class " + ClassDeclareStatement(node).c.type.toString() + "\n"
+ output += "Source: " + Location(ClassDeclareStatement(node).c.location).file + ":" + Location(ClassDeclareStatement(node).c.location).startLine + ":" + Location(ClassDeclareStatement(node).c.location).startColumn + "\n"
+
+ var commentOutput = ""
+ foreach(ClassDeclareStatement(node).comments as comment){
+ if(comment.value.startsWith("// " + ClassDeclareStatement(node).c.type.toString())){
+ commentOutput += "> " + comment.value.after("// ".length - 1) + "\n"
+ }
+ }
+ if(commentOutput != ""){
+ output += "\n"
+ output += commentOutput
+ output += "\n"
+ }
+
+ foreach(list(ClassDeclareStatement(node).c.code) as statement){
+ if(statement oftype PropertyDeclareStatement && PropertyDeclareStatement(statement).p.isPublic){
+ output += "### property " + PropertyDeclareStatement(statement).p.name + "\n"
+ if(PropertyDeclareStatement(statement).p oftype CustomNormalProperty){
+ output += "Source: " + Location(CustomNormalProperty(PropertyDeclareStatement(statement).p).location).file + ":" + Location(CustomNormalProperty(PropertyDeclareStatement(statement).p).location).startLine + ":" + Location(CustomNormalProperty(PropertyDeclareStatement(statement).p).location).startColumn + "\n"
+ }elseif(PropertyDeclareStatement(statement).p oftype CustomReactiveProperty){
+ output += "Source: " + Location(CustomReactiveProperty(PropertyDeclareStatement(statement).p).location).file + ":" + Location(CustomReactiveProperty(PropertyDeclareStatement(statement).p).location).startLine + ":" + Location(CustomReactiveProperty(PropertyDeclareStatement(statement).p).location).startColumn + "\n"
+ }
+
+ var propertyCommentOutput = ""
+ foreach(PropertyDeclareStatement(statement).comments as comment){
+ if(comment.value.startsWith("// " + PropertyDeclareStatement(statement).p.name)){
+ propertyCommentOutput += "> " + comment.value.after("// ".length - 1) + "\n"
+ }
+ }
+ if(propertyCommentOutput != ""){
+ output += "\n"
+ output += propertyCommentOutput
+ output += "\n"
+ }
+
+ output += "```aspl\n"
+ if(PropertyDeclareStatement(statement).p oftype CustomNormalProperty){
+ var lines = io.read_file(Location(CustomNormalProperty(PropertyDeclareStatement(statement).p).location).file).replace("\r\n", "\n").replace("\r", "\n").split("\n")
+ var line = Location(CustomNormalProperty(PropertyDeclareStatement(statement).p).location).startLine
+ var code = ""
+ while(line <= Location(CustomNormalProperty(PropertyDeclareStatement(statement).p).headerEndLocation).endLine){
+ var l = lines[line - 1]
+ if(line == Location(CustomNormalProperty(PropertyDeclareStatement(statement).p).headerEndLocation).endLine){
+ l = l.before(Location(CustomNormalProperty(PropertyDeclareStatement(statement).p).headerEndLocation).endColumn - 2)
+ }
+ // The start cutting has to be done after the end cutting because the start cutting can change the column indices
+ if(line == Location(CustomNormalProperty(PropertyDeclareStatement(statement).p).location).startLine){
+ l = l.after(Location(CustomNormalProperty(PropertyDeclareStatement(statement).p).location).startColumn - 2)
+ }
+ code += l + "\n"
+ line++
+ }
+ output += code.trim()
+ }elseif(PropertyDeclareStatement(statement).p oftype CustomReactiveProperty){
+ var lines = io.read_file(Location(CustomReactiveProperty(PropertyDeclareStatement(statement).p).location).file).replace("\r\n", "\n").replace("\r", "\n").split("\n")
+ var line = Location(CustomReactiveProperty(PropertyDeclareStatement(statement).p).location).startLine
+ var code = ""
+ while(line <= Location(CustomReactiveProperty(PropertyDeclareStatement(statement).p).headerEndLocation).endLine){
+ var l = lines[line - 1]
+ if(line == Location(CustomReactiveProperty(PropertyDeclareStatement(statement).p).headerEndLocation).endLine){
+ l = l.before(Location(CustomReactiveProperty(PropertyDeclareStatement(statement).p).headerEndLocation).endColumn - 2)
+ }
+ // The start cutting has to be done after the end cutting because the start cutting can change the column indices
+ if(line == Location(CustomReactiveProperty(PropertyDeclareStatement(statement).p).location).startLine){
+ l = l.after(Location(CustomReactiveProperty(PropertyDeclareStatement(statement).p).location).startColumn - 2)
+ }
+ code += l + "\n"
+ line++
+ }
+ output += code.trim()
+ }
+ output += "\n```\n"
+ }
+ }
+ foreach(list(ClassDeclareStatement(node).c.code) as statement){
+ if(statement oftype MethodDeclareStatement && MethodDeclareStatement(statement).m.isPublic){
+ if(CustomMethod(MethodDeclareStatement(statement).m).location == null){
+ continue // Automatically generated constructors should not be documented
+ }
+ output += "### method " + MethodDeclareStatement(statement).m.name + "\n"
+ output += "Source: " + Location(CustomMethod(MethodDeclareStatement(statement).m).location).file + ":" + Location(CustomMethod(MethodDeclareStatement(statement).m).location).startLine + ":" + Location(CustomMethod(MethodDeclareStatement(statement).m).location).startColumn + "\n"
+
+ var methodCommentOutput = ""
+ foreach(MethodDeclareStatement(statement).comments as comment){
+ if(comment.value.startsWith("// " + MethodDeclareStatement(statement).m.name)){
+ methodCommentOutput += "> " + comment.value.after("// ".length - 1) + "\n"
+ }
+ }
+ if(methodCommentOutput != ""){
+ output += "\n"
+ output += methodCommentOutput
+ output += "\n"
+ }
+
+ output += "```aspl\n"
+ var lines = io.read_file(Location(CustomMethod(MethodDeclareStatement(statement).m).location).file).replace("\r\n", "\n").replace("\r", "\n").split("\n")
+ var line = Location(CustomMethod(MethodDeclareStatement(statement).m).location).startLine
+ var code = ""
+ while(line <= Location(CustomMethod(MethodDeclareStatement(statement).m).headerEndLocation).endLine){
+ var l = lines[line - 1]
+ if(line == Location(CustomMethod(MethodDeclareStatement(statement).m).headerEndLocation).endLine){
+ l = l.before(Location(CustomMethod(MethodDeclareStatement(statement).m).headerEndLocation).endColumn - 2)
+ }
+ // The start cutting has to be done after the end cutting because the start cutting can change the column indices
+ if(line == Location(CustomMethod(MethodDeclareStatement(statement).m).location).startLine){
+ l = l.after(Location(CustomMethod(MethodDeclareStatement(statement).m).location).startColumn - 2)
+ }
+ code += l + "\n"
+ line++
+ }
+ output += code.trim()
+ output += "\n```\n"
+ }
+ }
+
+ output += "\n"
+ }
+ }
+ }
+ }
+ foreach(result.nodes as node){
+ if(node oftype EnumDeclareStatement){
+ if(EnumDeclareStatement(node).e.type.toString().toLower().startsWith(Module:mainModule.id) && EnumDeclareStatement(node).e.isPublic){
+ if(outputMode == "markdown"){
+ output += "## enum " + EnumDeclareStatement(node).e.type.toString() + "\n"
+ output += "Source: " + Location(EnumDeclareStatement(node).e.location).file + ":" + Location(EnumDeclareStatement(node).e.location).startLine + ":" + Location(EnumDeclareStatement(node).e.location).startColumn + "\n"
+
+ output += "```aspl\n"
+ var lines = io.read_file(Location(EnumDeclareStatement(node).e.location).file).replace("\r\n", "\n").replace("\r", "\n").split("\n")
+ var line = Location(EnumDeclareStatement(node).e.location).startLine
+ var code = ""
+ while(line <= Location(EnumDeclareStatement(node).e.location).endLine){
+ var l = lines[line - 1]
+ if(line == Location(EnumDeclareStatement(node).e.location).endLine){
+ l = l.before(Location(EnumDeclareStatement(node).e.location).endColumn - 1)
+ }
+ // The start cutting has to be done after the end cutting because the start cutting can change the column indices
+ if(line == Location(EnumDeclareStatement(node).e.location).startLine){
+ l = l.after(Location(EnumDeclareStatement(node).e.location).startColumn - 2)
+ }
+ code += l + "\n"
+ line++
+ }
+
+ output += "\n```\n"
+
+ var commentOutput = ""
+ foreach(EnumDeclareStatement(node).comments as comment){
+ if(comment.value.startsWith("// " + EnumDeclareStatement(node).e.type.toString())){
+ commentOutput += "> " + comment.value.after("// ".length - 1) + "\n"
+ }
+ }
+ if(commentOutput != ""){
+ output += "\n"
+ output += commentOutput
+ output += "\n"
+ }
+
+ output += "\n"
+ }
+ }
+ }
+ }
+ foreach(result.nodes as node){
+ if(node oftype FunctionDeclareStatement){
+ if(FunctionDeclareStatement(node).func.identifier.toLower().startsWith(Module:mainModule.id) && FunctionDeclareStatement(node).func.isPublic){
+ if(outputMode == "markdown"){
+ output += "## function " + FunctionDeclareStatement(node).func.identifier + "\n"
+ output += "Source: " + Location(CustomFunction(FunctionDeclareStatement(node).func).location).file + ":" + Location(CustomFunction(FunctionDeclareStatement(node).func).location).startLine + ":" + Location(CustomFunction(FunctionDeclareStatement(node).func).location).startColumn + "\n"
+
+ output += "```aspl\n"
+ var lines = io.read_file(Location(CustomFunction(FunctionDeclareStatement(node).func).location).file).replace("\r\n", "\n").replace("\r", "\n").split("\n")
+ var line = Location(CustomFunction(FunctionDeclareStatement(node).func).location).startLine
+ var code = ""
+ while(line <= Location(CustomFunction(FunctionDeclareStatement(node).func).headerEndLocation).endLine){
+ var l = lines[line - 1]
+ if(line == Location(CustomFunction(FunctionDeclareStatement(node).func).headerEndLocation).endLine){
+ l = l.before(Location(CustomFunction(FunctionDeclareStatement(node).func).headerEndLocation).endColumn - 2)
+ }
+ // The start cutting has to be done after the end cutting because the start cutting can change the column indices
+ if(line == Location(CustomFunction(FunctionDeclareStatement(node).func).location).startLine){
+ l = l.after(Location(CustomFunction(FunctionDeclareStatement(node).func).location).startColumn - 2)
+ }
+ code += l + "\n"
+ line++
+ }
+ output += code.trim()
+ output += "\n```\n"
+
+ var commentOutput = ""
+ foreach(FunctionDeclareStatement(node).comments as comment){
+ if(comment.value.startsWith("// " + FunctionDeclareStatement(node).func.identifier.split(".", 2)[1])){
+ commentOutput += "> " + comment.value.after("// ".length - 1) + "\n"
+ }
+ }
+ if(commentOutput != ""){
+ output += "\n"
+ output += commentOutput
+ output += "\n"
+ }
+
+ output += "\n"
+ }
+ }
+ }
+ }
+ output = output.trim()
+ if(outputMode == "markdown"){
+ var file = Options:outputFile
+ if(file == null){
+ file = Module:mainModule.id
+ }
+ io.write_file(file + ".md", output)
+ }
+}elseif(subcommand == "install"){
+ i++
+ if(os.args().length < i + 1){
+ print(console.red("aspl install: missing module URL or local path"))
+ exit(2)
+ }
+ var module = os.args()[i]
+ var moduleParts = module.replace("\\", "/").split("/")
+ var moduleName = moduleParts[moduleParts.length - 1]
+ if(io.exists_file(module) || io.exists_directory(module)){
+ io.create_symlink(io.abs(module), ModuleUtils:getModulePath(moduleName))
+ }else{
+ os.execute("git clone " + module + " " + ModuleUtils:getModulePath(moduleName))
+ }
+}elseif(subcommand == "update"){
+ i++
+ if(os.args().length < i + 1){
+ if(!io.exists_directory(io.join_path([io.full_directory_path(io.get_executable_path()), ".git"]))){
+ print(console.red("aspl update: not in a git repository"))
+ exit(1)
+ }
+ os.execute("cmd /C cd " + io.full_directory_path(io.get_executable_path()) + " && git pull")
+ cli.utils.download_templates()
+ cli.utils.download_executable()
+ }else{
+ var module = os.args()[i]
+ var moduleParts = module.split("/")
+ var moduleName = moduleParts[moduleParts.length - 1]
+ os.execute("cmd /C cd " + ModuleUtils:getModulePath(moduleName) + " && git pull")
+ }
+}elseif(subcommand == "internal-update-1"){
+ $if windows{
+ io.move(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl.exe"]), io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_not_updated.exe"]))
+ os.execvp(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_not_updated.exe"]), ["internal-update-2"])
+ }$else{
+ io.move(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl"]), io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_not_updated"]))
+ os.execvp(io.join_path(["./", io.full_directory_path(io.get_executable_path()), "aspl_not_updated"]), ["internal-update-2"])
+ }
+ exit(0)
+}elseif(subcommand == "internal-update-2"){
+ $if windows{
+ io.move(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_updated.exe"]), io.join_path([io.full_directory_path(io.get_executable_path()), "aspl.exe"]))
+ os.execvp(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl.exe"]), ["internal-update-3"])
+ }$else{
+ io.move(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_updated"]), io.join_path([io.full_directory_path(io.get_executable_path()), "aspl"]))
+ os.execvp(io.join_path(["./", io.full_directory_path(io.get_executable_path()), "aspl"]), ["internal-update-3"])
+ }
+ exit(0)
+}elseif(subcommand == "internal-update-3"){
+ $if windows{
+ io.delete_file(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_not_updated.exe"]))
+ }$else{
+ io.delete_file(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_not_updated"]))
+ }
+ print(console.green("aspl update: successfully updated ASPL to the latest version (" + cli.utils.get_current_version() + ")"))
+}elseif(subcommand == "version"){
+ print("aspl version: " + cli.utils.get_current_version() + " (Git SHA " + cli.utils.get_git_sha()+ ")")
+}elseif(subcommand == "build-minimal-template"){
+ io.create_directory("minimal_template")
+ io.write_file("minimal_template/main.aspl", "")
+ if(Options:targetOs != "windows"){
+ LinkUtils:libraries.add("dl")
+ }
+ Options:backend = "ail"
+ Options:outputFile = "Template"
+ Options:production = true
+ Options:noCachedTemplate = true
+ Options:internalTemplateType = "minimal"
+ Options:internalDoNotBundle = true
+ aspl.compiler.compile("minimal_template")
+ io.delete_file("minimal_template/main.aspl")
+ io.delete_directory("minimal_template")
+}elseif(subcommand == "build-full-template"){
+ io.create_directory("full_template")
+ io.write_file("full_template/main.aspl", "")
+ cli.utils.gather_implementation_calls()
+ if(Options:targetOs != "windows"){
+ LinkUtils:libraries.add("dl")
+ }
+ Options:backend = "ail"
+ Options:outputFile = "Template"
+ Options:production = true
+ Options:noCachedTemplate = true
+ Options:internalTemplateType = "full"
+ Options:internalDoNotBundle = true
+ aspl.compiler.compile("full_template")
+ io.delete_file("full_template/main.aspl")
+ io.delete_directory("full_template")
+}elseif(subcommand == "help"){
+ display_help()
+}else{
+ print(console.red("Unknown subcommand: " + subcommand))
+ display_help()
+ exit(2)
+}
+
+function display_help(){
+ print("Usage:\n\t$ aspl [options] [arguments]")
+ print("Available commands:")
+ print("\taspl compile Compile and build a source file or directory")
+ print("\taspl run Compile and directly run a source file or directory")
+ print("\taspl test-all Compile and run all tests")
+ print("\taspl format Format a source file or directory")
+ print("\taspl document Document a source file or directory")
+ print("\taspl install Install a module from the internet or a local directory")
+ print("\taspl update [source] Update a given module or ASPL itself")
+ print("\taspl help Display this help message")
+}
+
+function test_all(){
+ foreach(io.glob("tests/*") as test){
+ var testPath = test
+ if(!testPath.replace("\\", "/").startsWith("tests/")){ // TODO: glob() returns paths incosistently
+ testPath = io.join_path(["tests", test])
+ }
+ test = io.directory_name(testPath)
+ if(!io.exists_directory(testPath)){
+ print(console.red("Tests have to be directories; " + testPath + " is a file"))
+ exit(1)
+ }
+ if(test.endsWith("/")){
+ test = test.before(test.length - 1)
+ }
+ var files = io.glob(testPath + "/*")
+ var found = false
+ foreach(files as file){
+ if(file.endsWith(".aspl") || file.endsWith(".taspl")){
+ found = true
+ }
+ }
+ if(!found){
+ print(console.red("No tests found in " + testPath)) // TODO: Support nested test directories
+ exit(1)
+ }
+ os.chdir(testPath)
+ // Note: We're using separate threads here to prevent static properties from being shared between tests (they're marked as [threadlocal])
+ var compilationDone = false
+ callback(){
+ aspl.compiler.compile(".")
+ compilationDone = true
+ }.+()
+ while(!compilationDone){
+ time.millisleep(10)
+ }
+ if(run_test(test)){
+ print(console.gray("Test " + test + " passed..."))
+ }else{
+ print(console.red("Test " + test + " failed!"))
+ exit(1)
+ }
+ $if windows{
+ io.delete_file(test + ".exe")
+ if(io.exists_file(test + ".pdb")){
+ io.delete_file(test + ".pdb")
+ }
+ }$else{
+ io.delete_file(test)
+ }
+ os.chdir("..")
+ os.chdir("..")
+ }
+ print(console.green("All tests passed!"))
+}
+
+function run_test(string test) returns bool{
+ $if windows{
+ return os.system(test + ".exe") == 0
+ }$else{
+ return os.system("./" + test) == 0
+ }
+}
\ No newline at end of file
diff --git a/cli/utils/full_template.aspl b/cli/utils/full_template.aspl
new file mode 100644
index 0000000..10a4394
--- /dev/null
+++ b/cli/utils/full_template.aspl
@@ -0,0 +1,71 @@
+import aspl.parser
+import aspl.parser.utils
+import aspl.compiler.utils
+import io
+import strings
+
+function gather_implementation_calls(){
+ foreach(find_files_recursively("stdlib", "implementations.c") as file){ // TODO: Also allow other file names
+ var lines = io.read_file(file).split("\n")
+ var isImplementationFile = false
+ foreach(lines as line){
+ if(line.contains("ASPL_OBJECT_TYPE ASPL_IMPLEMENT_")){
+ isImplementationFile = true
+ var argc = count_string_occurrences(line, "ASPL_OBJECT_TYPE* ")
+ var implementationCallNameMatch = regex.match_string("ASPL_OBJECT_TYPE ASPL_IMPLEMENT_", line)?!
+ var implementationCallName = line.after(implementationCallNameMatch.end - 1).split("(")[0]
+ ImplementationCallUtils:usedImplementationCalls[implementationCallName] = argc
+ }
+ }
+ if(isImplementationFile){
+ IncludeUtils:include(file)
+ }
+ }
+ // The following libraries are required for the graphics module
+ if(Options:targetOs == "windows"){
+ LinkUtils:libraries.add("gdi32")
+ }elseif(Options:targetOs == "linux"){
+ LinkUtils:libraries.add("X11")
+ LinkUtils:libraries.add("Xcursor")
+ LinkUtils:libraries.add("Xi")
+ LinkUtils:libraries.add("GL")
+ LinkUtils:libraries.add("dl")
+ }elseif(Options:targetOs == "macos"){
+ LinkUtils:libraries.add("framework:Cocoa")
+ LinkUtils:libraries.add("framework:OpenGL")
+ }
+ // The following libraries are required for the internet module
+ if(Options:targetOs == "windows"){
+ LinkUtils:libraries.add("ws2_32")
+ }
+}
+
+function find_files_recursively(string directory, string name) returns list{
+ var allFiles = io.files(directory)
+ var list files = []
+ foreach(allFiles as file){
+ if(file.contains(name)){
+ files.add(io.join_path([directory, file]))
+ }
+ }
+ var dirs = io.directories(directory)
+ foreach(dirs as dir){
+ files.insertElements(files.length, find_files_recursively(io.join_path([directory, dir]), name))
+ }
+ return files
+}
+
+function count_string_occurrences(string haystack, string needle) returns int{
+ var count = 0
+ var index = 0
+ while(index < haystack.length){
+ var remainder = haystack.after(index - 1)
+ if(remainder.startsWith(needle)){
+ count++
+ index += needle.length
+ }else{
+ index++
+ }
+ }
+ return count
+}
\ No newline at end of file
diff --git a/cli/utils/update.aspl b/cli/utils/update.aspl
new file mode 100644
index 0000000..5278382
--- /dev/null
+++ b/cli/utils/update.aspl
@@ -0,0 +1,108 @@
+import internet
+import json
+import io
+import encoding.base64
+import zip
+import os
+
+function download_templates(){
+ var githubToken = fetch_github_token()
+ var asset = fetch_asset_id("templates.zip")
+ var response = internet.get("https://api.github.com/repos/aspl-lang/cd/releases/assets/" + asset, "", {"Authorization" => ["token " + githubToken], "Accept" => ["application/octet-stream"]})
+ io.write_file(io.join_path([io.full_directory_path(io.get_executable_path()), "templates.zip"]), response.text)
+ zip.unzip(io.join_path([io.full_directory_path(io.get_executable_path()), "templates.zip"]), ".")
+ io.delete_file(io.join_path([io.full_directory_path(io.get_executable_path()), "templates.zip"]))
+}
+
+// download_executable downloads the latest aspl cli executable
+// Warning: this function never returns and starts a new process instead
+function download_executable(){
+ var githubToken = fetch_github_token()
+ var assetName = "aspl"
+ $if windows{
+ assetName += "_windows"
+ $if amd64{
+ assetName += "_x86_64"
+ }
+ $if i386{
+ assetName += "_x86"
+ }
+ $if arm64{
+ assetName += "_arm64"
+ }
+ assetName += ".exe"
+ }
+ $if linux{
+ assetName += "_linux"
+ $if amd64{
+ assetName += "_x86_64"
+ }
+ $if i386{
+ assetName += "_x86"
+ }
+ $if arm32{
+ assetName += "_arm32"
+ }
+ $if arm64{
+ assetName += "_arm64"
+ }
+ }
+ $if macos{
+ assetName += "_macos"
+ $if amd64{
+ assetName += "_x86_64"
+ }
+ $if i386{
+ assetName += "_x86"
+ }
+ $if arm64{
+ assetName += "_arm64"
+ }
+ }
+ var asset = fetch_asset_id(assetName)
+ var response = internet.get("https://api.github.com/repos/aspl-lang/cd/releases/assets/" + asset, "", {"Authorization" => ["token " + githubToken], "Accept" => ["application/octet-stream"]})
+ $if windows{
+ io.write_file(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_updated.exe"]), response.text)
+ os.execvp(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_updated.exe"]), ["internal-update-1"])
+ }$else{
+ io.write_file(io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_updated"]), response.text)
+ os.execute("chmod +x " + io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_updated"]))
+ os.execvp("./" + io.join_path([io.full_directory_path(io.get_executable_path()), "aspl_updated"]), ["internal-update-1"])
+ }
+ exit(0)
+}
+
+function fetch_github_token() returns string{
+ if(io.exists_file("github.token")){
+ return io.read_file("github.token").trim()
+ }
+ print(console.red("aspl update: github token not found, please create a file named github.token in the current directory and put your github token in it"))
+ exit(1)
+}
+
+function fetch_asset_id(string name) returns string{
+ var githubToken = fetch_github_token()
+ var sha = ""
+ var asset = ""
+ {
+ var response = internet.get("https://api.github.com/repos/aspl-lang/cd/contents/latest.txt", "", {"Authorization" => ["token " + githubToken]})
+ var contents = map(json.decode(response.text))
+ var content = string(contents["content"]).trim()
+ if(content.endsWith("\\n")){
+ content = content.before(content.length - 2)
+ }
+ sha = encoding.base64.decode(content).trim()
+ }
+ {
+ var response = internet.get("https://api.github.com/repos/aspl-lang/cd/releases/tags/SHA-" + sha, "", {"Authorization" => ["token " + githubToken]})
+ var contents = map(json.decode(response.text))
+ var assets = list(contents["assets"])
+ foreach(assets as a){
+ if(map(a)["name"] == name){
+ asset = string(map(a)["id"])
+ break
+ }
+ }
+ }
+ return asset
+}
\ No newline at end of file
diff --git a/cli/utils/version.aspl b/cli/utils/version.aspl
new file mode 100644
index 0000000..2875d07
--- /dev/null
+++ b/cli/utils/version.aspl
@@ -0,0 +1,19 @@
+import os
+
+function get_git_sha() returns string{
+ var wd = os.getwd()
+ os.chdir(io.full_directory_path(io.get_executable_path()))
+ var sha = os.execute("git rev-parse --short HEAD").output.trim()
+ os.chdir(wd)
+ return sha
+}
+
+function get_current_version() returns string{
+ var string nextRelease = "0.1"
+ var bool isDevelopementBuild = false
+ if(isDevelopementBuild){
+ return "v" + nextRelease + "-" + get_git_sha()
+ }else{
+ return "v" + nextRelease
+ }
+}
\ No newline at end of file
diff --git a/examples/consonants_and_vowels/main.aspl b/examples/consonants_and_vowels/main.aspl
new file mode 100644
index 0000000..3afa148
--- /dev/null
+++ b/examples/consonants_and_vowels/main.aspl
@@ -0,0 +1,15 @@
+var string input = input("Enter a string: ")
+
+var int consonants = 0
+var int vowels = 0
+
+foreach(input as c){
+ if(["a", "e", "i", "o", "u"].contains(c)){
+ vowels++
+ }else{
+ consonants++
+ }
+}
+
+print("Consonants: " + consonants)
+print("Vowels: " + vowels)
\ No newline at end of file
diff --git a/examples/double_pendulum/Pendulum.aspl b/examples/double_pendulum/Pendulum.aspl
new file mode 100644
index 0000000..bf5a138
--- /dev/null
+++ b/examples/double_pendulum/Pendulum.aspl
@@ -0,0 +1,41 @@
+class Pendulum {
+
+ property float g = 9.81
+ property float dt = 0.05
+
+ [readpublic]
+ property double angle
+ [readpublic]
+ property double angVel = 0
+ property double angAcc = 0
+ property double length
+ property double mass
+
+ [public]
+ method construct(double angle, double length, double mass){
+ this.angle = angle
+ this.length = length
+ this.mass = mass
+ }
+
+ [public]
+ method update1(){
+ this.angAcc = (-g / this.length) * math.sin(this.angle)
+ this.angVel += this.angAcc * dt
+ this.angle += this.angVel * dt
+ }
+
+ [public]
+ method update2(double angle1, double angVel1, double length1, double mass1){
+ var num1 = -g * (2 * this.mass + mass1) * math.sin(this.angle)
+ var num2 = -this.mass * g * math.sin(this.angle - 2 * angle1)
+ var num3 = -2 * math.sin(this.angle - angle1) * this.mass
+ var num4 = angVel1 * angVel1 * length1 + this.angVel * this.angVel * this.length * math.cos(this.angle - angle1)
+ var den = length1 * (2 * this.mass + mass1 - this.mass * math.cos(2 * this.angle - 2 * angle1))
+
+ this.angAcc = (num1 + num2 + num3 * num4) / den
+ this.angVel += this.angAcc * dt
+ this.angle += this.angVel * dt
+ }
+
+}
\ No newline at end of file
diff --git a/examples/double_pendulum/main.aspl b/examples/double_pendulum/main.aspl
new file mode 100644
index 0000000..271204d
--- /dev/null
+++ b/examples/double_pendulum/main.aspl
@@ -0,0 +1,33 @@
+import graphics
+import math
+import math.geometry
+import rand
+
+var double length1 = 100
+var double length2 = 100
+var double mass1 = 8
+var double mass2 = 6
+
+var pendulum1 = new Pendulum(math.pi() / 2, length1, mass1)
+var pendulum2 = new Pendulum(math.pi() * rand.drange(0, 1), length2, mass2)
+var window = new Window("Double Pendulum Example", 500, 300)
+window.onPaint = callback(Canvas canvas){
+ length1 = canvas.height / 3d
+ length2 = canvas.height / 3d
+
+ canvas.fill(Color:fromRGB(220, 220, 220), false)
+ pendulum1.update1()
+ pendulum2.update2(pendulum1.angle, pendulum1.angVel, length1, mass1)
+
+ var x1 = int(length1 * math.sin(pendulum1.angle))
+ var y1 = int(length1 * math.cos(pendulum1.angle))
+ var x2 = int(x1 + length2 * math.sin(pendulum2.angle))
+ var y2 = int(y1 + length2 * math.cos(pendulum2.angle))
+
+ canvas.drawLine(canvas.width / 2, canvas.height / 6, canvas.width / 2 + x1, canvas.height / 6 + y1, Color:fromRGB(0, 0, 0))
+ canvas.drawLine(canvas.width / 2 + x1, canvas.height / 6 + y1, canvas.width / 2 + x2, canvas.height / 6 + y2, Color:fromRGB(0, 0, 0))
+ canvas.fillCircle(new Ellipse(new Point(canvas.width / 2f, canvas.height / 6f), new Size(float(10), float(10))), Color:fromRGB(0, 200, 0))
+ canvas.fillCircle(new Ellipse(new Point(canvas.width / 2f + x1, canvas.height / 6f + y1), new Size(float(mass1), float(mass1))), Color:fromRGB(200, 0, 0), false)
+ canvas.fillCircle(new Ellipse(new Point(canvas.width / 2f + x2, canvas.height / 6f + y2), new Size(float(mass2), float(mass2))), Color:fromRGB(0, 0, 200), false)
+}
+window.show()
\ No newline at end of file
diff --git a/examples/fahrenheit_to_celsius/main.aspl b/examples/fahrenheit_to_celsius/main.aspl
new file mode 100644
index 0000000..84210f0
--- /dev/null
+++ b/examples/fahrenheit_to_celsius/main.aspl
@@ -0,0 +1,9 @@
+import math
+
+function fahrenheit_to_celsius(float degrees) returns float{
+ return (degrees - 32) * (5f / 9)
+}
+
+var fahrenheit = float(math.round(double(float(input("Enter a temperature in °F: "))), 2))
+var celsius = math.round(double(fahrenheit_to_celsius(fahrenheit)), 2)
+print(fahrenheit + "°F ≙ " + celsius + "°C")
\ No newline at end of file
diff --git a/examples/fibonacci_numbers/main.aspl b/examples/fibonacci_numbers/main.aspl
new file mode 100644
index 0000000..0338c45
--- /dev/null
+++ b/examples/fibonacci_numbers/main.aspl
@@ -0,0 +1,14 @@
+var long a = 1
+var long b = 0
+var long n = 0
+
+while(true){
+ n = a + b
+ if(n < 0){
+ print("The program reached the maximum fibonacci number it can store.")
+ exit(0)
+ }
+ print(n)
+ b = a
+ a = n
+}
\ No newline at end of file
diff --git a/examples/hello_world/main.aspl b/examples/hello_world/main.aspl
new file mode 100644
index 0000000..1dc45ac
--- /dev/null
+++ b/examples/hello_world/main.aspl
@@ -0,0 +1 @@
+print("Hello World!")
\ No newline at end of file
diff --git a/examples/json_basics/main.aspl b/examples/json_basics/main.aspl
new file mode 100644
index 0000000..079dff9
--- /dev/null
+++ b/examples/json_basics/main.aspl
@@ -0,0 +1,7 @@
+import json
+
+var list l = list["Hello", "World", 1, 2, true]
+print(json.encode(l))
+
+var map m = map{"Hello" => "World", "x" => 1, "y" => 2, "b" => true}
+print(json.encode(m, true)) // true for pretty print
\ No newline at end of file
diff --git a/examples/julia_set/main.aspl b/examples/julia_set/main.aspl
new file mode 100644
index 0000000..4ee5295
--- /dev/null
+++ b/examples/julia_set/main.aspl
@@ -0,0 +1,33 @@
+// Original code taken and translated from: https://www.geeksforgeeks.org/julia-fractal-python/
+
+import graphics
+
+var int width = 500
+var int height = 500
+var float zoom = 1f
+
+var Canvas canvas = new Canvas(500, 500)
+canvas.fill(new Color(255b, 255b, 255b, 255b), false)
+
+var float cX = -0.7
+var float cY = 0.27015
+var float moveX = 0f
+var float moveY = 0f
+var int maxIterations = 255
+
+repeat(width, x = 0){
+ repeat(height, y = 0){
+ var zx = 1.5 * (x - width / 2f) / (0.5 * zoom * width) + moveX
+ var zy = 1.5 * (y - height / 2f) / (0.5 * zoom * height) + moveY
+ var int i = maxIterations
+ while((zx * zx + zy * zy) < 4 && i > 1){
+ var temp = zx * zx - zy * zy + cX
+ zy = 2 * zx * zy + cY
+ zx = temp
+ i--
+ }
+ canvas.setPixel(x, y, Color:fromRGB(byte(i * 4), byte(i * 4), byte(i * 4)))
+ }
+}
+
+canvas.save("julia_set.png")
\ No newline at end of file
diff --git a/examples/quicksort/main.aspl b/examples/quicksort/main.aspl
new file mode 100644
index 0000000..5b34062
--- /dev/null
+++ b/examples/quicksort/main.aspl
@@ -0,0 +1,45 @@
+function quicksort(list l) returns list{
+ if(l.length < 2){
+ return l
+ }
+
+ var float pivot = l[l.length / 2]
+
+ var list left = []
+ var list right = []
+ var list equal = []
+
+ foreach(l as element){
+ if(element < pivot){
+ left.add(element)
+ } elseif(element == pivot){
+ equal.add(element)
+ } else {
+ right.add(element)
+ }
+ }
+
+ left = quicksort(left)
+ right = quicksort(right)
+
+ var list result = []
+ foreach(left as element){
+ result.add(element)
+ }
+ foreach(equal as element){
+ result.add(element)
+ }
+ foreach(right as element){
+ result.add(element)
+ }
+
+ return result
+}
+
+var list l1 = input("Enter a list of numbers you want to sort: ").split(",")
+var list l2 = []
+foreach(l1 as element){
+ l2.add(float(element.trim()))
+}
+print("Unsorted: " + l2)
+print("Sorted: " + quicksort(l2))
\ No newline at end of file
diff --git a/install.bat b/install.bat
new file mode 100755
index 0000000..5a8e239
--- /dev/null
+++ b/install.bat
@@ -0,0 +1,48 @@
+@echo off
+
+FOR /F "tokens=*" %%g IN ('git config --get remote.origin.url') do (SET remote=%%g)
+echo %remote% | findstr /C:"github.com/aspl-lang/aspl" > nul && (
+ git "pull" "origin" "main"
+) || (
+ git "clone" "https://github.com/aspl-lang/aspl.git"
+ cd "aspl"
+)
+
+curl -L -o jq.exe https://github.com/stedolan/jq/releases/latest/download/jq-win64.exe
+
+(curl -s https://api.github.com/repos/aspl-lang/cd/contents/latest.txt | jq -r .content) > sha.txt
+certutil -decode sha.txt sha_decoded.txt
+SET /p SHA= id.txt
+SET /p ASSET_ID= id.txt
+SET /p ASSET_ID= **Note:** if you run the installer inside an already checked out ASPL repository, it will automatically update the repository to the latest version.
+
+> **Note:** you will need [`git` to be installed on your computer](https://github.com/git-guides/install-git) in order to use the ASPL installer.
+
+> **Note:** on Unix-like systems (Linux, macOS), you may need additionally to install `jq`, `curl` and `unzip` in order to run the installer script. On Debian-based systems, you can do this by running the following command:
+```sh
+sudo apt install jq curl unzip
+```
+
+After you have installed ASPL, you can check if everything is working by trying to compile and run the "Hello World" program:
+```sh
+$ aspl run "examples/hello_world"
+Hello World!
+```
+
+Congratulations! You have successfully installed ASPL!
+ Be sure to check out the [introduction](introduction.md) next to learn more about ASPL and how to use it.
\ No newline at end of file
diff --git a/install.sh b/install.sh
new file mode 100755
index 0000000..7f90a98
--- /dev/null
+++ b/install.sh
@@ -0,0 +1,40 @@
+if $(git config --get remote.origin.url | grep -q 'github.com/aspl-lang/aspl'); then
+ git pull origin main
+else
+ git clone https://github.com/aspl-lang/aspl.git
+ cd aspl
+fi
+SHA=$(curl -s https://api.github.com/repos/aspl-lang/cd/contents/latest.txt | jq -r '.content' | base64 -d)
+if [ "$(uname)" = "Darwin" ]; then
+ if [ "$(uname -m)" = "x86_64" ]; then
+ EXECUTABLE=aspl_macos_x86_64
+ elif [ "$(uname -m)" = "armv7l" ]; then
+ EXECUTABLE=aspl_macos_arm32
+ elif [ "$(uname -m)" = "aarch64" ]; then
+ EXECUTABLE=aspl_macos_arm64
+ else
+ echo "Unsupported architecture"
+ exit 1
+ fi
+elif [ "$(uname)" = "Linux" ]; then
+ if [ "$(uname -m)" = "x86_64" ]; then
+ EXECUTABLE=aspl_linux_x86_64
+ elif [ "$(uname -m)" = "armv7l" ]; then
+ EXECUTABLE=aspl_linux_arm32
+ elif [ "$(uname -m)" = "aarch64" ]; then
+ EXECUTABLE=aspl_linux_arm64
+ else
+ echo "Unsupported architecture"
+ exit 1
+ fi
+else
+ echo "Unsupported operating system"
+ exit 1
+fi
+CURL="curl https://api.github.com/repos/aspl-lang/cd/releases"; ASSET_ID=$(eval "$CURL/tags/SHA-$SHA" | jq .assets | jq '.[] | select(.name == "'$EXECUTABLE'").id'); eval "$CURL/assets/$ASSET_ID -LJOH 'Accept: application/octet-stream'"
+mv $EXECUTABLE aspl
+chmod +x aspl
+ln -s $PWD/aspl /usr/local/bin/aspl
+CURL="curl https://api.github.com/repos/aspl-lang/cd/releases"; ASSET_ID=$(eval "$CURL/tags/SHA-$SHA" | jq .assets | jq '.[] | select(.name == "templates.zip").id'); eval "$CURL/assets/$ASSET_ID -LJOH 'Accept: application/octet-stream'"
+unzip templates.zip
+rm templates.zip
\ No newline at end of file
diff --git a/install_ci.sh b/install_ci.sh
new file mode 100755
index 0000000..f06e1f7
--- /dev/null
+++ b/install_ci.sh
@@ -0,0 +1,34 @@
+SHA=$(curl -s https://api.github.com/repos/aspl-lang/cd/contents/latest.txt | jq -r '.content' | base64 -d)
+if [ "$(uname)" = "Darwin" ]; then
+ if [ "$(uname -m)" = "x86_64" ]; then
+ EXECUTABLE=aspl_macos_x86_64
+ elif [ "$(uname -m)" = "armv7l" ]; then
+ EXECUTABLE=aspl_macos_arm32
+ elif [ "$(uname -m)" = "aarch64" ]; then
+ EXECUTABLE=aspl_macos_arm64
+ else
+ echo "Unsupported architecture"
+ exit 1
+ fi
+elif [ "$(uname)" = "Linux" ]; then
+ if [ "$(uname -m)" = "x86_64" ]; then
+ EXECUTABLE=aspl_linux_x86_64
+ elif [ "$(uname -m)" = "armv7l" ]; then
+ EXECUTABLE=aspl_linux_arm32
+ elif [ "$(uname -m)" = "aarch64" ]; then
+ EXECUTABLE=aspl_linux_arm64
+ else
+ echo "Unsupported architecture"
+ exit 1
+ fi
+else
+ echo "Unsupported operating system"
+ exit 1
+fi
+CURL="curl https://api.github.com/repos/aspl-lang/cd/releases"; ASSET_ID=$(eval "$CURL/tags/SHA-$SHA" | jq .assets | jq '.[] | select(.name == "'$EXECUTABLE'").id'); eval "$CURL/assets/$ASSET_ID -LJOH 'Accept: application/octet-stream'"
+mv $EXECUTABLE aspl
+chmod +x aspl
+ln -s $PWD/aspl /usr/local/bin/aspl
+CURL="curl https://api.github.com/repos/aspl-lang/cd/releases"; ASSET_ID=$(eval "$CURL/tags/SHA-$SHA" | jq .assets | jq '.[] | select(.name == "templates.zip").id'); eval "$CURL/assets/$ASSET_ID -LJOH 'Accept: application/octet-stream'"
+unzip templates.zip
+rm templates.zip
\ No newline at end of file
diff --git a/introduction.md b/introduction.md
new file mode 100644
index 0000000..6a88a59
--- /dev/null
+++ b/introduction.md
@@ -0,0 +1,1028 @@
+# Introduction to ASPL
+ASPL is a simple yet powerful modern cross-platform programming language.
+ It is especially suited for people who are just getting into programming, but can also be a powerful tool for more experienced programmers.
+ ASPL has (obviously) been influenced by multiple different other languages, most notably C# and V.
+
+Going through this introduction will take you under an hour, but you will learn the most important skills for developing software with ASPL.
+ Please keep in mind that this document is not (yet) a complete walkthrough of the language and there is still a lot of information missing.
+
+> [!NOTE]
+> This introduction is written very beginner-friendly, but if you aren't familiar with software engineering at all yet, you might want to learn the basics of programming through other media (e.g. blog posts or videos) first.
+
+## Installation
+ASPL supports most modern operating systems and easily cross-compiles between them.
+ Please refer to the installation instructions to install ASPL on your system.
+
+## Table of contents
+
+
+
+* [Hello World](#hello-world)
+* [Comments](#comments)
+* [Function basics](#function-basics)
+* [Expression vs. Statement](#expression-vs-statement)
+* [Variables](#variables)
+* [Type system](#type-system)
+ * [Type aliases](#type-aliases)
+ * [Nullability](#nullability)
+ * [Oftype](#oftype)
+* [If statements](#if-statements)
+* [While statements](#while-statements)
+* [Repeat statements](#repeat-statements)
+* [Assertions](#assertions)
+* [Strings](#strings)
+* [Lists](#lists)
+* [Maps](#maps)
+* [Functions](#functions)
+* [Callbacks](#callbacks)
+ * [Closures](#closures)
+
+
+
+* [Modules](#modules)
+* [Namespaces](#namespaces)
+* [Visibility modifiers](#visibility-modifiers)
+* [Memory management](#memory-management)
+* [Order of evaluation](#order-of-evaluation)
+* [Error handling](#error-handling)
+ * [Catch blocks](#catch-blocks)
+ * [Error Propagation](#error-propagation)
+ * [Panics](#panics)
+* [Classes](#classes)
+ * [Static classes](#static-classes)
+* [Properties](#properties)
+ * [Static properties](#static-properties)
+* [Methods](#methods)
+ * [Static methods](#static-methods)
+* [Inheritance](#inheritance)
+* [Parent Method Calls](#parent-method-calls)
+* [Enums](#enums)
+ * [Bitfields/Flags](#bitfieldsflags)
+
+
+
+* [Compiler options](#compiler-options)
+ * [Cross-compilation](#cross-compilation)
+ * [Debug vs. production builds](#debug-vs-production-builds)
+ * [Conditional compilation](#conditional-compilation)
+ * [Choosing a backend](#choosing-a-backend)
+ * [Stack vs. heaped based builds](#stack-vs-heaped-based-builds)
+* [Resource embedding](#resource-embedding)
+* [Debugging](#debugging)
+ * [Breakpoints](#breakpoints)
+* [Android & iOS](#android--ios)
+
+
+
+
+## Hello World
+```aspl
+print("Hello World!")
+```
+Save this code to a file and run it with `aspl run file.aspl`.
+ That's it. You have just written your very first ASPL program! 🎉
+
+## Comments
+ASPL supports two styles of comments
+
+### Single-line comments
+If a line starts with `//`, everything after that is ignored by the ASPL parser and treated as a comment.
+For example:
+```
+print("Hello World!") // You can write whatever you want here
+// This is a comment too
+print("But this is executed again!")
+```
+
+### Multi-line comments
+If you want to span a comment over multiple lines (very useful for commenting out pieces of code temporarily), you can use `/*` to start a comment and `*/` to end it.
+ Note that multi-line comments can be nested in ASPL.
+```
+print("Hello World!")
+/*Start of comment
+This is a comment
+print("This is ignored since it's in a comment")
+/*
+Nested comment
+*/
+End of comment*/
+print("This is executed again")
+```
+
+## Function basics
+Functions are a computer science concept inspired by mathematical functions.
+ They can take an input value, process it and return an output.
+ Some functions don't return anything though, for example `print` - it only writes the text you gave it to the console.
+ As already shown above, you can invoke a function like this:
+```aspl
+myFunction("argument 1", 42, "argument 3") // takes 3 arguments
+myOtherFunction() // takes 0 arguments
+```
+Functions that return something can be used as an expression and their return value can for example be passed to other functions:
+```aspl
+print(multiply(20 * 20)) // prints 400
+```
+
+## Expression vs. Statement
+A statement is an instruction to the computer. For example, `while`, `return`, function declarations, ...
+ An expression, on the other hand, is also like an instruction, but it returns a value, e.g. an access to a variable, mathematical operators, the `this` keyword, ...
+ Expressions can be passed to functions, assigned to variables, used with operators and so on.
+ If you want to refer to any instruction, whether it's an expression or a statement, use the term `node`.
+
+## Variables
+ASPL is statically typed. That means that all variables are associated with a certain type, e.g. a string or an integer.
+ You can declare a variable with the `var` keyword, optionally followed by its types, the variable name, and its initial value.
+ All variables in ASPL have an initial value. If no types are given, the type is inferred from the initial value.
+```aspl
+var int i = 0
+var float f = 1f
+var s = "Hello"
+```
+Updating a variable works like this:
+```aspl
+var i = 0
+print(i)
+i = 1
+print(i)
+```
+The output of the above program is:
+```aspl
+0
+1
+```
+Note that you cannot assign a value of a wrong type to a variable:
+```aspl
+var s = "Hello"
+s = 0 // error, the variable 's' can only hold strings
+```
+
+## Type system
+ASPL has a very advanced and powerful type system .
+ Every expression and everything holding an expression (e.g. a variable) can have either a single type (e.g. literals or simple variables) or of a multitype:
+```aspl
+var int i = 0 // i can hold values of the type integer (since int is an alias to integer)
+var int|float x = 0f // x can hold values of either integer or float, even though its initialized with a float here
+```
+If something expects a certain type (e.g. a variable or function parameter), it will fail to compile if any of the types of the expression is incompatible with the expected type:
+```aspl
+var int|float x = 0f
+var string|float y = 0f
+x = y // error, y could be of type string, but x cannot hold strings
+```
+
+### Type aliases
+Type aliases allow to refer to a certain type with another name:
+```aspl
+var int a = 0
+var integer b = 0
+a = b // no error, a and b are both of type integer
+```
+All builtin type aliases are:
+ `bool` => `boolean`
+ `int` => `integer`
+
+It is currently not possible to declare custom type aliases.
+
+### Nullability
+All types in ASPL are **non-nullable** by default:
+```aspl
+var int i = 0
+i = null // error, i cannot hold null
+```
+
+If you want to allow a variable (or property, etc.) to hold null, you can append a `?` to its type:
+```aspl
+var int? i = 0
+i = null // no error, i can hold null
+```
+Note that the `?` is just a shorthand for `type|null`, so the following is also valid:
+```aspl
+var int|null i = 0
+i = null // no error, i can hold null
+```
+
+If you are sure that a value of type `type?` is not null in a certain situation (for example because you checked it before), you can use the `?!` ("null forgiving") operator to force the compiler to treat it as a value of type `type`:
+```aspl
+var string? s = "Hello"
+if(s != null){
+ print(s?!.length) // no error, s is assured to not be null here
+}
+```
+Note that the `?!` operator is just a shorthand for a cast to `type`, so the following is also valid:
+```aspl
+var string? s = "Hello"
+if(s != null){
+ print((string)s.length) // no error, s is assured to be not null here
+}
+```
+
+### Oftype
+The `oftype` expression checks if a certain expression is of a certain type at runtime:
+```aspl
+var any x = 0
+print(x oftype int) // true
+print(x oftypr string) // false
+```
+
+## If statements
+The `if` statement is an essential control flow structure in computer programming. It only executes a certain block of code if a condition is met, i.e. a given expression evaluates to true:
+```aspl
+if(true){
+ print("Will certainly be printed")
+}
+if(false){
+ print("Will certainly not be printed")
+}
+```
+Combined with for example variables or user input, this allows a programmer to dynamically react to a program's environment:
+```aspl
+if(input("Do you like ASPL? ") == "y"){
+ print("Great!")
+}
+```
+For the above example, it would also be useful to execute some other code if the condition was not met. For this, there is the `else` keyword that can only follow after an `if` statement:
+```aspl
+if(input("Do you like ASPL? ") == "y"){
+ print("Great!")
+}else{
+ print("Please let us know how we can improve!")
+}
+```
+Furthermore, if you want to check for multiple possible scenarios, you might want to use `elseif`:
+```aspl
+var requestedProduct = input("Which product do you want to buy? ")
+if(requestedProduct == "apple"){
+ print("You successfully bought an apple")
+}elseif(requestedProduct == "banana"){
+ print("You successfully bought a banana")
+}else{
+ print("I'm sorry, but we don't offer the product: " + product)
+}
+```
+
+## While statements
+Similar to the `if` statement, a `while` statement is a conditional control structure. But instead of executing the code in the body only once, it will be executed as long as the condition evaluates to true:
+```aspl
+// This never evaluates to true and thus directly skips the body
+while(false){
+ print("Will never be executed")
+}
+
+// Prints all numbers from 0 to 9
+var i = 0
+while(i < 10){
+ print(i)
+ i++
+}
+
+// This will repeat the body forever
+while(true){
+ print("Inside infinite loop...")
+}
+```
+There is no `else` for while loops.
+
+## Repeat statements
+A `repeat` statement is like a while statement, but instead of taking a condition, it takes an integer specifying how often the code in the body should be repeated:
+```aspl
+repeat(100){
+ print("Repeat...")
+}
+```
+You can also create a variable that will automatically hold the value of the current iteration:
+```aspl
+repeat(42, i = 0){
+ print(i)
+}
+```
+As you can see, you can (and should) even specify the start value of the variable.
+
+## Assertions
+Assertions are statements that are used to make sure certain conditions are met. If they are not met, the program will crash and print the location of the failed assertion.
+ They are used for debugging purposes and can also help in documentation.
+```aspl
+assert true // ignored, no error
+assert false // error, the assertion failed
+```
+
+## Strings
+A string is simply a sequence of characters, as in most other programming languages. However, ASPL strings are special in two ways:
+
+1. They are immutable, i.e. you cannot change a string after it has been created
+```aspl
+var s = "Hello"
+s[0] = "h" // error, strings are immutable
+s = "h" + s.after(0) // this is the correct way to change a string
+```
+This makes a lot of code much cleaner, less error-prone, and allows for better optimizations.
+
+2. They are natively Unicode, i.e. they can hold any character from any language:
+```aspl
+var s = "👋"
+print(s) // prints 👋
+assert s.length == 1
+```
+> [!WARNING]
+> Native Unicode strings are NOT fully implemented in ASPL yet. For example, the `length` property currently returns the number of bytes, not the number of characters. This is most likely to change in the future.
+
+## Lists
+A list contains a variable amount of elements of the same type:
+```aspl
+var list l = ["Hello", "World", "!"]
+print(l) // ["Hello", "World", "!"]
+```
+
+You can access an element in the list through indexing:
+```aspl
+var list l = ["Hello", "World", "!"]
+print(l[0]) // Hello
+print(l[2]) // !
+```
+**Note:** Indices start at **0** in ASPL (like in most other languages)!
+
+Adding elements to the list works using the `.add` method:
+```aspl
+var list l = ["Hello", "World"]
+l.add("!")
+print(l) // ["Hello", "World", "!"]
+```
+
+Removing elements can be either done by value or by index:
+```aspl
+var list l = ["Hello", "World", "!"]
+l.removeAt(0)
+l.remove("!")
+print(l) // ["World"]
+```
+
+## Maps
+A map is similar to a [list](#lists), but contains pairs of elements instead of individual elements.
+ In a map, each pair contains a key and an associated value; you may know this concept from other languages under the terms `dictionary` or `associative array`.
+```aspl
+var map m = {"Universe" => 42}
+print(m["Universe"]) // 42
+m["zero"] = 0
+print(m["zero"]) // 0
+m.removeKey("zero")
+print(m) // {"Universe" => 42}
+m.removeValue(42)
+print(m) // {}
+```
+
+## Functions
+As already described above, a function is a block of code with certain parameters. There are several builtin functions, e.g. `print`, `input` or `exit`.
+ Moreover, you can define your own functions, as well as library developers, of course.
+ This makes it possible to split a program into several smaller subroutines and reuse code. Because of this, functions are an essential concept in virtually every programming language today.
+
+```aspl
+function hello(){
+ print("Hello World!")
+}
+```
+The above code declares a function called `hello` that prints `Hello World!` to the console when executed.
+ It can be invoked like this:
+```aspl
+hello()
+```
+
+Another example with parameters:
+```aspl
+function greet(string person){
+ print("Hello " + person)
+}
+
+greet("Jonas") // Hello Jonas
+greet("Marie") // Hello Marie
+```
+
+## Callbacks
+A callback is an anonymous function :
+```aspl
+var greet = callback(string name){
+ print("Hello " + name + "!")
+}
+greet.invoke("John") // Hello John!
+// Alternative (and preferred) syntax for invoking a callback
+greet.("John") // Hello John!
+```
+In this case, the callback itself does not have a name, although it is stored inside a variable called `greet`.
+
+### Closures
+A callback can also be used as a closure .
+ That means that callbacks can access the variables in their creation scope, even after it has already been popped.
+ Furthermore, `this` also points to the instance in whose methods the callable was created.
+```aspl
+function create() returns callback{
+ var num = 42
+ return callback(){
+ print(num)
+ }
+}
+var callback c = create()
+c.invoke() // 42
+```
+
+## Modules
+In ASPL, the term `module` is used for code libraries, i.e. files that contain e.g. functions and classes and are intended to be used together in other applications.
+ The modules of the standard library (`stdlib`) are automatically shipped with every complete ASPL installation.
+ Of course, programmers can also create their own third-party modules.
+
+Modules are imported using the `import` statement:
+```aspl
+import math
+
+print(math.sqrt(9)) // 3
+```
+
+Currently, modules do not need any special structure, but they might require a `module.json` or something similar in the future.
+
+You can install modules that are not in the standard library by using the `install` command, for example: `aspl install https://github.com/user/repo`
+ These modules are stored in the `.aspl_modules` folder in the user's home directory.
+ The modules of the standard library are located in the `stdlib` folder of the ASPL installation.
+
+## Namespaces
+You can organize your code into folders and modules. To access functions and types defined in other folders, you can import them:
+
+`project/folder/hello.aspl`:
+```aspl
+function hello(){
+ print("Hello World!")
+}
+```
+
+`project/main.aspl`:
+```aspl
+import folder
+
+folder.hello() // Hello World!
+```
+
+Namespaces are always implicit in ASPL and inferred from the folder structure.
+
+If you import a module, you automatically import the main namespace of the module. You can import other folders in the module by prefixing the name of the folder with the name of the module:
+```aspl
+import encoding.ascii // imports the ascii folder of the encoding module
+```
+
+## Visibility modifiers
+By default, all symbols, i.e. functions, methods, classes, properties and enums, are **private** in ASPL; this means that
+* functions can only be called from within the same module they are defined in *(module bound)*
+* classes can only be used from within the same module they are defined in *(module bound)*
+ * note that instantiating a class from outside that class is only possible if the constructor is marked as `[public]`
+* enums can only be used from within the same module they are defined in *(module bound)*
+* properties and methods can only be accessed from within the same class or children of the class they are defined in *(class bound)*
+
+You can export a symbol/make it public by using the `[public]` attribute:
+```aspl
+[public]
+function hello(){
+ print("Hello World!")
+}
+```
+
+There is also the `[readpublic]` attribute, which makes a property read-only from outside the class it is defined in:
+```aspl
+class Person{
+
+ [readpublic]
+ property string name
+
+ method construct(string name){
+ this.name = name
+ }
+
+}
+```
+Note that the property can still be modified from within the class; it is **not instance bound**, that means that all instances of the class can modify properties that are readpublic to the class, even if those properties are in a different instance.
+
+## Memory management
+ASPL uses a garbage collector to automatically clean up memory after it has been used.
+ This means that you don't have to remember manually deallocating memory or checking if it has already been freed. It allows for much faster development times and prevents leaks as well as bugs.
+
+## Order of evaluation
+> [!WARNING]
+> The C backend currently does not ensure a strict left-to-right evaluation order for function/method/callback invocations! This is at the moment considered a bug, although the specification of the argument evaluation order may still change in the future.
+
+Evaluation in ASPL is always left-to-right; while this may seem irrelevant in some situations, it is especially important for boolean operators like `&&` (logical and):
+
+```aspl
+var list? l = get_list_or_null()
+if(l != null && l?!.length > 0){
+ // ...
+}
+```
+
+In this example, it is crucial that the `l != null` side of the `&&` operator is evaluated before the `l?!.length > 0` side, as `l?!.length` would throw an error if `l` was `null`.
+
+## Error handling
+> [!IMPORTANT]
+> The error handling mechanisms described here are still experimental. They may contain bugs and are not fully implemented yet, which is why this feature set is currently hidden behind the `-enableErrorHandling` flag.
+
+ASPL uses a variation of the result type concept to handle errors with some influence from exception handling .
+
+Every function (or method) that can fail has to be marked with the `[throws]` attribute:
+```aspl
+[throws]
+function divide(int a, int b) returns int{
+ if(b == 0){
+ throw new ArithmeticError("Division by zero")
+ }
+ return a / b
+}
+```
+As you can see above, the actual error is thrown using the `throw` keyword, similar to exceptions in other languages.
+
+The error thrown here is an instance of a class called `ArithmeticError`. A declaration of this class could look like this:
+```aspl
+[error]
+class ArithmeticError{
+
+ property string message
+
+ method construct(string message){
+ this.message = message
+ }
+
+ method string() returns string{
+ return message
+ }
+
+}
+```
+The `[error]` attribute is used to mark a class as an error, and the `string()` cast method has to be defined to allow the error to be printed by the ASPL runtime.
+
+### Catch blocks
+You can catch errors using `catch` blocks (example 1).
+
+If you try to catch an error from an expression inside another expression, you either have to specify a fallback value using the `fallback` keyword (example 2) or escape from the `catch` block and simultaneously return from the function the catch block was defined in using the `escape` keyword (example 3); both work similar to `return`, but "return on a different level", which is why they have been assigned separate keywords and using the regular `return` is not allowed inside `catch blocks`.
+
+```aspl
+divide(10, 0) catch error{
+ print(error.message)
+}
+```
+```aspl
+print(divide(10, 0) catch error{
+ print(error.message)
+ fallback 0
+})
+```
+```aspl
+function reciprocal_squared(int a) returns int{
+ var r = divide(1, a) catch error{
+ print(error.message)
+ escape 0
+ }
+ return r * r
+}
+```
+
+### Error Propagation
+If you don't want to or cannot handle an error, you can pass it on to the caller:
+```aspl
+[throws]
+function reciprocal(int a) returns int{
+ return divide(1, a)! // ! is the error propagation operator
+}
+```
+As you can see, the `!` operator here is used to propagate the error.
+
+Note that this requires the caller to also be marked with the `[throws]` attribute.
+
+### Panics
+If an error is so severe that the program cannot continue, you can use the `panic` keyword to stop the execution immediately:
+```aspl
+function divide(int a, int b) returns int{
+ if(b == 0){
+ panic("Division by zero")
+ }
+ return a / b
+}
+```
+Functions that can panic only have to be marked with the `[throws]` attribute if they can also throw an error. Recovering from panics is, at least currently, not possible.
+
+### Defer blocks
+If you want to make sure a certain piece of code is executed once a scope is left (via break, return, error propagation or simply after reaching the end of a block), you can use the `defer` statement:
+```aspl
+function modifyFile(){
+ var file = io.open_file("file.txt", "r")
+ defer { file.close() }
+ // Modify the file somehow
+}
+```
+
+Note that...
+* ...defer blocks will be evaluated in their reverse order of appearance in the code ("reverse order" like opening/closing tags in markdown languages)
+* ...when returning a value, the defer blocks are executed after the return value has been evaluated
+* ...defer blocks may not use the defer statement in their own body
+* ...defer blocks may not use the return statement or throw/propagate any errors
+* ...defer blocks may not use the break or continue statements with a level greater than their own loop depth
+* ...defer blocks will not be called when the program exits directly using calls to `exit` or `panic` and thus some resources may stay open, but normally the OS will close most of them (e.g. file handles) automatically when the process exits anyway
+
+## Classes
+Classes are the base of object-oriented programming in ASPL.
+ They encapsulate data and make it easily accessible as well as passable.
+ Classes can have properties and methods.
+```aspl
+class Hello{
+
+ property string name
+ property int number = 5
+
+ method construct(string name){
+ print("An instance has been created")
+ this.name = name
+ }
+
+ method hello(){
+ print("Hello from " + name)
+ print("My number is: " + number)
+ }
+
+}
+```
+You can instantiate a new instance of the `Hello` class using the `new` keyword:
+```aspl
+var Hello instance = new Hello("Daniel")
+instance.hello()
+```
+The above program will output:
+```
+An instance has been created
+Hello from Daniel
+My number is: 5
+```
+
+For information on inheritance in ASPL, see [inheritance](#inheritance).
+
+### Static classes
+Static classes are classes that cannot be instantiated and can only contain static properties and methods (see [static properties](#static-properties) and [static methods](#static-methods)):
+```aspl
+[static]
+class Example{
+
+ [static]
+ property int number = 42
+
+ [static]
+ method hello(){
+ print("Hello World!")
+ }
+
+}
+```
+```aspl
+Example:number = 5
+Example:hello() // Hello World!
+```
+
+## Properties
+Properties are a class's way of storing data.
+ They are similar to variables, but bound to an instance of a class instead of a scope:
+```aspl
+class Example{
+
+ property int number = 42 // default value is 42
+
+}
+```
+```aspl
+var Example a = new Example()
+var Example b = new Example()
+print(a.number) // 42
+print(a.number) // 42
+a.number = -1
+print(a.number) // -1
+print(b.number) // 42
+```
+
+### Static properties
+Static properties are properties that are not bound to an instance of a class, but to the class itself:
+```aspl
+class Example{
+
+ [static]
+ property int number = 42
+
+}
+```
+```aspl
+print(Example:number) // 42
+Example:number = -1
+print(Example:number) // -1
+```
+
+## Methods
+Methods are class-bound functions:
+```aspl
+import math
+
+class Point{
+
+ property int x
+ property int z
+
+ method construct(int x, int z){
+ this.x = x
+ this.z = z
+ }
+
+ method distanceTo(Point other){
+ return math.sqrt(math.pow(double(x - other.x), 2d) + math.pow(double(y - other.y), 2d))
+ }
+
+}
+```
+The class `Point` has two methods:
+
+1. `construct`: This is a special method called the `constructor`, which allows you to initialize an instance with arguments; it is automatically called every time a class is instantiated
+2. `distanceTo`: This method calculates the distance between the point it is called on and another point (which is passed to it) using the Pythagorean theorem.
+
+### Static methods
+Similar to static properties, static methods are methods that act on a class itself instead of an instance of that class and thus can only use static properties instead of regular ones:
+```aspl
+class Example{
+
+ [static]
+ property string name = "Daniel"
+
+ [static]
+ method hello(){
+ print("Hello " + name)
+ }
+
+}
+```
+```aspl
+Example:hello() // Hello Daniel
+```
+
+## Inheritance
+Classes can inherit properties and methods from other classes using the `extends` keyword:
+```aspl
+class Base{
+
+ property int number = 42
+
+ method construct(){
+ print("Base class constructor")
+ }
+
+}
+```
+```aspl
+class Example extends Base{
+}
+```
+```
+var Example e = new Example() // prints "Base class constructor"
+print(e.number) // 42
+```
+ASPL uses multiple inheritance instead of interfaces or traits:
+```aspl
+class Mammal{
+
+}
+```
+```aspl
+class Pet{
+
+}
+```
+```aspl
+class Dog extends Mammal, Pet{
+
+}
+```
+```aspl
+var Dog d = new Dog()
+assert d oftype Mammal
+assert d oftype Pet
+```
+> [!IMPORTANT]
+> Multiple inheritance is a tricky concept and generally not very popular amongst other programming languages. While it simplifies a lot of things and can make intuitive sense sometimes, there are also good reasons not to use it. Because of this, ASPL might switch to single inheritance + interfaces/traits in the future, although this is currently not planned.
+
+## Parent Method Calls
+You can explicitly invoke the implementation of a method in certain parent classes even if the method is overridden in the current class using the `parent` keyword; you might also know this concept from other languages as `super`.
+```aspl
+class Base{
+
+ [public]
+ method hello(){
+ print("Hello from Base")
+ }
+
+}
+```
+```aspl
+class Example extends Base{
+
+ [public]
+ method hello(){
+ print("Hello from Example")
+ parent(Base).hello()
+ }
+
+}
+```
+```aspl
+var Example e = new Example()
+e.hello()
+```
+The above program will output:
+```
+Hello from Example
+Hello from Base
+```
+
+## Enums
+Enums are a way to define a list of constants.
+ They are defined using the `enum` keyword:
+```aspl
+enum Color{
+ Red,
+ Green,
+ Blue
+}
+```
+The above code defines an enum called `Color` with three so-called enum fields:
+```aspl
+Color.Red
+Color.Green
+Color.Blue
+```
+The values of the enum fields are assigned automatically, starting at **0**.
+ You can also specify custom values though:
+```aspl
+enum Color{
+ Red = 1,
+ Green = 2,
+ Blue = 3
+}
+```
+
+### Bitfields/Flags
+There is a `[flags]` attribute that allows for creating bitflags.
+ Multiple enum fields can be merged with the `|` operator in these enums.
+ This is widely used for passing multiple options as one value.
+
+You can check if an enum flag contains a field like this:
+```aspl
+var Color c = Color.Red || Color.Green
+print((c && Color.Red) == Color.Red) // true - the enum flag contains the Red field
+```
+
+**Limitations:**
+* No field value overriding
+* Only up to 32 enum fields
+
+## Compiler options
+### Cross-compilation
+One of ASPL's main goals has always been seamless cross-compilation and support for as many platforms as possible. In fact, you can simply cross-compile your ASPL code to any other platform using the `-os` and `-arch` flags:
+```bash
+aspl -os windows -arch amd64 hello.aspl
+```
+The above command will compile `hello.aspl` to a 64-bit Windows executable.
+
+Such a sophisticated cross-compilation toolchain is only possible due to ASPL's unique architecture based on Zig CC (when using the C backend) and a mechanism called "templating" (when using the AIL backend; see the `templates` folder for more information).
+
+Additionally, ASPL uses dynamic loading of system graphics libraries (see the `thirdparty/ssl` folder for more information) to enable cross-compilation of graphical applications; in theory, this can even work on macOS! In practice, this has not been properly implemented yet and might suffer from licensing issues.
+ This option is currently hidden behind the `-ssl` flag, but will probably soon be on by default.
+
+> [!NOTE]
+> Cross-compiling to macOS using the C backend is currently not possible due to issues with the garbage collector. This will hopefully be fixed soon.
+
+### Debug vs. production builds
+You can compile your code in production mode using the `-prod` flag:
+```bash
+aspl -prod hello.aspl
+```
+This will disable all debug features and use optimizations to make your code run faster. Furthermore, the `production` conditional compilation symbol will be defined instead of `debug`.
+
+Usually, you will want to compile your code in debug mode when developing and in production mode when releasing your application.
+
+### Conditional compilation
+Like in many other compiled languages, you can make parts of your code only compile in certain situations using the `$if` and `$else` keywords, for example:
+```aspl
+$if debug{
+ print("Debug build")
+}$else{
+ print("Production build")
+}
+```
+The above code will print `Debug build` when compiling in debug mode and `Production build` when compiling in production mode (using the `-prod` compiler option).
+
+In addition to the `debug` and `production` symbols, there are also the following symbols:
+* `windows`/`linux`/`macos`/`android`/`ios`/... - the target operating system
+* `amd64`/`arm64`/`arm32`/`rv64`/`rv32`/`i386`/... - the target architecture
+* `x32`/`x64` - the bitness of the target architecture (32-bit or 64-bit)
+* `console`/`gui` - the target application type (console or GUI); this is only relevant for Windows
+* `main` - whether the current file is part of the main module
+
+You can also define your own symbols using the `-d` compiler option:
+```bash
+aspl -d mySymbol hello.aspl
+```
+```aspl
+$if mySymbol{
+ print("mySymbol is defined")
+}
+```
+
+### Choosing a backend
+ASPL currently supports two different backends: `ail` and `c`.
+
+The `ail` backend compiles your code to a **bytecode format called `AIL`** (formerly an acronym for "ASPL Intermediate Language") and uses [templating](templates) to bundle the AIL bytecode together with an AIL interpreter into a single executable.
+
+Benefits of the `ail` backend:
+* Faster compilation
+* Better debugging experience
+* Not dependent on a C compiler
+* Less bug prone
+* Easier to develop for the ASPL team
+
+On the other hand, the `c` backend compiles your code to **C code** and then uses a C compiler to compile it to an executable.
+
+Benefits of the `c` backend:
+* Faster execution
+* Smaller executable size (sometimes)
+* Even better cross-compilation
+* Easier C interoperability
+* Does not require prebuilt templates
+
+You can choose a backend using the `-backend` compiler option:
+```bash
+aspl -backend c hello.aspl
+```
+
+It's generally advised to use the `ail` backend for development/debug builds and the `c` backend for production; note that the `ail` backend is currently the default for all builds.
+
+The different backends are designed to be **as compatible as possible**, so you can easily switch between them, optimally without changing anything in your code (as long as you don't use C interoperability).
+
+### Stack vs. heaped based builds
+> [!NOTE]
+> This section only applies to the C backend.
+
+> [!WARNING]
+> This is a rather sophisticated optimization technique; while it may speed up your program significantially, it's a complicated matter and might cause issues in some cases (although that's a bug and should be reported).
+
+ASPL allocates wrapper C-objects for all ASPL objects created in your program; i.e. if you write `"Hello World"`, it will be translated to something like `ASPL_STRING_LITERAL("Hello World")` (where `ASPL_STRING_LITERAL` is a helper function allocating the actual wrapper object).
+
+These objects can either be allocated on the stack or on the heap, both having their own advantages and disadvantages. Since ASPL is permanently using a GC and the C backend has been carefully designed to support both of these options, you can easily toggle between the different allocation methods.
+
+By default, wrapper objects are allocated on the stack. You can change this using the `-heapBased` compiler flag.
+
+Various performance tests have shown that neither of these methods is per se faster than the other one, so carefully profile your program first and test if switching the allocation method is really worth it.
+
+> [!TIP]
+> Some very large programs might experience stack-overflow issues when allocating on the stack; passing `-heapBased` can potentially fix this. Alternatively, you can also try increasing the stack size using the `-stacksize` compiler option (although this is not supported on all platforms).
+
+## Resource embedding
+You can embed resources (such as images, audio files, etc.) directly into your executable using the `$embed` compile-time function:
+```aspl
+var image = $embed("image.png")
+```
+In the above example, the `image.png` file will be embedded into the executable and the `image` variable will contain a `list` containing the raw image data.
+You can for example then load this image with the graphics module:
+```aspl
+import graphics
+
+var image = Canvas:fromFileData($embed("image.png"))
+img.save("embedded.png")
+```
+
+## Debugging
+### Breakpoints
+> [!NOTE]
+> This feature is currently only supported in the AIL backend.
+
+ASPL has a built-in debugger that allows you to step through your code and inspect variables, stack traces, etc.
+
+Just import the `breakpoints` module and use the `break` function to set a breakpoint:
+```aspl
+import breakpoints
+
+var x = ["a", "b", "c"]
+breakpoints.break()
+print(x.length)
+```
+
+You can also pass a message to the `break` function to make it easier to identify the breakpoint, as it will be printed to the console when the breakpoint is reached:
+```aspl
+import breakpoints
+
+var x = ["a", "b", "c"]
+breakpoints.break("x is initialized")
+print(x.length)
+```
+
+As soon as the program reaches the breakpoint, the debugger will be started, and you can use several commands such as `stack`, `scope`, `continue` or `quit` to inspect the current state of the program.
+ For more commands, see the `help` command.
+
+## Android & iOS
+Deploying apps to mobile operating systems has traditionally been a horrible experience. Not only because of bad tooling, but also because of the lack of cross-platform GUI toolkits in many programming languages.
+ Since ASPL's `graphics` module is designed to be platform-agnostic and non-native, most apps written for desktop operating systems will also work on phones, tablets, etc. with little to no adjustments.
+
+ASPL apps can easily be deployed to Android using [ASAPP](https://github.com/aspl-lang/asapp).
+
+iOS support is also planned, but currently not one of the main priorities.
\ No newline at end of file
diff --git a/runtime/ailinterpreter/README.md b/runtime/ailinterpreter/README.md
new file mode 100644
index 0000000..6814095
--- /dev/null
+++ b/runtime/ailinterpreter/README.md
@@ -0,0 +1,10 @@
+# AIL Bytecode Interpreter
+This is a simple AIL bytecode interpreter, implemented in C.
+
+It partially uses the C backend template as its runtime library.
+
+TODO: Clean & speed up the code.
+
+## Dependencies
+* `stdlib/backend/stringcode/c/template.c` as well as all of its dependencies
+* `thirdparty/appbundle` (only when building the interpreter as a standalone application, i.e. `-DASPL_AILI_BUNDLED`)
\ No newline at end of file
diff --git a/runtime/ailinterpreter/builtins.c b/runtime/ailinterpreter/builtins.c
new file mode 100644
index 0000000..0ffb2b7
--- /dev/null
+++ b/runtime/ailinterpreter/builtins.c
@@ -0,0 +1,221 @@
+#include "interpreter.h"
+
+void aspl_ailinterpreter_print(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, char* identifier, ASPL_AILI_ArgumentList arguments) {
+ ASPL_OBJECT_TYPE object = arguments.arguments[0];
+ ASPL_OBJECT_TYPE newLine = arguments.arguments[1];
+ aspl_function_print(C_REFERENCE(object), C_REFERENCE(newLine));
+ aspl_ailinterpreter_return(context, bytes, ASPL_NULL());
+}
+
+void aspl_ailinterpreter_key(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, char* identifier, ASPL_AILI_ArgumentList arguments) {
+ ASPL_OBJECT_TYPE prompt = arguments.arguments[0];
+ // TODO: Implement key when it's implemented in the C backend
+}
+
+void aspl_ailinterpreter_input(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, char* identifier, ASPL_AILI_ArgumentList arguments) {
+ ASPL_OBJECT_TYPE prompt = arguments.arguments[0];
+ aspl_ailinterpreter_return(context, bytes, aspl_function_input(C_REFERENCE(prompt)));
+}
+
+void aspl_ailinterpreter_exit(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, char* identifier, ASPL_AILI_ArgumentList arguments) {
+ ASPL_OBJECT_TYPE code = arguments.arguments[0];
+ aspl_function_exit(C_REFERENCE(code));
+}
+
+void aspl_ailinterpreter_range(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, char* identifier, ASPL_AILI_ArgumentList arguments) {
+ ASPL_OBJECT_TYPE start = arguments.arguments[0];
+ ASPL_OBJECT_TYPE end = arguments.arguments[1];
+ // TODO: Implement range when it's implemented in the C backend
+}
+
+void aspl_ailinterpreter_panic(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, char* identifier, ASPL_AILI_ArgumentList arguments) {
+ ASPL_OBJECT_TYPE object = arguments.arguments[0];
+ ASPL_OBJECT_TYPE newLine = arguments.arguments[1];
+ aspl_function_panic(C_REFERENCE(object));
+ aspl_ailinterpreter_return(context, bytes, ASPL_NULL());
+}
+
+void aspl_ailinterpreter_initialize_builtin_functions(ASPL_AILI_EnvironmentContext* context) {
+ hashmap_str_to_voidptr_hashmap_set(context->functions, "_print", aspl_ailinterpreter_print);
+ ASPL_AILI_BuiltinFunction* print = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinFunction));
+ print->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 2), .size = 2 };
+ print->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "object", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "any" }, .size = 1 }, .optional = 0 };
+ print->parameters.parameters[1] = (ASPL_AILI_Parameter){ .name = "newLine", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "bool" }, .size = 1 }, .optional = 1, .default_value = ASPL_TRUE() };
+ hashmap_str_to_voidptr_hashmap_set(context->builtin_functions, "_print", print);
+
+ hashmap_str_to_voidptr_hashmap_set(context->functions, "_key", aspl_ailinterpreter_key);
+ ASPL_AILI_BuiltinFunction* key = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinFunction));
+ key->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 };
+ key->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "prompt", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "string" }, .size = 1 }, .optional = 1, .default_value = ASPL_STRING_LITERAL("") };
+ hashmap_str_to_voidptr_hashmap_set(context->builtin_functions, "_key", key);
+
+ hashmap_str_to_voidptr_hashmap_set(context->functions, "_input", aspl_ailinterpreter_input);
+ ASPL_AILI_BuiltinFunction* input = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinFunction));
+ input->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 };
+ input->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "prompt", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "string" }, .size = 1 }, .optional = 1, .default_value = ASPL_STRING_LITERAL("") };
+ hashmap_str_to_voidptr_hashmap_set(context->builtin_functions, "_input", input);
+
+ hashmap_str_to_voidptr_hashmap_set(context->functions, "_exit", aspl_ailinterpreter_exit);
+ ASPL_AILI_BuiltinFunction* exit = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinFunction));
+ exit->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 };
+ exit->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "code", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "int" }, .size = 1 }, .optional = 1, .default_value = ASPL_INT_LITERAL(0) };
+ hashmap_str_to_voidptr_hashmap_set(context->builtin_functions, "_exit", exit);
+
+ hashmap_str_to_voidptr_hashmap_set(context->functions, "_range", aspl_ailinterpreter_range);
+ ASPL_AILI_BuiltinFunction* range = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinFunction));
+ range->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 2), .size = 2 };
+ range->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "start", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "byte", "int", "long", "float", "double" }, .size = 5 }, .optional = 0 };
+ range->parameters.parameters[1] = (ASPL_AILI_Parameter){ .name = "end", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "byte", "int", "long", "float", "double" }, .size = 5 }, .optional = 0 };
+ hashmap_str_to_voidptr_hashmap_set(context->builtin_functions, "_range", range);
+
+ hashmap_str_to_voidptr_hashmap_set(context->functions, "_panic", aspl_ailinterpreter_panic);
+ ASPL_AILI_BuiltinFunction* panic = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinFunction));
+ panic->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 2), .size = 2 };
+ panic->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "object", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "any" }, .size = 1 }, .optional = 0 };
+ panic->parameters.parameters[1] = (ASPL_AILI_Parameter){ .name = "newLine", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "bool" }, .size = 1 }, .optional = 1, .default_value = ASPL_TRUE() };
+ hashmap_str_to_voidptr_hashmap_set(context->builtin_functions, "_panic", panic);
+}
+
+void aspl_ailinterpreter_initialize_builtin_methods(ASPL_AILI_EnvironmentContext* context) {
+ hashmap_str_to_voidptr_HashMap* any_methods = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 2 });
+
+ ASPL_AILI_BuiltinMethod* any_cloneShallow = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ any_cloneShallow->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 0), .size = 0 };
+ hashmap_str_to_voidptr_hashmap_set(any_methods, "cloneShallow", any_cloneShallow);
+
+ ASPL_AILI_BuiltinMethod* any_cloneDeep = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ any_cloneDeep->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 0), .size = 0 };
+ hashmap_str_to_voidptr_hashmap_set(any_methods, "cloneDeep", any_cloneDeep);
+
+ hashmap_str_to_voidptr_hashmap_set(context->builtin_methods, "any", any_methods);
+
+ hashmap_str_to_voidptr_HashMap* string_methods = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 13 });
+
+ ASPL_AILI_BuiltinMethod* string_toLower = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ string_toLower->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 0), .size = 0 };
+ hashmap_str_to_voidptr_hashmap_set(string_methods, "toLower", string_toLower);
+
+ ASPL_AILI_BuiltinMethod* string_toUpper = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ string_toUpper->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 0), .size = 0 };
+ hashmap_str_to_voidptr_hashmap_set(string_methods, "toUpper", string_toUpper);
+
+ ASPL_AILI_BuiltinMethod* string_replace = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ string_replace->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 2), .size = 2 };
+ string_replace->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "search", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "string" }, .size = 1 }, .optional = 0 };
+ string_replace->parameters.parameters[1] = (ASPL_AILI_Parameter){ .name = "replace", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "string" }, .size = 1 }, .optional = 0 };
+ hashmap_str_to_voidptr_hashmap_set(string_methods, "replace", string_replace);
+
+ ASPL_AILI_BuiltinMethod* string_startsWith = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ string_startsWith->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 };
+ string_startsWith->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "substring", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "string" }, .size = 1 }, .optional = 0 };
+ hashmap_str_to_voidptr_hashmap_set(string_methods, "startsWith", string_startsWith);
+
+ ASPL_AILI_BuiltinMethod* string_endsWith = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ string_endsWith->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 };
+ string_endsWith->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "substring", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "string" }, .size = 1 }, .optional = 0 };
+ hashmap_str_to_voidptr_hashmap_set(string_methods, "endsWith", string_endsWith);
+
+ ASPL_AILI_BuiltinMethod* string_contains = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ string_contains->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 };
+ string_contains->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "substring", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "string" }, .size = 1 }, .optional = 0 };
+ hashmap_str_to_voidptr_hashmap_set(string_methods, "contains", string_contains);
+
+ ASPL_AILI_BuiltinMethod* string_after = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ string_after->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 };
+ string_after->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "position", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "int" }, .size = 1 }, .optional = 0 };
+ hashmap_str_to_voidptr_hashmap_set(string_methods, "after", string_after);
+
+ ASPL_AILI_BuiltinMethod* string_before = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ string_before->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 };
+ string_before->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "position", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "int" }, .size = 1 }, .optional = 0 };
+ hashmap_str_to_voidptr_hashmap_set(string_methods, "before", string_before);
+
+ ASPL_AILI_BuiltinMethod* string_trim = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ string_trim->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 };
+ string_trim->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "chars", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "string" }, .size = 1 }, .optional = 1, .default_value = ASPL_STRING_LITERAL(" \n\t\v\f\r") };
+ hashmap_str_to_voidptr_hashmap_set(string_methods, "trim", string_trim);
+
+ ASPL_AILI_BuiltinMethod* string_trimStart = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ string_trimStart->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 0), .size = 0 };
+ hashmap_str_to_voidptr_hashmap_set(string_methods, "trimStart", string_trimStart);
+
+ ASPL_AILI_BuiltinMethod* string_trimEnd = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ string_trimEnd->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 0), .size = 0 };
+ hashmap_str_to_voidptr_hashmap_set(string_methods, "trimEnd", string_trimEnd);
+
+ ASPL_AILI_BuiltinMethod* string_split = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ string_split->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 2), .size = 2 };
+ string_split->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "delimiter", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "string" }, .size = 1 }, .optional = 0 };
+ string_split->parameters.parameters[1] = (ASPL_AILI_Parameter){ .name = "maxParts", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "int" }, .size = 1 }, .optional = 1, .default_value = ASPL_INT_LITERAL(-1) };
+ hashmap_str_to_voidptr_hashmap_set(string_methods, "split", string_split);
+
+ ASPL_AILI_BuiltinMethod* string_reverse = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ string_reverse->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 0), .size = 0 };
+ hashmap_str_to_voidptr_hashmap_set(string_methods, "reverse", string_reverse);
+ hashmap_str_to_voidptr_hashmap_set(context->builtin_methods, "string", string_methods);
+
+ hashmap_str_to_voidptr_hashmap_set(context->builtin_methods, "string", string_methods);
+
+ hashmap_str_to_voidptr_HashMap* list_methods = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 8 });
+
+ ASPL_AILI_BuiltinMethod* list_contains = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ list_contains->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 };
+ list_contains->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "element", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "T" }, .size = 1 }, .optional = 0 };
+ hashmap_str_to_voidptr_hashmap_set(list_methods, "contains", list_contains);
+
+ ASPL_AILI_BuiltinMethod* list_add = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ list_add->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 };
+ list_add->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "element", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "T" }, .size = 1 }, .optional = 0 };
+ hashmap_str_to_voidptr_hashmap_set(list_methods, "add", list_add);
+
+ ASPL_AILI_BuiltinMethod* list_insert = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ list_insert->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 2), .size = 2 };
+ list_insert->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "index", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "int" }, .size = 1 }, .optional = 0 };
+ list_insert->parameters.parameters[1] = (ASPL_AILI_Parameter){ .name = "element", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "T" }, .size = 1 }, .optional = 0 };
+ hashmap_str_to_voidptr_hashmap_set(list_methods, "insert", list_insert);
+
+ ASPL_AILI_BuiltinMethod* list_insertElements = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ list_insertElements->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 2), .size = 2 };
+ list_insertElements->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "index", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "int" }, .size = 1 }, .optional = 0 };
+ list_insertElements->parameters.parameters[1] = (ASPL_AILI_Parameter){ .name = "elements", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "list" }, .size = 1 }, .optional = 0 };
+ hashmap_str_to_voidptr_hashmap_set(list_methods, "insertElements", list_insertElements);
+
+ ASPL_AILI_BuiltinMethod* list_remove = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ list_remove->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 };
+ list_remove->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "element", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "T" }, .size = 1 }, .optional = 0 };
+ hashmap_str_to_voidptr_hashmap_set(list_methods, "remove", list_remove);
+
+ ASPL_AILI_BuiltinMethod* list_removeAt = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ list_removeAt->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 };
+ list_removeAt->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "index", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "int" }, .size = 1 }, .optional = 0 };
+ hashmap_str_to_voidptr_hashmap_set(list_methods, "removeAt", list_removeAt);
+
+ ASPL_AILI_BuiltinMethod* list_clear = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ list_clear->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 0), .size = 0 };
+ hashmap_str_to_voidptr_hashmap_set(list_methods, "clear", list_clear);
+
+ ASPL_AILI_BuiltinMethod* list_join = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ list_join->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 };
+ list_join->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "delimiter", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "string" }, .size = 1 }, .optional = 0 };
+ hashmap_str_to_voidptr_hashmap_set(list_methods, "join", list_join);
+
+ hashmap_str_to_voidptr_hashmap_set(context->builtin_methods, "list", list_methods);
+
+ hashmap_str_to_voidptr_HashMap* map_methods = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 3 });
+
+ ASPL_AILI_BuiltinMethod* map_containsKey = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ map_containsKey->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 };
+ map_containsKey->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "key", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "T" }, .size = 1 }, .optional = 0 };
+ hashmap_str_to_voidptr_hashmap_set(map_methods, "containsKey", map_containsKey);
+
+ ASPL_AILI_BuiltinMethod* map_remove = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ map_remove->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 1), .size = 1 };
+ map_remove->parameters.parameters[0] = (ASPL_AILI_Parameter){ .name = "key", .expected_types = (ASPL_AILI_TypeList) {.types = (char* []) { "T" }, .size = 1 }, .optional = 0 };
+ hashmap_str_to_voidptr_hashmap_set(map_methods, "remove", map_remove);
+
+ ASPL_AILI_BuiltinMethod* map_clear = ASPL_MALLOC(sizeof(ASPL_AILI_BuiltinMethod));
+ map_clear->parameters = (ASPL_AILI_ParameterList){ .parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 0), .size = 0 };
+ hashmap_str_to_voidptr_hashmap_set(map_methods, "clear", map_clear);
+
+ hashmap_str_to_voidptr_hashmap_set(context->builtin_methods, "map", map_methods);
+}
\ No newline at end of file
diff --git a/runtime/ailinterpreter/byte_list.c b/runtime/ailinterpreter/byte_list.c
new file mode 100644
index 0000000..d06b86e
--- /dev/null
+++ b/runtime/ailinterpreter/byte_list.c
@@ -0,0 +1,94 @@
+#include
+
+#include "byte_list.h"
+
+ASPL_AILI_ByteList* aspl_ailinterpreter_bytelist_clone(ASPL_AILI_ByteList* old) {
+ ASPL_AILI_ByteList* new = ASPL_MALLOC(sizeof(ASPL_AILI_ByteList));
+ *new = *old;
+ return new;
+}
+
+unsigned char aspl_ailinterpreter_read_byte(ASPL_AILI_ByteList* bytes) {
+ return bytes->data[bytes->position++];
+}
+
+unsigned char aspl_ailinterpreter_peek_byte(ASPL_AILI_ByteList* bytes) {
+ return bytes->data[bytes->position];
+}
+
+ASPL_AILI_Instruction aspl_ailinterpreter_fetch_instruction(ASPL_AILI_ByteList* bytes) {
+ return aspl_ailinterpreter_read_byte(bytes);
+}
+
+ASPL_AILI_Instruction aspl_ailinterpreter_peek_instruction(ASPL_AILI_ByteList* bytes) {
+ return aspl_ailinterpreter_peek_byte(bytes);
+}
+
+unsigned char aspl_ailinterpreter_read_bool(ASPL_AILI_ByteList* bytes) {
+ return bytes->data[bytes->position++];
+}
+
+short aspl_ailinterpreter_read_short(ASPL_AILI_ByteList* bytes) {
+ short result = 0;
+ for (int i = 0; i < 2; i++) {
+ result |= (unsigned short)aspl_ailinterpreter_read_byte(bytes) << (8 * i);
+ }
+ return result;
+}
+
+int aspl_ailinterpreter_read_int(ASPL_AILI_ByteList* bytes) {
+ int result = 0;
+ for (int i = 0; i < 4; i++) {
+ result |= (unsigned int)aspl_ailinterpreter_read_byte(bytes) << (8 * i);
+ }
+ return result;
+}
+
+long long aspl_ailinterpreter_read_long(ASPL_AILI_ByteList* bytes) {
+ long long result = 0;
+ for (int i = 0; i < 8; i++) {
+ unsigned char byte = aspl_ailinterpreter_read_byte(bytes);
+ result |= ((unsigned long long)byte) << (8 * i);
+ }
+ return result;
+}
+
+float aspl_ailinterpreter_read_float(ASPL_AILI_ByteList* bytes) {
+ uint32_t integerResult = 0;
+ for (int i = 0; i < 4; i++) {
+ integerResult |= ((uint32_t)aspl_ailinterpreter_read_byte(bytes)) << (8 * i);
+ }
+ float result;
+ memcpy(&result, &integerResult, sizeof(float)); // use memcpy to avoid endianess issues
+ return result;
+}
+
+double aspl_ailinterpreter_read_double(ASPL_AILI_ByteList* bytes) {
+ uint64_t integerResult = 0;
+ for (int i = 0; i < 8; i++) {
+ integerResult |= ((uint64_t)aspl_ailinterpreter_read_byte(bytes)) << (8 * i);
+ }
+ double result;
+ memcpy(&result, &integerResult, sizeof(double)); // use memcpy to avoid endianess issues
+ return result;
+}
+
+char* aspl_ailinterpreter_read_short_string(ASPL_AILI_ByteList* bytes) {
+ short length = aspl_ailinterpreter_read_short(bytes);
+ char* result = ASPL_MALLOC(sizeof(char) * (length + 1));
+ for (int i = 0; i < length; i++) {
+ result[i] = aspl_ailinterpreter_read_byte(bytes);
+ }
+ result[length] = '\0';
+ return result;
+}
+
+char* aspl_ailinterpreter_read_long_string(ASPL_AILI_ByteList* bytes) {
+ long long length = aspl_ailinterpreter_read_long(bytes);
+ char* result = ASPL_MALLOC(sizeof(char) * (length + 1));
+ for (int i = 0; i < length; i++) {
+ result[i] = aspl_ailinterpreter_read_byte(bytes);
+ }
+ result[length] = '\0';
+ return result;
+}
\ No newline at end of file
diff --git a/runtime/ailinterpreter/byte_list.h b/runtime/ailinterpreter/byte_list.h
new file mode 100644
index 0000000..b161324
--- /dev/null
+++ b/runtime/ailinterpreter/byte_list.h
@@ -0,0 +1,38 @@
+#ifndef ASPL_AILI_BYTE_LIST_H_INCLUDED
+#define ASPL_AILI_BYTE_LIST_H_INCLUDED
+
+#include "instructions.h"
+
+typedef struct ASPL_AILI_ByteList {
+ long long size;
+ long long position;
+ unsigned char* data;
+} ASPL_AILI_ByteList;
+
+ASPL_AILI_ByteList* aspl_ailinterpreter_bytelist_clone(ASPL_AILI_ByteList* old);
+
+unsigned char aspl_ailinterpreter_read_byte(ASPL_AILI_ByteList* bytes);
+
+unsigned char aspl_ailinterpreter_peek_byte(ASPL_AILI_ByteList* bytes);
+
+ASPL_AILI_Instruction aspl_ailinterpreter_fetch_instruction(ASPL_AILI_ByteList* bytes);
+
+ASPL_AILI_Instruction aspl_ailinterpreter_peek_instruction(ASPL_AILI_ByteList* bytes);
+
+unsigned char aspl_ailinterpreter_read_bool(ASPL_AILI_ByteList* bytes);
+
+short aspl_ailinterpreter_read_short(ASPL_AILI_ByteList* bytes);
+
+int aspl_ailinterpreter_read_int(ASPL_AILI_ByteList* bytes);
+
+long long aspl_ailinterpreter_read_long(ASPL_AILI_ByteList* bytes);
+
+float aspl_ailinterpreter_read_float(ASPL_AILI_ByteList* bytes);
+
+double aspl_ailinterpreter_read_double(ASPL_AILI_ByteList* bytes);
+
+char* aspl_ailinterpreter_read_short_string(ASPL_AILI_ByteList* bytes);
+
+char* aspl_ailinterpreter_read_long_string(ASPL_AILI_ByteList* bytes);
+
+#endif
\ No newline at end of file
diff --git a/runtime/ailinterpreter/implementations.c b/runtime/ailinterpreter/implementations.c
new file mode 100644
index 0000000..2e4db8f
--- /dev/null
+++ b/runtime/ailinterpreter/implementations.c
@@ -0,0 +1,35 @@
+#ifndef ASPL_AILI_NO_STDLIB
+#ifndef ASPL_AILI_BUNDLED // include all default stdlib implementations when manually invoking the interpreter
+// TODO: This won't work as the implementation calls need certain wrappers
+#endif
+#endif
+
+#ifdef _WIN32
+#include
+#define aspl_ailinterpreter_dl_self NULL
+#define aspl_ailinterpreter_dl_sym(handle, symbol) GetProcAddress(handle, symbol)
+#else
+#include
+#define aspl_ailinterpreter_dl_self RTLD_DEFAULT // TODO: This might not be available on all platforms
+#define aspl_ailinterpreter_dl_sym(handle, symbol) dlsym(handle, symbol)
+#endif
+
+typedef ASPL_OBJECT_TYPE(*ASPL_AILI_ImplementationPtr)(ASPL_AILI_ArgumentList arguments);
+
+ASPL_OBJECT_TYPE aspl_ailinterpreter_implement(ASPL_AILI_ThreadContext* thread_context, char* call, ASPL_AILI_ArgumentList args) {
+ char* symbol = ASPL_MALLOC(strlen("aspl_ailinterpreter_implementation_") + strlen(call) + 1);
+ strcat(symbol, "aspl_ailinterpreter_implementation_");
+ int iBeforeCall = strlen(symbol);
+ strcat(symbol, call);
+ for (int i = iBeforeCall; i < strlen(symbol); i++) {
+ if (symbol[i] == '.') {
+ symbol[i] = '$';
+ }
+ }
+ symbol[strlen("aspl_ailinterpreter_implementation_") + strlen(call)] = '\0';
+ ASPL_AILI_ImplementationPtr cb = (ASPL_AILI_ImplementationPtr)aspl_ailinterpreter_dl_sym(aspl_ailinterpreter_dl_self, symbol);
+ if (cb == NULL) {
+ ASPL_PANIC("Cannot implement unknown implementation call %s", call);
+ }
+ return cb(args);
+}
\ No newline at end of file
diff --git a/runtime/ailinterpreter/instructions.h b/runtime/ailinterpreter/instructions.h
new file mode 100644
index 0000000..feeed75
--- /dev/null
+++ b/runtime/ailinterpreter/instructions.h
@@ -0,0 +1,72 @@
+#ifndef ASPL_AILI_INSTRUCTION_H_INCLUDED
+#define ASPL_AILI_INSTRUCTION_H_INCLUDED
+
+typedef enum ASPL_AILI_Instruction {
+ ASPL_AILI_INSTRUCTION_MANIFEST,
+ ASPL_AILI_INSTRUCTION_CREATE_OBJECT,
+ ASPL_AILI_INSTRUCTION_BYTE_ARRAY_LITERAL,
+ ASPL_AILI_INSTRUCTION_IMPLEMENT,
+ ASPL_AILI_INSTRUCTION_JUMP_RELATIVE,
+ ASPL_AILI_INSTRUCTION_JUMP_RELATIVE_IF_FALSE,
+ ASPL_AILI_INSTRUCTION_DECLARE_FUNCTION,
+ ASPL_AILI_INSTRUCTION_CALL_FUNCTION,
+ ASPL_AILI_INSTRUCTION_ADD,
+ ASPL_AILI_INSTRUCTION_INCREMENT,
+ ASPL_AILI_INSTRUCTION_SUBTRACT,
+ ASPL_AILI_INSTRUCTION_DECREMENT,
+ ASPL_AILI_INSTRUCTION_MULTIPLY,
+ ASPL_AILI_INSTRUCTION_DIVIDE,
+ ASPL_AILI_INSTRUCTION_MODULO,
+ ASPL_AILI_INSTRUCTION_SMALLER_THAN,
+ ASPL_AILI_INSTRUCTION_SMALLER_EQUAL,
+ ASPL_AILI_INSTRUCTION_GREATER_THAN,
+ ASPL_AILI_INSTRUCTION_GREATER_EQUAL,
+ ASPL_AILI_INSTRUCTION_EQUALS,
+ ASPL_AILI_INSTRUCTION_NOT_EQUALS,
+ ASPL_AILI_INSTRUCTION_BOOLEAN_AND,
+ ASPL_AILI_INSTRUCTION_BITWISE_AND,
+ ASPL_AILI_INSTRUCTION_BOOLEAN_OR,
+ ASPL_AILI_INSTRUCTION_BITWISE_OR,
+ ASPL_AILI_INSTRUCTION_BOOLEAN_XOR,
+ ASPL_AILI_INSTRUCTION_BITWISE_XOR,
+ ASPL_AILI_INSTRUCTION_DECLARE_VARIABLE,
+ ASPL_AILI_INSTRUCTION_ACCESS_VARIABLE,
+ ASPL_AILI_INSTRUCTION_UPDATE_VARIABLE,
+ ASPL_AILI_INSTRUCTION_DECLARE_METHOD,
+ ASPL_AILI_INSTRUCTION_CALL_METHOD,
+ ASPL_AILI_INSTRUCTION_CALL_EXACT_METHOD,
+ ASPL_AILI_INSTRUCTION_BREAK,
+ ASPL_AILI_INSTRUCTION_CONTINUE,
+ ASPL_AILI_INSTRUCTION_RETURN,
+ ASPL_AILI_INSTRUCTION_FALLBACK,
+ ASPL_AILI_INSTRUCTION_ESCAPE,
+ ASPL_AILI_INSTRUCTION_NEGATE,
+ ASPL_AILI_INSTRUCTION_COUNT_COLLECTION,
+ ASPL_AILI_INSTRUCTION_ACCESS_COLLECTION_KEY_WITH_INDEX,
+ ASPL_AILI_INSTRUCTION_ACCESS_COLLECTION_VALUE_WITH_INDEX,
+ ASPL_AILI_INSTRUCTION_INDEX_COLLECTION,
+ ASPL_AILI_INSTRUCTION_UPDATE_COLLECTION,
+ ASPL_AILI_INSTRUCTION_DECLARE_CALLBACK,
+ ASPL_AILI_INSTRUCTION_DECLARE_CLASS,
+ ASPL_AILI_INSTRUCTION_NEW,
+ ASPL_AILI_INSTRUCTION_THIS,
+ ASPL_AILI_INSTRUCTION_DECLARE_PROPERTY,
+ ASPL_AILI_INSTRUCTION_ACCESS_PROPERTY,
+ ASPL_AILI_INSTRUCTION_UPDATE_PROPERTY,
+ ASPL_AILI_INSTRUCTION_DECLARE_ENUM,
+ ASPL_AILI_INSTRUCTION_ENUM_FIELD,
+ ASPL_AILI_INSTRUCTION_DECLARE_POINTER,
+ ASPL_AILI_INSTRUCTION_DEREFERENCE_POINTER,
+ ASPL_AILI_INSTRUCTION_CAST,
+ ASPL_AILI_INSTRUCTION_ASSERT,
+ ASPL_AILI_INSTRUCTION_OFTYPE,
+ ASPL_AILI_INSTRUCTION_SPECIFY_LOOP,
+ ASPL_AILI_INSTRUCTION_POP_LOOP,
+ ASPL_AILI_INSTRUCTION_THROW,
+ ASPL_AILI_INSTRUCTION_CATCH,
+ ASPL_AILI_INSTRUCTION_PROPAGATE_ERROR,
+ ASPL_AILI_INSTRUCTION_POP,
+ ASPL_AILI_INSTRUCTION_END,
+} ASPL_AILI_Instruction;
+
+#endif
\ No newline at end of file
diff --git a/runtime/ailinterpreter/interpreter.c b/runtime/ailinterpreter/interpreter.c
new file mode 100644
index 0000000..c588bf8
--- /dev/null
+++ b/runtime/ailinterpreter/interpreter.c
@@ -0,0 +1,1779 @@
+#include "interpreter.h"
+
+#include "byte_list.c"
+#include "builtins.c"
+#include "implementations.c"
+
+ASPL_AILI_EnvironmentContext* aspl_ailinterpreter_new_environment_context() {
+ ASPL_AILI_EnvironmentContext* environment_context = ASPL_MALLOC(sizeof(ASPL_AILI_EnvironmentContext));
+
+ environment_context->functions = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 64 });
+ environment_context->builtin_functions = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 8 });
+ aspl_ailinterpreter_initialize_builtin_functions(environment_context);
+ environment_context->custom_functions = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 64 });
+
+ environment_context->builtin_methods = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 8 });
+ aspl_ailinterpreter_initialize_builtin_methods(environment_context);
+ environment_context->custom_methods = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 64 });
+
+ environment_context->classes = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 64 });
+ environment_context->enums = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 64 });
+
+ return environment_context;
+}
+
+ASPL_AILI_ThreadContext* aspl_ailinterpreter_new_thread_context(ASPL_AILI_EnvironmentContext* environment_context) {
+ ASPL_AILI_ThreadContext* thread_context = ASPL_MALLOC(sizeof(ASPL_AILI_ThreadContext));
+ thread_context->environment_context = environment_context;
+
+ thread_context->stack = ASPL_MALLOC(sizeof(ASPL_AILI_Stack));
+ thread_context->stack->frames = ASPL_MALLOC(sizeof(ASPL_AILI_StackFrame) * 1024);
+ thread_context->stack->frames_size = 1024;
+ thread_context->stack->current_frame_index = -1; // push_frame will increment this to 0
+ aspl_ailinterpreter_stack_push_frame(thread_context->stack);
+
+ thread_context->callable_invocation_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_CallableInvocationMetaStack));
+ thread_context->callable_invocation_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_CallableInvocationMeta) * 1024);
+ thread_context->callable_invocation_meta_stack->top = 0;
+
+ thread_context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack));
+ thread_context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024);
+ thread_context->loop_meta_stack->top = 0;
+
+ thread_context->class_stack = ASPL_MALLOC(sizeof(ASPL_AILI_ClassStack));
+ thread_context->class_stack->data = ASPL_MALLOC(sizeof(char*) * 8);
+ thread_context->class_stack->top = 0;
+
+ thread_context->scopes_top = -1; // push_scope will increment this to 0
+ thread_context->scopes_size = 128;
+ thread_context->scopes = ASPL_MALLOC(sizeof(hashmap_str_to_voidptr_HashMap*) * thread_context->scopes_size);
+ aspl_ailinterpreter_push_scope(thread_context);
+
+ thread_context->current_instance = ASPL_NULL();
+
+ thread_context->local_static_property_values = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 64 });
+ for (int i = 0; i < environment_context->classes->len; i++) {
+ char* class_name = environment_context->classes->pairs[i]->key;
+ hashmap_str_to_voidptr_hashmap_set(thread_context->local_static_property_values, class_name, hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 64 }));
+ ASPL_AILI_Class* class = environment_context->classes->pairs[i]->value;
+ for (int j = 0; j < class->default_threadlocal_static_property_values->len; j++) {
+ char* property_name = class->default_threadlocal_static_property_values->pairs[j]->key;
+ ASPL_OBJECT_TYPE* property_value = class->default_threadlocal_static_property_values->pairs[j]->value;
+ hashmap_str_to_voidptr_hashmap_set(hashmap_str_to_voidptr_hashmap_get_value(thread_context->local_static_property_values, class_name), property_name, property_value);
+ }
+ }
+
+ thread_context->stack_trace = ASPL_MALLOC(sizeof(ASPL_AILI_StackTrace));
+ thread_context->stack_trace->frames = ASPL_MALLOC(sizeof(char*) * 1024);
+ thread_context->stack_trace->size = 1024;
+ thread_context->stack_trace->top = 0;
+
+ return thread_context;
+}
+
+void aspl_ailinterpreter_stack_push_frame(ASPL_AILI_Stack* stack) {
+ if (stack->current_frame_index >= (stack->frames_size - 1)) {
+ stack->frames_size *= 2;
+ stack->frames = ASPL_REALLOC(stack->frames, sizeof(ASPL_AILI_StackFrame*) * stack->frames_size);
+ }
+ stack->frames[++stack->current_frame_index] = (ASPL_AILI_StackFrame){ .data = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * 64), .top = 0 };
+}
+
+void aspl_ailinterpreter_stack_push(ASPL_AILI_Stack* stack, ASPL_OBJECT_TYPE object) {
+ stack->frames[stack->current_frame_index].data[stack->frames[stack->current_frame_index].top++] = object;
+}
+
+ASPL_AILI_StackFrame aspl_ailinterpreter_stack_pop_frame(ASPL_AILI_Stack* stack) {
+ if (stack->current_frame_index <= 0) ASPL_PANIC("Cannot pop frame from empty stack");
+ return stack->frames[--stack->current_frame_index];
+}
+
+ASPL_OBJECT_TYPE aspl_ailinterpreter_stack_pop(ASPL_AILI_Stack* stack) {
+ if (stack->frames[stack->current_frame_index].top <= 0) ASPL_PANIC("Cannot pop from empty stack frame");
+ return stack->frames[stack->current_frame_index].data[--stack->frames[stack->current_frame_index].top];
+}
+
+ASPL_OBJECT_TYPE aspl_ailinterpreter_stack_peek(ASPL_AILI_Stack* stack) {
+ if (stack->frames[stack->current_frame_index].top <= 0) ASPL_PANIC("Cannot peek from empty stack frame");
+ return stack->frames[stack->current_frame_index].data[stack->frames[stack->current_frame_index].top - 1];
+}
+
+void aspl_ailinterpreter_cims_push(ASPL_AILI_CallableInvocationMetaStack* stack, ASPL_AILI_CallableInvocationMeta value) {
+ stack->data[stack->top++] = value;
+}
+
+ASPL_AILI_CallableInvocationMeta aspl_ailinterpreter_cims_pop(ASPL_AILI_CallableInvocationMetaStack* stack) {
+ if (stack->top <= 0) ASPL_PANIC("Cannot pop from empty callable invocation meta stack");
+ return stack->data[--stack->top];
+}
+
+ASPL_AILI_CallableInvocationMeta aspl_ailinterpreter_cims_peek(ASPL_AILI_CallableInvocationMetaStack* stack) {
+ if (stack->top <= 0) ASPL_PANIC("Cannot peek from empty callable invocation meta stack");
+ return stack->data[stack->top - 1];
+}
+
+void aspl_ailinterpreter_lms_push(ASPL_AILI_LoopMetaStack* stack, ASPL_AILI_LoopMeta value) {
+ stack->data[stack->top++] = value;
+}
+
+ASPL_AILI_LoopMeta aspl_ailinterpreter_lms_pop(ASPL_AILI_LoopMetaStack* stack) {
+ if (stack->top <= 0) ASPL_PANIC("Cannot pop from empty loop meta stack");
+ return stack->data[--stack->top];
+}
+
+ASPL_AILI_LoopMeta aspl_ailinterpreter_lms_peek(ASPL_AILI_LoopMetaStack* stack) {
+ if (stack->top <= 0) ASPL_PANIC("Cannot peek from empty loop meta stack");
+ return stack->data[stack->top - 1];
+}
+
+void aspl_ailinterpreter_class_stack_push(ASPL_AILI_ClassStack* stack, char* object) {
+ stack->data[stack->top++] = object;
+}
+
+char* aspl_ailinterpreter_class_stack_pop(ASPL_AILI_ClassStack* stack) {
+ if (stack->top <= 0) ASPL_PANIC("Cannot pop from empty class stack");
+ return stack->data[--stack->top];
+}
+
+void aspl_ailinterpreter_push_scope(ASPL_AILI_ThreadContext* context) {
+ if (context->scopes_top >= (context->scopes_size - 1)) {
+ context->scopes_size *= 2;
+ context->scopes = ASPL_REALLOC(context->scopes, sizeof(hashmap_str_to_voidptr_HashMap*) * context->scopes_size);
+ }
+ context->scopes[++context->scopes_top] = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 64 });
+}
+
+void aspl_ailinterpreter_pop_scope(ASPL_AILI_ThreadContext* context) {
+ context->scopes_top--;
+}
+
+void aspl_ailinterpreter_register_variable(ASPL_AILI_ThreadContext* context, char* identifier, ASPL_OBJECT_TYPE value) {
+ hashmap_str_to_voidptr_hashmap_set(context->scopes[context->scopes_top], identifier, C_REFERENCE(value));
+}
+
+ASPL_OBJECT_TYPE* aspl_ailinterpreter_access_variable_address(ASPL_AILI_ThreadContext* context, char* identifier) {
+ for (int i = context->scopes_top; i >= 0; i--) {
+ if (hashmap_str_to_voidptr_hashmap_contains_key(context->scopes[i], identifier)) {
+ return hashmap_str_to_voidptr_hashmap_get_value(context->scopes[i], identifier);
+ }
+ }
+ ASPL_PANIC("Cannot access unregistered variable '%s'", identifier);
+}
+
+ASPL_OBJECT_TYPE aspl_ailinterpreter_access_variable(ASPL_AILI_ThreadContext* context, char* identifier) {
+ return *aspl_ailinterpreter_access_variable_address(context, identifier);
+}
+
+void aspl_ailinterpreter_register_class(ASPL_AILI_EnvironmentContext* context, char* identifier, ASPL_AILI_Class* class) {
+ hashmap_str_to_voidptr_hashmap_set(context->classes, identifier, class);
+}
+
+ASPL_AILI_Class* aspl_ailinterpreter_access_class(ASPL_AILI_EnvironmentContext* context, char* identifier) {
+ return hashmap_str_to_voidptr_hashmap_get_value(context->classes, identifier);
+}
+
+void aspl_ailinterpreter_stack_trace_push(ASPL_AILI_ThreadContext* context, char* identifier) {
+ if (context->stack_trace->top >= (context->stack_trace->size - 1)) {
+ context->stack_trace->size *= 2;
+ context->stack_trace->frames = ASPL_REALLOC(context->stack_trace->frames, sizeof(char*) * context->stack_trace->size);
+ }
+ context->stack_trace->frames[context->stack_trace->top++] = identifier;
+}
+
+char* aspl_ailinterpreter_stack_trace_pop(ASPL_AILI_ThreadContext* context) {
+ if (context->stack_trace->top <= 0) ASPL_PANIC("Cannot pop from empty stack trace");
+ return context->stack_trace->frames[--context->stack_trace->top];
+}
+
+ASPL_AILI_TypeList aspl_ailinterpreter_parse_types(ASPL_AILI_ByteList* bytes) {
+ char** type_list = ASPL_MALLOC(sizeof(char*) * 8);
+ int type_list_size = 8;
+ int type_list_top = 0;
+ int type_list_length = aspl_ailinterpreter_read_short(bytes);
+ while (type_list_top < type_list_length) {
+ while (type_list_top >= type_list_size) {
+ type_list_size *= 2;
+ type_list = ASPL_REALLOC(type_list, sizeof(char*) * type_list_size);
+ }
+ type_list[type_list_top++] = aspl_ailinterpreter_read_short_string(bytes);
+ }
+ if (type_list_top == 0) {
+ type_list[type_list_top++] = "any";
+ }
+ return (ASPL_AILI_TypeList) { .types = type_list, .size = type_list_top };
+}
+
+ASPL_AILI_ParameterList aspl_ailinterpreter_parse_parameters(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes) {
+ ASPL_AILI_Parameter* parameters = ASPL_MALLOC(sizeof(ASPL_AILI_Parameter) * 8);
+ int parameters_size = 8;
+ int parameters_top = 0;
+ int parameters_length = aspl_ailinterpreter_read_short(bytes);
+ ASPL_OBJECT_TYPE* default_values = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * 8);
+ int default_values_size = 8;
+ int default_values_top = 0;
+ while (parameters_top < parameters_length) {
+ while (parameters_top >= parameters_size) {
+ parameters_size *= 2;
+ parameters = ASPL_REALLOC(parameters, sizeof(ASPL_AILI_Parameter) * parameters_size);
+ }
+ char* name = aspl_ailinterpreter_read_short_string(bytes);
+ ASPL_AILI_TypeList expected_types = aspl_ailinterpreter_parse_types(bytes);
+ int optional = aspl_ailinterpreter_read_bool(bytes);
+ if (optional) {
+ ASPL_OBJECT_TYPE default_value = aspl_ailinterpreter_stack_pop(context->stack);
+ while (default_values_top >= default_values_size) {
+ default_values_size *= 2;
+ default_values = ASPL_REALLOC(default_values, sizeof(ASPL_OBJECT_TYPE) * default_values_size);
+ }
+ default_values[default_values_top++] = default_value;
+ parameters[parameters_top++] = (ASPL_AILI_Parameter){ .name = name, .expected_types = expected_types, .optional = optional };
+ }
+ else {
+ parameters[parameters_top++] = (ASPL_AILI_Parameter){ .name = name, .expected_types = expected_types, .optional = optional };
+ }
+ }
+ for (int i = 0; i < parameters_top; i++) {
+ if (parameters[i].optional) {
+ parameters[i].default_value = default_values[--default_values_top];
+ }
+ }
+ return (ASPL_AILI_ParameterList) { .parameters = parameters, .size = parameters_top };
+}
+
+char* aspl_ailinterpreter_construct_list_from_type_list(ASPL_AILI_TypeList type_list) {
+ int length = 5; // "list<"
+ for (int i = 0; i < type_list.size; i++) {
+ length += strlen(type_list.types[i]);
+ if (i < type_list.size - 1) {
+ length += 1; // "|"
+ }
+ }
+ length += 1; // ">"
+ char* result = ASPL_MALLOC(sizeof(char) * (length + 1));
+ result[0] = 'l';
+ result[1] = 'i';
+ result[2] = 's';
+ result[3] = 't';
+ result[4] = '<';
+ int pos = 5;
+ for (int i = 0; i < type_list.size; i++) {
+ for (int j = 0; j < strlen(type_list.types[i]); j++) {
+ result[pos++] = type_list.types[i][j];
+ }
+ if (i < type_list.size - 1) {
+ result[pos++] = '|';
+ }
+ }
+ result[pos++] = '>';
+ result[pos] = '\0';
+ return result;
+}
+
+char* aspl_ailinterpreter_construct_map_from_type_lists(ASPL_AILI_TypeList key_type_list, ASPL_AILI_TypeList value_type_list) {
+ int length = 4; // "map<"
+ for (int i = 0; i < key_type_list.size; i++) {
+ length += strlen(key_type_list.types[i]);
+ if (i < key_type_list.size - 1) {
+ length += 1; // "|"
+ }
+ }
+ length += 2; // ", "
+ for (int i = 0; i < value_type_list.size; i++) {
+ length += strlen(value_type_list.types[i]);
+ if (i < value_type_list.size - 1) {
+ length += 1; // "|"
+ }
+ }
+ length += 1; // ">"
+ char* result = ASPL_MALLOC(sizeof(char) * (length + 1));
+ result[0] = 'm';
+ result[1] = 'a';
+ result[2] = 'p';
+ result[3] = '<';
+ int pos = 4;
+ for (int i = 0; i < key_type_list.size; i++) {
+ for (int j = 0; j < strlen(key_type_list.types[i]); j++) {
+ result[pos++] = key_type_list.types[i][j];
+ }
+ if (i < key_type_list.size - 1) {
+ result[pos++] = '|';
+ }
+ }
+ result[pos++] = ',';
+ result[pos++] = ' ';
+ for (int i = 0; i < value_type_list.size; i++) {
+ for (int j = 0; j < strlen(value_type_list.types[i]); j++) {
+ result[pos++] = value_type_list.types[i][j];
+ }
+ if (i < value_type_list.size - 1) {
+ result[pos++] = '|';
+ }
+ }
+ result[pos++] = '>';
+ result[pos] = '\0';
+ return result;
+}
+
+void aspl_ailinterpreter_return(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, ASPL_OBJECT_TYPE value) {
+#ifdef ASPL_INTERPRETER_FIND_STACK_LEAKS
+ if (context->stack->frames[context->stack->current_frame_index].top != 0) {
+ ASPL_PANIC("Leaked stack object(s) found!\n");
+ }
+#endif
+ aspl_ailinterpreter_stack_pop_frame(context->stack);
+ ASPL_AILI_CallableInvocationMeta meta = aspl_ailinterpreter_cims_pop(context->callable_invocation_meta_stack);
+ if (meta.is_reactive_property_setter) {
+ aspl_ailinterpreter_stack_push(context->stack, aspl_ailinterpreter_access_variable(context, "value"));
+ }
+ else if (!meta.is_constructor) {
+ aspl_ailinterpreter_stack_push(context->stack, value);
+ }
+ if (meta.type == ASPL_AILINTERPRETER_CALLABLE_TYPE_CALLBACK) {
+ context->scopes = meta.previous_scopes;
+ context->scopes_top = meta.previous_scopes_top;
+ }
+ else {
+ aspl_ailinterpreter_pop_scope(context);
+ }
+ if (meta.previous_instance != NULL) {
+ context->current_instance = *meta.previous_instance;
+ }
+ context->loop_meta_stack = meta.previous_loop_meta_stack;
+ aspl_ailinterpreter_stack_trace_pop(context);
+ bytes->position = meta.previous_address;
+}
+
+ASPL_AILI_FunctionPtr aspl_ailinterpreter_get_function(ASPL_AILI_ThreadContext* context, char* identifier) {
+ return hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->functions, identifier);
+}
+
+void aspl_ailinterpreter_custom_function_callback(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, char* identifier, ASPL_AILI_ArgumentList arguments) {
+ ASPL_AILI_CustomFunction* custom_function = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->custom_functions, identifier);
+ bytes->position = custom_function->address;
+ for (int i = 0; i < custom_function->parameters.size; i++) {
+ if (i < arguments.size) {
+ aspl_ailinterpreter_register_variable(context, custom_function->parameters.parameters[i].name, arguments.arguments[i]);
+ }
+ else if (custom_function->parameters.parameters[i].optional) {
+ aspl_ailinterpreter_register_variable(context, custom_function->parameters.parameters[i].name, aspl_object_clone_shallow(custom_function->parameters.parameters[i].default_value));
+ }
+ else {
+ ASPL_PANIC("Missing argument for parameter '%s'", custom_function->parameters.parameters[i].name);
+ }
+ }
+}
+
+ASPL_AILI_CustomMethod* aspl_ailinterpreter_util_find_custom_method(ASPL_AILI_ThreadContext* context, char* type, char* name) {
+ if (hashmap_str_to_voidptr_hashmap_contains_key(context->environment_context->custom_methods, type)) {
+ hashmap_str_to_voidptr_HashMap* methods = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->custom_methods, type);
+ if (hashmap_str_to_voidptr_hashmap_contains_key(methods, name)) {
+ return hashmap_str_to_voidptr_hashmap_get_value(methods, name);
+ }
+ }
+ ASPL_AILI_Class* class = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->classes, type);
+ for (int i = 0; i < class->parents_size; i++) {
+ ASPL_AILI_CustomMethod* method = aspl_ailinterpreter_util_find_custom_method(context, class->parents[i], name);
+ if (method != NULL) {
+ return method;
+ }
+ }
+ return NULL;
+}
+
+ASPL_AILI_ReactiveProperty* aspl_ailinterpreter_util_find_reactive_property(ASPL_AILI_ThreadContext* context, char* type, char* name) {
+ if (hashmap_str_to_voidptr_hashmap_contains_key(aspl_ailinterpreter_access_class(context->environment_context, type)->reactive_properties, name)) {
+ return hashmap_str_to_voidptr_hashmap_get_value(aspl_ailinterpreter_access_class(context->environment_context, type)->reactive_properties, name);
+ }
+ ASPL_AILI_Class* class = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->classes, type);
+ for (int i = 0; i < class->parents_size; i++) {
+ ASPL_AILI_ReactiveProperty* property = aspl_ailinterpreter_util_find_reactive_property(context, class->parents[i], name);
+ if (property != NULL) {
+ return property;
+ }
+ }
+ return NULL;
+}
+
+char aspl_ailinterpreter_util_is_class_child_of(ASPL_AILI_ThreadContext* context, char* child, char* parent) {
+ if (strcmp(child, parent) == 0) {
+ return 1;
+ }
+ ASPL_AILI_Class* class = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->classes, child);
+ for (int i = 0; i < class->parents_size; i++) {
+ if (aspl_ailinterpreter_util_is_class_child_of(context, class->parents[i], parent)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void aspl_ailinterpreter_display_stack_trace(ASPL_AILI_ThreadContext* context) {
+ printf("Stack trace (most recent call first):\n");
+ for (int i = context->stack_trace->top - 1; i >= 0; i--) {
+ if (i == context->stack_trace->top - 1) {
+ printf(" > %s\n", context->stack_trace->frames[i]);
+ }
+ else {
+ printf(" %s\n", context->stack_trace->frames[i]);
+ }
+ }
+}
+
+_Thread_local ASPL_AILI_ThreadContext* aspl_ailinterpreter_current_thread_context;
+_Thread_local ASPL_AILI_ByteList* aspl_ailinterpreter_current_byte_list;
+
+void aspl_ailinterpreter_invoke_callback_from_outside_of_loop_internal(ASPL_Callback* callback, ASPL_AILI_ArgumentList arguments, ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes) {
+ char* identifier = ASPL_MALLOC(sizeof(char) * (strlen(callback->typePtr) + strlen("invoke") + 2));
+ strcpy(identifier, callback->typePtr);
+ strcat(identifier, ".");
+ strcat(identifier, "invoke");
+ aspl_ailinterpreter_stack_trace_push(context, identifier);
+ aspl_ailinterpreter_stack_push_frame(context->stack);
+ ASPL_AILI_CallbackData* callback_data = (ASPL_AILI_CallbackData*)callback->function;
+ ASPL_AILI_CallableInvocationMetaStack* original_callable_invocation_meta_stack = context->callable_invocation_meta_stack;
+ ASPL_AILI_CallableInvocationMetaStack* callable_invocation_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_CallableInvocationMetaStack));
+ callable_invocation_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_CallableInvocationMeta) * 1024);
+ callable_invocation_meta_stack->top = 0;
+ context->callable_invocation_meta_stack = callable_invocation_meta_stack;
+ hashmap_str_to_voidptr_HashMap** original_scopes = context->scopes;
+ int original_scopes_top = context->scopes_top;
+ context->scopes = callback_data->creation_scopes;
+ context->scopes_top = callback_data->creation_scopes_top;
+ int original_position = bytes->position;
+ bytes->position = callback_data->address;
+ for (int i = 0; i < callback_data->parameters.size; i++) {
+ if (i < arguments.size) {
+ aspl_ailinterpreter_register_variable(context, callback_data->parameters.parameters[i].name, arguments.arguments[i]);
+ }
+ else if (callback_data->parameters.parameters[i].optional) {
+ aspl_ailinterpreter_register_variable(context, callback_data->parameters.parameters[i].name, aspl_object_clone_shallow(callback_data->parameters.parameters[i].default_value));
+ }
+ else {
+ ASPL_PANIC("Missing argument for parameter '%s'", callback_data->parameters.parameters[i].name);
+ }
+ }
+ aspl_ailinterpreter_loop(context, bytes);
+ bytes->position = original_position;
+ context->scopes = original_scopes;
+ context->scopes_top = original_scopes_top;
+ context->callable_invocation_meta_stack = original_callable_invocation_meta_stack;
+ aspl_ailinterpreter_stack_trace_pop(context);
+ aspl_ailinterpreter_stack_pop_frame(context->stack);
+}
+
+void aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_Callback* callback, ASPL_AILI_ArgumentList arguments) {
+ aspl_ailinterpreter_invoke_callback_from_outside_of_loop_internal(callback, arguments, aspl_ailinterpreter_current_thread_context, aspl_ailinterpreter_current_byte_list);
+}
+
+int aspl_ailinterpreter_new_thread_function_invocation_callback(void* arguments) {
+ struct GC_stack_base sb;
+ GC_get_stack_base(&sb);
+ GC_register_my_thread(&sb);
+
+ ASPL_AILI_ThreadFunctionWrapperData* data = (ASPL_AILI_ThreadFunctionWrapperData*)arguments;
+
+ aspl_ailinterpreter_current_thread_context = data->context;
+ aspl_ailinterpreter_current_byte_list = data->bytes;
+
+ aspl_ailinterpreter_stack_trace_push(data->context, data->identifier);
+ aspl_ailinterpreter_stack_push_frame(data->context->stack);
+ data->function(data->context, data->bytes, data->identifier, data->arguments);
+ aspl_ailinterpreter_loop(data->context, data->bytes);
+
+ GC_unregister_my_thread();
+ return 0;
+}
+
+int aspl_ailinterpreter_new_thread_method_invocation_callback(void* arguments) {
+ struct GC_stack_base sb;
+ GC_get_stack_base(&sb);
+ GC_register_my_thread(&sb);
+
+ ASPL_AILI_ThreadMethodWrapperData* data = (ASPL_AILI_ThreadMethodWrapperData*)arguments;
+
+ aspl_ailinterpreter_current_thread_context = data->context;
+ aspl_ailinterpreter_current_byte_list = data->bytes;
+
+ aspl_ailinterpreter_stack_trace_push(data->context, data->identifier);
+ aspl_ailinterpreter_stack_push_frame(data->context->stack);
+ ASPL_AILI_CustomMethod* custom_method = data->method;
+ data->bytes->position = custom_method->address;
+ for (int i = 0; i < custom_method->parameters.size; i++) {
+ if (i < data->arguments.size) {
+ aspl_ailinterpreter_register_variable(data->context, custom_method->parameters.parameters[i].name, data->arguments.arguments[i]);
+ }
+ else if (custom_method->parameters.parameters[i].optional) {
+ aspl_ailinterpreter_register_variable(data->context, custom_method->parameters.parameters[i].name, aspl_object_clone_shallow(custom_method->parameters.parameters[i].default_value));
+ }
+ else {
+ ASPL_PANIC("Missing argument for parameter '%s'", custom_method->parameters.parameters[i].name);
+ }
+ }
+ aspl_ailinterpreter_loop(data->context, data->bytes);
+
+ GC_unregister_my_thread();
+ return 0;
+}
+
+int aspl_ailinterpreter_new_thread_callback_invocation_callback(void* arguments) {
+ struct GC_stack_base sb;
+ GC_get_stack_base(&sb);
+ GC_register_my_thread(&sb);
+
+ ASPL_AILI_ThreadCallbackWrapperData* data = (ASPL_AILI_ThreadCallbackWrapperData*)arguments;
+
+ aspl_ailinterpreter_current_thread_context = data->context;
+ aspl_ailinterpreter_current_byte_list = data->bytes;
+
+ aspl_ailinterpreter_invoke_callback_from_outside_of_loop_internal(data->callback, data->arguments, data->context, data->bytes);
+
+ GC_unregister_my_thread();
+ return 0;
+}
+
+void aspl_ailinterpreter_loop(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes) {
+ while (1) {
+ ASPL_AILI_Instruction instruction = aspl_ailinterpreter_fetch_instruction(bytes);
+ switch (instruction) {
+ case ASPL_AILI_INSTRUCTION_MANIFEST: {
+ int length = aspl_ailinterpreter_read_int(bytes);
+ // Ignore the manifest for now...
+ bytes->position += length;
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_CREATE_OBJECT: {
+ char* type = aspl_ailinterpreter_read_short_string(bytes);
+ if (strcmp(type, "null") == 0) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_NULL());
+ }
+ else if (strcmp(type, "bool") == 0) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_BOOL_LITERAL(aspl_ailinterpreter_read_byte(bytes)));
+ }
+ else if (strcmp(type, "byte") == 0) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_BYTE_LITERAL(aspl_ailinterpreter_read_byte(bytes)));
+ }
+ else if (strcmp(type, "int") == 0) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_INT_LITERAL(aspl_ailinterpreter_read_int(bytes)));
+ }
+ else if (strcmp(type, "long") == 0) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_LONG_LITERAL(aspl_ailinterpreter_read_long(bytes)));
+ }
+ else if (strcmp(type, "float") == 0) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_FLOAT_LITERAL(aspl_ailinterpreter_read_float(bytes)));
+ }
+ else if (strcmp(type, "double") == 0) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_DOUBLE_LITERAL(aspl_ailinterpreter_read_double(bytes)));
+ }
+ else if (strcmp(type, "string") == 0) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_STRING_LITERAL(aspl_ailinterpreter_read_long_string(bytes)));
+ }
+ else if (strcmp(type, "list") == 0) {
+ ASPL_AILI_TypeList generic_types = aspl_ailinterpreter_parse_types(bytes);
+ char* type = aspl_ailinterpreter_construct_list_from_type_list(generic_types);
+ int length = aspl_ailinterpreter_read_int(bytes);
+ ASPL_OBJECT_TYPE* array = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * length);
+ for (int i = length - 1; i >= 0; i--) {
+ array[i] = aspl_ailinterpreter_stack_pop(context->stack);
+ }
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_LIST_LITERAL(type, strlen(type), array, length));
+ }
+ else if (strcmp(type, "map") == 0) {
+ ASPL_AILI_TypeList generic_key_types = aspl_ailinterpreter_parse_types(bytes);
+ ASPL_AILI_TypeList generic_value_types = aspl_ailinterpreter_parse_types(bytes);
+ char* type = aspl_ailinterpreter_construct_map_from_type_lists(generic_key_types, generic_value_types);
+ int length = aspl_ailinterpreter_read_int(bytes);
+ int initial_capacity = length;
+ if (initial_capacity < 1) initial_capacity = 1; // hashmap_new can't handle 0 as initial capacity
+ hashmap_aspl_object_to_aspl_object_HashMap* hashmap = hashmap_aspl_object_to_aspl_object_new_hashmap((hashmap_aspl_object_to_aspl_object_HashMapConfig) { .initial_capacity = initial_capacity });
+ for (int i = 0; i < length; i++) {
+ ASPL_OBJECT_TYPE value = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE key = aspl_ailinterpreter_stack_pop(context->stack);
+ hashmap_aspl_object_to_aspl_object_hashmap_set(hashmap, ASPL_HASHMAP_WRAP(key), ASPL_HASHMAP_WRAP(value));
+ }
+ hashmap = hashmap_aspl_object_to_aspl_object_hashmap_reverse(hashmap);
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_MAP;
+ ASPL_Map* m = ASPL_MALLOC(sizeof(ASPL_Map));
+ m->typePtr = type;
+ m->typeLen = strlen(type);
+ m->hashmap = hashmap;
+ ASPL_ACCESS(obj).value.map = m;
+ aspl_ailinterpreter_stack_push(context->stack, obj);
+ }
+ else {
+ ASPL_PANIC("Unknown type '%s'", type);
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_BYTE_ARRAY_LITERAL: {
+ long long length = aspl_ailinterpreter_read_long(bytes);
+ ASPL_OBJECT_TYPE* array = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * length);
+ for (int i = 0; i < length; i++) {
+ array[i] = ASPL_BYTE_LITERAL(aspl_ailinterpreter_read_byte(bytes));
+ }
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_LIST_LITERAL("list", 10, array, length));
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_IMPLEMENT: {
+ char* call = aspl_ailinterpreter_read_long_string(bytes);
+ int argc = aspl_ailinterpreter_read_int(bytes);
+ ASPL_AILI_ArgumentList arguments = { .arguments = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * argc), .size = argc };
+ for (int i = argc - 1; i >= 0; i--) {
+ arguments.arguments[i] = aspl_ailinterpreter_stack_pop(context->stack);
+ }
+ ASPL_OBJECT_TYPE value = aspl_ailinterpreter_implement(context, call, arguments);
+ if (!ASPL_IS_UNINITIALIZED(value)) {
+ aspl_ailinterpreter_stack_push(context->stack, value);
+ }
+ else {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_NULL());
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_DECLARE_VARIABLE: {
+ char* identifier = aspl_ailinterpreter_read_short_string(bytes);
+ ASPL_AILI_TypeList types = aspl_ailinterpreter_parse_types(bytes);
+ ASPL_OBJECT_TYPE value = aspl_ailinterpreter_stack_pop(context->stack);
+ aspl_ailinterpreter_register_variable(context, identifier, value);
+ aspl_ailinterpreter_stack_push(context->stack, value);
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_UPDATE_VARIABLE: {
+ char* identifier = aspl_ailinterpreter_read_short_string(bytes);
+ ASPL_OBJECT_TYPE value = aspl_ailinterpreter_stack_peek(context->stack);
+ ASPL_OBJECT_TYPE* address = aspl_ailinterpreter_access_variable_address(context, identifier);
+ *address = value;
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_ACCESS_VARIABLE: {
+ char* identifier = aspl_ailinterpreter_read_short_string(bytes);
+ ASPL_OBJECT_TYPE value = aspl_ailinterpreter_access_variable(context, identifier);
+ aspl_ailinterpreter_stack_push(context->stack, value);
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_JUMP_RELATIVE: {
+ long long offset = aspl_ailinterpreter_read_long(bytes);
+ bytes->position += offset;
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_JUMP_RELATIVE_IF_FALSE: {
+ long long offset = aspl_ailinterpreter_read_long(bytes);
+ if (!ASPL_IS_TRUE(aspl_ailinterpreter_stack_pop(context->stack))) {
+ bytes->position += offset;
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_CALL_FUNCTION: {
+ char* identifier = aspl_ailinterpreter_read_long_string(bytes);
+ int new_thread = aspl_ailinterpreter_read_bool(bytes);
+ int argc = aspl_ailinterpreter_read_int(bytes);
+ ASPL_AILI_ParameterList* function_parameters = NULL;
+ if (hashmap_str_to_voidptr_hashmap_contains_key(context->environment_context->builtin_functions, identifier)) {
+ ASPL_AILI_BuiltinFunction* builtin_function = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->builtin_functions, identifier);
+ function_parameters = &builtin_function->parameters;
+ }
+ else if (hashmap_str_to_voidptr_hashmap_contains_key(context->environment_context->custom_functions, identifier)) {
+ ASPL_AILI_CustomFunction* custom_function = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->custom_functions, identifier);
+ function_parameters = &custom_function->parameters;
+ }
+ int size = argc > function_parameters->size ? argc : function_parameters->size;
+ ASPL_AILI_ArgumentList arguments = { .arguments = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * size), .size = size };
+ for (int i = argc - 1; i >= 0; i--) {
+ arguments.arguments[i] = aspl_ailinterpreter_stack_pop(context->stack);
+ }
+ for (int i = argc; i < function_parameters->size; i++) {
+ if (function_parameters->parameters[i].optional) {
+ arguments.arguments[i] = aspl_object_clone_shallow(function_parameters->parameters[i].default_value);
+ }
+ else {
+ ASPL_PANIC("Missing argument for parameter '%s'", function_parameters->parameters[i].name);
+ }
+ }
+ ASPL_AILI_FunctionPtr function = aspl_ailinterpreter_get_function(context, identifier);
+ if (new_thread) {
+ ASPL_AILI_ThreadFunctionWrapperData* data = ASPL_MALLOC(sizeof(ASPL_AILI_ThreadFunctionWrapperData));
+ data->function = function;
+ data->context = aspl_ailinterpreter_new_thread_context(context->environment_context);
+ data->bytes = aspl_ailinterpreter_bytelist_clone(bytes);
+ data->identifier = identifier;
+ data->arguments = arguments;
+ thread_create(aspl_ailinterpreter_new_thread_function_invocation_callback, data, THREAD_STACK_SIZE_DEFAULT);
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_NULL());
+ }
+ else {
+ aspl_ailinterpreter_stack_trace_push(context, identifier);
+ aspl_ailinterpreter_stack_push_frame(context->stack);
+ aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, (ASPL_AILI_CallableInvocationMeta) { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_FUNCTION, .previous_address = bytes->position, .previous_loop_meta_stack = context->loop_meta_stack });
+ context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack));
+ context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024);
+ context->loop_meta_stack->top = 0;
+ aspl_ailinterpreter_push_scope(context);
+ function(context, bytes, identifier, arguments);
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_DECLARE_FUNCTION: {
+ char* identifier = aspl_ailinterpreter_read_short_string(bytes);
+ ASPL_AILI_ParameterList parameters = aspl_ailinterpreter_parse_parameters(context, bytes);
+ ASPL_AILI_TypeList return_types = aspl_ailinterpreter_parse_types(bytes);
+ ASPL_AILI_CustomFunction* custom_function = ASPL_MALLOC(sizeof(ASPL_AILI_CustomFunction));
+ custom_function->parameters = parameters;
+ long long code_length = aspl_ailinterpreter_read_long(bytes);
+ custom_function->address = bytes->position;
+ bytes->position += code_length;
+ hashmap_str_to_voidptr_hashmap_set(context->environment_context->custom_functions, identifier, custom_function);
+ hashmap_str_to_voidptr_hashmap_set(context->environment_context->functions, identifier, aspl_ailinterpreter_custom_function_callback);
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_DECLARE_METHOD: {
+ char is_static = aspl_ailinterpreter_read_bool(bytes);
+ char* name = aspl_ailinterpreter_read_short_string(bytes);
+ ASPL_AILI_ParameterList parameters = aspl_ailinterpreter_parse_parameters(context, bytes);
+ ASPL_AILI_TypeList return_types = aspl_ailinterpreter_parse_types(bytes);
+ ASPL_AILI_CustomMethod* custom_method = ASPL_MALLOC(sizeof(ASPL_AILI_CustomMethod));
+ custom_method->is_static = is_static;
+ custom_method->parameters = parameters;
+ long long code_length = aspl_ailinterpreter_read_long(bytes);
+ custom_method->address = bytes->position;
+ bytes->position += code_length;
+ hashmap_str_to_voidptr_HashMap* methods = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->custom_methods, context->current_class);
+ hashmap_str_to_voidptr_hashmap_set(methods, name, custom_method);
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_CALL_METHOD: {
+ char is_static = aspl_ailinterpreter_read_bool(bytes);
+ if (is_static) {
+ char* type = aspl_ailinterpreter_read_short_string(bytes);
+ char* name = aspl_ailinterpreter_read_short_string(bytes);
+ char new_thread = aspl_ailinterpreter_read_bool(bytes);
+ int argc = aspl_ailinterpreter_read_int(bytes);
+ ASPL_AILI_ParameterList* method_parameters = NULL;
+ // static builtin methods are not supported in ASPL yet
+ if (hashmap_str_to_voidptr_hashmap_contains_key(context->environment_context->custom_methods, type)) {
+ hashmap_str_to_voidptr_HashMap* methods = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->custom_methods, type);
+ if (hashmap_str_to_voidptr_hashmap_contains_key(methods, name)) {
+ ASPL_AILI_CustomMethod* method = hashmap_str_to_voidptr_hashmap_get_value(methods, name);
+ method_parameters = &method->parameters;
+ }
+ }
+ int size = argc > method_parameters->size ? argc : method_parameters->size;
+ ASPL_AILI_ArgumentList arguments = { .arguments = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * size), .size = size };
+ for (int i = argc - 1; i >= 0; i--) {
+ arguments.arguments[i] = aspl_ailinterpreter_stack_pop(context->stack);
+ }
+ for (int i = argc; i < method_parameters->size; i++) {
+ if (method_parameters->parameters[i].optional) {
+ arguments.arguments[i] = aspl_object_clone_shallow(method_parameters->parameters[i].default_value);
+ }
+ else {
+ ASPL_PANIC("Missing argument for parameter '%s'", method_parameters->parameters[i].name);
+ }
+ }
+ ASPL_AILI_CustomMethod* method = hashmap_str_to_voidptr_hashmap_get_value(hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->custom_methods, type), name);
+ char* identifier = ASPL_MALLOC(sizeof(char) * (strlen(type) + strlen(name) + 2));
+ strcpy(identifier, type);
+ strcat(identifier, ".");
+ strcat(identifier, name);
+ if (new_thread) {
+ ASPL_AILI_ThreadMethodWrapperData* data = ASPL_MALLOC(sizeof(ASPL_AILI_ThreadMethodWrapperData));
+ data->method = method;
+ data->context = aspl_ailinterpreter_new_thread_context(context->environment_context);
+ data->bytes = aspl_ailinterpreter_bytelist_clone(bytes);
+ data->identifier = identifier;
+ data->arguments = arguments;
+ thread_create(aspl_ailinterpreter_new_thread_method_invocation_callback, data, THREAD_STACK_SIZE_DEFAULT);
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_NULL());
+ }
+ else {
+ aspl_ailinterpreter_stack_trace_push(context, identifier);
+ aspl_ailinterpreter_stack_push_frame(context->stack);
+ ASPL_AILI_CallableInvocationMeta meta = { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_METHOD, .previous_address = bytes->position, .previous_loop_meta_stack = context->loop_meta_stack };
+ aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, meta);
+ context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack));
+ context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024);
+ context->loop_meta_stack->top = 0;
+ aspl_ailinterpreter_push_scope(context);
+ bytes->position = method->address;
+ for (int i = 0; i < method->parameters.size; i++) {
+ if (i < arguments.size) {
+ aspl_ailinterpreter_register_variable(context, method->parameters.parameters[i].name, arguments.arguments[i]);
+ }
+ else if (method->parameters.parameters[i].optional) {
+ aspl_ailinterpreter_register_variable(context, method->parameters.parameters[i].name, aspl_object_clone_shallow(method->parameters.parameters[i].default_value));
+ }
+ else {
+ ASPL_PANIC("Missing argument for parameter '%s'", method->parameters.parameters[i].name);
+ }
+ }
+ }
+ }
+ else {
+ char* name = aspl_ailinterpreter_read_short_string(bytes);
+ char new_thread = aspl_ailinterpreter_read_bool(bytes);
+ int argc = aspl_ailinterpreter_read_int(bytes);
+ ASPL_AILI_ArgumentList arguments = { .arguments = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * argc), .size = argc };
+ for (int i = argc - 1; i >= 0; i--) {
+ arguments.arguments[i] = aspl_ailinterpreter_stack_pop(context->stack);
+ }
+ ASPL_OBJECT_TYPE object = aspl_ailinterpreter_stack_pop(context->stack);
+ char isInvokeMethod = ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_CALLBACK && strcmp(name, "invoke") == 0;
+ if (!isInvokeMethod) {
+ ASPL_AILI_ParameterList* method_parameters = NULL;
+ if (hashmap_str_to_voidptr_hashmap_contains_key(context->environment_context->builtin_methods, aspl_object_get_short_type_pointer(object))) {
+ hashmap_str_to_voidptr_HashMap* methods = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->builtin_methods, aspl_object_get_short_type_pointer(object));
+ if (hashmap_str_to_voidptr_hashmap_contains_key(methods, name)) {
+ ASPL_AILI_BuiltinMethod* method = hashmap_str_to_voidptr_hashmap_get_value(methods, name);
+ method_parameters = &method->parameters;
+ }
+ }
+ if (method_parameters == NULL) {
+ if (hashmap_str_to_voidptr_hashmap_contains_key(context->environment_context->builtin_methods, "any")) {
+ hashmap_str_to_voidptr_HashMap* methods = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->builtin_methods, "any");
+ if (hashmap_str_to_voidptr_hashmap_contains_key(methods, name)) {
+ ASPL_AILI_BuiltinMethod* method = hashmap_str_to_voidptr_hashmap_get_value(methods, name);
+ method_parameters = &method->parameters;
+ }
+ }
+ }
+ if (method_parameters == NULL) {
+ method_parameters = &aspl_ailinterpreter_util_find_custom_method(context, aspl_object_get_short_type_pointer(object), name)->parameters;
+ }
+ if (method_parameters->size > argc) {
+ arguments.arguments = ASPL_REALLOC(arguments.arguments, sizeof(ASPL_OBJECT_TYPE) * method_parameters->size);
+ arguments.size = method_parameters->size;
+ }
+ for (int i = argc; i < method_parameters->size; i++) {
+ if (method_parameters->parameters[i].optional) {
+ arguments.arguments[i] = aspl_object_clone_shallow(method_parameters->parameters[i].default_value);
+ }
+ else {
+ ASPL_PANIC("Missing argument for parameter '%s'", method_parameters->parameters[i].name);
+ }
+ }
+ if (ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_NULL || ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_BOOLEAN || ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_BYTE || ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_INTEGER || ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_LONG || ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_FLOAT || ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_DOUBLE || ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_STRING || ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_LIST || ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_MAP) {
+ ASPL_OBJECT_TYPE** args = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * argc);
+ for (int i = 0; i < arguments.size; i++) {
+ args[i] = C_REFERENCE(arguments.arguments[i]);
+ }
+ aspl_ailinterpreter_stack_push(context->stack, aspl_object_method_invoke(object, name, args));
+ }
+ else {
+ ASPL_AILI_CustomMethod* method = aspl_ailinterpreter_util_find_custom_method(context, aspl_object_get_short_type_pointer(object), name);
+ char* identifier = ASPL_MALLOC(sizeof(char) * (strlen(aspl_object_get_short_type_pointer(object)) + strlen(name) + 2));
+ strcpy(identifier, aspl_object_get_short_type_pointer(object));
+ strcat(identifier, ".");
+ strcat(identifier, name);
+ if (new_thread) {
+ ASPL_AILI_ThreadMethodWrapperData* data = ASPL_MALLOC(sizeof(ASPL_AILI_ThreadMethodWrapperData));
+ data->method = method;
+ data->context = aspl_ailinterpreter_new_thread_context(context->environment_context);
+ data->bytes = aspl_ailinterpreter_bytelist_clone(bytes);
+ data->identifier = identifier;
+ data->arguments = arguments;
+ data->context->current_instance = object;
+ thread_create(aspl_ailinterpreter_new_thread_method_invocation_callback, data, THREAD_STACK_SIZE_DEFAULT);
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_NULL());
+ }
+ else {
+ aspl_ailinterpreter_stack_trace_push(context, identifier);
+ aspl_ailinterpreter_stack_push_frame(context->stack);
+ ASPL_AILI_CallableInvocationMeta meta = { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_METHOD, .previous_address = bytes->position, .previous_instance = C_REFERENCE(context->current_instance), .previous_loop_meta_stack = context->loop_meta_stack };
+ aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, meta);
+ context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack));
+ context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024);
+ context->loop_meta_stack->top = 0;
+ aspl_ailinterpreter_push_scope(context);
+ bytes->position = method->address;
+ if (ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_CLASS_INSTANCE) { // optimization
+ meta.previous_instance = C_REFERENCE(context->current_instance);
+ context->current_instance = object;
+ for (int i = 0; i < method->parameters.size; i++) {
+ if (i < arguments.size) {
+ aspl_ailinterpreter_register_variable(context, method->parameters.parameters[i].name, arguments.arguments[i]);
+ }
+ else if (method->parameters.parameters[i].optional) {
+ aspl_ailinterpreter_register_variable(context, method->parameters.parameters[i].name, aspl_object_clone_shallow(method->parameters.parameters[i].default_value));
+ }
+ else {
+ ASPL_PANIC("Missing argument for parameter '%s'", method->parameters.parameters[i].name);
+ }
+ }
+ }
+ }
+ }
+ }
+ else {
+ ASPL_Callback* callback = ASPL_ACCESS(object).value.callback;
+ char* identifier = ASPL_MALLOC(sizeof(char) * (strlen(callback->typePtr) + strlen(name) + 2));
+ strcpy(identifier, callback->typePtr);
+ strcat(identifier, ".");
+ strcat(identifier, name);
+ if (new_thread) {
+ ASPL_AILI_ThreadCallbackWrapperData* data = ASPL_MALLOC(sizeof(ASPL_AILI_ThreadCallbackWrapperData));
+ data->callback = callback;
+ data->context = aspl_ailinterpreter_new_thread_context(context->environment_context);
+ data->bytes = aspl_ailinterpreter_bytelist_clone(bytes);
+ data->identifier = identifier;
+ data->arguments = arguments;
+ thread_create(aspl_ailinterpreter_new_thread_callback_invocation_callback, data, THREAD_STACK_SIZE_DEFAULT);
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_NULL());
+ }
+ else {
+ aspl_ailinterpreter_stack_trace_push(context, identifier);
+ aspl_ailinterpreter_stack_push_frame(context->stack);
+ ASPL_AILI_CallbackData* callback_data = (ASPL_AILI_CallbackData*)callback->function;
+ aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, (ASPL_AILI_CallableInvocationMeta) { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_CALLBACK, .previous_address = bytes->position, .previous_scopes = context->scopes, .previous_scopes_top = context->scopes_top, .previous_loop_meta_stack = context->loop_meta_stack });
+ context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack));
+ context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024);
+ context->loop_meta_stack->top = 0;
+ context->scopes = callback_data->creation_scopes;
+ context->scopes_top = callback_data->creation_scopes_top;
+ bytes->position = callback_data->address;
+ for (int i = 0; i < callback_data->parameters.size; i++) {
+ if (i < arguments.size) {
+ aspl_ailinterpreter_register_variable(context, callback_data->parameters.parameters[i].name, arguments.arguments[i]);
+ }
+ else if (callback_data->parameters.parameters[i].optional) {
+ aspl_ailinterpreter_register_variable(context, callback_data->parameters.parameters[i].name, aspl_object_clone_shallow(callback_data->parameters.parameters[i].default_value));
+ }
+ else {
+ ASPL_PANIC("Missing argument for parameter '%s'", callback_data->parameters.parameters[i].name);
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_CALL_EXACT_METHOD: {
+ char* type = aspl_ailinterpreter_read_short_string(bytes);
+ char* name = aspl_ailinterpreter_read_short_string(bytes);
+ char new_thread = aspl_ailinterpreter_read_bool(bytes);
+ int argc = aspl_ailinterpreter_read_int(bytes);
+ hashmap_str_to_voidptr_HashMap* methods = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->custom_methods, type);
+ ASPL_AILI_CustomMethod* method = hashmap_str_to_voidptr_hashmap_get_value(methods, name);
+ ASPL_AILI_ParameterList* method_parameters = &method->parameters;
+ int size = argc > method_parameters->size ? argc : method_parameters->size;
+ ASPL_AILI_ArgumentList arguments = { .arguments = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * size), .size = size };
+ for (int i = argc - 1; i >= 0; i--) {
+ arguments.arguments[i] = aspl_ailinterpreter_stack_pop(context->stack);
+ }
+ for (int i = argc; i < method_parameters->size; i++) {
+ if (method_parameters->parameters[i].optional) {
+ arguments.arguments[i] = aspl_object_clone_shallow(method_parameters->parameters[i].default_value);
+ }
+ else {
+ ASPL_PANIC("Missing argument for parameter '%s'", method_parameters->parameters[i].name);
+ }
+ }
+ char* identifier = ASPL_MALLOC(sizeof(char) * (strlen(type) + strlen(name) + 2));
+ strcpy(identifier, type);
+ strcat(identifier, ".");
+ strcat(identifier, name);
+ ASPL_OBJECT_TYPE object = aspl_ailinterpreter_stack_pop(context->stack);
+ if (new_thread) {
+ ASPL_AILI_ThreadMethodWrapperData* data = ASPL_MALLOC(sizeof(ASPL_AILI_ThreadMethodWrapperData));
+ data->method = method;
+ data->context = aspl_ailinterpreter_new_thread_context(context->environment_context);
+ data->bytes = aspl_ailinterpreter_bytelist_clone(bytes);
+ data->identifier = identifier;
+ data->arguments = arguments;
+ data->context->current_instance = object;
+ thread_create(aspl_ailinterpreter_new_thread_method_invocation_callback, data, THREAD_STACK_SIZE_DEFAULT);
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_NULL());
+ }
+ else {
+ aspl_ailinterpreter_stack_trace_push(context, identifier);
+ aspl_ailinterpreter_stack_push_frame(context->stack);
+ ASPL_AILI_CallableInvocationMeta meta = { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_METHOD, .previous_address = bytes->position, .previous_loop_meta_stack = context->loop_meta_stack };
+ aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, meta);
+ context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack));
+ context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024);
+ context->loop_meta_stack->top = 0;
+ aspl_ailinterpreter_push_scope(context);
+ bytes->position = method->address;
+ meta.previous_instance = C_REFERENCE(context->current_instance);
+ context->current_instance = object;
+ for (int i = 0; i < method->parameters.size; i++) {
+ if (i < arguments.size) {
+ aspl_ailinterpreter_register_variable(context, method->parameters.parameters[i].name, arguments.arguments[i]);
+ }
+ else if (method->parameters.parameters[i].optional) {
+ aspl_ailinterpreter_register_variable(context, method->parameters.parameters[i].name, aspl_object_clone_shallow(method->parameters.parameters[i].default_value));
+ }
+ else {
+ ASPL_PANIC("Missing argument for parameter '%s'", method->parameters.parameters[i].name);
+ }
+ }
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_BREAK: {
+ char legacy = aspl_ailinterpreter_read_bool(bytes); // TODO: Remove this
+ int level = aspl_ailinterpreter_read_int(bytes); // TODO: Take this into account
+ bytes->position = aspl_ailinterpreter_lms_peek(context->loop_meta_stack).full_end_address;
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_CONTINUE: {
+ char legacy = aspl_ailinterpreter_read_bool(bytes); // TODO: Remove this
+ int level = aspl_ailinterpreter_read_int(bytes); // TODO: Take this into account
+ bytes->position = aspl_ailinterpreter_lms_peek(context->loop_meta_stack).body_end_address;
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_RETURN: {
+ aspl_ailinterpreter_return(context, bytes, aspl_ailinterpreter_stack_pop(context->stack));
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_FALLBACK: {
+ aspl_ailinterpreter_return(context, bytes, aspl_ailinterpreter_stack_pop(context->stack));
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_ESCAPE: {
+ ASPL_OBJECT_TYPE value = aspl_ailinterpreter_stack_pop(context->stack);
+ aspl_ailinterpreter_return(context, bytes, ASPL_UNINITIALIZED);
+ aspl_ailinterpreter_return(context, bytes, value);
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_DECLARE_ENUM: {
+ char* identifier = aspl_ailinterpreter_read_short_string(bytes);
+ int field_count = aspl_ailinterpreter_read_int(bytes);
+ ASPL_Enum* e = ASPL_MALLOC(sizeof(ASPL_Enum));
+ e->typePtr = identifier;
+ e->typeLen = strlen(identifier);
+ e->isFlagEnum = aspl_ailinterpreter_read_bool(bytes);
+ e->stringValues = hashmap_int_to_str_new_hashmap((hashmap_int_to_str_HashMapConfig) { .initial_capacity = field_count });
+ for (int i = 0; i < field_count; i++) {
+ char* field = aspl_ailinterpreter_read_short_string(bytes);
+ int value = aspl_ailinterpreter_read_int(bytes);
+ hashmap_int_to_str_hashmap_set(e->stringValues, value, field);
+ }
+ hashmap_str_to_voidptr_hashmap_set(context->environment_context->enums, identifier, e);
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_ENUM_FIELD: {
+ char* identifier = aspl_ailinterpreter_read_short_string(bytes);
+ char* string_field = aspl_ailinterpreter_read_short_string(bytes);
+ (void)string_field; // ignored in this implementation
+ int int_field = aspl_ailinterpreter_read_int(bytes);
+ ASPL_Enum* e = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->enums, identifier);
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_ENUM_FIELD_LITERAL(e, int_field));
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_DECLARE_POINTER: {
+ ASPL_OBJECT_TYPE* address;
+ switch (aspl_ailinterpreter_fetch_instruction(bytes)) {
+ case ASPL_AILI_INSTRUCTION_ACCESS_VARIABLE: {
+ address = aspl_ailinterpreter_access_variable_address(context, aspl_ailinterpreter_read_short_string(bytes));
+ break;
+ }
+ default:
+ ASPL_PANIC("Cannot declare a pointer to an invalid address");
+ }
+ char* to_type = aspl_object_get_type_pointer(*address);
+ char* type = ASPL_MALLOC(sizeof(char) * (strlen(to_type) + 1));
+ strcpy(type, to_type);
+ type[strlen(to_type)] = '*';
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_REFERENCE(type, strlen(type), address));
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_DEREFERENCE_POINTER: {
+ ASPL_OBJECT_TYPE pointer = aspl_ailinterpreter_stack_pop(context->stack);
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_DEREFERENCE(pointer));
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_DECLARE_CALLBACK: {
+ char* type = aspl_ailinterpreter_read_short_string(bytes);
+ ASPL_AILI_ParameterList parameters = aspl_ailinterpreter_parse_parameters(context, bytes);
+ ASPL_AILI_TypeList return_types = aspl_ailinterpreter_parse_types(bytes);
+ long long code_length = aspl_ailinterpreter_read_long(bytes);
+ ASPL_Callback* callback = ASPL_MALLOC(sizeof(ASPL_Callback));
+ callback->typeLen = strlen(type);
+ callback->typePtr = type;
+ callback->function = 0;
+ callback->function = ASPL_MALLOC(sizeof(ASPL_AILI_CallbackData)); // TODO: This is a nasty hack to avoid having to add a new field to the ASPL_Callback struct
+ ((ASPL_AILI_CallbackData*)callback->function)->address = bytes->position;
+ ((ASPL_AILI_CallbackData*)callback->function)->creation_scopes = memcpy(ASPL_MALLOC(context->scopes_size * sizeof(hashmap_str_to_voidptr_HashMap*)), context->scopes, context->scopes_size * sizeof(hashmap_str_to_voidptr_HashMap*));
+ ((ASPL_AILI_CallbackData*)callback->function)->creation_scopes_top = context->scopes_top;
+ ((ASPL_AILI_CallbackData*)callback->function)->parameters = parameters;
+ bytes->position += code_length;
+ ASPL_OBJECT_TYPE callback_object = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(callback_object).kind = ASPL_OBJECT_KIND_CALLBACK;
+ ASPL_ACCESS(callback_object).value.callback = callback;
+ aspl_ailinterpreter_stack_push(context->stack, callback_object);
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_CAST: {
+ char* target_type = aspl_ailinterpreter_read_short_string(bytes);
+ ASPL_OBJECT_TYPE object = aspl_ailinterpreter_stack_pop(context->stack);
+ char pushed = 0;
+ if (ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_INTEGER || ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_ENUM_FIELD) {
+ if (hashmap_str_to_voidptr_hashmap_contains_key(context->environment_context->enums, target_type)) {
+
+ pushed = 1;
+ switch (ASPL_ACCESS(object).kind)
+ {
+ case ASPL_OBJECT_KIND_INTEGER:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_ENUM_FIELD_LITERAL(hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->enums, target_type), ASPL_ACCESS(object).value.integer32));
+ break;
+ case ASPL_OBJECT_KIND_ENUM_FIELD:
+ aspl_ailinterpreter_stack_push(context->stack, object);
+ break;
+ }
+ }
+ }
+ if (!pushed) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_CAST(object, target_type));
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_COUNT_COLLECTION: {
+ ASPL_OBJECT_TYPE collection = aspl_ailinterpreter_stack_pop(context->stack);
+ switch (ASPL_ACCESS(collection).kind) {
+ case ASPL_OBJECT_KIND_LIST:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_LIST_LENGTH(collection));
+ break;
+ case ASPL_OBJECT_KIND_MAP:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_MAP_LENGTH(collection));
+ break;
+ case ASPL_OBJECT_KIND_STRING:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_STRING_LENGTH(collection));
+ break;
+ default:
+ ASPL_PANIC("Cannot count a non-collection object");
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_ACCESS_COLLECTION_KEY_WITH_INDEX: {
+ ASPL_OBJECT_TYPE index = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE collection = aspl_ailinterpreter_stack_pop(context->stack);
+ switch (ASPL_ACCESS(collection).kind) {
+ case ASPL_OBJECT_KIND_MAP:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_MAP_GET_KEY_FROM_INDEX(collection, index));
+ break;
+ default:
+ ASPL_PANIC("Cannot access a key from something that is not a map");
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_ACCESS_COLLECTION_VALUE_WITH_INDEX: {
+ ASPL_OBJECT_TYPE index = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE collection = aspl_ailinterpreter_stack_pop(context->stack);
+ switch (ASPL_ACCESS(collection).kind) {
+ case ASPL_OBJECT_KIND_LIST:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_LIST_GET(collection, index));
+ break;
+ case ASPL_OBJECT_KIND_MAP:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_MAP_GET_VALUE_FROM_INDEX(collection, index));
+ break;
+ case ASPL_OBJECT_KIND_STRING:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_STRING_INDEX(collection, index));
+ break;
+ default:
+ ASPL_PANIC("Cannot access a value from something that is not a collection");
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_INDEX_COLLECTION: {
+ ASPL_OBJECT_TYPE key = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE collection = aspl_ailinterpreter_stack_pop(context->stack);
+ switch (ASPL_ACCESS(collection).kind) {
+ case ASPL_OBJECT_KIND_LIST:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_LIST_GET(collection, key));
+ break;
+ case ASPL_OBJECT_KIND_MAP:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_MAP_GET(collection, key));
+ break;
+ case ASPL_OBJECT_KIND_STRING:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_STRING_INDEX(collection, key));
+ break;
+ default:
+ ASPL_PANIC("Cannot index a non-collection object");
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_UPDATE_COLLECTION: {
+ ASPL_OBJECT_TYPE value = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE key = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE collection = aspl_ailinterpreter_stack_pop(context->stack);
+ switch (ASPL_ACCESS(collection).kind) {
+ case ASPL_OBJECT_KIND_LIST:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_LIST_SET(collection, key, value));
+ break;
+ case ASPL_OBJECT_KIND_MAP:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_MAP_SET(collection, key, value));
+ break;
+ default:
+ ASPL_PANIC("Cannot update a non-collection object by an index");
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_NEGATE: {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_NEGATE(aspl_ailinterpreter_stack_pop(context->stack)));
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_DECLARE_CLASS: {
+ char is_static = aspl_ailinterpreter_read_bool(bytes);
+ char* identifier = aspl_ailinterpreter_read_short_string(bytes);
+ short parents_size = aspl_ailinterpreter_read_short(bytes);
+ char** parents = ASPL_MALLOC(sizeof(char*) * parents_size);
+ for (int i = 0; i < parents_size; i++) {
+ parents[i] = aspl_ailinterpreter_read_short_string(bytes);
+ }
+ char is_error = aspl_ailinterpreter_read_bool(bytes);
+ ASPL_AILI_Class* class = ASPL_MALLOC(sizeof(ASPL_AILI_Class));
+ class->is_static = is_static;
+ class->parents = parents;
+ class->parents_size = parents_size;
+ class->is_error = is_error;
+ aspl_ailinterpreter_register_class(context->environment_context, identifier, class);
+ aspl_ailinterpreter_class_stack_push(context->class_stack, context->current_class);
+ context->current_class = identifier;
+ hashmap_str_to_voidptr_hashmap_set(context->environment_context->custom_methods, context->current_class, hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 4 }));
+ aspl_ailinterpreter_access_class(context->environment_context, context->current_class)->default_threadlocal_static_property_values = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 4 });
+ aspl_ailinterpreter_access_class(context->environment_context, context->current_class)->static_property_values = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 4 });
+ aspl_ailinterpreter_access_class(context->environment_context, context->current_class)->reactive_properties = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 4 });
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_NEW: {
+ char* identifier = aspl_ailinterpreter_read_short_string(bytes);
+ int argc = aspl_ailinterpreter_read_int(bytes);
+ ASPL_AILI_ArgumentList arguments = { .arguments = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * argc), .size = argc };
+ for (int i = argc - 1; i >= 0; i--) {
+ arguments.arguments[i] = aspl_ailinterpreter_stack_pop(context->stack);
+ }
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_CLASS_INSTANCE;
+ ASPL_ClassInstance* instance = ASPL_MALLOC(sizeof(ASPL_ClassInstance));
+ instance->typePtr = identifier;
+ instance->typeLen = strlen(identifier);
+ instance->properties = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 1 });
+ instance->isError = aspl_ailinterpreter_access_class(context->environment_context, identifier)->is_error;
+ ASPL_ACCESS(obj).value.classInstance = instance;
+
+ aspl_ailinterpreter_stack_push(context->stack, obj);
+
+ hashmap_str_to_voidptr_HashMap* methods = hashmap_str_to_voidptr_hashmap_get_value(context->environment_context->custom_methods, identifier);
+ if (hashmap_str_to_voidptr_hashmap_contains_key(methods, "construct")) {
+ ASPL_AILI_CustomMethod* method = hashmap_str_to_voidptr_hashmap_get_value(methods, "construct");
+ char* method_identifier = ASPL_MALLOC(sizeof(char) * (strlen(identifier) + strlen("construct") + 2));
+ strcpy(method_identifier, identifier);
+ strcat(method_identifier, ".");
+ strcat(method_identifier, "construct");
+ aspl_ailinterpreter_stack_trace_push(context, method_identifier);
+ aspl_ailinterpreter_stack_push_frame(context->stack);
+ aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, (ASPL_AILI_CallableInvocationMeta) { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_METHOD, .previous_instance = C_REFERENCE(context->current_instance), .previous_address = bytes->position, .is_constructor = 1, .previous_loop_meta_stack = context->loop_meta_stack });
+ context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack));
+ context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024);
+ context->loop_meta_stack->top = 0;
+ aspl_ailinterpreter_push_scope(context);
+ context->current_instance = obj;
+ bytes->position = method->address;
+ for (int i = 0; i < method->parameters.size; i++) {
+ if (i < arguments.size) {
+ aspl_ailinterpreter_register_variable(context, method->parameters.parameters[i].name, arguments.arguments[i]);
+ }
+ else if (method->parameters.parameters[i].optional) {
+ aspl_ailinterpreter_register_variable(context, method->parameters.parameters[i].name, aspl_object_clone_shallow(method->parameters.parameters[i].default_value));
+ }
+ else {
+ ASPL_PANIC("Missing argument for parameter '%s'", method->parameters.parameters[i].name);
+ }
+ }
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_THIS: {
+ aspl_ailinterpreter_stack_push(context->stack, context->current_instance);
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_DECLARE_PROPERTY: {
+ char is_static = aspl_ailinterpreter_read_bool(bytes);
+ char is_thread_local = aspl_ailinterpreter_read_bool(bytes);
+ char* name = aspl_ailinterpreter_read_short_string(bytes);
+ ASPL_AILI_TypeList types = aspl_ailinterpreter_parse_types(bytes);
+ if (aspl_ailinterpreter_read_bool(bytes)) {
+ long long get_code_length = aspl_ailinterpreter_read_long(bytes);
+ ASPL_AILI_ReactiveProperty* reactive_property = ASPL_MALLOC(sizeof(ASPL_AILI_ReactiveProperty));
+ if (get_code_length > -1) {
+ reactive_property->get_address = bytes->position;
+ bytes->position += get_code_length;
+ }
+ long long set_code_length = aspl_ailinterpreter_read_long(bytes);
+ if (set_code_length > -1) {
+ reactive_property->set_address = bytes->position;
+ bytes->position += set_code_length;
+ }
+ hashmap_str_to_voidptr_hashmap_set(aspl_ailinterpreter_access_class(context->environment_context, context->current_class)->reactive_properties, name, reactive_property);
+ }
+ else {
+ if (is_static) {
+ ASPL_OBJECT_TYPE default_value = aspl_ailinterpreter_stack_pop(context->stack);
+ if (is_thread_local) {
+ hashmap_str_to_voidptr_HashMap* default_threadlocal_static_property_values = aspl_ailinterpreter_access_class(context->environment_context, context->current_class)->default_threadlocal_static_property_values;
+ hashmap_str_to_voidptr_hashmap_set(default_threadlocal_static_property_values, name, C_REFERENCE(default_value));
+ if (!hashmap_str_to_voidptr_hashmap_contains_key(context->local_static_property_values, context->current_class)) {
+ hashmap_str_to_voidptr_hashmap_set(context->local_static_property_values, context->current_class, hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 4 }));
+ }
+ hashmap_str_to_voidptr_hashmap_set(hashmap_str_to_voidptr_hashmap_get_value(context->local_static_property_values, context->current_class), name, C_REFERENCE(default_value));
+ }
+ else {
+ hashmap_str_to_voidptr_HashMap* static_property_values = aspl_ailinterpreter_access_class(context->environment_context, context->current_class)->static_property_values;
+ hashmap_str_to_voidptr_hashmap_set(static_property_values, name, C_REFERENCE(default_value));
+ }
+ }
+ else {
+ // TOOD: Do we need anything here?
+ }
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_ACCESS_PROPERTY: {
+ char is_static = aspl_ailinterpreter_read_bool(bytes);
+ if (is_static) {
+ char* type = aspl_ailinterpreter_read_short_string(bytes);
+ char* name = aspl_ailinterpreter_read_short_string(bytes);
+ if (hashmap_str_to_voidptr_hashmap_contains_key(aspl_ailinterpreter_access_class(context->environment_context, type)->reactive_properties, name)) {
+ ASPL_AILI_ReactiveProperty* reactive_property = hashmap_str_to_voidptr_hashmap_get_value(aspl_ailinterpreter_access_class(context->environment_context, type)->reactive_properties, name);
+ char* identifier = ASPL_MALLOC(sizeof(char) * (strlen(type) + strlen(name) + 2));
+ strcpy(identifier, type);
+ strcat(identifier, ".");
+ strcat(identifier, name);
+ aspl_ailinterpreter_stack_trace_push(context, identifier);
+ aspl_ailinterpreter_stack_push_frame(context->stack);
+ aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, (ASPL_AILI_CallableInvocationMeta) { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_REACTIVE_PROPERTY, .previous_address = bytes->position, .previous_loop_meta_stack = context->loop_meta_stack });
+ context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack));
+ context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024);
+ context->loop_meta_stack->top = 0;
+ aspl_ailinterpreter_push_scope(context);
+ bytes->position = reactive_property->get_address;
+ }
+ else if (hashmap_str_to_voidptr_hashmap_contains_key(context->local_static_property_values, type) && hashmap_str_to_voidptr_hashmap_contains_key(hashmap_str_to_voidptr_hashmap_get_value(context->local_static_property_values, type), name)) {
+ aspl_ailinterpreter_stack_push(context->stack, *(ASPL_OBJECT_TYPE*)hashmap_str_to_voidptr_hashmap_get_value(hashmap_str_to_voidptr_hashmap_get_value(context->local_static_property_values, type), name));
+ }
+ else {
+ hashmap_str_to_voidptr_HashMap* static_property_values = aspl_ailinterpreter_access_class(context->environment_context, type)->static_property_values;
+ aspl_ailinterpreter_stack_push(context->stack, *(ASPL_OBJECT_TYPE*)hashmap_str_to_voidptr_hashmap_get_value(static_property_values, name));
+ }
+ }
+ else {
+ char* name = aspl_ailinterpreter_read_short_string(bytes);
+ ASPL_OBJECT_TYPE object = aspl_ailinterpreter_stack_pop(context->stack);
+ if (ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_CLASS_INSTANCE) {
+ if (hashmap_str_to_voidptr_hashmap_contains_key(aspl_ailinterpreter_access_class(context->environment_context, aspl_object_get_short_type_pointer(object))->reactive_properties, name)) { // TODO: Support reactive properties in parents
+ ASPL_AILI_ReactiveProperty* reactive_property = hashmap_str_to_voidptr_hashmap_get_value(aspl_ailinterpreter_access_class(context->environment_context, aspl_object_get_short_type_pointer(object))->reactive_properties, name);
+ char* identifier = ASPL_MALLOC(sizeof(char) * (strlen(aspl_object_get_short_type_pointer(object)) + strlen(name) + 2));
+ strcpy(identifier, aspl_object_get_short_type_pointer(object));
+ strcat(identifier, ".");
+ strcat(identifier, name);
+ aspl_ailinterpreter_stack_trace_push(context, identifier);
+ aspl_ailinterpreter_stack_push_frame(context->stack);
+ aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, (ASPL_AILI_CallableInvocationMeta) { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_REACTIVE_PROPERTY, .previous_address = bytes->position, .previous_instance = C_REFERENCE(context->current_instance), .previous_loop_meta_stack = context->loop_meta_stack });
+ context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack));
+ context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024);
+ context->loop_meta_stack->top = 0;
+ aspl_ailinterpreter_push_scope(context);
+ context->current_instance = object;
+ bytes->position = reactive_property->get_address;
+ }
+ else {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_CLASS_INSTANCE_GET_PROPERTY(ASPL_ACCESS(object).value.classInstance, name));
+ }
+ }
+ else {
+ switch (ASPL_ACCESS(object).kind) {
+ case ASPL_OBJECT_KIND_STRING:
+ if (strcmp(name, "length") == 0) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_STRING_LENGTH(object));
+ }
+ else {
+ ASPL_PANIC("Cannot access the unknown property string.%s", name);
+ }
+ break;
+ case ASPL_OBJECT_KIND_LIST:
+ if (strcmp(name, "length") == 0) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_LIST_LENGTH(object));
+ }
+ else {
+ ASPL_PANIC("Cannot access the unknown property list.%s", name);
+ }
+ break;
+ case ASPL_OBJECT_KIND_MAP:
+ if (strcmp(name, "length") == 0) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_MAP_LENGTH(object));
+ }
+ else {
+ ASPL_PANIC("Cannot access the unknown property map.%s", name);
+ }
+ break;
+ default:
+ ASPL_PANIC("Cannot access the unknown property %s.%s", aspl_object_get_type_pointer(object), name);
+ }
+ }
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_UPDATE_PROPERTY: {
+ char is_static = aspl_ailinterpreter_read_bool(bytes);
+ char is_reactive = aspl_ailinterpreter_read_bool(bytes);
+ ASPL_OBJECT_TYPE value = aspl_ailinterpreter_stack_pop(context->stack);
+ if (is_static) {
+ char* type = aspl_ailinterpreter_read_short_string(bytes);
+ char* name = aspl_ailinterpreter_read_short_string(bytes);
+ if (is_reactive) {
+ ASPL_AILI_ReactiveProperty* reactive_property = hashmap_str_to_voidptr_hashmap_get_value(aspl_ailinterpreter_access_class(context->environment_context, type)->reactive_properties, name);
+ char* identifier = ASPL_MALLOC(sizeof(char) * (strlen(type) + strlen(name) + 2));
+ strcpy(identifier, type);
+ strcat(identifier, ".");
+ strcat(identifier, name);
+ aspl_ailinterpreter_stack_trace_push(context, identifier);
+ aspl_ailinterpreter_stack_push_frame(context->stack);
+ aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, (ASPL_AILI_CallableInvocationMeta) { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_REACTIVE_PROPERTY, .previous_address = bytes->position, .previous_loop_meta_stack = context->loop_meta_stack, .is_reactive_property_setter = 1 });
+ context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack));
+ context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024);
+ context->loop_meta_stack->top = 0;
+ aspl_ailinterpreter_push_scope(context);
+ bytes->position = reactive_property->set_address;
+ aspl_ailinterpreter_register_variable(context, "value", value);
+ }
+ else if (hashmap_str_to_voidptr_hashmap_contains_key(context->local_static_property_values, type) && hashmap_str_to_voidptr_hashmap_contains_key(hashmap_str_to_voidptr_hashmap_get_value(context->local_static_property_values, type), name)) {
+ *(ASPL_OBJECT_TYPE*)hashmap_str_to_voidptr_hashmap_get_value(hashmap_str_to_voidptr_hashmap_get_value(context->local_static_property_values, type), name) = value;
+ }
+ else {
+ hashmap_str_to_voidptr_HashMap* static_property_values = aspl_ailinterpreter_access_class(context->environment_context, type)->static_property_values;
+ hashmap_str_to_voidptr_hashmap_set(static_property_values, name, C_REFERENCE(value));
+ }
+ }
+ else {
+ char* name = aspl_ailinterpreter_read_short_string(bytes);
+ ASPL_OBJECT_TYPE object = aspl_ailinterpreter_stack_pop(context->stack);
+ if (is_reactive) {
+ ASPL_AILI_ReactiveProperty* reactive_property = aspl_ailinterpreter_util_find_reactive_property(context, aspl_object_get_short_type_pointer(object), name);
+ char* identifier = ASPL_MALLOC(sizeof(char) * (strlen(aspl_object_get_short_type_pointer(object)) + strlen(name) + 2));
+ strcpy(identifier, aspl_object_get_short_type_pointer(object));
+ strcat(identifier, ".");
+ strcat(identifier, name);
+ aspl_ailinterpreter_stack_trace_push(context, identifier);
+ aspl_ailinterpreter_stack_push_frame(context->stack);
+ aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, (ASPL_AILI_CallableInvocationMeta) { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_REACTIVE_PROPERTY, .previous_address = bytes->position, .previous_instance = C_REFERENCE(context->current_instance), .previous_loop_meta_stack = context->loop_meta_stack, .is_reactive_property_setter = 1 });
+ context->loop_meta_stack = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMetaStack));
+ context->loop_meta_stack->data = ASPL_MALLOC(sizeof(ASPL_AILI_LoopMeta) * 1024);
+ context->loop_meta_stack->top = 0;
+ aspl_ailinterpreter_push_scope(context);
+ context->current_instance = object;
+ bytes->position = reactive_property->set_address;
+ aspl_ailinterpreter_register_variable(context, "value", value);
+ }
+ else {
+ ASPL_CLASS_INSTANCE_SET_PROPERTY(ASPL_ACCESS(object).value.classInstance, name, value);
+ }
+ }
+ if (!is_reactive) {
+ aspl_ailinterpreter_stack_push(context->stack, value);
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_ADD: {
+ ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack);
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_PLUS(a, b));
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_INCREMENT: {
+ ASPL_OBJECT_TYPE object = aspl_ailinterpreter_stack_pop(context->stack);
+ switch (ASPL_ACCESS(object).kind) {
+ case ASPL_OBJECT_KIND_BYTE:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_BYTE_LITERAL(ASPL_ACCESS(object).value.integer8 + 1));
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_INT_LITERAL(ASPL_ACCESS(object).value.integer32 + 1));
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_LONG_LITERAL(ASPL_ACCESS(object).value.integer64 + 1));
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_FLOAT_LITERAL(ASPL_ACCESS(object).value.float32 + 1));
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_DOUBLE_LITERAL(ASPL_ACCESS(object).value.float64 + 1));
+ break;
+ default:
+ ASPL_PANIC("Unknown or invalid object type for the ++ operator");
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_SUBTRACT: {
+ ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack);
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_MINUS(a, b));
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_DECREMENT: {
+ ASPL_OBJECT_TYPE object = aspl_ailinterpreter_stack_pop(context->stack);
+ switch (ASPL_ACCESS(object).kind) {
+ case ASPL_OBJECT_KIND_BYTE:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_BYTE_LITERAL(ASPL_ACCESS(object).value.integer8 - 1));
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_INT_LITERAL(ASPL_ACCESS(object).value.integer32 - 1));
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_LONG_LITERAL(ASPL_ACCESS(object).value.integer64 - 1));
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_FLOAT_LITERAL(ASPL_ACCESS(object).value.float32 - 1));
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_DOUBLE_LITERAL(ASPL_ACCESS(object).value.float64 - 1));
+ break;
+ default:
+ ASPL_PANIC("Unknown or invalid object type for the -- operator");
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_MULTIPLY: {
+ ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack);
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_MULTIPLY(a, b));
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_DIVIDE: {
+ ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack);
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_DIVIDE(a, b));
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_MODULO: {
+ ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack);
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_MODULO(a, b));
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_SMALLER_THAN: {
+ ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack);
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_LESS_THAN(a, b));
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_SMALLER_EQUAL: {
+ ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack);
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_LESS_THAN_OR_EQUAL(a, b));
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_GREATER_THAN: {
+ ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack);
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_GREATER_THAN(a, b));
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_GREATER_EQUAL: {
+ ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack);
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_GREATER_THAN_OR_EQUAL(a, b));
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_EQUALS: {
+ ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack);
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_EQUALS(a, b));
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_NOT_EQUALS: {
+ ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack);
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_NEGATE(ASPL_EQUALS(a, b)));
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_BOOLEAN_AND: {
+ ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack);
+ int length = aspl_ailinterpreter_read_int(bytes);
+ if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_BOOLEAN) {
+ if (!ASPL_ACCESS(a).value.boolean) {
+ bytes->position += length;
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_FALSE());
+ }
+ // b is now just evaluated normally
+ }
+ else {
+ ASPL_PANIC("Cannot use the boolean AND (&&) operator on a non-boolean value");
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_BITWISE_AND: {
+ ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack);
+ if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_BYTE && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_BYTE) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_BYTE_LITERAL(ASPL_ACCESS(a).value.integer8 & ASPL_ACCESS(b).value.integer8));
+ }
+ else if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_INTEGER && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_INTEGER) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_INT_LITERAL(ASPL_ACCESS(a).value.integer32 & ASPL_ACCESS(b).value.integer32));
+ }
+ else if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_LONG && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_LONG) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_LONG_LITERAL(ASPL_ACCESS(a).value.integer64 & ASPL_ACCESS(b).value.integer64));
+ }
+ else if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_ENUM_FIELD && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_ENUM_FIELD) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_ENUM_AND(a, b));
+ }
+ else {
+ ASPL_PANIC("Invalid types for the & operator");
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_BOOLEAN_OR: {
+ ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack);
+ int length = aspl_ailinterpreter_read_int(bytes);
+ if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_BOOLEAN) {
+ if (ASPL_ACCESS(a).value.boolean) {
+ bytes->position += length;
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_TRUE());
+ }
+ // b is now just evaluated normally
+ }
+ else {
+ ASPL_PANIC("Cannot use the boolean OR (||) operator on a non-boolean value");
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_BITWISE_OR: {
+ ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack);
+ if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_BYTE && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_BYTE) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_BYTE_LITERAL(ASPL_ACCESS(a).value.integer8 | ASPL_ACCESS(b).value.integer8));
+ }
+ else if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_INTEGER && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_INTEGER) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_INT_LITERAL(ASPL_ACCESS(a).value.integer32 | ASPL_ACCESS(b).value.integer32));
+ }
+ else if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_LONG && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_LONG) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_LONG_LITERAL(ASPL_ACCESS(a).value.integer64 | ASPL_ACCESS(b).value.integer64));
+ }
+ else if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_ENUM_FIELD && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_ENUM_FIELD) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_ENUM_OR(a, b));
+ }
+ else {
+ ASPL_PANIC("Invalid types for the | operator");
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_BOOLEAN_XOR: {
+ ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack);
+ if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_BOOLEAN && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_BOOLEAN) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_BOOL_LITERAL(ASPL_ACCESS(a).value.boolean ^ ASPL_ACCESS(b).value.boolean));
+ }
+ else {
+ ASPL_PANIC("Invalid types for the ^ operator");
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_BITWISE_XOR: {
+ ASPL_OBJECT_TYPE b = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_OBJECT_TYPE a = aspl_ailinterpreter_stack_pop(context->stack);
+ if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_BYTE && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_BYTE) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_BYTE_LITERAL(ASPL_ACCESS(a).value.integer8 ^ ASPL_ACCESS(b).value.integer8));
+ }
+ else if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_INTEGER && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_INTEGER) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_INT_LITERAL(ASPL_ACCESS(a).value.integer32 ^ ASPL_ACCESS(b).value.integer32));
+ }
+ else if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_LONG && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_LONG) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_LONG_LITERAL(ASPL_ACCESS(a).value.integer64 ^ ASPL_ACCESS(b).value.integer64));
+ }
+ else if (ASPL_ACCESS(a).kind == ASPL_OBJECT_KIND_ENUM_FIELD && ASPL_ACCESS(b).kind == ASPL_OBJECT_KIND_ENUM_FIELD) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_ENUM_XOR(a, b));
+ }
+ else {
+ ASPL_PANIC("Invalid types for the ^ operator");
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_ASSERT: {
+ char* file = aspl_ailinterpreter_read_short_string(bytes);
+ int line = aspl_ailinterpreter_read_int(bytes);
+ int column = aspl_ailinterpreter_read_int(bytes);
+ ASPL_OBJECT_TYPE value = aspl_ailinterpreter_stack_pop(context->stack);
+ ASPL_ASSERT(value, file, line, column);
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_OFTYPE: {
+ ASPL_OBJECT_TYPE object = aspl_ailinterpreter_stack_pop(context->stack);
+ char* type = aspl_ailinterpreter_read_short_string(bytes);
+ if (ASPL_ACCESS(object).kind == ASPL_OBJECT_KIND_CLASS_INSTANCE) {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_BOOL_LITERAL(aspl_ailinterpreter_util_is_class_child_of(context, ASPL_ACCESS(object).value.classInstance->typePtr, type)));
+ }
+ else {
+ aspl_ailinterpreter_stack_push(context->stack, ASPL_OFTYPE(object, type));
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_SPECIFY_LOOP: {
+ long long body_length = aspl_ailinterpreter_read_long(bytes);
+ long long full_length = aspl_ailinterpreter_read_long(bytes);
+ aspl_ailinterpreter_lms_push(context->loop_meta_stack, (ASPL_AILI_LoopMeta) { .body_end_address = bytes->position + body_length, .full_end_address = bytes->position + full_length });
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_THROW: {
+ ASPL_OBJECT_TYPE error_instance = aspl_ailinterpreter_stack_pop(context->stack);
+ aspl_ailinterpreter_return(context, bytes, error_instance);
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_CATCH: {
+ char* variable = aspl_ailinterpreter_read_short_string(bytes);
+ long long body_length = aspl_ailinterpreter_read_long(bytes);
+ ASPL_OBJECT_TYPE value = aspl_ailinterpreter_stack_pop(context->stack);
+ if (aspl_object_is_error(value)) {
+ aspl_ailinterpreter_stack_trace_push(context, "catch_block"); // TODO
+ aspl_ailinterpreter_stack_push_frame(context->stack);
+ aspl_ailinterpreter_cims_push(context->callable_invocation_meta_stack, (ASPL_AILI_CallableInvocationMeta) { .type = ASPL_AILINTERPRETER_CALLABLE_TYPE_CATCH_BLOCK, .previous_address = bytes->position + body_length, .previous_loop_meta_stack = context->loop_meta_stack });
+ if (strlen(variable) > 0) {
+ aspl_ailinterpreter_register_variable(context, variable, value);
+ }
+ }
+ else {
+ aspl_ailinterpreter_stack_push(context->stack, value);
+ bytes->position += body_length;
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_PROPAGATE_ERROR: {
+ ASPL_OBJECT_TYPE value = aspl_ailinterpreter_stack_pop(context->stack);
+ if (aspl_object_is_error(value)) {
+ int n = context->stack->frames[context->stack->current_frame_index].top;
+ for (int i = 0; i < n; i++) {
+ aspl_ailinterpreter_stack_pop(context->stack);
+ }
+ if (context->callable_invocation_meta_stack->top > 0) {
+ aspl_ailinterpreter_return(context, bytes, value);
+ }
+ else {
+ ASPL_PANIC("Uncaught %s error", ASPL_ACCESS(value).value.classInstance->typePtr);
+ }
+ }
+ else {
+ aspl_ailinterpreter_stack_push(context->stack, value);
+ }
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_POP_LOOP: {
+ aspl_ailinterpreter_lms_pop(context->loop_meta_stack);
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_POP: {
+ aspl_ailinterpreter_stack_pop(context->stack);
+ break;
+ }
+ case ASPL_AILI_INSTRUCTION_END:
+ if (context->callable_invocation_meta_stack->top > 0) {
+ aspl_ailinterpreter_return(context, bytes, ASPL_NULL());
+ break;
+ }
+ else if (context->class_stack->top > 0) {
+ context->current_class = aspl_ailinterpreter_class_stack_pop(context->class_stack);
+ break;
+ }
+ else {
+ return;
+ }
+ default:
+ ASPL_PANIC("Unknown instruction %d", instruction);
+ }
+ }
+}
\ No newline at end of file
diff --git a/runtime/ailinterpreter/interpreter.h b/runtime/ailinterpreter/interpreter.h
new file mode 100644
index 0000000..daa3a79
--- /dev/null
+++ b/runtime/ailinterpreter/interpreter.h
@@ -0,0 +1,261 @@
+#ifndef ASPL_AILI_INTERPRETER_H_INCLUDED
+#define ASPL_AILI_INTERPRETER_H_INCLUDED
+
+#define ASPL_INTERPRETER_MODE
+
+#include "byte_list.h"
+#include "interpreter.h"
+
+#ifndef ASPL_TEMPLATE_INCLUDED
+#include "../../stdlib/aspl/compiler/backend/stringcode/c/template.c"
+#define ASPL_TEMPLATE_INCLUDED
+#endif
+
+typedef struct {
+ hashmap_str_to_voidptr_HashMap* functions;
+ hashmap_str_to_voidptr_HashMap* builtin_functions;
+ hashmap_str_to_voidptr_HashMap* custom_functions;
+ hashmap_str_to_voidptr_HashMap* builtin_methods;
+ hashmap_str_to_voidptr_HashMap* custom_methods;
+ hashmap_str_to_voidptr_HashMap* classes;
+ hashmap_str_to_voidptr_HashMap* enums;
+} ASPL_AILI_EnvironmentContext;
+
+typedef struct ASPL_AILI_StackFrame{
+ ASPL_OBJECT_TYPE* data;
+ int top;
+} ASPL_AILI_StackFrame;
+
+typedef struct ASPL_AILI_Stack {
+ ASPL_AILI_StackFrame* frames;
+ int frames_size;
+ int current_frame_index;
+} ASPL_AILI_Stack;
+
+typedef enum ASPL_AILI_CallableType {
+ ASPL_AILINTERPRETER_CALLABLE_TYPE_FUNCTION,
+ ASPL_AILINTERPRETER_CALLABLE_TYPE_METHOD,
+ ASPL_AILINTERPRETER_CALLABLE_TYPE_CALLBACK,
+ ASPL_AILINTERPRETER_CALLABLE_TYPE_REACTIVE_PROPERTY,
+ ASPL_AILINTERPRETER_CALLABLE_TYPE_CATCH_BLOCK
+} ASPL_AILI_CallableType;
+
+typedef struct ASPL_AILI_LoopMeta {
+ long long body_end_address;
+ long long full_end_address;
+} ASPL_AILI_LoopMeta;
+
+typedef struct ASPL_AILI_LoopMetaStack {
+ ASPL_AILI_LoopMeta* data;
+ int top;
+} ASPL_AILI_LoopMetaStack;
+
+typedef struct ASPL_AILI_ClassStack {
+ char** data;
+ int top;
+} ASPL_AILI_ClassStack;
+
+typedef struct ASPL_AILI_CallableInvocationMeta {
+ ASPL_AILI_CallableType type;
+ long long previous_address;
+ hashmap_str_to_voidptr_HashMap** previous_scopes;
+ int previous_scopes_top;
+ ASPL_OBJECT_TYPE* previous_instance;
+ ASPL_AILI_LoopMetaStack* previous_loop_meta_stack;
+ char is_constructor;
+ char is_reactive_property_setter;
+} ASPL_AILI_CallableInvocationMeta;
+
+typedef struct ASPL_AILI_CallableInvocationMetaStack {
+ ASPL_AILI_CallableInvocationMeta* data;
+ int top;
+} ASPL_AILI_CallableInvocationMetaStack;
+
+typedef struct ASPL_AILI_TypeList {
+ char** types;
+ int size;
+} ASPL_AILI_TypeList;
+
+typedef struct ASPL_AILI_Parameter {
+ char* name;
+ ASPL_AILI_TypeList expected_types;
+ int optional;
+ ASPL_OBJECT_TYPE default_value;
+} ASPL_AILI_Parameter;
+
+typedef struct ASPL_AILI_ParameterList {
+ ASPL_AILI_Parameter* parameters;
+ int size;
+} ASPL_AILI_ParameterList;
+
+typedef struct ASPL_AILI_BuiltinFunction {
+ ASPL_AILI_ParameterList parameters;
+} ASPL_AILI_BuiltinFunction;
+
+typedef struct ASPL_AILI_CustomFunction {
+ int address;
+ ASPL_AILI_ParameterList parameters;
+} ASPL_AILI_CustomFunction;
+
+typedef struct ASPL_AILI_BuiltinMethod {
+ // static builtin methods are not supported yet in ASPL
+ ASPL_AILI_ParameterList parameters;
+} ASPL_AILI_BuiltinMethod;
+
+typedef struct ASPL_AILI_CustomMethod {
+ int address;
+ char is_static;
+ ASPL_AILI_ParameterList parameters;
+} ASPL_AILI_CustomMethod;
+
+typedef struct ASPL_AILI_ReactiveProperty {
+ int get_address;
+ int set_address;
+} ASPL_AILI_ReactiveProperty;
+
+typedef struct ASPL_AILI_Class {
+ char is_static;
+ char** parents;
+ int parents_size;
+ char is_error;
+ hashmap_str_to_voidptr_HashMap* default_threadlocal_static_property_values;
+ hashmap_str_to_voidptr_HashMap* static_property_values;
+ hashmap_str_to_voidptr_HashMap* reactive_properties;
+} ASPL_AILI_Class;
+
+typedef struct ASPL_AILI_MemoryAddress {
+ ASPL_OBJECT_TYPE object;
+} ASPL_AILI_MemoryAddress;
+
+typedef struct ASPL_AILI_StackTrace {
+ char** frames;
+ int size;
+ int top;
+} ASPL_AILI_StackTrace;
+
+typedef struct ASPL_AILI_ThreadContext {
+ ASPL_AILI_EnvironmentContext* environment_context;
+ ASPL_AILI_Stack* stack;
+ ASPL_AILI_CallableInvocationMetaStack* callable_invocation_meta_stack;
+ ASPL_AILI_LoopMetaStack* loop_meta_stack;
+ ASPL_AILI_ClassStack* class_stack;
+ hashmap_str_to_voidptr_HashMap** scopes;
+ int scopes_size;
+ int scopes_top;
+ char* current_class; // only used for declaring properties and methods
+ ASPL_OBJECT_TYPE current_instance;
+ hashmap_str_to_voidptr_HashMap* local_static_property_values;
+ ASPL_AILI_StackTrace* stack_trace;
+} ASPL_AILI_ThreadContext;
+
+typedef struct ASPL_AILI_ArgumentList {
+ ASPL_OBJECT_TYPE* arguments;
+ int size;
+} ASPL_AILI_ArgumentList;
+
+typedef void(*ASPL_AILI_FunctionPtr)(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, char* identifier, ASPL_AILI_ArgumentList arguments);
+
+typedef struct ASPL_AILI_ThreadFunctionWrapperData {
+ ASPL_AILI_FunctionPtr function;
+ ASPL_AILI_ThreadContext* context;
+ ASPL_AILI_ByteList* bytes;
+ char* identifier;
+ ASPL_AILI_ArgumentList arguments;
+} ASPL_AILI_ThreadFunctionWrapperData;
+
+typedef struct ASPL_AILI_ThreadMethodWrapperData {
+ ASPL_AILI_CustomMethod* method;
+ ASPL_AILI_ThreadContext* context;
+ ASPL_AILI_ByteList* bytes;
+ char* identifier;
+ ASPL_AILI_ArgumentList arguments;
+} ASPL_AILI_ThreadMethodWrapperData;
+
+typedef struct ASPL_AILI_ThreadCallbackWrapperData {
+ ASPL_Callback* callback;
+ ASPL_AILI_ThreadContext* context;
+ ASPL_AILI_ByteList* bytes;
+ char* identifier;
+ ASPL_AILI_ArgumentList arguments;
+} ASPL_AILI_ThreadCallbackWrapperData;
+
+typedef struct ASPL_AILI_CallbackData {
+ long long address;
+ hashmap_str_to_voidptr_HashMap** creation_scopes;
+ int creation_scopes_top;
+ ASPL_AILI_ParameterList parameters;
+} ASPL_AILI_CallbackData;
+
+ASPL_AILI_EnvironmentContext* aspl_ailinterpreter_new_environment_context();
+
+ASPL_AILI_ThreadContext* aspl_ailinterpreter_new_thread_context(ASPL_AILI_EnvironmentContext* environment_context);
+
+void aspl_ailinterpreter_stack_push_frame(ASPL_AILI_Stack* stack);
+
+void aspl_ailinterpreter_stack_push(ASPL_AILI_Stack* stack, ASPL_OBJECT_TYPE object);
+
+ASPL_AILI_StackFrame aspl_ailinterpreter_stack_pop_frame(ASPL_AILI_Stack* stack);
+
+ASPL_OBJECT_TYPE aspl_ailinterpreter_stack_pop(ASPL_AILI_Stack* stack);
+
+ASPL_OBJECT_TYPE aspl_ailinterpreter_stack_peek(ASPL_AILI_Stack* stack);
+
+void aspl_ailinterpreter_cims_push(ASPL_AILI_CallableInvocationMetaStack* stack, ASPL_AILI_CallableInvocationMeta value);
+
+ASPL_AILI_CallableInvocationMeta aspl_ailinterpreter_cims_pop(ASPL_AILI_CallableInvocationMetaStack* stack);
+
+ASPL_AILI_CallableInvocationMeta aspl_ailinterpreter_cims_peek(ASPL_AILI_CallableInvocationMetaStack* stack);
+
+void aspl_ailinterpreter_lms_push(ASPL_AILI_LoopMetaStack* stack, ASPL_AILI_LoopMeta value);
+
+ASPL_AILI_LoopMeta aspl_ailinterpreter_lms_pop(ASPL_AILI_LoopMetaStack* stack);
+
+ASPL_AILI_LoopMeta aspl_ailinterpreter_lms_peek(ASPL_AILI_LoopMetaStack* stack);
+
+void aspl_ailinterpreter_class_stack_push(ASPL_AILI_ClassStack* stack, char* object);
+
+char* aspl_ailinterpreter_class_stack_pop(ASPL_AILI_ClassStack* stack);
+
+void aspl_ailinterpreter_push_scope(ASPL_AILI_ThreadContext* context);
+
+void aspl_ailinterpreter_pop_scope(ASPL_AILI_ThreadContext* context);
+
+void aspl_ailinterpreter_register_variable(ASPL_AILI_ThreadContext* context, char* identifier, ASPL_OBJECT_TYPE value);
+
+ASPL_OBJECT_TYPE* aspl_ailinterpreter_access_variable_address(ASPL_AILI_ThreadContext* context, char* identifier);
+
+ASPL_OBJECT_TYPE aspl_ailinterpreter_access_variable(ASPL_AILI_ThreadContext* context, char* identifier);
+
+void aspl_ailinterpreter_register_class(ASPL_AILI_EnvironmentContext* context, char* identifier, ASPL_AILI_Class* class);
+
+ASPL_AILI_Class* aspl_ailinterpreter_access_class(ASPL_AILI_EnvironmentContext* context, char* identifier);
+
+ASPL_AILI_TypeList aspl_ailinterpreter_parse_types(ASPL_AILI_ByteList* bytes);
+
+ASPL_AILI_ParameterList aspl_ailinterpreter_parse_parameters(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes);
+
+char* aspl_ailinterpreter_construct_list_from_type_list(ASPL_AILI_TypeList type_list);
+
+char* aspl_ailinterpreter_construct_map_from_type_lists(ASPL_AILI_TypeList key_type_list, ASPL_AILI_TypeList value_type_list);
+
+void aspl_ailinterpreter_return(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, ASPL_OBJECT_TYPE value);
+
+ASPL_AILI_FunctionPtr aspl_ailinterpreter_get_function(ASPL_AILI_ThreadContext* context, char* identifier);
+
+void aspl_ailinterpreter_custom_function_callback(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes, char* identifier, ASPL_AILI_ArgumentList arguments);
+
+ASPL_AILI_CustomMethod* aspl_ailinterpreter_util_find_custom_method(ASPL_AILI_ThreadContext* context, char* type, char* name);
+
+ASPL_AILI_ReactiveProperty* aspl_ailinterpreter_util_find_reactive_property(ASPL_AILI_ThreadContext* context, char* type, char* name);
+
+void aspl_ailinterpreter_display_stack_trace(ASPL_AILI_ThreadContext* context);
+
+void aspl_ailinterpreter_invoke_callback_from_outside_of_loop_internal(ASPL_Callback* callback, ASPL_AILI_ArgumentList arguments, ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes);
+
+void aspl_ailinterpreter_invoke_callback_from_outside_of_loop(ASPL_Callback* callback, ASPL_AILI_ArgumentList arguments);
+
+int aspl_ailinterpreter_new_thread_function_invocation_callback(void* arguments);
+
+void aspl_ailinterpreter_loop(ASPL_AILI_ThreadContext* context, ASPL_AILI_ByteList* bytes);
+
+#endif
\ No newline at end of file
diff --git a/runtime/ailinterpreter/main.c b/runtime/ailinterpreter/main.c
new file mode 100644
index 0000000..eb48aed
--- /dev/null
+++ b/runtime/ailinterpreter/main.c
@@ -0,0 +1,64 @@
+#include "interpreter.c"
+
+#define APPBUNDLE_MALLOC ASPL_MALLOC
+#define APPBUNDLE_REALLOC ASPL_REALLOC
+#define APPBUNDLE_FREE ASPL_FREE
+#include "thirdparty/appbundle/loader.c"
+
+ASPL_AILI_ByteList* aspl_ailinterpreter_read_file_into_byte_list(char* filename) {
+ FILE* file = fopen(filename, "rb");
+ fseek(file, 0, SEEK_END);
+ long length = ftell(file);
+ fseek(file, 0, SEEK_SET);
+ unsigned char* buffer = ASPL_MALLOC(sizeof(char) * length);
+ fread(buffer, 1, length, file);
+ fclose(file);
+ ASPL_AILI_ByteList* result = ASPL_MALLOC(sizeof(ASPL_AILI_ByteList));
+ result->size = length;
+ result->position = 0;
+ result->data = buffer;
+ return result;
+}
+
+int main(int argc, char** argv) {
+ aspl_argc = argc;
+ aspl_argv = argv;
+ aspl_setup_gc();
+
+#ifdef _WIN32
+ aspl_setup_windows_console();
+#endif
+
+ aspl_setup_builtin_method_pointers();
+ class_parents_map = *hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 64 }); // TODO: Is this required?
+
+ ASPL_AILI_EnvironmentContext* environment_context = aspl_ailinterpreter_new_environment_context();
+ ASPL_AILI_ThreadContext* thread_context = aspl_ailinterpreter_new_thread_context(environment_context);
+ aspl_ailinterpreter_current_thread_context = thread_context;
+
+
+#ifdef ASPL_AILI_BUNDLED
+ appbundle_Bundle* bundle = appbundle_Bundle_load();
+ appbundle_Resource resource = appbundle_Bundle_get_resource(bundle, "AIL Code");
+ ASPL_AILI_ByteList* bytes = ASPL_MALLOC(sizeof(ASPL_AILI_ByteList));
+ bytes->size = resource.length;
+ bytes->position = 0;
+ bytes->data = resource.data;
+#else
+ if (argc < 2) {
+ printf("ailbyteinterpreter: no file specified\n");
+ return 1;
+ }
+ char* file;
+ file = argv[1];
+ if (access(file, F_OK) == -1) {
+ printf("ailbyteinterpreter: file %s not found\n", file);
+ return 1;
+ }
+ ASPL_AILI_ByteList* bytes = aspl_ailinterpreter_read_file_into_byte_list(file);
+#endif
+ aspl_ailinterpreter_current_byte_list = bytes;
+
+ aspl_ailinterpreter_loop(thread_context, bytes);
+ return 0;
+}
\ No newline at end of file
diff --git a/stdlib/appbundle/Workaround.aspl b/stdlib/appbundle/Workaround.aspl
new file mode 100644
index 0000000..b251769
--- /dev/null
+++ b/stdlib/appbundle/Workaround.aspl
@@ -0,0 +1 @@
+//Needed because of a bug in the real_path function of the V standard library.
\ No newline at end of file
diff --git a/stdlib/appbundle/generator/Bundle.aspl b/stdlib/appbundle/generator/Bundle.aspl
new file mode 100644
index 0000000..48b7d4a
--- /dev/null
+++ b/stdlib/appbundle/generator/Bundle.aspl
@@ -0,0 +1,56 @@
+import encoding.utf8
+import io
+import bitconverter
+
+[public]
+class Bundle {
+
+ property string executable
+ [readpublic]
+ property map resources = {}
+
+ [public]
+ method construct(string executable){
+ this.executable = executable
+ }
+
+ [public]
+ method addResource(string name, any resource){
+ resources[name] = resource
+ }
+
+ [public]
+ method generate(){
+ var bytes = io.read_file_bytes(executable)
+ var originalLength = bytes.length
+ foreach(resources as name => resource){
+ var type = 255b
+ var list resourceBytes = []
+ if(resource oftype list){
+ type = 0b
+ resourceBytes = list(resource)
+ }elseif(resource oftype string){
+ type = 2b
+ resourceBytes = encoding.utf8.encode(string(resource))
+ }
+ bytes.add(type)
+ foreach(bitconverter.long_to_bytes(long(name.length)) as b){
+ bytes.add(b)
+ }
+ foreach(encoding.utf8.encode(name) as b){
+ bytes.add(b)
+ }
+ foreach(bitconverter.long_to_bytes(long(resourceBytes.length)) as b){
+ bytes.add(b)
+ }
+ foreach(resourceBytes as b){
+ bytes.add(b)
+ }
+ }
+ foreach(bitconverter.long_to_bytes(long(bytes.length - originalLength)) as b){
+ bytes.add(b)
+ }
+ io.write_file_bytes(executable, bytes)
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/README.md b/stdlib/aspl/README.md
new file mode 100644
index 0000000..005729f
--- /dev/null
+++ b/stdlib/aspl/README.md
@@ -0,0 +1,17 @@
+# The ASPL compiler & co.
+## Overview
+This module contains submodules for the `parser`, `compiler` and `formatter` of the ASPL toolchain.
+ It can be imported and used like any other ASPL module and has no dependencies[^1] except for other modules of the standard library.
+ Note that this module does not contain any CLI functionality, but it is instead imported and used by the `cli` folder in the root directory of the ASPL project, which implements the `aspl` command along with all its subcommands.
+
+[^1]: The compiler requires pre-compiled binary templates when packaging ASPL programs into an executable. Refer to the `templates` folder in the root directory of the ASPL project for more information.
+
+## Example usage
+```aspl
+import aspl.compiler
+
+aspl.compiler.compile("hello.aspl")
+```
+That's it. You can now run the compiled program by executing `./hello` (or `hello` if you're on Windows).
+
+For configuration options and more information, please refer to the `parser` and `compiler` submodules.
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/CompilationResult.aspl b/stdlib/aspl/compiler/CompilationResult.aspl
new file mode 100644
index 0000000..8bc0a07
--- /dev/null
+++ b/stdlib/aspl/compiler/CompilationResult.aspl
@@ -0,0 +1,14 @@
+import aspl.parser.ast
+
+[public]
+class CompilationResult {
+
+ [readpublic]
+ property list output
+
+ [public]
+ method construct(list output){
+ this.output = output
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/backend/Backend.aspl b/stdlib/aspl/compiler/backend/Backend.aspl
new file mode 100644
index 0000000..6041ce2
--- /dev/null
+++ b/stdlib/aspl/compiler/backend/Backend.aspl
@@ -0,0 +1,12 @@
+import aspl.compiler
+import aspl.parser
+
+[public]
+[abstract]
+class Backend {
+
+ [public]
+ [abstract]
+ method compile(ParserResult result) returns CompilationResult
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/backend/bytecode/ByteList.aspl b/stdlib/aspl/compiler/backend/bytecode/ByteList.aspl
new file mode 100644
index 0000000..3bf1ad1
--- /dev/null
+++ b/stdlib/aspl/compiler/backend/bytecode/ByteList.aspl
@@ -0,0 +1,95 @@
+import encoding.utf8
+import bitconverter
+import aspl.compiler.backend.bytecode.ail
+import aspl.compiler.backend.bytecode.twail
+
+[public]
+class ByteList {
+
+ [public]
+ property list bytes = []
+ [public]
+ property int length{
+ get{
+ return bytes.length
+ }
+ }
+
+ [public]
+ method add(any value, IntType type = IntType.Int) returns self{
+ if(value oftype byte){
+ bytes.add(byte(value))
+ }elseif(value oftype list){
+ foreach(list(value) as b){
+ bytes.add(b)
+ }
+ }elseif(value oftype self){
+ bytes.insertElements(bytes.length, self(value).bytes)
+ }elseif(value oftype aspl.compiler.backend.bytecode.ail.Instruction){ // TODO: This class should not be specialised for any backend
+ bytes.add(byte(int(aspl.compiler.backend.bytecode.ail.Instruction(value))))
+ }elseif(value oftype aspl.compiler.backend.bytecode.twail.Instruction){ // TODO: This class should not be specialised for any backend
+ bytes.add(byte(int(aspl.compiler.backend.bytecode.twail.Instruction(value))))
+ }elseif(value oftype string){
+ if(type == IntType.Short){
+ foreach(bitconverter.short_to_bytes(string(value).length) as b){
+ bytes.add(b)
+ }
+ }elseif(type == IntType.Int){
+ foreach(bitconverter.int_to_bytes(string(value).length) as b){
+ bytes.add(b)
+ }
+ }elseif(type == IntType.Long){
+ foreach(bitconverter.long_to_bytes(long(string(value).length)) as b){
+ bytes.add(b)
+ }
+ }
+ foreach(encoding.utf8.encode(string(value)) as c){
+ bytes.add(c)
+ }
+ }elseif(value oftype bool){
+ if(bool(value)){
+ bytes.add(1b)
+ }else{
+ bytes.add(0b)
+ }
+ }elseif(value oftype int){
+ if(type == IntType.Short){
+ foreach(bitconverter.short_to_bytes(int(value)) as b){
+ bytes.add(b)
+ }
+ }elseif(type == IntType.Long){
+ foreach(bitconverter.long_to_bytes(long(value)) as b){
+ bytes.add(b)
+ }
+ }else{
+ foreach(bitconverter.int_to_bytes(int(value)) as b){
+ bytes.add(b)
+ }
+ }
+ }
+ elseif(value oftype long){
+ foreach(bitconverter.long_to_bytes(long(value)) as b){
+ bytes.add(b)
+ }
+ }
+ elseif(value oftype float){
+ foreach(bitconverter.float_to_bytes(float(value)) as b){
+ bytes.add(b)
+ }
+ }
+ elseif(value oftype double){
+ foreach(bitconverter.double_to_bytes(double(value)) as b){
+ bytes.add(b)
+ }
+ }
+ return this
+ }
+
+ /*
+ TODO: Readd this when custom cast methods are implemented in the new compiler
+ [public]
+ method cast(string) returns string{
+ return encoding.utf8.decode(bytes)
+ }*/
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/backend/bytecode/BytecodeBackend.aspl b/stdlib/aspl/compiler/backend/bytecode/BytecodeBackend.aspl
new file mode 100644
index 0000000..269e60b
--- /dev/null
+++ b/stdlib/aspl/compiler/backend/bytecode/BytecodeBackend.aspl
@@ -0,0 +1,387 @@
+import aspl.compiler.backend
+import aspl.compiler.utils
+import aspl.parser.ast
+import aspl.parser.ast.statements
+import aspl.parser.ast.expressions
+import aspl.parser.ast.literals
+import aspl.parser.functions
+import aspl.parser.utils
+
+// This class is the base class for all bytecode-based backends (e.g. the AIL bytecode backend)
+[public]
+[abstract]
+class BytecodeBackend extends Backend {
+
+ [public]
+ method encode(Node node, bool standalone = false) returns ByteList{
+ if(node oftype Expression && standalone){
+ return encodeDiscard(Expression(node))
+ }
+
+ if(node oftype BlockStatement){
+ return encodeCodeBlock(BlockStatement(node))
+ }elseif(node oftype NullLiteral){
+ return encodeNullLiteral(NullLiteral(node))
+ }elseif(node oftype BooleanLiteral){
+ return encodeBooleanLiteral(BooleanLiteral(node))
+ }elseif(node oftype ByteLiteral){
+ return encodeByteLiteral(ByteLiteral(node))
+ }elseif(node oftype IntegerLiteral){
+ return encodeIntegerLiteral(IntegerLiteral(node))
+ }elseif(node oftype LongLiteral){
+ return encodeLongLiteral(LongLiteral(node))
+ }elseif(node oftype FloatLiteral){
+ return encodeFloatLiteral(FloatLiteral(node))
+ }elseif(node oftype DoubleLiteral){
+ return encodeDoubleLiteral(DoubleLiteral(node))
+ }elseif(node oftype StringLiteral){
+ return encodeStringLiteral(StringLiteral(node))
+ }elseif(node oftype ListLiteral){
+ return encodeListLiteral(ListLiteral(node))
+ }elseif(node oftype MapLiteral){
+ return encodeMapLiteral(MapLiteral(node))
+ }elseif(node oftype StringIndexExpression){
+ return encodeStringIndex(StringIndexExpression(node))
+ }elseif(node oftype AssertStatement){
+ return encodeAssertion(AssertStatement(node))
+ }elseif(node oftype CheckEqualsExpression){
+ return encodeEqualsCheck(CheckEqualsExpression(node))
+ }elseif(node oftype NegateExpression){
+ return encodeNegation(NegateExpression(node))
+ }elseif(node oftype FunctionCallExpression){
+ return encodeFunctionCall(FunctionCallExpression(node))
+ }elseif(node oftype VariableDeclareExpression){
+ return encodeVariableDeclaration(VariableDeclareExpression(node))
+ }elseif(node oftype VariableAccessExpression){
+ return encodeVariableAccess(VariableAccessExpression(node))
+ }elseif(node oftype VariableAssignExpression){
+ return encodeVariableAssignment(VariableAssignExpression(node))
+ }elseif(node oftype FunctionDeclareStatement){
+ return encodeFunctionDeclaration(FunctionDeclareStatement(node))
+ }elseif(node oftype AndExpression){
+ return encodeAnd(AndExpression(node))
+ }elseif(node oftype OrExpression){
+ return encodeOr(OrExpression(node))
+ }elseif(node oftype XorExpression){
+ return encodeXor(XorExpression(node))
+ }elseif(node oftype PlusExpression){
+ return encodePlus(PlusExpression(node))
+ }elseif(node oftype MinusExpression){
+ return encodeMinus(MinusExpression(node))
+ }elseif(node oftype MultiplyExpression){
+ return encodeMultiplication(MultiplyExpression(node))
+ }elseif(node oftype DivideExpression){
+ return encodeDivision(DivideExpression(node))
+ }elseif(node oftype ModuloExpression){
+ return encodeModulo(ModuloExpression(node))
+ }elseif(node oftype LessThanExpression){
+ return encodeLessThan(LessThanExpression(node))
+ }elseif(node oftype LessThanOrEqualExpression){
+ return encodeLessThanOrEqual(LessThanOrEqualExpression(node))
+ }elseif(node oftype GreaterThanExpression){
+ return encodeGreaterThan(GreaterThanExpression(node))
+ }elseif(node oftype GreaterThanOrEqualExpression){
+ return encodeGreaterThanOrEqual(GreaterThanOrEqualExpression(node))
+ }elseif(node oftype ReferenceExpression){
+ return encodeReference(ReferenceExpression(node))
+ }elseif(node oftype DereferenceExpression){
+ return encodeDereference(DereferenceExpression(node))
+ }elseif(node oftype ReturnStatement){
+ return encodeReturnStatement(ReturnStatement(node))
+ }elseif(node oftype FallbackStatement){
+ return encodeFallbackStatement(FallbackStatement(node))
+ }elseif(node oftype EscapeStatement){
+ return encodeEscapeStatement(EscapeStatement(node))
+ }elseif(node oftype IfStatement){
+ return encodeIfStatement(IfStatement(node))
+ }elseif(node oftype IfElseStatement){
+ return encodeIfElseStatement(IfElseStatement(node))
+ }elseif(node oftype IfElseIfStatement){
+ return encodeIfElseIfStatement(IfElseIfStatement(node))
+ }elseif(node oftype WhileStatement){
+ return encodeWhileStatement(WhileStatement(node))
+ }elseif(node oftype RepeatStatement){
+ return encodeRepeatStatement(RepeatStatement(node))
+ }elseif(node oftype ForeachStatement){
+ return encodeForeachStatement(ForeachStatement(node))
+ }elseif(node oftype NonStaticMethodCallExpression){
+ return encodeNonStaticMethodCall(NonStaticMethodCallExpression(node))
+ }elseif(node oftype StaticMethodCallExpression){
+ return encodeStaticMethodCall(StaticMethodCallExpression(node))
+ }elseif(node oftype NonStaticPropertyAccessExpression){
+ return encodeNonStaticPropertyAccess(NonStaticPropertyAccessExpression(node))
+ }elseif(node oftype NonStaticPropertyAssignExpression){
+ return encodeNonStaticPropertyAssignment(NonStaticPropertyAssignExpression(node))
+ }elseif(node oftype StaticPropertyAccessExpression){
+ return encodeStaticPropertyAccess(StaticPropertyAccessExpression(node))
+ }elseif(node oftype StaticPropertyAssignExpression){
+ return encodeStaticPropertyAssignment(StaticPropertyAssignExpression(node))
+ }elseif(node oftype ListIndexExpression){
+ return encodeListIndex(ListIndexExpression(node))
+ }elseif(node oftype ListAssignExpression){
+ return encodeListAssignment(ListAssignExpression(node))
+ }elseif(node oftype MapAccessExpression){
+ return encodeMapAccess(MapAccessExpression(node))
+ }elseif(node oftype MapAssignExpression){
+ return encodeMapAssignment(MapAssignExpression(node))
+ }elseif(node oftype ClassDeclareStatement){
+ return encodeClassDeclaration(ClassDeclareStatement(node))
+ }elseif(node oftype ClassInstantiateExpression){
+ return encodeClassInstantiation(ClassInstantiateExpression(node))
+ }elseif(node oftype MethodDeclareStatement){
+ return encodeMethodDeclaration(MethodDeclareStatement(node))
+ }elseif(node oftype PropertyDeclareStatement){
+ return encodePropertyDeclaration(PropertyDeclareStatement(node))
+ }elseif(node oftype ThisExpression){
+ return encodeThis(ThisExpression(node))
+ }elseif(node oftype EnumDeclareStatement){
+ return encodeEnumDeclaration(EnumDeclareStatement(node))
+ }elseif(node oftype EnumFieldAccessExpression){
+ return encodeEnumFieldAccess(EnumFieldAccessExpression(node))
+ }elseif(node oftype CallbackLiteral){
+ return encodeCallbackLiteral(CallbackLiteral(node))
+ }elseif(node oftype ImplementationCallExpression){
+ return encodeImplementationCall(ImplementationCallExpression(node))
+ }elseif(node oftype OfTypeExpression){
+ return encodeOfType(OfTypeExpression(node))
+ }elseif(node oftype BreakStatement){
+ return encodeBreakStatement(BreakStatement(node))
+ }elseif(node oftype ContinueStatement){
+ return encodeContinueStatement(ContinueStatement(node))
+ }elseif(node oftype CastExpression){
+ return encodeCast(CastExpression(node))
+ }elseif(node oftype ParentExpression){
+ return encode(new ThisExpression(ParentExpression(node).c, ParentExpression(node).location)) // the parent stuff is already handled in the parser
+ }elseif(node oftype ThrowStatement){
+ return encodeThrowStatement(ThrowStatement(node))
+ }elseif(node oftype CatchExpression){
+ return encodeCatchExpression(CatchExpression(node))
+ }elseif(node oftype PropagateErrorExpression){
+ return encodeErrorPropagation(PropagateErrorExpression(node))
+ }elseif(node oftype EmbedFileExpression){
+ return encodeFileEmbedding(EmbedFileExpression(node))
+ }elseif(node oftype IncludeFileStatement){
+ IncludeUtils:include(IncludeFileStatement(node).file)
+ return new ByteList()
+ }elseif(node oftype LinkLibraryStatement){
+ LinkUtils:libraries.add(LinkLibraryStatement(node).library)
+ return new ByteList()
+ }else{
+ aspl.parser.utils.fatal_error("Unsupported node type")
+ }
+ }
+
+ [abstract]
+ method encodeCodeBlock(BlockStatement statement) returns ByteList
+
+ [abstract]
+ method encodeNullLiteral(NullLiteral literal) returns ByteList
+
+ [abstract]
+ method encodeBooleanLiteral(BooleanLiteral literal) returns ByteList
+
+ [abstract]
+ method encodeByteLiteral(ByteLiteral literal) returns ByteList
+
+ [abstract]
+ method encodeIntegerLiteral(IntegerLiteral literal) returns ByteList
+
+ [abstract]
+ method encodeLongLiteral(LongLiteral literal) returns ByteList
+
+ [abstract]
+ method encodeFloatLiteral(FloatLiteral literal) returns ByteList
+
+ [abstract]
+ method encodeDoubleLiteral(DoubleLiteral literal) returns ByteList
+
+ [abstract]
+ method encodeStringLiteral(StringLiteral literal) returns ByteList
+
+ [abstract]
+ method encodeListLiteral(ListLiteral literal) returns ByteList
+
+ [abstract]
+ method encodeMapLiteral(MapLiteral literal) returns ByteList
+
+ [abstract]
+ method encodeAssertion(AssertStatement statement) returns ByteList
+
+ [abstract]
+ method encodeEqualsCheck(CheckEqualsExpression expression) returns ByteList
+
+ [abstract]
+ method encodeNegation(NegateExpression expression) returns ByteList
+
+ [abstract]
+ method encodeFunctionCall(FunctionCallExpression expression) returns ByteList
+
+ [abstract]
+ method encodeVariableDeclaration(VariableDeclareExpression expression) returns ByteList
+
+ [abstract]
+ method encodeVariableAccess(VariableAccessExpression expression) returns ByteList
+
+ [abstract]
+ method encodeVariableAssignment(VariableAssignExpression expression) returns ByteList
+
+ [abstract]
+ method encodeFunctionDeclaration(FunctionDeclareStatement statement) returns ByteList
+
+ [abstract]
+ method encodeAnd(AndExpression expression) returns ByteList
+
+ [abstract]
+ method encodeOr(OrExpression expression) returns ByteList
+
+ [abstract]
+ method encodeXor(XorExpression expression) returns ByteList
+
+ [abstract]
+ method encodePlus(PlusExpression expression) returns ByteList
+
+ [abstract]
+ method encodeMinus(MinusExpression expression) returns ByteList
+
+ [abstract]
+ method encodeMultiplication(MultiplyExpression expression) returns ByteList
+
+ [abstract]
+ method encodeDivision(DivideExpression expression) returns ByteList
+
+ [abstract]
+ method encodeModulo(ModuloExpression expression) returns ByteList
+
+ [abstract]
+ method encodeLessThan(LessThanExpression expression) returns ByteList
+
+ [abstract]
+ method encodeLessThanOrEqual(LessThanOrEqualExpression expression) returns ByteList
+
+ [abstract]
+ method encodeGreaterThan(GreaterThanExpression expression) returns ByteList
+
+ [abstract]
+ method encodeGreaterThanOrEqual(GreaterThanOrEqualExpression expression) returns ByteList
+
+ [abstract]
+ method encodeReference(ReferenceExpression expression) returns ByteList
+
+ [abstract]
+ method encodeDereference(DereferenceExpression expression) returns ByteList
+
+ [abstract]
+ method encodeReturnStatement(ReturnStatement statement) returns ByteList
+
+ [abstract]
+ method encodeFallbackStatement(FallbackStatement statement) returns ByteList
+
+ [abstract]
+ method encodeEscapeStatement(EscapeStatement statement) returns ByteList
+
+ [abstract]
+ method encodeIfStatement(IfStatement statement) returns ByteList
+
+ [abstract]
+ method encodeIfElseStatement(IfElseStatement statement) returns ByteList
+
+ [abstract]
+ method encodeIfElseIfStatement(IfElseIfStatement statement) returns ByteList
+
+ [abstract]
+ method encodeWhileStatement(WhileStatement statement) returns ByteList
+
+ [abstract]
+ method encodeRepeatStatement(RepeatStatement statement) returns ByteList
+
+ [abstract]
+ method encodeForeachStatement(ForeachStatement statement) returns ByteList
+
+ [abstract]
+ method encodeNonStaticMethodCall(NonStaticMethodCallExpression expression) returns ByteList
+
+ [abstract]
+ method encodeStaticMethodCall(StaticMethodCallExpression expression) returns ByteList
+
+ [abstract]
+ method encodeNonStaticPropertyAccess(NonStaticPropertyAccessExpression expression) returns ByteList
+
+ [abstract]
+ method encodeStaticPropertyAccess(StaticPropertyAccessExpression expression) returns ByteList
+
+ [abstract]
+ method encodeNonStaticPropertyAssignment(NonStaticPropertyAssignExpression expression) returns ByteList
+
+ [abstract]
+ method encodeStaticPropertyAssignment(StaticPropertyAssignExpression expression) returns ByteList
+
+ [abstract]
+ method encodeListIndex(ListIndexExpression expression) returns ByteList
+
+ [abstract]
+ method encodeListAssignment(ListAssignExpression expression) returns ByteList
+
+ [abstract]
+ method encodeStringIndex(StringIndexExpression expression) returns ByteList
+
+ [abstract]
+ method encodeMapAccess(MapAccessExpression expression) returns ByteList
+
+ [abstract]
+ method encodeMapAssignment(MapAssignExpression expression) returns ByteList
+
+ [abstract]
+ method encodeClassDeclaration(ClassDeclareStatement statement) returns ByteList
+
+ [abstract]
+ method encodeClassInstantiation(ClassInstantiateExpression expression) returns ByteList
+
+ [abstract]
+ method encodeMethodDeclaration(MethodDeclareStatement statement) returns ByteList
+
+ [abstract]
+ method encodePropertyDeclaration(PropertyDeclareStatement statement) returns ByteList
+
+ [abstract]
+ method encodeThis(ThisExpression expression) returns ByteList
+
+ [abstract]
+ method encodeEnumDeclaration(EnumDeclareStatement statement) returns ByteList
+
+ [abstract]
+ method encodeEnumFieldAccess(EnumFieldAccessExpression expression) returns ByteList
+
+ [abstract]
+ method encodeCallbackLiteral(CallbackLiteral literal) returns ByteList
+
+ [abstract]
+ method encodeImplementationCall(ImplementationCallExpression expression) returns ByteList
+
+ [abstract]
+ method encodeOfType(OfTypeExpression expression) returns ByteList
+
+ [abstract]
+ method encodeBreakStatement(BreakStatement statement) returns ByteList
+
+ [abstract]
+ method encodeContinueStatement(ContinueStatement statement) returns ByteList
+
+ [abstract]
+ method encodeCast(CastExpression expression) returns ByteList
+
+ [abstract]
+ method encodeThrowStatement(ThrowStatement statement) returns ByteList
+
+ [abstract]
+ method encodeCatchExpression(CatchExpression expression) returns ByteList
+
+ [abstract]
+ method encodeErrorPropagation(PropagateErrorExpression expression) returns ByteList
+
+ [abstract]
+ method encodeFileEmbedding(EmbedFileExpression expression) returns ByteList
+
+ [abstract]
+ method encodeDiscard(Expression expression) returns ByteList
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/backend/bytecode/IntType.aspl b/stdlib/aspl/compiler/backend/bytecode/IntType.aspl
new file mode 100644
index 0000000..fe33fb5
--- /dev/null
+++ b/stdlib/aspl/compiler/backend/bytecode/IntType.aspl
@@ -0,0 +1,6 @@
+[public]
+enum IntType {
+ Short,
+ Int,
+ Long
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/backend/bytecode/ail/AILBytecodeBackend.aspl b/stdlib/aspl/compiler/backend/bytecode/ail/AILBytecodeBackend.aspl
new file mode 100644
index 0000000..dc0ea90
--- /dev/null
+++ b/stdlib/aspl/compiler/backend/bytecode/ail/AILBytecodeBackend.aspl
@@ -0,0 +1,776 @@
+import aspl.compiler
+import aspl.compiler.backend
+import aspl.compiler.backend.bytecode
+import aspl.parser
+import aspl.parser.ast
+import aspl.parser.ast.statements
+import aspl.parser.ast.expressions
+import aspl.parser.ast.literals
+import aspl.parser.functions
+import aspl.parser.properties
+import aspl.parser.methods
+import aspl.parser.enums
+import aspl.parser.attributes
+import aspl.parser.utils
+
+[public]
+class AILBytecodeBackend extends BytecodeBackend {
+
+ property int tempVariableId = 0
+
+ [public]
+ method compile(ParserResult result) returns CompilationResult{
+ var output = new ByteList()
+ output.add(Instruction.Manifest)
+ var manifestBody = new ByteList()
+ // TODO: Encode manifest information
+ output.add(manifestBody.length)
+ output.add(manifestBody)
+ foreach(result.nodes as node){
+ output.add(this.encode(node, true))
+ }
+ output.add(Instruction.End)
+ return new CompilationResult(output.bytes)
+ }
+
+ method encodeCodeBlock(BlockStatement statement) returns ByteList{
+ var bytes = new ByteList()
+ foreach(statement.statements as s){
+ bytes.add(this.encode(s, true))
+ }
+ return bytes
+ }
+
+ method encodeNullLiteral(NullLiteral literal) returns ByteList{
+ return new ByteList().add(Instruction.CreateObject).add("null", IntType.Short)
+ }
+
+ method encodeBooleanLiteral(BooleanLiteral literal) returns ByteList{
+ return new ByteList().add(Instruction.CreateObject).add("bool", IntType.Short).add(literal.value) // "bool" instead of "boolean" for speed and minimal size
+ }
+
+ method encodeByteLiteral(ByteLiteral literal) returns ByteList{
+ return new ByteList().add(Instruction.CreateObject).add("byte", IntType.Short).add(literal.value)
+ }
+
+ method encodeIntegerLiteral(IntegerLiteral literal) returns ByteList{
+ return new ByteList().add(Instruction.CreateObject).add("int", IntType.Short).add(literal.value) // "int" instead of "integer" for speed and minimal size
+ }
+
+ method encodeLongLiteral(LongLiteral literal) returns ByteList{
+ return new ByteList().add(Instruction.CreateObject).add("long", IntType.Short).add(literal.value)
+ }
+
+ method encodeFloatLiteral(FloatLiteral literal) returns ByteList{
+ return new ByteList().add(Instruction.CreateObject).add("float", IntType.Short).add(literal.value)
+ }
+
+ method encodeDoubleLiteral(DoubleLiteral literal) returns ByteList{
+ return new ByteList().add(Instruction.CreateObject).add("double", IntType.Short).add(literal.value)
+ }
+
+ method encodeStringLiteral(StringLiteral literal) returns ByteList{
+ return new ByteList().add(Instruction.CreateObject).add("string", IntType.Short).add(literal.value, IntType.Long)
+ }
+
+ method encodeListLiteral(ListLiteral literal) returns ByteList{
+ var bytes = new ByteList()
+ foreach(literal.value as element){
+ bytes.add(this.encode(element))
+ }
+ bytes.add(Instruction.CreateObject).add("list", IntType.Short).add(literal.type.types.length, IntType.Short)
+ foreach(literal.type.types as type){
+ bytes.add(type.toString(), IntType.Short)
+ }
+ bytes.add(literal.value.length)
+ return bytes
+ }
+
+ method encodeMapLiteral(MapLiteral literal) returns ByteList{
+ var bytes = new ByteList()
+ foreach(literal.value as pair){
+ bytes.add(this.encode(pair.k))
+ bytes.add(this.encode(pair.v))
+ }
+ bytes.add(Instruction.CreateObject).add("map", IntType.Short).add(literal.keyType.types.length, IntType.Short)
+ foreach(literal.keyType.types as type){
+ bytes.add(type.toString(), IntType.Short)
+ }
+ bytes.add(literal.valueType.types.length, IntType.Short)
+ foreach(literal.valueType.types as type){
+ bytes.add(type.toString(), IntType.Short)
+ }
+ bytes.add(literal.value.length)
+ return bytes
+ }
+
+ method encodeAssertion(AssertStatement statement) returns ByteList{
+ return new ByteList().add(this.encode(statement.expression)).add(Instruction.Assert).add(Location(statement.location).file, IntType.Short).add(Location(statement.location).startLine).add(Location(statement.location).startColumn)
+ }
+
+ method encodeEqualsCheck(CheckEqualsExpression expression) returns ByteList{
+ return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.Equals)
+ }
+
+ method encodeNegation(NegateExpression expression) returns ByteList{
+ if(expression.expression oftype CheckEqualsExpression){
+ return new ByteList().add(this.encode(CheckEqualsExpression(expression.expression).left)).add(this.encode(CheckEqualsExpression(expression.expression).right)).add(Instruction.NotEquals)
+ }
+ return new ByteList().add(this.encode(expression.expression)).add(Instruction.Negate)
+ }
+
+ method encodeFunctionCall(FunctionCallExpression expression) returns ByteList{
+ var bytes = new ByteList()
+ foreach(expression.arguments as argument){
+ bytes.add(this.encode(argument))
+ }
+ var identifier = expression.func.identifier
+ if(expression.func oftype InternalFunction){
+ identifier = "_" + identifier
+ }
+ bytes.add(Instruction.CallFunction).add(identifier, IntType.Long).add(expression.newThread)
+ bytes.add(expression.arguments.length)
+ return bytes
+ }
+
+ method encodeVariableDeclaration(VariableDeclareExpression expression) returns ByteList{
+ var bytes = new ByteList()
+ bytes.add(this.encode(expression.value))
+ bytes.add(Instruction.RegisterVariable).add(expression.variable.identifier, IntType.Short).add(expression.getType().types.length, IntType.Short)
+ foreach(expression.getType().types as type){
+ bytes.add(TypeUtils:shortName(type.toString()), IntType.Short)
+ }
+ return bytes
+ }
+
+ method encodeVariableAccess(VariableAccessExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.AccessVariable).add(expression.variable.identifier, IntType.Short)
+ }
+
+ method encodeVariableAssignment(VariableAssignExpression expression) returns ByteList{
+ var bytes = new ByteList()
+ bytes.add(this.encode(expression.value))
+ bytes.add(Instruction.UpdateVariable).add(expression.variable.identifier, IntType.Short)
+ return bytes
+ }
+
+ method encodeFunctionDeclaration(FunctionDeclareStatement statement) returns ByteList{
+ var bytes = new ByteList()
+ foreach(statement.func.parameters as parameter){
+ bytes.add(encodeParameterValue(parameter))
+ }
+ bytes.add(Instruction.DeclareFunction)
+ bytes.add(statement.func.identifier, IntType.Short).add(statement.func.parameters.length, IntType.Short)
+ foreach(statement.func.parameters as parameter){
+ bytes.add(encodeParameterMeta(parameter))
+ }
+ bytes.add(statement.func.returnTypes.types.length, IntType.Short)
+ foreach(statement.func.returnTypes.types as returnType){
+ bytes.add(TypeUtils:shortName(returnType.toString()), IntType.Short)
+ }
+ var body = new ByteList()
+ foreach(CustomFunction(statement.func).code as s){
+ body.add(this.encode(s, true))
+ }
+ body.add(Instruction.End)
+ bytes.add(body.length, IntType.Long)
+ bytes.add(body)
+ return bytes
+ }
+
+ method encodeParameterValue(Parameter parameter) returns ByteList{
+ if(parameter.optional){
+ return this.encode(parameter.defaultValue)
+ }else{
+ return new ByteList()
+ }
+ }
+
+ method encodeParameterMeta(Parameter parameter) returns ByteList{
+ var bytes = new ByteList().add(parameter.name, IntType.Short).add(parameter.types.types.length, IntType.Short)
+ foreach(parameter.types.types as type){
+ bytes.add(TypeUtils:shortName(type.toString()), IntType.Short)
+ }
+ bytes.add(parameter.optional)
+ return bytes
+ }
+
+ method encodeReturnStatement(ReturnStatement statement) returns ByteList{
+ var bytes = new ByteList()
+ if(statement.value != null){
+ bytes.add(this.encode(statement.value))
+ }else{
+ bytes.add(encodeNullLiteral(new NullLiteral(null)))
+ }
+ bytes.add(Instruction.ReturnStatement)
+ return bytes
+ }
+
+ method encodeFallbackStatement(FallbackStatement statement) returns ByteList{
+ var bytes = new ByteList()
+ bytes.add(this.encode(statement.value))
+ bytes.add(Instruction.FallbackStatement)
+ return bytes
+ }
+
+ method encodeEscapeStatement(EscapeStatement statement) returns ByteList{
+ var bytes = new ByteList()
+ if(statement.value != null){
+ bytes.add(this.encode(statement.value))
+ }else{
+ bytes.add(encodeNullLiteral(new NullLiteral(null)))
+ }
+ bytes.add(Instruction.EscapeStatement)
+ return bytes
+ }
+
+ method encodeAnd(AndExpression expression) returns ByteList{
+ if(Type:matches(new Types([Type:fromString("boolean")]), expression.getType())){
+ var right = this.encode(expression.right)
+ return new ByteList().add(this.encode(expression.left)).add(Instruction.BooleanAnd).add(right.length).add(right)
+ }else{
+ return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.BitwiseAnd)
+ }
+ }
+
+ method encodeOr(OrExpression expression) returns ByteList{
+ if(Type:matches(new Types([Type:fromString("boolean")]), expression.getType())){
+ var right = this.encode(expression.right)
+ return new ByteList().add(this.encode(expression.left)).add(Instruction.BooleanOr).add(right.length).add(right)
+ }else{
+ return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.BitwiseOr)
+ }
+ }
+
+ method encodeXor(XorExpression expression) returns ByteList{
+ if(Type:matches(new Types([Type:fromString("boolean")]), expression.getType())){
+ return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.BooleanXor)
+ }else{
+ return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.BitwiseXor)
+ }
+ }
+
+ method encodePlus(PlusExpression expression) returns ByteList{
+ if((expression.left oftype ByteLiteral && ByteLiteral(expression.left).value == 1b) || (expression.left oftype IntegerLiteral && IntegerLiteral(expression.left).value == 1) || (expression.left oftype LongLiteral && LongLiteral(expression.left).value == 1l) || (expression.left oftype FloatLiteral && FloatLiteral(expression.left).value == 1f) || (expression.left oftype DoubleLiteral && DoubleLiteral(expression.left).value == 1d)){
+ return new ByteList().add(this.encode(expression.right)).add(Instruction.Increment)
+ }elseif((expression.right oftype ByteLiteral && ByteLiteral(expression.right).value == 1b) || (expression.right oftype IntegerLiteral && IntegerLiteral(expression.right).value == 1) || (expression.right oftype LongLiteral && LongLiteral(expression.right).value == 1l) || (expression.right oftype FloatLiteral && FloatLiteral(expression.right).value == 1f) || (expression.right oftype DoubleLiteral && DoubleLiteral(expression.right).value == 1d)){
+ return new ByteList().add(this.encode(expression.left)).add(Instruction.Increment)
+ }
+ return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.Add)
+ }
+
+ method encodeMinus(MinusExpression expression) returns ByteList{
+ if((expression.right oftype ByteLiteral && ByteLiteral(expression.right).value == 1b) || (expression.right oftype IntegerLiteral && IntegerLiteral(expression.right).value == 1) || (expression.right oftype LongLiteral && LongLiteral(expression.right).value == 1l) || (expression.right oftype FloatLiteral && FloatLiteral(expression.right).value == 1f) || (expression.right oftype DoubleLiteral && DoubleLiteral(expression.right).value == 1d)){
+ return new ByteList().add(this.encode(expression.left)).add(Instruction.Decrement)
+ }
+ // no left side check since minus is not commutative
+ return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.Subtract)
+ }
+
+ method encodeMultiplication(MultiplyExpression expression) returns ByteList{
+ return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.Multiply)
+ }
+
+ method encodeDivision(DivideExpression expression) returns ByteList{
+ return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.Divide)
+ }
+
+ method encodeModulo(ModuloExpression expression) returns ByteList{
+ return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.Modulo)
+ }
+
+ method encodeLessThan(LessThanExpression expression) returns ByteList{
+ return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.LessThan)
+ }
+
+ method encodeLessThanOrEqual(LessThanOrEqualExpression expression) returns ByteList{
+ return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.LessEqual)
+ }
+
+ method encodeGreaterThan(GreaterThanExpression expression) returns ByteList{
+ return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.GreaterThan)
+ }
+
+ method encodeGreaterThanOrEqual(GreaterThanOrEqualExpression expression) returns ByteList{
+ return new ByteList().add(this.encode(expression.left)).add(this.encode(expression.right)).add(Instruction.GreaterEqual)
+ }
+
+ method encodeReference(ReferenceExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.DeclarePointer).add(this.encode(expression.expression))
+ }
+
+ method encodeDereference(DereferenceExpression expression) returns ByteList{
+ return new ByteList().add(this.encode(expression.pointer)).add(Instruction.DereferencePointer)
+ }
+
+ method encodeIfStatement(IfStatement statement) returns ByteList{
+ var bytes = new ByteList()
+ bytes.add(this.encode(statement.condition))
+ bytes.add(Instruction.JumpRelativeIfFalse)
+ var body = new ByteList()
+ foreach(statement.code as s){
+ body.add(this.encode(s, true))
+ }
+ bytes.add(body.length, IntType.Long)
+ bytes.add(body)
+ return bytes
+ }
+
+ method encodeIfElseStatement(IfElseStatement statement) returns ByteList{
+ var ifBody = new ByteList()
+ foreach(statement.ifCode as s){
+ ifBody.add(this.encode(s, true))
+ }
+ var elseBody = new ByteList()
+ foreach(statement.elseCode as s){
+ elseBody.add(this.encode(s, true))
+ }
+ var bytes = new ByteList()
+ bytes.add(this.encode(statement.condition))
+ bytes.add(Instruction.JumpRelativeIfFalse)
+ bytes.add(ifBody.length + 8 + 1, IntType.Long) // + 1 for the jump instruction and + 8 for the length of the jump (at the beginning of the else statement)
+ bytes.add(ifBody)
+ bytes.add(Instruction.JumpRelative)
+ bytes.add(elseBody.length, IntType.Long)
+ bytes.add(elseBody)
+ return bytes
+ }
+
+ method encodeIfElseIfStatement(IfElseIfStatement statement) returns ByteList{
+ var ifBody = new ByteList()
+ foreach(statement.ifCode as s){
+ ifBody.add(this.encode(s, true))
+ }
+ var elseBody = this.encode(statement.elseIf)
+ var bytes = new ByteList()
+ bytes.add(this.encode(statement.condition))
+ bytes.add(Instruction.JumpRelativeIfFalse)
+ bytes.add(ifBody.length + 8 + 1, IntType.Long) // + 1 for the jump instruction and + 8 for the length of the jump (at the beginning of the else statement)
+ bytes.add(ifBody)
+ bytes.add(Instruction.JumpRelative)
+ bytes.add(elseBody.length, IntType.Long)
+ bytes.add(elseBody)
+ return bytes
+ }
+
+ method encodeWhileStatement(WhileStatement statement) returns ByteList{
+ var body = new ByteList()
+ foreach(statement.code as s){
+ body.add(this.encode(s, true))
+ }
+ var actualCode = new ByteList()
+ actualCode.add(this.encode(statement.condition))
+ actualCode.add(Instruction.JumpRelativeIfFalse)
+ actualCode.add(body.length + 1 + 8, IntType.Long) // + 1 for the jump instruction and + 8 for the length of the jump (at the end)
+ actualCode.add(body)
+ var bytes = new ByteList()
+ bytes.add(Instruction.SpecifyLoop)
+ bytes.add(actualCode.length, IntType.Long) // loop body length
+ bytes.add(actualCode.length + 1 + 8, IntType.Long) // full loop length (1 for the "relative jump" instruction and 8 for the length of the jump)
+ bytes.add(actualCode)
+ bytes.add(Instruction.JumpRelative)
+ bytes.add(-(bytes.length + 8 - (1 + 8 + 8)), IntType.Long) // + 8 for the length of the jump
+ bytes.add(Instruction.PopLoop)
+ return bytes
+ }
+
+ method encodeRepeatStatement(RepeatStatement statement) returns ByteList{
+ var bytes = new ByteList()
+ var variable = "_temp_" + tempVariableId++
+ if(statement.variable != null){
+ variable = statement.variable
+ }
+ bytes.add(this.encode(new IntegerLiteral(statement.start, null)))
+ bytes.add(Instruction.RegisterVariable).add(variable, IntType.Short)
+ bytes.add(1, IntType.Short)
+ bytes.add("int", IntType.Short)
+ bytes.add(Instruction.Pop) // pop the result of the variable declaration
+
+ var body = new ByteList()
+ foreach(statement.code as s){
+ body.add(this.encode(s, true))
+ }
+ var preUpdateCounterVariable = body.length
+ body.add(Instruction.AccessVariable).add(variable, IntType.Short)
+ body.add(Instruction.Increment)
+ body.add(Instruction.UpdateVariable).add(variable, IntType.Short)
+ body.add(Instruction.Pop) // pop the result of the variable update
+
+ var actualCode = new ByteList()
+ actualCode.add(Instruction.AccessVariable).add(variable, IntType.Short)
+ actualCode.add(this.encode(statement.iterations))
+ actualCode.add(this.encode(new IntegerLiteral(statement.start, null)))
+ actualCode.add(Instruction.Add)
+ actualCode.add(Instruction.LessThan)
+ actualCode.add(Instruction.JumpRelativeIfFalse)
+ actualCode.add(body.length + 1 + 8, IntType.Long) // + 1 for the jump instruction and + 8 for the length of the jump (at the end)
+ actualCode.add(body)
+
+ bytes.add(Instruction.SpecifyLoop)
+ bytes.add(actualCode.length - (body.length - preUpdateCounterVariable), IntType.Long) // loop body length
+ bytes.add(actualCode.length + 1 + 8, IntType.Long) // full loop length (1 for the "relative jump" instruction and 8 for the length of the jump)
+ bytes.add(actualCode)
+
+ bytes.add(Instruction.JumpRelative)
+ bytes.add(-(actualCode.length + 1 + 8), IntType.Long) // + 1 for the jump instruction and + 8 for the length of the jump
+
+ bytes.add(Instruction.PopLoop)
+ return bytes
+ }
+
+ method encodeForeachStatement(ForeachStatement statement) returns ByteList{
+ var bytes = new ByteList()
+ var collectionVariable = "_temp_" + tempVariableId++
+ var indexVariable = "_temp_" + tempVariableId++
+ var string? mapKeyVariable = null
+ if(Type:matches(new Types([Type:fromString("map")]), statement.collection.getType(), true)){
+ mapKeyVariable = statement.key
+ }elseif(statement.key != null){
+ indexVariable = statement.key
+ }
+ var valueVariable = statement.value
+ bytes.add(this.encode(statement.collection))
+ bytes.add(Instruction.RegisterVariable).add(collectionVariable, IntType.Short)
+ bytes.add(1, IntType.Short)
+ bytes.add("any", IntType.Short)
+ bytes.add(Instruction.Pop) // pop the result of the variable declaration
+ bytes.add(this.encode(new IntegerLiteral(0, null)))
+ bytes.add(Instruction.RegisterVariable).add(indexVariable, IntType.Short)
+ bytes.add(1, IntType.Short)
+ bytes.add("int", IntType.Short)
+ bytes.add(Instruction.Pop) // pop the result of the variable declaration
+ var initDoneAddress = bytes.length
+ var actualCode = new ByteList()
+ actualCode.add(Instruction.AccessVariable).add(indexVariable, IntType.Short)
+ actualCode.add(Instruction.AccessVariable).add(collectionVariable, IntType.Short)
+ actualCode.add(Instruction.CountCollection)
+ actualCode.add(Instruction.LessThan)
+ var conditionLength = actualCode.length
+ var body = new ByteList()
+ body.add(Instruction.AccessVariable).add(collectionVariable, IntType.Short)
+ body.add(Instruction.AccessVariable).add(indexVariable, IntType.Short)
+ body.add(Instruction.AccessCollectionValueWithIndex)
+ body.add(Instruction.RegisterVariable).add(valueVariable, IntType.Short)
+ body.add(1, IntType.Short)
+ body.add("any", IntType.Short)
+ body.add(Instruction.Pop) // pop the result of the variable declaration
+ if(mapKeyVariable != null){
+ body.add(Instruction.AccessVariable).add(collectionVariable, IntType.Short)
+ body.add(Instruction.AccessVariable).add(indexVariable, IntType.Short)
+ body.add(Instruction.AccessCollectionKeyWithIndex)
+ body.add(Instruction.RegisterVariable).add(mapKeyVariable, IntType.Short)
+ body.add(1, IntType.Short)
+ body.add("any", IntType.Short)
+ body.add(Instruction.Pop) // pop the result of the variable declaration
+ }
+ foreach(statement.code as s){
+ body.add(this.encode(s, true))
+ }
+ var preUpdateIndexVariable = body.length
+ body.add(Instruction.AccessVariable).add(indexVariable, IntType.Short)
+ body.add(Instruction.Increment)
+ body.add(Instruction.UpdateVariable).add(indexVariable, IntType.Short)
+ body.add(Instruction.Pop) // pop the result of the variable update
+ body.add(Instruction.JumpRelative)
+ body.add(-(body.length + conditionLength + 1 + 8 + 8), IntType.Long) // + 8 for the length of the jump
+ actualCode.add(Instruction.JumpRelativeIfFalse)
+ actualCode.add(body.length, IntType.Long)
+ actualCode.add(body)
+ bytes.add(Instruction.SpecifyLoop)
+ bytes.add(actualCode.length - (body.length - preUpdateIndexVariable), IntType.Long) // - 1 for the jump instruction and - 8 for the length of the jump
+ bytes.add(actualCode.length, IntType.Long)
+ bytes.add(actualCode)
+ bytes.add(Instruction.PopLoop)
+ return bytes
+ }
+
+ method encodeNonStaticMethodCall(NonStaticMethodCallExpression expression) returns ByteList{
+ var bytes = new ByteList()
+ bytes.add(this.encode(expression.base))
+ foreach(expression.arguments as argument){
+ bytes.add(this.encode(argument))
+ }
+ if(expression.exactClass == null){
+ bytes.add(Instruction.CallMethod)
+ bytes.add(false)
+ }else{
+ bytes.add(Instruction.CallExactMethod)
+ bytes.add(expression.exactClass?!.type.toString(), IntType.Short)
+ }
+ bytes.add(expression.m.name, IntType.Short)
+ bytes.add(expression.newThread)
+ bytes.add(expression.arguments.length)
+ return bytes
+ }
+
+ method encodeStaticMethodCall(StaticMethodCallExpression expression) returns ByteList{
+ var bytes = new ByteList()
+ foreach(expression.arguments as argument){
+ bytes.add(this.encode(argument))
+ }
+ bytes.add(Instruction.CallMethod)
+ bytes.add(true)
+ bytes.add(expression.base.toString(), IntType.Short)
+ bytes.add(expression.m.name, IntType.Short)
+ bytes.add(expression.newThread)
+ bytes.add(expression.arguments.length)
+ return bytes
+ }
+
+ method encodeNonStaticPropertyAccess(NonStaticPropertyAccessExpression expression) returns ByteList{
+ return new ByteList().add(this.encode(expression.base)).add(Instruction.AccessProperty).add(false).add(expression.p.name, IntType.Short)
+ }
+
+ method encodeStaticPropertyAccess(StaticPropertyAccessExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.AccessProperty).add(true).add(expression.base.toString(), IntType.Short).add(expression.p.name, IntType.Short)
+ }
+
+ method encodeNonStaticPropertyAssignment(NonStaticPropertyAssignExpression expression) returns ByteList{
+ return new ByteList().add(this.encode(expression.base)).add(this.encode(expression.value)).add(Instruction.UpdateProperty).add(false).add(expression.p oftype CustomReactiveProperty).add(expression.p.name, IntType.Short)
+ }
+
+ method encodeStaticPropertyAssignment(StaticPropertyAssignExpression expression) returns ByteList{
+ return new ByteList().add(this.encode(expression.value)).add(Instruction.UpdateProperty).add(true).add(expression.p oftype CustomReactiveProperty).add(expression.base.toString(), IntType.Short).add(expression.p.name, IntType.Short)
+ }
+
+ method encodeListIndex(ListIndexExpression expression) returns ByteList{
+ return new ByteList().add(this.encode(expression.base)).add(this.encode(expression.index)).add(Instruction.IndexCollection)
+ }
+
+ method encodeListAssignment(ListAssignExpression expression) returns ByteList{
+ return new ByteList().add(this.encode(expression.base)).add(this.encode(expression.index)).add(this.encode(expression.value)).add(Instruction.UpdateCollection)
+ }
+
+ method encodeMapAccess(MapAccessExpression expression) returns ByteList{
+ return new ByteList().add(this.encode(expression.base)).add(this.encode(expression.key)).add(Instruction.IndexCollection)
+ }
+
+ method encodeMapAssignment(MapAssignExpression expression) returns ByteList{
+ return new ByteList().add(this.encode(expression.base)).add(this.encode(expression.key)).add(this.encode(expression.value)).add(Instruction.UpdateCollection)
+ }
+
+ method encodeStringIndex(StringIndexExpression expression) returns ByteList{
+ return new ByteList().add(this.encode(expression.base)).add(this.encode(expression.index)).add(Instruction.IndexCollection)
+ }
+
+ method encodeClassDeclaration(ClassDeclareStatement statement) returns ByteList{
+ var bytes = new ByteList()
+ bytes.add(Instruction.DeclareClass)
+ bytes.add(statement.c.isStatic)
+ bytes.add(statement.c.type.toString(), IntType.Short).add(list(statement.c.parents).length, IntType.Short)
+ foreach(list(statement.c.parents) as parent){
+ bytes.add(parent.toString(), IntType.Short)
+ }
+ bytes.add(statement.c.isError)
+ var body = new ByteList()
+ foreach(statement.c.code as s){
+ body.add(this.encode(s, true))
+ }
+ body.add(Instruction.End)
+ bytes.add(body)
+ return bytes
+ }
+
+ method encodeClassInstantiation(ClassInstantiateExpression expression) returns ByteList{
+ var bytes = new ByteList()
+ foreach(expression.arguments as argument){
+ bytes.add(this.encode(argument))
+ }
+ bytes.add(Instruction.NewStatement).add(expression.c.type.toString(), IntType.Short).add(expression.arguments.length)
+ return bytes
+ }
+
+ method encodeMethodDeclaration(MethodDeclareStatement statement) returns ByteList{
+ var bytes = new ByteList()
+ foreach(statement.m.parameters as parameter){
+ bytes.add(encodeParameterValue(parameter))
+ }
+ bytes.add(Instruction.DeclareMethod)
+ bytes.add(statement.m.isStatic)
+ bytes.add(statement.m.name, IntType.Short)
+ bytes.add(statement.m.parameters.length, IntType.Short)
+ foreach(statement.m.parameters as parameter){
+ bytes.add(encodeParameterMeta(parameter))
+ }
+ bytes.add(statement.m.returnTypes.types.length, IntType.Short)
+ foreach(statement.m.returnTypes.types as returnType){
+ bytes.add(TypeUtils:shortName(returnType.toString()), IntType.Short)
+ }
+ var body = new ByteList()
+ foreach(CustomMethod(statement.m).code as s){
+ body.add(this.encode(s, true))
+ }
+ body.add(Instruction.End)
+ bytes.add(body.length, IntType.Long)
+ bytes.add(body)
+ return bytes
+ }
+
+ method encodePropertyDeclaration(PropertyDeclareStatement statement) returns ByteList{
+ var bytes = new ByteList()
+ if(statement.p oftype CustomNormalProperty){
+ if(CustomNormalProperty(statement.p).isStatic){
+ if(CustomNormalProperty(statement.p).defaultValue != null){
+ bytes.add(this.encode(CustomNormalProperty(statement.p).defaultValue))
+ }else{
+ bytes.add(this.encode(new NullLiteral(null)))
+ }
+ }
+ }
+ bytes.add(Instruction.DeclareProperty)
+ bytes.add(statement.p.isStatic)
+ bytes.add(statement.p.isThreadLocal)
+ bytes.add(statement.p.name, IntType.Short)
+ bytes.add(statement.p.types.types.length, IntType.Short)
+ foreach(statement.p.types.types as type){
+ bytes.add(TypeUtils:shortName(type.toString()), IntType.Short)
+ }
+ if(statement.p oftype CustomReactiveProperty){
+ bytes.add(1b)
+ if(CustomReactiveProperty(statement.p).getCode == null){
+ bytes.add(-1, IntType.Long)
+ }else{
+ var getCode = new ByteList()
+ foreach(CustomReactiveProperty(statement.p).getCode as s){
+ getCode.add(this.encode(s, true))
+ }
+ getCode.add(Instruction.End)
+ bytes.add(getCode.length, IntType.Long)
+ bytes.add(getCode)
+ }
+ if(CustomReactiveProperty(statement.p).setCode == null){
+ bytes.add(-1, IntType.Long)
+ }else{
+ var setCode = new ByteList()
+ foreach(CustomReactiveProperty(statement.p).setCode as s){
+ setCode.add(this.encode(s, true))
+ }
+ setCode.add(Instruction.End)
+ bytes.add(setCode.length, IntType.Long)
+ bytes.add(setCode)
+ }
+ }else{
+ bytes.add(0b)
+ }
+ return bytes
+ }
+
+ method encodeThis(ThisExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.ThisStatement)
+ }
+
+ method encodeEnumDeclaration(EnumDeclareStatement statement) returns ByteList{
+ var bytes = new ByteList()
+ var list keys = []
+ foreach(map(statement.e.fields) as key => _){
+ keys.add(key)
+ }
+ bytes.add(Instruction.DeclareEnum).add(statement.e.type.toString(), IntType.Short).add(map(statement.e.fields).length).add(statement.e.isFlags)
+ foreach(map(statement.e.fields) as field){
+ bytes.add(field.name, IntType.Short)
+ bytes.add(field.value)
+ }
+ return bytes
+ }
+
+ method encodeEnumFieldAccess(EnumFieldAccessExpression expression) returns ByteList{
+ var e = Enum:enums[expression.field.e.type.toString()] // TODO: Figure out if these workarounds can be simplified
+ var intValue = e.fields?![expression.field.name].value
+ return new ByteList().add(Instruction.EnumField).add(expression.field.e.type.toString(), IntType.Short).add(expression.field.name, IntType.Short).add(intValue)
+ }
+
+ method encodeCallbackLiteral(CallbackLiteral literal) returns ByteList{
+ var bytes = new ByteList()
+ foreach(literal.value.parameters as parameter){
+ bytes.add(encodeParameterValue(parameter))
+ }
+ bytes.add(Instruction.DeclareCallback)
+ bytes.add(literal.value.type.toString(), IntType.Short)
+ bytes.add(literal.value.parameters.length, IntType.Short)
+ foreach(literal.value.parameters as parameter){
+ bytes.add(encodeParameterMeta(parameter))
+ }
+ bytes.add(literal.value.returnTypes.types.length, IntType.Short)
+ foreach(literal.value.returnTypes.types as returnType){
+ bytes.add(TypeUtils:shortName(returnType.toString()), IntType.Short)
+ }
+ var body = new ByteList()
+ foreach(literal.value.code as s){
+ body.add(this.encode(s, true))
+ }
+ body.add(Instruction.End)
+ bytes.add(body.length, IntType.Long)
+ bytes.add(body)
+ return bytes
+ }
+
+ method encodeImplementationCall(ImplementationCallExpression expression) returns ByteList{
+ var bytes = new ByteList()
+ foreach(expression.arguments as argument){
+ bytes.add(this.encode(argument))
+ }
+ bytes.add(Instruction.Implement).add(expression.call, IntType.Long).add(expression.arguments.length)
+ return bytes
+ }
+
+ method encodeOfType(OfTypeExpression expression) returns ByteList{
+ var bytes = new ByteList()
+ bytes.add(this.encode(expression.expression))
+ bytes.add(Instruction.OfType)
+ bytes.add(TypeUtils:shortName(expression.type.toString()), IntType.Short)
+ return bytes
+ }
+
+ method encodeBreakStatement(BreakStatement statement) returns ByteList{
+ return new ByteList().add(Instruction.BreakStatement)
+ .add(false) // true would be legacy mode, which interprets if as a breakable/continuable statement
+ .add(statement.level)
+ }
+
+ method encodeContinueStatement(ContinueStatement statement) returns ByteList{
+ return new ByteList().add(Instruction.ContinueStatement)
+ .add(false) // true would be legacy mode, which interprets if as a breakable/continuable statement
+ .add(statement.level)
+ }
+
+ method encodeCast(CastExpression expression) returns ByteList{
+ return new ByteList().add(this.encode(expression.value)).add(Instruction.Cast).add(TypeUtils:shortName(expression.type.toString()), IntType.Short)
+ }
+
+ method encodeThrowStatement(ThrowStatement statement) returns ByteList{
+ return new ByteList().add(this.encode(statement.errorInstance)).add(Instruction.Throw)
+ }
+
+ method encodeCatchExpression(CatchExpression expression) returns ByteList{
+ var bytes = new ByteList()
+ bytes.add(this.encode(expression.expression))
+ bytes.add(Instruction.Catch)
+ var variable = ""
+ if(expression.variable != null){
+ variable = expression.variable?!
+ }
+ bytes.add(variable, IntType.Short)
+ var body = new ByteList()
+ foreach(expression.code as s){
+ body.add(this.encode(s, true))
+ }
+ body.add(Instruction.End)
+ bytes.add(body.length, IntType.Long)
+ bytes.add(body)
+ return bytes
+ }
+
+ method encodeErrorPropagation(PropagateErrorExpression expression) returns ByteList{
+ return new ByteList().add(this.encode(expression.expression)).add(Instruction.PropagateError)
+ }
+
+ method encodeFileEmbedding(EmbedFileExpression expression) returns ByteList{
+ var bytes = io.read_file_bytes(expression.file)
+ return new ByteList().add(Instruction.ByteArrayLiteral).add(bytes.length, IntType.Long).add(bytes)
+ }
+
+ method encodeDiscard(Expression expression) returns ByteList{
+ return this.encode(expression).add(Instruction.Pop)
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/backend/bytecode/ail/Instruction.aspl b/stdlib/aspl/compiler/backend/bytecode/ail/Instruction.aspl
new file mode 100644
index 0000000..4592bb4
--- /dev/null
+++ b/stdlib/aspl/compiler/backend/bytecode/ail/Instruction.aspl
@@ -0,0 +1,67 @@
+enum Instruction {
+ Manifest,
+ CreateObject,
+ ByteArrayLiteral,
+ Implement,
+ JumpRelative,
+ JumpRelativeIfFalse,
+ DeclareFunction,
+ CallFunction,
+ Add,
+ Increment,
+ Subtract,
+ Decrement,
+ Multiply,
+ Divide,
+ Modulo,
+ LessThan,
+ LessEqual,
+ GreaterThan,
+ GreaterEqual,
+ Equals,
+ NotEquals,
+ BooleanAnd,
+ BitwiseAnd,
+ BooleanOr,
+ BitwiseOr,
+ BooleanXor,
+ BitwiseXor,
+ RegisterVariable,
+ AccessVariable,
+ UpdateVariable,
+ DeclareMethod,
+ CallMethod,
+ CallExactMethod,
+ BreakStatement,
+ ContinueStatement,
+ ReturnStatement,
+ FallbackStatement,
+ EscapeStatement,
+ Negate,
+ CountCollection,
+ AccessCollectionKeyWithIndex,
+ AccessCollectionValueWithIndex,
+ IndexCollection,
+ UpdateCollection,
+ DeclareCallback,
+ DeclareClass,
+ NewStatement,
+ ThisStatement,
+ DeclareProperty,
+ AccessProperty,
+ UpdateProperty,
+ DeclareEnum,
+ EnumField,
+ DeclarePointer,
+ DereferencePointer,
+ Cast,
+ Assert,
+ OfType,
+ SpecifyLoop,
+ PopLoop,
+ Throw,
+ Catch,
+ PropagateError,
+ Pop,
+ End
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/backend/bytecode/ail/TypeUtils.aspl b/stdlib/aspl/compiler/backend/bytecode/ail/TypeUtils.aspl
new file mode 100644
index 0000000..96dd4fa
--- /dev/null
+++ b/stdlib/aspl/compiler/backend/bytecode/ail/TypeUtils.aspl
@@ -0,0 +1,17 @@
+[public]
+[static]
+class TypeUtils {
+
+ [public]
+ [static]
+ method shortName(string s) returns string{
+ if(s == "integer"){
+ return "int"
+ }elseif(s == "boolean"){
+ return "bool"
+ }else{
+ return s
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/backend/bytecode/twail/Instruction.aspl b/stdlib/aspl/compiler/backend/bytecode/twail/Instruction.aspl
new file mode 100644
index 0000000..9fd4b41
--- /dev/null
+++ b/stdlib/aspl/compiler/backend/bytecode/twail/Instruction.aspl
@@ -0,0 +1,56 @@
+enum Instruction {
+ Manifest,
+ CreateObject,
+ Implement,
+ CreatePair,
+ DeclareFunction,
+ CallFunction,
+ IfStatement,
+ IfElseStatement,
+ WhileStatement,
+ RepeatStatement,
+ ForeachStatement,
+ Add,
+ Subtract,
+ Multiply,
+ Divide,
+ Modulo,
+ LessThan,
+ LessEqual,
+ GreaterThan,
+ GreaterEqual,
+ Equals,
+ And,
+ Or,
+ Xor,
+ RegisterVariable,
+ AccessVariable,
+ UpdateVariable,
+ DeclareMethod,
+ CallMethod,
+ BreakStatement,
+ ContinueStatement,
+ ReturnStatement,
+ FallbackStatement,
+ EscapeStatement,
+ Negate,
+ IndexCollection,
+ UpdateCollection,
+ DeclareCallback,
+ DeclareClass,
+ NewStatement,
+ ThisStatement,
+ DeclareProperty,
+ AccessProperty,
+ UpdateProperty,
+ DeclareEnum,
+ EnumField,
+ DeclarePointer,
+ DereferencePointer,
+ Cast,
+ Assert,
+ OfType,
+ Throw,
+ Catch,
+ PropagateError
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/backend/bytecode/twail/TreeWalkingAILBytecodeBackend.aspl b/stdlib/aspl/compiler/backend/bytecode/twail/TreeWalkingAILBytecodeBackend.aspl
new file mode 100644
index 0000000..75732ff
--- /dev/null
+++ b/stdlib/aspl/compiler/backend/bytecode/twail/TreeWalkingAILBytecodeBackend.aspl
@@ -0,0 +1,601 @@
+import aspl.compiler
+import aspl.compiler.backend
+import aspl.compiler.backend.bytecode
+import aspl.parser
+import aspl.parser.ast
+import aspl.parser.ast.statements
+import aspl.parser.ast.expressions
+import aspl.parser.ast.literals
+import aspl.parser.functions
+import aspl.parser.properties
+import aspl.parser.methods
+import aspl.parser.enums
+import aspl.parser.utils
+
+[public]
+// NOTE: This backend is deprecated; only use it if you actually need it!
+class TreeWalkingAILBytecodeBackend extends BytecodeBackend {
+
+ [public]
+ method compile(ParserResult result) returns CompilationResult{
+ var output = new ByteList()
+ output.add(Instruction.Manifest)
+ var manifestBody = new ByteList()
+ // TODO: Encode manifest information
+ output.add(manifestBody.length)
+ output.add(manifestBody)
+ foreach(result.nodes as node){
+ output.add(this.encode(node))
+ }
+ return new CompilationResult(output.bytes)
+ }
+
+ method encodeCodeBlock(BlockStatement statement) returns ByteList{
+ var bytes = new ByteList()
+ foreach(statement.statements as s){
+ bytes.add(this.encode(s))
+ }
+ return bytes
+ }
+
+ method encodeNullLiteral(NullLiteral literal) returns ByteList{
+ return new ByteList().add(Instruction.CreateObject).add("null", IntType.Short)
+ }
+
+ method encodeBooleanLiteral(BooleanLiteral literal) returns ByteList{
+ return new ByteList().add(Instruction.CreateObject).add("bool", IntType.Short).add(literal.value) // "bool" instead of "boolean" for speed and minimal size
+ }
+
+ method encodeByteLiteral(ByteLiteral literal) returns ByteList{
+ return new ByteList().add(Instruction.CreateObject).add("byte", IntType.Short).add(literal.value)
+ }
+
+ method encodeIntegerLiteral(IntegerLiteral literal) returns ByteList{
+ return new ByteList().add(Instruction.CreateObject).add("int", IntType.Short).add(literal.value) // "int" instead of "integer" for speed and minimal size
+ }
+
+ method encodeLongLiteral(LongLiteral literal) returns ByteList{
+ return new ByteList().add(Instruction.CreateObject).add("long", IntType.Short).add(literal.value)
+ }
+
+ method encodeFloatLiteral(FloatLiteral literal) returns ByteList{
+ return new ByteList().add(Instruction.CreateObject).add("float", IntType.Short).add(literal.value)
+ }
+
+ method encodeDoubleLiteral(DoubleLiteral literal) returns ByteList{
+ return new ByteList().add(Instruction.CreateObject).add("double", IntType.Short).add(literal.value)
+ }
+
+ method encodeStringLiteral(StringLiteral literal) returns ByteList{
+ return new ByteList().add(Instruction.CreateObject).add("string", IntType.Short).add(literal.value, IntType.Long)
+ }
+
+ method encodeListLiteral(ListLiteral literal) returns ByteList{
+ var bytes = new ByteList().add(Instruction.CreateObject).add("list", IntType.Short).add(literal.type.types.length, IntType.Short)
+ foreach(literal.type.types as type){
+ bytes.add(type.toString(), IntType.Short)
+ }
+ bytes.add(literal.value.length)
+ foreach(literal.value as element){
+ bytes.add(this.encode(element))
+ }
+ return bytes
+ }
+
+ method encodeMapLiteral(MapLiteral literal) returns ByteList{
+ var bytes = new ByteList().add(Instruction.CreateObject).add("map", IntType.Short).add(literal.keyType.types.length, IntType.Short)
+ foreach(literal.keyType.types as type){
+ bytes.add(type.toString(), IntType.Short)
+ }
+ bytes.add(literal.valueType.types.length, IntType.Short)
+ foreach(literal.valueType.types as type){
+ bytes.add(type.toString(), IntType.Short)
+ }
+ bytes.add(literal.value.length)
+ foreach(literal.value as pair){
+ bytes.add(Instruction.CreatePair)
+ bytes.add(this.encode(pair.k))
+ bytes.add(this.encode(pair.v))
+ }
+ return bytes
+ }
+
+ method encodeAssertion(AssertStatement statement) returns ByteList{
+ return new ByteList().add(Instruction.Assert).add(Location(statement.location).file, IntType.Short).add(Location(statement.location).startLine).add(Location(statement.location).startColumn).add(this.encode(statement.expression))
+ }
+
+ method encodeEqualsCheck(CheckEqualsExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.Equals).add(this.encode(expression.left)).add(this.encode(expression.right))
+ }
+
+ method encodeNegation(NegateExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.Negate).add(this.encode(expression.expression))
+ }
+
+ method encodeFunctionCall(FunctionCallExpression expression) returns ByteList{
+ var identifier = expression.func.identifier
+ if(expression.func oftype InternalFunction){
+ identifier = "_" + identifier
+ }
+ var bytes = new ByteList().add(Instruction.CallFunction).add(identifier, IntType.Long).add(expression.newThread).add(expression.arguments.length, IntType.Short)
+ foreach(expression.arguments as argument){
+ bytes.add(this.encode(argument))
+ }
+ return bytes
+ }
+
+ method encodeVariableDeclaration(VariableDeclareExpression expression) returns ByteList{
+ var bytes = new ByteList().add(Instruction.RegisterVariable).add(expression.variable.identifier, IntType.Short).add(expression.getType().types.length, IntType.Short)
+ foreach(expression.getType().types as type){
+ bytes.add(TypeUtils:shortName(type.toString()), IntType.Short)
+ }
+ bytes.add(this.encode(expression.value))
+ return bytes
+ }
+
+ method encodeVariableAccess(VariableAccessExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.AccessVariable).add(expression.variable.identifier, IntType.Short)
+ }
+
+ method encodeVariableAssignment(VariableAssignExpression expression) returns ByteList{
+ var bytes = new ByteList().add(Instruction.UpdateVariable).add(expression.variable.identifier, IntType.Short)
+ bytes.add(this.encode(expression.value))
+ return bytes
+ }
+
+ method encodeFunctionDeclaration(FunctionDeclareStatement statement) returns ByteList{
+ var bytes = new ByteList().add(Instruction.DeclareFunction)
+ bytes.add(statement.func.identifier, IntType.Short).add(statement.func.parameters.length, IntType.Short)
+ foreach(statement.func.parameters as parameter){
+ bytes.add(encodeParameter(parameter))
+ }
+ bytes.add(statement.func.returnTypes.types.length, IntType.Short)
+ foreach(statement.func.returnTypes.types as returnType){
+ bytes.add(TypeUtils:shortName(returnType.toString()), IntType.Short)
+ }
+ var body = new ByteList()
+ foreach(CustomFunction(statement.func).code as s){
+ body.add(this.encode(s))
+ }
+ bytes.add(body.length, IntType.Long)
+ bytes.add(body)
+ return bytes
+ }
+
+ method encodeParameter(Parameter parameter) returns ByteList{
+ var bytes = new ByteList().add(parameter.name, IntType.Short).add(parameter.types.types.length, IntType.Short)
+ foreach(parameter.types.types as type){
+ bytes.add(TypeUtils:shortName(type.toString()), IntType.Short)
+ }
+ if(parameter.optional){
+ bytes.add(true)
+ bytes.add(this.encode(parameter.defaultValue))
+ }else{
+ bytes.add(false)
+ }
+ return bytes
+ }
+
+ method encodeReturnStatement(ReturnStatement statement) returns ByteList{
+ var bytes = new ByteList().add(Instruction.ReturnStatement)
+ if(statement.value != null){
+ bytes.add(this.encode(statement.value))
+ }else{
+ bytes.add(encodeNullLiteral(new NullLiteral(null)))
+ }
+ return bytes
+ }
+
+ method encodeFallbackStatement(FallbackStatement statement) returns ByteList{
+ var bytes = new ByteList().add(Instruction.FallbackStatement)
+ bytes.add(this.encode(statement.value))
+ return bytes
+ }
+
+ method encodeEscapeStatement(EscapeStatement statement) returns ByteList{
+ var bytes = new ByteList().add(Instruction.EscapeStatement)
+ bytes.add(this.encode(statement.value))
+ return bytes
+ }
+
+ method encodeAnd(AndExpression expression) returns ByteList{
+ var right = this.encode(expression.right)
+ return new ByteList().add(Instruction.And).add(this.encode(expression.left)).add(right.length).add(right)
+ }
+
+ method encodeOr(OrExpression expression) returns ByteList{
+ var right = this.encode(expression.right)
+ return new ByteList().add(Instruction.Or).add(this.encode(expression.left)).add(right.length).add(right)
+ }
+
+ method encodeXor(XorExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.Xor).add(this.encode(expression.left)).add(this.encode(expression.right))
+ }
+
+ method encodePlus(PlusExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.Add).add(this.encode(expression.left)).add(this.encode(expression.right))
+ }
+
+ method encodeMinus(MinusExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.Subtract).add(this.encode(expression.left)).add(this.encode(expression.right))
+ }
+
+ method encodeMultiplication(MultiplyExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.Multiply).add(this.encode(expression.left)).add(this.encode(expression.right))
+ }
+
+ method encodeDivision(DivideExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.Divide).add(this.encode(expression.left)).add(this.encode(expression.right))
+ }
+
+ method encodeModulo(ModuloExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.Modulo).add(this.encode(expression.left)).add(this.encode(expression.right))
+ }
+
+ method encodeLessThan(LessThanExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.LessThan).add(this.encode(expression.left)).add(this.encode(expression.right))
+ }
+
+ method encodeLessThanOrEqual(LessThanOrEqualExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.LessEqual).add(this.encode(expression.left)).add(this.encode(expression.right))
+ }
+
+ method encodeGreaterThan(GreaterThanExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.GreaterThan).add(this.encode(expression.left)).add(this.encode(expression.right))
+ }
+
+ method encodeGreaterThanOrEqual(GreaterThanOrEqualExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.GreaterEqual).add(this.encode(expression.left)).add(this.encode(expression.right))
+ }
+
+ method encodeReference(ReferenceExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.DeclarePointer).add(this.encode(expression.expression))
+ }
+
+ method encodeDereference(DereferenceExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.DereferencePointer).add(this.encode(expression.pointer))
+ }
+
+ method encodeIfStatement(IfStatement statement) returns ByteList{
+ var bytes = new ByteList().add(Instruction.IfStatement)
+ var body = new ByteList()
+ foreach(statement.code as s){
+ body.add(this.encode(s))
+ }
+ bytes.add(body.length, IntType.Long)
+ bytes.add(this.encode(statement.condition))
+ bytes.add(body)
+ return bytes
+ }
+
+ method encodeIfElseStatement(IfElseStatement statement) returns ByteList{
+ var bytes = new ByteList().add(Instruction.IfElseStatement)
+ var ifBody = new ByteList()
+ foreach(statement.ifCode as s){
+ ifBody.add(this.encode(s))
+ }
+ bytes.add(ifBody.length, IntType.Long)
+ bytes.add(this.encode(statement.condition))
+ bytes.add(ifBody)
+ var elseBody = new ByteList()
+ foreach(statement.elseCode as s){
+ elseBody.add(this.encode(s))
+ }
+ bytes.add(elseBody.length, IntType.Long)
+ bytes.add(elseBody)
+ return bytes
+ }
+
+ method encodeIfElseIfStatement(IfElseIfStatement statement) returns ByteList{
+ var bytes = new ByteList().add(Instruction.IfElseStatement)
+ var ifBody = new ByteList()
+ foreach(statement.ifCode as s){
+ ifBody.add(this.encode(s))
+ }
+ bytes.add(ifBody.length, IntType.Long)
+ bytes.add(this.encode(statement.condition))
+ bytes.add(ifBody)
+ var elseBody = this.encode(statement.elseIf)
+ bytes.add(elseBody.length, IntType.Long)
+ bytes.add(elseBody)
+ return bytes
+ }
+
+ method encodeWhileStatement(WhileStatement statement) returns ByteList{
+ var bytes = new ByteList().add(Instruction.WhileStatement)
+ var body = new ByteList()
+ foreach(statement.code as s){
+ body.add(this.encode(s))
+ }
+ bytes.add(body.length, IntType.Long)
+ bytes.add(this.encode(statement.condition))
+ bytes.add(body)
+ return bytes
+ }
+
+ method encodeRepeatStatement(RepeatStatement statement) returns ByteList{
+ var bytes = new ByteList().add(Instruction.RepeatStatement)
+ var body = new ByteList()
+ foreach(statement.code as s){
+ body.add(this.encode(s))
+ }
+ bytes.add(body.length, IntType.Long)
+ bytes.add(this.encode(statement.iterations))
+ if(statement.variable == null){
+ bytes.add("_", IntType.Short)
+ }else{
+ bytes.add(statement.variable, IntType.Short)
+ }
+ bytes.add(statement.start)
+ bytes.add(body)
+ return bytes
+ }
+
+ method encodeForeachStatement(ForeachStatement statement) returns ByteList{
+ var bytes = new ByteList().add(Instruction.ForeachStatement)
+ var body = new ByteList()
+ foreach(statement.code as s){
+ body.add(this.encode(s))
+ }
+ bytes.add(body.length, IntType.Long)
+ bytes.add(this.encode(statement.collection))
+ if(statement.key == null){
+ bytes.add("_", IntType.Long)
+ }else{
+ bytes.add(statement.key, IntType.Long)
+ }
+ if(statement.value == null){
+ bytes.add("_", IntType.Long)
+ }else{
+ bytes.add(statement.value, IntType.Long)
+ }
+ bytes.add(body)
+ return bytes
+ }
+
+ method encodeNonStaticMethodCall(NonStaticMethodCallExpression expression) returns ByteList{
+ var bytes = new ByteList().add(Instruction.CallMethod)
+ .add(false)
+ .add(this.encode(expression.base)).add(expression.m.name, IntType.Short).add(expression.newThread).add(expression.arguments.length, IntType.Short)
+ foreach(expression.arguments as argument){
+ bytes.add(this.encode(argument))
+ }
+ return bytes
+ }
+
+ method encodeStaticMethodCall(StaticMethodCallExpression expression) returns ByteList{
+ var bytes = new ByteList().add(Instruction.CallMethod)
+ .add(true)
+ .add(expression.base.toString(), IntType.Short).add(expression.m.name, IntType.Short).add(expression.newThread).add(expression.arguments.length, IntType.Short)
+ foreach(expression.arguments as argument){
+ bytes.add(this.encode(argument))
+ }
+ return bytes
+ }
+
+ method encodeNonStaticPropertyAccess(NonStaticPropertyAccessExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.AccessProperty)
+ .add(false)
+ .add(this.encode(expression.base)).add(expression.p.name, IntType.Short)
+ }
+
+ method encodeStaticPropertyAccess(StaticPropertyAccessExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.AccessProperty)
+ .add(true)
+ .add(expression.base.toString(), IntType.Short).add(expression.p.name, IntType.Short)
+ }
+
+ method encodeNonStaticPropertyAssignment(NonStaticPropertyAssignExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.UpdateProperty)
+ .add(false)
+ .add(this.encode(expression.base), IntType.Short).add(expression.p.name, IntType.Short).add(this.encode(expression.value))
+ }
+
+ method encodeStaticPropertyAssignment(StaticPropertyAssignExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.UpdateProperty)
+ .add(true)
+ .add(expression.base.toString(), IntType.Short).add(expression.p.name, IntType.Short).add(this.encode(expression.value))
+ }
+
+ method encodeListIndex(ListIndexExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.IndexCollection).add(this.encode(expression.base)).add(this.encode(expression.index))
+ }
+
+ method encodeListAssignment(ListAssignExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.UpdateCollection).add(this.encode(expression.base)).add(this.encode(expression.index)).add(this.encode(expression.value))
+ }
+
+ method encodeMapAccess(MapAccessExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.IndexCollection).add(this.encode(expression.base)).add(this.encode(expression.key))
+ }
+
+ method encodeMapAssignment(MapAssignExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.UpdateCollection).add(this.encode(expression.base)).add(this.encode(expression.key)).add(this.encode(expression.value))
+ }
+
+ method encodeStringIndex(StringIndexExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.IndexCollection).add(this.encode(expression.base)).add(this.encode(expression.index))
+ }
+
+ method encodeClassDeclaration(ClassDeclareStatement statement) returns ByteList{
+ var bytes = new ByteList().add(Instruction.DeclareClass)
+ bytes.add(statement.c.isStatic)
+ bytes.add(statement.c.type.toString(), IntType.Short).add(list(statement.c.parents).length, IntType.Short)
+ foreach(list(statement.c.parents) as parent){
+ bytes.add(parent.toString(), IntType.Short)
+ }
+ var body = new ByteList()
+ foreach(statement.c.code as s){
+ body.add(this.encode(s))
+ }
+ bytes.add(body.length, IntType.Long)
+ bytes.add(body)
+ return bytes
+ }
+
+ method encodeClassInstantiation(ClassInstantiateExpression expression) returns ByteList{
+ var bytes = new ByteList().add(Instruction.NewStatement).add(expression.c.type.toString(), IntType.Short).add(expression.arguments.length, IntType.Short)
+ foreach(expression.arguments as argument){
+ bytes.add(this.encode(argument))
+ }
+ return bytes
+ }
+
+ method encodeMethodDeclaration(MethodDeclareStatement statement) returns ByteList{
+ var bytes = new ByteList().add(Instruction.DeclareMethod)
+ bytes.add(statement.m.isStatic)
+ bytes.add(statement.m.name, IntType.Short)
+ bytes.add(statement.m.parameters.length, IntType.Short)
+ foreach(statement.m.parameters as parameter){
+ bytes.add(encodeParameter(parameter))
+ }
+ bytes.add(statement.m.returnTypes.types.length, IntType.Short)
+ foreach(statement.m.returnTypes.types as returnType){
+ bytes.add(TypeUtils:shortName(returnType.toString()), IntType.Short)
+ }
+ var body = new ByteList()
+ foreach(CustomMethod(statement.m).code as s){
+ body.add(this.encode(s))
+ }
+ bytes.add(body.length, IntType.Long)
+ bytes.add(body)
+ return bytes
+ }
+
+ method encodePropertyDeclaration(PropertyDeclareStatement statement) returns ByteList{
+ var bytes = new ByteList().add(Instruction.DeclareProperty)
+ bytes.add(statement.p.isStatic)
+ bytes.add(statement.p.isThreadLocal)
+ bytes.add(statement.p.name, IntType.Short)
+ bytes.add(statement.p.types.types.length, IntType.Short)
+ foreach(statement.p.types.types as type){
+ bytes.add(TypeUtils:shortName(type.toString()), IntType.Short)
+ }
+ if(statement.p oftype CustomReactiveProperty){
+ bytes.add(1b)
+ if(CustomReactiveProperty(statement.p).getCode == null){
+ bytes.add(-1, IntType.Long)
+ }else{
+ var getCode = new ByteList()
+ foreach(CustomReactiveProperty(statement.p).getCode as s){
+ getCode.add(this.encode(s))
+ }
+ bytes.add(getCode.length, IntType.Long)
+ bytes.add(getCode)
+ }
+ if(CustomReactiveProperty(statement.p).setCode == null){
+ bytes.add(-1, IntType.Long)
+ }else{
+ var setCode = new ByteList()
+ foreach(CustomReactiveProperty(statement.p).setCode as s){
+ setCode.add(this.encode(s))
+ }
+ bytes.add(setCode.length, IntType.Long)
+ bytes.add(setCode)
+ }
+ }else{
+ bytes.add(0b)
+ if(CustomNormalProperty(statement.p).isStatic){
+ if(CustomNormalProperty(statement.p).defaultValue != null){
+ bytes.add(this.encode(CustomNormalProperty(statement.p).defaultValue))
+ }
+ }
+ }
+ return bytes
+ }
+
+ method encodeThis(ThisExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.ThisStatement)
+ }
+
+ method encodeEnumDeclaration(EnumDeclareStatement statement) returns ByteList{
+ var bytes = new ByteList().add(Instruction.DeclareEnum)
+ bytes.add(statement.e.type.toString(), IntType.Short)
+ bytes.add(map(statement.e.fields).length)
+ foreach(map(statement.e.fields) as field){
+ bytes.add(field.name, IntType.Short)
+ bytes.add(field.value)
+ }
+ return bytes
+ }
+
+ method encodeEnumFieldAccess(EnumFieldAccessExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.EnumField).add(expression.field.e.type.toString(), IntType.Short).add(expression.field.name, IntType.Short)
+ }
+
+ method encodeCallbackLiteral(CallbackLiteral literal) returns ByteList{
+ var bytes = new ByteList().add(Instruction.DeclareCallback)
+ bytes.add(literal.value.type.toString(), IntType.Short)
+ bytes.add(literal.value.parameters.length, IntType.Short)
+ foreach(literal.value.parameters as parameter){
+ bytes.add(encodeParameter(parameter))
+ }
+ bytes.add(literal.value.returnTypes.types.length, IntType.Short)
+ foreach(literal.value.returnTypes.types as returnType){
+ bytes.add(TypeUtils:shortName(returnType.toString()), IntType.Short)
+ }
+ var body = new ByteList()
+ foreach(literal.value.code as s){
+ body.add(this.encode(s))
+ }
+ bytes.add(body.length, IntType.Long)
+ bytes.add(body)
+ return bytes
+ }
+
+ method encodeImplementationCall(ImplementationCallExpression expression) returns ByteList{
+ var bytes = new ByteList().add(Instruction.Implement).add(expression.call, IntType.Long).add(expression.arguments.length, IntType.Short)
+ foreach(expression.arguments as argument){
+ bytes.add(this.encode(argument))
+ }
+ return bytes
+ }
+
+ method encodeOfType(OfTypeExpression expression) returns ByteList{
+ var bytes = new ByteList().add(Instruction.OfType)
+ bytes.add(this.encode(expression.expression))
+ bytes.add(expression.type.toString(), IntType.Short)
+ return bytes
+ }
+
+ method encodeBreakStatement(BreakStatement statement) returns ByteList{
+ return new ByteList().add(Instruction.BreakStatement)
+ .add(false) // true would be legacy mode, which interprets if as a breakable/continuable statement
+ .add(statement.level)
+ }
+
+ method encodeContinueStatement(ContinueStatement statement) returns ByteList{
+ return new ByteList().add(Instruction.ContinueStatement)
+ .add(false) // true would be legacy mode, which interprets if as a breakable/continuable statement
+ .add(statement.level)
+ }
+
+ method encodeCast(CastExpression expression) returns ByteList{
+ return new ByteList().add(Instruction.Cast).add(this.encode(expression.value)).add(expression.type.toString(), IntType.Short)
+ }
+
+ method encodeThrowStatement(ThrowStatement statement) returns ByteList{
+ // TODO
+ }
+
+ method encodeCatchExpression(CatchExpression expression) returns ByteList{
+ // TODO
+ }
+
+ method encodeErrorPropagation(PropagateErrorExpression expression) returns ByteList{
+ // TODO
+ }
+
+ method encodeFileEmbedding(EmbedFileExpression expression) returns ByteList{
+ var bytes = list[]
+ foreach(io.read_file_bytes(expression.file) as b){
+ bytes.add(new ByteLiteral(b, expression.location))
+ }
+ return encodeListLiteral(new ListLiteral(new Types([Type:fromString("byte")]), bytes, expression.location))
+ }
+
+ method encodeDiscard(Expression expression) returns ByteList{
+ return this.encode(expression)
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/backend/bytecode/twail/TypeUtils.aspl b/stdlib/aspl/compiler/backend/bytecode/twail/TypeUtils.aspl
new file mode 100644
index 0000000..96dd4fa
--- /dev/null
+++ b/stdlib/aspl/compiler/backend/bytecode/twail/TypeUtils.aspl
@@ -0,0 +1,17 @@
+[public]
+[static]
+class TypeUtils {
+
+ [public]
+ [static]
+ method shortName(string s) returns string{
+ if(s == "integer"){
+ return "int"
+ }elseif(s == "boolean"){
+ return "bool"
+ }else{
+ return s
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/backend/stringcode/StringcodeBackend.aspl b/stdlib/aspl/compiler/backend/stringcode/StringcodeBackend.aspl
new file mode 100644
index 0000000..2ff8ae8
--- /dev/null
+++ b/stdlib/aspl/compiler/backend/stringcode/StringcodeBackend.aspl
@@ -0,0 +1,402 @@
+import aspl.compiler.backend
+import aspl.compiler.utils
+import aspl.parser.ast
+import aspl.parser.ast.statements
+import aspl.parser.ast.expressions
+import aspl.parser.ast.literals
+import aspl.parser.functions
+import aspl.parser.utils
+
+// This class is the base class for all stringcode-based backends (e.g. the C backend)
+[public]
+[abstract]
+class StringcodeBackend extends Backend {
+
+ [public] // TODO: Remove this public modifier; it is currently needed because of a bug in the compiler
+ property int indentLevel = 0
+
+ [public]
+ method encode(Node node, bool standalone = false) returns string{
+ var indentStub = ""
+ var terminator = ""
+ if(standalone){
+ repeat(indentLevel){
+ indentStub += "\t"
+ }
+ terminator = getLineTerminator()
+ }
+
+ if(node oftype Expression && standalone){
+ return indentStub + encodeDiscard(Expression(node)) + terminator
+ }
+
+ if(node oftype BlockStatement){
+ return indentStub + encodeCodeBlock(BlockStatement(node)) // no line terminator
+ }elseif(node oftype NullLiteral){
+ return indentStub + encodeNullLiteral(NullLiteral(node)) + terminator
+ }elseif(node oftype BooleanLiteral){
+ return indentStub + encodeBooleanLiteral(BooleanLiteral(node)) + terminator
+ }elseif(node oftype ByteLiteral){
+ return indentStub + encodeByteLiteral(ByteLiteral(node)) + terminator
+ }elseif(node oftype IntegerLiteral){
+ return indentStub + encodeIntegerLiteral(IntegerLiteral(node)) + terminator
+ }elseif(node oftype LongLiteral){
+ return indentStub + encodeLongLiteral(LongLiteral(node)) + terminator
+ }elseif(node oftype FloatLiteral){
+ return indentStub + encodeFloatLiteral(FloatLiteral(node)) + terminator
+ }elseif(node oftype DoubleLiteral){
+ return indentStub + encodeDoubleLiteral(DoubleLiteral(node)) + terminator
+ }elseif(node oftype StringLiteral){
+ return indentStub + encodeStringLiteral(StringLiteral(node)) + terminator
+ }elseif(node oftype ListLiteral){
+ return indentStub + encodeListLiteral(ListLiteral(node)) + terminator
+ }elseif(node oftype MapLiteral){
+ return indentStub + encodeMapLiteral(MapLiteral(node)) + terminator
+ }elseif(node oftype StringIndexExpression){
+ return indentStub + encodeStringIndex(StringIndexExpression(node)) + terminator
+ }elseif(node oftype AssertStatement){
+ return indentStub + encodeAssertion(AssertStatement(node)) + terminator
+ }elseif(node oftype CheckEqualsExpression){
+ return indentStub + encodeEqualsCheck(CheckEqualsExpression(node)) + terminator
+ }elseif(node oftype NegateExpression){
+ return indentStub + encodeNegation(NegateExpression(node)) + terminator
+ }elseif(node oftype FunctionCallExpression){
+ return indentStub + encodeFunctionCall(FunctionCallExpression(node)) + terminator
+ }elseif(node oftype VariableDeclareExpression){
+ return indentStub + encodeVariableDeclaration(VariableDeclareExpression(node)) + terminator
+ }elseif(node oftype VariableAccessExpression){
+ return indentStub + encodeVariableAccess(VariableAccessExpression(node)) + terminator
+ }elseif(node oftype VariableAssignExpression){
+ return indentStub + encodeVariableAssignment(VariableAssignExpression(node)) + terminator
+ }elseif(node oftype FunctionDeclareStatement){
+ return indentStub + encodeFunctionDeclaration(FunctionDeclareStatement(node)) // no line terminator
+ }elseif(node oftype AndExpression){
+ return indentStub + encodeAnd(AndExpression(node)) + terminator
+ }elseif(node oftype OrExpression){
+ return indentStub + encodeOr(OrExpression(node)) + terminator
+ }elseif(node oftype XorExpression){
+ return indentStub + encodeXor(XorExpression(node)) + terminator
+ }elseif(node oftype PlusExpression){
+ return indentStub + encodePlus(PlusExpression(node)) + terminator
+ }elseif(node oftype MinusExpression){
+ return indentStub + encodeMinus(MinusExpression(node)) + terminator
+ }elseif(node oftype MultiplyExpression){
+ return indentStub + encodeMultiplication(MultiplyExpression(node)) + terminator
+ }elseif(node oftype DivideExpression){
+ return indentStub + encodeDivision(DivideExpression(node)) + terminator
+ }elseif(node oftype ModuloExpression){
+ return indentStub + encodeModulo(ModuloExpression(node)) + terminator
+ }elseif(node oftype LessThanExpression){
+ return indentStub + encodeLessThan(LessThanExpression(node)) + terminator
+ }elseif(node oftype LessThanOrEqualExpression){
+ return indentStub + encodeLessThanOrEqual(LessThanOrEqualExpression(node)) + terminator
+ }elseif(node oftype GreaterThanExpression){
+ return indentStub + encodeGreaterThan(GreaterThanExpression(node)) + terminator
+ }elseif(node oftype GreaterThanOrEqualExpression){
+ return indentStub + encodeGreaterThanOrEqual(GreaterThanOrEqualExpression(node)) + terminator
+ }elseif(node oftype ReferenceExpression){
+ return indentStub + encodeReference(ReferenceExpression(node)) + terminator
+ }elseif(node oftype DereferenceExpression){
+ return indentStub + encodeDereference(DereferenceExpression(node)) + terminator
+ }elseif(node oftype ReturnStatement){
+ return indentStub + encodeReturnStatement(ReturnStatement(node)) + terminator
+ }elseif(node oftype FallbackStatement){
+ return indentStub + encodeFallbackStatement(FallbackStatement(node)) + terminator
+ }elseif(node oftype EscapeStatement){
+ return indentStub + encodeEscapeStatement(EscapeStatement(node)) + terminator
+ }elseif(node oftype IfStatement){
+ return indentStub + encodeIfStatement(IfStatement(node)) // no line terminator
+ }elseif(node oftype IfElseStatement){
+ return indentStub + encodeIfElseStatement(IfElseStatement(node)) // no line terminator
+ }elseif(node oftype IfElseIfStatement){
+ return indentStub + encodeIfElseIfStatement(IfElseIfStatement(node)) // no line terminator
+ }elseif(node oftype WhileStatement){
+ return indentStub + encodeWhileStatement(WhileStatement(node)) // no line terminator
+ }elseif(node oftype RepeatStatement){
+ return indentStub + encodeRepeatStatement(RepeatStatement(node)) // no line terminator
+ }elseif(node oftype ForeachStatement){
+ return indentStub + encodeForeachStatement(ForeachStatement(node)) // no line terminator
+ }elseif(node oftype NonStaticMethodCallExpression){
+ return indentStub + encodeNonStaticMethodCall(NonStaticMethodCallExpression(node)) + terminator
+ }elseif(node oftype StaticMethodCallExpression){
+ return indentStub + encodeStaticMethodCall(StaticMethodCallExpression(node)) + terminator
+ }elseif(node oftype NonStaticPropertyAccessExpression){
+ return indentStub + encodeNonStaticPropertyAccess(NonStaticPropertyAccessExpression(node)) + terminator
+ }elseif(node oftype NonStaticPropertyAssignExpression){
+ return indentStub + encodeNonStaticPropertyAssignment(NonStaticPropertyAssignExpression(node)) + terminator
+ }elseif(node oftype StaticPropertyAccessExpression){
+ return indentStub + encodeStaticPropertyAccess(StaticPropertyAccessExpression(node)) + terminator
+ }elseif(node oftype StaticPropertyAssignExpression){
+ return indentStub + encodeStaticPropertyAssignment(StaticPropertyAssignExpression(node)) + terminator
+ }elseif(node oftype ListIndexExpression){
+ return indentStub + encodeListIndex(ListIndexExpression(node)) + terminator
+ }elseif(node oftype ListAssignExpression){
+ return indentStub + encodeListAssignment(ListAssignExpression(node)) + terminator
+ }elseif(node oftype MapAccessExpression){
+ return indentStub + encodeMapAccess(MapAccessExpression(node)) + terminator
+ }elseif(node oftype MapAssignExpression){
+ return indentStub + encodeMapAssignment(MapAssignExpression(node)) + terminator
+ }elseif(node oftype ClassDeclareStatement){
+ return indentStub + encodeClassDeclaration(ClassDeclareStatement(node)) // no line terminator
+ }elseif(node oftype ClassInstantiateExpression){
+ return indentStub + encodeClassInstantiation(ClassInstantiateExpression(node)) + terminator
+ }elseif(node oftype MethodDeclareStatement){
+ return indentStub + encodeMethodDeclaration(MethodDeclareStatement(node)) // no line terminator
+ }elseif(node oftype PropertyDeclareStatement){
+ return indentStub + encodePropertyDeclaration(PropertyDeclareStatement(node)) + terminator
+ }elseif(node oftype ThisExpression){
+ return indentStub + encodeThis(ThisExpression(node)) + terminator
+ }elseif(node oftype EnumDeclareStatement){
+ return indentStub + encodeEnumDeclaration(EnumDeclareStatement(node)) // no line terminator
+ }elseif(node oftype EnumFieldAccessExpression){
+ return indentStub + encodeEnumFieldAccess(EnumFieldAccessExpression(node)) + terminator
+ }elseif(node oftype CallbackLiteral){
+ return indentStub + encodeCallbackLiteral(CallbackLiteral(node)) + terminator
+ }elseif(node oftype ImplementationCallExpression){
+ return indentStub + encodeImplementationCall(ImplementationCallExpression(node)) + terminator
+ }elseif(node oftype OfTypeExpression){
+ return indentStub + encodeOfType(OfTypeExpression(node)) + terminator
+ }elseif(node oftype BreakStatement){
+ return indentStub + encodeBreakStatement(BreakStatement(node)) + terminator
+ }elseif(node oftype ContinueStatement){
+ return indentStub + encodeContinueStatement(ContinueStatement(node)) + terminator
+ }elseif(node oftype CastExpression){
+ return indentStub + encodeCast(CastExpression(node)) + terminator
+ }elseif(node oftype ParentExpression){
+ return encode(new ThisExpression(ParentExpression(node).c, ParentExpression(node).location)) // the parent stuff is already handled in the parser
+ }elseif(node oftype ThrowStatement){
+ return indentStub + encodeThrowStatement(ThrowStatement(node)) + terminator
+ }elseif(node oftype CatchExpression){
+ return indentStub + encodeCatchExpression(CatchExpression(node)) + terminator
+ }elseif(node oftype PropagateErrorExpression){
+ return indentStub + encodeErrorPropagation(PropagateErrorExpression(node)) + terminator
+ }elseif(node oftype EmbedFileExpression){
+ return indentStub + encodeFileEmbedding(EmbedFileExpression(node)) + terminator
+ }elseif(node oftype IncludeFileStatement){
+ IncludeUtils:include(IncludeFileStatement(node).file)
+ return indentStub + "// Including file " + IncludeFileStatement(node).file // no line terminator
+ }elseif(node oftype LinkLibraryStatement){
+ LinkUtils:libraries.add(LinkLibraryStatement(node).library)
+ return indentStub + "// Linking library " + LinkLibraryStatement(node).library // no line terminator
+ }else{
+ aspl.parser.utils.fatal_error("Unsupported node type " + node)
+ }
+ }
+
+ [abstract]
+ method getLineTerminator() returns string
+
+ [abstract]
+ method encodeCodeBlock(BlockStatement statement) returns string
+
+ [abstract]
+ method encodeNullLiteral(NullLiteral literal) returns string
+
+ [abstract]
+ method encodeBooleanLiteral(BooleanLiteral literal) returns string
+
+ [abstract]
+ method encodeByteLiteral(ByteLiteral literal) returns string
+
+ [abstract]
+ method encodeIntegerLiteral(IntegerLiteral literal) returns string
+
+ [abstract]
+ method encodeLongLiteral(LongLiteral literal) returns string
+
+ [abstract]
+ method encodeFloatLiteral(FloatLiteral literal) returns string
+
+ [abstract]
+ method encodeDoubleLiteral(DoubleLiteral literal) returns string
+
+ [abstract]
+ method encodeStringLiteral(StringLiteral literal) returns string
+
+ [abstract]
+ method encodeListLiteral(ListLiteral literal) returns string
+
+ [abstract]
+ method encodeMapLiteral(MapLiteral literal) returns string
+
+ [abstract]
+ method encodeAssertion(AssertStatement statement) returns string
+
+ [abstract]
+ method encodeEqualsCheck(CheckEqualsExpression expression) returns string
+
+ [abstract]
+ method encodeNegation(NegateExpression expression) returns string
+
+ [abstract]
+ method encodeFunctionCall(FunctionCallExpression expression) returns string
+
+ [abstract]
+ method encodeVariableDeclaration(VariableDeclareExpression expression) returns string
+
+ [abstract]
+ method encodeVariableAccess(VariableAccessExpression expression) returns string
+
+ [abstract]
+ method encodeVariableAssignment(VariableAssignExpression expression) returns string
+
+ [abstract]
+ method encodeFunctionDeclaration(FunctionDeclareStatement statement) returns string
+
+ [abstract]
+ method encodeAnd(AndExpression expression) returns string
+
+ [abstract]
+ method encodeOr(OrExpression expression) returns string
+
+ [abstract]
+ method encodeXor(XorExpression expression) returns string
+
+ [abstract]
+ method encodePlus(PlusExpression expression) returns string
+
+ [abstract]
+ method encodeMinus(MinusExpression expression) returns string
+
+ [abstract]
+ method encodeMultiplication(MultiplyExpression expression) returns string
+
+ [abstract]
+ method encodeDivision(DivideExpression expression) returns string
+
+ [abstract]
+ method encodeModulo(ModuloExpression expression) returns string
+
+ [abstract]
+ method encodeLessThan(LessThanExpression expression) returns string
+
+ [abstract]
+ method encodeLessThanOrEqual(LessThanOrEqualExpression expression) returns string
+
+ [abstract]
+ method encodeGreaterThan(GreaterThanExpression expression) returns string
+
+ [abstract]
+ method encodeGreaterThanOrEqual(GreaterThanOrEqualExpression expression) returns string
+
+ [abstract]
+ method encodeReference(ReferenceExpression expression) returns string
+
+ [abstract]
+ method encodeDereference(DereferenceExpression expression) returns string
+
+ [abstract]
+ method encodeReturnStatement(ReturnStatement statement) returns string
+
+ [abstract]
+ method encodeFallbackStatement(FallbackStatement statement) returns string
+
+ [abstract]
+ method encodeEscapeStatement(EscapeStatement statement) returns string
+
+ [abstract]
+ method encodeIfStatement(IfStatement statement) returns string
+
+ [abstract]
+ method encodeIfElseStatement(IfElseStatement statement) returns string
+
+ [abstract]
+ method encodeIfElseIfStatement(IfElseIfStatement statement) returns string
+
+ [abstract]
+ method encodeWhileStatement(WhileStatement statement) returns string
+
+ [abstract]
+ method encodeRepeatStatement(RepeatStatement statement) returns string
+
+ [abstract]
+ method encodeForeachStatement(ForeachStatement statement) returns string
+
+ [abstract]
+ method encodeNonStaticMethodCall(NonStaticMethodCallExpression expression) returns string
+
+ [abstract]
+ method encodeStaticMethodCall(StaticMethodCallExpression expression) returns string
+
+ [abstract]
+ method encodeNonStaticPropertyAccess(NonStaticPropertyAccessExpression expression) returns string
+
+ [abstract]
+ method encodeStaticPropertyAccess(StaticPropertyAccessExpression expression) returns string
+
+ [abstract]
+ method encodeNonStaticPropertyAssignment(NonStaticPropertyAssignExpression expression) returns string
+
+ [abstract]
+ method encodeStaticPropertyAssignment(StaticPropertyAssignExpression expression) returns string
+
+ [abstract]
+ method encodeListIndex(ListIndexExpression expression) returns string
+
+ [abstract]
+ method encodeListAssignment(ListAssignExpression expression) returns string
+
+ [abstract]
+ method encodeStringIndex(StringIndexExpression expression) returns string
+
+ [abstract]
+ method encodeMapAccess(MapAccessExpression expression) returns string
+
+ [abstract]
+ method encodeMapAssignment(MapAssignExpression expression) returns string
+
+ [abstract]
+ method encodeClassDeclaration(ClassDeclareStatement statement) returns string
+
+ [abstract]
+ method encodeClassInstantiation(ClassInstantiateExpression expression) returns string
+
+ [abstract]
+ method encodeMethodDeclaration(MethodDeclareStatement statement) returns string
+
+ [abstract]
+ method encodePropertyDeclaration(PropertyDeclareStatement statement) returns string
+
+ [abstract]
+ method encodeThis(ThisExpression expression) returns string
+
+ [abstract]
+ method encodeEnumDeclaration(EnumDeclareStatement statement) returns string
+
+ [abstract]
+ method encodeEnumFieldAccess(EnumFieldAccessExpression expression) returns string
+
+ [abstract]
+ method encodeCallbackLiteral(CallbackLiteral literal) returns string
+
+ [abstract]
+ method encodeImplementationCall(ImplementationCallExpression expression) returns string
+
+ [abstract]
+ method encodeOfType(OfTypeExpression expression) returns string
+
+ [abstract]
+ method encodeBreakStatement(BreakStatement statement) returns string
+
+ [abstract]
+ method encodeContinueStatement(ContinueStatement statement) returns string
+
+ [abstract]
+ method encodeCast(CastExpression expression) returns string
+
+ [abstract]
+ method encodeThrowStatement(ThrowStatement statement) returns string
+
+ [abstract]
+ method encodeCatchExpression(CatchExpression expression) returns string
+
+ [abstract]
+ method encodeErrorPropagation(PropagateErrorExpression expression) returns string
+
+ [abstract]
+ method encodeFileEmbedding(EmbedFileExpression expression) returns string
+
+ [abstract]
+ method encodeDiscard(Expression expression) returns string
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/backend/stringcode/c/.gitignore b/stdlib/aspl/compiler/backend/stringcode/c/.gitignore
new file mode 100644
index 0000000..339efda
--- /dev/null
+++ b/stdlib/aspl/compiler/backend/stringcode/c/.gitignore
@@ -0,0 +1,2 @@
+!.gitignore
+!template.c
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/backend/stringcode/c/CBackend.aspl b/stdlib/aspl/compiler/backend/stringcode/c/CBackend.aspl
new file mode 100644
index 0000000..04fd3be
--- /dev/null
+++ b/stdlib/aspl/compiler/backend/stringcode/c/CBackend.aspl
@@ -0,0 +1,2244 @@
+import aspl.compiler
+import aspl.compiler.backend
+import aspl.compiler.backend.stringcode
+import aspl.compiler.utils
+import aspl.parser
+import aspl.parser.ast
+import aspl.parser.ast.statements
+import aspl.parser.ast.expressions
+import aspl.parser.ast.literals
+import aspl.parser.functions
+import aspl.parser.properties
+import aspl.parser.methods
+import aspl.parser.classes
+import aspl.parser.enums
+import aspl.parser.utils
+import encoding.utf8
+import io
+import strings
+
+[public]
+class CBackend extends StringcodeBackend {
+
+ property int tempVariableId = 0
+ property list callbackDeclarations
+ property list callbackInvokeFunctionHandledTypes
+ property list methodDeclarations
+ property list staticNormalPropertyDeclarations
+ property list reactivePropertyDeclarations
+ property list staticReactivePropertyDeclarations
+ property list enumDeclarations
+ property bool insideClassDeclaration
+ property map constructors
+ property map threadFunctionWrappers
+ property list threadCallbackWrappers
+ property list fileEmbeds
+ property list catchBlockDeclarations
+
+ [public]
+ method compile(ParserResult result) returns CompilationResult{
+ var functionDeclarationsHeadersOutput = new StringBuilder()
+ foreach(result.nodes as node){
+ if(node oftype FunctionDeclareStatement){ // TODO: Also find functions nested in other nodes
+ functionDeclarationsHeadersOutput.append(this.encodeFunctionDeclarationHeader(FunctionDeclareStatement(node)))
+ functionDeclarationsHeadersOutput.append("\n")
+ }
+ }
+ functionDeclarationsHeadersOutput.append("\n")
+ foreach(Class:classes as c){
+ foreach(c.code as node){
+ this.encode(node, true) // register methods etc.
+ }
+ }
+ var generalOutput = new StringBuilder()
+ var mainBlockBegan = false
+ foreach(result.nodes as node){
+ if(!(node oftype ClassDeclareStatement || node oftype EnumDeclareStatement || node oftype MethodDeclareStatement || node oftype PropertyDeclareStatement || node oftype FunctionDeclareStatement)){
+ if(!mainBlockBegan){
+ if(Options:targetOs == "android"){
+ generalOutput.append("sapp_desc sokol_main(int argc, char* argv[]){\n")
+ }else{
+ generalOutput.append("int main(int argc, char** argv){\n")
+ }
+ indentLevel++
+ mainBlockBegan = true
+
+ repeat(indentLevel){
+ generalOutput.append("\t")
+ }
+ generalOutput.append("aspl_argc = argc;\n")
+ repeat(indentLevel){
+ generalOutput.append("\t")
+ }
+ generalOutput.append("aspl_argv = argv;\n")
+ if(Options:targetOs == "windows"){
+ repeat(indentLevel){
+ generalOutput.append("\t")
+ }
+ generalOutput.append("aspl_setup_windows_console();\n")
+ }
+ repeat(indentLevel){
+ generalOutput.append("\t")
+ }
+ generalOutput.append("aspl_setup_gc();\n")
+ if(Options:useSsl){
+ repeat(indentLevel){
+ generalOutput.append("\t")
+ }
+ generalOutput.append("aspl_setup_ssl();\n")
+ }
+ repeat(indentLevel){
+ generalOutput.append("\t")
+ }
+ generalOutput.append("aspl_setup_enums();\n")
+ repeat(indentLevel){
+ generalOutput.append("\t")
+ }
+ generalOutput.append("aspl_setup_parent_pointers();\n")
+ repeat(indentLevel){
+ generalOutput.append("\t")
+ }
+ generalOutput.append("aspl_setup_method_pointers();\n")
+ repeat(indentLevel){
+ generalOutput.append("\t")
+ }
+ generalOutput.append("aspl_setup_reactive_properties();\n")
+ repeat(indentLevel){
+ generalOutput.append("\t")
+ }
+ generalOutput.append("aspl_setup_static_properties();\n")
+ generalOutput.append("\n")
+ repeat(indentLevel){
+ generalOutput.append("\t")
+ }
+ generalOutput.append("aspl_setup_embeds();\n")
+ generalOutput.append("\n")
+ if(Options:enableErrorHandling){
+ repeat(indentLevel){
+ generalOutput.append("\t")
+ }
+ generalOutput.append("try{\n")
+ indentLevel++
+ }
+ }
+ }
+ generalOutput.append(this.encode(node, true))
+ generalOutput.append("\n")
+ }
+ if(mainBlockBegan){
+ if(Options:enableErrorHandling){
+ indentLevel--
+ repeat(indentLevel){
+ generalOutput.append("\t")
+ }
+ generalOutput.append("}catch{\n")
+ indentLevel++
+ repeat(indentLevel){
+ generalOutput.append("\t")
+ }
+ generalOutput.append("ASPL_PANIC(\"Uncaught %s error\", ASPL_ACCESS(aspl_current_error).value.classInstance->typePtr);\n")
+ indentLevel--
+ repeat(indentLevel){
+ generalOutput.append("\t")
+ }
+ generalOutput.append("}\n")
+ }
+ repeat(indentLevel){
+ generalOutput.append("\t")
+ }
+ if(Options:targetOs == "android"){
+ generalOutput.append("return aspl_global_sapp_desc;\n")
+ }else{
+ generalOutput.append("return 0;\n")
+ }
+ indentLevel--
+ repeat(indentLevel){
+ generalOutput.append("\t")
+ }
+ generalOutput.append("}")
+ }
+ var includeOutput = new StringBuilder()
+ foreach(IncludeUtils:files as file){
+ includeOutput.append("#include \"").append(file).append("\"\n")
+ }
+ if(IncludeUtils:files.length > 0){
+ includeOutput.append("\n")
+ }
+ var newClassDeclarationHeadersOutput = new StringBuilder()
+ foreach(Class:classes as c){
+ newClassDeclarationHeadersOutput.append("ASPL_OBJECT_TYPE aspl_new_" + TypeUtils:typeToCIdentifier(c.type.identifier) + "(")
+ if(constructors.containsKey(c.type.identifier)){
+ var constructor = constructors[c.type.identifier]
+ if(constructor.parameters.length > 0){
+ var i = 0
+ foreach(constructor.parameters as parameter){
+ newClassDeclarationHeadersOutput.append("ASPL_OBJECT_TYPE* " + parameter.name)
+ if(i < constructor.parameters.length - 1){
+ newClassDeclarationHeadersOutput.append(", ")
+ }
+ i++
+ }
+ }
+ }
+ newClassDeclarationHeadersOutput.append(");\n")
+ }
+ var enumDeclarationsOutput = new StringBuilder()
+ foreach(this.enumDeclarations as declaration){
+ enumDeclarationsOutput.append("ASPL_Enum* aspl_enum_").append(TypeUtils:typeToCIdentifier(declaration.e.type.identifier)).append(";\n")
+ }
+ if(this.enumDeclarations.length > 0){
+ enumDeclarationsOutput.append("\n")
+ }
+ var methodDeclarationsHeadersOutput = new StringBuilder()
+ foreach(this.methodDeclarations as declaration){
+ methodDeclarationsHeadersOutput.append(this.encodeMethodDeclarationHeader(declaration))
+ methodDeclarationsHeadersOutput.append("\n")
+ }
+ if(this.methodDeclarations.length > 0){
+ methodDeclarationsHeadersOutput.append("\n")
+ }
+ var threadFunctionWrappersOutput = new StringBuilder()
+ foreach(this.threadFunctionWrappers as identifier => func){
+ threadFunctionWrappersOutput.append("int aspl_function_" + identifier.replace(".", "$") + "_thread_wrapper(void* args){\n")
+ indentLevel++
+ repeat(indentLevel){
+ threadFunctionWrappersOutput.append("\t")
+ }
+ threadFunctionWrappersOutput.append("struct GC_stack_base sb;\n")
+ repeat(indentLevel){
+ threadFunctionWrappersOutput.append("\t")
+ }
+ threadFunctionWrappersOutput.append("GC_get_stack_base(&sb);\n")
+ repeat(indentLevel){
+ threadFunctionWrappersOutput.append("\t")
+ }
+ threadFunctionWrappersOutput.append("GC_register_my_thread(&sb);\n")
+ repeat(indentLevel){
+ threadFunctionWrappersOutput.append("\t")
+ }
+ threadFunctionWrappersOutput.append("aspl_function_" + identifier.replace(".", "$") + "(")
+ var i = 0
+ foreach(func.parameters as parameter){
+ threadFunctionWrappersOutput.append("((ASPL_OBJECT_TYPE**)args)[" + i + "]")
+ if(i < func.parameters.length - 1){
+ threadFunctionWrappersOutput.append(", ")
+ }
+ i++
+ }
+ threadFunctionWrappersOutput.append(");\n")
+ repeat(indentLevel){
+ threadFunctionWrappersOutput.append("\t")
+ }
+ threadFunctionWrappersOutput.append("GC_unregister_my_thread();\n")
+ repeat(indentLevel){
+ threadFunctionWrappersOutput.append("\t")
+ }
+ threadFunctionWrappersOutput.append("return 0;\n")
+ indentLevel--
+ repeat(indentLevel){
+ threadFunctionWrappersOutput.append("\t")
+ }
+ threadFunctionWrappersOutput.append("}\n")
+ }
+ if(this.threadFunctionWrappers.length > 0){
+ threadFunctionWrappersOutput.append("\n")
+ }
+ var threadCallbackWrappersOutput = new StringBuilder()
+ foreach(this.threadCallbackWrappers as identifier){
+ threadCallbackWrappersOutput.append("int aspl_" + TypeUtils:typeToCIdentifier(identifier) + "_invoke_thread_wrapper(void* args){\n")
+ indentLevel++
+ repeat(indentLevel){
+ threadCallbackWrappersOutput.append("\t")
+ }
+ threadCallbackWrappersOutput.append("struct GC_stack_base sb;\n")
+ repeat(indentLevel){
+ threadCallbackWrappersOutput.append("\t")
+ }
+ threadCallbackWrappersOutput.append("GC_get_stack_base(&sb);\n")
+ repeat(indentLevel){
+ threadCallbackWrappersOutput.append("\t")
+ }
+ threadCallbackWrappersOutput.append("GC_register_my_thread(&sb);\n")
+ repeat(indentLevel){
+ threadCallbackWrappersOutput.append("\t")
+ }
+ threadCallbackWrappersOutput.append("ASPL_OBJECT_TYPE closure = *(((ASPL_OBJECT_TYPE**)args)[0]);\n")
+ repeat(indentLevel){
+ threadCallbackWrappersOutput.append("\t")
+ }
+ threadCallbackWrappersOutput.append("ASPL_ClosureMap* closure_map = ASPL_ACCESS(closure).value.callback->closure_map;\n")
+ repeat(indentLevel){
+ threadCallbackWrappersOutput.append("\t")
+ }
+ threadCallbackWrappersOutput.append("aspl_" + TypeUtils:typeToCIdentifier(identifier) + "_invoke(closure")
+ var genericTypes = Type:getGenericTypesIdentifiers(identifier)
+ if(genericTypes.length > 0){
+ threadCallbackWrappersOutput.append(", ")
+ }
+ var i = 0
+ while(i < genericTypes.length){
+ threadCallbackWrappersOutput.append("((ASPL_OBJECT_TYPE**)args)[").append(string(i + 1)).append("]")
+ if(i < genericTypes.length - 1){
+ threadCallbackWrappersOutput.append(", ")
+ }
+ i++
+ }
+ threadCallbackWrappersOutput.append(");\n")
+ repeat(indentLevel){
+ threadCallbackWrappersOutput.append("\t")
+ }
+ threadCallbackWrappersOutput.append("GC_unregister_my_thread();\n")
+ repeat(indentLevel){
+ threadCallbackWrappersOutput.append("\t")
+ }
+ threadCallbackWrappersOutput.append("return 0;\n")
+ this.indentLevel--
+ repeat(indentLevel){
+ threadCallbackWrappersOutput.append("\t")
+ }
+ threadCallbackWrappersOutput.append("}\n")
+ }
+ if(this.threadCallbackWrappers.length > 0){
+ threadCallbackWrappersOutput.append("\n")
+ }
+ var staticNormalPropertyDeclarationsOutput = new StringBuilder()
+ foreach(this.staticNormalPropertyDeclarations as declaration){
+ staticNormalPropertyDeclarationsOutput.append(this.encodeStaticNormalPropertyDeclaration(declaration))
+ staticNormalPropertyDeclarationsOutput.append("\n")
+ }
+ if(this.staticNormalPropertyDeclarations.length > 0){
+ staticNormalPropertyDeclarationsOutput.append("\n")
+ }
+ var reactivePropertyDeclarationHeadersOutput = new StringBuilder()
+ foreach(this.reactivePropertyDeclarations as declaration){
+ reactivePropertyDeclarationHeadersOutput.append(this.encodeReactivePropertyDeclarationHeader(declaration))
+ reactivePropertyDeclarationHeadersOutput.append("\n")
+ }
+ if(this.reactivePropertyDeclarations.length > 0){
+ reactivePropertyDeclarationHeadersOutput.append("\n")
+ }
+ var staticReactivePropertyDeclarationHeadersOutput = new StringBuilder()
+ foreach(this.staticReactivePropertyDeclarations as declaration){
+ staticReactivePropertyDeclarationHeadersOutput.append(this.encodeStaticReactivePropertyDeclarationHeader(declaration))
+ staticReactivePropertyDeclarationHeadersOutput.append("\n")
+ }
+ if(this.staticReactivePropertyDeclarations.length > 0){
+ staticReactivePropertyDeclarationHeadersOutput.append("\n")
+ }
+ var reactivePropertyDeclarationsOutput = new StringBuilder()
+ foreach(this.reactivePropertyDeclarations as declaration){
+ reactivePropertyDeclarationsOutput.append(this.encodeReactivePropertyDeclaration(declaration))
+ reactivePropertyDeclarationsOutput.append("\n")
+ }
+ if(this.reactivePropertyDeclarations.length > 0){
+ reactivePropertyDeclarationsOutput.append("\n")
+ }
+ var staticReactivePropertyDeclarationsOutput = new StringBuilder()
+ foreach(this.staticReactivePropertyDeclarations as declaration){
+ staticReactivePropertyDeclarationsOutput.append(this.encodeStaticReactivePropertyDeclaration(declaration))
+ staticReactivePropertyDeclarationsOutput.append("\n")
+ }
+ if(this.staticReactivePropertyDeclarations.length > 0){
+ staticReactivePropertyDeclarationsOutput.append("\n")
+ }
+ var newClassDeclarationsOutput = new StringBuilder()
+ foreach(Class:classes as c){
+ newClassDeclarationsOutput.append("ASPL_OBJECT_TYPE aspl_new_" + TypeUtils:typeToCIdentifier(c.type.identifier) + "(")
+ if(constructors.containsKey(c.type.identifier)){
+ var constructor = constructors[c.type.identifier]
+ if(constructor.parameters.length > 0){
+ var i = 0
+ foreach(constructor.parameters as parameter){
+ newClassDeclarationsOutput.append("ASPL_OBJECT_TYPE* " + parameter.name)
+ if(i < constructor.parameters.length - 1){
+ newClassDeclarationsOutput.append(", ")
+ }
+ i++
+ }
+ }
+ }
+ newClassDeclarationsOutput.append("){\n")
+ indentLevel++
+ var indentStub = ""
+ repeat(indentLevel){
+ indentStub += "\t"
+ }
+ newClassDeclarationsOutput.append(indentStub + "ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();\n")
+ newClassDeclarationsOutput.append(indentStub + "ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_CLASS_INSTANCE;\n")
+ newClassDeclarationsOutput.append(indentStub + "ASPL_ClassInstance* instance = ASPL_MALLOC(sizeof(ASPL_ClassInstance));\n")
+ newClassDeclarationsOutput.append(indentStub + "instance->typePtr = \"" + c.type.identifier + "\";\n")
+ newClassDeclarationsOutput.append(indentStub + "instance->typeLen = " + TypeUtils:typeToCIdentifier(c.type.identifier).length + ";\n")
+ newClassDeclarationsOutput.append(indentStub + "instance->properties = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig){.initial_capacity = 1});\n")
+ if(Options:enableErrorHandling){
+ var isError = "0"
+ if(c.isError){
+ isError = "1"
+ }
+ newClassDeclarationsOutput.append(indentStub + "instance->isError = " + isError + ";\n")
+ }
+ newClassDeclarationsOutput.append(indentStub + "ASPL_ACCESS(obj).value.classInstance = instance;\n")
+ if(constructors.containsKey(c.type.identifier)){
+ newClassDeclarationsOutput.append(indentStub + "aspl_method_" + TypeUtils:typeToCIdentifier(c.type.identifier) + "_construct(C_REFERENCE(obj)")
+ var constructor = constructors[c.type.identifier]
+ if(constructor.parameters.length > 0){
+ newClassDeclarationsOutput.append(", ")
+ var i = 0
+ foreach(constructor.parameters as parameter){
+ newClassDeclarationsOutput.append(parameter.name)
+ if(i < constructor.parameters.length - 1){
+ newClassDeclarationsOutput.append(", ")
+ }
+ i++
+ }
+ }
+ newClassDeclarationsOutput.append(");\n")
+ }
+ newClassDeclarationsOutput.append(indentStub + "return obj;\n")
+ indentLevel--
+ newClassDeclarationsOutput.append("}\n")
+ }
+ var methodDeclarationsOutput = new StringBuilder()
+ foreach(this.methodDeclarations as declaration){
+ methodDeclarationsOutput.append(this.encodeMethodDeclarationOutsideClass(declaration))
+ methodDeclarationsOutput.append("\n")
+ }
+ if(this.methodDeclarations.length > 0){
+ methodDeclarationsOutput.append("\n")
+ }
+ var setupParentPointersOutput = new StringBuilder()
+ setupParentPointersOutput.append("void aspl_setup_parent_pointers(){\n")
+ indentLevel++
+ repeat(indentLevel){
+ setupParentPointersOutput.append("\t")
+ }
+ setupParentPointersOutput.append("class_parents_map = *hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig){.initial_capacity = 1});\n\n")
+ foreach(Class:classes as c){
+ repeat(indentLevel){
+ setupParentPointersOutput.append("\t")
+ }
+ foreach(ClassUtils:getAllParentsRecursively(c) as parent){
+ repeat(indentLevel){
+ setupParentPointersOutput.append("\t")
+ }
+ setupParentPointersOutput.append("aspl_class_parent_init(\"" + c.type.identifier + "\", \"" + parent.identifier + "\");\n")
+ }
+ }
+ indentLevel--
+ setupParentPointersOutput.append("}\n")
+ setupParentPointersOutput.append("\n")
+ var setupMethodPointersOutput = new StringBuilder()
+ setupMethodPointersOutput.append("void aspl_setup_method_pointers(){\n")
+ indentLevel++
+ repeat(indentLevel){
+ setupMethodPointersOutput.append("\n")
+ }
+ setupMethodPointersOutput.append("aspl_setup_builtin_method_pointers();")
+ if(Class:classes.length > 0){
+ setupMethodPointersOutput.append("\n\n")
+ }
+ foreach(Class:classes as c){
+ repeat(indentLevel){
+ setupMethodPointersOutput.append("\t")
+ }
+ foreach(Method:getAllFor(c.type) as m){
+ if(m.createdFromAny || m.isStatic || m.isAbstract){
+ continue
+ }
+ repeat(indentLevel){
+ setupMethodPointersOutput.append("\t")
+ }
+ setupMethodPointersOutput.append("aspl_object_method_init(\"" + c.type.identifier + "\", \"" + m.name + "\", " + "aspl_method_" + TypeUtils:typeToCIdentifier(m.type.identifier) + "_" + m.name + "_wrapper);\n")
+ }
+ setupMethodPointersOutput.append("\n") // TODO: Do not append if last handled class
+ }
+ indentLevel--
+ setupMethodPointersOutput.append("}\n")
+ setupMethodPointersOutput.append("\n")
+ var setupReactivePropertiesOutput = new StringBuilder()
+ setupReactivePropertiesOutput.append("void aspl_setup_reactive_properties(){\n")
+ indentLevel++
+ repeat(indentLevel){
+ setupReactivePropertiesOutput.append("\t")
+ }
+ setupReactivePropertiesOutput.append("reactive_properties_map = *hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig){.initial_capacity = 1});\n\n")
+ foreach(Class:classes as c){
+ foreach(Property:getAllFor(c.type) as p){
+ if(p.isStatic || !(p oftype CustomReactiveProperty)){
+ continue
+ }
+ repeat(indentLevel){
+ setupReactivePropertiesOutput.append("\t")
+ }
+ var getCallback = "NULL"
+ if(CustomReactiveProperty(p).getCode != null){
+ getCallback = "(void*)aspl_reactive_property_" + TypeUtils:typeToCIdentifier(p.type.identifier) + "_" + p.name + "_get"
+ }
+ var setCallback = "NULL"
+ if(CustomReactiveProperty(p).setCode != null){
+ setCallback = "(void*)aspl_reactive_property_" + TypeUtils:typeToCIdentifier(p.type.identifier) + "_" + p.name + "_set"
+ }
+ setupReactivePropertiesOutput.append("ASPL_CLASS_INIT_REACTIVE_PROPERTY(\"").append(c.type.identifier).append("\", \"").append(p.name).append("\", ").append(getCallback).append(", ").append(setCallback).append(");\n")
+ }
+ }
+ indentLevel--
+ setupReactivePropertiesOutput.append("}\n")
+ setupReactivePropertiesOutput.append("\n")
+ var setupStaticPropertiesOutput = new StringBuilder()
+ setupStaticPropertiesOutput.append("void aspl_setup_static_properties(){\n")
+ indentLevel++
+ foreach(this.staticNormalPropertyDeclarations as declaration){
+ repeat(indentLevel){
+ setupStaticPropertiesOutput.append("\t")
+ }
+ setupStaticPropertiesOutput.append("aspl_static_property_" + TypeUtils:typeToCIdentifier(declaration.p.type.identifier) + "_" + declaration.p.name + " = C_REFERENCE(")
+ if(CustomNormalProperty(declaration.p).defaultValue == null){
+ setupStaticPropertiesOutput.append("ASPL_NULL()")
+ }else{
+ setupStaticPropertiesOutput.append(this.encode(CustomNormalProperty(declaration.p).defaultValue))
+ }
+ setupStaticPropertiesOutput.append(");\n")
+ }
+ indentLevel--
+ setupStaticPropertiesOutput.append("}\n")
+ setupStaticPropertiesOutput.append("\n")
+ var setupEnumsOutput = new StringBuilder()
+ setupEnumsOutput.append("void aspl_setup_enums(){\n")
+ indentLevel++
+ repeat(indentLevel){
+ setupEnumsOutput.append("\t")
+ }
+ var initialCapacity = this.enumDeclarations.length
+ if(initialCapacity < 1){
+ initialCapacity = 1
+ }
+ setupEnumsOutput.append("enums_map = *hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig){.initial_capacity = ").append(string(initialCapacity)).append("});\n")
+ foreach(this.enumDeclarations as declaration){
+ repeat(indentLevel){
+ setupEnumsOutput.append("\t")
+ }
+ setupEnumsOutput.append("aspl_enum_").append(TypeUtils:typeToCIdentifier(declaration.e.type.identifier)).append(" = ASPL_MALLOC(sizeof(ASPL_Enum));\n")
+ repeat(indentLevel){
+ setupEnumsOutput.append("\t")
+ }
+ setupEnumsOutput.append("aspl_enum_").append(TypeUtils:typeToCIdentifier(declaration.e.type.identifier)).append("->typePtr = \"").append(declaration.e.type.identifier).append("\";\n")
+ repeat(indentLevel){
+ setupEnumsOutput.append("\t")
+ }
+ setupEnumsOutput.append("aspl_enum_").append(TypeUtils:typeToCIdentifier(declaration.e.type.identifier)).append("->typeLen = ").append(string(declaration.e.type.identifier.length)).append(";\n")
+ repeat(indentLevel){
+ setupEnumsOutput.append("\t")
+ }
+ var isFlags = "0"
+ if(declaration.e.isFlags){
+ isFlags = "1"
+ }
+ setupEnumsOutput.append("aspl_enum_").append(TypeUtils:typeToCIdentifier(declaration.e.type.identifier)).append("->isFlagEnum = ").append(isFlags).append(";\n")
+ repeat(indentLevel){
+ setupEnumsOutput.append("\t")
+ }
+ setupEnumsOutput.append("aspl_enum_").append(TypeUtils:typeToCIdentifier(declaration.e.type.identifier)).append("->stringValues = hashmap_int_to_str_new_hashmap((hashmap_int_to_str_HashMapConfig){.initial_capacity = ").append(string(declaration.e.fields?!.length)).append("});\n")
+ foreach(declaration.e.fields as field){
+ repeat(indentLevel){
+ setupEnumsOutput.append("\t")
+ }
+ setupEnumsOutput.append("hashmap_int_to_str_hashmap_set(aspl_enum_").append(TypeUtils:typeToCIdentifier(declaration.e.type.identifier)).append("->stringValues, ").append(string(field.value)).append(", \"").append(field.name).append("\");\n")
+ }
+ repeat(indentLevel){
+ setupEnumsOutput.append("\t")
+ }
+ setupEnumsOutput.append("hashmap_str_to_voidptr_hashmap_set(&enums_map, \"").append(declaration.e.type.identifier).append("\", aspl_enum_").append(TypeUtils:typeToCIdentifier(declaration.e.type.identifier)).append(");\n")
+ }
+ indentLevel--
+ setupEnumsOutput.append("}\n")
+ setupEnumsOutput.append("\n")
+ var callbackDeclarationsOutput = new StringBuilder()
+ foreach(this.callbackDeclarations as declaration){ // TODO: This will ignore callbacks inside methods etc. as they are registered later; TODO: is this TODO still up to date?
+ callbackDeclarationsOutput.append(this.encodeCallbackDeclaration(declaration))
+ callbackDeclarationsOutput.append("\n")
+ }
+ if(this.callbackDeclarations.length > 0){
+ callbackDeclarationsOutput.append("\n")
+ }
+ this.callbackInvokeFunctionHandledTypes = []
+ var callbackDeclarationHeadersOutput = new StringBuilder()
+ foreach(this.callbackDeclarations as declaration){
+ callbackDeclarationHeadersOutput.append(this.encodeCallbackDeclarationHeader(declaration))
+ callbackDeclarationHeadersOutput.append("\n")
+ }
+ if(this.callbackDeclarations.length > 0){
+ callbackDeclarationHeadersOutput.append("\n")
+ }
+ var catchBlockDeclarationsOutput = new StringBuilder()
+ foreach(this.catchBlockDeclarations as declaration){
+ catchBlockDeclarationsOutput.append(this.encodeCatchBlockDeclaration(declaration))
+ catchBlockDeclarationsOutput.append("\n")
+ }
+ if(this.catchBlockDeclarations.length > 0){
+ catchBlockDeclarationsOutput.append("\n")
+ }
+ var fileEmbedsOutput = new StringBuilder()
+ {
+ var int i = 0
+ foreach(this.fileEmbeds as expression){
+ fileEmbedsOutput.append("ASPL_OBJECT_TYPE aspl_embed_")
+ fileEmbedsOutput.append(string(i))
+ fileEmbedsOutput.append(";\n")
+ i++
+ }
+ if(this.fileEmbeds.length > 0){
+ fileEmbedsOutput.append("\n")
+ }
+ fileEmbedsOutput.append("void aspl_setup_embeds(){\n")
+ indentLevel++
+ i = 0
+ foreach(this.fileEmbeds as expression){
+ var bytes = io.read_file_bytes(expression.file)
+ repeat(indentLevel){
+ fileEmbedsOutput.append("\t")
+ }
+ fileEmbedsOutput.append("ASPL_OBJECT_TYPE* aspl_embed_")
+ fileEmbedsOutput.append(string(i))
+ fileEmbedsOutput.append("_array = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE) * ")
+ fileEmbedsOutput.append(string(bytes.length))
+ fileEmbedsOutput.append(");\n")
+ repeat(indentLevel){
+ fileEmbedsOutput.append("\t")
+ }
+ fileEmbedsOutput.append("static unsigned char const aspl_embed_")
+ fileEmbedsOutput.append(string(i))
+ fileEmbedsOutput.append("_carray[] = {")
+ var j = 0
+ foreach(bytes as b){
+ fileEmbedsOutput.append(string(b))
+ if(j < bytes.length - 1){
+ fileEmbedsOutput.append(", ")
+ }
+ j++
+ }
+ fileEmbedsOutput.append("};\n")
+ repeat(indentLevel){
+ fileEmbedsOutput.append("\t")
+ }
+ fileEmbedsOutput.append("for(int i = 0; i < ")
+ fileEmbedsOutput.append(string(bytes.length))
+ fileEmbedsOutput.append("; i++){\n")
+ indentLevel++
+ repeat(indentLevel){
+ fileEmbedsOutput.append("\t")
+ }
+ fileEmbedsOutput.append("aspl_embed_")
+ fileEmbedsOutput.append(string(i))
+ fileEmbedsOutput.append("_array[i] = ASPL_BYTE_LITERAL(aspl_embed_")
+ fileEmbedsOutput.append(string(i))
+ fileEmbedsOutput.append("_carray[i]);\n")
+ indentLevel--
+ repeat(indentLevel){
+ fileEmbedsOutput.append("\t")
+ }
+ fileEmbedsOutput.append("}\n")
+ repeat(indentLevel){
+ fileEmbedsOutput.append("\t")
+ }
+ fileEmbedsOutput.append("aspl_embed_")
+ fileEmbedsOutput.append(string(i))
+ fileEmbedsOutput.append(" = ")
+ fileEmbedsOutput.append("ASPL_LIST_LITERAL(\"list\", 10, aspl_embed_")
+ fileEmbedsOutput.append(string(i))
+ fileEmbedsOutput.append("_array, ")
+ fileEmbedsOutput.append(string(bytes.length))
+ fileEmbedsOutput.append(");\n")
+ i++
+ }
+ indentLevel--
+ }
+ fileEmbedsOutput.append("}\n")
+
+ var template = encoding.utf8.decode($embed("template.c"))
+ if(Options:useDynamicCTemplate){
+ template = io.read_file(io.join_path([io.full_directory_path(io.get_executable_path()), "stdlib", "aspl", "compiler", "backend", "stringcode", "c", "template.c"]))
+ }
+ var output = new StringBuilder(template).append("\n\n// Everything from now on is generated by the ASPL compiler:\n\n")
+ output.append(includeOutput)
+ output.append("\n")
+ output.append(fileEmbedsOutput)
+ output.append("\n")
+ output.append(functionDeclarationsHeadersOutput)
+ output.append("\n")
+ output.append(newClassDeclarationHeadersOutput)
+ output.append(enumDeclarationsOutput)
+ output.append(methodDeclarationsHeadersOutput)
+ output.append(callbackDeclarationHeadersOutput)
+ output.append(threadFunctionWrappersOutput)
+ output.append(threadCallbackWrappersOutput)
+ output.append(staticNormalPropertyDeclarationsOutput)
+ output.append(reactivePropertyDeclarationHeadersOutput)
+ output.append(staticReactivePropertyDeclarationHeadersOutput)
+ output.append(reactivePropertyDeclarationsOutput)
+ output.append(staticReactivePropertyDeclarationsOutput)
+ output.append(newClassDeclarationsOutput)
+ output.append("\n")
+ output.append(methodDeclarationsOutput)
+ output.append(callbackDeclarationsOutput)
+ output.append(catchBlockDeclarationsOutput)
+ output.append("\n")
+ output.append(setupParentPointersOutput)
+ output.append(setupMethodPointersOutput)
+ output.append(setupReactivePropertiesOutput)
+ output.append(setupStaticPropertiesOutput)
+ output.append(setupEnumsOutput)
+ output.append("\n")
+ output.append(generalOutput)
+
+ return new CompilationResult(encoding.utf8.encode(output.toString()))
+ }
+
+ method encodeCodeBlock(BlockStatement statement) returns string{
+ var s = new StringBuilder("{\n")
+ indentLevel++
+ foreach(statement.statements as node){
+ s.append(this.encode(node, true))
+ s.append("\n")
+ }
+ indentLevel--
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}")
+ return s.toString()
+ }
+
+ method getLineTerminator() returns string{
+ return ";"
+ }
+
+ method encodeNullLiteral(NullLiteral literal) returns string{
+ return "ASPL_NULL()"
+ }
+
+ method encodeBooleanLiteral(BooleanLiteral literal) returns string{
+ if(literal.value){
+ return "ASPL_TRUE()"
+ }else{
+ return "ASPL_FALSE()"
+ }
+ }
+
+ method encodeByteLiteral(ByteLiteral literal) returns string{
+ return "ASPL_BYTE_LITERAL(" + literal.value + ")"
+ }
+
+ method encodeIntegerLiteral(IntegerLiteral literal) returns string{
+ return "ASPL_INT_LITERAL(" + literal.value + ")"
+ }
+
+ method encodeLongLiteral(LongLiteral literal) returns string{
+ return "ASPL_LONG_LITERAL(" + literal.value + ")"
+ }
+
+ method encodeFloatLiteral(FloatLiteral literal) returns string{
+ return "ASPL_FLOAT_LITERAL(" + literal.value + ")"
+ }
+
+ method encodeDoubleLiteral(DoubleLiteral literal) returns string{
+ return "ASPL_DOUBLE_LITERAL(" + literal.value + ")"
+ }
+
+ method encodeStringLiteral(StringLiteral literal) returns string{
+ var s = literal.literalString
+ var s2 = ""
+ if(!s.contains("\\u")){ // Optimize for common case
+ s2 = s
+ }else{
+ var i = 0
+ while(i < s.length){ // TODO: Speed this up somehow
+ if(s[i] == "\\" && i < s.length - 1 && s[i + 1] == "u"){
+ if(i > 0 && s[i - 1] == "\\"){ // TODO: Figure out why this is necessary
+ s2 += s[i]
+ i++
+ continue
+ }
+ if(i < s.length - 5){
+ var codepoint = s.after(i + 1).before(4) // Note: before(4) is correct here, as it operates on the already sliced string
+ var value = encoding.hex.encode(codepoint)
+ if(value > 159){
+ s2 += "\\u" + codepoint
+ }else{
+ s2 += "\\x" + codepoint
+ }
+ i += 5
+ }
+ }else{
+ s2 += s[i]
+ }
+ i++
+ }
+ }
+ return "ASPL_STRING_LITERAL_NO_COPY(\"" + s2 + "\")"
+ }
+
+ method encodeListLiteral(ListLiteral literal) returns string{
+ var s = new StringBuilder("ASPL_LIST_LITERAL(\"").append(literal.getType().toString()).append("\", ").append(string(literal.getType().toString().length)).append(", (ASPL_OBJECT_TYPE[]){")
+ var i = 0
+ foreach(literal.value as value){
+ s.append(this.encode(value))
+ if(i < literal.value.length - 1){
+ s.append(", ")
+ }
+ i++
+ }
+ s.append("}, " + literal.value.length + ")")
+ return s.toString()
+ }
+
+ method encodeMapLiteral(MapLiteral literal) returns string{
+ var s = new StringBuilder("ASPL_MAP_LITERAL(\"").append(literal.getType().toString()).append("\", ").append(string(literal.getType().toString().length)).append(", (ASPL_OBJECT_TYPE[]){")
+ var i = 0
+ foreach(literal.value as pair){
+ s.append(this.encode(pair.k))
+ s.append(", ")
+ s.append(this.encode(pair.v))
+ if(i < literal.value.length - 1){
+ s.append(", ")
+ }
+ }
+ s.append("}, " + literal.value.length + ")")
+ return s.toString()
+ }
+
+ method encodeAssertion(AssertStatement statement) returns string{
+ return "ASPL_ASSERT(" + this.encode(statement.expression) + ", \"" + Location(statement.location).file.replace("\\", "\\\\") + "\", " + Location(statement.location).startLine + ", " + Location(statement.location).startColumn + ")"
+ }
+
+ method encodeEqualsCheck(CheckEqualsExpression expression) returns string{
+ return "ASPL_EQUALS(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")"
+ }
+
+ method encodeNegation(NegateExpression expression) returns string{
+ return "ASPL_NEGATE(" + this.encode(expression.expression) + ")"
+ }
+
+ method encodeFunctionCall(FunctionCallExpression expression) returns string{
+ var s = new StringBuilder()
+ if(expression.newThread){
+ s.append("ASPL_LAUNCH_THREAD(")
+ s.append("aspl_function_").append(expression.func.identifier.replace(".", "$")).append("_thread_wrapper, (ASPL_OBJECT_TYPE*[]){")
+ this.threadFunctionWrappers[expression.func.identifier] = expression.func
+ }else{
+ s.append("aspl_function_").append(expression.func.identifier.replace(".", "$")).append("(")
+ }
+ var i = 0
+ foreach(expression.arguments as argument){
+ s.append("C_REFERENCE(").append(this.encode(argument)).append(")")
+ if(i < expression.arguments.length - 1){
+ s.append(", ")
+ }
+ i++
+ }
+ if(expression.arguments.length < expression.func.parameters.length){
+ if(expression.arguments.length > 0){
+ s.append(", ")
+ }
+ while(i < expression.func.parameters.length){
+ var parameter = expression.func.parameters[i]
+ if(parameter.defaultValue != null){
+ s.append("C_REFERENCE(").append(this.encode(parameter.defaultValue)).append(")")
+ if(i < expression.func.parameters.length - 1){
+ s.append(", ")
+ }
+ }
+ i++
+ }
+ }
+ if(expression.newThread){
+ s.append("}")
+ var length = expression.arguments.length
+ if(expression.arguments.length < expression.func.parameters.length){
+ length = expression.func.parameters.length
+ }
+ s.append(", ").append(string(length)).append(" * sizeof(ASPL_OBJECT_TYPE*)")
+ }
+ s.append(")")
+ return s.toString()
+ }
+
+ method encodeVariableDeclaration(VariableDeclareExpression expression) returns string{
+ return "ASPL_OBJECT_TYPE* " + IdentifierEscapeUtils:escapeIdentifier(expression.variable.identifier) + " = C_REFERENCE(" + this.encode(expression.value) + ")" // TODO: Make this usable as an expression
+ }
+
+ method encodeVariableAccess(VariableAccessExpression expression) returns string{
+ return "*" + IdentifierEscapeUtils:escapeIdentifier(expression.variable.identifier)
+ }
+
+ method encodeVariableAssignment(VariableAssignExpression expression) returns string{
+ return "*" + IdentifierEscapeUtils:escapeIdentifier(expression.variable.identifier) + " = " + this.encode(expression.value)
+ }
+
+ method encodeFunctionDeclaration(FunctionDeclareStatement statement) returns string{
+ var s = new StringBuilder("")
+ if(statement.func.returnTypes.types.length > 0 || statement.func.canThrow){
+ s.append("ASPL_OBJECT_TYPE ")
+ }else{
+ s.append("void ")
+ }
+ s.append("aspl_function_").append(statement.func.identifier.replace(".", "$")).append("(")
+ var i = 0
+ foreach(statement.func.parameters as parameter){
+ s.append(this.encodeParameter(parameter))
+ if(i < statement.func.parameters.length - 1){
+ s.append(", ")
+ }
+ i++
+ }
+ s.append("){\n")
+ this.indentLevel++
+ if(Options:enableErrorHandling){
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("try{\n")
+ this.indentLevel++
+ }
+ foreach(CustomFunction(statement.func).code as node){
+ s.append(this.encode(node, true))
+ s.append("\n")
+ }
+ if(Options:enableErrorHandling){
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("return_uninitialized_from_try;\n")
+ }
+ this.indentLevel--
+ if(Options:enableErrorHandling){
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}catch{\n")
+ this.indentLevel++
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("return aspl_current_error;\n")
+ this.indentLevel--
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}\n")
+ this.indentLevel--
+ }
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}")
+ s.append("\n")
+ return s.toString()
+ }
+
+ method encodeFunctionDeclarationHeader(FunctionDeclareStatement statement) returns string{
+ var s = new StringBuilder("")
+ if(statement.func.returnTypes.types.length > 0 || statement.func.canThrow){
+ s.append("ASPL_OBJECT_TYPE ")
+ }else{
+ s.append("void ")
+ }
+ s.append("aspl_function_").append(statement.func.identifier.replace(".", "$")).append("(")
+ var i = 0
+ foreach(statement.func.parameters as parameter){
+ s.append(this.encodeParameter(parameter))
+ if(i < statement.func.parameters.length - 1){
+ s.append(", ")
+ }
+ i++
+ }
+ s.append(");")
+ return s.toString()
+ }
+
+ method encodeParameter(Parameter parameter) returns string{
+ var s = "ASPL_OBJECT_TYPE* " + parameter.name
+ if(parameter.optional){
+ s += " /* optional */"
+ }
+ return s
+ }
+
+ method encodeReturnStatement(ReturnStatement statement) returns string{
+ if(statement.value != null){
+ if(Options:enableErrorHandling){
+ return "return_from_try(" + this.encode(statement.value) + ")"
+ }else{
+ return "return " + this.encode(statement.value)
+ }
+ }else{
+ if(Options:enableErrorHandling){
+ if(ErrorUtils:canCallableThrow(statement.callable)){
+ return "return_uninitialized_from_try"
+ }else{
+ return "return_void_from_try"
+ }
+ }else{
+ return "return"
+ }
+ }
+ }
+
+ method encodeFallbackStatement(FallbackStatement statement) returns string{
+ return "return " + this.encode(statement.value)
+ }
+
+ method encodeEscapeStatement(EscapeStatement statement) returns string{
+ if(statement.value != null){
+ return "escape(" + this.encode(statement.value) + ")"
+ }else{
+ return "escape(ASPL_NULL())"
+ }
+ }
+
+ method encodeAnd(AndExpression expression) returns string{
+ if(Type:matches(new Types([Type:fromString("boolean")]), expression.getType())){
+ return "ASPL_AND(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")"
+ }else{
+ return "ASPL_ENUM_AND(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")"
+ }
+ }
+
+ method encodeOr(OrExpression expression) returns string{
+ if(Type:matches(new Types([Type:fromString("boolean")]), expression.getType())){
+ return "ASPL_OR(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")"
+ }else{
+ return "ASPL_ENUM_OR(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")"
+ }
+ }
+
+ method encodeXor(XorExpression expression) returns string{
+ if(Type:matches(new Types([Type:fromString("boolean")]), expression.getType())){
+ return "ASPL_XOR(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")"
+ }else{
+ return "ASPL_ENUM_XOR(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")"
+ }
+ }
+
+ method canAccessNumberValue(Expression expression) returns bool{
+ if(expression.getType().types.length > 1){
+ return false
+ }
+ if(Type:matches(new Types([Type:fromString("byte")]), expression.getType())){
+ return true
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), expression.getType())){
+ return true
+ }elseif(Type:matches(new Types([Type:fromString("long")]), expression.getType())){
+ return true
+ }elseif(Type:matches(new Types([Type:fromString("float")]), expression.getType())){
+ return true
+ }elseif(Type:matches(new Types([Type:fromString("double")]), expression.getType())){
+ return true
+ }elseif(Enum:enums.containsKey(expression.getType().types[0].identifier)){
+ return true
+ }
+ return false
+ }
+
+ method encodeNumberValueAccess(Expression expression) returns string{
+ if(expression.getType().types.length > 1){
+ aspl.parser.utils.type_error("Cannot directly access the value of a number with an ambiguous type", expression.location)
+ }
+ if(Type:matches(new Types([Type:fromString("byte")]), expression.getType())){
+ return "(ASPL_ACCESS(" + this.encode(expression) + ").value.integer8)"
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), expression.getType())){
+ return "(ASPL_ACCESS(" + this.encode(expression) + ").value.integer32)"
+ }elseif(Type:matches(new Types([Type:fromString("long")]), expression.getType())){
+ return "(ASPL_ACCESS(" + this.encode(expression) + ").value.integer64)"
+ }elseif(Type:matches(new Types([Type:fromString("float")]), expression.getType())){
+ return "(ASPL_ACCESS(" + this.encode(expression) + ").value.float32)"
+ }elseif(Type:matches(new Types([Type:fromString("double")]), expression.getType())){
+ return "(ASPL_ACCESS(" + this.encode(expression) + ").value.float64)"
+ }elseif(Enum:enums.containsKey(expression.getType().types[0].identifier)){
+ return "(ASPL_ACCESS(" + this.encode(expression) + ").value.enumField->intValue)"
+ }else{
+ aspl.parser.utils.type_error("Cannot directly access the value of a number with a non-numeric type", expression.location)
+ }
+ return "-1"
+ }
+
+ method encodePlus(PlusExpression expression) returns string{
+ if(expression.right oftype IntegerLiteral && IntegerLiteral(expression.right).value == 1){
+ return "ASPL_PLUS_PLUS(" + this.encode(expression.left) + ")"
+ }
+ return "ASPL_PLUS(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")"
+ }
+
+ method encodeMinus(MinusExpression expression) returns string{
+ return "ASPL_MINUS(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")"
+ }
+
+ method encodeMultiplication(MultiplyExpression expression) returns string{
+ return "ASPL_MULTIPLY(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")"
+ }
+
+ method encodeDivision(DivideExpression expression) returns string{
+ return "ASPL_DIVIDE(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")"
+ }
+
+ method encodeModulo(ModuloExpression expression) returns string{
+ return "ASPL_MODULO(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")"
+ }
+
+ method encodeLessThan(LessThanExpression expression) returns string{
+ if(canAccessNumberValue(expression.left) && canAccessNumberValue(expression.right)){
+ return "ASPL_BOOL_LITERAL(" + encodeNumberValueAccess(expression.left) + " < " + encodeNumberValueAccess(expression.right) + ")"
+ }else{
+ return "ASPL_LESS_THAN(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")"
+ }
+ }
+
+ method encodeLessThanOrEqual(LessThanOrEqualExpression expression) returns string{
+ if(canAccessNumberValue(expression.left) && canAccessNumberValue(expression.right)){
+ return "ASPL_BOOL_LITERAL(" + encodeNumberValueAccess(expression.left) + " <= " + encodeNumberValueAccess(expression.right) + ")"
+ }else{
+ return "ASPL_LESS_THAN_OR_EQUAL(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")"
+ }
+ }
+
+ method encodeGreaterThan(GreaterThanExpression expression) returns string{
+ if(canAccessNumberValue(expression.left) && canAccessNumberValue(expression.right)){
+ return "ASPL_BOOL_LITERAL(" + encodeNumberValueAccess(expression.left) + " > " + encodeNumberValueAccess(expression.right) + ")"
+ }else{
+ return "ASPL_GREATER_THAN(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")"
+ }
+ }
+
+ method encodeGreaterThanOrEqual(GreaterThanOrEqualExpression expression) returns string{
+ if(canAccessNumberValue(expression.left) && canAccessNumberValue(expression.right)){
+ return "ASPL_BOOL_LITERAL(" + encodeNumberValueAccess(expression.left) + " >= " + encodeNumberValueAccess(expression.right) + ")"
+ }else{
+ return "ASPL_GREATER_THAN_OR_EQUAL(" + this.encode(expression.left) + ", " + this.encode(expression.right) + ")"
+ }
+ }
+
+ method encodeReference(ReferenceExpression expression) returns string{
+ if(expression.expression oftype VariableAccessExpression){
+ return "ASPL_REFERENCE(\"" + TypeUtils:shortName(expression.expression.getType().getPointer().identifier) + "\", " + TypeUtils:shortName(expression.expression.getType().getPointer().identifier).length + ", " + VariableAccessExpression(expression.expression).variable.identifier + ")"
+ }elseif(expression.expression oftype NonStaticPropertyAccessExpression){
+ if(NonStaticPropertyAccessExpression(expression.expression).p oftype CustomReactiveProperty){
+ aspl.parser.utils.type_error("Cannot reference a reactive property", expression.location)
+ return ""
+ }
+ return "ASPL_REFERENCE(\"" + TypeUtils:shortName(expression.expression.getType().getPointer().identifier) + "\", " + TypeUtils:shortName(expression.expression.getType().getPointer().identifier).length + ", ASPL_CLASS_INSTANCE_GET_PROPERTY_ADDRESS(ASPL_ACCESS(" + this.encode(NonStaticPropertyAccessExpression(expression.expression).base) + ").value.classInstance, \"" + NonStaticPropertyAccessExpression(expression.expression).p.name + "\"))"
+ }elseif(expression.expression oftype StaticPropertyAccessExpression){
+ if(StaticPropertyAccessExpression(expression.expression).p oftype CustomReactiveProperty){
+ aspl.parser.utils.type_error("Cannot reference a reactive property", expression.location)
+ return ""
+ }
+ return "ASPL_REFERENCE(\"" + TypeUtils:shortName(expression.expression.getType().getPointer().identifier) + "\", " + TypeUtils:shortName(expression.expression.getType().getPointer().identifier).length + ", aspl_static_property_" + TypeUtils:typeToCIdentifier(StaticPropertyAccessExpression(expression.expression).p.type.identifier) + "_" + StaticPropertyAccessExpression(expression.expression).p.name + ")"
+ }else{
+ aspl.parser.utils.type_error("Cannot reference something that is neither a variable nor a property", expression.location)
+ return ""
+ }
+ }
+
+ method encodeDereference(DereferenceExpression expression) returns string{
+ return "ASPL_DEREFERENCE(" + this.encode(expression.pointer) + ")"
+ }
+
+ method encodeIfStatement(IfStatement statement) returns string{
+ var conditionStub = "ASPL_IS_TRUE(" + this.encode(statement.condition) + ")"
+ if(statement.condition oftype CheckEqualsExpression){
+ conditionStub = "ASPL_IS_EQUAL(" + this.encode(CheckEqualsExpression(statement.condition).left) + ", " + this.encode(CheckEqualsExpression(statement.condition).right) + ")"
+ }
+ var s = new StringBuilder("if(").append(conditionStub).append("){\n")
+ this.indentLevel++
+ foreach(statement.code as node){
+ s.append(this.encode(node, true))
+ s.append("\n")
+ }
+ this.indentLevel--
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}")
+ return s.toString()
+ }
+
+ method encodeIfElseStatement(IfElseStatement statement) returns string{
+ var s = new StringBuilder("if(ASPL_IS_TRUE(").append(this.encode(statement.condition)).append(")){\n")
+ this.indentLevel++
+ foreach(statement.ifCode as node){
+ s.append(this.encode(node, true))
+ s.append("\n")
+ }
+ this.indentLevel--
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}else{\n")
+ this.indentLevel++
+ foreach(statement.elseCode as node){
+ s.append(this.encode(node, true))
+ s.append("\n")
+ }
+ this.indentLevel--
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}")
+ return s.toString()
+ }
+
+ method encodeIfElseIfStatement(IfElseIfStatement statement) returns string{
+ var s = new StringBuilder("if(ASPL_IS_TRUE(").append(this.encode(statement.condition)).append(")){\n")
+ this.indentLevel++
+ foreach(statement.ifCode as node){
+ s.append(this.encode(node, true))
+ s.append("\n")
+ }
+ this.indentLevel--
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}else ")
+ s.append(this.encode(statement.elseIf))
+ return s.toString()
+ }
+
+ method encodeWhileStatement(WhileStatement statement) returns string{
+ var conditionStub = new StringBuilder("ASPL_IS_TRUE(").append(this.encode(statement.condition)).append(")")
+ if(statement.condition oftype BooleanLiteral){
+ if(BooleanLiteral(statement.condition).value == false){
+ conditionStub = new StringBuilder("0")
+ }else{
+ conditionStub = new StringBuilder("1")
+ }
+ }
+ var s = new StringBuilder("while(").append(conditionStub).append("){\n")
+ this.indentLevel++
+ foreach(statement.code as node){
+ s.append(this.encode(node, true))
+ s.append("\n")
+ }
+ this.indentLevel--
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}")
+ return s.toString()
+ }
+
+ method encodeRepeatStatement(RepeatStatement statement) returns string{
+ var variable = "_temp_" + tempVariableId++
+ if(statement.variable != null){
+ variable = statement.variable?!
+ }
+ var s = new StringBuilder("for(ASPL_OBJECT_TYPE* ").append(variable).append(" = C_REFERENCE(ASPL_INT_LITERAL(").append(string(statement.start)).append(")); ").append("ASPL_IS_TRUE(ASPL_LESS_THAN(*").append(variable).append(", ASPL_PLUS(").append(this.encode(statement.iterations)).append(", ASPL_INT_LITERAL(").append(string(statement.start)).append(")))); ").append("*").append(variable).append(" = ASPL_PLUS_PLUS(*").append(variable).append(")){\n")
+ indentLevel++
+ foreach(statement.code as node){
+ s.append(this.encode(node, true))
+ s.append("\n")
+ }
+ this.indentLevel--
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}")
+ return s.toString()
+ }
+
+ method encodeForeachStatement(ForeachStatement statement) returns string{
+ var collectionVariable = "_temp_" + tempVariableId++
+ var indexVariable = "_temp_" + tempVariableId++
+ var string? mapKeyVariable = null
+ if(Type:matches(new Types([Type:fromString("map")]), statement.collection.getType(), true)){
+ mapKeyVariable = IdentifierEscapeUtils:escapeIdentifier(statement.key?!)
+ }elseif(statement.key != null){
+ indexVariable = IdentifierEscapeUtils:escapeIdentifier(statement.key?!)
+ }
+ var s = new StringBuilder("ASPL_OBJECT_TYPE* ").append(collectionVariable).append(" = C_REFERENCE(").append(this.encode(statement.collection)).append(");\n")
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("for(ASPL_OBJECT_TYPE* ").append(indexVariable).append(" = C_REFERENCE(ASPL_INT_LITERAL(0)); ASPL_IS_TRUE(ASPL_LESS_THAN(*").append(indexVariable)
+ if(Type:matches(new Types([Type:fromString("list")]), statement.collection.getType(), true)){
+ s.append(", ASPL_LIST_LENGTH(*")
+ }elseif(Type:matches(new Types([Type:fromString("map")]), statement.collection.getType(), true)){
+ s.append(", ASPL_MAP_LENGTH(*")
+ }elseif(Type:matches(new Types([Type:fromString("string")]), statement.collection.getType())){
+ s.append(", ASPL_STRING_LENGTH(*")
+ }
+ s.append(collectionVariable).append("))); ").append("*").append(indexVariable).append(" = ASPL_PLUS_PLUS(*").append(indexVariable).append(")){\n")
+ indentLevel++
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ if(mapKeyVariable != null){
+ s.append("ASPL_OBJECT_TYPE* ").append(mapKeyVariable).append(" = C_REFERENCE(ASPL_MAP_GET_KEY_FROM_INDEX(*").append(collectionVariable).append(", *").append(indexVariable).append("));\n")
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ }
+ if(statement.value != null){
+ s.append("ASPL_OBJECT_TYPE* ").append(IdentifierEscapeUtils:escapeIdentifier(statement.value))
+ if(Type:matches(new Types([Type:fromString("list")]), statement.collection.getType(), true)){
+ s.append(" = C_REFERENCE(ASPL_LIST_GET(*")
+ }elseif(Type:matches(new Types([Type:fromString("map")]), statement.collection.getType(), true)){
+ s.append(" = C_REFERENCE(ASPL_MAP_GET_VALUE_FROM_INDEX(*")
+ }elseif(Type:matches(new Types([Type:fromString("string")]), statement.collection.getType())){
+ s.append(" = C_REFERENCE(ASPL_STRING_INDEX(*")
+ }
+ s.append(collectionVariable).append(", *").append(indexVariable).append("));\n")
+ }
+ foreach(statement.code as node){
+ s.append(this.encode(node, true))
+ s.append("\n")
+ }
+ this.indentLevel--
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}")
+ return s.toString()
+ }
+
+ method encodeNonStaticMethodCall(NonStaticMethodCallExpression expression) returns string{
+ var strippedTypeIdentifier = expression.m.type.toString()
+ if(strippedTypeIdentifier.startsWith("list<")){
+ strippedTypeIdentifier = "list"
+ }elseif(strippedTypeIdentifier.startsWith("map<")){
+ strippedTypeIdentifier = "map"
+ }
+ var s = new StringBuilder()
+ if(strippedTypeIdentifier.startsWith("callback<") || strippedTypeIdentifier == "callback"){
+ if(expression.m.name == "invoke"){
+ if(expression.newThread){
+ s.append("ASPL_LAUNCH_THREAD(")
+ s.append("aspl_").append(TypeUtils:typeToCIdentifier(strippedTypeIdentifier)).append("_invoke_thread_wrapper, (ASPL_OBJECT_TYPE*[]){")
+ s.append("C_REFERENCE(" + this.encode(expression.base) + ")")
+ if(!this.threadCallbackWrappers.contains(expression.m.type.identifier)){
+ this.threadCallbackWrappers.add(expression.m.type.identifier)
+ }
+ }else{
+ s.append("aspl_" + TypeUtils:typeToCIdentifier(strippedTypeIdentifier) + "_invoke(")
+ s.append(this.encode(expression.base))
+ }
+ if(expression.arguments.length > 0){
+ s.append(", ")
+ }
+ }
+ }elseif(expression.exactClass != null){
+ if(expression.newThread){
+ s.append("aspl_method_invoke_newthread(")
+ s.append(this.encode(expression.base))
+ s.append(", ")
+ s.append("aspl_method_" + TypeUtils:typeToCIdentifier(expression.exactClass?!.type.identifier) + "_" + expression.m.name + ", (ASPL_OBJECT_TYPE*[]){")
+ }else{
+ s.append("aspl_method_" + TypeUtils:typeToCIdentifier(expression.exactClass?!.type.identifier) + "_" + expression.m.name + "(C_REFERENCE(")
+ s.append(this.encode(expression.base))
+ s.append(")")
+ if(expression.arguments.length > 0 || expression.m.parameters.length > 0){
+ s.append(", ")
+ }
+ }
+ }else{
+ if(expression.newThread){
+ s.append("aspl_object_method_invoke_newthread(")
+ }else{
+ s.append("aspl_object_method_invoke(")
+ }
+ s.append(this.encode(expression.base))
+ s.append(", \"")
+ s.append(expression.m.name)
+ s.append("\", (ASPL_OBJECT_TYPE*[]){")
+ }
+ var i = 0
+ foreach(expression.arguments as argument){
+ s.append("C_REFERENCE(" + this.encode(argument) + ")")
+ if(i < expression.arguments.length - 1){
+ s.append(", ")
+ }
+ i++
+ }
+ if(expression.arguments.length < expression.m.parameters.length){
+ if(expression.arguments.length > 0){
+ s.append(", ")
+ }
+ while(i < expression.m.parameters.length){
+ var parameter = expression.m.parameters[i]
+ if(parameter.defaultValue != null){
+ s.append("C_REFERENCE(" + this.encode(parameter.defaultValue) + ")")
+ if(i < expression.m.parameters.length - 1){
+ s.append(", ")
+ }
+ }
+ i++
+ }
+ }
+ if(!(expression.exactClass != null || ((strippedTypeIdentifier.startsWith("callback<") || strippedTypeIdentifier == "callback") && expression.m.name == "invoke")) || expression.newThread){
+ s.append("}")
+ }
+ if(expression.newThread){
+ var length = expression.arguments.length
+ if(expression.arguments.length < expression.m.parameters.length){
+ length = expression.m.parameters.length
+ }
+ if((strippedTypeIdentifier.startsWith("callback<") || strippedTypeIdentifier == "callback") && expression.m.name == "invoke"){
+ length++ // the callback itself
+ }
+ s.append(", ").append(string(length)).append(" * sizeof(ASPL_OBJECT_TYPE*)")
+ }
+ s.append(")")
+ return s.toString()
+ }
+
+ method encodeStaticMethodCall(StaticMethodCallExpression expression) returns string{
+ var s = new StringBuilder("aspl_method_").append(TypeUtils:typeToCIdentifier(expression.m.type.identifier)).append("_").append(expression.m.name).append("(")
+ var i = 0
+ foreach(expression.arguments as argument){
+ s.append("C_REFERENCE(" + this.encode(argument) + ")")
+ if(i < expression.arguments.length - 1){
+ s.append(", ")
+ }
+ i++
+ }
+ if(expression.arguments.length < expression.m.parameters.length){
+ if(expression.arguments.length > 0){
+ s.append(", ")
+ }
+ while(i < expression.m.parameters.length){
+ var parameter = expression.m.parameters[i]
+ if(parameter.defaultValue != null){
+ s.append("C_REFERENCE(" + this.encode(parameter.defaultValue) + ")")
+ if(i < expression.m.parameters.length - 1){
+ s.append(", ")
+ }
+ }
+ i++
+ }
+ }
+ s.append(")")
+ return s.toString()
+ }
+
+ method encodeNonStaticPropertyAccess(NonStaticPropertyAccessExpression expression) returns string{
+ if(expression.p oftype CustomReactiveProperty){
+ var s = new StringBuilder()
+ s.append("ASPL_REACTIVE_PROPERTY_GET(C_REFERENCE(").append(this.encode(expression.base)).append("), \"").append(expression.p.name).append("\")")
+ return s.toString()
+ }else{
+ if(expression.base.getType().toType().isPrimitive()){
+ if(Type:matches(new Types([Type:fromString("string")]), expression.base.getType())){
+ if(expression.p.name == "length"){
+ return "ASPL_STRING_LENGTH(" + this.encode(expression.base) + ")"
+ }
+ }elseif(Type:matches(new Types([Type:fromString("list")]), expression.base.getType(), true)){
+ if(expression.p.name == "length"){
+ return "ASPL_LIST_LENGTH(" + this.encode(expression.base) + ")"
+ }
+ }elseif(Type:matches(new Types([Type:fromString("map")]), expression.base.getType(), true)){
+ if(expression.p.name == "length"){
+ return "ASPL_MAP_LENGTH(" + this.encode(expression.base) + ")"
+ }
+ }
+ }
+ return "ASPL_CLASS_INSTANCE_GET_PROPERTY(ASPL_ACCESS(" + this.encode(expression.base) + ").value.classInstance, \"" + expression.p.name + "\")"
+ }
+ }
+
+ method encodeStaticPropertyAccess(StaticPropertyAccessExpression expression) returns string{
+ if(expression.p oftype CustomReactiveProperty){
+ return "aspl_static_reactive_property_" + TypeUtils:typeToCIdentifier(expression.base.identifier) + "_" + expression.p.name + "_get()"
+ }else{
+ return "*aspl_static_property_" + TypeUtils:typeToCIdentifier(expression.base.identifier) + "_" + expression.p.name
+ }
+ }
+
+ method encodeNonStaticPropertyAssignment(NonStaticPropertyAssignExpression expression) returns string{
+ if(expression.p oftype CustomReactiveProperty){
+ var s = new StringBuilder()
+ s.append("ASPL_REACTIVE_PROPERTY_SET(C_REFERENCE(").append(this.encode(expression.base)).append("), \"").append(expression.p.name).append("\", C_REFERENCE(").append(this.encode(expression.value)).append("))")
+ return s.toString()
+ }else{
+ //return "((" + TypeUtils:typeToCIdentifier(expression.p.type.identifier) + "*)((" + this.encode(expression.base) + ").value.classInstance->data))->" + expression.p.name + " = C_REFERENCE(" + this.encode(expression.value) + ")"
+
+ return "ASPL_CLASS_INSTANCE_SET_PROPERTY(ASPL_ACCESS(" + this.encode(expression.base) + ").value.classInstance, \"" + expression.p.name + "\", " + this.encode(expression.value) + ")"
+ }
+ }
+
+ method encodeStaticPropertyAssignment(StaticPropertyAssignExpression expression) returns string{
+ if(expression.p oftype CustomReactiveProperty){
+ return "aspl_static_reactive_property_" + TypeUtils:typeToCIdentifier(expression.base.identifier) + "_" + expression.p.name + "_set(C_REFERENCE(" + this.encode(expression.value) + "))"
+ }else{
+ return "*aspl_static_property_" + TypeUtils:typeToCIdentifier(expression.base.identifier) + "_" + expression.p.name + " = " + this.encode(expression.value)
+ }
+ }
+
+ method encodeListIndex(ListIndexExpression expression) returns string{
+ return "ASPL_LIST_GET(" + this.encode(expression.base) + ", " + this.encode(expression.index) + ")"
+ }
+
+ method encodeListAssignment(ListAssignExpression expression) returns string{
+ return "ASPL_LIST_SET(" + this.encode(expression.base) + ", " + this.encode(expression.index) + ", " + this.encode(expression.value) + ")"
+ }
+
+ method encodeMapAccess(MapAccessExpression expression) returns string{
+ return "ASPL_MAP_GET(" + this.encode(expression.base) + ", " + this.encode(expression.key) + ")"
+ }
+
+ method encodeMapAssignment(MapAssignExpression expression) returns string{
+ return "ASPL_MAP_SET(" + this.encode(expression.base) + ", " + this.encode(expression.key) + ", " + this.encode(expression.value) + ")"
+ }
+
+ method encodeStringIndex(StringIndexExpression expression) returns string{
+ return "ASPL_STRING_INDEX(" + this.encode(expression.base) + ", " + this.encode(expression.index) + ")"
+ }
+
+ method encodeClassDeclaration(ClassDeclareStatement statement) returns string{
+ return "// Declare class " + statement.c.type.identifier
+ }
+
+ method encodeClassInstantiation(ClassInstantiateExpression expression) returns string{
+ var s = new StringBuilder("aspl_new_")
+ s.append(TypeUtils:typeToCIdentifier(expression.c.type.identifier)).append("(")
+ var i = 0
+ foreach(expression.arguments as argument){
+ s.append("C_REFERENCE(").append(this.encode(argument)).append(")")
+ if(i < expression.arguments.length - 1){
+ s.append(", ")
+ }
+ i++
+ }
+ if(Method:exists(expression.c.type, "construct")){
+ var m = Method:get(expression.c.type, "construct")
+ if(expression.arguments.length < m.parameters.length){
+ if(expression.arguments.length > 0){
+ s.append(", ")
+ }
+ while(i < m.parameters.length){
+ var parameter = m.parameters[i]
+ if(parameter.defaultValue != null){
+ s.append("C_REFERENCE(" + this.encode(parameter.defaultValue) + ")")
+ if(i < m.parameters.length - 1){
+ s.append(", ")
+ }
+ }
+ i++
+ }
+ }
+ }
+ s.append(")")
+ return s.toString()
+ }
+
+ method encodeMethodDeclaration(MethodDeclareStatement statement) returns string{
+ if(!statement.m.isAbstract){
+ methodDeclarations.add(statement)
+ }
+ if(statement.m.name == "construct"){
+ constructors[statement.m.type.identifier] = statement.m
+ }
+ return "// Declare method " + statement.m.name
+ }
+
+ method encodeMethodDeclarationHeader(MethodDeclareStatement statement) returns string{
+ var s = new StringBuilder("")
+ if(statement.m.returnTypes.types.length > 0 || statement.m.canThrow){
+ s.append("ASPL_OBJECT_TYPE ")
+ }else{
+ s.append("void ")
+ }
+ s.append("aspl_method_").append(TypeUtils:typeToCIdentifier(statement.m.type.identifier)).append("_").append(statement.m.name).append("(")
+ if(!statement.m.isStatic){
+ s.append("ASPL_OBJECT_TYPE* this")
+ if(statement.m.parameters.length > 0){
+ s.append(", ")
+ }
+ }
+ var i = 0
+ foreach(statement.m.parameters as parameter){
+ s.append("ASPL_OBJECT_TYPE* ").append(parameter.name)
+ if(i < statement.m.parameters.length - 1){
+ s.append(", ")
+ }
+ i++
+ }
+ s.append(");")
+ return s.toString()
+ }
+
+ method encodeMethodDeclarationOutsideClass(MethodDeclareStatement statement) returns string{
+ var s = new StringBuilder("")
+ if(statement.m.returnTypes.types.length > 0 || statement.m.canThrow){
+ s.append("ASPL_OBJECT_TYPE ")
+ }else{
+ s.append("void ")
+ }
+ s.append("aspl_method_").append(TypeUtils:typeToCIdentifier(statement.m.type.identifier)).append("_").append(statement.m.name).append("(")
+ if(!CustomMethod(statement.m).isStatic){
+ s.append("ASPL_OBJECT_TYPE* this")
+ if(statement.m.parameters.length > 0){
+ s.append(", ")
+ }
+ }
+ var i = 0
+ foreach(statement.m.parameters as parameter){
+ s.append("ASPL_OBJECT_TYPE* ").append(parameter.name)
+ if(i < statement.m.parameters.length - 1){
+ s.append(", ")
+ }
+ i++
+ }
+ s.append("){\n")
+ indentLevel++
+ if(Options:enableErrorHandling){
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("try{\n")
+ indentLevel++
+ }
+ foreach(CustomMethod(statement.m).code as node){
+ s.append(this.encode(node, true))
+ s.append("\n")
+ }
+ if(Options:enableErrorHandling){
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("return_uninitialized_from_try;\n")
+ }
+ indentLevel--
+ if(Options:enableErrorHandling){
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}catch{\n")
+ this.indentLevel++
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("return aspl_current_error;\n")
+ this.indentLevel--
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}\n")
+ this.indentLevel--
+ }
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}")
+ if(!CustomMethod(statement.m).isStatic){
+ s.append("\n\n")
+ s.append("ASPL_OBJECT_TYPE aspl_method_").append(TypeUtils:typeToCIdentifier(statement.m.type.identifier)).append("_").append(statement.m.name).append("_wrapper(")
+ s.append("ASPL_OBJECT_TYPE* this, ")
+ s.append("ASPL_OBJECT_TYPE* arguments[]){\n")
+ indentLevel++
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ if(statement.m.returnTypes.types.length > 0){
+ s.append("return ")
+ }
+ s.append("aspl_method_").append(TypeUtils:typeToCIdentifier(statement.m.type.identifier)).append("_").append(statement.m.name).append("(")
+ if(!CustomMethod(statement.m).isStatic){
+ s.append("this")
+ if(statement.m.parameters.length > 0){
+ s.append(", ")
+ }
+ }
+ i = 0
+ foreach(statement.m.parameters as parameter){
+ s.append("arguments[").append(string(i)).append("]")
+ if(i < statement.m.parameters.length - 1){
+ s.append(", ")
+ }
+ i++
+ }
+ s.append(");\n")
+ if(statement.m.returnTypes.types.length == 0){
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("return ASPL_UNINITIALIZED;\n")
+ }
+ indentLevel--
+ s.append("}")
+ }
+ return s.toString()
+ }
+
+ method encodePropertyDeclaration(PropertyDeclareStatement statement) returns string{
+ if(statement.p oftype CustomNormalProperty){
+ if(statement.p.isStatic){
+ this.staticNormalPropertyDeclarations.add(statement)
+ return "// Declare static property " + statement.p.name
+ }else{
+ return "ASPL_OBJECT_TYPE* " + statement.p.name
+ }
+ }else{
+ if(statement.p.isStatic){
+ this.staticReactivePropertyDeclarations.add(statement)
+ return "// Declare static reactive property " + statement.p.name
+ }else{
+ this.reactivePropertyDeclarations.add(statement)
+ return "// Declare reactive property " + statement.p.name
+ }
+ }
+ }
+
+ method encodeStaticNormalPropertyDeclaration(PropertyDeclareStatement statement) returns string{
+ var s = "ASPL_OBJECT_TYPE* aspl_static_property_" + TypeUtils:typeToCIdentifier(statement.p.type.identifier) + "_" + statement.p.name + ";"
+ if(statement.p.isThreadLocal){
+ // TODO: Thread local properties are not supported by GC boehm, we need to find a workaround
+ //return "_Thread_local " + s
+ return s
+ }else{
+ return s
+ }
+ }
+
+ method encodeReactivePropertyDeclarationHeader(PropertyDeclareStatement statement) returns string{
+ var s = new StringBuilder("")
+ if(CustomReactiveProperty(statement.p).getCode != null){
+ s.append("ASPL_OBJECT_TYPE aspl_reactive_property_").append(TypeUtils:typeToCIdentifier(statement.p.type.identifier)).append("_").append(statement.p.name).append("_get(ASPL_OBJECT_TYPE* this);")
+ s.append("\n\n")
+ }
+ if(CustomReactiveProperty(statement.p).setCode != null){
+ s.append("ASPL_OBJECT_TYPE aspl_reactive_property_").append(TypeUtils:typeToCIdentifier(statement.p.type.identifier)).append("_").append(statement.p.name).append("_set(ASPL_OBJECT_TYPE* this, ASPL_OBJECT_TYPE* value);")
+ }
+ return s.toString()
+ }
+
+ method encodeReactivePropertyDeclaration(PropertyDeclareStatement statement) returns string{
+ var s = new StringBuilder()
+ if(CustomReactiveProperty(statement.p).getCode != null){
+ s.append("ASPL_OBJECT_TYPE aspl_reactive_property_").append(TypeUtils:typeToCIdentifier(statement.p.type.identifier)).append("_").append(statement.p.name).append("_get(ASPL_OBJECT_TYPE* this){\n")
+ indentLevel++
+ if(Options:enableErrorHandling){
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("try{\n")
+ indentLevel++
+ }
+ foreach(CustomReactiveProperty(statement.p).getCode as node){
+ s.append(this.encode(node, true))
+ s.append("\n")
+ }
+ indentLevel--
+ if(Options:enableErrorHandling){
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}catch{\n")
+ this.indentLevel++
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("return aspl_current_error;\n")
+ this.indentLevel--
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}\n")
+ this.indentLevel--
+ }
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}\n\n")
+ }
+ if(CustomReactiveProperty(statement.p).setCode != null){
+ s.append("ASPL_OBJECT_TYPE aspl_reactive_property_").append(TypeUtils:typeToCIdentifier(statement.p.type.identifier)).append("_").append(statement.p.name).append("_set(ASPL_OBJECT_TYPE* this, ASPL_OBJECT_TYPE* value){\n")
+ indentLevel++
+ if(Options:enableErrorHandling){
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("try{\n")
+ indentLevel++
+ }
+ foreach(CustomReactiveProperty(statement.p).setCode as node){
+ s.append(this.encode(node, true))
+ s.append("\n")
+ }
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ if(Options:enableErrorHandling){
+ s.append("return_from_try(*value);\n")
+ }else{
+ s.append("return *value;\n")
+ }
+ indentLevel--
+ if(Options:enableErrorHandling){
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}catch{\n")
+ this.indentLevel++
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("return aspl_current_error;\n")
+ this.indentLevel--
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}\n")
+ this.indentLevel--
+ }
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}")
+ }
+ return s.toString()
+ }
+
+ method encodeStaticReactivePropertyDeclarationHeader(PropertyDeclareStatement statement) returns string{
+ var s = new StringBuilder("")
+ s.append("ASPL_OBJECT_TYPE aspl_static_reactive_property_").append(TypeUtils:typeToCIdentifier(statement.p.type.identifier)).append("_").append(statement.p.name).append("_get();")
+ s.append("\n\n")
+ s.append("ASPL_OBJECT_TYPE aspl_static_reactive_property_").append(TypeUtils:typeToCIdentifier(statement.p.type.identifier)).append("_").append(statement.p.name).append("_set(ASPL_OBJECT_TYPE* value);")
+ return s.toString()
+ }
+
+ method encodeStaticReactivePropertyDeclaration(PropertyDeclareStatement statement) returns string{
+ var s = new StringBuilder()
+ if(CustomReactiveProperty(statement.p).getCode != null){
+ s.append("ASPL_OBJECT_TYPE aspl_static_reactive_property_").append(TypeUtils:typeToCIdentifier(statement.p.type.identifier)).append("_").append(statement.p.name).append("_get(){\n")
+ indentLevel++
+ if(Options:enableErrorHandling){
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("try{\n")
+ indentLevel++
+ }
+ foreach(CustomReactiveProperty(statement.p).getCode as node){
+ s.append(this.encode(node, true))
+ s.append("\n")
+ }
+ indentLevel--
+ if(Options:enableErrorHandling){
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}catch{\n")
+ this.indentLevel++
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("return aspl_current_error;\n")
+ this.indentLevel--
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}\n")
+ this.indentLevel--
+ }
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}\n\n")
+ }
+ if(CustomReactiveProperty(statement.p).setCode != null){
+ s.append("ASPL_OBJECT_TYPE aspl_static_reactive_property_").append(TypeUtils:typeToCIdentifier(statement.p.type.identifier)).append("_").append(statement.p.name).append("_set(ASPL_OBJECT_TYPE* value){\n")
+ indentLevel++
+ if(Options:enableErrorHandling){
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("try{\n")
+ indentLevel++
+ }
+ foreach(CustomReactiveProperty(statement.p).setCode as node){
+ s.append(this.encode(node, true))
+ s.append("\n")
+ }
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ if(Options:enableErrorHandling){
+ s.append("return_from_try(*value);\n")
+ }else{
+ s.append("return *value;\n")
+ }
+ indentLevel--
+ if(Options:enableErrorHandling){
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}catch{\n")
+ this.indentLevel++
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("return aspl_current_error;\n")
+ this.indentLevel--
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}\n")
+ this.indentLevel--
+ }
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}")
+ }
+ return s.toString()
+ }
+
+ method encodeThis(ThisExpression expression) returns string{
+ return "*this"
+ }
+
+ method encodeEnumDeclaration(EnumDeclareStatement statement) returns string{
+ this.enumDeclarations.add(statement)
+ return "// Declare enum " + statement.e.type.identifier
+ }
+
+ method encodeEnumFieldAccess(EnumFieldAccessExpression expression) returns string{
+ return "ASPL_ENUM_FIELD_LITERAL(aspl_enum_" + TypeUtils:typeToCIdentifier(expression.field.e.type.identifier) + ", " + expression.field.e.fields?![expression.field.name].value + ")"
+ }
+
+ method encodeCallbackDeclarationHeader(CallbackDeclaration declaration) returns string{
+ var s = new StringBuilder("")
+ if(declaration.literal.value.returnTypes.types.length > 0){
+ s.append("ASPL_OBJECT_TYPE ")
+ }else{
+ s.append("void ")
+ }
+ s.append("aspl_callback_").append(string(declaration.id)).append("(")
+ var i = 0
+ foreach(declaration.literal.value.parameters as parameter){
+ s.append(this.encodeParameter(parameter))
+ if(i < declaration.literal.value.parameters.length - 1){
+ s.append(", ")
+ }
+ i++
+ }
+ if(declaration.literal.value.parameters.length > 0){
+ s.append(", ")
+ }
+ s.append("ASPL_ClosureMap* closure_map")
+ s.append(");")
+
+ if(callbackInvokeFunctionHandledTypes.contains(declaration.literal.value.type.identifier)){
+ return s.toString()
+ }
+ callbackInvokeFunctionHandledTypes.add(declaration.literal.value.type.identifier)
+
+ s.append("\n\n")
+
+ var genericTypes = Type:getGenericTypesIdentifiers(declaration.literal.value.type.identifier)
+ var hasReturnType = false
+ foreach(genericTypes as genericType){
+ if(genericType.startsWith("returns ")){
+ hasReturnType = true
+ break
+ }
+ }
+ if(hasReturnType){
+ s.append("ASPL_OBJECT_TYPE ")
+ }else{
+ s.append("void ")
+ }
+ s.append("aspl_" + TypeUtils:typeToCIdentifier(declaration.literal.value.type.identifier) + "_invoke(")
+ s.append("ASPL_OBJECT_TYPE closure")
+ if(declaration.literal.value.parameters.length > 0){
+ s.append(", ")
+ }
+ i = 0
+ foreach(declaration.literal.value.parameters as parameter){
+ s.append(this.encodeParameter(parameter))
+ if(i < declaration.literal.value.parameters.length - 1){
+ s.append(", ")
+ }
+ i++
+ }
+ s.append(");")
+ return s.toString()
+ }
+
+ method encodeCallbackDeclaration(CallbackDeclaration declaration) returns string{
+ var s = new StringBuilder("")
+ if(declaration.literal.value.returnTypes.types.length > 0){
+ s.append("ASPL_OBJECT_TYPE ")
+ }else{
+ s.append("void ")
+ }
+ s.append("aspl_callback_").append(string(declaration.id)).append("(")
+ var i = 0
+ foreach(declaration.literal.value.parameters as parameter){
+ s.append(this.encodeParameter(parameter))
+ if(i < declaration.literal.value.parameters.length - 1){
+ s.append(", ")
+ }
+ i++
+ }
+ if(declaration.literal.value.parameters.length > 0){
+ s.append(", ")
+ }
+ s.append("ASPL_ClosureMap* closure_map")
+ s.append("){\n")
+ this.indentLevel++
+ foreach(declaration.literal.capturedVariables as variable){
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("ASPL_OBJECT_TYPE* ").append(variable).append(" = ASPL_CLOSURE_MAP_GET(closure_map, \"").append(variable).append("\");\n")
+ }
+ if(Options:enableErrorHandling){
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("try{\n")
+ indentLevel++
+ }
+ foreach(declaration.literal.value.code as node){
+ s.append(this.encode(node, true))
+ s.append("\n")
+ }
+ if(Options:enableErrorHandling){
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("return_uninitialized_from_try;\n")
+ }
+ this.indentLevel--
+ if(Options:enableErrorHandling){
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}catch{\n")
+ this.indentLevel++
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("return aspl_current_error;\n")
+ this.indentLevel--
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}\n")
+ this.indentLevel--
+ }
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}")
+
+ if(callbackInvokeFunctionHandledTypes.contains(declaration.literal.value.type.identifier)){
+ return s.toString()
+ }
+ callbackInvokeFunctionHandledTypes.add(declaration.literal.value.type.identifier)
+
+ s.append("\n\n")
+
+ {
+ var genericTypes = Type:getGenericTypesIdentifiers(declaration.literal.value.type.identifier)
+ var hasReturnType = false
+ foreach(genericTypes as genericType){
+ if(genericType.startsWith("returns ")){
+ hasReturnType = true
+ break
+ }
+ }
+ if(hasReturnType){
+ s.append("ASPL_OBJECT_TYPE ")
+ }else{
+ s.append("void ")
+ }
+ }
+ s.append("aspl_" + TypeUtils:typeToCIdentifier(declaration.literal.value.type.identifier) + "_invoke(")
+ s.append("ASPL_OBJECT_TYPE closure")
+ if(declaration.literal.value.parameters.length > 0){
+ s.append(", ")
+ }
+ i = 0
+ foreach(declaration.literal.value.parameters as parameter){
+ s.append(this.encodeParameter(parameter))
+ if(i < declaration.literal.value.parameters.length - 1){
+ s.append(", ")
+ }
+ i++
+ }
+ s.append("){\n")
+ this.indentLevel++
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("ASPL_ClosureMap* closure_map = ASPL_ACCESS(closure).value.callback->closure_map;\n")
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ var castStr = "(%returntype% (*)("
+ var genericTypes = Type:getGenericTypesIdentifiers(declaration.literal.value.type.identifier)
+ var hasReturnType = false
+ foreach(genericTypes as genericType){
+ if(genericType.startsWith("returns ")){
+ hasReturnType = true
+ }else{
+ castStr += "ASPL_OBJECT_TYPE*, "
+ }
+ }
+ castStr += "ASPL_ClosureMap*"
+ if(hasReturnType){
+ castStr = castStr.replace("%returntype%", "ASPL_OBJECT_TYPE")
+ }else{
+ castStr = castStr.replace("%returntype%", "void")
+ }
+ castStr += "))"
+ if(hasReturnType){
+ s.append("return ")
+ }
+ s.append("(" + castStr + "ASPL_ACCESS(closure).value.callback->function)(")
+ i = 0
+ foreach(declaration.literal.value.parameters as parameter){
+ s.append(parameter.name)
+ if(i < declaration.literal.value.parameters.length - 1){
+ s.append(", ")
+ }
+ i++
+ }
+ if(declaration.literal.value.parameters.length > 0){
+ s.append(", ")
+ }
+ s.append("closure_map);\n")
+ this.indentLevel--
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}")
+ return s.toString()
+ }
+
+ method encodeCallbackLiteral(CallbackLiteral literal) returns string{
+ var declaration = new CallbackDeclaration(literal)
+ this.callbackDeclarations.add(declaration)
+ var s = new StringBuilder("ASPL_CALLBACK_LITERAL(\"").append(literal.getType().toString()).append("\", ").append(string(literal.getType().toString().length)).append(", aspl_callback_").append(string(declaration.id))
+ s.append(", ASPL_NEW_CLOSURE_MAP(")
+ s.append(string(literal.capturedVariables.length))
+ if(literal.capturedVariables.length > 0){
+ s.append(", ")
+ }
+ var i = 0
+ foreach(literal.capturedVariables as variable){
+ s.append("\"").append(variable).append("\", ").append(variable)
+ if(i < literal.capturedVariables.length - 1){
+ s.append(", ")
+ }
+ i++
+ }
+ s.append("))")
+ return s.toString()
+ }
+
+ method encodeImplementationCall(ImplementationCallExpression expression) returns string{
+ var s = new StringBuilder("ASPL_IMPLEMENT_").append(expression.call.replace(".", "$")).append("(")
+ var i = 0
+ foreach(expression.arguments as argument){
+ s.append("C_REFERENCE(" + this.encode(argument) + ")")
+ if(i < expression.arguments.length - 1){
+ s.append(", ")
+ }
+ i++
+ }
+ s.append(")")
+ return s.toString()
+ }
+
+ method encodeOfType(OfTypeExpression expression) returns string{
+ return "ASPL_OFTYPE(" + this.encode(expression.expression) + ", \"" + TypeUtils:shortName(expression.type.toString()) + "\")"
+ }
+
+ method encodeBreakStatement(BreakStatement statement) returns string{
+ return "break" // TODO: Add support for multiple levels
+ }
+
+ method encodeContinueStatement(ContinueStatement statement) returns string{
+ return "continue" // TODO: Add support for multiple levels
+ }
+
+ method encodeCast(CastExpression expression) returns string{
+ return "ASPL_CAST(" + this.encode(expression.value) + ", \"" + TypeUtils:shortName(expression.type.toString()) + "\")"
+ }
+
+ method encodeThrowStatement(ThrowStatement statement) returns string{
+ return "throw(" + this.encode(statement.errorInstance) + ")"
+ }
+
+ method encodeCatchExpression(CatchExpression expression) returns string{
+ var declaration = new CatchBlockDeclaration(expression)
+ this.catchBlockDeclarations.add(declaration)
+ var s = new StringBuilder("")
+ s.append("aspl_catch_block_" + declaration.id + "(C_REFERENCE(")
+ s.append(this.encode(expression.expression))
+ s.append("), ASPL_NEW_CLOSURE_MAP(")
+ s.append(string(expression.capturedVariables.length))
+ if(expression.capturedVariables.length > 0){
+ s.append(", ")
+ }
+ var i = 0
+ foreach(expression.capturedVariables as variable){
+ s.append("\"").append(variable).append("\", ").append(variable)
+ if(i < expression.capturedVariables.length - 1){
+ s.append(", ")
+ }
+ i++
+ }
+ s.append("))")
+ return s.toString()
+ }
+
+ method encodeCatchBlockDeclaration(CatchBlockDeclaration declaration) returns string{
+ var s = new StringBuilder("")
+ if(declaration.block.standalone){
+ s.append("void ")
+ }else{
+ s.append("ASPL_OBJECT_TYPE ")
+ }
+ s.append("aspl_catch_block_").append(string(declaration.id)).append("(")
+ s.append("ASPL_OBJECT_TYPE* error, ")
+ s.append("ASPL_ClosureMap* closure_map")
+ s.append("){\n")
+ this.indentLevel++
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("if(!aspl_object_is_error(*error)) return *error;\n")
+ foreach(declaration.block.capturedVariables as variable){
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("ASPL_OBJECT_TYPE* ").append(variable).append(" = ASPL_CLOSURE_MAP_GET(closure_map, \"").append(variable).append("\");\n")
+ }
+ foreach(declaration.block.code as node){
+ s.append(this.encode(node, true))
+ s.append("\n")
+ }
+ this.indentLevel--
+ repeat(indentLevel){
+ s.append("\t")
+ }
+ s.append("}")
+ s.append("\n\n")
+ return s.toString()
+ }
+
+ method encodeErrorPropagation(PropagateErrorExpression expression) returns string{
+ return "ASPL_UNWRAP_RESULT(" + this.encode(expression.expression) + ")"
+ }
+
+ method encodeFileEmbedding(EmbedFileExpression expression) returns string{
+ var embedId = fileEmbeds.length
+ fileEmbeds.add(expression)
+ return "aspl_embed_" + embedId
+ }
+
+ method encodeDiscard(Expression expression) returns string{
+ return this.encode(expression)
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/backend/stringcode/c/CallbackDeclaration.aspl b/stdlib/aspl/compiler/backend/stringcode/c/CallbackDeclaration.aspl
new file mode 100644
index 0000000..fea9252
--- /dev/null
+++ b/stdlib/aspl/compiler/backend/stringcode/c/CallbackDeclaration.aspl
@@ -0,0 +1,19 @@
+import aspl.parser.ast.literals
+
+class CallbackDeclaration {
+
+ [public]
+ [static]
+ property int _id
+
+ [readpublic]
+ property CallbackLiteral literal
+ [readpublic]
+ property int id
+
+ method construct(CallbackLiteral literal){
+ this.literal = literal
+ this.id = self:_id++
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/backend/stringcode/c/CatchBlockDeclaration.aspl b/stdlib/aspl/compiler/backend/stringcode/c/CatchBlockDeclaration.aspl
new file mode 100644
index 0000000..8f0d4ee
--- /dev/null
+++ b/stdlib/aspl/compiler/backend/stringcode/c/CatchBlockDeclaration.aspl
@@ -0,0 +1,20 @@
+import aspl.parser.ast.literals
+import aspl.parser.ast.expressions
+
+class CatchBlockDeclaration {
+
+ [public]
+ [static]
+ property int _id
+
+ [readpublic]
+ property CatchExpression block
+ [readpublic]
+ property int id
+
+ method construct(CatchExpression block){
+ this.block = block
+ this.id = self:_id++
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/backend/stringcode/c/IdentifierEscapeUtils.aspl b/stdlib/aspl/compiler/backend/stringcode/c/IdentifierEscapeUtils.aspl
new file mode 100644
index 0000000..2d3a898
--- /dev/null
+++ b/stdlib/aspl/compiler/backend/stringcode/c/IdentifierEscapeUtils.aspl
@@ -0,0 +1,49 @@
+[static]
+class IdentifierEscapeUtils {
+
+ [static]
+ property list reservedIdentifiers = [
+ "auto",
+ "break",
+ "case",
+ "char",
+ "const",
+ "continue",
+ "default",
+ "do",
+ "double",
+ "else",
+ "enum",
+ "extern",
+ "float",
+ "for",
+ "goto",
+ "if",
+ "int",
+ "long",
+ "register",
+ "return",
+ "short",
+ "signed",
+ "sizeof",
+ "static",
+ "struct",
+ "switch",
+ "typedef",
+ "union",
+ "unsigned",
+ "void",
+ "volatile",
+ "while"
+ ]
+
+ [public]
+ [static]
+ method escapeIdentifier(string identifier) returns string{
+ if(reservedIdentifiers.contains(identifier)){
+ return "_escaped_" + identifier
+ }
+ return identifier
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/backend/stringcode/c/TypeUtils.aspl b/stdlib/aspl/compiler/backend/stringcode/c/TypeUtils.aspl
new file mode 100644
index 0000000..4007310
--- /dev/null
+++ b/stdlib/aspl/compiler/backend/stringcode/c/TypeUtils.aspl
@@ -0,0 +1,23 @@
+[public]
+[static]
+class TypeUtils {
+
+ [public]
+ [static]
+ method shortName(string s) returns string{
+ if(s == "integer"){
+ return "int"
+ }elseif(s == "boolean"){
+ return "bool"
+ }else{
+ return s
+ }
+ }
+
+ [public]
+ [static]
+ method typeToCIdentifier(string type) returns string{
+ return type.replace(".", "$").replace("|", "_OR_").replace("<", "_").replace(">", "_").replace(", ", "_").replace(" ", "_")
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/backend/stringcode/c/template.c b/stdlib/aspl/compiler/backend/stringcode/c/template.c
new file mode 100644
index 0000000..24bfd4f
--- /dev/null
+++ b/stdlib/aspl/compiler/backend/stringcode/c/template.c
@@ -0,0 +1,4370 @@
+// TODO: Clean up & refactor this whole file sometime in the (near) future
+
+#ifdef __linux__
+#define _GNU_SOURCE // TODO: I think this shouldn't be necessary
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef __linux__
+#include
+#include
+#include
+#endif
+
+#ifndef ASPL_NO_MULTITHREADING
+#define THREAD_IMPLEMENTATION
+#define thread_id_t libthread_id_t
+#ifdef __APPLE__
+#define thread_create libthread_create // thread_create is already used by macOS
+#endif
+#include "thirdparty/thread.h/thread.h"
+#ifdef __APPLE__
+#undef thread_create
+#endif
+#undef thread_id_t
+#undef thread_h
+#define GC_THREADS
+#endif
+
+#ifdef _WIN32
+#define GC_NOT_DLL = 1
+#else
+#define GC_NO_DLOPEN
+#endif
+#define GC_BUILTIN_ATOMIC = 1
+
+#ifdef ASPL_DEBUG
+#define GC_DEBUG
+#endif
+
+#include "thirdparty/libgc/gc.c"
+#undef PROC
+
+#ifdef __APPLE__
+#define thread_create libthread_create
+#endif
+
+#define ASPL_MALLOC GC_MALLOC
+#define ASPL_MALLOC_ATOMIC GC_MALLOC_ATOMIC
+#define ASPL_REALLOC GC_REALLOC
+#define ASPL_FREE GC_FREE
+
+#ifdef ASPL_HEAP_BASED
+#define ASPL_OBJECT_TYPE ASPL_Object_internal*
+#define ASPL_ALLOC_OBJECT() ASPL_MALLOC(sizeof(ASPL_Object_internal))
+#define ASPL_ACCESS(obj) (*obj)
+#define ASPL_UNINITIALIZED NULL
+#define ASPL_IS_UNINITIALIZED(obj) (obj == NULL)
+#define ASPL_HASHMAP_WRAP(obj) (obj)
+#define ASPL_HASHMAP_UNWRAP(obj) (obj)
+#else
+#define ASPL_OBJECT_TYPE ASPL_Object_internal
+#define ASPL_ALLOC_OBJECT() (ASPL_OBJECT_TYPE){}
+#define ASPL_ACCESS(obj) (obj)
+#define ASPL_UNINITIALIZED (ASPL_OBJECT_TYPE){.kind = ASPL_OBJECT_KIND_UNINITIALIZED}
+#define ASPL_IS_UNINITIALIZED(obj) (obj.kind == ASPL_OBJECT_KIND_UNINITIALIZED)
+#define ASPL_HASHMAP_WRAP(obj) C_REFERENCE(obj)
+#define ASPL_HASHMAP_UNWRAP(obj) (*obj)
+#endif
+
+#ifdef __ANDROID__
+#include
+#define aspl_util_print(...) __android_log_print(ANDROID_LOG_INFO, ASPL_APP_NAME_STRING, __VA_ARGS__)
+#define aspl_util_vprint(...) __android_log_print(ANDROID_LOG_INFO, ASPL_APP_NAME_STRING, __VA_ARGS__)
+#else
+#define aspl_util_print(...) printf(__VA_ARGS__);
+#define aspl_util_vprint(...) vprintf(__VA_ARGS__);
+#endif
+
+typedef enum ASPL_ObjectKind
+{
+ ASPL_OBJECT_KIND_UNINITIALIZED,
+ ASPL_OBJECT_KIND_NULL,
+ ASPL_OBJECT_KIND_BOOLEAN,
+ ASPL_OBJECT_KIND_BYTE,
+ ASPL_OBJECT_KIND_INTEGER,
+ ASPL_OBJECT_KIND_LONG,
+ ASPL_OBJECT_KIND_FLOAT,
+ ASPL_OBJECT_KIND_DOUBLE,
+ ASPL_OBJECT_KIND_STRING,
+ ASPL_OBJECT_KIND_LIST,
+ ASPL_OBJECT_KIND_MAP,
+ ASPL_OBJECT_KIND_CALLBACK,
+ ASPL_OBJECT_KIND_POINTER,
+ ASPL_OBJECT_KIND_CLASS_INSTANCE,
+ ASPL_OBJECT_KIND_ENUM_FIELD,
+ ASPL_OBJECT_KIND_HANDLE
+} ASPL_ObjectKind;
+
+typedef struct ASPL_Object_internal
+{
+ ASPL_ObjectKind kind;
+ union
+ {
+ char boolean;
+ unsigned char integer8;
+ long integer32;
+ long long integer64;
+ float float32;
+ double float64;
+ struct ASPL_String* string;
+ struct ASPL_List* list;
+ struct ASPL_Map* map;
+ struct ASPL_Pointer* pointer;
+ struct ASPL_Callback* callback;
+ struct ASPL_ClassInstance* classInstance;
+ struct ASPL_EnumField* enumField;
+ void* handle;
+ } value;
+} ASPL_Object_internal;
+
+typedef struct ASPL_String
+{
+ char* str;
+ size_t length;
+} ASPL_String;
+
+typedef struct ASPL_List
+{
+ char* typePtr;
+ int typeLen;
+ ASPL_OBJECT_TYPE* value;
+ size_t length;
+} ASPL_List;
+
+ASPL_OBJECT_TYPE* C_REFERENCE(ASPL_OBJECT_TYPE x)
+{
+ ASPL_OBJECT_TYPE* ptr = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE));
+ *ptr = x;
+ return ptr;
+}
+
+#define HASHMAP_PREFIX hashmap_aspl_object_to_aspl_object
+#define HASHMAP_KEY_TYPE ASPL_Object_internal *
+#define HASHMAP_KEY_NULL_VALUE NULL
+#define HASHMAP_VALUE_TYPE ASPL_Object_internal *
+#define HASHMAP_VALUE_NULL_VALUE NULL
+
+unsigned int aspl_hash(ASPL_OBJECT_TYPE obj);
+
+unsigned int hashmap_aspl_object_to_aspl_object_hash_key(HASHMAP_KEY_TYPE key)
+{
+ return aspl_hash(ASPL_HASHMAP_UNWRAP(key));
+}
+
+int ASPL_IS_EQUAL(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b);
+
+int hashmap_aspl_object_to_aspl_object_equals_key(HASHMAP_KEY_TYPE a, HASHMAP_KEY_TYPE b)
+{
+ return ASPL_IS_EQUAL(ASPL_HASHMAP_UNWRAP(a), ASPL_HASHMAP_UNWRAP(b));
+}
+
+unsigned int hashmap_aspl_object_to_aspl_object_hash_value(HASHMAP_VALUE_TYPE value)
+{
+ return aspl_hash(ASPL_HASHMAP_UNWRAP(value));
+}
+
+int hashmap_aspl_object_to_aspl_object_equals_value(HASHMAP_VALUE_TYPE a, HASHMAP_VALUE_TYPE b)
+{
+ return ASPL_IS_EQUAL(ASPL_HASHMAP_UNWRAP(a), ASPL_HASHMAP_UNWRAP(b));
+}
+
+#define HASHMAP_MALLOC ASPL_MALLOC
+#define HASHMAP_REALLOC ASPL_REALLOC
+#define HASHMAP_FREE ASPL_FREE
+#include "thirdparty/hashmap/hashmap.h"
+
+typedef struct ASPL_Map
+{
+ char* typePtr;
+ int typeLen;
+ hashmap_aspl_object_to_aspl_object_HashMap* hashmap;
+} ASPL_Map;
+
+typedef struct ASPL_Pointer
+{
+ char* typePtr;
+ int typeLen;
+ ASPL_OBJECT_TYPE* object;
+} ASPL_Pointer;
+
+typedef struct ASPL_ClosureMapPair
+{
+ char* key;
+ ASPL_OBJECT_TYPE* value;
+} ASPL_ClosureMapPair;
+
+typedef struct ASPL_ClosureMap
+{
+ ASPL_ClosureMapPair* pairs;
+ int length;
+} ASPL_ClosureMap;
+
+typedef struct ASPL_Callback
+{
+ char* typePtr;
+ int typeLen;
+ void* function;
+ ASPL_ClosureMap* closure_map;
+} ASPL_Callback;
+
+#undef HASHMAP_PREFIX
+#undef HASHMAP_KEY_TYPE
+#undef HASHMAP_KEY_NULL_VALUE
+#undef HASHMAP_VALUE_TYPE
+#undef HASHMAP_VALUE_NULL_VALUE
+#define HASHMAP_PREFIX hashmap_str_to_voidptr
+#define HASHMAP_KEY_TYPE char *
+#define HASHMAP_KEY_NULL_VALUE NULL
+#define HASHMAP_VALUE_TYPE void *
+#define HASHMAP_VALUE_NULL_VALUE NULL
+
+unsigned int hashmap_str_to_voidptr_hash_key(char* key)
+{
+ unsigned int hash = 5381;
+ for (int i = 0; key[i] != '\0'; i++)
+ {
+ hash = ((hash << 5) + hash) + key[i];
+ }
+ return hash;
+}
+
+int hashmap_str_to_voidptr_equals_key(char* key1, char* key2)
+{
+ return strcmp(key1, key2) == 0;
+}
+
+unsigned int hashmap_str_to_voidptr_hash_value(void* value)
+{
+ return (unsigned int)(size_t)value;
+}
+
+int hashmap_str_to_voidptr_equals_value(void* value1, void* value2)
+{
+ return value1 == value2;
+}
+
+#include "thirdparty/hashmap/hashmap.h"
+
+typedef struct ASPL_ClassInstance
+{
+ char* typePtr;
+ int typeLen;
+ hashmap_str_to_voidptr_HashMap* properties;
+ char isError;
+} ASPL_ClassInstance;
+
+ASPL_OBJECT_TYPE* ASPL_CLASS_INSTANCE_GET_PROPERTY_ADDRESS(ASPL_ClassInstance* instance, char* property)
+{
+ return ASPL_HASHMAP_UNWRAP((ASPL_Object_internal**)hashmap_str_to_voidptr_hashmap_get_value(instance->properties, property));
+}
+
+ASPL_OBJECT_TYPE ASPL_CLASS_INSTANCE_GET_PROPERTY(ASPL_ClassInstance* instance, char* property)
+{
+ return *ASPL_HASHMAP_UNWRAP((ASPL_Object_internal**)hashmap_str_to_voidptr_hashmap_get_value(instance->properties, property));
+}
+
+ASPL_OBJECT_TYPE ASPL_CLASS_INSTANCE_SET_PROPERTY(ASPL_ClassInstance* instance, char* property, ASPL_OBJECT_TYPE value)
+{
+ if (hashmap_str_to_voidptr_hashmap_get_value(instance->properties, property) == NULL)
+ {
+ ASPL_Object_internal** address = ASPL_MALLOC(sizeof(ASPL_Object_internal*));
+ *address = ASPL_HASHMAP_WRAP(value);
+ hashmap_str_to_voidptr_hashmap_set(instance->properties, property, address);
+ }
+ else {
+ *ASPL_HASHMAP_UNWRAP((ASPL_Object_internal**)hashmap_str_to_voidptr_hashmap_get_value(instance->properties, property)) = value;
+ }
+ return value;
+}
+
+typedef struct ASPL_ReactiveProperty
+{
+ void* get_callback;
+ void* set_callback;
+} ASPL_ReactiveProperty;
+
+hashmap_str_to_voidptr_HashMap reactive_properties_map;
+
+void ASPL_CLASS_INIT_REACTIVE_PROPERTY(char* class, char* property, void* get_callback, void* set_callback)
+{
+ ASPL_ReactiveProperty* reactive_property = ASPL_MALLOC(sizeof(ASPL_ReactiveProperty));
+ reactive_property->get_callback = get_callback;
+ reactive_property->set_callback = set_callback;
+ hashmap_str_to_voidptr_HashMap* reactive_properties = hashmap_str_to_voidptr_hashmap_get_value(&reactive_properties_map, class);
+ if (reactive_properties == NULL)
+ {
+ reactive_properties = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 1 });
+ hashmap_str_to_voidptr_hashmap_set(&reactive_properties_map, class, reactive_properties);
+ }
+ hashmap_str_to_voidptr_hashmap_set(reactive_properties, property, reactive_property);
+}
+
+ASPL_OBJECT_TYPE ASPL_REACTIVE_PROPERTY_GET(ASPL_OBJECT_TYPE* this, char* property)
+{
+ ASPL_ReactiveProperty* reactive_property = hashmap_str_to_voidptr_hashmap_get_value(hashmap_str_to_voidptr_hashmap_get_value(&reactive_properties_map, ASPL_ACCESS(*this).value.classInstance->typePtr), property);
+ // TODO: Maybe add a debug check here to see if the property exists
+ return ((ASPL_OBJECT_TYPE(*)(ASPL_OBJECT_TYPE*))reactive_property->get_callback)(this);
+}
+
+ASPL_OBJECT_TYPE ASPL_REACTIVE_PROPERTY_SET(ASPL_OBJECT_TYPE* this, char* property, ASPL_OBJECT_TYPE* value)
+{
+ ASPL_ReactiveProperty* reactive_property = hashmap_str_to_voidptr_hashmap_get_value(hashmap_str_to_voidptr_hashmap_get_value(&reactive_properties_map, ASPL_ACCESS(*this).value.classInstance->typePtr), property);
+ return ((ASPL_OBJECT_TYPE(*)(ASPL_OBJECT_TYPE*, ASPL_OBJECT_TYPE*))reactive_property->set_callback)(this, value);
+}
+
+#undef HASHMAP_PREFIX
+#undef HASHMAP_KEY_TYPE
+#undef HASHMAP_KEY_NULL_VALUE
+#undef HASHMAP_VALUE_TYPE
+#undef HASHMAP_VALUE_NULL_VALUE
+#define HASHMAP_PREFIX hashmap_int_to_str
+#define HASHMAP_KEY_TYPE int
+#define HASHMAP_KEY_NULL_VALUE 0
+#define HASHMAP_VALUE_TYPE char *
+#define HASHMAP_VALUE_NULL_VALUE NULL
+
+unsigned int hashmap_int_to_str_hash_key(int key)
+{
+ return key;
+}
+
+int hashmap_int_to_str_equals_key(int key1, int key2)
+{
+ return key1 == key2;
+}
+
+unsigned int hashmap_int_to_str_hash_value(char* value)
+{
+ unsigned long hash = 5381;
+ int c;
+ char* str = value;
+ while ((c = *str++))
+ hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+ return hash;
+}
+
+int hashmap_int_to_str_equals_value(char* value1, char* value2)
+{
+ return strcmp(value1, value2) == 0;
+}
+
+#include "thirdparty/hashmap/hashmap.h"
+
+typedef struct ASPL_Enum
+{
+ char* typePtr;
+ int typeLen;
+ char isFlagEnum;
+ hashmap_int_to_str_HashMap* stringValues;
+} ASPL_Enum;
+
+typedef struct ASPL_EnumField
+{
+ ASPL_Enum* e;
+ int intValue;
+} ASPL_EnumField;
+
+hashmap_str_to_voidptr_HashMap enums_map;
+
+unsigned int aspl_hash(ASPL_OBJECT_TYPE obj)
+{
+ switch (ASPL_ACCESS(obj).kind)
+ {
+ case ASPL_OBJECT_KIND_NULL:
+ return 0;
+ case ASPL_OBJECT_KIND_BOOLEAN:
+ return ASPL_ACCESS(obj).value.boolean;
+ case ASPL_OBJECT_KIND_BYTE:
+ return ASPL_ACCESS(obj).value.integer8;
+ case ASPL_OBJECT_KIND_INTEGER:
+ return ASPL_ACCESS(obj).value.integer32;
+ case ASPL_OBJECT_KIND_LONG:
+ return ASPL_ACCESS(obj).value.integer64;
+ case ASPL_OBJECT_KIND_FLOAT:
+ return ASPL_ACCESS(obj).value.float32;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ return ASPL_ACCESS(obj).value.float64;
+ case ASPL_OBJECT_KIND_STRING:
+ {
+ // hash function from https://stackoverflow.com/a/7666577
+ unsigned long hash = 5381;
+ int c;
+ char* str = ASPL_ACCESS(obj).value.string->str;
+ while ((c = *str++))
+ hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+ return hash;
+ }
+ case ASPL_OBJECT_KIND_LIST:
+ {
+ unsigned long hash = 5381;
+ for (int i = 0; i < ASPL_ACCESS(obj).value.list->length; i++)
+ {
+ ASPL_OBJECT_TYPE item = ASPL_ACCESS(obj).value.list->value[i];
+ hash = ((hash << 5) + hash) + aspl_hash(item);
+ }
+ return hash;
+ }
+ case ASPL_OBJECT_KIND_MAP:
+ {
+ unsigned long hash = 5381;
+ hashmap_aspl_object_to_aspl_object_Pair* pair = NULL;
+ while ((pair = hashmap_aspl_object_to_aspl_object_hashmap_next(ASPL_ACCESS(obj).value.map->hashmap)) != NULL)
+ {
+ hash = ((hash << 5) + hash) + aspl_hash(ASPL_HASHMAP_UNWRAP(pair->key));
+ hash = ((hash << 5) + hash) + aspl_hash(ASPL_HASHMAP_UNWRAP(pair->value));
+ }
+ return hash;
+ }
+ case ASPL_OBJECT_KIND_CALLBACK:
+ return (int)(size_t)ASPL_ACCESS(obj).value.callback;
+ case ASPL_OBJECT_KIND_POINTER:
+ return aspl_hash(*ASPL_ACCESS(obj).value.pointer->object);
+ case ASPL_OBJECT_KIND_CLASS_INSTANCE:
+ return (int)(size_t)ASPL_ACCESS(obj).value.classInstance;
+ case ASPL_OBJECT_KIND_ENUM_FIELD:
+ return ASPL_ACCESS(obj).value.enumField->intValue;
+ default:
+ return 0;
+ }
+}
+
+ASPL_OBJECT_TYPE aspl_object_clone_shallow(ASPL_OBJECT_TYPE obj)
+{
+ ASPL_OBJECT_TYPE clone = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(clone).kind = ASPL_ACCESS(obj).kind;
+ switch (ASPL_ACCESS(obj).kind)
+ {
+ case ASPL_OBJECT_KIND_LIST:
+ ASPL_ACCESS(clone).value.list = ASPL_MALLOC(sizeof(ASPL_List));
+ ASPL_ACCESS(clone).value.list->typePtr = ASPL_ACCESS(obj).value.list->typePtr;
+ ASPL_ACCESS(clone).value.list->typeLen = ASPL_ACCESS(obj).value.list->typeLen;
+ ASPL_ACCESS(clone).value.list->length = ASPL_ACCESS(obj).value.list->length;
+ int mallocSize = sizeof(ASPL_OBJECT_TYPE) * ASPL_ACCESS(obj).value.list->length;
+ if (mallocSize < 1) mallocSize = 1; // malloc can return a null pointer if size is 0, but e.g. memcpy can't handle null pointers (even if the size is 0)
+ ASPL_ACCESS(clone).value.list->value = ASPL_MALLOC(mallocSize);
+ memcpy(ASPL_ACCESS(clone).value.list->value, ASPL_ACCESS(obj).value.list->value, sizeof(ASPL_OBJECT_TYPE) * ASPL_ACCESS(obj).value.list->length);
+ break;
+ case ASPL_OBJECT_KIND_MAP:
+ {
+ ASPL_ACCESS(clone).value.map = ASPL_MALLOC(sizeof(ASPL_Map));
+ ASPL_ACCESS(clone).value.map->typePtr = ASPL_ACCESS(obj).value.map->typePtr;
+ ASPL_ACCESS(clone).value.map->typeLen = ASPL_ACCESS(obj).value.map->typeLen;
+ int initial_capacity = ASPL_ACCESS(obj).value.map->hashmap->len;
+ if (initial_capacity < 1) initial_capacity = 1; // hashmap_new can't handle 0 as initial capacity
+ ASPL_ACCESS(clone).value.map->hashmap = hashmap_aspl_object_to_aspl_object_new_hashmap((hashmap_aspl_object_to_aspl_object_HashMapConfig) { .initial_capacity = initial_capacity });
+ hashmap_aspl_object_to_aspl_object_Pair* pair = NULL;
+ while ((pair = hashmap_aspl_object_to_aspl_object_hashmap_next(ASPL_ACCESS(obj).value.map->hashmap)) != NULL)
+ {
+ hashmap_aspl_object_to_aspl_object_hashmap_set(ASPL_ACCESS(clone).value.map->hashmap, pair->key, pair->value);
+ }
+ break;
+ }
+ case ASPL_OBJECT_KIND_POINTER:
+ ASPL_ACCESS(clone).value.pointer = ASPL_MALLOC(sizeof(ASPL_Pointer));
+ ASPL_ACCESS(clone).value.pointer->typePtr = ASPL_ACCESS(obj).value.pointer->typePtr;
+ ASPL_ACCESS(clone).value.pointer->typeLen = ASPL_ACCESS(obj).value.pointer->typeLen;
+ ASPL_ACCESS(clone).value.pointer->object = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE));
+ *ASPL_ACCESS(clone).value.pointer->object = *ASPL_ACCESS(obj).value.pointer->object;
+ break;
+ case ASPL_OBJECT_KIND_CLASS_INSTANCE:
+ {
+ ASPL_ACCESS(clone).value.classInstance = ASPL_MALLOC(sizeof(ASPL_ClassInstance));
+ ASPL_ACCESS(clone).value.classInstance->typePtr = ASPL_ACCESS(obj).value.classInstance->typePtr;
+ ASPL_ACCESS(clone).value.classInstance->typeLen = ASPL_ACCESS(obj).value.classInstance->typeLen;
+ int initial_capacity = ASPL_ACCESS(obj).value.classInstance->properties->len;
+ if (initial_capacity < 1) initial_capacity = 1; // hashmap_new can't handle 0 as initial capacity
+ ASPL_ACCESS(clone).value.classInstance->properties = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = initial_capacity });
+ char* key = NULL;
+ for (int i = 0; i < ASPL_ACCESS(obj).value.classInstance->properties->len; i++)
+ {
+ key = ASPL_ACCESS(obj).value.classInstance->properties->pairs[i]->key;
+ if (key != NULL)
+ {
+ hashmap_str_to_voidptr_hashmap_set(ASPL_ACCESS(clone).value.classInstance->properties, key, hashmap_str_to_voidptr_hashmap_get_value(ASPL_ACCESS(obj).value.classInstance->properties, key));
+ }
+ }
+ ASPL_ACCESS(clone).value.classInstance->isError = ASPL_ACCESS(obj).value.classInstance->isError;
+ break;
+ }
+ default:
+ ASPL_ACCESS(clone).value = ASPL_ACCESS(obj).value;
+ }
+ return clone;
+}
+
+ASPL_OBJECT_TYPE aspl_object_clone_deep(ASPL_OBJECT_TYPE obj)
+{
+ ASPL_OBJECT_TYPE clone = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(clone).kind = ASPL_ACCESS(obj).kind;
+ switch (ASPL_ACCESS(obj).kind)
+ {
+ case ASPL_OBJECT_KIND_STRING:
+ ASPL_ACCESS(clone).value.string = ASPL_MALLOC(sizeof(ASPL_String));
+ ASPL_ACCESS(clone).value.string->str = ASPL_MALLOC(ASPL_ACCESS(obj).value.string->length + 1);
+ memcpy(ASPL_ACCESS(clone).value.string->str, ASPL_ACCESS(obj).value.string->str, ASPL_ACCESS(obj).value.string->length);
+ ASPL_ACCESS(clone).value.string->str[ASPL_ACCESS(obj).value.string->length] = '\0';
+ ASPL_ACCESS(clone).value.string->length = ASPL_ACCESS(obj).value.string->length;
+ break;
+ case ASPL_OBJECT_KIND_LIST:
+ ASPL_ACCESS(clone).value.list = ASPL_MALLOC(sizeof(ASPL_List));
+ ASPL_ACCESS(clone).value.list->typePtr = ASPL_ACCESS(obj).value.list->typePtr;
+ ASPL_ACCESS(clone).value.list->typeLen = ASPL_ACCESS(obj).value.list->typeLen;
+ ASPL_ACCESS(clone).value.list->length = ASPL_ACCESS(obj).value.list->length;
+ int mallocSize = sizeof(ASPL_OBJECT_TYPE) * ASPL_ACCESS(obj).value.list->length;
+ if (mallocSize < 1) mallocSize = 1; // malloc can return a null pointer if size is 0, but e.g. memcpy can't handle null pointers (even if the size is 0
+ ASPL_ACCESS(clone).value.list->value = ASPL_MALLOC(mallocSize);
+ for (int i = 0; i < ASPL_ACCESS(obj).value.list->length; i++)
+ {
+ ASPL_ACCESS(clone).value.list->value[i] = aspl_object_clone_deep(ASPL_ACCESS(obj).value.list->value[i]);
+ }
+ break;
+ case ASPL_OBJECT_KIND_MAP:
+ {
+ ASPL_ACCESS(clone).value.map = ASPL_MALLOC(sizeof(ASPL_Map));
+ ASPL_ACCESS(clone).value.map->typePtr = ASPL_ACCESS(obj).value.map->typePtr;
+ ASPL_ACCESS(clone).value.map->typeLen = ASPL_ACCESS(obj).value.map->typeLen;
+ int initial_capacity = ASPL_ACCESS(obj).value.map->hashmap->len;
+ if (initial_capacity < 1) initial_capacity = 1; // hashmap_new can't handle 0 as initial capacity
+ ASPL_ACCESS(clone).value.map->hashmap = hashmap_aspl_object_to_aspl_object_new_hashmap((hashmap_aspl_object_to_aspl_object_HashMapConfig) { .initial_capacity = initial_capacity });
+ hashmap_aspl_object_to_aspl_object_Pair* pair = NULL;
+ while ((pair = hashmap_aspl_object_to_aspl_object_hashmap_next(ASPL_ACCESS(obj).value.map->hashmap)) != NULL)
+ {
+ hashmap_aspl_object_to_aspl_object_hashmap_set(ASPL_ACCESS(clone).value.map->hashmap, ASPL_HASHMAP_WRAP(aspl_object_clone_deep(ASPL_HASHMAP_UNWRAP(pair->key))), ASPL_HASHMAP_WRAP(aspl_object_clone_deep(ASPL_HASHMAP_UNWRAP(pair->value))));
+ }
+ break;
+ }
+ case ASPL_OBJECT_KIND_POINTER:
+ ASPL_ACCESS(clone).value.pointer = ASPL_MALLOC(sizeof(ASPL_Pointer));
+ ASPL_ACCESS(clone).value.pointer->typePtr = ASPL_ACCESS(obj).value.pointer->typePtr;
+ ASPL_ACCESS(clone).value.pointer->typeLen = ASPL_ACCESS(obj).value.pointer->typeLen;
+ ASPL_ACCESS(clone).value.pointer->object = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE));
+ *ASPL_ACCESS(clone).value.pointer->object = aspl_object_clone_deep(*ASPL_ACCESS(obj).value.pointer->object);
+ break;
+ case ASPL_OBJECT_KIND_CLASS_INSTANCE:
+ {
+ ASPL_ACCESS(clone).value.classInstance = ASPL_MALLOC(sizeof(ASPL_ClassInstance));
+ ASPL_ACCESS(clone).value.classInstance->typePtr = ASPL_ACCESS(obj).value.classInstance->typePtr;
+ ASPL_ACCESS(clone).value.classInstance->typeLen = ASPL_ACCESS(obj).value.classInstance->typeLen;
+ int initial_capacity = ASPL_ACCESS(obj).value.classInstance->properties->len;
+ if (initial_capacity < 1) initial_capacity = 1; // hashmap_new can't handle 0 as initial capacity
+ ASPL_ACCESS(clone).value.classInstance->properties = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = initial_capacity });
+ char* key = NULL;
+ for (int i = 0; i < ASPL_ACCESS(obj).value.classInstance->properties->len; i++)
+ {
+ key = ASPL_ACCESS(obj).value.classInstance->properties->pairs[i]->key;
+ if (key != NULL)
+ {
+ hashmap_str_to_voidptr_hashmap_set(ASPL_ACCESS(clone).value.classInstance->properties, key, ASPL_HASHMAP_WRAP(aspl_object_clone_deep(ASPL_HASHMAP_UNWRAP((ASPL_Object_internal*)hashmap_str_to_voidptr_hashmap_get_value(ASPL_ACCESS(obj).value.classInstance->properties, key)))));
+ }
+ }
+ ASPL_ACCESS(clone).value.classInstance->isError = ASPL_ACCESS(obj).value.classInstance->isError;
+ break;
+ }
+ default:
+ ASPL_ACCESS(clone).value = ASPL_ACCESS(obj).value;
+ break;
+ }
+ return clone;
+}
+
+#ifndef ASPL_NO_MULTITHREADING
+void ASPL_LAUNCH_THREAD(int (*callback)(void*), void* arguments, int arg_size)
+{
+ void* args = ASPL_MALLOC(arg_size);
+ memcpy(args, arguments, arg_size);
+ thread_create(callback, args, THREAD_STACK_SIZE_DEFAULT);
+}
+typedef struct ASPL_ThreadMethodWrapperData
+{
+ ASPL_OBJECT_TYPE (*callback)(ASPL_OBJECT_TYPE*, ASPL_OBJECT_TYPE* []);
+ ASPL_OBJECT_TYPE* this;
+ ASPL_OBJECT_TYPE** args;
+} ASPL_ThreadMethodWrapperData;
+
+int aspl_method_thread_wrapper(void* arguments)
+{
+ struct GC_stack_base sb;
+ GC_get_stack_base(&sb);
+ GC_register_my_thread(&sb);
+
+ ASPL_ThreadMethodWrapperData* data = arguments;
+ ASPL_OBJECT_TYPE (*callback)(ASPL_OBJECT_TYPE*, ASPL_OBJECT_TYPE* []) = data->callback;
+ ASPL_OBJECT_TYPE* this = data->this;
+ ASPL_OBJECT_TYPE** args = data->args;
+ callback(this, args);
+
+ GC_unregister_my_thread();
+ return 0;
+}
+
+void ASPL_LAUNCH_THREAD_METHOD(ASPL_OBJECT_TYPE (*callback)(ASPL_OBJECT_TYPE*, ASPL_OBJECT_TYPE* []), ASPL_OBJECT_TYPE* this, void* arguments, int arg_size)
+{
+ ASPL_ThreadMethodWrapperData* args = ASPL_MALLOC(sizeof(ASPL_ThreadMethodWrapperData));
+ args->callback = callback;
+ args->this = this;
+ args->args = arguments;
+ thread_create(aspl_method_thread_wrapper, args, THREAD_STACK_SIZE_DEFAULT);
+}
+#endif
+
+__attribute__((noreturn)) void ASPL_PANIC(const char* format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ aspl_util_print("ASPL panic: ");
+ aspl_util_vprint(format, args);
+ aspl_util_print("\n");
+ va_end(args);
+
+#ifdef ASPL_DEBUG
+ abort();
+#else
+ exit(1);
+#endif
+}
+
+ASPL_OBJECT_TYPE ASPL_NULL()
+{
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_NULL;
+ return obj;
+}
+
+ASPL_OBJECT_TYPE ASPL_TRUE()
+{
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(obj).value.boolean = 1;
+ return obj;
+}
+
+ASPL_OBJECT_TYPE ASPL_FALSE()
+{
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(obj).value.boolean = 0;
+ return obj;
+}
+
+ASPL_OBJECT_TYPE ASPL_BOOL_LITERAL(char b)
+{
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(obj).value.boolean = b;
+ return obj;
+}
+
+ASPL_OBJECT_TYPE ASPL_EQUALS(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b)
+{
+ return ASPL_BOOL_LITERAL(ASPL_IS_EQUAL(a, b));
+}
+
+int ASPL_IS_TRUE(ASPL_OBJECT_TYPE x)
+{
+ ASPL_OBJECT_TYPE obj = (ASPL_OBJECT_TYPE)x;
+ if (ASPL_ACCESS(obj).kind == ASPL_OBJECT_KIND_BOOLEAN)
+ {
+ return ASPL_ACCESS(obj).value.boolean;
+ }
+ return 0;
+}
+
+int ASPL_IS_EQUAL(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b)
+{
+ if (ASPL_ACCESS(a).kind != ASPL_ACCESS(b).kind)
+ return 0;
+ switch (ASPL_ACCESS(a).kind)
+ {
+ case ASPL_OBJECT_KIND_NULL:
+ return 1;
+ case ASPL_OBJECT_KIND_BOOLEAN:
+ return ASPL_ACCESS(a).value.boolean == ASPL_ACCESS(b).value.boolean;
+ case ASPL_OBJECT_KIND_BYTE:
+ return ASPL_ACCESS(a).value.integer8 == ASPL_ACCESS(b).value.integer8;
+ case ASPL_OBJECT_KIND_INTEGER:
+ return ASPL_ACCESS(a).value.integer32 == ASPL_ACCESS(b).value.integer32;
+ case ASPL_OBJECT_KIND_LONG:
+ return ASPL_ACCESS(a).value.integer64 == ASPL_ACCESS(b).value.integer64;
+ case ASPL_OBJECT_KIND_FLOAT:
+ return ASPL_ACCESS(a).value.float32 == ASPL_ACCESS(b).value.float32;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ return ASPL_ACCESS(a).value.float64 == ASPL_ACCESS(b).value.float64;
+ case ASPL_OBJECT_KIND_STRING:
+ return ASPL_ACCESS(a).value.string->length == ASPL_ACCESS(b).value.string->length && strcmp(ASPL_ACCESS(a).value.string->str, ASPL_ACCESS(b).value.string->str) == 0;
+ case ASPL_OBJECT_KIND_LIST:
+ return ASPL_ACCESS(a).value.list == ASPL_ACCESS(b).value.list; // TODO: Shouldn't this compare the elements and check if they are equal?
+ case ASPL_OBJECT_KIND_MAP:
+ // return ASPL_ACCESS(a).value.map == ASPL_ACCESS(b).value.map;
+ return hashmap_aspl_object_to_aspl_object_hashmap_equals(ASPL_ACCESS(a).value.map->hashmap, ASPL_ACCESS(b).value.map->hashmap);
+ case ASPL_OBJECT_KIND_CALLBACK:
+ return ASPL_ACCESS(a).value.callback == ASPL_ACCESS(b).value.callback;
+ case ASPL_OBJECT_KIND_POINTER:
+ return ASPL_ACCESS(a).value.pointer == ASPL_ACCESS(b).value.pointer;
+ case ASPL_OBJECT_KIND_CLASS_INSTANCE:
+ return ASPL_ACCESS(a).value.classInstance == ASPL_ACCESS(b).value.classInstance;
+ case ASPL_OBJECT_KIND_ENUM_FIELD:
+ return ASPL_ACCESS(a).value.enumField->intValue == ASPL_ACCESS(b).value.enumField->intValue;
+ default:
+ return 0;
+ }
+}
+
+ASPL_OBJECT_TYPE ASPL_NEGATE(ASPL_OBJECT_TYPE a)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a;
+ if (ASPL_ACCESS(objA).kind == ASPL_OBJECT_KIND_BOOLEAN)
+ {
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(obj).value.boolean = !ASPL_ACCESS(objA).value.boolean;
+ return obj;
+ }
+ return ASPL_UNINITIALIZED;
+}
+
+ASPL_OBJECT_TYPE ASPL_STRING_LITERAL(const char* str)
+{
+ size_t len = strlen(str);
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_STRING;
+ ASPL_ACCESS(obj).value.string = ASPL_MALLOC(sizeof(ASPL_String));
+ ASPL_ACCESS(obj).value.string->str = ASPL_MALLOC_ATOMIC(len + 1);
+ memcpy(ASPL_ACCESS(obj).value.string->str, str, len);
+ ASPL_ACCESS(obj).value.string->str[len] = '\0';
+ ASPL_ACCESS(obj).value.string->length = len;
+ return obj;
+}
+
+ASPL_OBJECT_TYPE ASPL_STRING_LITERAL_NO_COPY(char* str)
+{
+ size_t len = strlen(str);
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_STRING;
+ ASPL_ACCESS(obj).value.string = ASPL_MALLOC(sizeof(ASPL_String));
+ ASPL_ACCESS(obj).value.string->str = str;
+ ASPL_ACCESS(obj).value.string->length = len;
+ return obj;
+}
+
+ASPL_OBJECT_TYPE ASPL_BYTE_LITERAL(unsigned char b)
+{
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_BYTE;
+ ASPL_ACCESS(obj).value.integer8 = b;
+ return obj;
+}
+
+ASPL_OBJECT_TYPE ASPL_INT_LITERAL(long i)
+{
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(obj).value.integer32 = i;
+ return obj;
+}
+
+ASPL_OBJECT_TYPE ASPL_LONG_LITERAL(long long l)
+{
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(obj).value.integer64 = l;
+ return obj;
+}
+
+ASPL_OBJECT_TYPE ASPL_FLOAT_LITERAL(float f)
+{
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(obj).value.float32 = f;
+ return obj;
+}
+
+ASPL_OBJECT_TYPE ASPL_DOUBLE_LITERAL(double d)
+{
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(obj).value.float64 = d;
+ return obj;
+}
+
+ASPL_OBJECT_TYPE ASPL_LIST_LITERAL(char* typePtr, int typeLen, ASPL_OBJECT_TYPE list[], size_t size)
+{
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_LIST;
+ ASPL_List* l = ASPL_MALLOC(sizeof(ASPL_List));
+ l->typePtr = typePtr;
+ l->typeLen = typeLen;
+ l->length = size;
+ int mallocSize = sizeof(ASPL_OBJECT_TYPE) * size;
+ if (mallocSize < 1) mallocSize = 1; // malloc can return a null pointer if size is 0, but e.g. memcpy can't handle null pointers (even if the size is 0)
+ l->value = ASPL_MALLOC(mallocSize);
+ memcpy(l->value, list, sizeof(ASPL_OBJECT_TYPE) * size);
+ ASPL_ACCESS(obj).value.list = l;
+ return obj;
+}
+
+ASPL_OBJECT_TYPE ASPL_MAP_LITERAL(char* typePtr, int typeLen, ASPL_OBJECT_TYPE list[], size_t size)
+{
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_MAP;
+ ASPL_Map* m = ASPL_MALLOC(sizeof(ASPL_Map));
+ m->typePtr = typePtr;
+ m->typeLen = typeLen;
+ hashmap_aspl_object_to_aspl_object_HashMap* hashmap = hashmap_aspl_object_to_aspl_object_new_hashmap((hashmap_aspl_object_to_aspl_object_HashMapConfig) { .initial_capacity = size < 1 ? 1 : size });
+ for (int i = 0; i < size * 2; i += 2)
+ {
+ hashmap_aspl_object_to_aspl_object_hashmap_set(hashmap, ASPL_HASHMAP_WRAP(list[i]), ASPL_HASHMAP_WRAP(list[i + 1]));
+ }
+ m->hashmap = hashmap;
+ ASPL_ACCESS(obj).value.map = m;
+ return obj;
+}
+
+ASPL_ClosureMap* ASPL_NEW_CLOSURE_MAP(int count, ...)
+{
+ va_list args;
+ va_start(args, count);
+
+ ASPL_ClosureMapPair* pairs = ASPL_MALLOC(count * sizeof(ASPL_ClosureMapPair));
+ for (int i = 0; i < count; i++)
+ {
+ pairs[i].key = va_arg(args, char*);
+ pairs[i].value = va_arg(args, ASPL_OBJECT_TYPE*);
+ }
+
+ va_end(args);
+ ASPL_ClosureMap* map = ASPL_MALLOC(sizeof(ASPL_ClosureMap));
+ map->pairs = pairs;
+ map->length = count;
+ return map;
+}
+
+ASPL_OBJECT_TYPE* ASPL_CLOSURE_MAP_GET(ASPL_ClosureMap* map, char* key)
+{
+ for (int i = 0; i < map->length; i++)
+ {
+ if (strcmp(map->pairs[i].key, key) == 0)
+ {
+ return map->pairs[i].value;
+ }
+ }
+ return NULL;
+}
+
+ASPL_OBJECT_TYPE ASPL_CALLBACK_LITERAL(char* typePtr, int typeLen, void* ptr, ASPL_ClosureMap* closure_map)
+{
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_CALLBACK;
+ ASPL_Callback* cb = ASPL_MALLOC(sizeof(ASPL_Callback));
+ cb->typePtr = typePtr;
+ cb->typeLen = typeLen;
+ cb->function = ptr;
+ cb->closure_map = closure_map;
+ ASPL_ACCESS(obj).value.callback = cb;
+ return obj;
+}
+
+ASPL_OBJECT_TYPE ASPL_ENUM_FIELD_LITERAL(ASPL_Enum* e, int value)
+{
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_ENUM_FIELD;
+ ASPL_EnumField* field = ASPL_MALLOC(sizeof(ASPL_EnumField));
+ field->e = e;
+ field->intValue = value;
+ ASPL_ACCESS(obj).value.enumField = field;
+ return obj;
+}
+
+char* aspl_object_get_type_pointer(ASPL_OBJECT_TYPE obj)
+{
+ switch (ASPL_ACCESS(obj).kind)
+ {
+ case ASPL_OBJECT_KIND_NULL:
+ return "null";
+ case ASPL_OBJECT_KIND_BOOLEAN:
+ return "boolean";
+ case ASPL_OBJECT_KIND_INTEGER:
+ return "integer";
+ case ASPL_OBJECT_KIND_LONG:
+ return "integer64";
+ case ASPL_OBJECT_KIND_FLOAT:
+ return "float";
+ case ASPL_OBJECT_KIND_DOUBLE:
+ return "double";
+ case ASPL_OBJECT_KIND_STRING:
+ return "string";
+ case ASPL_OBJECT_KIND_LIST:
+ return ASPL_ACCESS(obj).value.list->typePtr;
+ case ASPL_OBJECT_KIND_MAP:
+ return ASPL_ACCESS(obj).value.map->typePtr;
+ case ASPL_OBJECT_KIND_CALLBACK:
+ return ASPL_ACCESS(obj).value.callback->typePtr;
+ case ASPL_OBJECT_KIND_POINTER:
+ return ASPL_ACCESS(obj).value.pointer->typePtr;
+ case ASPL_OBJECT_KIND_CLASS_INSTANCE:
+ return ASPL_ACCESS(obj).value.classInstance->typePtr;
+ case ASPL_OBJECT_KIND_ENUM_FIELD:
+ return ASPL_ACCESS(obj).value.enumField->e->typePtr;
+ case ASPL_OBJECT_KIND_HANDLE:
+ return "handle";
+ default:
+ return "unknown";
+ }
+}
+
+char* aspl_object_get_short_type_pointer(ASPL_OBJECT_TYPE obj)
+{
+ switch (ASPL_ACCESS(obj).kind)
+ {
+ case ASPL_OBJECT_KIND_LIST:
+ return "list";
+ case ASPL_OBJECT_KIND_MAP:
+ return "map";
+ default:
+ return aspl_object_get_type_pointer(obj);
+ }
+}
+
+char aspl_object_is_error(ASPL_OBJECT_TYPE obj) {
+ if (ASPL_ACCESS(obj).kind == ASPL_OBJECT_KIND_CLASS_INSTANCE) {
+ return ASPL_ACCESS(obj).value.classInstance->isError;
+ }
+ return 0;
+}
+
+typedef struct ASPL_ParentsList
+{
+ int amount;
+ char** parents;
+} ASPL_ParentsList;
+
+hashmap_str_to_voidptr_HashMap class_parents_map;
+
+void aspl_class_parent_init(char* class, char* parent)
+{
+ unsigned int key = hashmap_str_to_voidptr_hash_key(class);
+ ASPL_ParentsList* parents = hashmap_str_to_voidptr_hashmap_get_value(&class_parents_map, class);
+ if (parents == NULL)
+ {
+ parents = ASPL_MALLOC(sizeof(ASPL_ParentsList));
+ parents->amount = 0;
+ parents->parents = ASPL_MALLOC(sizeof(char*));
+ hashmap_str_to_voidptr_hashmap_set(&class_parents_map, class, parents);
+ }
+ parents->amount++;
+ parents->parents = ASPL_REALLOC(parents->parents, sizeof(char*) * parents->amount);
+ parents->parents[parents->amount - 1] = parent;
+}
+
+int aspl_is_class_child_of(char* class, char* parent)
+{
+ ASPL_ParentsList* parents = hashmap_str_to_voidptr_hashmap_get_value(&class_parents_map, class);
+ if (parents == NULL)
+ {
+ return 0;
+ }
+ for (int i = 0; i < parents->amount; i++)
+ {
+ if (strcmp(parents->parents[i], parent) == 0)
+ {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+ASPL_OBJECT_TYPE ASPL_OFTYPE(ASPL_OBJECT_TYPE a, char* type)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a;
+ int typeLen = strlen(type);
+ switch (ASPL_ACCESS(a).kind)
+ {
+ case ASPL_OBJECT_KIND_NULL:
+ return ASPL_BOOL_LITERAL(strcmp(type, "null") == 0);
+ case ASPL_OBJECT_KIND_BOOLEAN:
+ return ASPL_BOOL_LITERAL(strcmp(type, "bool") == 0);
+ case ASPL_OBJECT_KIND_BYTE:
+ return ASPL_BOOL_LITERAL(strcmp(type, "byte") == 0);
+ case ASPL_OBJECT_KIND_INTEGER:
+ return ASPL_BOOL_LITERAL(strcmp(type, "int") == 0);
+ case ASPL_OBJECT_KIND_LONG:
+ return ASPL_BOOL_LITERAL(strcmp(type, "long") == 0);
+ case ASPL_OBJECT_KIND_FLOAT:
+ return ASPL_BOOL_LITERAL(strcmp(type, "float") == 0);
+ case ASPL_OBJECT_KIND_DOUBLE:
+ return ASPL_BOOL_LITERAL(strcmp(type, "double") == 0);
+ case ASPL_OBJECT_KIND_STRING:
+ return ASPL_BOOL_LITERAL(strcmp(type, "string") == 0);
+ case ASPL_OBJECT_KIND_LIST:
+ return ASPL_BOOL_LITERAL(ASPL_ACCESS(objA).value.list->typeLen == typeLen && memcmp(ASPL_ACCESS(objA).value.list->typePtr, type, typeLen) == 0);
+ case ASPL_OBJECT_KIND_MAP:
+ return ASPL_BOOL_LITERAL(ASPL_ACCESS(objA).value.map->typeLen == typeLen && memcmp(ASPL_ACCESS(objA).value.map->typePtr, type, typeLen) == 0);
+ case ASPL_OBJECT_KIND_CALLBACK:
+ return ASPL_BOOL_LITERAL(ASPL_ACCESS(objA).value.callback->typeLen == typeLen && memcmp(ASPL_ACCESS(objA).value.callback->typePtr, type, typeLen) == 0);
+ case ASPL_OBJECT_KIND_POINTER:
+ return ASPL_BOOL_LITERAL(ASPL_ACCESS(objA).value.pointer->typeLen == typeLen && memcmp(ASPL_ACCESS(objA).value.pointer->typePtr, type, typeLen) == 0);
+ case ASPL_OBJECT_KIND_CLASS_INSTANCE:
+ if (ASPL_ACCESS(objA).value.classInstance->typeLen == typeLen && memcmp(ASPL_ACCESS(objA).value.classInstance->typePtr, type, typeLen) == 0)
+ {
+ return ASPL_TRUE();
+ }
+ else
+ {
+ return ASPL_BOOL_LITERAL(aspl_is_class_child_of(ASPL_ACCESS(objA).value.classInstance->typePtr, type));
+ }
+ case ASPL_OBJECT_KIND_ENUM_FIELD:
+ return ASPL_BOOL_LITERAL(ASPL_ACCESS(objA).value.enumField->e->typeLen == typeLen && memcmp(ASPL_ACCESS(objA).value.enumField->e->typePtr, type, typeLen) == 0);
+ default:
+ return ASPL_FALSE();
+ }
+}
+
+ASPL_OBJECT_TYPE ASPL_REFERENCE(char* typePtr, int typeLen, ASPL_OBJECT_TYPE* address)
+{
+ ASPL_OBJECT_TYPE pointerObject = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(pointerObject).kind = ASPL_OBJECT_KIND_POINTER;
+ ASPL_Pointer* ptr = ASPL_MALLOC(sizeof(ASPL_Pointer));
+ ptr->typePtr = typePtr;
+ ptr->typeLen = typeLen;
+ ptr->object = address;
+ ASPL_ACCESS(pointerObject).value.pointer = ptr;
+ return pointerObject;
+}
+
+#define ASPL_DEREFERENCE(obj) (*ASPL_ACCESS(obj).value.pointer->object)
+
+ASPL_OBJECT_TYPE ASPL_HANDLE_LITERAL(void* ptr)
+{
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_HANDLE;
+ ASPL_ACCESS(obj).value.handle = ptr;
+ return obj;
+}
+
+void ASPL_ASSERT(ASPL_OBJECT_TYPE obj, char* file, int line, int column)
+{
+ if (ASPL_IS_TRUE(obj))
+ {
+ return;
+ }
+ aspl_util_print("%s:%d:%d: Assertion failed\n", file, line, column);
+ exit(1);
+}
+
+char* aspl_stringify(ASPL_OBJECT_TYPE obj)
+{
+ char* str = NULL;
+ switch (ASPL_ACCESS(obj).kind)
+ {
+ case ASPL_OBJECT_KIND_NULL:
+ return "null";
+ break;
+ case ASPL_OBJECT_KIND_BOOLEAN:
+ return ASPL_ACCESS(obj).value.boolean ? "true" : "false";
+ break;
+ case ASPL_OBJECT_KIND_BYTE:
+ str = ASPL_MALLOC(sizeof(char) * 4);
+ sprintf(str, "%u", ASPL_ACCESS(obj).value.integer8);
+ return str;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ str = ASPL_MALLOC(sizeof(char) * 12);
+ sprintf(str, "%ld", ASPL_ACCESS(obj).value.integer32);
+ return str;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ str = ASPL_MALLOC(sizeof(char) * 24);
+ sprintf(str, "%lld", ASPL_ACCESS(obj).value.integer64);
+ return str;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ str = ASPL_MALLOC(sizeof(char) * 12);
+ sprintf(str, "%f", ASPL_ACCESS(obj).value.float32);
+ return str;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ str = ASPL_MALLOC(sizeof(char) * 24);
+ sprintf(str, "%f", ASPL_ACCESS(obj).value.float64);
+ return str;
+ break;
+ case ASPL_OBJECT_KIND_STRING:
+ return ASPL_ACCESS(obj).value.string->str;
+ case ASPL_OBJECT_KIND_LIST:
+ str = ASPL_MALLOC(sizeof(char) * 3);
+ strcat(str, "[");
+ for (int i = 0; i < ASPL_ACCESS(obj).value.list->length; i++)
+ {
+ char* str2 = aspl_stringify(ASPL_ACCESS(obj).value.list->value[i]);
+ if (i != ASPL_ACCESS(obj).value.list->length - 1)
+ {
+ str = ASPL_REALLOC(str, sizeof(char) * (strlen(str) + strlen(str2) + 3));
+ strcat(str, str2);
+ strcat(str, ", ");
+ }
+ else
+ {
+ str = ASPL_REALLOC(str, sizeof(char) * (strlen(str) + strlen(str2) + 2));
+ strcat(str, str2);
+ }
+ }
+ strcat(str, "]");
+ return str;
+ break;
+ case ASPL_OBJECT_KIND_MAP:
+ str = ASPL_MALLOC(sizeof(char) * 2);
+ strcat(str, "{");
+ for (int i = 0; i < ASPL_ACCESS(obj).value.map->hashmap->len; i++)
+ {
+ char* str2 = aspl_stringify(ASPL_HASHMAP_UNWRAP(ASPL_ACCESS(obj).value.map->hashmap->pairs[i]->key));
+ char* str3 = aspl_stringify(ASPL_HASHMAP_UNWRAP(ASPL_ACCESS(obj).value.map->hashmap->pairs[i]->value));
+ if (i != ASPL_ACCESS(obj).value.map->hashmap->len - 1)
+ {
+ str = ASPL_REALLOC(str, sizeof(char) * (strlen(str) + strlen(str2) + strlen(str3) + 5));
+ strcat(str, str2);
+ strcat(str, ": ");
+ strcat(str, str3);
+ strcat(str, ", ");
+ }
+ else
+ {
+ str = ASPL_REALLOC(str, sizeof(char) * (strlen(str) + strlen(str2) + strlen(str3) + 4));
+ strcat(str, str2);
+ strcat(str, ": ");
+ strcat(str, str3);
+ }
+ }
+ strcat(str, "}");
+ return str;
+ break;
+ case ASPL_OBJECT_KIND_CALLBACK:
+ return ASPL_ACCESS(obj).value.callback->typePtr;
+ break;
+ case ASPL_OBJECT_KIND_POINTER:
+ {
+ char* s = aspl_stringify(*ASPL_ACCESS(obj).value.pointer->object);
+ str = ASPL_MALLOC(sizeof(char) * (strlen(s) + 2));
+ strcat(str, s);
+ strcat(str, "*");
+ return str;
+ }
+ break;
+ case ASPL_OBJECT_KIND_ENUM_FIELD:
+ if (ASPL_ACCESS(obj).value.enumField->e->isFlagEnum)
+ {
+ for (int i = 0; i < ASPL_ACCESS(obj).value.enumField->e->stringValues->len; i++)
+ {
+ if (ASPL_ACCESS(obj).value.enumField->intValue & (1 << i))
+ {
+ char* str2 = hashmap_int_to_str_hashmap_get_value(ASPL_ACCESS(obj).value.enumField->e->stringValues, 1 << i);
+ if (i != ASPL_ACCESS(obj).value.enumField->e->stringValues->len - 1 && (ASPL_ACCESS(obj).value.enumField->intValue & ~((1 << (i + 1)) - 1)))
+ {
+ if (str == NULL)
+ {
+ str = ASPL_MALLOC(sizeof(char) * (strlen(str2) + 2));
+ }
+ else
+ {
+ str = ASPL_REALLOC(str, sizeof(char) * (strlen(str) + strlen(str2) + 2));
+ }
+ strcat(str, str2);
+ strcat(str, "|");
+ }
+ else
+ {
+ if (str == NULL)
+ {
+ str = ASPL_MALLOC(sizeof(char) * (strlen(str2) + 1));
+ }
+ else
+ {
+ str = ASPL_REALLOC(str, sizeof(char) * (strlen(str) + strlen(str2) + 1));
+ }
+ strcat(str, str2);
+ }
+ }
+ }
+ return str;
+ }
+ else
+ {
+ return hashmap_int_to_str_hashmap_get_value(ASPL_ACCESS(obj).value.enumField->e->stringValues, ASPL_ACCESS(obj).value.enumField->intValue);
+ }
+ default:
+ return "ASPL: Unknown type";
+ break;
+ }
+}
+
+ASPL_String aspl_stringify_rso(ASPL_OBJECT_TYPE obj)
+{
+ ASPL_String str = { 0 };
+ switch (ASPL_ACCESS(obj).kind)
+ {
+ case ASPL_OBJECT_KIND_NULL:
+ str.str = "null";
+ str.length = 4;
+ return str;
+ break;
+ case ASPL_OBJECT_KIND_BOOLEAN:
+ str.str = ASPL_ACCESS(obj).value.boolean ? "true" : "false";
+ str.length = ASPL_ACCESS(obj).value.boolean ? 4 : 5;
+ return str;
+ break;
+ case ASPL_OBJECT_KIND_BYTE:
+ str.str = ASPL_MALLOC(sizeof(char) * 4);
+ sprintf(str.str, "%u", ASPL_ACCESS(obj).value.integer8);
+ str.length = strlen(str.str);
+ return str;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ str.str = ASPL_MALLOC(sizeof(char) * 12);
+ sprintf(str.str, "%ld", ASPL_ACCESS(obj).value.integer32);
+ str.length = strlen(str.str);
+ return str;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ str.str = ASPL_MALLOC(sizeof(char) * 24);
+ sprintf(str.str, "%lld", ASPL_ACCESS(obj).value.integer64);
+ str.length = strlen(str.str);
+ return str;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ str.str = ASPL_MALLOC(sizeof(char) * 12);
+ sprintf(str.str, "%f", ASPL_ACCESS(obj).value.float32);
+ str.length = strlen(str.str);
+ return str;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ str.str = ASPL_MALLOC(sizeof(char) * 24);
+ sprintf(str.str, "%f", ASPL_ACCESS(obj).value.float64);
+ str.length = strlen(str.str);
+ return str;
+ break;
+ case ASPL_OBJECT_KIND_STRING:
+ return *ASPL_ACCESS(obj).value.string;
+ case ASPL_OBJECT_KIND_LIST:
+ str.str = ASPL_MALLOC(sizeof(char) * 3);
+ strcat(str.str, "[");
+ for (int i = 0; i < ASPL_ACCESS(obj).value.list->length; i++)
+ {
+ ASPL_String str2 = aspl_stringify_rso(ASPL_ACCESS(obj).value.list->value[i]);
+ if (i != ASPL_ACCESS(obj).value.list->length - 1)
+ {
+ str.str = ASPL_REALLOC(str.str, sizeof(char) * (str.length + str2.length + 3));
+ strcat(str.str, str2.str);
+ strcat(str.str, ", ");
+ str.length += str2.length + 2;
+ }
+ else
+ {
+ str.str = ASPL_REALLOC(str.str, sizeof(char) * (str.length + str2.length + 2));
+ strcat(str.str, str2.str);
+ str.length += str2.length + 1;
+ }
+ }
+ strcat(str.str, "]");
+ str.length += 1;
+ return str;
+ break;
+ case ASPL_OBJECT_KIND_MAP:
+ str.str = ASPL_MALLOC(sizeof(char) * 2);
+ strcat(str.str, "{");
+ for (int i = 0; i < ASPL_ACCESS(obj).value.map->hashmap->len; i++)
+ {
+ ASPL_String str2 = aspl_stringify_rso(ASPL_HASHMAP_UNWRAP(ASPL_ACCESS(obj).value.map->hashmap->pairs[i]->key));
+ ASPL_String str3 = aspl_stringify_rso(ASPL_HASHMAP_UNWRAP(ASPL_ACCESS(obj).value.map->hashmap->pairs[i]->value));
+ if (i != ASPL_ACCESS(obj).value.map->hashmap->len - 1)
+ {
+ str.str = ASPL_REALLOC(str.str, sizeof(char) * (str.length + str2.length + str3.length + 5));
+ strcat(str.str, str2.str);
+ strcat(str.str, ": ");
+ strcat(str.str, str3.str);
+ strcat(str.str, ", ");
+ str.length += str2.length + str3.length + 4;
+ }
+ else
+ {
+ str.str = ASPL_REALLOC(str.str, sizeof(char) * (str.length + str2.length + str3.length + 4));
+ strcat(str.str, str2.str);
+ strcat(str.str, ": ");
+ strcat(str.str, str3.str);
+ str.length += str2.length + str3.length + 3;
+ }
+ }
+ strcat(str.str, "}");
+ str.length += 1;
+ return str;
+ break;
+ case ASPL_OBJECT_KIND_CALLBACK:
+ str.str = ASPL_ACCESS(obj).value.callback->typePtr;
+ str.length = ASPL_ACCESS(obj).value.callback->typeLen;
+ return str;
+ break;
+ case ASPL_OBJECT_KIND_POINTER:
+ {
+ ASPL_String s = aspl_stringify_rso(*ASPL_ACCESS(obj).value.pointer->object);
+ str.str = ASPL_MALLOC(sizeof(char) * (s.length + 2));
+ strcat(str.str, s.str);
+ strcat(str.str, "*");
+ str.length = s.length + 1;
+ return str;
+ }
+ break;
+ case ASPL_OBJECT_KIND_ENUM_FIELD:
+ /*if (((ASPL_EnumField *)ASPL_ACCESS(objA).value.custom)->e->isFlagEnum)
+ {
+ for (int i = 0; i < ((ASPL_EnumField *)ASPL_ACCESS(objA).value.custom)->e->stringValues->len; i++)
+ {
+ if (((ASPL_EnumField *)ASPL_ACCESS(objA).value.custom)->intValue & (1 << i))
+ {
+ printf("%s", hashmap_int_to_str_hashmap_get_value(((ASPL_EnumField *)ASPL_ACCESS(objA).value.custom)->e->stringValues, 1 << i));
+ if (i != ((ASPL_EnumField *)ASPL_ACCESS(objA).value.custom)->e->stringValues->len - 1 && (((ASPL_EnumField *)ASPL_ACCESS(objA).value.custom)->intValue & ~((1 << (i + 1)) - 1)))
+ {
+ printf("|");
+ }
+ }
+ }
+ }
+ else
+ {
+ printf("%s", hashmap_int_to_str_hashmap_get_value(((ASPL_EnumField *)ASPL_ACCESS(objA).value.custom)->e->stringValues, ((ASPL_EnumField *)ASPL_ACCESS(objA).value.custom)->intValue));
+ }
+ break;*/
+ if (ASPL_ACCESS(obj).value.enumField->e->isFlagEnum)
+ {
+ for (int i = 0; i < ASPL_ACCESS(obj).value.enumField->e->stringValues->len; i++)
+ {
+ if (ASPL_ACCESS(obj).value.enumField->intValue & (1 << i))
+ {
+ ASPL_String str2;
+ str2.str = hashmap_int_to_str_hashmap_get_value(ASPL_ACCESS(obj).value.enumField->e->stringValues, 1 << i);
+ str2.length = strlen(str2.str);
+ if (i != ASPL_ACCESS(obj).value.enumField->e->stringValues->len - 1 && (ASPL_ACCESS(obj).value.enumField->intValue & ~((1 << (i + 1)) - 1)))
+ {
+ if (str.str == NULL)
+ {
+ str.str = ASPL_MALLOC(sizeof(char) * (str2.length + 2));
+ }
+ else
+ {
+ str.str = ASPL_REALLOC(str.str, sizeof(char) * (str.length + str2.length + 2));
+ }
+ strcat(str.str, str2.str);
+ strcat(str.str, "|");
+ str.length += str2.length + 1;
+ }
+ else
+ {
+ if (str.str == NULL)
+ {
+ str.str = ASPL_MALLOC(sizeof(char) * (str2.length + 1));
+ }
+ else
+ {
+ str.str = ASPL_REALLOC(str.str, sizeof(char) * (str.length + str2.length + 1));
+ }
+ strcat(str.str, str2.str);
+ str.length += str2.length;
+ }
+ }
+ }
+ return str;
+ }
+ else
+ {
+ ASPL_String str2;
+ str2.str = hashmap_int_to_str_hashmap_get_value(ASPL_ACCESS(obj).value.enumField->e->stringValues, ASPL_ACCESS(obj).value.enumField->intValue);
+ str2.length = strlen(str2.str);
+ return str2;
+ }
+ default:
+ str.str = "ASPL: Unknown type";
+ str.length = 18;
+ return str;
+ break;
+ }
+}
+
+#define ASPL_AND(a, b) (ASPL_IS_TRUE(a) ? b : ASPL_FALSE())
+#define ASPL_OR(a, b) (ASPL_IS_TRUE(a) ? ASPL_TRUE() : b)
+#define ASPL_XOR(a, b) (ASPL_IS_TRUE(a) ? (ASPL_IS_TRUE(b) ? ASPL_FALSE() : ASPL_TRUE()) : b)
+#define ASPL_NEGATE(a) (ASPL_IS_TRUE(a) ? ASPL_FALSE() : ASPL_TRUE())
+
+ASPL_OBJECT_TYPE ASPL_ENUM_AND(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b;
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_ENUM_FIELD;
+ ASPL_ACCESS(obj).value.enumField = ASPL_MALLOC(sizeof(ASPL_EnumField));
+ ASPL_ACCESS(obj).value.enumField->e = (ASPL_ACCESS(objA).value.enumField)->e;
+ ASPL_ACCESS(obj).value.enumField->intValue = (ASPL_ACCESS(objA).value.enumField)->intValue & ASPL_ACCESS(objB).value.enumField->intValue;
+ return obj;
+}
+
+ASPL_OBJECT_TYPE ASPL_ENUM_OR(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b;
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_ENUM_FIELD;
+ ASPL_ACCESS(obj).value.enumField = ASPL_MALLOC(sizeof(ASPL_EnumField));
+ ASPL_ACCESS(obj).value.enumField->e = (ASPL_ACCESS(objA).value.enumField)->e;
+ ASPL_ACCESS(obj).value.enumField->intValue = (ASPL_ACCESS(objA).value.enumField)->intValue | ASPL_ACCESS(objB).value.enumField->intValue;
+ return obj;
+}
+
+ASPL_OBJECT_TYPE ASPL_ENUM_XOR(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b;
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_ENUM_FIELD;
+ ASPL_ACCESS(obj).value.enumField = ASPL_MALLOC(sizeof(ASPL_EnumField));
+ ASPL_ACCESS(obj).value.enumField->e = (ASPL_ACCESS(objA).value.enumField)->e;
+ ASPL_ACCESS(obj).value.enumField->intValue = (ASPL_ACCESS(objA).value.enumField)->intValue ^ ASPL_ACCESS(objB).value.enumField->intValue;
+ return obj;
+}
+
+ASPL_OBJECT_TYPE ASPL_PLUS(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b;
+ ASPL_OBJECT_TYPE result = ASPL_UNINITIALIZED;
+
+ if (ASPL_ACCESS(objA).kind == ASPL_OBJECT_KIND_STRING)
+ {
+ ASPL_String s = aspl_stringify_rso(objB);
+ int sLen = s.length;
+ char* str = ASPL_MALLOC(ASPL_ACCESS(objA).value.string->length + sLen + 1);
+ memcpy(str, ASPL_ACCESS(objA).value.string->str, ASPL_ACCESS(objA).value.string->length);
+ memcpy(str + ASPL_ACCESS(objA).value.string->length, s.str, sLen);
+ str[ASPL_ACCESS(objA).value.string->length + sLen] = '\0';
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_STRING;
+ ASPL_ACCESS(result).value.string = ASPL_MALLOC(sizeof(ASPL_String));
+ ASPL_ACCESS(result).value.string->str = str;
+ ASPL_ACCESS(result).value.string->length = ASPL_ACCESS(objA).value.string->length + sLen;
+ }
+ else if (ASPL_ACCESS(objB).kind == ASPL_OBJECT_KIND_STRING)
+ {
+ ASPL_String s = aspl_stringify_rso(objA);
+ int sLen = s.length;
+ char* str = ASPL_MALLOC(ASPL_ACCESS(objB).value.string->length + sLen + 1);
+ memcpy(str, s.str, sLen);
+ memcpy(str + sLen, ASPL_ACCESS(objB).value.string->str, ASPL_ACCESS(objB).value.string->length);
+ str[ASPL_ACCESS(objB).value.string->length + sLen] = '\0';
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_STRING;
+ ASPL_ACCESS(result).value.string = ASPL_MALLOC(sizeof(ASPL_String));
+ ASPL_ACCESS(result).value.string->str = str;
+ ASPL_ACCESS(result).value.string->length = ASPL_ACCESS(objB).value.string->length + sLen;
+ }
+ else
+ {
+ switch (ASPL_ACCESS(objA).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BYTE;
+ ASPL_ACCESS(result).value.integer8 = ASPL_ACCESS(objA).value.integer8 + ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer8 + ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer8 + ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer8 + ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer8 + ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for + operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 + ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 + ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer32 + ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer32 + ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer32 + ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for + operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 + ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 + ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 + ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer64 + ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer64 + ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for + operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 + ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 + ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 + ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 + ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float32 + ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for + operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 + ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 + ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 + ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 + ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 + ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for + operator");
+ }
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for + operator");
+ }
+ }
+
+ return result;
+}
+
+ASPL_OBJECT_TYPE ASPL_PLUS_PLUS(ASPL_OBJECT_TYPE a)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a;
+ ASPL_OBJECT_TYPE result = ASPL_UNINITIALIZED;
+
+ switch (ASPL_ACCESS(objA).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BYTE;
+ ASPL_ACCESS(result).value.integer8 = ASPL_ACCESS(objA).value.integer8 + 1;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 + 1;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 + 1;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 + 1;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 + 1;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for + operator");
+ }
+
+ return result;
+}
+
+ASPL_OBJECT_TYPE ASPL_MINUS(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b;
+ ASPL_OBJECT_TYPE result = ASPL_UNINITIALIZED;
+
+ switch (ASPL_ACCESS(objA).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BYTE;
+ ASPL_ACCESS(result).value.integer8 = ASPL_ACCESS(objA).value.integer8 - ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer8 - ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer8 - ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer8 - ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer8 - ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for - operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 - ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 - ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer32 - ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer32 - ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer32 - ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for - operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 - ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 - ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 - ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer64 - ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer64 - ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for - operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 - ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 - ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 - ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 - ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float32 - ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for - operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 - ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 - ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 - ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 - ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 - ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for - operator");
+ }
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for - operator");
+ }
+
+ return result;
+}
+
+ASPL_OBJECT_TYPE ASPL_MULTIPLY(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b;
+ ASPL_OBJECT_TYPE result = ASPL_UNINITIALIZED;
+
+ switch (ASPL_ACCESS(objA).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BYTE;
+ ASPL_ACCESS(result).value.integer8 = ASPL_ACCESS(objA).value.integer8 * ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer8 * ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer8 * ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer8 * ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer8 * ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for * operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 * ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 * ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer32 * ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer32 * ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer32 * ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for * operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 * ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 * ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 * ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer64 * ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer64 * ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for * operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 * ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 * ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 * ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 * ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float32 * ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for * operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 * ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 * ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 * ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 * ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 * ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for * operator");
+ }
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for * operator");
+ }
+
+ return result;
+}
+
+ASPL_OBJECT_TYPE ASPL_DIVIDE(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b;
+ ASPL_OBJECT_TYPE result = ASPL_UNINITIALIZED;
+
+ switch (ASPL_ACCESS(objA).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BYTE;
+ ASPL_ACCESS(result).value.integer8 = ASPL_ACCESS(objA).value.integer8 / ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer8 / ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer8 / ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer8 / ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer8 / ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for / operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 / ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 / ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer32 / ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.integer32 / ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer32 / ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for / operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 / ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 / ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 / ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer64 / ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.integer64 / ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for / operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 / (float)ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 / (float)ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float32 / (double)ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = ASPL_ACCESS(objA).value.float32 / ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float32 / ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for / operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 / (double)ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 / (double)ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 / (double)ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 / (double)ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = ASPL_ACCESS(objA).value.float64 / ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for / operator");
+ }
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for / operator");
+ }
+
+ return result;
+}
+
+ASPL_OBJECT_TYPE ASPL_MODULO(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b;
+ ASPL_OBJECT_TYPE result = ASPL_UNINITIALIZED;
+
+ switch (ASPL_ACCESS(objA).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BYTE;
+ ASPL_ACCESS(result).value.integer8 = ASPL_ACCESS(objA).value.integer8 % ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer8 % ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer8 % ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = fmodf(ASPL_ACCESS(objA).value.integer8, ASPL_ACCESS(objB).value.float32);
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = fmod(ASPL_ACCESS(objA).value.integer8, ASPL_ACCESS(objB).value.float64);
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for \% operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 % ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(result).value.integer32 = ASPL_ACCESS(objA).value.integer32 % ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer32 % ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = fmodf(ASPL_ACCESS(objA).value.integer32, ASPL_ACCESS(objB).value.float32);
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = fmod(ASPL_ACCESS(objA).value.integer32, ASPL_ACCESS(objB).value.float64);
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for \% operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 % ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 % ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(result).value.integer64 = ASPL_ACCESS(objA).value.integer64 % ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = fmodf(ASPL_ACCESS(objA).value.integer64, ASPL_ACCESS(objB).value.float32);
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = fmod(ASPL_ACCESS(objA).value.integer64, ASPL_ACCESS(objB).value.float64);
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for \% operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = fmodf(ASPL_ACCESS(objA).value.float32, (float)ASPL_ACCESS(objB).value.integer8);
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = fmodf(ASPL_ACCESS(objA).value.float32, (float)ASPL_ACCESS(objB).value.integer32);
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = fmodf(ASPL_ACCESS(objA).value.float32, (float)ASPL_ACCESS(objB).value.integer64);
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(result).value.float32 = fmodf(ASPL_ACCESS(objA).value.float32, ASPL_ACCESS(objB).value.float32);
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = fmod(ASPL_ACCESS(objA).value.float32, ASPL_ACCESS(objB).value.float64);
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for \% operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = fmod(ASPL_ACCESS(objA).value.float64, (double)ASPL_ACCESS(objB).value.integer8);
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = fmod(ASPL_ACCESS(objA).value.float64, (double)ASPL_ACCESS(objB).value.integer32);
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = fmod(ASPL_ACCESS(objA).value.float64, (double)ASPL_ACCESS(objB).value.integer64);
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = fmod(ASPL_ACCESS(objA).value.float64, (double)ASPL_ACCESS(objB).value.float32);
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(result).value.float64 = fmod(ASPL_ACCESS(objA).value.float64, ASPL_ACCESS(objB).value.float64);
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for \% operator");
+ }
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for \% operator");
+ }
+
+ return result;
+}
+
+ASPL_OBJECT_TYPE ASPL_LESS_THAN(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b;
+ ASPL_OBJECT_TYPE result = ASPL_UNINITIALIZED;
+
+ switch (ASPL_ACCESS(objA).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 < ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 < ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 < ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 < ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 < ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for < operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 < ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 < ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 < ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 < ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 < ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for < operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 < ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 < ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 < ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 < ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 < ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for < operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 < ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 < ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 < ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 < ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 < ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for < operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 < ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 < ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 < ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 < ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 < ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for < operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_ENUM_FIELD:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_ENUM_FIELD:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.enumField->intValue < ASPL_ACCESS(objB).value.enumField->intValue;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for < operator");
+ }
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for < operator");
+ }
+
+ return result;
+}
+
+ASPL_OBJECT_TYPE ASPL_LESS_THAN_OR_EQUAL(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b;
+ ASPL_OBJECT_TYPE result = ASPL_UNINITIALIZED;
+
+ switch (ASPL_ACCESS(objA).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 <= ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 <= ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 <= ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 <= ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 <= ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for <= operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 <= ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 <= ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 <= ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 <= ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 <= ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for <= operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 <= ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 <= ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 <= ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 <= ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 <= ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for <= operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 <= ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 <= ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 <= ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 <= ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 <= ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for <= operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 <= ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 <= ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 <= ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 <= ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 <= ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for <= operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_ENUM_FIELD:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_ENUM_FIELD:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.enumField->intValue <= ASPL_ACCESS(objB).value.enumField->intValue;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for <= operator");
+ }
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for <= operator");
+ }
+
+ return result;
+}
+
+ASPL_OBJECT_TYPE ASPL_GREATER_THAN(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b;
+ ASPL_OBJECT_TYPE result = ASPL_UNINITIALIZED;
+
+ switch (ASPL_ACCESS(objA).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 > ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 > ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 > ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 > ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 > ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for > operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 > ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 > ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 > ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 > ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 > ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for > operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 > ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 > ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 > ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 > ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 > ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for > operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 > ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 > ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 > ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 > ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 > ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for > operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 > ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 > ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 > ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 > ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 > ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for > operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_ENUM_FIELD:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_ENUM_FIELD:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.enumField->intValue > ASPL_ACCESS(objB).value.enumField->intValue;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for > operator");
+ }
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for > operator");
+ }
+
+ return result;
+}
+
+ASPL_OBJECT_TYPE ASPL_GREATER_THAN_OR_EQUAL(ASPL_OBJECT_TYPE a, ASPL_OBJECT_TYPE b)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)a;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)b;
+ ASPL_OBJECT_TYPE result = ASPL_UNINITIALIZED;
+
+ switch (ASPL_ACCESS(objA).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 >= ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 >= ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 >= ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 >= ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer8 >= ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for >= operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 >= ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 >= ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 >= ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 >= ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer32 >= ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for >= operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 >= ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 >= ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 >= ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 >= ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.integer64 >= ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for >= operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 >= ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 >= ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 >= ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 >= ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float32 >= ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for >= operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 >= ASPL_ACCESS(objB).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 >= ASPL_ACCESS(objB).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 >= ASPL_ACCESS(objB).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 >= ASPL_ACCESS(objB).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.float64 >= ASPL_ACCESS(objB).value.float64;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for >= operator");
+ }
+ break;
+ case ASPL_OBJECT_KIND_ENUM_FIELD:
+ switch (ASPL_ACCESS(objB).kind)
+ {
+ case ASPL_OBJECT_KIND_ENUM_FIELD:
+ result = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(result).kind = ASPL_OBJECT_KIND_BOOLEAN;
+ ASPL_ACCESS(result).value.boolean = ASPL_ACCESS(objA).value.enumField->intValue >= ASPL_ACCESS(objB).value.enumField->intValue;
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for >= operator");
+ }
+ break;
+ default:
+ ASPL_PANIC("Invalid type combination for >= operator");
+ }
+
+ return result;
+}
+
+ASPL_OBJECT_TYPE ASPL_LIST_LENGTH(ASPL_OBJECT_TYPE list)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)list;
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(obj).value.integer32 = ASPL_ACCESS(objA).value.list->length;
+ return obj;
+}
+
+ASPL_OBJECT_TYPE ASPL_LIST_GET(ASPL_OBJECT_TYPE list, ASPL_OBJECT_TYPE index)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)list;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)index;
+ ASPL_List* l = ASPL_ACCESS(objA).value.list;
+ if (ASPL_ACCESS(objB).value.integer32 >= 0 && ASPL_ACCESS(objB).value.integer32 < l->length)
+ {
+ return l->value[ASPL_ACCESS(objB).value.integer32];
+ }
+ else {
+ ASPL_PANIC("Indexing list out of range (index: %d, length: %d)", ASPL_ACCESS(objB).value.integer32, l->length);
+ }
+}
+
+ASPL_OBJECT_TYPE ASPL_LIST_SET(ASPL_OBJECT_TYPE list, ASPL_OBJECT_TYPE index, ASPL_OBJECT_TYPE value)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)list;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)index;
+ ASPL_OBJECT_TYPE objC = (ASPL_OBJECT_TYPE)value;
+ ASPL_List* l = ASPL_ACCESS(objA).value.list;
+ if (ASPL_ACCESS(objB).value.integer32 >= 0 && ASPL_ACCESS(objB).value.integer32 < l->length)
+ {
+ l->value[ASPL_ACCESS(objB).value.integer32] = objC;
+ }
+ return list;
+}
+
+ASPL_OBJECT_TYPE ASPL_MAP_LENGTH(ASPL_OBJECT_TYPE map)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)map;
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(obj).value.integer32 = ASPL_ACCESS(objA).value.map->hashmap->len;
+ return obj;
+}
+
+ASPL_OBJECT_TYPE ASPL_MAP_GET(ASPL_OBJECT_TYPE map, ASPL_OBJECT_TYPE key)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)map;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)key;
+ ASPL_Object_internal* value = hashmap_aspl_object_to_aspl_object_hashmap_get_value(ASPL_ACCESS(objA).value.map->hashmap, ASPL_HASHMAP_WRAP(objB));
+ if (value == NULL)
+ {
+ return ASPL_NULL();
+ }
+ return ASPL_HASHMAP_UNWRAP(value);
+}
+
+ASPL_OBJECT_TYPE ASPL_MAP_GET_KEY_FROM_INDEX(ASPL_OBJECT_TYPE map, ASPL_OBJECT_TYPE index)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)map;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)index;
+ ASPL_Map* m = ASPL_ACCESS(objA).value.map;
+ if (ASPL_ACCESS(objB).value.integer32 >= 0 && ASPL_ACCESS(objB).value.integer32 < m->hashmap->len)
+ {
+ return ASPL_HASHMAP_UNWRAP(m->hashmap->pairs[ASPL_ACCESS(objB).value.integer32]->key);
+ }
+ else {
+ ASPL_PANIC("Indexing map out of range (index: %d, length: %d)", ASPL_ACCESS(objB).value.integer32, m->hashmap->len);
+ }
+}
+
+ASPL_OBJECT_TYPE ASPL_MAP_GET_VALUE_FROM_INDEX(ASPL_OBJECT_TYPE map, ASPL_OBJECT_TYPE index)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)map;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)index;
+ ASPL_Map* m = ASPL_ACCESS(objA).value.map;
+ if (ASPL_ACCESS(objB).value.integer32 >= 0 && ASPL_ACCESS(objB).value.integer32 < m->hashmap->len)
+ {
+ return ASPL_HASHMAP_UNWRAP(m->hashmap->pairs[ASPL_ACCESS(objB).value.integer32]->value);
+ }
+ else {
+ ASPL_PANIC("Indexing map out of range (index: %d, length: %d)", ASPL_ACCESS(objB).value.integer32, m->hashmap->len);
+ }
+}
+
+ASPL_OBJECT_TYPE ASPL_MAP_SET(ASPL_OBJECT_TYPE map, ASPL_OBJECT_TYPE key, ASPL_OBJECT_TYPE value)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)map;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)key;
+ ASPL_OBJECT_TYPE objC = (ASPL_OBJECT_TYPE)value;
+ hashmap_aspl_object_to_aspl_object_hashmap_set(ASPL_ACCESS(objA).value.map->hashmap, ASPL_HASHMAP_WRAP(objB), ASPL_HASHMAP_WRAP(objC));
+ return map;
+}
+
+ASPL_OBJECT_TYPE ASPL_STRING_LENGTH(ASPL_OBJECT_TYPE string)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)string;
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(obj).value.integer32 = ASPL_ACCESS(objA).value.string->length;
+ return obj;
+}
+
+ASPL_OBJECT_TYPE ASPL_STRING_INDEX(ASPL_OBJECT_TYPE string, ASPL_OBJECT_TYPE index)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)string;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)index;
+ char* str = ASPL_ACCESS(objA).value.string->str;
+ int len = ASPL_ACCESS(objA).value.string->length;
+ if (ASPL_ACCESS(objB).value.integer32 >= 0 && ASPL_ACCESS(objB).value.integer32 < len)
+ {
+ ASPL_OBJECT_TYPE obj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(obj).kind = ASPL_OBJECT_KIND_STRING;
+ ASPL_ACCESS(obj).value.string = ASPL_MALLOC(sizeof(ASPL_String));
+ ASPL_ACCESS(obj).value.string->str = ASPL_MALLOC(sizeof(char) * 2);
+ ASPL_ACCESS(obj).value.string->str[0] = str[ASPL_ACCESS(objB).value.integer32];
+ ASPL_ACCESS(obj).value.string->str[1] = '\0';
+ ASPL_ACCESS(obj).value.string->length = 1;
+ return obj;
+ }
+ else
+ {
+ ASPL_PANIC("Indexing string out of range (index: %d, length: %d)", ASPL_ACCESS(objB).value.integer32, len);
+ }
+}
+
+ASPL_OBJECT_TYPE ASPL_CAST(ASPL_OBJECT_TYPE obj, char* type)
+{
+ ASPL_OBJECT_TYPE newObj = ASPL_UNINITIALIZED;
+ if (strcmp(type, "null") == 0)
+ {
+ switch (ASPL_ACCESS(obj).kind)
+ {
+ case ASPL_OBJECT_KIND_NULL:
+ return obj;
+ default:
+ ASPL_PANIC("Cannot an object of type %s to type null", aspl_object_get_type_pointer(obj));
+ }
+ }
+ else if (strcmp(type, "bool") == 0)
+ {
+ switch (ASPL_ACCESS(obj).kind)
+ {
+ case ASPL_OBJECT_KIND_BOOLEAN:
+ return obj;
+ default:
+ ASPL_PANIC("Cannot cast an object of type %s to type bool", aspl_object_get_type_pointer(obj));
+ }
+ }
+ else if (strcmp(type, "byte") == 0)
+ {
+ switch (ASPL_ACCESS(obj).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ return obj;
+ case ASPL_OBJECT_KIND_INTEGER:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_BYTE;
+ ASPL_ACCESS(newObj).value.integer8 = ASPL_ACCESS(obj).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_BYTE;
+ ASPL_ACCESS(newObj).value.integer8 = ASPL_ACCESS(obj).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_BYTE;
+ ASPL_ACCESS(newObj).value.integer8 = ASPL_ACCESS(obj).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_BYTE;
+ ASPL_ACCESS(newObj).value.integer8 = ASPL_ACCESS(obj).value.float64;
+ break;
+ case ASPL_OBJECT_KIND_STRING:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_BYTE;
+ ASPL_ACCESS(newObj).value.integer8 = atoi(ASPL_ACCESS(obj).value.string->str);
+ break;
+ default:
+ ASPL_PANIC("Cannot cast an object of type %s to type byte", aspl_object_get_type_pointer(obj));
+ }
+ }
+ else if (strcmp(type, "int") == 0)
+ {
+ switch (ASPL_ACCESS(obj).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(newObj).value.integer32 = ASPL_ACCESS(obj).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ return obj;
+ case ASPL_OBJECT_KIND_LONG:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(newObj).value.integer32 = ASPL_ACCESS(obj).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(newObj).value.integer32 = ASPL_ACCESS(obj).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(newObj).value.integer32 = ASPL_ACCESS(obj).value.float64;
+ break;
+ case ASPL_OBJECT_KIND_ENUM_FIELD:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(newObj).value.integer32 = ASPL_ACCESS(obj).value.enumField->intValue;
+ break;
+ case ASPL_OBJECT_KIND_STRING:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_INTEGER;
+ ASPL_ACCESS(newObj).value.integer32 = atoi(ASPL_ACCESS(obj).value.string->str);
+ break;
+ default:
+ ASPL_PANIC("Cannot cast an object of type %s to type int", aspl_object_get_type_pointer(obj));
+ }
+ }
+ else if (strcmp(type, "long") == 0)
+ {
+ switch (ASPL_ACCESS(obj).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(newObj).value.integer64 = ASPL_ACCESS(obj).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(newObj).value.integer64 = ASPL_ACCESS(obj).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ return obj;
+ case ASPL_OBJECT_KIND_FLOAT:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(newObj).value.integer64 = ASPL_ACCESS(obj).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(newObj).value.integer64 = ASPL_ACCESS(obj).value.float64;
+ break;
+ case ASPL_OBJECT_KIND_STRING:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_LONG;
+ ASPL_ACCESS(newObj).value.integer64 = atoi(ASPL_ACCESS(obj).value.string->str);
+ break;
+ default:
+ ASPL_PANIC("Cannot cast an object of type %s to type long", aspl_object_get_type_pointer(obj));
+ }
+ }
+ else if (strcmp(type, "float") == 0)
+ {
+ switch (ASPL_ACCESS(obj).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(newObj).value.float32 = ASPL_ACCESS(obj).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(newObj).value.float32 = ASPL_ACCESS(obj).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(newObj).value.float32 = ASPL_ACCESS(obj).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ return obj;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(newObj).value.float32 = ASPL_ACCESS(obj).value.float64;
+ break;
+ case ASPL_OBJECT_KIND_STRING:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_FLOAT;
+ ASPL_ACCESS(newObj).value.float32 = atof(ASPL_ACCESS(obj).value.string->str);
+ break;
+ default:
+ ASPL_PANIC("Cannot cast an object of type %s to type float", aspl_object_get_type_pointer(obj));
+ }
+ }
+ else if (strcmp(type, "double") == 0)
+ {
+ switch (ASPL_ACCESS(obj).kind)
+ {
+ case ASPL_OBJECT_KIND_BYTE:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(newObj).value.float64 = ASPL_ACCESS(obj).value.integer8;
+ break;
+ case ASPL_OBJECT_KIND_INTEGER:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(newObj).value.float64 = ASPL_ACCESS(obj).value.integer32;
+ break;
+ case ASPL_OBJECT_KIND_LONG:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(newObj).value.float64 = ASPL_ACCESS(obj).value.integer64;
+ break;
+ case ASPL_OBJECT_KIND_FLOAT:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(newObj).value.float64 = ASPL_ACCESS(obj).value.float32;
+ break;
+ case ASPL_OBJECT_KIND_STRING:
+ newObj = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(newObj).kind = ASPL_OBJECT_KIND_DOUBLE;
+ ASPL_ACCESS(newObj).value.float64 = atof(ASPL_ACCESS(obj).value.string->str);
+ break;
+ case ASPL_OBJECT_KIND_DOUBLE:
+ return obj;
+ default:
+ ASPL_PANIC("Cannot cast an object of type %s to type double", aspl_object_get_type_pointer(obj));
+ }
+ }
+ else if (strcmp(type, "string") == 0)
+ {
+ return ASPL_STRING_LITERAL(aspl_stringify(obj));
+ }
+ else if (strncmp(type, "list<", 5) == 0)
+ {
+ switch (ASPL_ACCESS(obj).kind)
+ {
+ case ASPL_OBJECT_KIND_LIST:
+ return obj;
+ default:
+ ASPL_PANIC("Cannot cast an object of type %s to type list", aspl_object_get_type_pointer(obj));
+ }
+ }
+ else if (strncmp(type, "map<", 4) == 0)
+ {
+ switch (ASPL_ACCESS(obj).kind)
+ {
+ case ASPL_OBJECT_KIND_MAP:
+ return obj;
+ default:
+ ASPL_PANIC("Cannot cast an object of type %s to type map", aspl_object_get_type_pointer(obj));
+ }
+ }
+ else if (hashmap_str_to_voidptr_hashmap_contains_key(&enums_map, type))
+ {
+ switch (ASPL_ACCESS(obj).kind)
+ {
+ case ASPL_OBJECT_KIND_INTEGER:
+ return ASPL_ENUM_FIELD_LITERAL(hashmap_str_to_voidptr_hashmap_get_value(&enums_map, type), ASPL_ACCESS(obj).value.integer32);
+ case ASPL_OBJECT_KIND_ENUM_FIELD:
+ return obj;
+ default:
+ ASPL_PANIC("Cannot cast an object of type %s to an enum", aspl_object_get_type_pointer(obj));
+ }
+ }
+ else
+ {
+ return obj;
+ }
+ return newObj;
+}
+
+void aspl_function_print(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* newline)
+{
+ aspl_util_print("%s", aspl_stringify((ASPL_OBJECT_TYPE)*obj));
+ ASPL_OBJECT_TYPE newlineObj = (ASPL_OBJECT_TYPE)*newline;
+ if (ASPL_IS_TRUE(newlineObj))
+ {
+ aspl_util_print("\n");
+ }
+}
+
+ASPL_OBJECT_TYPE aspl_function_input(ASPL_OBJECT_TYPE* prompt)
+{
+ aspl_util_print("%s", aspl_stringify((ASPL_OBJECT_TYPE)*prompt));
+ fflush(stdout);
+ size_t buffer_size = 1024;
+ char* str = ASPL_MALLOC(buffer_size);
+ if (fgets(str, buffer_size, stdin) == NULL) {
+ ASPL_FREE(str);
+ return ASPL_STRING_LITERAL("");
+ }
+ size_t len = strlen(str);
+ if (len > 0 && str[len - 1] == '\n') {
+ str[len - 1] = '\0';
+ }
+ return ASPL_STRING_LITERAL_NO_COPY(str);
+}
+
+void aspl_function_exit(ASPL_OBJECT_TYPE* code)
+{
+ ASPL_OBJECT_TYPE codeObj = (ASPL_OBJECT_TYPE)*code;
+ exit(ASPL_ACCESS(codeObj).value.integer32);
+}
+
+void aspl_function_panic(ASPL_OBJECT_TYPE* obj)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ ASPL_PANIC(ASPL_ACCESS(objA).value.string->str);
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_toLower(ASPL_OBJECT_TYPE* obj)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ char* str = ASPL_ACCESS(objA).value.string->str;
+ char* str2 = ASPL_MALLOC(sizeof(char) * (ASPL_ACCESS(objA).value.string->length + 1));
+ for (int i = 0; i < ASPL_ACCESS(objA).value.string->length; i++)
+ {
+ str2[i] = tolower(str[i]);
+ }
+ str2[ASPL_ACCESS(objA).value.string->length] = '\0';
+ ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING;
+ ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String));
+ ASPL_ACCESS(s).value.string->str = str2;
+ ASPL_ACCESS(s).value.string->length = ASPL_ACCESS(objA).value.string->length;
+ return s;
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_toLower_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_string_toLower(obj);
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_toUpper(ASPL_OBJECT_TYPE* obj)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ char* str = ASPL_ACCESS(objA).value.string->str;
+ char* str2 = ASPL_MALLOC(sizeof(char) * (ASPL_ACCESS(objA).value.string->length + 1));
+ for (int i = 0; i < ASPL_ACCESS(objA).value.string->length; i++)
+ {
+ str2[i] = toupper(str[i]);
+ }
+ str2[ASPL_ACCESS(objA).value.string->length] = '\0';
+ ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING;
+ ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String));
+ ASPL_ACCESS(s).value.string->str = str2;
+ ASPL_ACCESS(s).value.string->length = ASPL_ACCESS(objA).value.string->length;
+ return s;
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_toUpper_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_string_toUpper(obj);
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_replace(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* oldValue, ASPL_OBJECT_TYPE* newValue)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*oldValue;
+ ASPL_OBJECT_TYPE objC = (ASPL_OBJECT_TYPE)*newValue;
+ char* str = ASPL_ACCESS(objA).value.string->str;
+ char* str2 = ASPL_ACCESS(objB).value.string->str;
+ char* str3 = ASPL_ACCESS(objC).value.string->str;
+
+ int lenA = ASPL_ACCESS(objA).value.string->length;
+ int lenB = ASPL_ACCESS(objB).value.string->length;
+ int lenC = ASPL_ACCESS(objC).value.string->length;
+
+ int newSize = lenA + 1;
+ for (int i = 0; i < lenA; i++)
+ {
+ if (strncmp(str + i, str2, lenB) == 0)
+ {
+ newSize += lenC - lenB;
+ i += lenB - 1;
+ }
+ }
+
+ char* str4 = ASPL_MALLOC(sizeof(char) * newSize);
+ int i = 0;
+ int j = 0;
+
+ while (i < lenA)
+ {
+ if (strncmp(str + i, str2, lenB) == 0)
+ {
+ for (int k = 0; k < lenC; k++)
+ {
+ str4[j++] = str3[k];
+ }
+ i += lenB;
+ }
+ else
+ {
+ str4[j++] = str[i++];
+ }
+ }
+
+ str4[j] = '\0';
+
+ ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING;
+ ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String));
+ ASPL_ACCESS(s).value.string->str = str4;
+ ASPL_ACCESS(s).value.string->length = newSize - 1;
+ return s;
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_replace_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_string_replace(obj, arguments[0], arguments[1]);
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_startsWith(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* prefix)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*prefix;
+ char* str = ASPL_ACCESS(objA).value.string->str;
+ char* str2 = ASPL_ACCESS(objB).value.string->str;
+ return ASPL_BOOL_LITERAL(strncmp(str, str2, ASPL_ACCESS(objB).value.string->length) == 0);
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_startsWith_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_string_startsWith(obj, arguments[0]);
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_endsWith(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* suffix)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*suffix;
+ char* str = ASPL_ACCESS(objA).value.string->str;
+ char* str2 = ASPL_ACCESS(objB).value.string->str;
+ return ASPL_BOOL_LITERAL(ASPL_ACCESS(objA).value.string->length >= ASPL_ACCESS(objB).value.string->length && strncmp(str + ASPL_ACCESS(objA).value.string->length - ASPL_ACCESS(objB).value.string->length, str2, ASPL_ACCESS(objB).value.string->length) == 0);
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_endsWith_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_string_endsWith(obj, arguments[0]);
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_contains(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* substring)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*substring;
+ char* str = ASPL_ACCESS(objA).value.string->str;
+ char* str2 = ASPL_ACCESS(objB).value.string->str;
+ return ASPL_BOOL_LITERAL(strstr(str, str2) != NULL);
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_contains_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_string_contains(obj, arguments[0]);
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_after(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* index)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*index;
+ char* str = ASPL_ACCESS(objA).value.string->str;
+ int i = ASPL_ACCESS(objB).value.integer32;
+ if (i >= -1 && i < (signed int)ASPL_ACCESS(objA).value.string->length)
+ {
+ char* str2 = ASPL_MALLOC(sizeof(char) * (ASPL_ACCESS(objA).value.string->length - i));
+ for (int j = i + 1; j < ASPL_ACCESS(objA).value.string->length; j++)
+ {
+ str2[j - (i + 1)] = str[j];
+ }
+ str2[ASPL_ACCESS(objA).value.string->length - (i + 1)] = '\0';
+ ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING;
+ ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String));
+ ASPL_ACCESS(s).value.string->str = str2;
+ ASPL_ACCESS(s).value.string->length = ASPL_ACCESS(objA).value.string->length - (i + 1);
+ return s;
+ }
+ ASPL_PANIC("string.after(): Indexing string out of range (index: %d, length: %d)", i, ASPL_ACCESS(objA).value.string->length);
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_after_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_string_after(obj, arguments[0]);
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_before(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* index)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*index;
+ char* str = ASPL_ACCESS(objA).value.string->str;
+ int i = ASPL_ACCESS(objB).value.integer32;
+ if (i >= 0 && i <= ASPL_ACCESS(objA).value.string->length)
+ {
+ char* str2 = ASPL_MALLOC(sizeof(char) * (i + 1));
+ for (int j = 0; j < i; j++)
+ {
+ str2[j] = str[j];
+ }
+ str2[i] = '\0';
+ ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING;
+ ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String));
+ ASPL_ACCESS(s).value.string->str = str2;
+ ASPL_ACCESS(s).value.string->length = i;
+ return s;
+ }
+ ASPL_PANIC("string.before(): Indexing string out of range (index: %d, length: %d)", i, ASPL_ACCESS(objA).value.string->length);
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_before_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_string_before(obj, arguments[0]);
+}
+
+char aspl_custom_is_space(char c, ASPL_OBJECT_TYPE* chars)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*chars;
+ char* str = ASPL_ACCESS(objA).value.string->str;
+ for (int i = 0; i < ASPL_ACCESS(objA).value.string->length; i++)
+ {
+ if (c == str[i])
+ {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_trim(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* chars)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ char* str = ASPL_ACCESS(objA).value.string->str;
+ char* str2 = ASPL_MALLOC(sizeof(char) * (ASPL_ACCESS(objA).value.string->length + 1));
+ int i = 0;
+ int j = ASPL_ACCESS(objA).value.string->length - 1;
+ while (i < ASPL_ACCESS(objA).value.string->length && aspl_custom_is_space(str[i], chars))
+ {
+ i++;
+ }
+ while (j >= 0 && aspl_custom_is_space(str[j], chars))
+ {
+ j--;
+ }
+ for (int k = i; k <= j; k++)
+ {
+ str2[k - i] = str[k];
+ }
+ str2[j - i + 1] = '\0';
+ ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING;
+ ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String));
+ ASPL_ACCESS(s).value.string->str = str2;
+ ASPL_ACCESS(s).value.string->length = j - i + 1;
+ return s;
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_trim_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_string_trim(obj, arguments[0]);
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_trimStart(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* chars)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ char* str = ASPL_ACCESS(objA).value.string->str;
+ char* str2 = ASPL_MALLOC(sizeof(char) * (ASPL_ACCESS(objA).value.string->length + 1));
+ int i = 0;
+ while (i < ASPL_ACCESS(objA).value.string->length && aspl_custom_is_space(str[i], chars))
+ {
+ i++;
+ }
+ for (int k = i; k < ASPL_ACCESS(objA).value.string->length; k++)
+ {
+ str2[k - i] = str[k];
+ }
+ str2[ASPL_ACCESS(objA).value.string->length - i] = '\0';
+ ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING;
+ ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String));
+ ASPL_ACCESS(s).value.string->str = str2;
+ ASPL_ACCESS(s).value.string->length = ASPL_ACCESS(objA).value.string->length - i;
+ return s;
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_trimStart_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_string_trimStart(obj, arguments[0]);
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_trimEnd(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* chars)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ char* str = ASPL_ACCESS(objA).value.string->str;
+ char* str2 = ASPL_MALLOC(sizeof(char) * (ASPL_ACCESS(objA).value.string->length + 1));
+ int i = ASPL_ACCESS(objA).value.string->length - 1;
+ while (i >= 0 && aspl_custom_is_space(str[i], chars))
+ {
+ i--;
+ }
+ for (int k = 0; k <= i; k++)
+ {
+ str2[k] = str[k];
+ }
+ str2[i + 1] = '\0';
+ ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING;
+ ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String));
+ ASPL_ACCESS(s).value.string->str = str2;
+ ASPL_ACCESS(s).value.string->length = i + 1;
+ return s;
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_trimEnd_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_string_trimEnd(obj, arguments[0]);
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_split(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* separator, ASPL_OBJECT_TYPE* limit)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*separator;
+ char* str = ASPL_ACCESS(objA).value.string->str;
+ char* str2 = ASPL_ACCESS(objB).value.string->str;
+ ASPL_List* l = ASPL_MALLOC(sizeof(ASPL_List));
+ l->length = 0;
+ l->value = ASPL_MALLOC(sizeof(ASPL_OBJECT_TYPE));
+
+ char* str3 = strstr(str, str2);
+ int remaining_splits = ASPL_ACCESS(*limit).value.integer32 - 1;
+
+ while (str3 != NULL && remaining_splits != 0)
+ {
+ l->length++;
+ l->value = ASPL_REALLOC(l->value, sizeof(ASPL_OBJECT_TYPE) * l->length);
+
+ size_t token_len = str3 - str;
+ char* token = (char*)ASPL_MALLOC(token_len + 1);
+ strncpy(token, str, token_len);
+ token[token_len] = '\0';
+
+ ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING;
+ ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String));
+ ASPL_ACCESS(s).value.string->str = token;
+ ASPL_ACCESS(s).value.string->length = token_len;
+ l->value[l->length - 1] = s;
+
+ str = str3 + ASPL_ACCESS(objB).value.string->length;
+ str3 = strstr(str, str2);
+
+ remaining_splits--;
+ }
+
+ if (strlen(str) > 0) // Attention: strlen() has to be used here (because the pointer is updated in the loop above)
+ {
+ l->length++;
+ l->value = ASPL_REALLOC(l->value, sizeof(ASPL_OBJECT_TYPE) * l->length);
+
+ ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING;
+ ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String));
+ ASPL_ACCESS(s).value.string->str = ASPL_MALLOC(strlen(str) + 1);
+ strcpy(ASPL_ACCESS(s).value.string->str, str);
+ ASPL_ACCESS(s).value.string->str[strlen(str)] = '\0';
+ ASPL_ACCESS(s).value.string->length = strlen(str);
+ l->value[l->length - 1] = s;
+ }
+
+ ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_LIST;
+ ASPL_ACCESS(s).value.list = l;
+ return s;
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_split_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_string_split(obj, arguments[0], arguments[1]);
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_reverse(ASPL_OBJECT_TYPE* obj)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ char* str = ASPL_ACCESS(objA).value.string->str;
+ char* str2 = ASPL_MALLOC(sizeof(char) * (ASPL_ACCESS(objA).value.string->length + 1));
+ for (int i = 0; i < ASPL_ACCESS(objA).value.string->length; i++)
+ {
+ str2[i] = str[ASPL_ACCESS(objA).value.string->length - i - 1];
+ }
+ str2[ASPL_ACCESS(objA).value.string->length] = '\0';
+ ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING;
+ ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String));
+ ASPL_ACCESS(s).value.string->str = str2;
+ ASPL_ACCESS(s).value.string->length = ASPL_ACCESS(objA).value.string->length;
+ return s;
+}
+
+ASPL_OBJECT_TYPE aspl_method_string_reverse_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_string_reverse(obj);
+}
+
+ASPL_OBJECT_TYPE aspl_method_list_contains(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* value)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*value;
+ ASPL_List* l = ASPL_ACCESS(objA).value.list;
+ for (int i = 0; i < l->length; i++)
+ {
+ if (ASPL_IS_EQUAL(l->value[i], objB))
+ {
+ return ASPL_TRUE();
+ }
+ }
+ return ASPL_FALSE();
+}
+
+ASPL_OBJECT_TYPE aspl_method_list_contains_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_list_contains(obj, arguments[0]);
+}
+
+ASPL_OBJECT_TYPE aspl_method_list_add(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* value)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*value;
+ ASPL_List* l = ASPL_ACCESS(objA).value.list;
+ l->length++;
+ l->value = ASPL_REALLOC(l->value, sizeof(ASPL_OBJECT_TYPE) * l->length);
+ l->value[l->length - 1] = objB;
+ return objA;
+}
+
+ASPL_OBJECT_TYPE aspl_method_list_add_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_list_add(obj, arguments[0]);
+}
+
+ASPL_OBJECT_TYPE aspl_method_list_insert(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* index, ASPL_OBJECT_TYPE* value)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*index;
+ ASPL_OBJECT_TYPE objC = (ASPL_OBJECT_TYPE)*value;
+ ASPL_List* l = ASPL_ACCESS(objA).value.list;
+ if (ASPL_ACCESS(objB).value.integer32 >= 0 && ASPL_ACCESS(objB).value.integer32 <= l->length)
+ {
+ l->length++;
+ l->value = ASPL_REALLOC(l->value, sizeof(ASPL_OBJECT_TYPE) * l->length);
+ for (int i = l->length - 1; i > ASPL_ACCESS(objB).value.integer32; i--)
+ {
+ l->value[i] = l->value[i - 1];
+ }
+ l->value[ASPL_ACCESS(objB).value.integer32] = objC;
+ }
+ return objA;
+}
+
+ASPL_OBJECT_TYPE aspl_method_list_insert_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_list_insert(obj, arguments[0], arguments[1]);
+}
+
+ASPL_OBJECT_TYPE aspl_method_list_insertElements(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* index, ASPL_OBJECT_TYPE* values)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*index;
+ ASPL_OBJECT_TYPE objC = (ASPL_OBJECT_TYPE)*values;
+ ASPL_List* l = ASPL_ACCESS(objA).value.list;
+ if (ASPL_ACCESS(objB).value.integer32 >= 0 && ASPL_ACCESS(objB).value.integer32 <= l->length)
+ {
+ l->length += ASPL_ACCESS(objC).value.list->length;
+ l->value = ASPL_REALLOC(l->value, sizeof(ASPL_OBJECT_TYPE) * l->length);
+ for (int i = l->length - 1; i > ASPL_ACCESS(objB).value.integer32; i--)
+ {
+ l->value[i] = l->value[i - 1];
+ }
+ for (int i = 0; i < ASPL_ACCESS(objC).value.list->length; i++)
+ {
+ l->value[ASPL_ACCESS(objB).value.integer32 + i] = ASPL_ACCESS(objC).value.list->value[i];
+ }
+ }
+ return objA;
+}
+
+ASPL_OBJECT_TYPE aspl_method_list_insertElements_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_list_insertElements(obj, arguments[0], arguments[1]);
+}
+
+ASPL_OBJECT_TYPE aspl_method_list_remove(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* value)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*value;
+ ASPL_List* l = ASPL_ACCESS(objA).value.list;
+ for (int i = 0; i < l->length; i++)
+ {
+ if (ASPL_IS_EQUAL(l->value[i], objB))
+ {
+ for (int j = i; j < l->length - 1; j++)
+ {
+ l->value[j] = l->value[j + 1];
+ }
+ l->length--;
+ int mallocSize = sizeof(ASPL_OBJECT_TYPE) * l->length;
+ if (mallocSize < 1) mallocSize = 1; // realloc can return a null pointer if size is 0, but e.g. memcpy can't handle null pointers (even if the size is 0)
+ l->value = ASPL_REALLOC(l->value, mallocSize);
+ return ASPL_TRUE();
+ }
+ }
+ return ASPL_FALSE();
+}
+
+ASPL_OBJECT_TYPE aspl_method_list_remove_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_list_remove(obj, arguments[0]);
+}
+
+ASPL_OBJECT_TYPE aspl_method_list_removeAt(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* index)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*index;
+ ASPL_List* l = ASPL_ACCESS(objA).value.list;
+ if (ASPL_ACCESS(objB).value.integer32 >= 0 && ASPL_ACCESS(objB).value.integer32 < l->length)
+ {
+ for (int i = ASPL_ACCESS(objB).value.integer32; i < l->length - 1; i++)
+ {
+ l->value[i] = l->value[i + 1];
+ }
+ l->length--;
+ int mallocSize = sizeof(ASPL_OBJECT_TYPE) * l->length;
+ if (mallocSize < 1) mallocSize = 1; // realloc can return a null pointer if size is 0, but e.g. memcpy can't handle null pointers (even if the size is 0)
+ l->value = ASPL_REALLOC(l->value, mallocSize);
+ }
+ return objA;
+}
+
+ASPL_OBJECT_TYPE aspl_method_list_removeAt_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_list_removeAt(obj, arguments[0]);
+}
+
+ASPL_OBJECT_TYPE aspl_method_list_clear(ASPL_OBJECT_TYPE* obj)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ ASPL_List* l = ASPL_ACCESS(objA).value.list;
+ // don't free the objects as the GC will take care of it; freeing them here manually may free objects that are still in use
+ l->length = 0;
+ l->value = ASPL_REALLOC(l->value, 1); // realloc can return a null pointer if size is 0, but e.g. memcpy can't handle null pointers (even if the size is 0)
+ return objA;
+}
+
+ASPL_OBJECT_TYPE aspl_method_list_clear_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_list_clear(obj);
+}
+
+ASPL_OBJECT_TYPE aspl_method_list_join(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* separator)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*separator;
+ ASPL_List* l = ASPL_ACCESS(objA).value.list;
+ char* str = ASPL_MALLOC(sizeof(char));
+ str[0] = '\0';
+ for (int i = 0; i < l->length; i++)
+ {
+ char* str2 = aspl_stringify(l->value[i]);
+ str = ASPL_REALLOC(str, sizeof(char) * (strlen(str) + strlen(str2) + ASPL_ACCESS(objB).value.string->length + 1));
+ strcat(str, str2);
+ if (i != l->length - 1)
+ {
+ strcat(str, ASPL_ACCESS(objB).value.string->str);
+ }
+ }
+ ASPL_OBJECT_TYPE s = ASPL_ALLOC_OBJECT();
+ ASPL_ACCESS(s).kind = ASPL_OBJECT_KIND_STRING;
+ ASPL_ACCESS(s).value.string = ASPL_MALLOC(sizeof(ASPL_String));
+ ASPL_ACCESS(s).value.string->str = str;
+ ASPL_ACCESS(s).value.string->length = strlen(str);
+ return s;
+}
+
+ASPL_OBJECT_TYPE aspl_method_list_join_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_list_join(obj, arguments[0]);
+}
+
+ASPL_OBJECT_TYPE aspl_method_map_containsKey(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* key)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*key;
+ ASPL_Map* m = ASPL_ACCESS(objA).value.map;
+ return ASPL_BOOL_LITERAL(hashmap_aspl_object_to_aspl_object_hashmap_contains_key(m->hashmap, ASPL_HASHMAP_WRAP(objB)));
+}
+
+ASPL_OBJECT_TYPE aspl_method_map_containsKey_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_map_containsKey(obj, arguments[0]);
+}
+
+ASPL_OBJECT_TYPE aspl_method_map_containsValue(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* value)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*value;
+ ASPL_Map* m = ASPL_ACCESS(objA).value.map;
+ return ASPL_BOOL_LITERAL(hashmap_aspl_object_to_aspl_object_hashmap_contains_value(m->hashmap, ASPL_HASHMAP_WRAP(objB)));
+}
+
+ASPL_OBJECT_TYPE aspl_method_map_containsValue_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_map_containsValue(obj, arguments[0]);
+}
+
+ASPL_OBJECT_TYPE aspl_method_map_remove(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* key)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ ASPL_OBJECT_TYPE objB = (ASPL_OBJECT_TYPE)*key;
+ ASPL_Map* m = ASPL_ACCESS(objA).value.map;
+ hashmap_aspl_object_to_aspl_object_hashmap_remove(m->hashmap, ASPL_HASHMAP_WRAP(objB));
+ return objA;
+}
+
+ASPL_OBJECT_TYPE aspl_method_map_remove_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_map_remove(obj, arguments[0]);
+}
+
+ASPL_OBJECT_TYPE aspl_method_map_clear(ASPL_OBJECT_TYPE* obj)
+{
+ ASPL_OBJECT_TYPE objA = (ASPL_OBJECT_TYPE)*obj;
+ ASPL_Map* m = ASPL_ACCESS(objA).value.map;
+ hashmap_aspl_object_to_aspl_object_hashmap_clear(m->hashmap);
+ return objA;
+}
+
+ASPL_OBJECT_TYPE aspl_method_map_clear_wrapper(ASPL_OBJECT_TYPE* obj, ASPL_OBJECT_TYPE* arguments[])
+{
+ return aspl_method_map_clear(obj);
+}
+
+hashmap_str_to_voidptr_HashMap object_methods_map;
+
+void aspl_object_method_init(char* class, char* method, ASPL_OBJECT_TYPE (*func)(ASPL_OBJECT_TYPE*, ASPL_OBJECT_TYPE* []))
+{
+ unsigned int key = hashmap_str_to_voidptr_hash_key(class);
+ hashmap_str_to_voidptr_HashMap* methods = hashmap_str_to_voidptr_hashmap_get_value(&object_methods_map, class);
+ if (methods == NULL)
+ {
+ methods = hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 1 });
+ hashmap_str_to_voidptr_hashmap_set(&object_methods_map, class, methods);
+ }
+ hashmap_str_to_voidptr_hashmap_set(methods, method, func);
+}
+
+ASPL_OBJECT_TYPE (*aspl_object_method_get(char* class, char* method))(ASPL_OBJECT_TYPE*, ASPL_OBJECT_TYPE* [])
+{
+ hashmap_str_to_voidptr_HashMap* methods = hashmap_str_to_voidptr_hashmap_get_value(&object_methods_map, class);
+ if (methods == NULL)
+ {
+ return NULL;
+ }
+ return hashmap_str_to_voidptr_hashmap_get_value(methods, method);
+}
+
+ASPL_OBJECT_TYPE aspl_object_method_invoke(ASPL_OBJECT_TYPE base, char* method, ASPL_OBJECT_TYPE* args[])
+{
+ if (strcmp(method, "cloneShallow") == 0)
+ { // TODO: Improve the performance of these
+ return aspl_object_clone_shallow(base);
+ }
+ else if (strcmp(method, "cloneDeep") == 0)
+ {
+ return aspl_object_clone_deep(base);
+ }
+
+ return aspl_object_method_get(aspl_object_get_short_type_pointer(base), method)(C_REFERENCE(base), args);
+}
+
+#ifndef ASPL_NO_MULTITHREADING
+void aspl_method_invoke_newthread(ASPL_OBJECT_TYPE base, void* method, ASPL_OBJECT_TYPE* args[], int arg_size)
+{
+ // no cloneShallow and cloneDeep as it wouldn't make sense to run them on a new thread
+ ASPL_LAUNCH_THREAD_METHOD(method, C_REFERENCE(base), args, arg_size);
+}
+
+void aspl_object_method_invoke_newthread(ASPL_OBJECT_TYPE base, char* method, ASPL_OBJECT_TYPE* args[], int arg_size)
+{
+ aspl_method_invoke_newthread(base, aspl_object_method_get(aspl_object_get_short_type_pointer(base), method), args, arg_size);
+}
+#endif
+
+void aspl_setup_builtin_method_pointers()
+{
+ object_methods_map = *hashmap_str_to_voidptr_new_hashmap((hashmap_str_to_voidptr_HashMapConfig) { .initial_capacity = 1 });
+
+ aspl_object_method_init("string", "toLower", aspl_method_string_toLower_wrapper);
+ aspl_object_method_init("string", "toUpper", aspl_method_string_toUpper_wrapper);
+ aspl_object_method_init("string", "replace", aspl_method_string_replace_wrapper);
+ aspl_object_method_init("string", "startsWith", aspl_method_string_startsWith_wrapper);
+ aspl_object_method_init("string", "endsWith", aspl_method_string_endsWith_wrapper);
+ aspl_object_method_init("string", "contains", aspl_method_string_contains_wrapper);
+ aspl_object_method_init("string", "after", aspl_method_string_after_wrapper);
+ aspl_object_method_init("string", "before", aspl_method_string_before_wrapper);
+ aspl_object_method_init("string", "trim", aspl_method_string_trim_wrapper);
+ aspl_object_method_init("string", "trimStart", aspl_method_string_trimStart_wrapper);
+ aspl_object_method_init("string", "trimEnd", aspl_method_string_trimEnd_wrapper);
+ aspl_object_method_init("string", "split", aspl_method_string_split_wrapper);
+ aspl_object_method_init("string", "reverse", aspl_method_string_reverse_wrapper);
+
+ aspl_object_method_init("list", "contains", aspl_method_list_contains_wrapper);
+ aspl_object_method_init("list", "add", aspl_method_list_add_wrapper);
+ aspl_object_method_init("list", "insert", aspl_method_list_insert_wrapper);
+ aspl_object_method_init("list", "insertElements", aspl_method_list_insertElements_wrapper);
+ aspl_object_method_init("list", "remove", aspl_method_list_remove_wrapper);
+ aspl_object_method_init("list", "removeAt", aspl_method_list_removeAt_wrapper);
+ aspl_object_method_init("list", "clear", aspl_method_list_clear_wrapper);
+ aspl_object_method_init("list", "join", aspl_method_list_join_wrapper);
+
+ aspl_object_method_init("map", "containsKey", aspl_method_map_containsKey_wrapper);
+ aspl_object_method_init("map", "containsValue", aspl_method_map_containsValue_wrapper);
+ aspl_object_method_init("map", "remove", aspl_method_map_remove_wrapper);
+ aspl_object_method_init("map", "clear", aspl_method_map_clear_wrapper);
+}
+
+#include
+
+#define ASPL_MAX_ERROR_HANDLING_DEPTH 1028
+
+_Thread_local jmp_buf aspl_error_handling_stack[ASPL_MAX_ERROR_HANDLING_DEPTH];
+_Thread_local int aspl_error_handling_stack_index = -1;
+_Thread_local ASPL_OBJECT_TYPE aspl_current_error;
+
+int aspl_validate_error_handling_depth(int depth){
+ if(depth < 0 || depth > ASPL_MAX_ERROR_HANDLING_DEPTH){
+ ASPL_PANIC("Error handling depth out of bounds: %d\n", depth);
+ }
+ return 1;
+}
+
+#define try if (aspl_validate_error_handling_depth(++aspl_error_handling_stack_index) && !setjmp(aspl_error_handling_stack[aspl_error_handling_stack_index]))
+
+#define catch else \
+ if (aspl_validate_error_handling_depth(--aspl_error_handling_stack_index))
+
+#define actually_throw(error) do { \
+ if (aspl_validate_error_handling_depth(aspl_error_handling_stack_index)) { \
+ aspl_current_error = error; \
+ longjmp(aspl_error_handling_stack[aspl_error_handling_stack_index], 1); \
+ } \
+} while (0)
+
+#define return_from_try(value) do { \
+ aspl_validate_error_handling_depth(--aspl_error_handling_stack_index); \
+ return (value); \
+} while (0)
+
+#define return_void_from_try do { \
+ aspl_validate_error_handling_depth(--aspl_error_handling_stack_index); \
+ return; \
+} while (0)
+
+#define return_uninitialized_from_try do { \
+ aspl_validate_error_handling_depth(--aspl_error_handling_stack_index); \
+ return ASPL_UNINITIALIZED; \
+} while (0)
+
+#define throw(value) do { \
+ aspl_validate_error_handling_depth(--aspl_error_handling_stack_index); \
+ return aspl_current_error = (value); \
+} while (0)
+
+#define escape(value) do { \
+ aspl_current_error = (value); \
+ longjmp(aspl_error_handling_stack[aspl_error_handling_stack_index], 1); \
+} while (0)
+
+ASPL_OBJECT_TYPE ASPL_UNWRAP_RESULT(ASPL_OBJECT_TYPE result) {
+ if (aspl_object_is_error(result)) {
+ actually_throw(result);
+ }
+ else {
+ return result;
+ }
+}
+
+#ifdef _WIN32
+#if !defined(_WIN32_WINNT) || _WIN32_WINNT > 0x0600
+// TODO: Is this really how it should be done?
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600 // ensure Windows Vista functions like CreateSymbolicLink are included
+#endif
+#include
+#include
+
+unsigned int aspl_original_codepage = 0;
+
+void aspl_restore_windows_console() {
+ SetConsoleOutputCP(aspl_original_codepage);
+}
+
+#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
+#endif
+
+void aspl_setup_windows_console() {
+ aspl_original_codepage = GetConsoleOutputCP();
+ atexit(aspl_restore_windows_console);
+ SetConsoleOutputCP(CP_UTF8);
+ DWORD mode = 0;
+ HANDLE osfh = (HANDLE)_get_osfhandle(1);
+ GetConsoleMode(osfh, &mode);
+ if (mode > 0) {
+ SetConsoleMode(osfh, mode | ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
+ setbuf(stdout, NULL);
+ }
+ // TODO: stderr
+}
+#endif
+
+void aspl_setup_gc() {
+ GC_set_pages_executable(0);
+ GC_INIT();
+#ifndef ASPL_NO_MULTITHREADING
+ GC_allow_register_threads();
+#endif
+}
+
+#ifdef ASPL_USE_SSL
+#ifdef __linux__
+#include "thirdparty/ssl/linux/ssl.h"
+#elif __APPLE__
+#include "thirdparty/ssl/macos/ssl.h"
+#else
+#error SSL is not supported for this target OS
+#endif
+
+void aspl_setup_ssl() {
+ ssl_init();
+}
+#endif
+
+int aspl_argc;
+char** aspl_argv;
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/main.aspl b/stdlib/aspl/compiler/main.aspl
new file mode 100644
index 0000000..34ae4fa
--- /dev/null
+++ b/stdlib/aspl/compiler/main.aspl
@@ -0,0 +1,243 @@
+import aspl.compiler.backend
+import aspl.compiler.backend.bytecode.ail
+import aspl.compiler.backend.bytecode.twail
+import aspl.compiler.backend.stringcode.c
+import aspl.compiler.utils
+import aspl.parser
+import aspl.parser.utils
+import io
+import os
+import appbundle.generator
+
+$if main{
+ import console
+
+ if(os.args().length < 2){
+ aspl.parser.utils.syntax_error("Usage: compiler or ")
+ }
+ var main = io.abs(os.args()[1])
+ compile(main)
+ //print("Took: " + (Timings:total / 1000) + " seconds")
+ var string? mainOutFile = null
+ var tempFile = aspl.compiler.utils.out_temp_name(main)
+ var tempFileParts = tempFile.replace("\\", "/").split("/")
+ tempFile = tempFileParts[tempFileParts.length - 1]
+ if(Options:targetOs == "android"){
+ mainOutFile = tempFile
+ }else{
+ var exeFile = aspl.compiler.utils.out_exe_name(main)
+ var exeParts = exeFile.replace("\\", "/").split("/")
+ exeFile = exeParts[exeParts.length - 1]
+ mainOutFile = exeFile
+ }
+ print(console.green("Successfully compiled: " + mainOutFile))
+}
+
+[public]
+function compile(string main) returns CompilationResult{
+ main = io.abs(main) // if the path remaind relative, below usage (e.g. executable naming) would fail
+ var string mainDirectory = ""
+ if(io.exists_file(main)){
+ mainDirectory = io.full_directory_path(main)
+ }elseif(io.exists_directory(main)){
+ mainDirectory = main
+ }else{
+ aspl.parser.utils.fatal_error("Main is neither a valid file nor a valid directory: " + main)
+ }
+ Module:init(new Module(io.directory_name(mainDirectory), mainDirectory))
+ var Backend backend = new AILBytecodeBackend()
+ if(Options:backend == "twail"){
+ backend = new TreeWalkingAILBytecodeBackend()
+ }elseif(Options:backend == "c"){
+ backend = new CBackend()
+ }
+ var result = backend.compile(aspl.parser.parse())
+ var tempFile = aspl.compiler.utils.out_temp_name(main)
+ var tempFileParts = tempFile.replace("\\", "/").split("/")
+ tempFile = tempFileParts[tempFileParts.length - 1]
+ if(Options:targetOs == "android"){
+ var extension = ".ail"
+ if(Options:backend == "c"){
+ extension = ".c"
+ }
+ aspl.parser.utils.notice("Direct Android packaging is not yet supported; a " + extension + " file will be generated instead...")
+ aspl.parser.utils.notice("You can use ASAPP to package the application for Android")
+ io.write_file_bytes(tempFile, result.output)
+ }elseif(Options:backend == "twail"){
+ aspl.parser.utils.notice("Direct packaging with the tree-walking AIL backend is currently not supported; an .ail file will be generated instead...")
+ io.write_file_bytes(tempFile, result.output)
+ }else{
+ if(Options:keepTemporary || Options:backend == "c"){
+ io.write_file_bytes(tempFile, result.output)
+ }
+ var exeFile = aspl.compiler.utils.out_exe_name(main)
+ var exeParts = exeFile.replace("\\", "/").split("/")
+ exeFile = exeParts[exeParts.length - 1]
+ if(Options:backend == "ail"){
+ var templateType = "minimal"
+ if(Options:noCachedTemplate){
+ templateType = "none"
+ }elseif(IncludeUtils:files.length > 0){
+ templateType = "full"
+ }
+ foreach(IncludeUtils:files as file){
+ if(!ModuleUtils:isFilePartOfStdlib(file)){
+ templateType = "none"
+ break
+ }
+ }
+ if(templateType == "none"){
+ var mainFile = "#include \"runtime/ailinterpreter/main.c\"\n\n" // TODO: Speed this up using a string builder
+ foreach(IncludeUtils:files as include){
+ mainFile += "#include \"" + include + "\"\n"
+ }
+ if(IncludeUtils:files.length > 0){
+ mainFile += "\n"
+ }
+ foreach(ImplementationCallUtils:usedImplementationCalls as call => argc){
+ var callWrapper = ""
+ if(Options:targetOs == "windows"){
+ callWrapper += "__declspec(dllexport) "
+ }
+ callWrapper += "ASPL_OBJECT_TYPE aspl_ailinterpreter_implementation_" + call.replace(".", "$") + "(ASPL_AILI_ArgumentList args){\n"
+ callWrapper += "\treturn ASPL_IMPLEMENT_" + call.replace(".", "$") + "("
+ repeat(argc, i = 0){
+ callWrapper += "C_REFERENCE(args.arguments[" + i + "])"
+ if(i < argc - 1){
+ callWrapper += ", "
+ }
+ }
+ callWrapper += ");\n"
+ callWrapper += "}\n\n"
+ mainFile += callWrapper
+ }
+ io.write_file(exeFile + ".c", mainFile)
+ var ccmd = build_ccmd(exeFile + ".c", exeFile)
+ ccmd += " -DASPL_AILI_BUNDLED"
+ if(ImplementationCallUtils:usedImplementationCalls.length > 0){
+ // TODO: Figure out a less verbose way than these flags
+ if(Options:targetOs == "macos"){
+ ccmd += " -Wl,-export_dynamic"
+ }elseif(Options:targetOs != "windows"){
+ ccmd += " -export-dynamic"
+ }
+ }
+ if(Options:showCCommand){
+ print("cc: " + ccmd)
+ }
+ var ccmdResult = os.execute(ccmd)
+ print(ccmdResult.output, false)
+ if(ccmdResult.exitCode != 0){
+ aspl.parser.utils.fatal_error("C compiler returned with exit code " + ccmdResult.exitCode)
+ }
+ if(!Options:keepTemporary){
+ io.delete_file(exeFile + ".c")
+ }
+ }else{
+ var template = aspl.compiler.utils.choose_executable_template(Options:targetOs, Options:targetArchitecture, templateType, Options:guiApp)
+ if(!io.exists_file(template)) {
+ aspl.parser.utils.fatal_error("Template file not found: " + template)
+ }
+ io.write_file_bytes(exeFile, io.read_file_bytes(template))
+ }
+ if(!Options:internalDoNotBundle){
+ os.chmod(exeFile, 509)
+ var Bundle bundle = new Bundle(exeFile)
+ bundle.addResource("AIL Code", result.output)
+ bundle.generate()
+ }
+ }elseif(Options:backend == "c"){
+ var ccmd = build_ccmd(tempFile, exeFile)
+ if(Options:showCCommand){
+ print("cc: " + ccmd)
+ }
+ var ccmdResult = os.execute(ccmd)
+ print(ccmdResult.output, false)
+ if(ccmdResult.exitCode != 0){
+ aspl.parser.utils.fatal_error("C compiler returned with exit code " + ccmdResult.exitCode)
+ }
+ if(!Options:keepTemporary){
+ io.delete_file(tempFile)
+ }
+ }
+ }
+ return result
+}
+
+function build_ccmd(string sourceFile, string outputFile) returns string{
+ var ccmd = Options:cCompiler + " " + sourceFile + " -o " + outputFile
+ if(Options:cCompiler == "zig cc"){
+ var architecture = string(Options:targetArchitecture)
+ if(architecture == "amd64"){
+ architecture = "x86_64"
+ }
+ ccmd += " -target " + architecture + "-" + Options:targetOs
+ if(Options:targetOs == "linux" || Options:targetOs == "windows"){
+ ccmd += "-gnu"
+ }
+ }else{
+ // TODO: Cross compilation with other compilers
+ }
+ if(Options:targetOs != "windows"){
+ ccmd += " -lm"
+ }
+ if(!Options:getConditionCompilationSymbols().contains("singlethreaded")){
+ if(Options:targetOs == "windows"){
+ ccmd += " -lkernel32 -lwinmm"
+ }else{
+ // TODO: The following code looks shady; check it
+
+ // zig cc links to pthread automatically when cross-compiling, but complains when the library is manually specified
+ if(Options:cCompiler == "zig cc"){
+ ccmd += " -lpthread"
+ }else{
+ if(Options:targetOs == "linux"){ // TODO: Isn't this required for all Unix-like systems?
+ ccmd += " -lpthread"
+ }
+ }
+ }
+ }else{
+ ccmd += " -DASPL_NO_MULTITHREADING"
+ }
+ var isObjectiveC = false
+ foreach(LinkUtils:libraries as library){
+ if(library.startsWith("framework:")){
+ ccmd += " -framework " + library.after("framework:".length - 1)
+ isObjectiveC = true
+ }else{
+ ccmd += " -l" + library
+ }
+ }
+ if(isObjectiveC){
+ ccmd += " -ObjC"
+ }
+ ccmd += " -I " + io.full_directory_path(io.get_executable_path())
+ ccmd += " -I " + io.full_directory_path(io.get_executable_path()) + "/thirdparty/libgc/include"
+ if(Options:production){
+ ccmd += " -O3"
+ }else{
+ ccmd += " -g"
+ ccmd += " -DASPL_DEBUG"
+ }
+ if(Options:heapBased){
+ ccmd += " -DASPL_HEAP_BASED"
+ }
+ if(Options:stackSize != null){
+ if(Options:targetOs == "windows" && (Options:cCompiler == "gcc" || Options:cCompiler == "zig cc")){ // TODO: Figure this out for more C compilers and targets
+ ccmd += " -Wl,--stack," + Options:stackSize
+ }
+ }
+ if(Options:useSsl){
+ ccmd += " -DASPL_USE_SSL"
+ ccmd += " -lssl"
+ ccmd += " -L " + io.full_directory_path(io.get_executable_path()) + "/thirdparty/ssl/" + Options:targetOs + "/bin"
+ }
+ if(Options:targetOs == "windows" && Options:guiApp){
+ ccmd += " -Wl,--subsystem,windows"
+ }
+ ccmd += " -Wno-return-type" // TODO: Remove this once return statements are enforced in all control flow paths
+ if(Options:cCompiler == "gcc"){
+ ccmd += " -Wno-stringop-overflow" // TODO: Figure out if these warnings are actually false-positives or not
+ }
+ return ccmd
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/utils/IncludeUtils.aspl b/stdlib/aspl/compiler/utils/IncludeUtils.aspl
new file mode 100644
index 0000000..94bd44b
--- /dev/null
+++ b/stdlib/aspl/compiler/utils/IncludeUtils.aspl
@@ -0,0 +1,19 @@
+[public]
+[static]
+class IncludeUtils {
+
+ [public]
+ [static]
+ [threadlocal]
+ property list files
+
+ [public]
+ [static]
+ method include(string file) {
+ if(files.contains(file)) {
+ return
+ }
+ files.add(file)
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/utils/LinkUtils.aspl b/stdlib/aspl/compiler/utils/LinkUtils.aspl
new file mode 100644
index 0000000..01c10a7
--- /dev/null
+++ b/stdlib/aspl/compiler/utils/LinkUtils.aspl
@@ -0,0 +1,10 @@
+[public]
+[static]
+class LinkUtils {
+
+ [public]
+ [static]
+ [threadlocal]
+ property list libraries
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/utils/filenames.aspl b/stdlib/aspl/compiler/utils/filenames.aspl
new file mode 100644
index 0000000..8fd012d
--- /dev/null
+++ b/stdlib/aspl/compiler/utils/filenames.aspl
@@ -0,0 +1,64 @@
+import aspl.parser
+import io
+
+[public]
+function out_exe_name(string file) returns string{
+ var name = ""
+ if(Options:outputFile == null){
+ var parts = file.split(".") // TODO: This will break if the file or directory name contains a dot
+ var i = 0
+ foreach(parts as part){
+ if(i != parts.length - 1){
+ name += part + "."
+ }
+ i++
+ }
+ if(name == ""){ // check if file was a directory
+ name = io.directory_name(file) + "." // the . is needed below
+ }
+ }else{
+ name = Options:outputFile + "." // the . is needed below
+ }
+
+ if(Options:targetOs == "windows"){
+ name += "exe" // the . was already added before
+ }else{
+ name = name.before(name.length - 1) // remove the last .
+ }
+ if(io.exists_directory(name)){ // the file system doesn't allow files with the same name as directories
+ var i = 1
+ while(io.exists_directory(name + "(" + i + ")") || io.exists_file(name + "(" + i + ")")){
+ i++
+ }
+ return name + "(" + i + ")"
+ }
+ return name
+}
+
+[public]
+function out_temp_name(string file) returns string{
+ var name = ""
+ if(Options:outputFile == null){
+ var parts = file.split(".") // TODO: This will break if the file or directory name contains a dot
+ var i = 0
+ foreach(parts as part){
+ if(i != parts.length - 1){
+ name += part + "."
+ }
+ i++
+ }
+ if(name == ""){ // check if file was a directory
+ name = io.directory_name(file) + "." // the . is needed below
+ }
+ }else{
+ name = Options:outputFile + "." // the . is needed below
+ }
+ if(Options:backend == "ail" || Options:backend == "twail"){
+ name += "ail" // the . was already added before
+ }elseif(Options:backend == "c"){
+ name += "c" // the . was already added before
+ }else{
+ name += "temp" // the . was already added before
+ }
+ return name
+}
\ No newline at end of file
diff --git a/stdlib/aspl/compiler/utils/templates.aspl b/stdlib/aspl/compiler/utils/templates.aspl
new file mode 100644
index 0000000..1878a64
--- /dev/null
+++ b/stdlib/aspl/compiler/utils/templates.aspl
@@ -0,0 +1,30 @@
+import io
+import os
+import aspl.parser.utils
+
+[public]
+function choose_executable_template(string os, Architecture arch, string internalTemplateType, bool gui) returns string{
+ var baseTemplatePath = io.join_path([io.full_directory_path(io.get_executable_path()), "templates"])
+ var archName = architecture_to_folder_name(arch)
+ if(os == "windows"){
+ if(internalTemplateType == "minimal"){
+ return io.join_path([baseTemplatePath, "windows", archName, internalTemplateType, "Template.exe"])
+ }elseif(gui){
+ return io.join_path([baseTemplatePath, "windows", archName, internalTemplateType, "gui", "Template.exe"])
+ }else{
+ return io.join_path([baseTemplatePath, "windows", archName, internalTemplateType, "cli", "Template.exe"])
+ }
+ }else{
+ return io.join_path([baseTemplatePath, os, archName, internalTemplateType, "Template"])
+ }
+}
+
+function architecture_to_folder_name(Architecture arch) returns string{
+ if(arch == Architecture.amd64){
+ return "x86_64"
+ }elseif(arch == Architecture.i386){
+ return "x86_32"
+ }else{
+ return string(arch)
+ }
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/Module.aspl b/stdlib/aspl/parser/Module.aspl
new file mode 100644
index 0000000..9894e4b
--- /dev/null
+++ b/stdlib/aspl/parser/Module.aspl
@@ -0,0 +1,42 @@
+[public]
+class Module {
+
+ [public]
+ [static]
+ [threadlocal]
+ property map modules = map{}
+ [public]
+ [static]
+ [threadlocal]
+ property Module mainModule
+ [readpublic]
+ [static]
+ [threadlocal]
+ property bool initialized = false
+
+ [public]
+ [static]
+ method init(Module mainModule){
+ self:mainModule = mainModule
+ self:modules = {self:mainModule.name => self:mainModule}
+ self:initialized = true
+ }
+
+ [readpublic]
+ property string name
+ [public]
+ property string id{
+ get{
+ return name.toLower()
+ }
+ }
+ [readpublic]
+ property string directory
+
+ [public]
+ method construct(string name, string directory){
+ this.name = name
+ this.directory = directory
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/Options.aspl b/stdlib/aspl/parser/Options.aspl
new file mode 100644
index 0000000..4219bc3
--- /dev/null
+++ b/stdlib/aspl/parser/Options.aspl
@@ -0,0 +1,166 @@
+import os
+
+[public]
+class Options {
+
+ [readpublic]
+ [static]
+ property list SUPPORTED_OS_LIST = [
+ "windows",
+ "linux",
+ "macos",
+ "android"
+ ]
+
+ [static]
+ [threadlocal]
+ property string? _targetOs = null
+ [public]
+ [static]
+ property string targetOs{
+ get{
+ if(self:_targetOs == null){
+ self:_targetOs = os.user_os()
+ }
+ return string(self:_targetOs)
+ }
+ set{
+ if(!self:SUPPORTED_OS_LIST.contains(value)){
+ aspl.parser.utils.fatal_error("Unsupported OS: " + value)
+ }
+ self:_targetOs = value
+ }
+ }
+ [static]
+ [threadlocal]
+ property Architecture? _targetArchitecture = null
+ [public]
+ [static]
+ property Architecture targetArchitecture{
+ get{
+ if(self:_targetArchitecture == null){
+ self:_targetArchitecture = os.user_architecture_generic()
+ }
+ return Architecture(self:_targetArchitecture)
+ }
+ set{
+ self:_targetArchitecture = value
+ }
+ }
+ [public]
+ [static]
+ [threadlocal]
+ property string? outputFile = null
+ [public]
+ [static]
+ [threadlocal]
+ property bool production = false
+ [public]
+ [static]
+ [threadlocal]
+ property bool keepTemporary = false
+ [public]
+ [static]
+ [threadlocal]
+ property bool guiApp = false // Windows GUI subsystem
+ [public]
+ [static]
+ [threadlocal]
+ property list customConditionalCompilationSymbols = []
+ [public]
+ [static]
+ [threadlocal]
+ property string backend = "ail"
+ [public]
+ [static]
+ [threadlocal]
+ property string? _cCompiler = null
+ [public]
+ [static]
+ property string cCompiler{
+ get{
+ if(self:_cCompiler == null){
+ var result = os.execute("zig cc --version")
+ if (result.exitCode == 0) {
+ self:_cCompiler = "zig cc"
+ } else {
+ result = os.execute("gcc --version")
+ if (result.exitCode == 0) {
+ self:_cCompiler = "gcc"
+ } else {
+ self:_cCompiler = "cc"
+ }
+ }
+ }
+ return self:_cCompiler
+ }
+ set{
+ self:_cCompiler = value
+ }
+ }
+ [public]
+ [static]
+ [threadlocal]
+ property bool useDynamicCTemplate = false
+ [public]
+ [static]
+ [threadlocal]
+ property bool showCCommand = false
+ [public]
+ [static]
+ [threadlocal]
+ property bool heapBased = false
+ [public]
+ [static]
+ [threadlocal]
+ property int? stackSize = null
+ [public]
+ [static]
+ [threadlocal]
+ property bool useSsl = false
+ [public]
+ [static]
+ [threadlocal]
+ property bool enableErrorHandling = false
+ [public]
+ [static]
+ [threadlocal]
+ property bool noCachedTemplate = false
+ [public]
+ [static]
+ [threadlocal]
+ property string internalTemplateType = "minimal"
+ [public]
+ [static]
+ [threadlocal]
+ property bool internalDoNotBundle = false
+
+ [public]
+ [static]
+ method getConditionCompilationSymbols() returns list{
+ var symbols = self:customConditionalCompilationSymbols.cloneShallow()
+ symbols.add(self:targetOs)
+ symbols.add(string(targetArchitecture).toLower())
+ if(targetArchitecture == Architecture.amd64 || targetArchitecture == Architecture.arm64 || targetArchitecture == Architecture.rv64){
+ symbols.add("x64")
+ }elseif(targetArchitecture == Architecture.i386 || targetArchitecture == Architecture.arm32 || targetArchitecture == Architecture.rv32){
+ symbols.add("x32")
+ }
+ if(self:production){
+ symbols.add("production")
+ }else{
+ symbols.add("debug")
+ }
+ if(self:guiApp){
+ symbols.add("gui")
+ }else{
+ symbols.add("console")
+ }
+ if(self:useSsl){
+ symbols.add("ssl")
+ }
+ symbols.add(self:backend + "backend")
+ return symbols
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ParseFileResult.aspl b/stdlib/aspl/parser/ParseFileResult.aspl
new file mode 100644
index 0000000..840b0a8
--- /dev/null
+++ b/stdlib/aspl/parser/ParseFileResult.aspl
@@ -0,0 +1,14 @@
+import aspl.parser.ast
+
+[public]
+class ParseFileResult {
+
+ [readpublic]
+ property list nodes
+
+ [public]
+ method construct(list nodes){
+ this.nodes = nodes
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/Parser.aspl b/stdlib/aspl/parser/Parser.aspl
new file mode 100644
index 0000000..abc2b71
--- /dev/null
+++ b/stdlib/aspl/parser/Parser.aspl
@@ -0,0 +1,3333 @@
+import aspl.parser.lexer
+import aspl.parser.ast
+import aspl.parser.ast.expressions
+import aspl.parser.ast.statements
+import aspl.parser.ast.literals
+import aspl.parser.utils
+import aspl.parser.functions
+import aspl.parser.precedence
+import aspl.parser.variables
+import aspl.parser.methods
+import aspl.parser.properties
+import aspl.parser.classes
+import aspl.parser.enums
+import aspl.parser.attributes
+import aspl.parser.callbacks
+import math
+import io
+
+[public]
+class Parser{
+
+ [public]
+ property Module module
+ [public]
+ property string file
+ [public]
+ property string currentNamespace{
+ get{
+ var modulePath = module.directory
+ if(modulePath.endsWith("/") || modulePath.endsWith("\\")){
+ modulePath = modulePath.before(modulePath.length - 1)
+ }
+ var relativePath = io.full_directory_path(io.abs(file)).after(modulePath.length - 1).replace("/", ".").replace("\\", ".")
+ if(relativePath.startsWith(".")){
+ relativePath = relativePath.after(0)
+ }
+ if(relativePath.endsWith(".")){
+ relativePath = relativePath.before(relativePath.length - 1)
+ }
+ var namespace = io.directory_name(module.directory) + "." + relativePath
+ while(namespace.endsWith(".")){
+ namespace = namespace.before(namespace.length - 1)
+ }
+ return namespace.toLower()
+ }
+ }
+ [public]
+ property Class? currentClass = null
+ property bool inStaticContext = false
+ [public]
+ property Enum? currentEnum = null
+ [public]
+ property Method? currentMethod = null
+ [public]
+ property list attributeCache = list[]
+ [static]
+ [threadlocal]
+ property map importTables = map{}
+ [public]
+ property ImportTable importTable{
+ get{
+ if(!self:importTables.containsKey(file)){
+ self:importTables[file] = new ImportTable()
+ }
+ return self:importTables[file]
+ }
+ set{
+ self:importTables[file] = value
+ }
+ }
+ property int loopDepth = 0
+
+ [static]
+ [threadlocal]
+ property bool initialized = false
+ [readpublic]
+ [static]
+ [threadlocal]
+ property list importProcessedFiles = []
+
+ [readpublic]
+ property ParseMode? currentParseMode = null
+
+ [public]
+ method construct(Module module, string file){
+ if(!Module:initialized){
+ aspl.parser.utils.fatal_error("Cannot construct a parser without initializing the modules first (parser.Module:init())")
+ }
+ this.module = module
+ this.file = file
+ if(!self:initialized){
+ self:initialized = true
+ Attribute:init()
+ GenericsUtils:init()
+ Function:init()
+ Method:init()
+ Property:init()
+ }
+ }
+
+ [public]
+ method parse(ParseMode parseMode = ParseMode.Normal) returns ParseFileResult{
+ currentParseMode = parseMode
+
+ var TokenList tokens = new TokenList(list[])
+ if(Lexer:cache.containsKey(this.file)){
+ tokens = Lexer:cache[this.file].clone()
+ }else{
+ var Lexer lexer = new Lexer(this.file)
+ tokens = new TokenList(lexer.lex())
+ Lexer:cache[this.file] = tokens
+ }
+ Scope:pushBundle(null)
+ Scope:push()
+ var list nodes = []
+ if(parseMode == ParseMode.Import){
+ while(true){
+ if(tokens.length == 0){
+ break
+ }
+ var token = tokens.next()
+ if(token.type == TokenType.Identifier && token.value == "import"){
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected identifier after import", tokens.peek().location)
+ }
+ var string namespace = parseTypeIdentifier(tokens).identifier
+
+ var string module = namespace.split(".", 2)[0]
+ var path = ModuleUtils:getModulePath(module)
+ if(io.exists_directory(path)){
+ module = io.directory_name(path)
+ }
+ var moduleFound = true
+ if(!Module:modules.containsKey(module)){
+ if(io.exists_directory(path)){
+ var m = new Module(module, path)
+ Module:modules[module] = m
+ foreach(DirectoryUtils:index(m.directory) as file){
+ if(!self:importProcessedFiles.contains(file)){
+ self:importProcessedFiles.add(file)
+ var Parser parser = new Parser(m, file)
+ parser.parse(ParseMode.Import)
+ }
+ }
+ }else{
+ aspl.parser.utils.generic_error("Module '" + module + "' not found", tokens.peek().location)
+ moduleFound = false
+ }
+ }
+
+ if(moduleFound){
+ var namespaceWithoutModule = ""
+ if(namespace.split(".", 2).length > 1){
+ namespaceWithoutModule = namespace.split(".", 2)[1]
+ }
+ if(!io.exists_directory(Module:modules[module].directory + "/" + namespaceWithoutModule.replace(".", "/"))){
+ aspl.parser.utils.generic_error("Namespace '" + namespace + "' not found", tokens.peek().location)
+ }else{
+ importTable.importNamespace(namespace)
+ }
+ }
+ }
+ }
+ }elseif(parseMode == ParseMode.PreprocessTypes){
+ preProcessTypes(tokens)
+ }elseif(parseMode == ParseMode.Preprocess){
+ preProcess(tokens)
+ }elseif(parseMode == ParseMode.Normal){
+ while(true){
+ if(tokens.length == 0){
+ break
+ }
+ var node = parseToken(tokens.next(), tokens, true)
+ if(!(node oftype NopStatement)){
+ nodes.add(node)
+ }
+ }
+ }
+ Scope:pop()
+ Scope:popBundle()
+ currentParseMode = null
+ return new ParseFileResult(nodes)
+ }
+
+ method preProcessTypes(TokenList tokens){
+ while(true){
+ if(tokens.length == 0){
+ break
+ }
+ preProcessTypesToken(tokens.next(), tokens)
+ }
+ }
+
+ method preProcessTypesToken(Token token, TokenList tokens){
+ if(token.type == TokenType.Identifier){
+ if(token.value == "class"){
+ if(currentClass != null){
+ aspl.parser.utils.syntax_error("Cannot declare a class inside another class", token.location)
+ }
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected class name after 'class'", tokens.peek().location)
+ }
+ var name = tokens.next().value
+ var type = Type:fromString(currentNamespace + "." + name, this, token.location)
+ var c = new Class(type, null, null, null, module, token.location)
+ Class:classes[type.toString()] = c
+ }elseif(token.value == "enum"){
+ if(currentEnum != null){
+ aspl.parser.utils.syntax_error("Cannot declare an enum inside another enum", token.location)
+ }
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected enum name after 'enum'", tokens.peek().location)
+ }
+ var name = tokens.next().value
+ if(tokens.peek().type != TokenType.BraceOpen){
+ aspl.parser.utils.syntax_error("Expected '{' after enum name", tokens.peek().location)
+ }
+ tokens.shift()
+ var type = Type:fromString(currentNamespace + "." + name, this, token.location)
+ var e = new Enum(type, null, null, module, token.location)
+ Enum:enums[type.toString()] = e
+ }
+ }
+ }
+
+ method preProcess(TokenList tokens){
+ while(true){
+ if(tokens.length == 0){
+ break
+ }
+ preProcessToken(tokens.next(), tokens)
+ }
+ }
+
+ method preProcessToken(Token token, TokenList tokens){
+ if(token.type == TokenType.Identifier){
+ if(token.value == "function"){
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected identifier after 'function'", tokens.peek().location)
+ }
+ var identifier = tokens.next()
+ var list parameters = []
+ tokens.shift()
+ while(true){
+ if(tokens.peek().type == TokenType.ParenthesisClose){
+ tokens.shift()
+ break
+ }
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected identifier after '('", tokens.peek().location)
+ }
+ var types = parseTypesIfAny(tokens)
+ if(types.types.length == 0){
+ if(tokens.peek(1).type == TokenType.Identifier){
+ aspl.parser.utils.fatal_error("Invalid parameter type '" + peekTypeIdentifier(tokens).identifier + "'", tokens.peek().location) // TODO: Use type_error when parseTypesIfAny for unknown types exists
+ }else{
+ aspl.parser.utils.syntax_error("Parameters must specify a type", tokens.peek().location)
+ }
+ }
+ var parameter = tokens.next()
+ var Expression? defaultValue = null
+ if(tokens.peek().type == TokenType.Equals){
+ tokens.shift()
+ defaultValue = new NullLiteral(tokens.peek().location)
+ TokenUtils:skipTokensTillSeparator(tokens)
+ }
+ parameters.add(new Parameter(parameter.value, types, defaultValue, token.location))
+ if(tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }else{
+ if(tokens.peek().type == TokenType.ParenthesisClose){
+ tokens.shift()
+ break
+ }
+ aspl.parser.utils.syntax_error("Expected ',' or ')'", tokens.peek().location)
+ }
+ }
+ var returnTypes = new Types([])
+ if(tokens.peek().value == "returns"){
+ tokens.shift()
+ returnTypes = parseTypesIfAny(tokens)
+ if(returnTypes.types.length == 0){
+ if(tokens.peek(1).type == TokenType.BraceOpen){
+ aspl.parser.utils.syntax_error("Invalid return type '" + peekTypeIdentifier(tokens).identifier + "'", tokens.peek().location)
+ }else{
+ aspl.parser.utils.syntax_error("Expected type identifier after 'returns'", tokens.peek().location)
+ }
+ }
+ }
+ var braceOpen = tokens.next()
+ if(braceOpen.type != TokenType.BraceOpen){
+ aspl.parser.utils.syntax_error("Expected '{' after function parameters", braceOpen.location)
+ }
+ foreach(attributeCache as attribute){
+ if((attribute.attribute.usage && AttributeUsage.Function) == 0){
+ aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used on functions", attribute.location)
+ }
+ foreach(attributeCache as other){
+ if(other.attribute.identifier != attribute.attribute.identifier){
+ if(!attribute.attribute.canPair(other.attribute)){
+ aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used together with the attribute '" + other.attribute.identifier + "'", attribute.location)
+ }
+ }
+ }
+ }
+ var f = new CustomFunction(IdentifierUtils:relativeToAbsoluteIdentifier(this, identifier.value), parameters, returnTypes, attributeCache.cloneShallow(), null, module, token.location, braceOpen.location)
+ attributeCache.clear()
+ while(true){
+ if(tokens.peek().type == TokenType.BraceClose){
+ tokens.shift()
+ break
+ }
+ preProcessToken(tokens.next(), tokens)
+ }
+ f.register(token.location)
+ }elseif(token.value == "method"){
+ if(currentClass == null){
+ aspl.parser.utils.syntax_error("Cannot declare a method outside of a class", token.location)
+ }
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected identifier after 'method'", tokens.peek().location)
+ }
+ var name = tokens.next()
+ if(Class(currentClass).isStatic && name.value == "construct"){
+ aspl.parser.utils.type_error("Static class " + Class(currentClass).type.toString() + " cannot have a constructor", token.location)
+ }
+ var list parameters = []
+ tokens.shift()
+ while(true){
+ if(tokens.peek().type == TokenType.ParenthesisClose){
+ tokens.shift()
+ break
+ }
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected identifier after '('", tokens.peek().location)
+ }
+ var types = parseTypesIfAny(tokens)
+ if(types.types.length == 0){
+ if(tokens.peek(1).type == TokenType.Identifier){
+ aspl.parser.utils.fatal_error("Invalid parameter type '" + peekTypeIdentifier(tokens).identifier + "'", tokens.peek().location) // TODO: Use type_error when parseTypesIfAny for unknown types exists
+ }else{
+ aspl.parser.utils.syntax_error("Parameters must specify a type", tokens.peek().location)
+ }
+ }
+ var parameter = tokens.next()
+ var Expression? defaultValue = null
+ if(tokens.peek().type == TokenType.Equals){
+ tokens.shift()
+ defaultValue = new NullLiteral(tokens.peek().location)
+ TokenUtils:skipTokensTillSeparator(tokens)
+ }
+ parameters.add(new Parameter(parameter.value, types, defaultValue, token.location))
+ if(tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }else{
+ if(tokens.peek().type == TokenType.ParenthesisClose){
+ tokens.shift()
+ break
+ }
+ aspl.parser.utils.syntax_error("Expected ',' or ')'", tokens.peek().location)
+ }
+ }
+ var returnTypes = new Types([])
+ if(tokens.peek().value == "returns"){
+ tokens.shift()
+ returnTypes = parseTypesIfAny(tokens)
+ if(returnTypes.types.length == 0){
+ if(tokens.peek(1).type == TokenType.BraceOpen){
+ aspl.parser.utils.fatal_error("Invalid return type '" + peekTypeIdentifier(tokens).identifier + "'", tokens.peek().location) // TODO: Use type_error when parseTypesIfAny for unknown types exists
+ }else{
+ aspl.parser.utils.syntax_error("Expected type identifier after 'returns'", tokens.peek().location)
+ }
+ }
+ }
+ var braceOpen = tokens.peek()
+ foreach(attributeCache as attribute){
+ if((attribute.attribute.usage && AttributeUsage.Method) == 0){
+ aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used on methods", attribute.location)
+ }elseif(attribute.attribute.identifier == "abstract" && !Class(currentClass).isAbstract){
+ aspl.parser.utils.type_error("Only methods in abstract classes can marked with 'abstract' attribute", attribute.location)
+ }
+ foreach(attributeCache as other){
+ if(other.attribute.identifier != attribute.attribute.identifier){
+ if(!attribute.attribute.canPair(other.attribute)){
+ aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used together with the attribute '" + other.attribute.identifier + "'", attribute.location)
+ }
+ }
+ }
+ }
+ var m = new CustomMethod(Class(currentClass).type, name.value, parameters, returnTypes, attributeCache.cloneShallow(), null, token.location, braceOpen.location)
+ if(Class(currentClass).isStatic && !m.isStatic){
+ aspl.parser.utils.type_error("Cannot declare a non-static method in a static class", token.location)
+ }
+ attributeCache.clear()
+ if(tokens.peek().type == TokenType.BraceOpen){
+ if(m.isAbstract){
+ aspl.parser.utils.syntax_error("Abstract methods cannot have a body", tokens.peek().location)
+ }
+ preProcessBlock(tokens)
+ }elseif(!m.isAbstract){
+ aspl.parser.utils.syntax_error("Non-abstract methods must have a body", tokens.peek().location)
+ }
+ m.register(token.location)
+ }elseif(token.value == "property"){
+ if(currentClass == null){
+ aspl.parser.utils.syntax_error("Cannot declare a property outside of a class", token.location)
+ }
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected property name after 'property'", tokens.peek().location)
+ }
+ var types = parseTypesIfAny(tokens)
+ var name = tokens.next()
+ if(types.types.length == 0){
+ aspl.parser.utils.warning("Expected type after property name in 'property' statement; will use 'any' for now", name.location)
+ types = new Types([Type:fromString("any")])
+ }
+ foreach(attributeCache as attribute){
+ if((attribute.attribute.usage && AttributeUsage.Property) == 0){
+ aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used on properties", attribute.location)
+ }elseif(attribute.attribute.identifier == "threadlocal" && tokens.peek().type == TokenType.BraceOpen){
+ aspl.parser.utils.type_error("The attribute 'threadlocal' cannot be used on reactive properties", attribute.location)
+ }
+ foreach(attributeCache as other){
+ if(other.attribute.identifier != attribute.attribute.identifier){
+ if(!attribute.attribute.canPair(other.attribute)){
+ aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used together with the attribute '" + other.attribute.identifier + "'", attribute.location)
+ }
+ }
+ }
+ }
+ var Property? p = null
+ var potentialBraceOpen = tokens.peek()
+ if(potentialBraceOpen.type == TokenType.BraceOpen){
+ tokens.shift()
+ if(tokens.peek().type == TokenType.Identifier){
+ if(tokens.peek().value == "get"){
+ tokens.shift()
+ preProcessBlock(tokens)
+ }
+ if(tokens.peek().value == "set"){
+ tokens.shift()
+ preProcessBlock(tokens)
+ }
+ }
+ tokens.shift()
+ p = new CustomReactiveProperty(Class(currentClass).type, name.value, types, attributeCache.cloneShallow(), null, null, token.location, potentialBraceOpen.location)
+ }else{
+ p = new CustomNormalProperty(Class(currentClass).type, name.value, types, attributeCache.cloneShallow(), null, token.location, new Location(name.location.file, name.location.endLine, name.location.endLine, name.location.endColumn, name.location.endColumn + 1))
+ }
+ if(Class(currentClass).isStatic && !Property(p).isStatic){
+ aspl.parser.utils.type_error("Cannot declare a non-static property in a static class", token.location)
+ }
+ attributeCache.clear()
+ Property(p).register(token.location)
+ }
+ elseif(token.value == "class"){
+ if(currentClass != null){
+ aspl.parser.utils.syntax_error("Cannot declare a class inside another class", token.location)
+ }
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected class name after 'class'", tokens.peek().location)
+ }
+ var name = tokens.next().value
+ var list parents = []
+ if(tokens.peek().value == "extends"){
+ tokens.shift()
+ while(tokens.peek().type != TokenType.BraceOpen){
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected class name after 'extends'", tokens.peek().location)
+ }
+ var t = tokens.peek()
+ var parent = parseTypeIdentifier(tokens).identifier
+ parents.add(Type:fromString(parent, this, t.location))
+ if(tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }
+ }
+ }
+ if(tokens.peek().type != TokenType.BraceOpen){
+ aspl.parser.utils.syntax_error("Expected '{' after class name", tokens.peek().location)
+ }
+ foreach(attributeCache as attribute){
+ if((attribute.attribute.usage && AttributeUsage.Class) == 0){
+ aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used on classes", attribute.location)
+ }
+ foreach(attributeCache as other){
+ if(other.attribute.identifier != attribute.attribute.identifier){
+ if(!attribute.attribute.canPair(other.attribute)){
+ aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used together with the attribute '" + other.attribute.identifier + "'", attribute.location)
+ }
+ }
+ }
+ }
+ var type = Type:fromString(currentNamespace + "." + name, this, token.location)
+ var c = new Class(type, parents, attributeCache.cloneShallow(), null, module, token.location)
+ attributeCache.clear()
+ Class:classes[type.toString()] = c
+ ClassUtils:classesWithParsers[type.toString()] = this
+ currentClass = c
+ preProcessBlock(tokens)
+ currentClass = null
+ }elseif(token.value == "enum"){
+ if(currentEnum != null){
+ aspl.parser.utils.syntax_error("Cannot declare an enum inside another enum", token.location)
+ }
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected enum name after 'enum'", tokens.peek().location)
+ }
+ var name = tokens.next().value
+ if(tokens.peek().type != TokenType.BraceOpen){
+ aspl.parser.utils.syntax_error("Expected '{' after enum name", tokens.peek().location)
+ }
+ tokens.shift()
+ foreach(attributeCache as attribute){
+ if((attribute.attribute.usage && AttributeUsage.Enum) == 0){
+ aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used on enums", attribute.location)
+ }
+ foreach(attributeCache as other){
+ if(other.attribute.identifier != attribute.attribute.identifier){
+ if(!attribute.attribute.canPair(other.attribute)){
+ aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used together with the attribute '" + other.attribute.identifier + "'", attribute.location)
+ }
+ }
+ }
+ }
+ var type = Type:fromString(currentNamespace + "." + name, this, token.location)
+ var e = new Enum(type, attributeCache.cloneShallow(), null, module, token.location)
+ attributeCache.clear()
+ Enum:enums[type.toString()] = e
+ currentEnum = e
+ var map fields = {}
+ while(true){
+ var field = tokens.next()
+ if(field.type == TokenType.BraceClose){
+ e.location = new Location(token.location.file, token.location.startLine, field.location.endLine, token.location.startColumn, field.location.endColumn)
+ break
+ }
+ if(AttributeUtils:parseAttributesIfAny(this, field, tokens)){
+ field = tokens.next()
+ }
+ if(field.type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected enum field name", field.location)
+ }
+ foreach(attributeCache as attribute){
+ if((attribute.attribute.usage && AttributeUsage.EnumField) == 0){
+ aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used on enum fields", attribute.location)
+ }
+ foreach(attributeCache as other){
+ if(other.attribute.identifier != attribute.attribute.identifier){
+ if(!attribute.attribute.canPair(other.attribute)){
+ aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used together with the attribute '" + other.attribute.identifier + "'", attribute.location)
+ }
+ }
+ }
+ }
+ if(tokens.peek().type == TokenType.Equals){
+ tokens.shift()
+ TokenUtils:skipTokensTillSeparator(tokens)
+ }
+ fields[field.value] = new EnumField(e, field.value, null, attributeCache, token.location)
+ attributeCache.clear()
+ if(tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }elseif(tokens.peek().type != TokenType.BraceClose){
+ aspl.parser.utils.syntax_error("Expected ',' or '}' after enum field", tokens.peek().location)
+ }
+ }
+ e.fields = fields
+ currentEnum = null
+ }
+ }elseif(token.type == TokenType.BracketOpen){
+ if(tokens.peek().type == TokenType.Identifier){
+ var attributePeek = peekTypeIdentifier(tokens)
+ var isAttribute = true
+ if((tokens.peek(attributePeek.tokenCount).type == TokenType.BracketClose) || (tokens.peek(attributePeek.tokenCount).type == TokenType.ParenthesisOpen)){
+ if(!Attribute:exists(attributePeek.identifier)){
+ isAttribute = false
+ }
+ }else{
+ isAttribute = false
+ }
+ if(isAttribute){
+ var attribute = Attribute:get(IdentifierUtils:handleTypeIdentifier(parseTypeIdentifier(tokens).identifier))
+ var list arguments = []
+ if(tokens.peek().type == TokenType.ParenthesisOpen){
+ tokens.shift()
+ foreach(attribute.parameters as parameter){
+ if(tokens.peek().type == TokenType.ParenthesisClose){
+ if(!parameter.optional){
+ aspl.parser.utils.generic_error("Too few arguments (" + arguments.length + ") given for attribute '" + attribute.identifier + "' (expected " + attribute.minimumParameterCount + ")", tokens.peek().location)
+ }
+ break
+ }
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens))
+ if(!Type:matches(parameter.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot pass a value of the type '" + value.getType().toString() + "' to a parameter of the type '" + parameter.types.toString() + "'", value.location)
+ }
+ arguments.add(value)
+ if(arguments.length < attribute.parameters.length && tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }
+ }
+ if(tokens.peek().type == TokenType.ParenthesisClose){
+ tokens.shift()
+ }else{
+ aspl.parser.utils.syntax_error("Expected ')' after attribute arguments", tokens.peek().location)
+ }
+ }elseif(attribute.parameters.length > 0 && !attribute.parameters[0].optional){
+ aspl.parser.utils.generic_error("Too few arguments (" + arguments.length + ") given for attribute '" + attribute.identifier + "' (expected " + attribute.minimumParameterCount + ")", tokens.peek().location)
+ }
+ if(tokens.peek().type == TokenType.BracketClose){
+ tokens.shift()
+ }else{
+ aspl.parser.utils.syntax_error("Expected ']' after attribute name", tokens.peek().location)
+ }
+ this.attributeCache.add(new AttributeInstance(attribute, arguments, token.location, list(token.comments)))
+ }
+ }
+ }
+ }
+
+ [public]
+ method parseToken(Token token, TokenList tokens, bool standalone = false, PrecedenceLevel precedenceLevel = PrecedenceLevel.None, Expression? previousExpression = null, Types? expectedTypes = null) returns Node{
+ if(token.type == TokenType.Byte){
+ if(token.value.endsWith("b")){
+ return applyOperators(new ByteLiteral(byte(token.value.before(token.value.length - 1)), token.location), tokens, precedenceLevel)
+ }else{
+ if(expectedTypes != null){
+ if(Type:matches(Types(expectedTypes), new Types([Type:fromString("byte")]))){
+ return applyOperators(new ByteLiteral(byte(token.value), token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("integer")]))){
+ return applyOperators(new IntegerLiteral(int(token.value), token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("long")]))){
+ return applyOperators(new LongLiteral(long(token.value), token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("float")]))){
+ return applyOperators(new FloatLiteral(float(token.value), token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("double")]))){
+ return applyOperators(new DoubleLiteral(double(token.value), token.location), tokens, precedenceLevel)
+ }
+ }
+ return applyOperators(new ByteLiteral(byte(token.value), token.location), tokens, precedenceLevel)
+ }
+ }elseif(token.type == TokenType.Integer){
+ if(expectedTypes != null){
+ if(Type:matches(Types(expectedTypes), new Types([Type:fromString("integer")]))){
+ return applyOperators(new IntegerLiteral(int(token.value), token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("byte")]))){
+ return applyOperators(new ByteLiteral(byte(token.value), token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("long")]))){
+ return applyOperators(new LongLiteral(long(token.value), token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("float")]))){
+ return applyOperators(new FloatLiteral(float(token.value), token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("double")]))){
+ return applyOperators(new DoubleLiteral(double(token.value), token.location), tokens, precedenceLevel)
+ }
+ }
+ return applyOperators(new IntegerLiteral(int(token.value), token.location), tokens, precedenceLevel)
+ }elseif(token.type == TokenType.Long){
+ if(token.value.endsWith("l")){
+ return applyOperators(new LongLiteral(long(token.value.before(token.value.length - 1)), token.location), tokens, precedenceLevel)
+ }else{
+ if(expectedTypes != null){
+ if(Type:matches(Types(expectedTypes), new Types([Type:fromString("long")]))){
+ return applyOperators(new LongLiteral(long(token.value), token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("byte")]))){
+ return applyOperators(new ByteLiteral(byte(token.value), token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("integer")]))){
+ return applyOperators(new IntegerLiteral(int(token.value), token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("float")]))){
+ return applyOperators(new FloatLiteral(float(token.value), token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("double")]))){
+ return applyOperators(new DoubleLiteral(double(token.value), token.location), tokens, precedenceLevel)
+ }
+ }
+ return applyOperators(new LongLiteral(long(token.value), token.location), tokens, precedenceLevel)
+ }
+ }elseif(token.type == TokenType.Float){
+ if(token.value.endsWith("f")){
+ return applyOperators(new FloatLiteral(float(token.value.before(token.value.length - 1)), token.location), tokens, precedenceLevel)
+ }else{
+ if(expectedTypes != null){
+ if(Type:matches(Types(expectedTypes), new Types([Type:fromString("float")]))){
+ return applyOperators(new FloatLiteral(float(token.value), token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("byte")]))){
+ return applyOperators(new ByteLiteral(byte(token.value), token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("integer")]))){
+ return applyOperators(new IntegerLiteral(int(token.value), token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("long")]))){
+ return applyOperators(new LongLiteral(long(token.value), token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("double")]))){
+ return applyOperators(new DoubleLiteral(double(token.value), token.location), tokens, precedenceLevel)
+ }
+ }
+ return applyOperators(new FloatLiteral(float(token.value), token.location), tokens, precedenceLevel)
+ }
+ }elseif(token.type == TokenType.Double){
+ if(token.value.endsWith("d")){
+ return applyOperators(new DoubleLiteral(double(token.value.before(token.value.length - 1)), token.location), tokens, precedenceLevel)
+ }else{
+ if(expectedTypes != null){
+ if(Type:matches(Types(expectedTypes), new Types([Type:fromString("double")]))){
+ return applyOperators(new DoubleLiteral(double(token.value), token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("byte")]))){
+ return applyOperators(new ByteLiteral(byte(token.value), token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("integer")]))){
+ return applyOperators(new IntegerLiteral(int(token.value), token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("long")]))){
+ return applyOperators(new LongLiteral(long(token.value), token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(Types(expectedTypes), new Types([Type:fromString("float")]))){
+ return applyOperators(new FloatLiteral(float(token.value), token.location), tokens, precedenceLevel)
+ }
+ }
+ return applyOperators(new DoubleLiteral(double(token.value), token.location), tokens, precedenceLevel)
+ }
+ }elseif(token.type == TokenType.String){
+ return applyOperators(new StringLiteral(token.value, StringToken(token).literalString, token.location), tokens, precedenceLevel)
+ }elseif(token.type == TokenType.Identifier){
+ if(token.value == "import"){
+ parseTypeIdentifier(tokens) // ignore the import statement as it has already been processed
+ return new NopStatement()
+ }
+ elseif(token.value == "null"){
+ return applyOperators(new NullLiteral(token.location), tokens, precedenceLevel)
+ }elseif(token.value == "false"){
+ return applyOperators(new BooleanLiteral(false, token.location), tokens, precedenceLevel)
+ }elseif(token.value == "true"){
+ return applyOperators(new BooleanLiteral(true, token.location), tokens, precedenceLevel)
+ }elseif(token.value == "assert"){
+ var expression = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("bool")])))
+ if(!Type:matches(new Types([Type:fromString("bool")]), expression.getType())){
+ aspl.parser.utils.type_error("Condition in assert statement must be of type boolean, but expression of type '" + expression.getType().toString() + "' given", token.location)
+ }
+ return new AssertStatement(expression, token.location)
+ }elseif(token.value == "var"){
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected identifier after 'var'", tokens.peek().location)
+ }
+ var types = parseTypesIfAny(tokens)
+ var identifier = tokens.next()
+ if(tokens.peek().type != TokenType.Equals){
+ if(tokens.peek().type == TokenType.Identifier){
+ aspl.parser.utils.fatal_error("Unknown type '" + identifier.value + "'", identifier.location) // TODO: Use type_error when parseTypesIfAny for unknown types exists
+ }
+ aspl.parser.utils.syntax_error("Expected '=' after variable declaration", tokens.peek().location)
+ }
+ tokens.shift()
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, types))
+ if(types.types.length == 0){
+ types = value.getType()
+ }else{
+ if(!Type:matches(types, value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a variable of type '" + types.toString() + "'", value.location)
+ }
+ }
+ var v = Variable:register(identifier.value, types, token.location)
+ return applyOperators(new VariableDeclareExpression(v, value, token.location), tokens, precedenceLevel)
+ }elseif(token.value == "function"){
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected identifier after 'function'", tokens.peek().location)
+ }
+ var identifier = tokens.next()
+ Scope:pushBundle(null)
+ Scope:push()
+ var list parameters = []
+ tokens.shift()
+ while(true){
+ if(tokens.peek().type == TokenType.ParenthesisClose){
+ tokens.shift()
+ break
+ }
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected identifier after '('", tokens.peek().location)
+ }
+ var types = parseTypesIfAny(tokens)
+ if(types.types.length == 0){
+ if(tokens.peek(1).type == TokenType.Identifier){
+ aspl.parser.utils.fatal_error("Invalid parameter type '" + peekTypeIdentifier(tokens).identifier + "'", tokens.peek().location) // TODO: Use type_error when parseTypesIfAny for unknown types exists
+ }else{
+ aspl.parser.utils.syntax_error("Parameters must specify a type", tokens.peek().location)
+ }
+ }
+ var parameter = tokens.next()
+ var Expression? defaultValue = null
+ if(tokens.peek().type == TokenType.Equals){
+ tokens.shift()
+ defaultValue = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, types))
+ if(!Expression(defaultValue).isConstant()){
+ aspl.parser.utils.generic_error("Default parameter values must be constant", tokens.peek().location)
+ }
+ if(!Type:matches(types, Expression(defaultValue).getType())){
+ aspl.parser.utils.type_error("Default parameter value must be of the same type as the parameter", tokens.peek().location)
+ }
+ }
+ parameters.add(new Parameter(parameter.value, types, defaultValue, token.location))
+ Variable:register(parameter.value, types, token.location)
+ if(tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }else{
+ if(tokens.peek().type == TokenType.ParenthesisClose){
+ tokens.shift()
+ break
+ }
+ aspl.parser.utils.syntax_error("Expected ',' or ')'", tokens.peek().location)
+ }
+ }
+ var f = CustomFunction(Function:functions[IdentifierUtils:relativeToAbsoluteIdentifier(this, identifier.value)])
+ var returnTypes = new Types([])
+ if(tokens.peek().value == "returns"){
+ tokens.shift()
+ returnTypes = parseTypesIfAny(tokens)
+ if(returnTypes.types.length == 0){
+ if(tokens.peek(1).type == TokenType.BraceOpen){
+ aspl.parser.utils.generic_error("Invalid return type '" + peekTypeIdentifier(tokens).identifier + "'", tokens.peek().location) // TODO: Use type_error when parseTypesIfAny for unknown types exists
+ }else{
+ aspl.parser.utils.syntax_error("Expected type identifier after 'returns'", tokens.peek().location)
+ }
+ }elseif(f.isPublic){
+ foreach(returnTypes.types as type){
+ if(!Type:isPublic(type)){
+ aspl.parser.utils.warning("The type '" + type.toString() + "' is not public and thus cannot be used as a return type of a public function", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ }
+ }
+ }
+ f.parameters = parameters
+ f.returnTypes = returnTypes
+ Scope:getCurrentBundle().func = f
+ var statements = parseBlock(tokens, null, false)
+ f.code = statements
+ Scope:pop()
+ Scope:popBundle()
+ f.register(token.location)
+ var comments = list[]
+ foreach(f.attributes as attribute){
+ foreach(attribute.comments as comment){
+ comments.add(comment)
+ }
+ }
+ foreach(list(token.comments) as comment){
+ comments.add(comment)
+ }
+ return new FunctionDeclareStatement(f, comments, token.location)
+ }elseif(token.value == "method"){
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected identifier after 'method'", tokens.peek().location)
+ }
+ var name = tokens.next()
+ var m = CustomMethod(Method:methods[Class(currentClass).type.toString()][name.value])
+ Scope:pushBundle(null)
+ Scope:push()
+ var list parameters = []
+ tokens.shift()
+ while(true){
+ if(tokens.peek().type == TokenType.ParenthesisClose){
+ tokens.shift()
+ break
+ }
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected identifier after '('", tokens.peek().location)
+ }
+ var types = parseTypesIfAny(tokens)
+ if(types.types.length == 0){
+ if(tokens.peek(1).type == TokenType.Identifier){
+ aspl.parser.utils.generic_error("Invalid parameter type '" + peekTypeIdentifier(tokens).identifier + "'", tokens.peek().location) // TODO: Use type_error when parseTypesIfAny for unknown types exists
+ }else{
+ aspl.parser.utils.syntax_error("Parameters must specify a type", tokens.peek().location)
+ }
+ }elseif(currentClass?!.isPublic && m.isPublic){
+ foreach(types.types as type){
+ if(!Type:isPublic(type)){
+ aspl.parser.utils.warning("The type '" + type.toString() + "' is not public and thus cannot be used as a parameter type of a public method in a public class", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ }
+ }
+ var parameter = tokens.next()
+ var Expression? defaultValue = null
+ if(tokens.peek().type == TokenType.Equals){
+ tokens.shift()
+ defaultValue = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, types))
+ if(!Expression(defaultValue).isConstant()){
+ aspl.parser.utils.generic_error("Default parameter values must be constant", tokens.peek().location)
+ }
+ if(!Type:matches(types, Expression(defaultValue).getType())){
+ aspl.parser.utils.type_error("Default parameter value must be of the same type as the parameter", tokens.peek().location)
+ }
+ }
+ parameters.add(new Parameter(parameter.value, types, defaultValue, token.location))
+ Variable:register(parameter.value, types, token.location)
+ if(tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }else{
+ if(tokens.peek().type == TokenType.ParenthesisClose){
+ tokens.shift()
+ break
+ }
+ aspl.parser.utils.syntax_error("Expected ',' or ')'", tokens.peek().location)
+ }
+ }
+ var returnTypes = new Types([])
+ if(tokens.peek().value == "returns"){
+ tokens.shift()
+ returnTypes = parseTypesIfAny(tokens)
+ if(returnTypes.types.length == 0){
+ if(tokens.peek(1).type == TokenType.BraceOpen){
+ aspl.parser.utils.generic_error("Invalid return type '" + peekTypeIdentifier(tokens).identifier + "'", tokens.peek().location) // TODO: Use type_error when parseTypesIfAny for unknown types exists
+ }else{
+ aspl.parser.utils.syntax_error("Expected type identifier after 'returns'", tokens.peek().location)
+ }
+ }elseif(currentClass?!.isPublic && m.isPublic){
+ foreach(returnTypes.types as type){
+ if(!Type:isPublic(type)){
+ aspl.parser.utils.warning("The type '" + type.toString() + "' is not public and thus cannot be used as a return typr of a public method in a public class", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ }
+ }
+ }
+ m.parameters = parameters
+ m.returnTypes = returnTypes
+ Scope:getCurrentBundle().func = m
+ var list statements = list[]
+ if(tokens.peek().type == TokenType.BraceOpen){
+ if(m.isAbstract){
+ aspl.parser.utils.syntax_error("Abstract methods cannot have a body", tokens.peek().location)
+ }
+ currentMethod = m
+ var oldStaticContextState = inStaticContext
+ inStaticContext = m.isStatic
+ statements = parseBlock(tokens, null, false)
+ inStaticContext = oldStaticContextState
+ currentMethod = null
+ }elseif(!m.isAbstract){
+ aspl.parser.utils.syntax_error("Non-abstract methods must have a body", tokens.peek().location)
+ }else{
+ statements = list[]
+ }
+ m.code = statements
+ Scope:pop()
+ Scope:popBundle()
+ m.register(token.location)
+ var comments = list[]
+ foreach(m.attributes as attribute){
+ foreach(attribute.comments as comment){
+ comments.add(comment)
+ }
+ }
+ foreach(list(token.comments) as comment){
+ comments.add(comment)
+ }
+ return new MethodDeclareStatement(m, comments, token.location)
+ }elseif(token.value == "return"){
+ // TODO: Disallow if inside error callback
+ if(Scope:getCurrentBundle().func == null){
+ aspl.parser.utils.generic_error("Cannot return when not inside a function", token.location)
+ }
+ elseif(tokens.peek() != null && tokens.peek().location.startLine == token.location.endLine){
+ var returnTypes = ReturnTypeUtils:getReturnTypes(Scope:getCurrentBundle().func)
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, returnTypes))
+ if(returnTypes.types.length == 0){
+ aspl.parser.utils.type_error("Cannot return a value from a function with no return type", token.location)
+ }
+ elseif(!Type:matches(returnTypes, value.getType())){
+ if(Scope:getCurrentBundle().func oftype Method){
+ aspl.parser.utils.type_error("Cannot return a value of type '" + value.getType().toString() + "' in the method " + Method(Scope:getCurrentBundle().func).type.toString() + "." + Method(Scope:getCurrentBundle().func).name + " which has a return type of '" + returnTypes.toString() + "'", token.location)
+ }elseif(Scope:getCurrentBundle().func oftype ReactivePropertyCallback){
+ if(ReactivePropertyCallback(Scope:getCurrentBundle().func).p.isStatic){
+ aspl.parser.utils.type_error("Cannot return a value of type '" + value.getType().toString() + "' in " + ReactivePropertyCallback(Scope:getCurrentBundle().func).p.type.toString() + ":" + ReactivePropertyCallback(Scope:getCurrentBundle().func).p.name + " which has a type of '" + returnTypes.toString() + "'", token.location)
+ }else{
+ aspl.parser.utils.type_error("Cannot return a value of type '" + value.getType().toString() + "' in " + ReactivePropertyCallback(Scope:getCurrentBundle().func).p.type.toString() + "." + ReactivePropertyCallback(Scope:getCurrentBundle().func).p.name + " which has a type of '" + returnTypes.toString() + "'", token.location)
+ }
+ }elseif(Scope:getCurrentBundle().func oftype Callback){
+ aspl.parser.utils.type_error("Cannot return a value of type '" + value.getType().toString() + "' in a " + Callback(Scope:getCurrentBundle().func).type.toString() + " which has a return type of '" + returnTypes.toString() + "'", token.location)
+ }else{
+ aspl.parser.utils.type_error("Cannot return a value of type '" + value.getType().toString() + "' in the function " + Function(Scope:getCurrentBundle().func).identifier + " which has a return type of '" + returnTypes.toString() + "'", token.location)
+ }
+ }
+ return new ReturnStatement(value, Scope:getCurrentBundle().func, token.location)
+ }
+ return new ReturnStatement(null, Scope:getCurrentBundle().func, token.location)
+ }elseif(token.value == "fallback"){
+ if(!Options:enableErrorHandling){
+ aspl.parser.utils.generic_error("Experimental error handling ('-enableErrorHandling') is not enabled for this build", token.location)
+ }
+ // TODO: Check if inside error callback
+ elseif(tokens.peek() != null && tokens.peek().location.startLine == token.location.endLine){
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null))
+ // TODO: Maybe check the type?
+ return new FallbackStatement(value, token.location)
+ }else{
+ aspl.parser.utils.generic_error("No fallback value provided", token.location)
+ }
+ }elseif(token.value == "escape"){
+ if(!Options:enableErrorHandling){
+ aspl.parser.utils.generic_error("Experimental error handling ('-enableErrorHandling') is not enabled for this build", token.location)
+ }
+ // TODO: Check if inside error callback
+ if(Scope:getCurrentBundle().func == null){
+ aspl.parser.utils.generic_error("Cannot escape return when not inside a function", token.location)
+ }
+ elseif(tokens.peek() != null && tokens.peek().location.startLine == token.location.endLine){
+ var returnTypes = ReturnTypeUtils:getReturnTypes(Scope:getCurrentBundle().func)
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, returnTypes))
+ if(returnTypes.types.length == 0){
+ aspl.parser.utils.type_error("Cannot escape return a value from a function with no return type", token.location)
+ }
+ elseif(!Type:matches(returnTypes, value.getType())){
+ if(Scope:getCurrentBundle().func oftype Method){
+ aspl.parser.utils.type_error("Cannot escape return a value of type '" + value.getType().toString() + "' in the method " + Method(Scope:getCurrentBundle().func).type.toString() + "." + Method(Scope:getCurrentBundle().func).name + " which has a return type of '" + returnTypes.toString() + "'", token.location)
+ }elseif(Scope:getCurrentBundle().func oftype ReactivePropertyCallback){
+ if(ReactivePropertyCallback(Scope:getCurrentBundle().func).p.isStatic){
+ aspl.parser.utils.type_error("Cannot escape return a value of type '" + value.getType().toString() + "' in " + ReactivePropertyCallback(Scope:getCurrentBundle().func).p.type.toString() + ":" + ReactivePropertyCallback(Scope:getCurrentBundle().func).p.name + " which has a type of '" + returnTypes.toString() + "'", token.location)
+ }else{
+ aspl.parser.utils.type_error("Cannot escape return a value of type '" + value.getType().toString() + "' in " + ReactivePropertyCallback(Scope:getCurrentBundle().func).p.type.toString() + "." + ReactivePropertyCallback(Scope:getCurrentBundle().func).p.name + " which has a type of '" + returnTypes.toString() + "'", token.location)
+ }
+ }elseif(Scope:getCurrentBundle().func oftype Callback){
+ aspl.parser.utils.type_error("Cannot escape return a value of type '" + value.getType().toString() + "' in a " + Callback(Scope:getCurrentBundle().func).type.toString() + " which has a return type of '" + returnTypes.toString() + "'", token.location)
+ }else{
+ aspl.parser.utils.type_error("Cannot escape return a value of type '" + value.getType().toString() + "' in the function " + Function(Scope:getCurrentBundle().func).identifier + " which has a return type of '" + returnTypes.toString() + "'", token.location)
+ }
+ }
+ return new EscapeStatement(value, token.location)
+ }
+ return new EscapeStatement(null, token.location)
+ }elseif(token.value == "callback"){
+ Scope:push(true)
+ var list parameters = []
+ if(tokens.peek().type != TokenType.ParenthesisOpen){
+ aspl.parser.utils.syntax_error("Expected '(' (opening parenthesis) after 'callback'", tokens.peek().location)
+ }
+ tokens.shift()
+ while(true){
+ if(tokens.peek().type == TokenType.ParenthesisClose){
+ tokens.shift()
+ break
+ }
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected identifier after '('", tokens.peek().location)
+ }
+ var types = parseTypesIfAny(tokens)
+ if(types.types.length == 0){
+ if(tokens.peek(1).type == TokenType.Identifier){
+ aspl.parser.utils.generic_error("Invalid parameter type '" + peekTypeIdentifier(tokens).identifier + "'", tokens.peek().location) // TODO: Use type_error when parseTypesIfAny for unknown types exists
+ }else{
+ aspl.parser.utils.syntax_error("Parameters must specify a type", tokens.peek().location)
+ }
+ }
+ var parameter = tokens.next()
+ var Expression? defaultValue = null
+ if(tokens.peek().type == TokenType.Equals){
+ aspl.parser.utils.generic_error("Optional callback parameters are not yet supported", tokens.peek().location) // TODO
+ /*tokens.shift()
+ defaultValue = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, types))
+ if(!Expression(defaultValue).isConstant()){
+ aspl.parser.utils.generic_error("Default parameter values must be constant", tokens.peek().location)
+ }
+ if(!Type:matches(types, Expression(defaultValue).getType())){
+ aspl.parser.utils.type_error("Default parameter value must be of the same type as the parameter", tokens.peek().location)
+ }*/
+ }
+ parameters.add(new Parameter(parameter.value, types, defaultValue, token.location))
+ Variable:register(parameter.value, types, token.location)
+ if(tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }else{
+ if(tokens.peek().type == TokenType.ParenthesisClose){
+ tokens.shift()
+ break
+ }
+ aspl.parser.utils.syntax_error("Expected ',' or ')'", tokens.peek().location)
+ }
+ }
+ var returnTypes = new Types([])
+ if(tokens.peek().value == "returns"){
+ tokens.shift()
+ returnTypes = parseTypesIfAny(tokens)
+ if(returnTypes.types.length == 0){
+ if(tokens.peek(1).type == TokenType.BraceOpen){
+ aspl.parser.utils.generic_error("Invalid return type '" + peekTypeIdentifier(tokens).identifier + "'", tokens.peek().location) // TODO: Use type_error when parseTypesIfAny for unknown types exists
+ }else{
+ aspl.parser.utils.syntax_error("Expected type identifier after 'returns'", tokens.peek().location)
+ }
+ }
+ }
+ var type = "callback"
+ if(parameters.length > 0 || returnTypes.types.length > 0){
+ type += "<"
+ }
+ foreach(parameters as parameter){
+ type += parameter.types.toString() + ", "
+ }
+ if(returnTypes.types.length > 0){
+ type += "returns " + returnTypes.toString() + ", "
+ }
+ if(parameters.length > 0 || returnTypes.types.length > 0){
+ type = type.before(type.length - 2) + ">"
+ }
+ var c = new Callback(Type:fromString(type, this, token.location), parameters, returnTypes, null, Scope:getCurrentBundle(), token.location)
+ var oldFunction = Scope:getCurrentBundle().func
+ Scope:getCurrentBundle().func = c
+ var statements = parseBlock(tokens, null, false)
+ Scope:getCurrentBundle().func = oldFunction
+ c.code = statements
+ var capturedVariables = Scope:getCurrent().capturedVariables.cloneShallow()
+ Scope:pop()
+ Scope:passCapturedVariables(capturedVariables)
+ return applyOperators(new CallbackLiteral(c, capturedVariables, token.location), tokens, precedenceLevel)
+ }
+ elseif(token.value == "if" || token.value == "elseif"){ // see below for elseif implementation
+ if(tokens.peek().type != TokenType.ParenthesisOpen){
+ aspl.parser.utils.syntax_error("Expected '(' after '" + token.value + "'", tokens.peek().location)
+ }
+ tokens.shift()
+ var condition = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("bool")])))
+ if(!Type:matches(new Types([Type:fromString("bool")]), condition.getType())){
+ aspl.parser.utils.type_error("Condition in " + token.value + " statement must be of type boolean, but expression of type '" + condition.getType().toString() + "' given", token.location)
+ }
+ if(tokens.peek().type != TokenType.ParenthesisClose){
+ aspl.parser.utils.syntax_error("Expected ')' after condition in " + token.value + " statement", tokens.peek().location)
+ }
+ tokens.shift()
+ if(tokens.peek().type != TokenType.BraceOpen){
+ aspl.parser.utils.syntax_error("Expected '{' after condition in " + token.value + " statement", tokens.peek().location)
+ }
+ var code = parseBlock(tokens)
+ if(!tokens.empty()){
+ if(tokens.peek().value == "elseif"){
+ var next = parseToken(tokens.next(), tokens)
+ if(next oftype IfStatement){
+ return new IfElseIfStatement(condition, code, IfStatement(next), token.location)
+ }elseif(next oftype IfElseIfStatement){
+ return new IfElseIfStatement(condition, code, IfElseIfStatement(next), token.location)
+ }elseif(next oftype IfElseStatement){
+ return new IfElseIfStatement(condition, code, IfElseStatement(next), token.location)
+ }else{
+ aspl.parser.utils.syntax_error("'elseif' didn't generate if statement (compiler bug)", token.location)
+ }
+ }elseif(tokens.peek().value == "else"){
+ tokens.shift()
+ if(tokens.peek().value == "if"){
+ aspl.parser.utils.syntax_error("Use 'elseif' instead of 'else if'", tokens.peek().location)
+ }elseif(tokens.peek().type != TokenType.BraceOpen){
+ aspl.parser.utils.syntax_error("Expected '{' after 'else'", tokens.peek().location)
+ }
+ var elseCode = parseBlock(tokens)
+ return new IfElseStatement(condition, code, elseCode, token.location)
+ }
+ }
+ return new IfStatement(condition, code, token.location)
+ }
+ elseif(token.value == "while"){
+ if(tokens.peek().type != TokenType.ParenthesisOpen){
+ aspl.parser.utils.syntax_error("Expected '(' after 'while'", tokens.peek().location)
+ }
+ tokens.shift()
+ var condition = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("bool")])))
+ if(!Type:matches(new Types([Type:fromString("bool")]), condition.getType())){
+ aspl.parser.utils.type_error("Condition in while statement must be of type boolean, but expression of type '" + condition.getType().toString() + "' given", token.location)
+ }
+ if(tokens.peek().type != TokenType.ParenthesisClose){
+ aspl.parser.utils.syntax_error("Expected ')' after condition in while statement", tokens.peek().location)
+ }
+ tokens.shift()
+ if(tokens.peek().type != TokenType.BraceOpen){
+ aspl.parser.utils.syntax_error("Expected '{' after condition in while statement", tokens.peek().location)
+ }
+ loopDepth++
+ var code = parseBlock(tokens)
+ loopDepth--
+ return new WhileStatement(condition, code, token.location)
+ }
+ elseif(token.value == "repeat"){
+ if(tokens.peek().type != TokenType.ParenthesisOpen){
+ aspl.parser.utils.syntax_error("Expected '(' after 'repeat'", tokens.peek().location)
+ }
+ tokens.shift()
+ var iterations = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("integer")])))
+ if(!Type:matches(new Types([Type:fromString("integer")]), iterations.getType())){
+ aspl.parser.utils.type_error("Amount in repeat statement must be of type 'integer', but expression of type '" + iterations.getType().toString() + "' given", token.location)
+ }
+ var string? variable = null
+ var int start = 1
+ if(tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ var variableToken = tokens.next()
+ if(variableToken.type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected variable name after ',' in repeat statement", variableToken.location)
+ }
+ if(variableToken.value == "_"){
+ aspl.parser.utils.warning("Unnecessary use of '_' as variable name in repeat statement", variableToken.location)
+ }else{
+ variable = variableToken.value
+ }
+ if(tokens.peek().type == TokenType.Equals){
+ tokens.shift()
+ var startExpression = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("integer")])))
+ if(!Type:matches(new Types([Type:fromString("integer")]), startExpression.getType())){
+ aspl.parser.utils.type_error("Start value in repeat statement must be of type 'integer', but expression of type '" + startExpression.getType().toString() + "' given", token.location)
+ }
+ if(!startExpression.isConstant()){
+ aspl.parser.utils.syntax_error("Start value in repeat statement must be constant", token.location)
+ }
+ start = int(startExpression.getConstantValue())
+ }else{
+ aspl.parser.utils.warning("Start value in repeat statement not specified, assuming " + start, token.location)
+ }
+ }
+ Scope:push()
+ if(variable != null){
+ Variable:register(string(variable), new Types([Type:fromString("integer")]), token.location)
+ }
+ if(tokens.peek().type != TokenType.ParenthesisClose){
+ aspl.parser.utils.syntax_error("Expected ')' after iterations in repeat statement", tokens.peek().location)
+ }
+ tokens.shift()
+ if(tokens.peek().type != TokenType.BraceOpen){
+ aspl.parser.utils.syntax_error("Expected '{' after iterations in repeat statement", tokens.peek().location)
+ }
+ loopDepth++
+ var code = parseBlock(tokens, null, false)
+ loopDepth--
+ Scope:pop()
+ return new RepeatStatement(iterations, variable, start, code, token.location)
+ }
+ elseif(token.value == "foreach"){
+ if(tokens.peek().type != TokenType.ParenthesisOpen){
+ aspl.parser.utils.syntax_error("Expected '(' after 'foreach'", tokens.peek().location)
+ }
+ tokens.shift()
+ var collection = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("list")])))
+ if(!(Type:matches(new Types([Type:fromString("list")]), collection.getType(), true) || Type:matches(new Types([Type:fromString("map")]), collection.getType(), true) || Type:matches(new Types([Type:fromString("string")]), collection.getType()))){
+ aspl.parser.utils.type_error("Collection in foreach statement must be of type 'list', 'map' or 'string', but expression of type '" + collection.getType().toString() + "' given", token.location)
+ }
+ if(tokens.peek().value != "as"){
+ aspl.parser.utils.syntax_error("Expected 'as' after collection in foreach statement", tokens.peek().location)
+ }
+ tokens.shift()
+ var first = tokens.next()
+ if(first.type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected variable name after 'as' in foreach statement", first.location)
+ }
+ var Token? second = null
+ if(tokens.peek().type == TokenType.Assign){
+ tokens.shift()
+ second = tokens.next()
+ if(Token(second).type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected variable name after '=>' in foreach statement", Token(second).location)
+ }
+ }elseif(tokens.peek().type == TokenType.Comma){
+ aspl.parser.utils.syntax_error("Use '=>' instead of ',' when specifying key and value variables in foreach statements", tokens.peek().location)
+ }
+ Scope:push()
+ var string? key = null
+ var string? value = null
+ if(second != null){
+ if(first.value == "_"){
+ aspl.parser.utils.warning("Unnecessary use of '_' as key variable name in foreach statement", first.location)
+ }else{
+ key = first.value
+ if(Type:matches(new Types([Type:fromString("list")]), collection.getType(), true)){
+ Variable:register(string(key), new Types([Type:fromString("integer")]), token.location)
+ }elseif(Type:matches(new Types([Type:fromString("map")]), collection.getType(), true)){
+ if(Type:getGenericTypes(collection.getType().toString()).length < 2){
+ Variable:register(string(key), new Types([]), token.location) // map (due to a previous non-fatal error)
+ }else{
+ Variable:register(string(key), Type:getGenericTypes(collection.getType().toString())[0], token.location)
+ }
+ }elseif(Type:matches(new Types([Type:fromString("string")]), collection.getType())){
+ Variable:register(string(key), new Types([Type:fromString("integer")]), token.location)
+ }
+ }
+ if(Token(second).value != "_"){
+ value = Token(second).value
+ if(Type:matches(new Types([Type:fromString("list")]), collection.getType(), true)){
+ if(Type:getGenericTypes(collection.getType().toString()).length < 1){
+ Variable:register(string(value), new Types([]), token.location) // list (due to a previous non-fatal error)
+ }else{
+ Variable:register(string(value), Type:getGenericTypes(collection.getType().toString())[0], token.location)
+ }
+ }elseif(Type:matches(new Types([Type:fromString("map")]), collection.getType(), true)){
+ if(Type:getGenericTypes(collection.getType().toString()).length < 2){
+ Variable:register(string(value), new Types([]), token.location) // map (due to a previous non-fatal error)
+ }else{
+ Variable:register(string(value), Type:getGenericTypes(collection.getType().toString())[1], token.location)
+ }
+ }elseif(Type:matches(new Types([Type:fromString("string")]), collection.getType())){
+ Variable:register(string(value), new Types([Type:fromString("string")]), token.location)
+ }
+ }
+ }else{
+ if(first.value == "_"){
+ aspl.parser.utils.warning("Unnecessary use of '_' as value variable name in foreach statement", first.location)
+ }else{
+ value = first.value
+ if(Type:matches(new Types([Type:fromString("list")]), collection.getType(), true)){
+ if(Type:getGenericTypes(collection.getType().toString()).length < 1){
+ Variable:register(string(value), new Types([]), token.location)
+ }else{
+ Variable:register(string(value), Type:getGenericTypes(collection.getType().toString())[0], token.location)
+ }
+ }elseif(Type:matches(new Types([Type:fromString("map")]), collection.getType(), true)){
+ if(Type:getGenericTypes(collection.getType().toString()).length < 2){
+ Variable:register(string(value), new Types([]), token.location)
+ }else{
+ Variable:register(string(value), Type:getGenericTypes(collection.getType().toString())[1], token.location)
+ }
+ }elseif(Type:matches(new Types([Type:fromString("string")]), collection.getType())){
+ Variable:register(string(value), new Types([Type:fromString("string")]), token.location)
+ }
+ }
+ }
+ if(tokens.peek().type != TokenType.ParenthesisClose){
+ aspl.parser.utils.syntax_error("Expected ')' after variable names in foreach statement", tokens.peek().location)
+ }
+ tokens.shift()
+ if(tokens.peek().type != TokenType.BraceOpen){
+ aspl.parser.utils.syntax_error("Expected '{' after variable names in foreach statement", tokens.peek().location)
+ }
+ loopDepth++
+ var code = parseBlock(tokens, null, false)
+ loopDepth--
+ Scope:pop()
+ return new ForeachStatement(collection, key, value, code, token.location)
+ }
+ elseif(token.value == "implement"){
+ if(tokens.peek().type != TokenType.ParenthesisOpen){
+ aspl.parser.utils.syntax_error("Expected '(' after 'implement'", tokens.peek().location)
+ }
+ tokens.shift()
+ var call = tokens.next()
+ if(call.type != TokenType.String){
+ aspl.parser.utils.syntax_error("Expected implementation call identifier (string) as first argument to 'implement'", call.location)
+ }
+ var list args = []
+ if(tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ while(true){
+ var arg = tokens.peek()
+ if(arg.type == TokenType.ParenthesisClose){
+ break
+ }
+ tokens.shift()
+ args.add(aspl.parser.utils.verify_expression(parseToken(arg, tokens)))
+ if(tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }
+ }
+ }
+ if(tokens.peek().type != TokenType.ParenthesisClose){
+ aspl.parser.utils.syntax_error("Expected ')' after arguments for 'implement'", tokens.peek().location)
+ }
+ tokens.shift()
+ ImplementationCallUtils:usedImplementationCalls[call.value] = args.length
+ return applyOperators(new ImplementationCallExpression(call.value, args, token.location), tokens, precedenceLevel)
+ }
+ elseif(token.value == "oftype"){
+ if(previousExpression == null){
+ aspl.parser.utils.syntax_error("Expected expression before 'oftype'", token.location)
+ }
+ var typeIdentifierFirstToken = tokens.peek()
+ var typeIdentifier = parseTypeIdentifier(tokens).identifier
+ if(!Type:existsByName(this, typeIdentifier)){
+ aspl.parser.utils.type_error("Type '" + typeIdentifier + "' does not exist", token.location)
+ }
+ var type = Type:fromString(typeIdentifier, this, typeIdentifierFirstToken.location)
+ /*
+ TODO: The below code is false-positive when type extends Expression(previousExpression).getType()
+ if(!Type:matches(new Types([type]), Expression(previousExpression).getType())){
+ aspl.parser.utils.warning("Expression of type '" + Expression(previousExpression).getType().toString() + "' will never be of type '" + type.toString() + "'", token.location)
+ }*/
+ return applyOperators(new OfTypeExpression(Expression(previousExpression), type, token.location), tokens, precedenceLevel)
+ }
+ elseif(token.value == "break" || token.value == "break_no_legacy"){ // TODO: Remove break_no_legacy when old codebases have been updated
+ var level = 1
+ if(tokens.peek().type == TokenType.Integer){
+ var levelExpression = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("integer")])))
+ if(!Type:matches(new Types([Type:fromString("integer")]), levelExpression.getType())){
+ aspl.parser.utils.type_error("Break level must be of type integer, but expression of type '" + levelExpression.getType().toString() + "' given", token.location)
+ }
+ if(!levelExpression.isConstant()){
+ aspl.parser.utils.generic_error("Break level must be known at compile time", token.location)
+ }
+ level = int(levelExpression.getConstantValue())
+ }
+ if(this.loopDepth < 1){
+ aspl.parser.utils.generic_error("Cannot use 'break' outside of a loop", token.location)
+ }elseif(level < 1){
+ aspl.parser.utils.generic_error("Break level must be greater than 0", token.location)
+ }elseif(level > this.loopDepth){
+ aspl.parser.utils.generic_error("Break level must be less than or equal to " + this.loopDepth + " because it is in only that many nested loops", token.location)
+ }
+ return new BreakStatement(level, token.location)
+ }
+ elseif(token.value == "continue" || token.value == "continue_no_legacy"){ // TODO: Remove continue_no_legacy when old codebases have been updated
+ var level = 1
+ if(tokens.peek().type == TokenType.Integer){
+ var levelExpression = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("integer")])))
+ if(!Type:matches(new Types([Type:fromString("integer")]), levelExpression.getType())){
+ aspl.parser.utils.type_error("Continue level must be of type integer, but expression of type '" + levelExpression.getType().toString() + "' given", token.location)
+ }
+ if(!levelExpression.isConstant()){
+ aspl.parser.utils.generic_error("Continue level must be known at compile time", token.location)
+ }
+ level = int(levelExpression.getConstantValue())
+ }
+ if(this.loopDepth < 1){
+ aspl.parser.utils.generic_error("Cannot use 'continue' outside of a loop", token.location)
+ }elseif(level < 1){
+ aspl.parser.utils.generic_error("Continue level must be greater than 0", token.location)
+ }elseif(level > this.loopDepth){
+ aspl.parser.utils.generic_error("Continue level must be less than or equal to " + this.loopDepth + " because it is in only that many nested loops", token.location)
+ }
+ return new ContinueStatement(level, token.location)
+ }
+ elseif(token.value == "class"){
+ if(currentClass != null){
+ aspl.parser.utils.syntax_error("Cannot declare a class inside another class", token.location)
+ }
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected class name after 'class'", tokens.peek().location)
+ }
+ var name = tokens.next().value
+ var list parents = []
+ if(tokens.peek().value == "extends"){
+ tokens.shift()
+ while(tokens.peek().type != TokenType.BraceOpen){
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected class name after 'extends'", tokens.peek().location)
+ }
+ var t = tokens.peek()
+ var parent = parseTypeIdentifier(tokens).identifier
+ if(Type:existsByName(this, parent)){
+ var type = Type:fromString(parent, this, t.location)
+ if(!Class:classes.containsKey(type.toString())){
+ aspl.parser.utils.type_error("Cannot extend the unknown class '" + parent + "'", tokens.peek().location)
+ }else{
+ parents.add(type)
+ }
+ }else{
+ aspl.parser.utils.type_error("Cannot extend the unknown class '" + parent + "'", tokens.peek().location)
+ }
+ if(tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }
+ }
+ }
+ if(tokens.peek().type != TokenType.BraceOpen){
+ aspl.parser.utils.syntax_error("Expected '{' after class name", tokens.peek().location)
+ }
+ var type = Type:fromString(currentNamespace + "." + name, this, token.location)
+ ClassUtils:throwOnInvalidInheritance(Class:classes[type.toString()], [], token.location)
+ var c = Class:classes[type.toString()]
+ currentClass = c
+ c.parents = parents
+ c.code = parseBlock(tokens)
+ currentClass = null
+ if(!c.isAbstract){
+ var stillAbstractMethods = ClassUtils:getAllAbstractMethods(c)
+ if(stillAbstractMethods.length > 0){
+ aspl.parser.utils.type_error("The class " + c.type.toString() + " does not implement the following abstract methods from its parents: " + stillAbstractMethods.join(", "))
+ }
+ }
+ var comments = list[]
+ foreach(c.attributes as attribute){
+ foreach(attribute.comments as comment){
+ comments.add(comment)
+ }
+ }
+ foreach(list(token.comments) as comment){
+ comments.add(comment)
+ }
+ return new ClassDeclareStatement(c, comments, token.location)
+ }
+ elseif(token.value == "enum"){
+ if(currentClass != null){
+ aspl.parser.utils.syntax_error("Cannot declare an enum inside another enum", token.location)
+ }
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected enum name after 'enum'", tokens.peek().location)
+ }
+ var name = tokens.next().value
+ if(tokens.peek().type != TokenType.BraceOpen){
+ aspl.parser.utils.syntax_error("Expected '{' after enum name", tokens.peek().location)
+ }
+ tokens.shift()
+ attributeCache.clear()
+ var type = Type:fromString(currentNamespace + "." + name, this, token.location)
+ var e = Enum:enums[type.toString()]
+ currentEnum = e
+ var map fields = {}
+ while(true){
+ if(e.isFlags && fields.length > 31){
+ aspl.parser.utils.generic_error("Cannot declare more than 32 fields in the enum \"" + e.type.toString() + "\", because it has the \"flags\" attribute", tokens.peek().location)
+ }
+ var field = tokens.next()
+ if(field.type == TokenType.BraceClose){
+ break
+ }
+ if(AttributeUtils:parseAttributesIfAny(this, field, tokens)){
+ field = tokens.next()
+ }
+ if(field.type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected enum field name", field.location)
+ }
+ if(fields.containsKey(field.value)){
+ aspl.parser.utils.generic_error("Enum field '" + field.value + "' already declared", field.location)
+ }
+ foreach(attributeCache as attribute){
+ if((attribute.attribute.usage && AttributeUsage.EnumField) == 0){
+ aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used on enum fields", attribute.location)
+ }
+ foreach(attributeCache as other){
+ if(other.attribute.identifier != attribute.attribute.identifier){
+ if(!attribute.attribute.canPair(other.attribute)){
+ aspl.parser.utils.type_error("The attribute '" + attribute.attribute.identifier + "' cannot be used together with the attribute '" + other.attribute.identifier + "'", attribute.location)
+ }
+ }
+ }
+ }
+ var value = fields.length
+ if(tokens.peek().type == TokenType.Equals){
+ if(e.isFlags){
+ aspl.parser.utils.generic_error("Cannot specify custom field values for enums with the 'flags' attribute", tokens.peek().location)
+ }
+ tokens.shift()
+ var v = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("integer")])))
+ if(!v.isConstant()){
+ aspl.parser.utils.generic_error("Value of enum field must be constant")
+ }elseif(!Type:matches(new Types([Type:fromString("integer")]), v.getType())){
+ aspl.parser.utils.type_error("Value of enum field must be an integer")
+ }else{
+ value = int(v.getConstantValue())
+ }
+ }elseif(e.isFlags){
+ value = int(math.pow(2d, double(fields.length)))
+ }
+ fields[field.value] = new EnumField(e, field.value, value, attributeCache, token.location)
+ attributeCache.clear()
+ if(tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }elseif(tokens.peek().type != TokenType.BraceClose){
+ aspl.parser.utils.syntax_error("Expected ',' or '}' after enum field", tokens.peek().location)
+ }
+ }
+ e.fields = fields
+ currentEnum = null
+ var comments = list[]
+ foreach(list(e.attributes) as attribute){
+ foreach(attribute.comments as comment){
+ comments.add(comment)
+ }
+ }
+ foreach(list(token.comments) as comment){
+ comments.add(comment)
+ }
+ return new EnumDeclareStatement(e, comments, token.location)
+ }elseif(token.value == "new"){
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected class identifier after 'new'", tokens.peek().location)
+ }
+ var identifierFirstToken = tokens.peek()
+ var unhandledIdentifier = parseTypeIdentifier(tokens).identifier
+ var type = Type:fromString(unhandledIdentifier, this, identifierFirstToken.location)
+ if(!Class:classes.containsKey(type.toString())){
+ aspl.parser.utils.fatal_error("Class '" + unhandledIdentifier + "' not found", token.location)
+ }
+ var c = Class:classes[type.toString()]
+ if(tokens.peek().type != TokenType.ParenthesisOpen){
+ aspl.parser.utils.syntax_error("Expected '(' after class identifier in 'new' expression", tokens.peek().location)
+ }
+ tokens.shift()
+ if(c.isAbstract){
+ aspl.parser.utils.generic_error("Cannot instantiate the abstract class " + type.toString(), token.location)
+ }
+ if(c.isStatic){
+ aspl.parser.utils.generic_error("Cannot instantiate the static class " + type.toString(), token.location)
+ }
+ var list arguments = []
+ if(Method:methods.containsKey(c.type.toString())){
+ if(Method:exists(c.type, "construct")){
+ var constructor = Method:get(c.type, "construct")
+ foreach(constructor.parameters as parameter){
+ if(tokens.peek().type == TokenType.ParenthesisClose){
+ if(!parameter.optional){
+ aspl.parser.utils.generic_error("Too few arguments (" + arguments.length + ") given for method '" + constructor.type.toString() + "." + constructor.name + "' (expected " + constructor.minimumParameterCount + ")", tokens.peek().location)
+ }
+ break
+ }
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, parameter.types))
+ if(!Type:matches(parameter.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot pass a value of the type '" + value.getType().toString() + "' to a parameter of the type '" + parameter.types.toString() + "'", value.location)
+ }
+ arguments.add(value)
+ if(arguments.length < constructor.parameters.length && tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }
+ }
+ if(!constructor.isPublic && module.id != c.module.id){
+ aspl.parser.utils.warning("Cannot instantiate the class '" + c.type.identifier + "' that has a private constructor here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ }
+ }
+ tokens.shift()
+ foreach(c.attributes as attribute){
+ if(attribute.attribute.identifier == "deprecated"){
+ if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){
+ aspl.parser.utils.warning("Class " + c.type.toString() + " is deprecated: " + attribute.arguments[0].getConstantValue(), identifierFirstToken.location)
+ }else{
+ aspl.parser.utils.warning("Class " + c.type.toString() + " is deprecated", identifierFirstToken.location)
+ }
+ }
+ }
+ if(!c.isPublic && module.id != c.module.id){
+ aspl.parser.utils.warning("Cannot instantiate the private class '" + c.type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ return applyOperators(new ClassInstantiateExpression(c, arguments, token.location), tokens, precedenceLevel)
+ }elseif(token.value == "property"){
+ if(currentClass == null){
+ aspl.parser.utils.generic_error("Cannot declare a property outside of a class", token.location)
+ }
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected property name after 'property'", tokens.peek().location)
+ }
+ var types = parseTypesIfAny(tokens)
+ var name = tokens.next()
+ var Expression? defaultValue = null
+ attributeCache.clear()
+ if(tokens.peek().type == TokenType.Equals){
+ tokens.shift()
+ defaultValue = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, types))
+ }
+ elseif(tokens.peek().type == TokenType.BraceOpen){
+ tokens.shift()
+ var p = CustomReactiveProperty(Property:properties[Class(currentClass).type.toString()][name.value])
+ var bool isStatic = false
+ foreach(p.attributes as attribute){
+ if(attribute.attribute.identifier == "static"){
+ isStatic = true
+ }
+ }
+ if(currentClass?!.isPublic && p.isPublic){
+ foreach(types.types as type){
+ if(!Type:isPublic(type)){
+ aspl.parser.utils.warning("The type '" + type.toString() + "' is not public and thus cannot be used as a type of a public property in a public class", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ }
+ }
+ var list? getCode = null
+ var list? setCode = null
+ if(tokens.peek().type == TokenType.Identifier){
+ if(tokens.peek().value == "get"){
+ tokens.shift()
+ Scope:pushBundle(new ReactivePropertyCallback(p, types))
+ Scope:push()
+ var oldStaticContextState = inStaticContext
+ inStaticContext = p.isStatic
+ getCode = parseBlock(tokens)
+ inStaticContext = oldStaticContextState
+ Scope:pop()
+ Scope:popBundle()
+ }
+ if(tokens.peek().value == "set"){
+ tokens.shift()
+ Scope:pushBundle(new ReactivePropertyCallback(p, new Types([])))
+ Scope:push()
+ Variable:register("value", types, token.location)
+ var oldStaticContextState = inStaticContext
+ inStaticContext = p.isStatic
+ setCode = parseBlock(tokens)
+ inStaticContext = oldStaticContextState
+ Scope:pop()
+ Scope:popBundle()
+ }
+ }
+ if(getCode == null && setCode == null){
+ aspl.parser.utils.generic_error("Expected 'get', 'set' or both in reactive property declaration", tokens.peek().location)
+ }
+ tokens.shift()
+ p.getCode = getCode
+ p.setCode = setCode
+ p.register(token.location)
+ var comments = list[]
+ foreach(p.attributes as attribute){
+ foreach(attribute.comments as comment){
+ comments.add(comment)
+ }
+ }
+ foreach(list(token.comments) as comment){
+ comments.add(comment)
+ }
+ return new PropertyDeclareStatement(p, comments, token.location)
+ }
+ if(types.types.length == 0){
+ if(defaultValue == null){
+ aspl.parser.utils.syntax_error("Expected type after property name in 'property' statement", name.location)
+ }
+ types = Expression(defaultValue).getType()
+ }else{
+ if(defaultValue != null){
+ if(!Type:matches(types, Expression(defaultValue).getType())){
+ aspl.parser.utils.generic_error("Cannot assign a default value of type '" + Expression(defaultValue).getType().toString() + "' to a property of type '" + types.toString() + "'", Expression(defaultValue).location)
+ }
+ }else{
+ if(Type:matches(types, new Types([Type:fromString("null")]))){
+ defaultValue = new NullLiteral(token.location)
+ }else{
+ if(types.types.length > 1){
+ var allComplexTypes = true
+ foreach(types.types as type){
+ if(!Class:classes.containsKey(type.toString()) && !Enum:enums.containsKey(type.toString())){
+ allComplexTypes = false
+ }
+ }
+ if(allComplexTypes){
+ defaultValue = null
+ }else{
+ aspl.parser.utils.generic_error("Cannot infer a default value for a property with multiple types", name.location)
+ }
+ }else{
+ defaultValue = types.types[0].getDefaultValue(token.location)
+ }
+ }
+ }
+ }
+ var p = CustomNormalProperty(Property:properties[Class(currentClass).type.toString()][name.value])
+ var bool isStatic = false
+ var bool isThreadLocal = false
+ foreach(p.attributes as attribute){
+ if(attribute.attribute.identifier == "static"){
+ isStatic = true
+ }elseif(attribute.attribute.identifier == "threadlocal"){
+ isThreadLocal = true
+ }
+ }
+ if(isThreadLocal && !isStatic){
+ aspl.parser.utils.type_error("The attribute 'threadlocal' can only be used on static properties", token.location)
+ }
+ if(currentClass?!.isPublic && p.isPublic){
+ foreach(types.types as type){
+ if(!Type:isPublic(type)){
+ aspl.parser.utils.warning("The type '" + type.toString() + "' is not public and thus cannot be used as a type of a public property in a public class", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ }
+ }
+ p.defaultValue = defaultValue
+ p.register(token.location)
+ var comments = list[]
+ foreach(p.attributes as attribute){
+ foreach(attribute.comments as comment){
+ comments.add(comment)
+ }
+ }
+ foreach(list(token.comments) as comment){
+ comments.add(comment)
+ }
+ return new PropertyDeclareStatement(p, comments, token.location)
+ }elseif(token.value == "this"){
+ if(currentClass == null){
+ aspl.parser.utils.generic_error("Cannot use the 'this' keyword outside of a class", token.location)
+ }
+ if(inStaticContext){
+ aspl.parser.utils.generic_error("Cannot use the 'this' keyword in a static context", token.location)
+ }
+ Scope:passCapturedVariables(["this"])
+ return applyOperators(new ThisExpression(Class(currentClass), token.location), tokens, precedenceLevel)
+ }elseif(token.value == "parent" && tokens.peek().type == TokenType.ParenthesisOpen){
+ if(currentClass == null){
+ aspl.parser.utils.generic_error("Cannot use the 'parent' keyword outside of a class", token.location)
+ }
+ if(inStaticContext){
+ aspl.parser.utils.generic_error("Cannot use the 'parent' keyword in a static context", token.location)
+ }
+ if(currentClass?!.parents == null || currentClass?!.parents?!.length == 0){ // TODO: Can .parents ever be null at this point?
+ aspl.parser.utils.generic_error("Cannot use the 'parent' keyword in a class that does not extend any other classes", token.location)
+ }
+ tokens.shift()
+ var parentType = Type:fromString(parseTypeIdentifier(tokens).identifier, this, token.location)
+ if(!Class:classes.containsKey(parentType.toString())){
+ aspl.parser.utils.type_error("Class '" + parentType.toString() + "' does not exist", token.location)
+ }
+ var parentClass = Class:classes[parentType.toString()]
+ if(!ClassUtils:isParent(currentClass?!, parentClass)){
+ aspl.parser.utils.generic_error("Cannot use the 'parent' keyword with a class that is not a parent of the current class", token.location)
+ }
+ if(tokens.peek().type != TokenType.ParenthesisClose){
+ aspl.parser.utils.syntax_error("Expected ')' after the parent class type in 'parent' expression", tokens.peek().location)
+ }
+ tokens.shift()
+ if(tokens.peek().type != TokenType.Dot){
+ aspl.parser.utils.syntax_error("Expected '.' after 'parent' expression", tokens.peek().location)
+ }
+ return applyOperators(new ParentExpression(parentClass, token.location), tokens, precedenceLevel)
+ }elseif(token.value == "throw"){
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens))
+ if(!(value oftype ClassInstantiateExpression) || !ClassInstantiateExpression(value).c.isError){
+ aspl.parser.utils.type_error("Only errors can be thrown", value.location)
+ }
+ if(!ErrorUtils:canCallableThrow(Scope:getCurrentBundle().func)){
+ aspl.parser.utils.generic_error("Cannot throw an error in a callable without the [throws] attribute", token.location)
+ }
+ return new ThrowStatement(ClassInstantiateExpression(value), token.location)
+ }elseif(token.value == "catch"){
+ if(!Options:enableErrorHandling){
+ aspl.parser.utils.generic_error("Experimental error handling ('-enableErrorHandling') is not enabled for this build", token.location)
+ }
+ if(previousExpression == null){
+ aspl.parser.utils.syntax_error("The " + token.value + " statement can only be used on expressions directly, i.e.: foo() catch { ... }", token.location)
+ }
+ if(!ErrorUtils:canExpressionThrow(previousExpression)){
+ aspl.parser.utils.syntax_error("Cannot catch an error from an expression that is not error-prone", token.location)
+ }
+ var string? variable = null
+ if(tokens.peek().type == TokenType.Identifier){
+ variable = tokens.next().value
+ }
+ if(tokens.peek().type != TokenType.BraceOpen){
+ aspl.parser.utils.syntax_error("Expected '{' after variable in " + token.value + " statement", tokens.peek().location)
+ }
+ Scope:push(true)
+ if(variable != null){
+ Variable:register(variable?!, new Types([Type:fromString("any")]), token.location)
+ }
+ var code = parseBlock(tokens)
+ var capturedVariables = Scope:getCurrent().capturedVariables.cloneShallow()
+ Scope:pop()
+ Scope:passCapturedVariables(capturedVariables)
+ return applyOperators(new CatchExpression(previousExpression?!, variable, code, capturedVariables, standalone, token.location), tokens, precedenceLevel) // TODO: The standalone variable will always be false here
+ }elseif(Variable:exists(token.value)){
+ if(!tokens.empty()){
+ if(tokens.peek().type == TokenType.Equals){
+ tokens.shift()
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, Variable:get(token.value).types))
+ if(!Type:matches(Variable:get(token.value).types, value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a variable of type '" + Variable:get(token.value).types.toString() + "'", value.location)
+ }
+ return applyOperators(new VariableAssignExpression(Variable:get(token.value), value, token.location), tokens, precedenceLevel)
+ }
+ }
+ return applyOperators(new VariableAccessExpression(Variable:get(token.value), token.location), tokens, precedenceLevel)
+ }
+ var typeIdentifier = peekTypeIdentifier(tokens, token)
+ if(tokens.length > typeIdentifier.tokenCount && (tokens.peek(typeIdentifier.tokenCount).type == TokenType.ParenthesisOpen) && Type:existsByName(this, typeIdentifier.identifier)){
+ parseTypeIdentifier(tokens, token)
+ var target = Type:fromString(typeIdentifier.identifier, this, token.location)
+ tokens.shift()
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens))
+ tokens.shift()
+ if(!value.getType().canCast(target)){
+ aspl.parser.utils.type_error("Cannot cast from type '" + value.getType().toString() + "' to type '" + target.toString() + "'", value.location)
+ }
+ return applyOperators(new CastExpression(value, target, token.location), tokens, precedenceLevel)
+ }
+ if(tokens.length > typeIdentifier.tokenCount && tokens.peek(typeIdentifier.tokenCount).type == TokenType.Colon){
+ if(!Type:existsByName(this, typeIdentifier.identifier)){
+ aspl.parser.utils.fatal_error("Unknown type '" + typeIdentifier.identifier + "'", typeIdentifier.location)
+ }
+ var type = Type:fromString(typeIdentifier.identifier, this, typeIdentifier.location)
+ tokens.shift(typeIdentifier.tokenCount + 1)
+ var name = tokens.next()
+ if(name.type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected identifier after ':'", name.location)
+ }
+ if(tokens.length > 0 && tokens.peek().type == TokenType.ParenthesisOpen){
+ if(!Method:exists(type, name.value, false)){
+ aspl.parser.utils.fatal_error("Unknown method " + type.toString() + ":" + name.value, name.location)
+ }
+ var newThread = false
+ if(tokens.peek().type == TokenType.Plus){
+ newThread = true
+ tokens.shift()
+ }
+ var m = Method:get(type, name.value)
+ if(!m.isStatic){
+ aspl.parser.utils.generic_error("Cannot call the non-static method " + type.toString() + "." + name.value + " statically", name.location)
+ }
+ tokens.shift()
+ var list arguments = []
+ foreach(m.parameters as parameter){
+ if(tokens.peek().type == TokenType.ParenthesisClose){
+ if(!parameter.optional){
+ aspl.parser.utils.generic_error("Too few arguments (" + arguments.length + ") given for method '" + m.type.toString() + ":" + m.name + "' (expected " + m.minimumParameterCount + ")", tokens.peek().location)
+ }
+ break
+ }
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, parameter.types))
+ if(!Type:matches(parameter.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot pass a value of the type '" + value.getType().toString() + "' to a parameter of the type '" + parameter.types.toString() + "'", value.location)
+ }
+ arguments.add(value)
+ if(arguments.length < m.parameters.length && tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }
+ }
+ foreach(m.attributes as attribute){
+ if(attribute.attribute.identifier == "deprecated"){
+ if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){
+ aspl.parser.utils.warning("Method " + m.type.toString() + ":" + m.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), name.location)
+ }else{
+ aspl.parser.utils.warning("Method " + m.type.toString() + ":" + m.name + " is deprecated", name.location)
+ }
+ }
+ }
+
+ if(!Class:classes[m.type.toString()].isPublic && module.id != Class:classes[m.type.toString()].module.id){
+ aspl.parser.utils.warning("Cannot call a method from the private class '" + Class:classes[m.type.toString()].type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ if(m oftype CustomMethod && !m.isPublic && (currentClass == null || !Type:matches(new Types([m.type]), new Types([currentClass?!.type])))){
+ aspl.parser.utils.warning("Cannot call the private method '" + m.type.identifier + ":" + m.name + "' here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ var node = new StaticMethodCallExpression(m, type, arguments, newThread, token.location)
+ if(tokens.peek().type != TokenType.ParenthesisClose){
+ if(arguments.length == 0 || tokens.peek().type == TokenType.Comma){
+ aspl.parser.utils.fatal_error("Too many arguments (" + arguments.length + ") given for method '" + m.type.toString() + ":" + m.name + "' (expected " + m.parameters.length + ")", tokens.peek().location)
+ }else{
+ aspl.parser.utils.syntax_error("Expected ')' after arguments in method call", tokens.peek().location)
+ }
+ }else{
+ tokens.shift()
+ }
+ return applyOperators(node, tokens, precedenceLevel)
+ }else{
+ if(Enum:enums.containsKey(type.toString())){
+ aspl.parser.utils.syntax_error("Enums fields are accessed with a '.' (dot) instead of a ':' (colon), so use " + typeIdentifier.identifier + "." + name.value + " instead of " + typeIdentifier.identifier + ":" + name.value, name.location)
+ }
+ if(!Property:exists(type, name.value, false)){
+ aspl.parser.utils.fatal_error("Unknown property " + type.toString() + ":" + name.value, name.location)
+ }
+ var p = Property:get(type, name.value)
+ if(!p.isStatic){
+ aspl.parser.utils.generic_error("Cannot access the non-static property " + type.toString() + "." + name.value + " statically", name.location)
+ }
+ if(!tokens.empty()){
+ if(tokens.peek().type == TokenType.Equals){
+ tokens.shift()
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, p.types))
+ if(!Type:matches(p.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + p.types.toString() + "'", value.location)
+ }
+ if(!Class:classes[p.type.toString()].isPublic && module.id != Class:classes[p.type.toString()].module.id){
+ aspl.parser.utils.warning("Cannot assign to a property from the private class '" + Class:classes[p.type.toString()].type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ foreach(p.attributes as attribute){
+ if(attribute.attribute.identifier == "deprecated"){
+ if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){
+ aspl.parser.utils.warning("Property " + p.type.toString() + ":" + p.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), name.location)
+ }else{
+ aspl.parser.utils.warning("Property " + p.type.toString() + ":" + p.name + " is deprecated", name.location)
+ }
+ }
+ }
+ if(p.isReadPublic){
+ if(currentClass != null && !Type:matches(new Types([p.type]), new Types([currentClass?!.type]))){
+ aspl.parser.utils.warning("Cannot assign to the property " + p.type.identifier + ":" + p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ }elseif(!p.isPublic && (currentClass == null || !Type:matches(new Types([p.type]), new Types([currentClass?!.type])))){
+ aspl.parser.utils.warning("Cannot assign to the private property " + p.type.identifier + ":" + p.name + " here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ return applyOperators(new StaticPropertyAssignExpression(p, type, value, token.location), tokens, precedenceLevel)
+ }
+ }
+ if(!Class:classes[p.type.toString()].isPublic && module.id != Class:classes[p.type.toString()].module.id){
+ aspl.parser.utils.warning("Cannot access a property from the private class '" + Class:classes[p.type.toString()].type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ foreach(p.attributes as attribute){
+ if(attribute.attribute.identifier == "deprecated"){
+ if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){
+ aspl.parser.utils.warning("Property " + p.type.toString() + ":" + p.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), name.location)
+ }else{
+ aspl.parser.utils.warning("Property " + p.type.toString() + ":" + p.name + " is deprecated", name.location)
+ }
+ }
+ }
+ if(!p.isPublic && !p.isReadPublic && (currentClass == null || !Type:matches(new Types([p.type]), new Types([currentClass?!.type])))){
+ aspl.parser.utils.warning("Cannot access the private property " + p.type.identifier + ":" + p.name + " here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ return applyOperators(new StaticPropertyAccessExpression(p, type, token.location), tokens, precedenceLevel)
+ }
+ }else{
+ if(typeIdentifier.identifier.contains(".")){
+ var enumIdentifierParts = typeIdentifier.identifier.split(".")
+ var fieldToken = tokens.peek(typeIdentifier.tokenCount - 2)
+ var field = enumIdentifierParts[enumIdentifierParts.length - 1]
+ enumIdentifierParts.removeAt(enumIdentifierParts.length - 1)
+ var enumIdentifier = enumIdentifierParts.join(".")
+ var type = Type:fromString(enumIdentifier, this, typeIdentifier.location)
+ if(Enum:enums.containsKey(type.toString())){
+ parseTypeIdentifier(tokens, token)
+ if(!map(Enum:enums[type.toString()].fields).containsKey(field)){
+ aspl.parser.utils.syntax_error("Unknown enum field " + type.toString() + "." + field, fieldToken.location)
+ }
+ if(!Enum:enums[type.toString()].isPublic && module.id != Enum:enums[type.toString()].module.id){
+ aspl.parser.utils.warning("Cannot access the private enum " + type.toString() + " here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ return applyOperators(new EnumFieldAccessExpression(map(Enum:enums[type.toString()].fields)[field], token.location), tokens, precedenceLevel)
+ }
+ }
+ }
+ if(token.value == "list"){
+ if(tokens.peek().type != TokenType.LessThan){
+ aspl.parser.utils.syntax_error("Expected '<' after 'list'", tokens.peek().location)
+ }
+ tokens.shift()
+ var Types type = parseTypesIfAny(tokens)
+ if(tokens.peek().type != TokenType.GreaterThan){
+ aspl.parser.utils.syntax_error("Expected '>' after list type", tokens.peek().location)
+ }
+ tokens.shift()
+ if(tokens.peek().type != TokenType.BracketOpen){
+ aspl.parser.utils.syntax_error("Expected '[' after list type", tokens.peek().location)
+ }
+ tokens.shift()
+ var list values = []
+ while(tokens.peek().type != TokenType.BracketClose){
+ var v = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, type))
+ if(!Type:matches(type, v.getType())){
+ aspl.parser.utils.type_error("Cannot add a value of type '" + v.getType().toString() + "' to a list of with the value type '" + type.toString() + "'", v.location)
+ }
+ values.add(v)
+ if(tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }
+ }
+ tokens.shift()
+ return applyOperators(new ListLiteral(type, values, token.location), tokens, precedenceLevel)
+ }elseif(token.value == "map"){
+ if(tokens.peek().type != TokenType.LessThan){
+ aspl.parser.utils.syntax_error("Expected '<' after 'map'", tokens.peek().location)
+ }
+ tokens.shift()
+ var Types keyType = parseTypesIfAny(tokens)
+ if(tokens.peek().type != TokenType.Comma){
+ aspl.parser.utils.syntax_error("Expected ',' after key type", tokens.peek().location)
+ }
+ tokens.shift()
+ var Types valueType = parseTypesIfAny(tokens)
+ if(tokens.peek().type != TokenType.GreaterThan){
+ aspl.parser.utils.syntax_error("Expected '>' after value type", tokens.peek().location)
+ }
+ tokens.shift()
+ if(tokens.peek().type != TokenType.BraceOpen){
+ aspl.parser.utils.syntax_error("Expected '{' after map types", tokens.peek().location)
+ }
+ tokens.shift()
+ var list pairs = []
+ while(tokens.peek().type != TokenType.BraceClose){
+ var k = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, keyType))
+ if(tokens.peek().type != TokenType.Assign){
+ aspl.parser.utils.syntax_error("Expected '=>' after key in map declaration", tokens.peek().location)
+ }
+ tokens.shift()
+ var v = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, valueType))
+ if(!Type:matches(keyType, k.getType())){
+ aspl.parser.utils.type_error("Cannot add a key of type '" + k.getType().toString() + "' to a map of with the key type '" + keyType.toString() + "'", k.location)
+ }
+ if(!Type:matches(valueType, v.getType())){
+ aspl.parser.utils.type_error("Cannot add a value of type '" + v.getType().toString() + "' to a map of with the value type '" + valueType.toString() + "'", v.location)
+ }
+ pairs.add(new Pair(k, v))
+ if(tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }
+ }
+ tokens.shift()
+ return applyOperators(new MapLiteral(keyType, valueType, pairs, token.location), tokens, precedenceLevel)
+ }
+ if(Function:exists(IdentifierUtils:handleFunctionIdentifier(this, peekFunctionIdentifier(tokens, token).identifier)) && ((tokens.peek(peekFunctionIdentifier(tokens, token).tokenCount).type == TokenType.ParenthesisOpen) || ((tokens.peek(peekFunctionIdentifier(tokens, token).tokenCount).type == TokenType.Plus) && (tokens.peek(peekFunctionIdentifier(tokens, token).tokenCount + 1).type == TokenType.ParenthesisOpen)))){
+ var identifierFirstToken = tokens.peek()
+ var identifierData = parseFunctionIdentifier(tokens, token)
+ var func = Function:get(IdentifierUtils:handleFunctionIdentifier(this, identifierData.identifier))
+ var compileIdentifier = func.identifier
+ if(func oftype InternalFunction){
+ compileIdentifier = "_" + compileIdentifier
+ }
+ var newThread = false
+ if(tokens.peek().type == TokenType.Plus){
+ newThread = true
+ tokens.shift()
+ }
+ tokens.shift()
+ var list arguments = []
+ foreach(func.parameters as parameter){
+ if(tokens.peek().type == TokenType.ParenthesisClose){
+ if(!parameter.optional){
+ aspl.parser.utils.generic_error("Too few arguments (" + arguments.length + ") given for function '" + func.identifier + "' (expected " + func.minimumParameterCount + ")", tokens.peek().location)
+ }
+ break
+ }
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, parameter.types))
+ if(!Type:matches(parameter.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot pass a value of the type '" + value.getType().toString() + "' to a parameter of the type '" + parameter.types.toString() + "'", value.location)
+ }
+ arguments.add(value)
+ if(arguments.length < func.parameters.length && tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }
+ }
+ foreach(func.attributes as attribute){
+ if(attribute.attribute.identifier == "deprecated"){
+ if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){
+ aspl.parser.utils.warning("Function " + func.identifier + " is deprecated: " + attribute.arguments[0].getConstantValue(), identifierFirstToken.location)
+ }else{
+ aspl.parser.utils.warning("Function " + func.identifier + " is deprecated", identifierFirstToken.location)
+ }
+ }
+ }
+ if(func oftype CustomFunction && !func.isPublic && module.id != CustomFunction(func).module.id){
+ aspl.parser.utils.warning("Cannot call the private function '" + func.identifier + "' here", identifierFirstToken.location) // TODO: Make this a generic_error after the grace period
+ }
+ var node = new FunctionCallExpression(compileIdentifier, func, arguments, newThread, token.location)
+ if(tokens.peek().type != TokenType.ParenthesisClose){
+ if(arguments.length == 0 || tokens.peek().type == TokenType.Comma){
+ aspl.parser.utils.fatal_error("Too many arguments given for function '" + func.identifier + "' (expected " + func.parameters.length + ")", tokens.peek().location)
+ }else{
+ aspl.parser.utils.syntax_error("Expected ')' after arguments in function call", tokens.peek().location)
+ }
+ }else{
+ tokens.shift()
+ }
+ return applyOperators(node, tokens, precedenceLevel)
+ }elseif(currentClass != null && Method:exists(Class(currentClass).type, token.value) && ((tokens.peek().type == TokenType.ParenthesisOpen) || (tokens.length > 1 && (tokens.peek().type == TokenType.Plus) && (tokens.peek(1).type == TokenType.ParenthesisOpen)))){
+ var name = token
+ var newThread = false
+ if(tokens.peek().type == TokenType.Plus){
+ newThread = true
+ tokens.shift()
+ }
+ var m = Method:get(Class(currentClass).type, name.value)
+ tokens.shift()
+ var list arguments = []
+ foreach(m.parameters as parameter){
+ if(tokens.peek().type == TokenType.ParenthesisClose){
+ if(!parameter.optional){
+ if(m.isStatic){
+ aspl.parser.utils.generic_error("Too few arguments (" + arguments.length + ") given for method '" + m.type.toString() + ":" + m.name + "' (expected " + m.minimumParameterCount + ")", tokens.peek().location)
+ }else{
+ aspl.parser.utils.generic_error("Too few arguments (" + arguments.length + ") given for method '" + m.type.toString() + "." + m.name + "' (expected " + m.minimumParameterCount + ")", tokens.peek().location)
+ }
+ }
+ break
+ }
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, parameter.types))
+ if(!Type:matches(parameter.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot pass a value of the type '" + value.getType().toString() + "' to a parameter of the type '" + parameter.types.toString() + "'", value.location)
+ }
+ arguments.add(value)
+ if(arguments.length < m.parameters.length && tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }
+ }
+ foreach(m.attributes as attribute){
+ if(attribute.attribute.identifier == "deprecated"){
+ if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){
+ if(m.isStatic){
+ aspl.parser.utils.warning("Method " + m.type.toString() + ":" + m.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), name.location)
+ }else{
+ aspl.parser.utils.warning("Method " + m.type.toString() + "." + m.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), name.location)
+ }
+ }else{
+ if(m.isStatic){
+ aspl.parser.utils.warning("Method " + m.type.toString() + ":" + m.name + " is deprecated", name.location)
+ }else{
+ aspl.parser.utils.warning("Method " + m.type.toString() + "." + m.name + " is deprecated", name.location)
+ }
+ }
+ }
+ }
+ var Expression? node = null
+ if(m.isStatic){
+ node = new StaticMethodCallExpression(m, Class(currentClass).type, arguments, newThread, token.location)
+ }else{
+ Scope:passCapturedVariables(["this"])
+ node = new NonStaticMethodCallExpression(m, new ThisExpression(Class(currentClass), token.location), null, arguments, newThread, token.location)
+ }
+ if(tokens.peek().type != TokenType.ParenthesisClose){
+ if(arguments.length == 0 || tokens.peek().type == TokenType.Comma){
+ if(m.isStatic){
+ aspl.parser.utils.fatal_error("Too many arguments given for method '" + m.type.toString() + ":" + m.name + "' (expected " + m.parameters.length + ")", tokens.peek().location)
+ }else{
+ aspl.parser.utils.fatal_error("Too many arguments given for method '" + m.type.toString() + "." + m.name + "' (expected " + m.parameters.length + ")", tokens.peek().location)
+ }
+ }else{
+ aspl.parser.utils.syntax_error("Expected ')' after arguments in method call", tokens.peek().location)
+ }
+ }else{
+ tokens.shift()
+ }
+ return applyOperators(Expression(node), tokens, precedenceLevel)
+ }elseif(currentClass != null && Property:exists(Class(currentClass).type, token.value)){
+ var p = Property:get(Class(currentClass).type, token.value)
+ if(!tokens.empty()){
+ if(tokens.peek().type == TokenType.Equals){
+ tokens.shift()
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, p.types))
+ if(!Type:matches(p.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + p.types.toString() + "'", value.location)
+ }
+ foreach(p.attributes as attribute){
+ if(attribute.attribute.identifier == "deprecated"){
+ if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){
+ if(p.isStatic){
+ aspl.parser.utils.warning("Property " + p.type.toString() + ":" + p.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), token.location)
+ }else{
+ aspl.parser.utils.warning("Property " + p.type.toString() + "." + p.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), token.location)
+ }
+ }else{
+ if(p.isStatic){
+ aspl.parser.utils.warning("Property " + p.type.toString() + ":" + p.name + " is deprecated", token.location)
+ }else{
+ aspl.parser.utils.warning("Property " + p.type.toString() + "." + p.name + " is deprecated", token.location)
+ }
+ }
+ }
+ }
+ if(p.isStatic){
+ return applyOperators(new StaticPropertyAssignExpression(p, p.type, value, token.location), tokens, precedenceLevel)
+ }else{
+ Scope:passCapturedVariables(["this"])
+ return applyOperators(new NonStaticPropertyAssignExpression(p, new ThisExpression(Class(currentClass), token.location), value, token.location), tokens, precedenceLevel)
+ }
+ }
+ }
+ foreach(p.attributes as attribute){
+ if(attribute.attribute.identifier == "deprecated"){
+ if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){
+ if(p.isStatic){
+ aspl.parser.utils.warning("Property " + p.type.toString() + "." + p.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), token.location)
+ }else{
+ aspl.parser.utils.warning("Property " + p.type.toString() + "." + p.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), token.location)
+ }
+ }else{
+ if(p.isStatic){
+ aspl.parser.utils.warning("Property " + p.type.toString() + "." + p.name + " is deprecated", token.location)
+ }else{
+ aspl.parser.utils.warning("Property " + p.type.toString() + "." + p.name + " is deprecated", token.location)
+ }
+ }
+ }
+ }
+ if(p.isStatic){
+ return applyOperators(new StaticPropertyAccessExpression(p, Class(currentClass).type, token.location), tokens, precedenceLevel)
+ }else{
+ Scope:passCapturedVariables(["this"])
+ return applyOperators(new NonStaticPropertyAccessExpression(p, new ThisExpression(Class(currentClass), token.location), token.location), tokens, precedenceLevel)
+ }
+ }
+ }elseif(token.type == TokenType.ParenthesisOpen){
+ var result = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None))
+ if(tokens.peek().type == TokenType.ParenthesisClose){
+ tokens.shift()
+ return applyOperators(result, tokens, precedenceLevel)
+ }else{
+ aspl.parser.utils.syntax_error("Expected closing parenthesis", tokens.peek().location)
+ }
+ }elseif(token.type == TokenType.CheckEquals){
+ return applyOperators(new CheckEqualsExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.CheckEquals)), token.location), tokens, precedenceLevel)
+ }elseif(token.type == TokenType.CheckNotEquals){
+ return applyOperators(new NegateExpression(new CheckEqualsExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.CheckNotEquals)), token.location), token.location), tokens, precedenceLevel)
+ }elseif(token.type == TokenType.Negate){
+ if(previousExpression == null){
+ return applyOperators(new NegateExpression(aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Negate, null, new Types([Type:fromString("bool")]))), token.location), tokens, precedenceLevel)
+ }
+ if(!Options:enableErrorHandling){
+ aspl.parser.utils.generic_error("Experimental error handling ('-enableErrorHandling') is not enabled for this build", token.location)
+ }
+ if(!ErrorUtils:canExpressionThrow(previousExpression)){
+ aspl.parser.utils.syntax_error("Cannot propagate an error from an expression that is not error-prone", token.location)
+ }
+ if(!ErrorUtils:canCallableThrow(Scope:getCurrentBundle().func)){
+ aspl.parser.utils.syntax_error("Cannot propagate an error in a callable that does not declare the [throws] attribute", token.location)
+ }
+ return applyOperators(new PropagateErrorExpression(previousExpression, token.location), tokens, precedenceLevel)
+ }elseif(token.type == TokenType.And){
+ var a = Expression(previousExpression)
+ var b = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.And))
+ if(a.getType().toString() != b.getType().toString()){
+ aspl.parser.utils.type_error("Cannot use the '&&' (and) operator on values of the types " + a.getType().toString() + " and " + b.getType().toString(), token.location)
+ }
+ if(Enum:enums.containsKey(a.getType().toString())){
+ if(!Enum:enums[a.getType().toString()].isFlags){
+ aspl.parser.utils.type_error("The '&&' (and) operator can only be used with enums that have the 'flags' attribute", token.location)
+ }
+ }
+ return applyOperators(new AndExpression(a, b, token.location), tokens, precedenceLevel)
+ }elseif(token.type == TokenType.Or){
+ var a = Expression(previousExpression)
+ var b = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Or))
+ if(a.getType().toString() != b.getType().toString()){
+ aspl.parser.utils.type_error("Cannot use the '||' (or) operator on values of the types " + a.getType().toString() + " and " + b.getType().toString(), token.location)
+ }
+ if(Enum:enums.containsKey(a.getType().toString())){
+ if(!Enum:enums[a.getType().toString()].isFlags){
+ aspl.parser.utils.type_error("The '||' (or) operator can only be used with enums that have the 'flags' attribute", token.location)
+ }
+ }
+ return applyOperators(new OrExpression(a, b, token.location), tokens, precedenceLevel)
+ }elseif(token.type == TokenType.Xor){
+ var a = Expression(previousExpression)
+ var b = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Xor))
+ if(a.getType().toString() != b.getType().toString()){
+ aspl.parser.utils.type_error("Cannot use the '^' (xpr) operator on values of the types " + a.getType().toString() + " and " + b.getType().toString(), token.location)
+ }
+ if(Enum:enums.containsKey(a.getType().toString())){
+ if(!Enum:enums[a.getType().toString()].isFlags){
+ aspl.parser.utils.type_error("The '^' (xor) operator can only be used with enums that have the 'flags' attribute", token.location)
+ }
+ }
+ return applyOperators(new XorExpression(a, b, token.location), tokens, precedenceLevel)
+ }elseif(token.type == TokenType.Plus){
+ if(previousExpression == null){
+ return applyOperators(new PlusExpression(new IntegerLiteral(0, null), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Plus)), token.location), tokens, precedenceLevel)
+ }
+ return applyOperators(new PlusExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Plus)), token.location), tokens, precedenceLevel)
+ }elseif(token.type == TokenType.PlusEquals){
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens))
+ if(previousExpression oftype VariableAccessExpression){
+ if(!Type:matches(VariableAccessExpression(previousExpression).variable.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a variable of type '" + VariableAccessExpression(previousExpression).variable.types.toString() + "'", value.location)
+ }
+ return applyOperators(new VariableAssignExpression(VariableAccessExpression(previousExpression).variable, new PlusExpression(new VariableAccessExpression(VariableAccessExpression(previousExpression).variable, token.location), value, token.location), token.location), tokens, precedenceLevel)
+ }elseif(previousExpression oftype NonStaticPropertyAccessExpression){
+ if(!Type:matches(NonStaticPropertyAccessExpression(previousExpression).p.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + NonStaticPropertyAccessExpression(previousExpression).p.types.toString() + "'", value.location)
+ }
+ if(NonStaticPropertyAccessExpression(previousExpression).p.isReadPublic){
+ if(currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type]))){
+ aspl.parser.utils.warning("Cannot assign to the property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ }elseif(!NonStaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type])))){
+ aspl.parser.utils.warning("Cannot assign to the private property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ return applyOperators(new NonStaticPropertyAssignExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, new PlusExpression(new NonStaticPropertyAccessExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, token.location), value, token.location), token.location), tokens, precedenceLevel)
+ }elseif(previousExpression oftype StaticPropertyAccessExpression){
+ if(!Type:matches(StaticPropertyAccessExpression(previousExpression).p.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + StaticPropertyAccessExpression(previousExpression).p.types.toString() + "'", value.location)
+ }
+ if(!Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].isPublic && module.id != Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].module.id){
+ aspl.parser.utils.warning("Cannot assign to a property from the private class '" + Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ if(StaticPropertyAccessExpression(previousExpression).p.isReadPublic){
+ if(currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type]))){
+ aspl.parser.utils.warning("Cannot assign to the property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ }elseif(!StaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type])))){
+ aspl.parser.utils.warning("Cannot assign to the private property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ return applyOperators(new StaticPropertyAssignExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, new PlusExpression(new StaticPropertyAccessExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, token.location), value, token.location), token.location), tokens, precedenceLevel)
+ }
+ aspl.parser.utils.syntax_error("The '+=' operator must be used on something assignable, e.g. a variable", token.location)
+ }elseif(token.type == TokenType.PlusPlus){
+ if(previousExpression oftype VariableAccessExpression){
+ return applyOperators(new VariableAssignExpression(VariableAccessExpression(previousExpression).variable, new PlusExpression(new VariableAccessExpression(VariableAccessExpression(previousExpression).variable, token.location), new IntegerLiteral(1, token.location), token.location), token.location), tokens, precedenceLevel)
+ }elseif(previousExpression oftype NonStaticPropertyAccessExpression){
+ if(NonStaticPropertyAccessExpression(previousExpression).p.isReadPublic){
+ if(currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type]))){
+ aspl.parser.utils.warning("Cannot assign to the property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ }elseif(!NonStaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type])))){
+ aspl.parser.utils.warning("Cannot assign to the private property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ return applyOperators(new NonStaticPropertyAssignExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, new PlusExpression(new NonStaticPropertyAccessExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, token.location), new IntegerLiteral(1, token.location), token.location), token.location), tokens, precedenceLevel)
+ }elseif(previousExpression oftype StaticPropertyAccessExpression){
+ if(!Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].isPublic && module.id != Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].module.id){
+ aspl.parser.utils.warning("Cannot assign to a property from the private class '" + Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ if(StaticPropertyAccessExpression(previousExpression).p.isReadPublic && currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type]))){
+ aspl.parser.utils.warning("Cannot assign to the property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ if(!StaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type])))){
+ aspl.parser.utils.warning("Cannot assign to the private property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ return applyOperators(new StaticPropertyAssignExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, new PlusExpression(new StaticPropertyAccessExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, token.location), new IntegerLiteral(1, token.location), token.location), token.location), tokens, precedenceLevel)
+ }
+ aspl.parser.utils.syntax_error("The '++' operator must be used on something assignable, e.g. a variable", token.location)
+ }elseif(token.type == TokenType.Minus){
+ if(previousExpression == null){
+ return applyOperators(new MinusExpression(new IntegerLiteral(0, null), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Minus)), token.location), tokens, precedenceLevel)
+ }
+ return applyOperators(new MinusExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Minus)), token.location), tokens, precedenceLevel)
+ }elseif(token.type == TokenType.MinusEquals){
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens))
+ if(previousExpression oftype VariableAccessExpression){
+ if(!Type:matches(VariableAccessExpression(previousExpression).variable.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a variable of type '" + VariableAccessExpression(previousExpression).variable.types.toString() + "'", value.location)
+ }
+ return applyOperators(new VariableAssignExpression(VariableAccessExpression(previousExpression).variable, new MinusExpression(new VariableAccessExpression(VariableAccessExpression(previousExpression).variable, token.location), value, token.location), token.location), tokens, precedenceLevel)
+ }elseif(previousExpression oftype NonStaticPropertyAccessExpression){
+ if(!Type:matches(NonStaticPropertyAccessExpression(previousExpression).p.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + NonStaticPropertyAccessExpression(previousExpression).p.types.toString() + "'", value.location)
+ }
+ if(NonStaticPropertyAccessExpression(previousExpression).p.isReadPublic){
+ if(currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type]))){
+ aspl.parser.utils.warning("Cannot assign to the property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ }elseif(!NonStaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type])))){
+ aspl.parser.utils.warning("Cannot assign to the private property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ return applyOperators(new NonStaticPropertyAssignExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, new MinusExpression(new NonStaticPropertyAccessExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, token.location), value, token.location), token.location), tokens, precedenceLevel)
+ }elseif(previousExpression oftype StaticPropertyAccessExpression){
+ if(!Type:matches(StaticPropertyAccessExpression(previousExpression).p.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + StaticPropertyAccessExpression(previousExpression).p.types.toString() + "'", value.location)
+ }
+ if(!Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].isPublic && module.id != Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].module.id){
+ aspl.parser.utils.warning("Cannot assign to a property from the private class '" + Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ if(StaticPropertyAccessExpression(previousExpression).p.isReadPublic && currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type]))){
+ aspl.parser.utils.warning("Cannot assign to the property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ if(!StaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type])))){
+ aspl.parser.utils.warning("Cannot assign to the private property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ return applyOperators(new StaticPropertyAssignExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, new MinusExpression(new StaticPropertyAccessExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, token.location), value, token.location), token.location), tokens, precedenceLevel)
+ }
+ aspl.parser.utils.syntax_error("The '-=' operator must be used on something assignable, e.g. a variable", token.location)
+ }elseif(token.type == TokenType.MinusMinus){
+ if(previousExpression oftype VariableAccessExpression){
+ return applyOperators(new VariableAssignExpression(VariableAccessExpression(previousExpression).variable, new MinusExpression(new VariableAccessExpression(VariableAccessExpression(previousExpression).variable, token.location), new IntegerLiteral(1, token.location), token.location), token.location), tokens, precedenceLevel)
+ }elseif(previousExpression oftype NonStaticPropertyAccessExpression){
+ if(NonStaticPropertyAccessExpression(previousExpression).p.isReadPublic){
+ if(currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type]))){
+ aspl.parser.utils.warning("Cannot assign to the property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ }elseif(!NonStaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type])))){
+ aspl.parser.utils.warning("Cannot assign to the private property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ return applyOperators(new NonStaticPropertyAssignExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, new MinusExpression(new NonStaticPropertyAccessExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, token.location), new IntegerLiteral(1, token.location), token.location), token.location), tokens, precedenceLevel)
+ }elseif(previousExpression oftype StaticPropertyAccessExpression){
+ if(!Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].isPublic && module.id != Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].module.id){
+ aspl.parser.utils.warning("Cannot assign to a property from the private class '" + Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ if(StaticPropertyAccessExpression(previousExpression).p.isReadPublic && currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type]))){
+ aspl.parser.utils.warning("Cannot assign to the property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ if(!StaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type])))){
+ aspl.parser.utils.warning("Cannot assign to the private property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ return applyOperators(new StaticPropertyAssignExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, new MinusExpression(new StaticPropertyAccessExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, token.location), new IntegerLiteral(1, token.location), token.location), token.location), tokens, precedenceLevel)
+ }
+ aspl.parser.utils.syntax_error("The '--' operator must be used on something assignable, e.g. a variable", token.location)
+ }elseif(token.type == TokenType.Asterisk){
+ if(previousExpression == null){
+ var pointer = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Reference))
+ if(!pointer.getType().isPointer()){
+ aspl.parser.utils.syntax_error("Cannot dereference a value of type '" + pointer.getType().toString() + "' (not a pointer)", pointer.location)
+ }
+ return applyOperators(new DereferenceExpression(pointer, token.location), tokens, precedenceLevel)
+ }
+ return applyOperators(new MultiplyExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Asterisk)), token.location), tokens, precedenceLevel)
+ }elseif(token.type == TokenType.MultiplyEquals){
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens))
+ if(previousExpression oftype VariableAccessExpression){
+ if(!Type:matches(VariableAccessExpression(previousExpression).variable.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a variable of type '" + VariableAccessExpression(previousExpression).variable.types.toString() + "'", value.location)
+ }
+ return applyOperators(new VariableAssignExpression(VariableAccessExpression(previousExpression).variable, new MultiplyExpression(new VariableAccessExpression(VariableAccessExpression(previousExpression).variable, token.location), value, token.location), token.location), tokens, precedenceLevel)
+ }elseif(previousExpression oftype NonStaticPropertyAccessExpression){
+ if(!Type:matches(NonStaticPropertyAccessExpression(previousExpression).p.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + NonStaticPropertyAccessExpression(previousExpression).p.types.toString() + "'", value.location)
+ }
+ if(NonStaticPropertyAccessExpression(previousExpression).p.isReadPublic){
+ if(currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type]))){
+ aspl.parser.utils.warning("Cannot assign to the property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ }elseif(!NonStaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type])))){
+ aspl.parser.utils.warning("Cannot assign to the private property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ return applyOperators(new NonStaticPropertyAssignExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, new MultiplyExpression(new NonStaticPropertyAccessExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, token.location), value, token.location), token.location), tokens, precedenceLevel)
+ }elseif(previousExpression oftype StaticPropertyAccessExpression){
+ if(!Type:matches(StaticPropertyAccessExpression(previousExpression).p.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + StaticPropertyAccessExpression(previousExpression).p.types.toString() + "'", value.location)
+ }
+ if(!Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].isPublic && module.id != Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].module.id){
+ aspl.parser.utils.warning("Cannot assign to a property from the private class '" + Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ if(StaticPropertyAccessExpression(previousExpression).p.isReadPublic && currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type]))){
+ aspl.parser.utils.warning("Cannot assign to the property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ if(!StaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type])))){
+ aspl.parser.utils.warning("Cannot assign to the private property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ return applyOperators(new StaticPropertyAssignExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, new MultiplyExpression(new StaticPropertyAccessExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, token.location), value, token.location), token.location), tokens, precedenceLevel)
+ }
+ aspl.parser.utils.syntax_error("The '*=' operator must be used on something assignable, e.g. a variable", token.location)
+ }elseif(token.type == TokenType.Slash){
+ return applyOperators(new DivideExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Slash)), token.location), tokens, precedenceLevel)
+ }elseif(token.type == TokenType.DivideEquals){
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens))
+ if(previousExpression oftype VariableAccessExpression){
+ if(!Type:matches(VariableAccessExpression(previousExpression).variable.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a variable of type '" + Variable:get(token.value).types.toString() + "'", value.location)
+ }
+ return applyOperators(new VariableAssignExpression(VariableAccessExpression(previousExpression).variable, new DivideExpression(new VariableAccessExpression(VariableAccessExpression(previousExpression).variable, token.location), value, token.location), token.location), tokens, precedenceLevel)
+ }elseif(previousExpression oftype NonStaticPropertyAccessExpression){
+ if(!Type:matches(NonStaticPropertyAccessExpression(previousExpression).p.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + NonStaticPropertyAccessExpression(previousExpression).p.types.toString() + "'", value.location)
+ }
+ if(NonStaticPropertyAccessExpression(previousExpression).p.isReadPublic){
+ if(currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type]))){
+ aspl.parser.utils.warning("Cannot assign to the property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ }elseif(!NonStaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type])))){
+ aspl.parser.utils.warning("Cannot assign to the private property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ return applyOperators(new NonStaticPropertyAssignExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, new DivideExpression(new NonStaticPropertyAccessExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, token.location), value, token.location), token.location), tokens, precedenceLevel)
+ }elseif(previousExpression oftype StaticPropertyAccessExpression){
+ if(!Type:matches(StaticPropertyAccessExpression(previousExpression).p.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + StaticPropertyAccessExpression(previousExpression).p.types.toString() + "'", value.location)
+ }
+ if(!Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].isPublic && module.id != Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].module.id){
+ aspl.parser.utils.warning("Cannot assign to a property from the private class '" + Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ if(StaticPropertyAccessExpression(previousExpression).p.isReadPublic && currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type]))){
+ aspl.parser.utils.warning("Cannot assign to the property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ if(!StaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type])))){
+ aspl.parser.utils.warning("Cannot assign to the private property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ return applyOperators(new StaticPropertyAssignExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, new DivideExpression(new StaticPropertyAccessExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, token.location), value, token.location), token.location), tokens, precedenceLevel)
+ }
+ aspl.parser.utils.syntax_error("The '/=' operator must be used on something assignable, e.g. a variable", token.location)
+ }elseif(token.type == TokenType.Modulo){
+ return applyOperators(new ModuloExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Modulo)), token.location), tokens, precedenceLevel)
+ }elseif(token.type == TokenType.ModuloEquals){
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens))
+ if(previousExpression oftype VariableAccessExpression){
+ if(!Type:matches(VariableAccessExpression(previousExpression).variable.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a variable of type '" + Variable:get(token.value).types.toString() + "'", value.location)
+ }
+ return applyOperators(new VariableAssignExpression(VariableAccessExpression(previousExpression).variable, new ModuloExpression(new VariableAccessExpression(VariableAccessExpression(previousExpression).variable, token.location), value, token.location), token.location), tokens, precedenceLevel)
+ }elseif(previousExpression oftype NonStaticPropertyAccessExpression){
+ if(!Type:matches(NonStaticPropertyAccessExpression(previousExpression).p.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + NonStaticPropertyAccessExpression(previousExpression).p.types.toString() + "'", value.location)
+ }
+ if(NonStaticPropertyAccessExpression(previousExpression).p.isReadPublic){
+ if(currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type]))){
+ aspl.parser.utils.warning("Cannot assign to the property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ }elseif(!NonStaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([NonStaticPropertyAccessExpression(previousExpression).p.type])))){
+ aspl.parser.utils.warning("Cannot assign to the private property " + NonStaticPropertyAccessExpression(previousExpression).p.type.identifier + "." + NonStaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ return applyOperators(new NonStaticPropertyAssignExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, new ModuloExpression(new NonStaticPropertyAccessExpression(NonStaticPropertyAccessExpression(previousExpression).p, NonStaticPropertyAccessExpression(previousExpression).base, token.location), value, token.location), token.location), tokens, precedenceLevel)
+ }elseif(previousExpression oftype StaticPropertyAccessExpression){
+ if(!Type:matches(StaticPropertyAccessExpression(previousExpression).p.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + StaticPropertyAccessExpression(previousExpression).p.types.toString() + "'", value.location)
+ }
+ if(!Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].isPublic && module.id != Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].module.id){
+ aspl.parser.utils.warning("Cannot assign to a property from the private class '" + Class:classes[StaticPropertyAccessExpression(previousExpression).p.type.toString()].type.identifier + "' here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ if(StaticPropertyAccessExpression(previousExpression).p.isReadPublic && currentClass != null && !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type]))){
+ aspl.parser.utils.warning("Cannot assign to the property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ if(!StaticPropertyAccessExpression(previousExpression).p.isPublic && (currentClass == null || !Type:matches(new Types([currentClass?!.type]), new Types([StaticPropertyAccessExpression(previousExpression).p.type])))){
+ aspl.parser.utils.warning("Cannot assign to the private property " + StaticPropertyAccessExpression(previousExpression).p.type.identifier + ":" + StaticPropertyAccessExpression(previousExpression).p.name + " here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ return applyOperators(new StaticPropertyAssignExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, new ModuloExpression(new StaticPropertyAccessExpression(StaticPropertyAccessExpression(previousExpression).p, StaticPropertyAccessExpression(previousExpression).base, token.location), value, token.location), token.location), tokens, precedenceLevel)
+ }
+ aspl.parser.utils.syntax_error("The '%=' operator must be used on something assignable, e.g. a variable", token.location)
+ }elseif(token.type == TokenType.LessThan){
+ return applyOperators(new LessThanExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.LessThan)), token.location), tokens, precedenceLevel)
+ }elseif(token.type == TokenType.LessThanOrEqual){
+ return applyOperators(new LessThanOrEqualExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.LessThanOrEqual)), token.location), tokens, precedenceLevel)
+ }elseif(token.type == TokenType.GreaterThan){
+ return applyOperators(new GreaterThanExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.GreaterThan)), token.location), tokens, precedenceLevel)
+ }elseif(token.type == TokenType.GreaterThanOrEqual){
+ return applyOperators(new GreaterThanOrEqualExpression(Expression(previousExpression), aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.GreaterThanOrEqual)), token.location), tokens, precedenceLevel)
+ }elseif(token.type == TokenType.Ampersand){
+ if(previousExpression == null){
+ var expression = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.Reference))
+ if(!(expression oftype VariableAccessExpression || expression oftype NonStaticPropertyAccessExpression || expression oftype StaticPropertyAccessExpression)){
+ aspl.parser.utils.syntax_error("The '&' operator must be used on something holding an object reference, e.g. a variable", token.location)
+ }
+ return applyOperators(new ReferenceExpression(expression, token.location), tokens, precedenceLevel)
+ }
+ aspl.parser.utils.syntax_error("Unxpected '&'", token.location)
+ }elseif(token.type == TokenType.Dot){
+ if(previousExpression == null){
+ aspl.parser.utils.syntax_error("Unexpected '" + token.value + "'", token.location)
+ }
+ var name = tokens.next()
+ if(name.type == TokenType.ParenthesisOpen || (tokens.length > 0 && name.type == TokenType.Plus && tokens.peek().type == TokenType.ParenthesisOpen)){
+ if(!Type:matches(new Types([Type:fromString("callback")]), Expression(previousExpression).getType(), true)){
+ aspl.parser.utils.syntax_error("Only callbacks can be invoked using the .(...) syntax", token.location)
+ }
+ var newThread = false
+ if(name.type == TokenType.Plus){
+ newThread = true
+ tokens.shift()
+ }
+ var m = Method:get(Expression(previousExpression).getType().toType(), "invoke")
+ var list arguments = []
+ foreach(m.parameters as parameter){
+ if(tokens.peek().type == TokenType.ParenthesisClose){
+ if(!parameter.optional){
+ aspl.parser.utils.generic_error("Too few arguments (" + arguments.length + ") given for method '" + m.type.toString() + ".invoke' (expected " + m.minimumParameterCount + ")", tokens.peek().location)
+ }
+ break
+ }
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, parameter.types))
+ if(!Type:matches(parameter.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot pass a value of the type '" + value.getType().toString() + "' to a parameter of the type '" + parameter.types.toString() + "'", value.location)
+ }
+ arguments.add(value)
+ if(arguments.length < m.parameters.length && tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }
+ }
+ var node = new NonStaticMethodCallExpression(m, Expression(previousExpression), null, arguments, newThread, token.location)
+ if(tokens.peek().type != TokenType.ParenthesisClose){
+ if(arguments.length == 0 || tokens.peek().type == TokenType.Comma){
+ aspl.parser.utils.fatal_error("Too many arguments (" + arguments.length + ") given for method '" + m.type.toString() + ".invoke' (expected " + m.parameters.length + ")", tokens.peek().location)
+ }else{
+ aspl.parser.utils.syntax_error("Expected ')' after arguments in callback invocation", tokens.peek().location)
+ }
+ }else{
+ tokens.shift()
+ }
+ return applyOperators(node, tokens, precedenceLevel)
+ }
+ if(name.type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected identifier after '.'", name.location)
+ }
+ if(tokens.length > 0 && (tokens.peek().type == TokenType.ParenthesisOpen || (tokens.length > 1 && tokens.peek().type == TokenType.Plus && tokens.peek(1).type == TokenType.ParenthesisOpen))){
+ if(!Method:exists(Expression(previousExpression).getType().toType(), name.value)){
+ aspl.parser.utils.fatal_error("Unknown method " + Expression(previousExpression).getType().toType().toString() + "." + name.value, name.location)
+ }
+ var newThread = false
+ if(tokens.peek().type == TokenType.Plus){
+ newThread = true
+ tokens.shift()
+ }
+ var m = Method:get(Expression(previousExpression).getType().toType(), name.value)
+ if(m.isStatic){
+ aspl.parser.utils.type_error("Cannot call the static method " + Expression(previousExpression).getType().toType().toString() + ":" + name.value + " non-statically", name.location)
+ }
+ tokens.shift()
+ var list arguments = []
+ foreach(m.parameters as parameter){
+ if(tokens.peek().type == TokenType.ParenthesisClose){
+ if(!parameter.optional){
+ aspl.parser.utils.generic_error("Too few arguments (" + arguments.length + ") given for method '" + m.type.toString() + "." + m.name + "' (expected " + m.parameters.length + ")", tokens.peek().location)
+ }
+ break
+ }
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, parameter.types))
+ if(!Type:matches(parameter.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot pass a value of the type '" + value.getType().toString() + "' to a parameter of the type '" + parameter.types.toString() + "'", value.location)
+ }
+ arguments.add(value)
+ if(arguments.length < m.parameters.length && tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }
+ }
+ foreach(m.attributes as attribute){
+ if(attribute.attribute.identifier == "deprecated"){
+ if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){
+ aspl.parser.utils.warning("Method " + m.type.toString() + "." + m.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), name.location)
+ }else{
+ aspl.parser.utils.warning("Method " + m.type.toString() + "." + m.name + " is deprecated", name.location)
+ }
+ }
+ }
+ if(m oftype CustomMethod && !m.isPublic && (currentClass == null || !Type:matches(new Types([m.type]), new Types([currentClass?!.type])))){
+ aspl.parser.utils.warning("Cannot call the private method '" + m.type.identifier + "." + m.name + "' here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ var Class? exactClass = null
+ if(previousExpression oftype ParentExpression){
+ exactClass = ParentExpression(previousExpression).c
+ }
+ var node = new NonStaticMethodCallExpression(m, Expression(previousExpression), exactClass, arguments, newThread, token.location)
+ if(tokens.peek().type != TokenType.ParenthesisClose){
+ if(arguments.length == 0 || tokens.peek().type == TokenType.Comma){
+ aspl.parser.utils.fatal_error("Too many arguments (" + arguments.length + ") given for method '" + m.type.toString() + "." + m.name + "' (expected " + m.parameters.length + ")", tokens.peek().location)
+ }else{
+ aspl.parser.utils.syntax_error("Expected ')' after arguments in method call", tokens.peek().location)
+ }
+ }else{
+ tokens.shift()
+ }
+ return applyOperators(node, tokens, precedenceLevel)
+ }else{
+ if(!Property:exists(Expression(previousExpression).getType().toType(), name.value)){
+ aspl.parser.utils.fatal_error("Unknown property " + Expression(previousExpression).getType().toType().toString() + "." + name.value + "", name.location)
+ }
+ var p = Property:get(Expression(previousExpression).getType().toType(), name.value)
+ if(p.isStatic){
+ aspl.parser.utils.type_error("Cannot access the static property " + p.type.toString() + ":" + p.name + " non-statically", name.location)
+ }
+ if(!tokens.empty()){
+ if(tokens.peek().type == TokenType.Equals){
+ tokens.shift()
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, p.types))
+ if(!Type:matches(p.types, value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a property of type '" + p.types.toString() + "'", value.location)
+ }
+ foreach(p.attributes as attribute){
+ if(attribute.attribute.identifier == "deprecated"){
+ if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){
+ aspl.parser.utils.warning("Property " + p.type.toString() + "." + p.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), name.location)
+ }else{
+ aspl.parser.utils.warning("Property " + p.type.toString() + "." + p.name + " is deprecated", name.location)
+ }
+ }
+ }
+ if(p.isReadPublic){
+ if(currentClass != null && !Type:matches(new Types([p.type]), new Types([currentClass?!.type]))){
+ aspl.parser.utils.warning("Cannot assign to the property " + p.type.identifier + "." + p.name + " here; it is read-only from outside the class it was defined in", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ }elseif(!p.isPublic && (currentClass == null || !Type:matches(new Types([p.type]), new Types([currentClass?!.type])))){
+ aspl.parser.utils.warning("Cannot assign to the private property " + p.type.identifier + "." + p.name + " here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ return applyOperators(new NonStaticPropertyAssignExpression(p, Expression(previousExpression), value, token.location), tokens, precedenceLevel)
+ }
+ }
+ foreach(p.attributes as attribute){
+ if(attribute.attribute.identifier == "deprecated"){
+ if(attribute.arguments.length > 0 && attribute.arguments[0].getConstantValue() != null){
+ aspl.parser.utils.warning("Property " + p.type.toString() + "." + p.name + " is deprecated: " + attribute.arguments[0].getConstantValue(), name.location)
+ }else{
+ aspl.parser.utils.warning("Property " + p.type.toString() + "." + p.name + " is deprecated", name.location)
+ }
+ }
+ }
+ if(!p.isPublic && !p.isReadPublic && (currentClass == null || !Type:matches(new Types([p.type]), new Types([currentClass?!.type])))){
+ aspl.parser.utils.warning("Cannot access the private property " + p.type.identifier + "." + p.name + " here", token.location) // TODO: Make this a generic_error after the grace period
+ }
+ return applyOperators(new NonStaticPropertyAccessExpression(p, Expression(previousExpression), token.location), tokens, precedenceLevel)
+ }
+ }elseif(token.type == TokenType.BracketOpen){
+ if(AttributeUtils:parseAttributesIfAny(this, token, tokens)){
+ return new NopStatement()
+ }
+ if(previousExpression != null){
+ if(Type:matches(new Types([Type:fromString("list")]), Expression(previousExpression).getType(), true)){
+ var index = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("integer")])))
+ if(!Type:matches(new Types([Type:fromString("integer")]), index.getType())){
+ aspl.parser.utils.syntax_error("Cannot index a list with a value of type '" + index.getType().toString() + "' (must be integer)", index.location)
+ }
+ if(tokens.peek().type == TokenType.BracketClose){
+ tokens.shift()
+ }else{
+ aspl.parser.utils.syntax_error("Expected ']' after index", tokens.peek().location)
+ }
+ if(tokens.peek().type == TokenType.Equals){
+ tokens.shift()
+ var Types? types = null
+ foreach(Expression(previousExpression).getType().types as type){
+ if(Type:getGenericTypes(type.toString()).length > 0){
+ types = Type:getGenericTypes(type.toString())[0]
+ }
+ }
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, Types(types)))
+ foreach(Expression(previousExpression).getType().types as type){
+ if(Type:getGenericTypes(type.toString()).length > 0){
+ if(!Type:matches(Type:getGenericTypes(type.toString())[0], value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a list of the type '" + type.toString() + "'", value.location)
+ }
+ }
+ }
+ return applyOperators(new ListAssignExpression(Expression(previousExpression), index, value, token.location), tokens, precedenceLevel)
+ }
+ return applyOperators(new ListIndexExpression(Expression(previousExpression), index, token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(new Types([Type:fromString("map")]), Expression(previousExpression).getType(), true)){
+ var k = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens))
+ foreach(Expression(previousExpression).getType().types as type){
+ if(Type:getGenericTypes(type.toString()).length > 0){
+ if(!Type:matches(Type:getGenericTypes(type.toString())[0], k.getType())){
+ aspl.parser.utils.type_error("Cannot access a map with the key type '" + type.toString() + "' with a key of the type '" + k.getType().toString() + "'", k.location)
+ }
+ }
+ }
+ if(tokens.peek().type == TokenType.BracketClose){
+ tokens.shift()
+ }else{
+ aspl.parser.utils.syntax_error("Expected ']' after map access", tokens.peek().location)
+ }
+ if(tokens.peek().type == TokenType.Equals){
+ tokens.shift()
+ var Types? types = null
+ foreach(Expression(previousExpression).getType().types as type){
+ if(Type:getGenericTypes(type.toString()).length > 0){
+ types = Type:getGenericTypes(type.toString())[1]
+ }
+ }
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, Types(types)))
+ foreach(Expression(previousExpression).getType().types as type){
+ if(Type:getGenericTypes(type.toString()).length > 1){
+ if(!Type:matches(Type:getGenericTypes(type.toString())[1], value.getType())){
+ aspl.parser.utils.type_error("Cannot assign a value of type '" + value.getType().toString() + "' to a map with the value type '" + type.toString() + "'", value.location)
+ }
+ }
+ }
+ return applyOperators(new MapAssignExpression(Expression(previousExpression), k, value, token.location), tokens, precedenceLevel)
+ }
+ return applyOperators(new MapAccessExpression(Expression(previousExpression), k, token.location), tokens, precedenceLevel)
+ }elseif(Type:matches(new Types([Type:fromString("string")]), Expression(previousExpression).getType())){
+ var index = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, new Types([Type:fromString("integer")])))
+ if(!Type:matches(new Types([Type:fromString("integer")]), index.getType())){
+ aspl.parser.utils.syntax_error("Cannot index a string with a value of type '" + index.getType().toString() + "' (must be integer)", index.location)
+ }
+ if(tokens.peek().type == TokenType.BracketClose){
+ tokens.shift()
+ }else{
+ aspl.parser.utils.syntax_error("Expected ']' after index", tokens.peek().location)
+ }
+ if(tokens.peek().type == TokenType.Equals){
+ aspl.parser.utils.syntax_error("Cannot modify a string", tokens.peek().location)
+ }
+ return applyOperators(new StringIndexExpression(Expression(previousExpression), index, token.location), tokens, precedenceLevel)
+ }else{
+ aspl.parser.utils.syntax_error("Cannot index a value of the type '" + Expression(previousExpression).getType().toString() + "'", token.location)
+ }
+ }
+ var Types? type = null
+ var list values = []
+ while(tokens.peek().type != TokenType.BracketClose){
+ var value = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, type))
+ if(type == null){
+ type = value.getType()
+ }
+ if(!Type:matches(type, value.getType())){
+ aspl.parser.utils.type_error("Cannot add a value of type '" + value.getType().toString() + "' to a list of type '" + Types(type).toString() + "'", value.location)
+ }
+ values.add(value)
+ if(tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }
+ }
+ if(type == null){
+ var found = false
+ if(expectedTypes != null){
+ foreach(Types(expectedTypes).types as t){
+ if(Type:matches(new Types([Type:fromString("list")]), new Types([t]), true)){
+ if(Type:getGenericTypes(t.toString()).length > 0){
+ type = Type:getGenericTypes(t.toString())[0]
+ }else{
+ type = new Types([])
+ }
+ found = true
+ break
+ }
+ }
+ }
+ if(!found){
+ aspl.parser.utils.type_error("List type not specified", token.location)
+ type = new Types([])
+ }
+ }
+ tokens.shift()
+ return applyOperators(new ListLiteral(Types(type), values, token.location), tokens, precedenceLevel)
+ }elseif(token.type == TokenType.BraceOpen){
+ if(standalone){
+ return new BlockStatement(parseBlock(tokens, token), false, token.location)
+ }else{
+ var Types? keyType = null
+ var Types? valueType = null
+ var list pairs = []
+ while(tokens.peek().type != TokenType.BraceClose){
+ var k = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, keyType))
+ if(keyType == null){
+ keyType = k.getType()
+ }
+ if(tokens.peek().type != TokenType.Assign){
+ aspl.parser.utils.syntax_error("Expected '=>' after key in map declaration", tokens.peek().location)
+ }
+ tokens.shift()
+ var v = aspl.parser.utils.verify_expression(parseToken(tokens.next(), tokens, false, PrecedenceLevel.None, null, valueType))
+ if(valueType == null){
+ valueType = v.getType()
+ }
+ if(!Type:matches(keyType, k.getType())){
+ aspl.parser.utils.type_error("Cannot add a key of type '" + k.getType().toString() + "' to a map of with the key type '" + Types(keyType).toString() + "'", k.location)
+ }
+ if(!Type:matches(valueType, v.getType())){
+ aspl.parser.utils.type_error("Cannot add a value of type '" + v.getType().toString() + "' to a map of with the value type '" + Types(valueType).toString() + "'", v.location)
+ }
+ pairs.add(new Pair(k, v))
+ if(tokens.peek().type == TokenType.Comma){
+ tokens.shift()
+ }
+ }
+ tokens.shift()
+ if(keyType == null){
+ var found = false
+ if(expectedTypes != null){
+ foreach(Types(expectedTypes).types as t){
+ if(Type:matches(new Types([Type:fromString("map")]), new Types([t]), true)){
+ if(Type:getGenericTypes(t.toString()).length > 0){
+ keyType = Type:getGenericTypes(t.toString())[0]
+ }else{
+ keyType = new Types([])
+ valueType = new Types([])
+ }
+ if(Type:getGenericTypes(t.toString()).length > 1){
+ valueType = Type:getGenericTypes(t.toString())[1]
+ }else{
+ valueType = new Types([])
+ }
+ found = true
+ break
+ }
+ }
+ }
+ if(!found){
+ aspl.parser.utils.type_error("Map key type not specified", token.location)
+ keyType = new Types([])
+ }
+ }
+ if(valueType == null){
+ aspl.parser.utils.type_error("Map value type not specified", token.location)
+ valueType = new Types([])
+ }
+ return applyOperators(new MapLiteral(keyType, valueType, pairs, token.location), tokens, precedenceLevel)
+ }
+ }elseif(token.type == TokenType.QuestionAndExclamationMark){
+ if(previousExpression == null){
+ aspl.parser.utils.syntax_error("Unexpected ?! (can only be used after nullable expressions)", token.location)
+ }
+ if(!Type:matches(Expression(previousExpression).getType(), new Types([Type:fromString("null")]))){
+ aspl.parser.utils.type_error("The ?! operator can only be used on nullable expressions (expression of type " + Expression(previousExpression).getType().toString() + " given)", token.location)
+ }
+ return applyOperators(new CastExpression(Expression(previousExpression), Expression(previousExpression).getType().withoutType(Type:fromString("null")).toType(), token.location), tokens, precedenceLevel)
+ }elseif(token.type == TokenType.Dollar){
+ if(tokens.peek().type == TokenType.Identifier){
+ if(tokens.peek().value == "if"){
+ tokens.shift()
+ var bool negate = false
+ if(tokens.peek().type == TokenType.Negate){
+ negate = true
+ tokens.shift()
+ }
+ if(tokens.peek().type != TokenType.Identifier){
+ aspl.parser.utils.syntax_error("Expected identifier after compile-time if ($if)", tokens.peek().location)
+ }
+ var string condition = tokens.next().value
+ if(tokens.peek().type != TokenType.BraceOpen){
+ aspl.parser.utils.syntax_error("Expected { after condition in compile-time if ($if)")
+ }
+ var brace = tokens.peek()
+ var block = parseBlock(tokens)
+ var list? elseBlock = null
+ if(!tokens.empty()){
+ if(tokens.peek().type == TokenType.Dollar){
+ if(tokens.length > 1 && tokens.peek(1).type == TokenType.Identifier && tokens.peek(1).value == "else"){
+ tokens.shift()
+ tokens.shift()
+ if(tokens.peek().type != TokenType.BraceOpen){
+ aspl.parser.utils.syntax_error("Expected { after else in compile-time if ($if)")
+ }
+ elseBlock = parseBlock(tokens)
+ }elseif(tokens.peek().value == "elseif"){
+ aspl.parser.utils.syntax_error("Compile-time elseif is not supported yet")
+ }
+ }elseif(tokens.peek().type == TokenType.Identifier && tokens.peek().value == "else"){
+ aspl.parser.utils.syntax_error("Use `$else` instead if regular `else` after compile-time if ($if)")
+ }
+ }
+ if((Options:getConditionCompilationSymbols().contains(condition) || (condition == "main" && module == Module:mainModule)) != negate){
+ return new BlockStatement(block, true, brace.location)
+ }
+ if(elseBlock != null){
+ return new BlockStatement(list(elseBlock), true, brace.location)
+ }else{
+ return new NopStatement()
+ }
+ }elseif(tokens.peek().value == "embed"){
+ tokens.shift()
+ if(tokens.peek().type != TokenType.ParenthesisOpen){
+ aspl.parser.utils.syntax_error("Expected ( after compile-time embed ($embed)")
+ }
+ tokens.shift()
+ if(tokens.peek().type != TokenType.String){
+ aspl.parser.utils.syntax_error("Expected string after compile-time embed ($embed)")
+ }
+ var string file = io.join_path([io.full_directory_path(io.abs(file)), tokens.next().value])
+ if(tokens.peek().type != TokenType.ParenthesisClose){
+ aspl.parser.utils.syntax_error("Expected ) after compile-time embed ($embed)")
+ }
+ tokens.shift()
+ return applyOperators(new EmbedFileExpression(file, token.location), tokens, precedenceLevel)
+ }elseif(tokens.peek().value == "include"){
+ tokens.shift()
+ if(tokens.peek().type != TokenType.ParenthesisOpen){
+ aspl.parser.utils.syntax_error("Expected ( after compile-time include ($include)")
+ }
+ tokens.shift()
+ if(tokens.peek().type != TokenType.String){
+ aspl.parser.utils.syntax_error("Expected string after compile-time include ($include)")
+ }
+ var string file = io.join_path([io.full_directory_path(io.abs(file)), tokens.next().value])
+ if(tokens.peek().type != TokenType.ParenthesisClose){
+ aspl.parser.utils.syntax_error("Expected ) after compile-time include ($include)")
+ }
+ tokens.shift()
+ return new IncludeFileStatement(file, token.location)
+ }elseif(tokens.peek().value == "link"){
+ tokens.shift()
+ if(tokens.peek().type != TokenType.ParenthesisOpen){
+ aspl.parser.utils.syntax_error("Expected ( after compile-time link ($link)")
+ }
+ tokens.shift()
+ if(tokens.peek().type != TokenType.String){
+ aspl.parser.utils.syntax_error("Expected string after compile-time link ($link)")
+ }
+ var string library = tokens.next().value // TODO: Also support files as libraries
+ if(tokens.peek().type != TokenType.ParenthesisClose){
+ aspl.parser.utils.syntax_error("Expected ) after compile-time link ($link)")
+ }
+ tokens.shift()
+ return new LinkLibraryStatement(library, token.location)
+ }
+ }
+ }
+ aspl.parser.utils.syntax_error("Unexpected token '" + token.value + "' (" + token.type + ")", token.location)
+ }
+
+ method applyOperators(Expression expression, TokenList tokens, PrecedenceLevel currentLevel) returns Expression{
+ if(ErrorUtils:canExpressionThrow(expression) && (tokens.empty() || (tokens.peek().type != TokenType.Negate && tokens.peek().value != "catch"))){ // TODO: Move this somewhere else for performance & readability
+ aspl.parser.utils.syntax_error("Expected '!' or 'catch' after invocation of an error-prone callable", expression.location)
+ }
+ if(tokens.empty()){
+ return expression
+ }
+ var nextToken = tokens.peek()
+ if(currentLevel < PrecedenceUtils:GetTokenPrecendenceLevel(nextToken)){
+ var result = parseToken(nextToken, tokens, false, PrecedenceUtils:GetTokenPrecendenceLevel(tokens.next()), expression)
+ if(result oftype Expression){
+ return applyOperators(Expression(result), tokens, currentLevel)
+ }
+ }
+ return expression
+ }
+
+ method peekFunctionIdentifier(TokenList tokens, Token? first = null) returns IdentifierResult{
+ var identifier = ""
+ var i = 0
+ if(first != null){
+ identifier = Token(first).value
+ }else{
+ if(tokens.peek().type != TokenType.Identifier){
+ return new IdentifierResult("", 0, null)
+ }
+ first = tokens.peek()
+ identifier = Token(first).value
+ i++
+ }
+ while(true){
+ if(tokens.length - 1 < i || tokens.peek(i).type != TokenType.Dot){
+ break
+ }
+ i++
+ identifier += "." + tokens.peek(i).value
+ i++
+ }
+ return new IdentifierResult(identifier, i, Token(first).location)
+ }
+
+ method parseFunctionIdentifier(TokenList tokens, Token? first = null) returns IdentifierResult{
+ var identifier = ""
+ var i = 0
+ if(first != null){
+ identifier = Token(first).value
+ }else{
+ if(tokens.peek().type != TokenType.Identifier){
+ return new IdentifierResult("", 0, null)
+ }
+ first = tokens.next()
+ identifier = Token(first).value
+ i++
+ }
+ while(true){
+ if(tokens.length == 0 || tokens.peek().type != TokenType.Dot){
+ break
+ }
+ tokens.shift()
+ i++
+ identifier += "." + tokens.next().value
+ i++
+ }
+ return new IdentifierResult(identifier, i, Token(first).location)
+ }
+
+ [public]
+ method peekTypeIdentifier(TokenList tokens, Token? first = null) returns IdentifierResult{
+ var identifier = ""
+ var i = 0
+ if(first != null){
+ identifier = Token(first).value
+ }else{
+ if(tokens.peek().type != TokenType.Identifier){
+ return new IdentifierResult("", 0, null)
+ }
+ first = tokens.peek()
+ identifier = Token(first).value
+ i++
+ }
+ while(true){
+ if(tokens.length - 1 < i){
+ break
+ }
+ if(tokens.peek(i).type == TokenType.LessThan){
+ var genericIdentifier = "<"
+ var tokenCountWithoutGeneric = i
+ i++
+ while(true){
+ if(tokens.length - 1 < i){
+ i -= tokenCountWithoutGeneric
+ return new IdentifierResult(identifier, i, Token(first).location)
+ }
+ if(tokens.peek(i).value == "returns"){
+ genericIdentifier += "returns "
+ i++
+ }
+ var peeked = peekTypeIdentifier(tokens.in(i), null)
+ genericIdentifier += peeked.identifier
+ i += peeked.tokenCount
+ if(tokens.peek(i).type == TokenType.Pipe){
+ genericIdentifier += "|"
+ i++
+ }elseif(tokens.peek(i).type == TokenType.Comma){
+ genericIdentifier += ", "
+ i++
+ }elseif(tokens.peek(i).type == TokenType.GreaterThan){
+ genericIdentifier += ">"
+ identifier += genericIdentifier
+ i++
+ if(tokens.peek(i).type == TokenType.Asterisk){
+ while(tokens.length > i && tokens.peek(i).type == TokenType.Asterisk){
+ identifier += "*"
+ i++
+ }
+ }
+ return new IdentifierResult(identifier, i, Token(first).location)
+ }else{
+ i -= tokenCountWithoutGeneric
+ return new IdentifierResult(identifier, i, Token(first).location)
+ }
+ }
+ genericIdentifier += ">"
+ identifier += genericIdentifier
+ i++
+ break
+ }
+ if(tokens.peek(i).type == TokenType.Asterisk){
+ while(tokens.length > i && tokens.peek(i).type == TokenType.Asterisk){
+ identifier += "*"
+ i++
+ }
+ break
+ }elseif(tokens.peek(i).type != TokenType.Dot){
+ break
+ }
+ i++
+ identifier += "." + tokens.peek(i).value
+ i++
+ }
+ return new IdentifierResult(identifier, i, Token(first).location)
+ }
+
+ [public]
+ method parseTypeIdentifier(TokenList tokens, Token? first = null) returns IdentifierResult{
+ var identifier = ""
+ var i = 0
+ if(first != null){
+ identifier = Token(first).value
+ }else{
+ if(tokens.peek().type != TokenType.Identifier){
+ return new IdentifierResult("", 0, null)
+ }
+ first = tokens.next()
+ identifier = Token(first).value
+ i++
+ }
+ while(true){
+ if(tokens.length == 0){
+ break
+ }
+ if(tokens.peek().type == TokenType.LessThan){
+ var genericIdentifier = "<"
+ var tokensWithoutGeneric = tokens.clone()
+ var tokenCountWithoutGeneric = i
+ tokens.shift()
+ i++
+ while(true){
+ if(tokens.empty()){
+ tokens.set(tokensWithoutGeneric)
+ i -= tokenCountWithoutGeneric
+ return new IdentifierResult(identifier, i, Token(first).location)
+ }
+ if(tokens.peek().value == "returns"){
+ genericIdentifier += "returns "
+ tokens.shift()
+ i++
+ }
+ var parsed = parseTypeIdentifier(tokens, null)
+ genericIdentifier += parsed.identifier
+ i += parsed.tokenCount
+ if(tokens.peek().type == TokenType.Pipe){
+ genericIdentifier += "|"
+ tokens.shift()
+ i++
+ }elseif(tokens.peek().type == TokenType.Comma){
+ genericIdentifier += ", "
+ tokens.shift()
+ i++
+ }elseif(tokens.peek().type == TokenType.GreaterThan){
+ genericIdentifier += ">"
+ identifier += genericIdentifier
+ tokens.shift()
+ i++
+ if(tokens.peek().type == TokenType.Asterisk){
+ while(tokens.length > 0 && tokens.peek().type == TokenType.Asterisk){
+ identifier += "*"
+ tokens.shift()
+ i++
+ }
+ }
+ return new IdentifierResult(identifier, i, Token(first).location)
+ }else{
+ tokens.set(tokensWithoutGeneric)
+ i -= tokenCountWithoutGeneric
+ return new IdentifierResult(identifier, i, Token(first).location)
+ }
+ }
+ genericIdentifier += ">"
+ identifier += genericIdentifier
+ tokens.shift()
+ i++
+ break
+ }
+ if(tokens.peek().type == TokenType.Asterisk){
+ while(tokens.length > 0 && tokens.peek().type == TokenType.Asterisk){
+ identifier += "*"
+ tokens.shift()
+ i++
+ }
+ break
+ }elseif(tokens.peek().type != TokenType.Dot){
+ break
+ }
+ tokens.shift()
+ i++
+ identifier += "." + tokens.next().value
+ i++
+ }
+ return new IdentifierResult(identifier, i, Token(first).location)
+ }
+
+ method parseTypesIfAny(TokenList tokens, Token? first = null) returns Types{
+ var Location location = tokens.peek().location
+ if(first != null){
+ location = first?!.location
+ }
+ var list types = []
+ if(Type:existsByName(this, peekTypeIdentifier(tokens, first).identifier)){
+ types.add(Type:fromString(parseTypeIdentifier(tokens, first).identifier, this, location))
+ while(true){
+ if(tokens.peek().type != TokenType.Pipe){
+ if(tokens.peek().type == TokenType.QuestionMark){
+ types.add(Type:fromString("null"))
+ tokens.shift()
+ }else{
+ break
+ }
+ }else{
+ tokens.shift()
+ if(Type:existsByName(this, peekTypeIdentifier(tokens).identifier)){
+ types.add(Type:fromString(parseTypeIdentifier(tokens).identifier, this, location))
+ }else{
+ aspl.parser.utils.syntax_error("Expected valid type identifier after '|'", tokens.peek().location)
+ }
+ }
+ }
+ }
+ return new Types(types)
+ }
+
+ method parseBlock(TokenList tokens, Token? first = null, bool pushScope = true) returns list{
+ if(pushScope){
+ Scope:push()
+ }
+ var list statements = []
+ if(first == null){
+ first = tokens.next()
+ }
+ var token = Token(first)
+ if(token.type != TokenType.BraceOpen){
+ aspl.parser.utils.syntax_error("Expected opening brace, got '" + token.value + "' (" + token.type + ")", token.location)
+ }
+ while(!tokens.empty()){
+ token = tokens.next()
+ if(token.type == TokenType.BraceClose){
+ if(pushScope){
+ Scope:pop()
+ }
+ return statements
+ }
+ var statement = parseToken(token, tokens, true)
+ if(!(statement oftype NopStatement)){
+ statements.add(statement)
+ }
+ }
+ aspl.parser.utils.syntax_error("Expected closing brace", token.location)
+ }
+
+ method preProcessBlock(TokenList tokens, Token? first = null, bool pushScope = true){
+ if(pushScope){
+ Scope:push()
+ }
+ if(first == null){
+ first = tokens.next()
+ }
+ var token = Token(first)
+ if(token.type != TokenType.BraceOpen){
+ aspl.parser.utils.syntax_error("Expected opening brace, got '" + token.value + "' (" + token.type + ")", token.location)
+ }
+ var bracesIndent = 0
+ while(!tokens.empty()){
+ token = tokens.peek()
+ if(token.type == TokenType.BraceOpen){
+ bracesIndent++
+ }elseif(token.type == TokenType.BraceClose){
+ if(bracesIndent == 0){
+ tokens.shift()
+ if(pushScope){
+ Scope:pop()
+ }
+ return
+ }
+ bracesIndent--
+ }
+ preProcessToken(tokens.next(), tokens)
+ }
+ aspl.parser.utils.syntax_error("Expected closing brace", token.location)
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ParserResult.aspl b/stdlib/aspl/parser/ParserResult.aspl
new file mode 100644
index 0000000..8d9d71b
--- /dev/null
+++ b/stdlib/aspl/parser/ParserResult.aspl
@@ -0,0 +1,14 @@
+import aspl.parser.ast
+
+[public]
+class ParserResult {
+
+ [readpublic]
+ property list nodes
+
+ [public]
+ method construct(list nodes){
+ this.nodes = nodes
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/Timings.aspl b/stdlib/aspl/parser/Timings.aspl
new file mode 100644
index 0000000..c2512da
--- /dev/null
+++ b/stdlib/aspl/parser/Timings.aspl
@@ -0,0 +1,91 @@
+import time
+
+[public]
+[static]
+class Timings {
+
+ [static]
+ [threadlocal]
+ property long lexer = 0
+ [static]
+ [threadlocal]
+ property long lexerStart = 0
+ [static]
+ [threadlocal]
+ property long preprocessorTypes = 0
+ [static]
+ [threadlocal]
+ property long preprocessorTypesStart = 0
+ [static]
+ [threadlocal]
+ property long preprocessor = 0
+ [static]
+ [threadlocal]
+ property long preprocessorStart = 0
+ [static]
+ [threadlocal]
+ property long parser = 0
+ [static]
+ [threadlocal]
+ property long parserStart = 0
+ [public]
+ [static]
+ property long total{
+ get{
+ return self:lexer + self:lexerStart + self:preprocessorTypes + self:preprocessor + self:parser
+ }
+ }
+
+ [public]
+ [static]
+ method startLexer(){
+ self:lexerStart = time.now().milliseconds
+ }
+
+ [public]
+ [static]
+ method stopLexer(){
+ self:lexer = time.now().milliseconds - self:lexerStart
+ self:lexerStart = 0
+ }
+
+ [public]
+ [static]
+ method startPreprocessorTypes(){
+ self:preprocessorTypesStart = time.now().milliseconds
+ }
+
+ [public]
+ [static]
+ method stopPreprocessorTypes(){
+ self:preprocessorTypes = time.now().milliseconds - self:preprocessorTypesStart
+ self:preprocessorTypes = 0
+ }
+
+ [public]
+ [static]
+ method startPreprocessor(){
+ self:preprocessorStart = time.now().milliseconds
+ }
+
+ [public]
+ [static]
+ method stopPreprocessor(){
+ self:preprocessor = time.now().milliseconds - self:preprocessorStart
+ self:preprocessorStart = 0
+ }
+
+ [public]
+ [static]
+ method startParser(){
+ self:parserStart = time.now().milliseconds
+ }
+
+ [public]
+ [static]
+ method stopParser(){
+ self:parser = time.now().milliseconds - self:parserStart
+ self:parserStart = 0
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/Node.aspl b/stdlib/aspl/parser/ast/Node.aspl
new file mode 100644
index 0000000..69ced98
--- /dev/null
+++ b/stdlib/aspl/parser/ast/Node.aspl
@@ -0,0 +1,16 @@
+import aspl.parser
+import aspl.parser.utils
+
+[public]
+[abstract]
+class Node{
+
+ [readpublic]
+ property Location? location = null
+
+ [public]
+ method construct(Location? location){
+ this.location = location
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/AndExpression.aspl b/stdlib/aspl/parser/ast/expressions/AndExpression.aspl
new file mode 100644
index 0000000..507deff
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/AndExpression.aspl
@@ -0,0 +1,21 @@
+import aspl.parser.utils
+
+[public]
+class AndExpression extends BinaryOperator {
+
+ [public]
+ method construct(Expression left, Expression right, Location? location){
+ parent(BinaryOperator).construct(left, right, location)
+ }
+
+ [public]
+ method getType() returns Types{
+ return this.left.getType()
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return this.left.getConstantValue() && this.right.getConstantValue()
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/BinaryOperator.aspl b/stdlib/aspl/parser/ast/expressions/BinaryOperator.aspl
new file mode 100644
index 0000000..439a859
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/BinaryOperator.aspl
@@ -0,0 +1,32 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+[abstract]
+class BinaryOperator extends Expression {
+
+ [readpublic]
+ property Expression left
+ [readpublic]
+ property Expression right
+
+ [public]
+ method construct(Expression left, Expression right, Location? location){
+ parent(Node).construct(location)
+ this.left = left
+ this.right = right
+ }
+
+ [public]
+ method isConstant() returns bool{
+ if(left.isConstant()){
+ if(right.isConstant()){
+ return true
+ }
+ else{
+ return false
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/CastExpression.aspl b/stdlib/aspl/parser/ast/expressions/CastExpression.aspl
new file mode 100644
index 0000000..106cdb8
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/CastExpression.aspl
@@ -0,0 +1,24 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class CastExpression extends Expression {
+
+ [readpublic]
+ property Expression value
+ [readpublic]
+ property Type type
+
+ [public]
+ method construct(Expression value, Type type, Location? location){
+ parent(Node).construct(location)
+ this.value = value
+ this.type = type
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([this.type])
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/CatchExpression.aspl b/stdlib/aspl/parser/ast/expressions/CatchExpression.aspl
new file mode 100644
index 0000000..b860d35
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/CatchExpression.aspl
@@ -0,0 +1,34 @@
+import aspl.parser.ast
+import aspl.parser.ast.expressions
+import aspl.parser.utils
+
+[public]
+class CatchExpression extends Expression {
+
+ [readpublic]
+ property Expression expression
+ [readpublic]
+ property string? variable
+ [readpublic]
+ property list code
+ [readpublic]
+ property list capturedVariables
+ [readpublic]
+ property bool standalone
+
+ [public]
+ method construct(Expression expression, string? variable, list code, list capturedVariables, bool standalone, Location? location){
+ parent(Node).construct(location)
+ this.expression = expression
+ this.variable = variable
+ this.code = code
+ this.capturedVariables = capturedVariables
+ this.standalone = standalone
+ }
+
+ [public]
+ method getType() returns Types{
+ return this.expression.getType()
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/CheckEqualsExpression.aspl b/stdlib/aspl/parser/ast/expressions/CheckEqualsExpression.aspl
new file mode 100644
index 0000000..a48924a
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/CheckEqualsExpression.aspl
@@ -0,0 +1,22 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class CheckEqualsExpression extends BinaryOperator {
+
+ [public]
+ method construct(Expression left, Expression right, Location? location){
+ parent(BinaryOperator).construct(left, right, location)
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([Type:fromString("boolean")])
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return this.left.getConstantValue() == this.right.getConstantValue()
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/ClassInstantiateExpression.aspl b/stdlib/aspl/parser/ast/expressions/ClassInstantiateExpression.aspl
new file mode 100644
index 0000000..11b5809
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/ClassInstantiateExpression.aspl
@@ -0,0 +1,25 @@
+import aspl.parser.ast
+import aspl.parser.utils
+import aspl.parser.classes
+
+[public]
+class ClassInstantiateExpression extends Expression {
+
+ [readpublic]
+ property Class c
+ [readpublic]
+ property list arguments
+
+ [public]
+ method construct(Class c, list arguments, Location? location){
+ parent(Node).construct(location)
+ this.c = c
+ this.arguments = arguments
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([this.c.type])
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/DereferenceExpression.aspl b/stdlib/aspl/parser/ast/expressions/DereferenceExpression.aspl
new file mode 100644
index 0000000..9fd1daa
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/DereferenceExpression.aspl
@@ -0,0 +1,21 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class DereferenceExpression extends Expression {
+
+ [readpublic]
+ property Expression pointer
+
+ [public]
+ method construct(Expression pointer, Location? location){
+ parent(Node).construct(location)
+ this.pointer = pointer
+ }
+
+ [public]
+ method getType() returns Types{
+ return this.pointer.getType().getReferenced()
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/DivideExpression.aspl b/stdlib/aspl/parser/ast/expressions/DivideExpression.aspl
new file mode 100644
index 0000000..52d5d1f
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/DivideExpression.aspl
@@ -0,0 +1,143 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+class DivideExpression extends BinaryOperator {
+
+ [public]
+ method construct(Expression left, Expression right, Location? location){
+ parent(BinaryOperator).construct(left, right, location)
+ }
+
+ [public]
+ method getType() returns Types{
+ if(Type:matches(new Types([Type:fromString("byte")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("byte")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("integer")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("integer")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("integer")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }
+ aspl.parser.utils.fatal_error("Incompatible types for / operator", this.location)
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ if(Type:matches(new Types([Type:fromString("byte")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) / byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) / int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) / long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) / float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) / double(this.right.getConstantValue())
+ }
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return int(this.left.getConstantValue()) / byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return int(this.left.getConstantValue()) / int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return int(this.left.getConstantValue()) / long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return int(this.left.getConstantValue()) / float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return int(this.left.getConstantValue()) / double(this.right.getConstantValue())
+ }
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return long(this.left.getConstantValue()) / byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return long(this.left.getConstantValue()) / int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return long(this.left.getConstantValue()) / long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return long(this.left.getConstantValue()) / float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return long(this.left.getConstantValue()) / double(this.right.getConstantValue())
+ }
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return float(this.left.getConstantValue()) / byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return float(this.left.getConstantValue()) / int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return float(this.left.getConstantValue()) / long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return float(this.left.getConstantValue()) / float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return float(this.left.getConstantValue()) / double(this.right.getConstantValue())
+ }
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return double(this.left.getConstantValue()) / byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return double(this.left.getConstantValue()) / int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return double(this.left.getConstantValue()) / long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return double(this.left.getConstantValue()) / float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return double(this.left.getConstantValue()) / double(this.right.getConstantValue())
+ }
+ }
+ aspl.parser.utils.fatal_error("Incompatible types for / operator", this.location)
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/EmbedFileExpression.aspl b/stdlib/aspl/parser/ast/expressions/EmbedFileExpression.aspl
new file mode 100644
index 0000000..7aff60f
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/EmbedFileExpression.aspl
@@ -0,0 +1,30 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class EmbedFileExpression extends Expression {
+
+ [readpublic]
+ property string file
+
+ method construct(string file, Location? location){
+ parent(Node).construct(location)
+ this.file = file
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([Type:fromString("list")])
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return true
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return io.read_file_bytes(file)
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/EnumFieldAccessExpression.aspl b/stdlib/aspl/parser/ast/expressions/EnumFieldAccessExpression.aspl
new file mode 100644
index 0000000..210bddb
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/EnumFieldAccessExpression.aspl
@@ -0,0 +1,31 @@
+import aspl.parser.ast
+import aspl.parser.enums
+import aspl.parser.utils
+
+[public]
+class EnumFieldAccessExpression extends Expression {
+
+ [readpublic]
+ property EnumField field
+
+ method construct(EnumField field, Location? location){
+ parent(Node).construct(location)
+ this.field = field
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([this.field.e.type])
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return true
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return field.value
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/Expression.aspl b/stdlib/aspl/parser/ast/expressions/Expression.aspl
new file mode 100644
index 0000000..ed09415
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/Expression.aspl
@@ -0,0 +1,23 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+// This class is for all nodes that may be used as expressions
+[public]
+[abstract]
+class Expression extends Node{
+
+ [public]
+ [abstract]
+ method getType() returns Types
+
+ [public]
+ method isConstant() returns bool{
+ return false
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ aspl.parser.utils.fatal_error("This expression is not constant")
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/FunctionCallExpression.aspl b/stdlib/aspl/parser/ast/expressions/FunctionCallExpression.aspl
new file mode 100644
index 0000000..25d4b08
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/FunctionCallExpression.aspl
@@ -0,0 +1,35 @@
+import aspl.parser.ast
+import aspl.parser.functions
+import aspl.parser.utils
+
+[public]
+class FunctionCallExpression extends Expression{
+
+ [readpublic]
+ property string identifier
+ [readpublic]
+ property Function func
+ [readpublic]
+ property list arguments
+ [readpublic]
+ property bool newThread
+
+ method construct(string identifier, Function func, list arguments, bool newThread, Location? location){
+ parent(Node).construct(location)
+ this.identifier = identifier
+ this.func = func
+ this.arguments = arguments
+ this.newThread = newThread
+ }
+
+ [public]
+ method getType() returns Types{
+ return this.func.returnTypes
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return false
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/GreaterThanExpression.aspl b/stdlib/aspl/parser/ast/expressions/GreaterThanExpression.aspl
new file mode 100644
index 0000000..157f66e
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/GreaterThanExpression.aspl
@@ -0,0 +1,22 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class GreaterThanExpression extends BinaryOperator {
+
+ [public]
+ method construct(Expression left, Expression right, Location? location){
+ parent(BinaryOperator).construct(left, right, location)
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([Type:fromString("boolean")])
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return this.left.getConstantValue() > this.right.getConstantValue()
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/GreaterThanOrEqualExpression.aspl b/stdlib/aspl/parser/ast/expressions/GreaterThanOrEqualExpression.aspl
new file mode 100644
index 0000000..9785182
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/GreaterThanOrEqualExpression.aspl
@@ -0,0 +1,22 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class GreaterThanOrEqualExpression extends BinaryOperator {
+
+ [public]
+ method construct(Expression left, Expression right, Location? location){
+ parent(BinaryOperator).construct(left, right, location)
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([Type:fromString("boolean")])
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return this.left.getConstantValue() >= this.right.getConstantValue()
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/ImplementationCallExpression.aspl b/stdlib/aspl/parser/ast/expressions/ImplementationCallExpression.aspl
new file mode 100644
index 0000000..5c068ad
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/ImplementationCallExpression.aspl
@@ -0,0 +1,29 @@
+import aspl.parser.ast
+import aspl.parser.functions
+import aspl.parser.utils
+
+[public]
+class ImplementationCallExpression extends Expression{
+
+ [readpublic]
+ property string call
+ [readpublic]
+ property list arguments
+
+ method construct(string call, list arguments, Location? location){
+ parent(Node).construct(location)
+ this.call = call
+ this.arguments = arguments
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([Type:fromString("any")])
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return false
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/LessThanExpression.aspl b/stdlib/aspl/parser/ast/expressions/LessThanExpression.aspl
new file mode 100644
index 0000000..bf305eb
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/LessThanExpression.aspl
@@ -0,0 +1,22 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class LessThanExpression extends BinaryOperator {
+
+ [public]
+ method construct(Expression left, Expression right, Location? location){
+ parent(BinaryOperator).construct(left, right, location)
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([Type:fromString("boolean")])
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return this.left.getConstantValue() < this.right.getConstantValue()
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/LessThanOrEqualExpression.aspl b/stdlib/aspl/parser/ast/expressions/LessThanOrEqualExpression.aspl
new file mode 100644
index 0000000..bdbf4d1
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/LessThanOrEqualExpression.aspl
@@ -0,0 +1,22 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class LessThanOrEqualExpression extends BinaryOperator {
+
+ [public]
+ method construct(Expression left, Expression right, Location? location){
+ parent(BinaryOperator).construct(left, right, location)
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([Type:fromString("boolean")])
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return this.left.getConstantValue() <= this.right.getConstantValue()
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/ListAssignExpression.aspl b/stdlib/aspl/parser/ast/expressions/ListAssignExpression.aspl
new file mode 100644
index 0000000..d5b840c
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/ListAssignExpression.aspl
@@ -0,0 +1,27 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class ListAssignExpression extends Expression {
+
+ [readpublic]
+ property Expression base
+ [readpublic]
+ property Expression index
+ [readpublic]
+ property Expression value
+
+ [public]
+ method construct(Expression base, Expression index, Expression value, Location? location){
+ parent(Node).construct(location)
+ this.base = base
+ this.index = index
+ this.value = value
+ }
+
+ [public]
+ method getType() returns Types{
+ return this.base.getType()
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/ListIndexExpression.aspl b/stdlib/aspl/parser/ast/expressions/ListIndexExpression.aspl
new file mode 100644
index 0000000..bf0a1b7
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/ListIndexExpression.aspl
@@ -0,0 +1,42 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class ListIndexExpression extends Expression {
+
+ [readpublic]
+ property Expression base
+ [readpublic]
+ property Expression index
+
+ [public]
+ method construct(Expression base, Expression index, Location? location){
+ parent(Node).construct(location)
+ this.base = base
+ this.index = index
+ }
+
+ [public]
+ method getType() returns Types{
+ var list types = []
+ foreach(base.getType().types as type){
+ if(Type:getGenericTypes(type.toString()).length > 0){
+ foreach(Type:getGenericTypes(type.toString())[0].types as t){
+ types.add(t)
+ }
+ }
+ }
+ return new Types(types)
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return this.base.isConstant() && this.index.isConstant()
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return list(this.base.getConstantValue())[int(this.index.getConstantValue())]
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/MapAccessExpression.aspl b/stdlib/aspl/parser/ast/expressions/MapAccessExpression.aspl
new file mode 100644
index 0000000..9e99585
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/MapAccessExpression.aspl
@@ -0,0 +1,42 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class MapAccessExpression extends Expression {
+
+ [readpublic]
+ property Expression base
+ [readpublic]
+ property Expression key
+
+ [public]
+ method construct(Expression base, Expression key, Location? location){
+ parent(Node).construct(location)
+ this.base = base
+ this.key = key
+ }
+
+ [public]
+ method getType() returns Types{
+ var list types = []
+ foreach(base.getType().types as type){
+ if(Type:getGenericTypes(type.toString()).length > 1){
+ foreach(Type:getGenericTypes(type.toString())[1].types as t){
+ types.add(t)
+ }
+ }
+ }
+ return new Types(types)
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return this.base.isConstant() && this.key.isConstant()
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return map(this.base.getConstantValue())[this.key.getConstantValue()]
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/MapAssignExpression.aspl b/stdlib/aspl/parser/ast/expressions/MapAssignExpression.aspl
new file mode 100644
index 0000000..2b712b7
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/MapAssignExpression.aspl
@@ -0,0 +1,27 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class MapAssignExpression extends Expression {
+
+ [readpublic]
+ property Expression base
+ [readpublic]
+ property Expression key
+ [readpublic]
+ property Expression value
+
+ [public]
+ method construct(Expression base, Expression key, Expression value, Location? location){
+ parent(Node).construct(location)
+ this.base = base
+ this.key = key
+ this.value = value
+ }
+
+ [public]
+ method getType() returns Types{
+ return this.base.getType()
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/MinusExpression.aspl b/stdlib/aspl/parser/ast/expressions/MinusExpression.aspl
new file mode 100644
index 0000000..bf64399
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/MinusExpression.aspl
@@ -0,0 +1,144 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class MinusExpression extends BinaryOperator {
+
+ [public]
+ method construct(Expression left, Expression right, Location? location){
+ parent(BinaryOperator).construct(left, right, location)
+ }
+
+ [public]
+ method getType() returns Types{
+ if(Type:matches(new Types([Type:fromString("byte")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("byte")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("integer")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("integer")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("integer")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }
+ aspl.parser.utils.fatal_error("Incompatible types for - operator", this.location)
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ if(Type:matches(new Types([Type:fromString("byte")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) - byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) - int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) - long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) - float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) - double(this.right.getConstantValue())
+ }
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return int(this.left.getConstantValue()) - byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return int(this.left.getConstantValue()) - int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return int(this.left.getConstantValue()) - long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return int(this.left.getConstantValue()) - float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return int(this.left.getConstantValue()) - double(this.right.getConstantValue())
+ }
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return long(this.left.getConstantValue()) - byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return long(this.left.getConstantValue()) - int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return long(this.left.getConstantValue()) - long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return long(this.left.getConstantValue()) - float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return long(this.left.getConstantValue()) - double(this.right.getConstantValue())
+ }
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return float(this.left.getConstantValue()) - byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return float(this.left.getConstantValue()) - int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return float(this.left.getConstantValue()) - long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return float(this.left.getConstantValue()) - float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return float(this.left.getConstantValue()) - double(this.right.getConstantValue())
+ }
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return double(this.left.getConstantValue()) - byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return double(this.left.getConstantValue()) - int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return double(this.left.getConstantValue()) - long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return double(this.left.getConstantValue()) - float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return double(this.left.getConstantValue()) - double(this.right.getConstantValue())
+ }
+ }
+ aspl.parser.utils.fatal_error("Incompatible types for - operator", this.location)
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/ModuloExpression.aspl b/stdlib/aspl/parser/ast/expressions/ModuloExpression.aspl
new file mode 100644
index 0000000..5708ccc
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/ModuloExpression.aspl
@@ -0,0 +1,144 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class ModuloExpression extends BinaryOperator {
+
+ [public]
+ method construct(Expression left, Expression right, Location? location){
+ parent(BinaryOperator).construct(left, right, location)
+ }
+
+ [public]
+ method getType() returns Types{
+ if(Type:matches(new Types([Type:fromString("byte")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("byte")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("integer")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("integer")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("integer")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }
+ aspl.parser.utils.fatal_error("Incompatible types for % operator", this.location)
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ if(Type:matches(new Types([Type:fromString("byte")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) % byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) % int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) % long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) % float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) % double(this.right.getConstantValue())
+ }
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return int(this.left.getConstantValue()) % byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return int(this.left.getConstantValue()) % int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return int(this.left.getConstantValue()) % long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return int(this.left.getConstantValue()) % float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return int(this.left.getConstantValue()) % double(this.right.getConstantValue())
+ }
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return long(this.left.getConstantValue()) % byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return long(this.left.getConstantValue()) % int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return long(this.left.getConstantValue()) % long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return long(this.left.getConstantValue()) % float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return long(this.left.getConstantValue()) % double(this.right.getConstantValue())
+ }
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return float(this.left.getConstantValue()) % byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return float(this.left.getConstantValue()) % int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return float(this.left.getConstantValue()) % long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return float(this.left.getConstantValue()) % float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return float(this.left.getConstantValue()) % double(this.right.getConstantValue())
+ }
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return double(this.left.getConstantValue()) % byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return double(this.left.getConstantValue()) % int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return double(this.left.getConstantValue()) % long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return double(this.left.getConstantValue()) % float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return double(this.left.getConstantValue()) % double(this.right.getConstantValue())
+ }
+ }
+ aspl.parser.utils.fatal_error("Incompatible types for % operator", this.location)
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/MultiplyExpression.aspl b/stdlib/aspl/parser/ast/expressions/MultiplyExpression.aspl
new file mode 100644
index 0000000..fd6cdd2
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/MultiplyExpression.aspl
@@ -0,0 +1,144 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class MultiplyExpression extends BinaryOperator {
+
+ [public]
+ method construct(Expression left, Expression right, Location? location){
+ parent(BinaryOperator).construct(left, right, location)
+ }
+
+ [public]
+ method getType() returns Types{
+ if(Type:matches(new Types([Type:fromString("byte")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("byte")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("integer")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("integer")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("integer")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }
+ aspl.parser.utils.fatal_error("Incompatible types for * operator", this.location)
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ if(Type:matches(new Types([Type:fromString("byte")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) * byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) * int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) * long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) * float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) * double(this.right.getConstantValue())
+ }
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return int(this.left.getConstantValue()) * byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return int(this.left.getConstantValue()) * int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return int(this.left.getConstantValue()) * long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return int(this.left.getConstantValue()) * float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return int(this.left.getConstantValue()) * double(this.right.getConstantValue())
+ }
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return long(this.left.getConstantValue()) * byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return long(this.left.getConstantValue()) * int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return long(this.left.getConstantValue()) * long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return long(this.left.getConstantValue()) * float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return long(this.left.getConstantValue()) * double(this.right.getConstantValue())
+ }
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return float(this.left.getConstantValue()) * byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return float(this.left.getConstantValue()) * int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return float(this.left.getConstantValue()) * long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return float(this.left.getConstantValue()) * float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return float(this.left.getConstantValue()) * double(this.right.getConstantValue())
+ }
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return double(this.left.getConstantValue()) * byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return double(this.left.getConstantValue()) * int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return double(this.left.getConstantValue()) * long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return double(this.left.getConstantValue()) * float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return double(this.left.getConstantValue()) * double(this.right.getConstantValue())
+ }
+ }
+ aspl.parser.utils.fatal_error("Incompatible types for * operator", this.location)
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/NegateExpression.aspl b/stdlib/aspl/parser/ast/expressions/NegateExpression.aspl
new file mode 100644
index 0000000..b47dde7
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/NegateExpression.aspl
@@ -0,0 +1,22 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class NegateExpression extends UnaryOperator {
+
+ [public]
+ method construct(Expression expression, Location? location){
+ parent(UnaryOperator).construct(expression, location)
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([Type:fromString("boolean")])
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return !this.expression.getConstantValue()
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/NonStaticMethodCallExpression.aspl b/stdlib/aspl/parser/ast/expressions/NonStaticMethodCallExpression.aspl
new file mode 100644
index 0000000..3c52033
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/NonStaticMethodCallExpression.aspl
@@ -0,0 +1,38 @@
+import aspl.parser.ast
+import aspl.parser.methods
+import aspl.parser.utils
+import aspl.parser.classes
+
+class NonStaticMethodCallExpression extends Expression{
+
+ [readpublic]
+ property Method m
+ [readpublic]
+ property Expression base
+ [readpublic]
+ property Class? exactClass
+ [readpublic]
+ property list arguments
+ [readpublic]
+ property bool newThread
+
+ method construct(Method m, Expression base, Class? exactClass, list arguments, bool newThread, Location? location){
+ parent(Node).construct(location)
+ this.m = m
+ this.base = base
+ this.exactClass = exactClass
+ this.arguments = arguments
+ this.newThread = newThread
+ }
+
+ [public]
+ method getType() returns Types{
+ return this.m.returnTypes
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return false
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/NonStaticPropertyAccessExpression.aspl b/stdlib/aspl/parser/ast/expressions/NonStaticPropertyAccessExpression.aspl
new file mode 100644
index 0000000..43f6bba
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/NonStaticPropertyAccessExpression.aspl
@@ -0,0 +1,29 @@
+import aspl.parser.ast
+import aspl.parser.properties
+import aspl.parser.utils
+
+[public]
+class NonStaticPropertyAccessExpression extends Expression {
+
+ [readpublic]
+ property Property p
+ [readpublic]
+ property Expression base
+
+ method construct(Property p, Expression base, Location? location){
+ parent(Node).construct(location)
+ this.p = p
+ this.base = base
+ }
+
+ [public]
+ method getType() returns Types{
+ return this.p.types
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return false
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/NonStaticPropertyAssignExpression.aspl b/stdlib/aspl/parser/ast/expressions/NonStaticPropertyAssignExpression.aspl
new file mode 100644
index 0000000..a584a3d
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/NonStaticPropertyAssignExpression.aspl
@@ -0,0 +1,33 @@
+import aspl.parser.ast
+import aspl.parser.functions
+import aspl.parser.utils
+import aspl.parser.properties
+
+[public]
+class NonStaticPropertyAssignExpression extends Expression{
+
+ [readpublic]
+ property Property p
+ [readpublic]
+ property Expression base
+ [readpublic]
+ property Expression value
+
+ method construct(Property p, Expression base, Expression value, Location? location){
+ parent(Node).construct(location)
+ this.p = p
+ this.base = base
+ this.value = value
+ }
+
+ [public]
+ method getType() returns Types{
+ return this.p.types
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return false
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/OfTypeExpression.aspl b/stdlib/aspl/parser/ast/expressions/OfTypeExpression.aspl
new file mode 100644
index 0000000..94a482b
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/OfTypeExpression.aspl
@@ -0,0 +1,41 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class OfTypeExpression extends Expression {
+
+ [readpublic]
+ property Expression expression
+ [readpublic]
+ property Type type
+
+ [public]
+ method construct(Expression expression, Type type, Location? location){
+ parent(Node).construct(location)
+ this.expression = expression
+ this.type = type
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([Type:fromString("boolean")])
+ }
+
+ [public]
+ method isConstant() returns bool{
+ if(!expression.getType().canCast(type)){
+ return true
+ }else{
+ return false
+ }
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ if(!expression.getType().canCast(type)){
+ return false
+ }
+ aspl.parser.utils.fatal_error("Cannot evaluate constant value of oftype expression")
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/OrExpression.aspl b/stdlib/aspl/parser/ast/expressions/OrExpression.aspl
new file mode 100644
index 0000000..dd18fc9
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/OrExpression.aspl
@@ -0,0 +1,22 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class OrExpression extends BinaryOperator {
+
+ [public]
+ method construct(Expression left, Expression right, Location? location){
+ parent(BinaryOperator).construct(left, right, location)
+ }
+
+ [public]
+ method getType() returns Types{
+ return this.left.getType()
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return this.left.getConstantValue() || this.right.getConstantValue()
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/ParentExpression.aspl b/stdlib/aspl/parser/ast/expressions/ParentExpression.aspl
new file mode 100644
index 0000000..d2b829c
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/ParentExpression.aspl
@@ -0,0 +1,27 @@
+import aspl.parser.ast
+import aspl.parser.classes
+import aspl.parser.utils
+
+[public]
+class ParentExpression extends Expression {
+
+ [readpublic]
+ property Class c
+
+ [public]
+ method construct(Class c, Location? location){
+ parent(Node).construct(location)
+ this.c = c
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([this.c.type])
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return false
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/PlusExpression.aspl b/stdlib/aspl/parser/ast/expressions/PlusExpression.aspl
new file mode 100644
index 0000000..6151527
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/PlusExpression.aspl
@@ -0,0 +1,156 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class PlusExpression extends BinaryOperator {
+
+ [public]
+ method construct(Expression left, Expression right, Location? location){
+ parent(BinaryOperator).construct(left, right, location)
+ }
+
+ [public]
+ method getType() returns Types{
+ if(Type:matches(new Types([Type:fromString("string")]), this.left.getType())){
+ return new Types([Type:fromString("string")])
+ }elseif(Type:matches(new Types([Type:fromString("string")]), this.right.getType())){
+ return new Types([Type:fromString("string")])
+ }else{
+ if(Type:matches(new Types([Type:fromString("byte")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("byte")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("integer")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("integer")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("integer")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("long")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("float")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return new Types([Type:fromString("double")])
+ }
+ }
+ aspl.parser.utils.fatal_error("Incompatible types for + operator", this.location)
+ }
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ if(Type:matches(new Types([Type:fromString("string")]), this.left.getType())){
+ return string(this.left.getConstantValue()) + string(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("string")]), this.right.getType())){
+ return string(this.left.getConstantValue()) + string(this.right.getConstantValue())
+ }else{
+ if(Type:matches(new Types([Type:fromString("byte")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) + byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) + int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) + long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) + float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return byte(this.left.getConstantValue()) + double(this.right.getConstantValue())
+ }
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return int(this.left.getConstantValue()) + byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return int(this.left.getConstantValue()) + int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return int(this.left.getConstantValue()) + long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return int(this.left.getConstantValue()) + float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return int(this.left.getConstantValue()) + double(this.right.getConstantValue())
+ }
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return long(this.left.getConstantValue()) + byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return long(this.left.getConstantValue()) + int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return long(this.left.getConstantValue()) + long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return long(this.left.getConstantValue()) + float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return long(this.left.getConstantValue()) + double(this.right.getConstantValue())
+ }
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return float(this.left.getConstantValue()) + byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return float(this.left.getConstantValue()) + int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return float(this.left.getConstantValue()) + long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return float(this.left.getConstantValue()) + float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return float(this.left.getConstantValue()) + double(this.right.getConstantValue())
+ }
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.left.getType())){
+ if(Type:matches(new Types([Type:fromString("byte")]), this.right.getType())){
+ return double(this.left.getConstantValue()) + byte(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("integer")]), this.right.getType())){
+ return double(this.left.getConstantValue()) + int(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("long")]), this.right.getType())){
+ return double(this.left.getConstantValue()) + long(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("float")]), this.right.getType())){
+ return double(this.left.getConstantValue()) + float(this.right.getConstantValue())
+ }elseif(Type:matches(new Types([Type:fromString("double")]), this.right.getType())){
+ return double(this.left.getConstantValue()) + double(this.right.getConstantValue())
+ }
+ }
+ aspl.parser.utils.fatal_error("Incompatible types for + operator", this.location)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/PropagateErrorExpression.aspl b/stdlib/aspl/parser/ast/expressions/PropagateErrorExpression.aspl
new file mode 100644
index 0000000..b5782d3
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/PropagateErrorExpression.aspl
@@ -0,0 +1,21 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class PropagateErrorExpression extends Expression {
+
+ [readpublic]
+ property Expression expression
+
+ [public]
+ method construct(Expression expression, Location? location){
+ parent(Node).construct(location)
+ this.expression = expression
+ }
+
+ [public]
+ method getType() returns Types{
+ return this.expression.getType()
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/ReferenceExpression.aspl b/stdlib/aspl/parser/ast/expressions/ReferenceExpression.aspl
new file mode 100644
index 0000000..2d0cd7f
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/ReferenceExpression.aspl
@@ -0,0 +1,21 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class ReferenceExpression extends Expression {
+
+ [readpublic]
+ property Expression expression
+
+ [public]
+ method construct(Expression expression, Location? location){
+ parent(Node).construct(location)
+ this.expression = expression
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([this.expression.getType().getPointer()])
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/StaticMethodCallExpression.aspl b/stdlib/aspl/parser/ast/expressions/StaticMethodCallExpression.aspl
new file mode 100644
index 0000000..e755c46
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/StaticMethodCallExpression.aspl
@@ -0,0 +1,35 @@
+import aspl.parser.ast
+import aspl.parser.methods
+import aspl.parser.utils
+
+[public]
+class StaticMethodCallExpression extends Expression{
+
+ [readpublic]
+ property Method m
+ [readpublic]
+ property Type base
+ [readpublic]
+ property list arguments
+ [readpublic]
+ property bool newThread
+
+ method construct(Method m, Type base, list arguments, bool newThread, Location? location){
+ parent(Node).construct(location)
+ this.m = m
+ this.base = base
+ this.arguments = arguments
+ this.newThread = newThread
+ }
+
+ [public]
+ method getType() returns Types{
+ return this.m.returnTypes
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return false
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/StaticPropertyAccessExpression.aspl b/stdlib/aspl/parser/ast/expressions/StaticPropertyAccessExpression.aspl
new file mode 100644
index 0000000..dcbf428
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/StaticPropertyAccessExpression.aspl
@@ -0,0 +1,29 @@
+import aspl.parser.ast
+import aspl.parser.properties
+import aspl.parser.utils
+
+[public]
+class StaticPropertyAccessExpression extends Expression {
+
+ [readpublic]
+ property Property p
+ [readpublic]
+ property Type base
+
+ method construct(Property p, Type base, Location? location){
+ parent(Node).construct(location)
+ this.p = p
+ this.base = base
+ }
+
+ [public]
+ method getType() returns Types{
+ return this.p.types
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return false
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/StaticPropertyAssignExpression.aspl b/stdlib/aspl/parser/ast/expressions/StaticPropertyAssignExpression.aspl
new file mode 100644
index 0000000..d82c99d
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/StaticPropertyAssignExpression.aspl
@@ -0,0 +1,33 @@
+import aspl.parser.ast
+import aspl.parser.functions
+import aspl.parser.utils
+import aspl.parser.properties
+
+[public]
+class StaticPropertyAssignExpression extends Expression{
+
+ [readpublic]
+ property Property p
+ [readpublic]
+ property Type base
+ [readpublic]
+ property Expression value
+
+ method construct(Property p, Type base, Expression value, Location? location){
+ parent(Node).construct(location)
+ this.p = p
+ this.base = base
+ this.value = value
+ }
+
+ [public]
+ method getType() returns Types{
+ return this.p.types
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return false
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/StringIndexExpression.aspl b/stdlib/aspl/parser/ast/expressions/StringIndexExpression.aspl
new file mode 100644
index 0000000..b342f8d
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/StringIndexExpression.aspl
@@ -0,0 +1,34 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class StringIndexExpression extends Expression {
+
+ [readpublic]
+ property Expression base
+ [readpublic]
+ property Expression index
+
+ [public]
+ method construct(Expression base, Expression index, Location location){
+ parent(Node).construct(location)
+ this.base = base
+ this.index = index
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([Type:fromString("string")])
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return this.base.isConstant() && this.index.isConstant()
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return string(this.base.getConstantValue())[int(this.index.getConstantValue())]
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/ThisExpression.aspl b/stdlib/aspl/parser/ast/expressions/ThisExpression.aspl
new file mode 100644
index 0000000..569df37
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/ThisExpression.aspl
@@ -0,0 +1,27 @@
+import aspl.parser.ast
+import aspl.parser.classes
+import aspl.parser.utils
+
+[public]
+class ThisExpression extends Expression {
+
+ [readpublic]
+ property Class c
+
+ [public]
+ method construct(Class c, Location? location){
+ parent(Node).construct(location)
+ this.c = c
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([this.c.type])
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return false
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/UnaryOperator.aspl b/stdlib/aspl/parser/ast/expressions/UnaryOperator.aspl
new file mode 100644
index 0000000..bee47de
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/UnaryOperator.aspl
@@ -0,0 +1,22 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+[abstract]
+class UnaryOperator extends Expression {
+
+ [readpublic]
+ property Expression expression
+
+ [public]
+ method construct(Expression expression, Location? location){
+ parent(Node).construct(location)
+ this.expression = expression
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return expression.isConstant()
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/VariableAccessExpression.aspl b/stdlib/aspl/parser/ast/expressions/VariableAccessExpression.aspl
new file mode 100644
index 0000000..d16cb3a
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/VariableAccessExpression.aspl
@@ -0,0 +1,27 @@
+import aspl.parser.ast
+import aspl.parser.functions
+import aspl.parser.utils
+import aspl.parser.variables
+
+[public]
+class VariableAccessExpression extends Expression{
+
+ [readpublic]
+ property Variable variable
+
+ method construct(Variable variable, Location? location){
+ parent(Node).construct(location)
+ this.variable = variable
+ }
+
+ [public]
+ method getType() returns Types{
+ return this.variable.types
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return false
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/VariableAssignExpression.aspl b/stdlib/aspl/parser/ast/expressions/VariableAssignExpression.aspl
new file mode 100644
index 0000000..76d35ab
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/VariableAssignExpression.aspl
@@ -0,0 +1,30 @@
+import aspl.parser.ast
+import aspl.parser.functions
+import aspl.parser.utils
+import aspl.parser.variables
+
+[public]
+class VariableAssignExpression extends Expression{
+
+ [readpublic]
+ property Variable variable
+ [readpublic]
+ property Expression value
+
+ method construct(Variable variable, Expression value, Location? location){
+ parent(Node).construct(location)
+ this.variable = variable
+ this.value = value
+ }
+
+ [public]
+ method getType() returns Types{
+ return this.variable.types
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return false
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/VariableDeclareExpression.aspl b/stdlib/aspl/parser/ast/expressions/VariableDeclareExpression.aspl
new file mode 100644
index 0000000..e897cc7
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/VariableDeclareExpression.aspl
@@ -0,0 +1,30 @@
+import aspl.parser.ast
+import aspl.parser.functions
+import aspl.parser.utils
+import aspl.parser.variables
+
+[public]
+class VariableDeclareExpression extends Expression{
+
+ [readpublic]
+ property Variable variable
+ [readpublic]
+ property Expression value
+
+ method construct(Variable variable, Expression value, Location? location){
+ parent(Node).construct(location)
+ this.variable = variable
+ this.value = value
+ }
+
+ [public]
+ method getType() returns Types{
+ return this.variable.types
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return false
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/expressions/XorExpression.aspl b/stdlib/aspl/parser/ast/expressions/XorExpression.aspl
new file mode 100644
index 0000000..99549e1
--- /dev/null
+++ b/stdlib/aspl/parser/ast/expressions/XorExpression.aspl
@@ -0,0 +1,22 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class XorExpression extends BinaryOperator {
+
+ [public]
+ method construct(Expression left, Expression right, Location? location){
+ parent(BinaryOperator).construct(left, right, location)
+ }
+
+ [public]
+ method getType() returns Types{
+ return this.left.getType()
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return this.left.getConstantValue()^this.right.getConstantValue()
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/literals/BooleanLiteral.aspl b/stdlib/aspl/parser/ast/literals/BooleanLiteral.aspl
new file mode 100644
index 0000000..589167d
--- /dev/null
+++ b/stdlib/aspl/parser/ast/literals/BooleanLiteral.aspl
@@ -0,0 +1,30 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class BooleanLiteral extends Literal{
+
+ [readpublic]
+ property bool value
+
+ method construct(bool value, Location? location){
+ parent(Node).construct(location)
+ this.value = value
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([Type:fromString("boolean")])
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return true
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return value
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/literals/ByteLiteral.aspl b/stdlib/aspl/parser/ast/literals/ByteLiteral.aspl
new file mode 100644
index 0000000..07643fc
--- /dev/null
+++ b/stdlib/aspl/parser/ast/literals/ByteLiteral.aspl
@@ -0,0 +1,29 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+class ByteLiteral extends Literal{
+
+ [readpublic]
+ property byte value
+
+ method construct(byte value, Location? location){
+ parent(Node).construct(location)
+ this.value = value
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([Type:fromString("byte")])
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return true
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return value
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/literals/CallbackLiteral.aspl b/stdlib/aspl/parser/ast/literals/CallbackLiteral.aspl
new file mode 100644
index 0000000..d979d30
--- /dev/null
+++ b/stdlib/aspl/parser/ast/literals/CallbackLiteral.aspl
@@ -0,0 +1,28 @@
+import aspl.parser.ast
+import aspl.parser.utils
+import aspl.parser.callbacks
+
+class CallbackLiteral extends Literal{
+
+ [readpublic]
+ property Callback value
+ [readpublic]
+ property list capturedVariables
+
+ method construct(Callback value, list capturedVariables, Location? location){
+ parent(Node).construct(location)
+ this.value = value
+ this.capturedVariables = capturedVariables
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([this.value.type])
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return false
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/literals/DoubleLiteral.aspl b/stdlib/aspl/parser/ast/literals/DoubleLiteral.aspl
new file mode 100644
index 0000000..8e747df
--- /dev/null
+++ b/stdlib/aspl/parser/ast/literals/DoubleLiteral.aspl
@@ -0,0 +1,30 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class DoubleLiteral extends Literal{
+
+ [readpublic]
+ property double value
+
+ method construct(double value, Location? location){
+ parent(Node).construct(location)
+ this.value = value
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([Type:fromString("double")])
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return true
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return value
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/literals/FloatLiteral.aspl b/stdlib/aspl/parser/ast/literals/FloatLiteral.aspl
new file mode 100644
index 0000000..6b6372b
--- /dev/null
+++ b/stdlib/aspl/parser/ast/literals/FloatLiteral.aspl
@@ -0,0 +1,30 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class FloatLiteral extends Literal{
+
+ [readpublic]
+ property float value
+
+ method construct(float value, Location? location){
+ parent(Node).construct(location)
+ this.value = value
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([Type:fromString("float")])
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return true
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return value
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/literals/IntegerLiteral.aspl b/stdlib/aspl/parser/ast/literals/IntegerLiteral.aspl
new file mode 100644
index 0000000..adbaa95
--- /dev/null
+++ b/stdlib/aspl/parser/ast/literals/IntegerLiteral.aspl
@@ -0,0 +1,30 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class IntegerLiteral extends Literal{
+
+ [readpublic]
+ property int value
+
+ method construct(int value, Location? location){
+ parent(Node).construct(location)
+ this.value = value
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([Type:fromString("integer")])
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return true
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return value
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/literals/ListLiteral.aspl b/stdlib/aspl/parser/ast/literals/ListLiteral.aspl
new file mode 100644
index 0000000..9c54c72
--- /dev/null
+++ b/stdlib/aspl/parser/ast/literals/ListLiteral.aspl
@@ -0,0 +1,43 @@
+import aspl.parser.ast
+import aspl.parser.utils
+import aspl.parser.ast.expressions
+
+[public]
+class ListLiteral extends Literal{
+
+ [public]
+ property Types type
+ [public]
+ property list value
+
+ method construct(Types type, list value, Location? location){
+ parent(Node).construct(location)
+ this.type = type
+ this.value = value
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([Type:fromString("list<" + this.type.toString() + ">")])
+ }
+
+ [public]
+ method isConstant() returns bool{
+ foreach(this.value as element){
+ if(!element.isConstant()){
+ return false
+ }
+ }
+ return true
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ var list l = list[]
+ foreach(this.value as element){
+ l.add(element.getConstantValue())
+ }
+ return l
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/literals/Literal.aspl b/stdlib/aspl/parser/ast/literals/Literal.aspl
new file mode 100644
index 0000000..42834c4
--- /dev/null
+++ b/stdlib/aspl/parser/ast/literals/Literal.aspl
@@ -0,0 +1,6 @@
+import aspl.parser.ast.expressions
+
+[public]
+[abstract]
+class Literal extends Expression{
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/literals/LongLiteral.aspl b/stdlib/aspl/parser/ast/literals/LongLiteral.aspl
new file mode 100644
index 0000000..fbaaaa6
--- /dev/null
+++ b/stdlib/aspl/parser/ast/literals/LongLiteral.aspl
@@ -0,0 +1,30 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class LongLiteral extends Literal{
+
+ [readpublic]
+ property long value
+
+ method construct(long value, Location? location){
+ parent(Node).construct(location)
+ this.value = value
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([Type:fromString("long")])
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return true
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return value
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/literals/MapLiteral.aspl b/stdlib/aspl/parser/ast/literals/MapLiteral.aspl
new file mode 100644
index 0000000..a1707fc
--- /dev/null
+++ b/stdlib/aspl/parser/ast/literals/MapLiteral.aspl
@@ -0,0 +1,48 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class MapLiteral extends Literal{
+
+ [readpublic]
+ property Types keyType
+ [readpublic]
+ property Types valueType
+ [readpublic]
+ property list value
+
+ method construct(Types keyType, Types valueType, list value, Location? location){
+ parent(Node).construct(location)
+ this.keyType = keyType
+ this.valueType = valueType
+ this.value = value
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([Type:fromString("map<" + this.keyType.toString() + ", " + this.valueType.toString() + ">")])
+ }
+
+ [public]
+ method isConstant() returns bool{
+ foreach(this.value as pair){
+ if(!pair.k.isConstant()){
+ return false
+ }
+ if(!pair.v.isConstant()){
+ return false
+ }
+ }
+ return true
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ var map m = map{}
+ foreach(this.value as pair){
+ m[pair.k.getConstantValue()] = pair.v.getConstantValue()
+ }
+ return m
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/literals/NullLiteral.aspl b/stdlib/aspl/parser/ast/literals/NullLiteral.aspl
new file mode 100644
index 0000000..047aedf
--- /dev/null
+++ b/stdlib/aspl/parser/ast/literals/NullLiteral.aspl
@@ -0,0 +1,26 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class NullLiteral extends Literal{
+
+ method construct(Location? location){
+ parent(Node).construct(location)
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([Type:fromString("null")])
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return true
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return null
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/literals/StringLiteral.aspl b/stdlib/aspl/parser/ast/literals/StringLiteral.aspl
new file mode 100644
index 0000000..668bcb3
--- /dev/null
+++ b/stdlib/aspl/parser/ast/literals/StringLiteral.aspl
@@ -0,0 +1,33 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class StringLiteral extends Literal{
+
+ [readpublic]
+ property string value
+ [readpublic]
+ property string literalString
+
+ method construct(string value, string literalString, Location? location){
+ parent(Node).construct(location)
+ this.value = value
+ this.literalString = literalString
+ }
+
+ [public]
+ method getType() returns Types{
+ return new Types([Type:fromString("string")])
+ }
+
+ [public]
+ method isConstant() returns bool{
+ return true
+ }
+
+ [public]
+ method getConstantValue() returns any{
+ return value
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/AssertStatement.aspl b/stdlib/aspl/parser/ast/statements/AssertStatement.aspl
new file mode 100644
index 0000000..f75d854
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/AssertStatement.aspl
@@ -0,0 +1,16 @@
+import aspl.parser.ast
+import aspl.parser.ast.expressions
+import aspl.parser.utils
+
+[public]
+class AssertStatement extends Statement{
+
+ [readpublic]
+ property Expression expression
+
+ method construct(Expression expression, Location? location){
+ parent(Node).construct(location)
+ this.expression = expression
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/BlockStatement.aspl b/stdlib/aspl/parser/ast/statements/BlockStatement.aspl
new file mode 100644
index 0000000..8123e62
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/BlockStatement.aspl
@@ -0,0 +1,20 @@
+import aspl.parser.ast
+import aspl.parser.ast.expressions
+import aspl.parser.utils
+
+[public]
+class BlockStatement extends Statement{
+
+ [readpublic]
+ property list statements
+
+ [readpublic]
+ property bool isCompileTime
+
+ method construct(list statements, bool isCompileTime, Location? location){
+ parent(Node).construct(location)
+ this.statements = statements
+ this.isCompileTime = isCompileTime
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/BreakStatement.aspl b/stdlib/aspl/parser/ast/statements/BreakStatement.aspl
new file mode 100644
index 0000000..4ff3b1d
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/BreakStatement.aspl
@@ -0,0 +1,16 @@
+import aspl.parser.ast
+import aspl.parser.ast.expressions
+import aspl.parser.utils
+
+[public]
+class BreakStatement extends Statement{
+
+ [readpublic]
+ property int level
+
+ method construct(int level, Location? location){
+ parent(Node).construct(location)
+ this.level = level
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/ClassDeclareStatement.aspl b/stdlib/aspl/parser/ast/statements/ClassDeclareStatement.aspl
new file mode 100644
index 0000000..a431e86
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/ClassDeclareStatement.aspl
@@ -0,0 +1,20 @@
+import aspl.parser.ast
+import aspl.parser.classes
+import aspl.parser.utils
+import aspl.parser.lexer
+
+[public]
+class ClassDeclareStatement extends Statement{
+
+ [readpublic]
+ property Class c
+ [readpublic]
+ property list comments
+
+ method construct(Class c, list comments, Location? location){
+ parent(Node).construct(location)
+ this.c = c
+ this.comments = comments
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/ContinueStatement.aspl b/stdlib/aspl/parser/ast/statements/ContinueStatement.aspl
new file mode 100644
index 0000000..909e2c2
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/ContinueStatement.aspl
@@ -0,0 +1,16 @@
+import aspl.parser.ast
+import aspl.parser.ast.expressions
+import aspl.parser.utils
+
+[public]
+class ContinueStatement extends Statement{
+
+ [readpublic]
+ property int level
+
+ method construct(int level, Location? location){
+ parent(Node).construct(location)
+ this.level = level
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/EnumDeclareStatement.aspl b/stdlib/aspl/parser/ast/statements/EnumDeclareStatement.aspl
new file mode 100644
index 0000000..7e67198
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/EnumDeclareStatement.aspl
@@ -0,0 +1,20 @@
+import aspl.parser.ast
+import aspl.parser.enums
+import aspl.parser.utils
+import aspl.parser.lexer
+
+[public]
+class EnumDeclareStatement extends Statement{
+
+ [readpublic]
+ property Enum e
+ [readpublic]
+ property list comments
+
+ method construct(Enum e, list comments, Location? location){
+ parent(Node).construct(location)
+ this.e = e
+ this.comments = comments
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/EscapeStatement.aspl b/stdlib/aspl/parser/ast/statements/EscapeStatement.aspl
new file mode 100644
index 0000000..3a634bd
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/EscapeStatement.aspl
@@ -0,0 +1,16 @@
+import aspl.parser.ast
+import aspl.parser.ast.expressions
+import aspl.parser.utils
+
+[public]
+class EscapeStatement extends Statement{
+
+ [readpublic]
+ property Expression? value
+
+ method construct(Expression? value, Location? location){
+ parent(Node).construct(location)
+ this.value = value
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/FallbackStatement.aspl b/stdlib/aspl/parser/ast/statements/FallbackStatement.aspl
new file mode 100644
index 0000000..93c8602
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/FallbackStatement.aspl
@@ -0,0 +1,16 @@
+import aspl.parser.ast
+import aspl.parser.ast.expressions
+import aspl.parser.utils
+
+[public]
+class FallbackStatement extends Statement{
+
+ [readpublic]
+ property Expression value
+
+ method construct(Expression value, Location? location){
+ parent(Node).construct(location)
+ this.value = value
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/ForeachStatement.aspl b/stdlib/aspl/parser/ast/statements/ForeachStatement.aspl
new file mode 100644
index 0000000..c5e5924
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/ForeachStatement.aspl
@@ -0,0 +1,26 @@
+import aspl.parser.ast
+import aspl.parser.ast.expressions
+import aspl.parser.utils
+
+[public]
+class ForeachStatement extends Statement {
+
+ [readpublic]
+ property Expression collection
+ [readpublic]
+ property string? key
+ [readpublic]
+ property string? value
+ [readpublic]
+ property list code
+
+ [public]
+ method construct(Expression collection, string? key, string? value, list code, Location? location){
+ parent(Node).construct(location)
+ this.collection = collection
+ this.key = key
+ this.value = value
+ this.code = code
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/FunctionDeclareStatement.aspl b/stdlib/aspl/parser/ast/statements/FunctionDeclareStatement.aspl
new file mode 100644
index 0000000..679decf
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/FunctionDeclareStatement.aspl
@@ -0,0 +1,20 @@
+import aspl.parser.ast
+import aspl.parser.functions
+import aspl.parser.utils
+import aspl.parser.lexer
+
+[public]
+class FunctionDeclareStatement extends Statement{
+
+ [readpublic]
+ property Function func
+ [readpublic]
+ property list comments
+
+ method construct(Function func, list comments, Location? location){
+ parent(Node).construct(location)
+ this.func = func
+ this.comments = comments
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/IfElseIfStatement.aspl b/stdlib/aspl/parser/ast/statements/IfElseIfStatement.aspl
new file mode 100644
index 0000000..283fee3
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/IfElseIfStatement.aspl
@@ -0,0 +1,23 @@
+import aspl.parser.ast
+import aspl.parser.ast.expressions
+import aspl.parser.utils
+
+[public]
+class IfElseIfStatement extends Statement {
+
+ [readpublic]
+ property Expression condition
+ [readpublic]
+ property list ifCode
+ [readpublic]
+ property IfStatement|IfElseIfStatement|IfElseStatement elseIf
+
+ [public]
+ method construct(Expression condition, list ifCode, IfStatement|IfElseIfStatement|IfElseStatement elseIf, Location? location){
+ parent(Node).construct(location)
+ this.condition = condition
+ this.ifCode = ifCode
+ this.elseIf = elseIf
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/IfElseStatement.aspl b/stdlib/aspl/parser/ast/statements/IfElseStatement.aspl
new file mode 100644
index 0000000..af4e8de
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/IfElseStatement.aspl
@@ -0,0 +1,23 @@
+import aspl.parser.ast
+import aspl.parser.ast.expressions
+import aspl.parser.utils
+
+[public]
+class IfElseStatement extends Statement {
+
+ [readpublic]
+ property Expression condition
+ [readpublic]
+ property list ifCode
+ [readpublic]
+ property list elseCode
+
+ [public]
+ method construct(Expression condition, list ifCode, list elseCode, Location? location){
+ parent(Node).construct(location)
+ this.condition = condition
+ this.ifCode = ifCode
+ this.elseCode = elseCode
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/IfStatement.aspl b/stdlib/aspl/parser/ast/statements/IfStatement.aspl
new file mode 100644
index 0000000..cdec186
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/IfStatement.aspl
@@ -0,0 +1,20 @@
+import aspl.parser.ast
+import aspl.parser.ast.expressions
+import aspl.parser.utils
+
+[public]
+class IfStatement extends Statement {
+
+ [readpublic]
+ property Expression condition
+ [readpublic]
+ property list code
+
+ [public]
+ method construct(Expression condition, list code, Location? location){
+ parent(Node).construct(location)
+ this.condition = condition
+ this.code = code
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/IncludeFileStatement.aspl b/stdlib/aspl/parser/ast/statements/IncludeFileStatement.aspl
new file mode 100644
index 0000000..6aab176
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/IncludeFileStatement.aspl
@@ -0,0 +1,15 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class IncludeFileStatement extends Statement{
+
+ [readpublic]
+ property string file
+
+ method construct(string file, Location? location){
+ parent(Node).construct(location)
+ this.file = file
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/LinkLibraryStatement.aspl b/stdlib/aspl/parser/ast/statements/LinkLibraryStatement.aspl
new file mode 100644
index 0000000..d8b5dbd
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/LinkLibraryStatement.aspl
@@ -0,0 +1,15 @@
+import aspl.parser.ast
+import aspl.parser.utils
+
+[public]
+class LinkLibraryStatement extends Statement{
+
+ [readpublic]
+ property string library
+
+ method construct(string library, Location? location){
+ parent(Node).construct(location)
+ this.library = library
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/MethodDeclareStatement.aspl b/stdlib/aspl/parser/ast/statements/MethodDeclareStatement.aspl
new file mode 100644
index 0000000..7cfae15
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/MethodDeclareStatement.aspl
@@ -0,0 +1,20 @@
+import aspl.parser.ast
+import aspl.parser.methods
+import aspl.parser.utils
+import aspl.parser.lexer
+
+[public]
+class MethodDeclareStatement extends Statement{
+
+ [readpublic]
+ property Method m
+ [readpublic]
+ property list comments
+
+ method construct(Method m, list comments, Location? location){
+ parent(Node).construct(location)
+ this.m = m
+ this.comments = comments
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/NopStatement.aspl b/stdlib/aspl/parser/ast/statements/NopStatement.aspl
new file mode 100644
index 0000000..bea2d2c
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/NopStatement.aspl
@@ -0,0 +1,5 @@
+// This class represents a no operation instruction
+// It should be automatically filtered out by the parser before being passed to e.g. the compiler
+[public]
+class NopStatement extends Statement {
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/PropertyDeclareStatement.aspl b/stdlib/aspl/parser/ast/statements/PropertyDeclareStatement.aspl
new file mode 100644
index 0000000..0d4eecb
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/PropertyDeclareStatement.aspl
@@ -0,0 +1,20 @@
+import aspl.parser.ast
+import aspl.parser.properties
+import aspl.parser.utils
+import aspl.parser.lexer
+
+[public]
+class PropertyDeclareStatement extends Statement{
+
+ [readpublic]
+ property Property p
+ [readpublic]
+ property list comments
+
+ method construct(Property p, list comments, Location? location){
+ parent(Node).construct(location)
+ this.p = p
+ this.comments = comments
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/RepeatStatement.aspl b/stdlib/aspl/parser/ast/statements/RepeatStatement.aspl
new file mode 100644
index 0000000..ef6e9f0
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/RepeatStatement.aspl
@@ -0,0 +1,26 @@
+import aspl.parser.ast
+import aspl.parser.ast.expressions
+import aspl.parser.utils
+
+[public]
+class RepeatStatement extends Statement {
+
+ [readpublic]
+ property Expression iterations
+ [readpublic]
+ property string? variable
+ [readpublic]
+ property int start
+ [readpublic]
+ property list code
+
+ [public]
+ method construct(Expression iterations, string? variable, int start, list code, Location? location){
+ parent(Node).construct(location)
+ this.iterations = iterations
+ this.variable = variable
+ this.start = start
+ this.code = code
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/ReturnStatement.aspl b/stdlib/aspl/parser/ast/statements/ReturnStatement.aspl
new file mode 100644
index 0000000..81d234c
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/ReturnStatement.aspl
@@ -0,0 +1,23 @@
+import aspl.parser.ast
+import aspl.parser.ast.expressions
+import aspl.parser.utils
+import aspl.parser.functions
+import aspl.parser.methods
+import aspl.parser.properties
+import aspl.parser.callbacks
+
+[public]
+class ReturnStatement extends Statement{
+
+ [readpublic]
+ property Expression? value
+ [readpublic]
+ property Function|Method|Callback|ReactivePropertyCallback callable
+
+ method construct(Expression? value, Function|Method|Callback|ReactivePropertyCallback callable, Location? location){
+ parent(Node).construct(location)
+ this.value = value
+ this.callable = callable
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/Statement.aspl b/stdlib/aspl/parser/ast/statements/Statement.aspl
new file mode 100644
index 0000000..6a02982
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/Statement.aspl
@@ -0,0 +1,7 @@
+import aspl.parser.ast
+
+// This class is only for nodes that can never be expressions
+[public]
+[abstract]
+class Statement extends Node{
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/ThrowStatement.aspl b/stdlib/aspl/parser/ast/statements/ThrowStatement.aspl
new file mode 100644
index 0000000..fe00549
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/ThrowStatement.aspl
@@ -0,0 +1,16 @@
+import aspl.parser.ast
+import aspl.parser.ast.expressions
+import aspl.parser.utils
+
+[public]
+class ThrowStatement extends Statement{
+
+ [readpublic]
+ property ClassInstantiateExpression errorInstance
+
+ method construct(ClassInstantiateExpression errorInstance, Location? location){
+ parent(Node).construct(location)
+ this.errorInstance = errorInstance
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/ast/statements/WhileStatement.aspl b/stdlib/aspl/parser/ast/statements/WhileStatement.aspl
new file mode 100644
index 0000000..03bd2f1
--- /dev/null
+++ b/stdlib/aspl/parser/ast/statements/WhileStatement.aspl
@@ -0,0 +1,20 @@
+import aspl.parser.ast
+import aspl.parser.ast.expressions
+import aspl.parser.utils
+
+[public]
+class WhileStatement extends Statement {
+
+ [readpublic]
+ property Expression condition
+ [readpublic]
+ property list code
+
+ [public]
+ method construct(Expression condition, list code, Location? location){
+ parent(Node).construct(location)
+ this.condition = condition
+ this.code = code
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/attributes/Attribute.aspl b/stdlib/aspl/parser/attributes/Attribute.aspl
new file mode 100644
index 0000000..87d60f2
--- /dev/null
+++ b/stdlib/aspl/parser/attributes/Attribute.aspl
@@ -0,0 +1,78 @@
+import aspl.parser.utils
+import aspl.parser.ast.literals
+
+[public]
+class Attribute {
+
+ [static]
+ property map attributes = map{}
+
+ [readpublic]
+ property string identifier
+ [readpublic]
+ property list parameters
+ [public]
+ property int minimumParameterCount{
+ get{
+ var count = 0
+ foreach(parameters as parameter){
+ if(parameter.optional){
+ return count
+ }
+ count++
+ }
+ return count
+ }
+ }
+ [readpublic]
+ property AttributeUsage usage
+ [readpublic]
+ property list conflicting
+
+ [public]
+ method construct(string identifier, list parameters, AttributeUsage usage, list conflicting){
+ this.identifier = identifier
+ this.parameters = parameters
+ this.usage = usage
+ this.conflicting = conflicting
+ }
+
+ [public]
+ method canPair(Attribute other) returns bool{
+ return !this.conflicting.contains(other.identifier)
+ }
+
+ [public]
+ [static]
+ method init(){
+ self:register(new Attribute("abstract", [], AttributeUsage.Class || AttributeUsage.Method, ["static"]))
+ self:register(new Attribute("deprecated", [new Parameter("message", new Types([Type:fromString("string")]), new NullLiteral(null))], AttributeUsage.Function || AttributeUsage.Callback || AttributeUsage.Class || AttributeUsage.Property || AttributeUsage.Method || AttributeUsage.Enum || AttributeUsage.EnumField, []))
+ self:register(new Attribute("description", [new Parameter("description", new Types([Type:fromString("string")]))], AttributeUsage.Function || AttributeUsage.Callback || AttributeUsage.Class || AttributeUsage.Property || AttributeUsage.Method || AttributeUsage.Enum || AttributeUsage.EnumField, []))
+ self:register(new Attribute("flags", [], AttributeUsage.Enum, []))
+ self:register(new Attribute("threadlocal", [], AttributeUsage.Property, []))
+ self:register(new Attribute("static", [], AttributeUsage.Class || AttributeUsage.Property || AttributeUsage.Method, ["abstract"]))
+ self:register(new Attribute("public", [], AttributeUsage.Function || AttributeUsage.Class || AttributeUsage.Property || AttributeUsage.Method || AttributeUsage.Enum, ["readpublic"]))
+ self:register(new Attribute("readpublic", [], AttributeUsage.Property, ["public"]))
+ self:register(new Attribute("error", [], AttributeUsage.Class, []))
+ self:register(new Attribute("throws", [], AttributeUsage.Function || AttributeUsage.Callback || AttributeUsage.Method, []))
+ }
+
+ [public]
+ [static]
+ method register(Attribute attribute){
+ self:attributes[attribute.identifier] = attribute
+ }
+
+ [public]
+ [static]
+ method exists(string identifier) returns bool{
+ return self:attributes.containsKey(identifier)
+ }
+
+ [public]
+ [static]
+ method get(string identifier) returns Attribute{
+ return self:attributes[identifier]
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/attributes/AttributeInstance.aspl b/stdlib/aspl/parser/attributes/AttributeInstance.aspl
new file mode 100644
index 0000000..6ebd44c
--- /dev/null
+++ b/stdlib/aspl/parser/attributes/AttributeInstance.aspl
@@ -0,0 +1,25 @@
+import aspl.parser.utils
+import aspl.parser.ast.expressions
+import aspl.parser.lexer
+
+[public]
+class AttributeInstance {
+
+ [readpublic]
+ property Attribute attribute
+ [readpublic]
+ property list arguments
+ [readpublic]
+ property Location? location
+ [readpublic]
+ property list comments
+
+ [public]
+ method construct(Attribute attribute, list arguments, Location? location, list comments){
+ this.attribute = attribute
+ this.arguments = arguments
+ this.location = location
+ this.comments = comments
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/attributes/AttributeUsage.aspl b/stdlib/aspl/parser/attributes/AttributeUsage.aspl
new file mode 100644
index 0000000..3971a4a
--- /dev/null
+++ b/stdlib/aspl/parser/attributes/AttributeUsage.aspl
@@ -0,0 +1,11 @@
+[public]
+[flags]
+enum AttributeUsage {
+ Function,
+ Callback,
+ Class,
+ Property,
+ Method,
+ Enum,
+ EnumField
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/callbacks/Callback.aspl b/stdlib/aspl/parser/callbacks/Callback.aspl
new file mode 100644
index 0000000..09af058
--- /dev/null
+++ b/stdlib/aspl/parser/callbacks/Callback.aspl
@@ -0,0 +1,31 @@
+import aspl.parser.utils
+import aspl.parser.variables
+import aspl.parser.ast
+
+[public]
+class Callback {
+
+ [readpublic]
+ property Type type
+ [readpublic]
+ property list parameters
+ [readpublic]
+ property Types returnTypes
+ [public]
+ property list? code
+ [readpublic]
+ property ScopeBundle creationScope
+ [readpublic]
+ property Location location
+
+ [public]
+ method construct(Type type, list parameters, Types returnTypes, list? code, ScopeBundle creationScope, Location location){
+ this.type = type
+ this.parameters = parameters
+ this.returnTypes = returnTypes
+ this.code = code
+ this.creationScope = creationScope
+ this.location = location
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/classes/Class.aspl b/stdlib/aspl/parser/classes/Class.aspl
new file mode 100644
index 0000000..e42e5f1
--- /dev/null
+++ b/stdlib/aspl/parser/classes/Class.aspl
@@ -0,0 +1,59 @@
+import aspl.parser
+import aspl.parser.utils
+import aspl.parser.ast
+import aspl.parser.attributes
+
+[public]
+class Class {
+
+ [public]
+ [static]
+ [threadlocal]
+ property map classes = map{}
+
+ [readpublic]
+ property Type type
+ [public]
+ property list? parents
+ [readpublic]
+ property list? attributes
+ [readpublic]
+ property bool isAbstract = false
+ [readpublic]
+ property bool isStatic = false
+ [readpublic]
+ property bool isPublic = false
+ [readpublic]
+ property bool isError = false
+ [public]
+ property list? code
+ [readpublic]
+ property Module module
+ [readpublic]
+ property Location location
+
+ [public]
+ method construct(Type type, list? parents, list? attributes, list? code, Module module, Location location) {
+ this.type = type
+ this.parents = parents
+ this.attributes = attributes
+ if(attributes != null){
+ foreach(attributes as attribute){
+ // Caching these for performance reasons
+ if(attribute.attribute.identifier == "abstract"){
+ isAbstract = true
+ }elseif(attribute.attribute.identifier == "static"){
+ isStatic = true
+ }elseif(attribute.attribute.identifier == "public"){
+ isPublic = true
+ }elseif(attribute.attribute.identifier == "error"){
+ isError = true
+ }
+ }
+ }
+ this.code = code
+ this.module = module
+ this.location = location
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/enums/Enum.aspl b/stdlib/aspl/parser/enums/Enum.aspl
new file mode 100644
index 0000000..44d4dc0
--- /dev/null
+++ b/stdlib/aspl/parser/enums/Enum.aspl
@@ -0,0 +1,47 @@
+import aspl.parser
+import aspl.parser.utils
+import aspl.parser.attributes
+
+[public]
+class Enum {
+
+ [public]
+ [static]
+ [threadlocal]
+ property map enums = map{}
+
+ [readpublic]
+ property Type type
+ [readpublic]
+ property list? attributes
+ [readpublic]
+ property bool isFlags = false
+ [readpublic]
+ property bool isPublic = false
+ [public]
+ property map? fields
+ [readpublic]
+ property Module module
+ [public]
+ property Location location
+
+ [public]
+ method construct(Type type, list? attributes, map? fields, Module module, Location location) {
+ this.type = type
+ this.attributes = attributes
+ if(attributes != null){
+ foreach(attributes as attribute){
+ // Caching these for performance reasons
+ if(attribute.attribute.identifier == "flags"){
+ isFlags = true
+ }elseif(attribute.attribute.identifier == "public"){
+ isPublic = true
+ }
+ }
+ }
+ this.fields = fields
+ this.module = module
+ this.location = location
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/enums/EnumField.aspl b/stdlib/aspl/parser/enums/EnumField.aspl
new file mode 100644
index 0000000..d0165bf
--- /dev/null
+++ b/stdlib/aspl/parser/enums/EnumField.aspl
@@ -0,0 +1,27 @@
+import aspl.parser.utils
+import aspl.parser.attributes
+
+[public]
+class EnumField{
+
+ [readpublic]
+ property Enum e
+ [readpublic]
+ property string name
+ [readpublic]
+ property int? value
+ [readpublic]
+ property list attributes
+ [readpublic]
+ property Location location
+
+ [public]
+ method construct(Enum e, string name, int? value, list attributes, Location location){
+ this.e = e
+ this.name = name
+ this.attributes = attributes
+ this.value = value
+ this.location = location
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/functions/CustomFunction.aspl b/stdlib/aspl/parser/functions/CustomFunction.aspl
new file mode 100644
index 0000000..2aa1399
--- /dev/null
+++ b/stdlib/aspl/parser/functions/CustomFunction.aspl
@@ -0,0 +1,37 @@
+import aspl.parser
+import aspl.parser.utils
+import aspl.parser.ast
+import aspl.parser.attributes
+
+[public]
+class CustomFunction extends Function{
+
+ [public]
+ property list? code
+ [readpublic]
+ property Module module
+ [readpublic]
+ property Location? location
+ [readpublic]
+ property Location? headerEndLocation
+
+ [public]
+ method construct(string identifier, list parameters, Types returnTypes, list attributes, list? code, Module module, Location? location, Location? headerEndLocation){
+ parent(Function).construct(identifier, parameters, returnTypes, attributes)
+ this.fullyInitialized = code != null
+ this.code = code
+ this.module = module
+ this.location = location
+ this.headerEndLocation = headerEndLocation
+
+ foreach(attributes as attribute){
+ // Caching these for performance reasons
+ if(attribute.attribute.identifier == "public"){
+ isPublic = true
+ }elseif(attribute.attribute.identifier == "throws"){
+ canThrow = true
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/stdlib/aspl/parser/functions/Function.aspl b/stdlib/aspl/parser/functions/Function.aspl
new file mode 100644
index 0000000..06764db
--- /dev/null
+++ b/stdlib/aspl/parser/functions/Function.aspl
@@ -0,0 +1,86 @@
+import aspl.parser
+import aspl.parser.utils
+import aspl.parser.ast.literals
+import aspl.parser.attributes
+
+[public]
+[abstract]
+class Function {
+
+ [public]
+ [static]
+ [threadlocal]
+ property map functions = map{}
+
+ [readpublic]
+ property string identifier
+ [public]
+ property list parameters
+ [public]
+ property int minimumParameterCount{
+ get{
+ var count = 0
+ foreach(parameters as parameter){
+ if(parameter.optional){
+ return count
+ }
+ count++
+ }
+ return count
+ }
+ }
+ [public]
+ property Types returnTypes
+ [readpublic]
+ property list attributes
+ [readpublic]
+ property bool isPublic
+ [readpublic]
+ property bool canThrow
+ [readpublic]
+ property bool fullyInitialized = true
+ [readpublic]
+ property bool used = false
+
+ [public]
+ method construct(string identifier, list parameters, Types returnTypes, list