Compare commits

..

No commits in common. "main" and "main-v2" have entirely different histories.

85 changed files with 3421 additions and 7743 deletions

View File

@ -1,26 +0,0 @@
name: Rust
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build
run: cargo build --release --verbose
- name: Run cargo tests
run: cargo test --release --verbose
- name: Run lang tests
run: ./target/release/mcl_test_dev -m test
- name: Check formatting with clippy
run: cargo clippy -- -W clippy::pedantic -A clippy::struct-excessive-bools -A clippy::too_many_lines -A clippy::similar_names

21
.gitignore vendored
View File

@ -1,18 +1,9 @@
/target
/*
# files
!/.vscode
!/.gitignore
!/cargo.lock
!/cargo.toml
!/README.md
# folders
!/.github
!/editor
!/examples
!/include
!/playground
!/.gitkeep
!/src
!/tests
!/tools
!/include
!/Cargo*
!/REF.md
!/*.mcl

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "include"]
path = include
url = git@github.com:mc-lang/libmc.git

11
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,11 @@
{
"recommendations": [
"aaron-bond.better-comments",
"usernamehw.errorlens",
"tamasfe.even-better-toml",
"platformio.platformio-ide",
"1YiB.rust-bundle",
"tamasfe.even-better-toml",
"ms-vscode.cpptools"
]
}

45
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,45 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug executable 'mclangc-v2'",
"cargo": {
"args": [
"build",
"--bin=mclangc-v2",
"--package=mclangc-v2"
],
"filter": {
"name": "mclangc-v2",
"kind": "bin"
}
},
"args": ["-o", "test", "test.mcl", "-r"],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug unit tests in executable 'mclangc-v2'",
"cargo": {
"args": [
"test",
"--no-run",
"--bin=mclangc-v2",
"--package=mclangc-v2"
],
"filter": {
"name": "mclangc-v2",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}"
}
]
}

5
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"files.associations": {
"*.s": "platformio-debug.asm"
}
}

331
Cargo.lock generated
View File

@ -3,46 +3,106 @@
version = 3
[[package]]
name = "anyhow"
version = "1.0.79"
name = "anstream"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
[[package]]
name = "anstyle-parse"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "anyhow"
version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[package]]
name = "cc"
version = "1.0.79"
name = "camino"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c"
[[package]]
name = "clap"
version = "4.1.8"
version = "4.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5"
checksum = "b230ab84b0ffdf890d5a10abdbc8b83ae1c4918275daea1ab8801f71536b2651"
dependencies = [
"bitflags",
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"is-terminal",
"once_cell",
"strsim",
"termcolor",
]
[[package]]
name = "clap_derive"
version = "4.1.8"
version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0"
checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
@ -50,33 +110,15 @@ dependencies = [
[[package]]
name = "clap_lex"
version = "0.3.2"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09"
dependencies = [
"os_str_bytes",
]
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]]
name = "errno"
version = "0.2.8"
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "heck"
@ -85,132 +127,89 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.3.1"
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "io-lifetimes"
version = "1.0.6"
name = "map-macro"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3"
dependencies = [
"libc",
"windows-sys",
]
checksum = "fb950a42259642e5a3483115aca87eebed2a64886993463af9c9739c205b8d3a"
[[package]]
name = "is-terminal"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857"
dependencies = [
"hermit-abi",
"io-lifetimes",
"rustix",
"windows-sys",
]
[[package]]
name = "libc"
version = "0.2.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
[[package]]
name = "linux-raw-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "mclangc"
name = "mclangc-v2"
version = "0.1.0"
dependencies = [
"anyhow",
"bitflags",
"camino",
"clap",
"clap_derive",
"lazy_static",
"map-macro",
"parse_int",
"snailquote",
]
[[package]]
name = "once_cell"
version = "1.17.1"
name = "num-traits"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "os_str_bytes"
version = "6.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
"autocfg",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
name = "parse_int"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
checksum = "2d695b79916a2c08bcff7be7647ab60d1402885265005a6658ffe6d763553c5a"
dependencies = [
"proc-macro2",
"quote",
"version_check",
"num-traits",
]
[[package]]
name = "proc-macro2"
version = "1.0.51"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.23"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustix"
version = "0.36.9"
name = "snailquote"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc"
checksum = "ec62a949bda7f15800481a711909f946e1204f2460f89210eaf7f57730f88f86"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
"thiserror",
"unicode_categories",
]
[[package]]
name = "strsim"
version = "0.10.0"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
[[package]]
name = "syn"
version = "1.0.109"
version = "2.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
dependencies = [
"proc-macro2",
"quote",
@ -218,71 +217,57 @@ dependencies = [
]
[[package]]
name = "termcolor"
version = "1.2.0"
name = "thiserror"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
dependencies = [
"winapi-util",
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.8"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "version_check"
version = "0.9.4"
name = "unicode_categories"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
[[package]]
name = "winapi"
version = "0.3.9"
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "windows-sys"
version = "0.45.0"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.1"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
@ -295,42 +280,42 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.1"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.1"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
[[package]]
name = "windows_i686_gnu"
version = "0.42.1"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
[[package]]
name = "windows_i686_msvc"
version = "0.42.1"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.1"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.1"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.1"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"

View File

@ -1,13 +1,19 @@
[package]
name = "mclangc"
description="The McLang Programming language compiler"
name = "mclangc-v2"
version = "0.1.0"
edition = "2021"
authors=[
"MCorange <mcorangecodes@gmail.com> (https://mcorangehq.xyz/)"
]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.79"
clap = { version = "4.1.8", features = ["derive"] }
anyhow = "1.0.80"
bitflags = "2.4.2"
camino = "1.1.6"
clap = { version = "4.5.2", features = ["derive"] }
clap_derive = "4.5.0"
lazy_static = "1.4.0"
map-macro = "0.3.0"
parse_int = "0.6.0"
# serde = { version = "1.0.197", features = ["derive"] }
# regex = "1.10.3"
snailquote = "0.3.1"

View File

@ -1,31 +0,0 @@
# mclang rev2
This is the second revision of [MCLang](https://github.com/mc-lang/mclang) now written in rust!
## Goals
✅ - relatevely usable by normal programmers
✅ - speed comparable to unoptimised C (sometimes)
✅ - static typing
❌ - self hosted (maybe better if not? Since rust is fast asf)
❌ - multiplatform (~~windows~~, linux and mac)
✅ - interop with other languages
❌ - package manager
❌ - installer
## Documentation
The docs are currently are just made in MarkDown.
You can find the docs [here](/docs/index.md)
## Cheatsheet
Usefull things that i search for a lot in the sourcecode so i added them here
add them in reverse order in mclang
Syscall arg order: \[rax ,rdi ,rsi ,rdx ,r10 ,r8 ,r9\]
## Credits
[MCotange](https://github.com/MCorange99) - The one and only me, the creator and current maintainer or mclang rev1 and rev2

53
REF.md Normal file
View File

@ -0,0 +1,53 @@
# Reference
```mclang
typedef str do int ptr end // [int, ptr]
include "std.mcl"
const sizeof(u8) 1 end
const sizeof(u16) 2 end
const sizeof(u32) 4 end
const sizeof(u64) 8 end
structdef Foo do
buz do sizeof(u64) end
baz do sizeof(u64) end
done
memory s_foo Foo end
//? Comments :3
extern fn a with void returns void then done
inline fn b with void returns void then done
export fn c with void returns void then done
fn puts with str returns void then drop drop done
// fn putd with int returns void then drop done
fn main with int ptr returns int then
// 1 2 add
69 _dbg_print
"Hewo" puts
if 3 4 eq do
"omg what impossible!\n"
else if 1 1 eq do
"whaaaaaaaaa\n"
else
"finally, some good soup\n"
done
puts
10
while dup 0 gt do
"uwu" puts
dup _dbg_print
1
done
done
```

View File

@ -1,2 +0,0 @@
**/*.log
**/node_modules/

View File

@ -1,17 +0,0 @@
// A launch configuration that launches the extension inside a new window
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"name": "Extension",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
]
}
]
}

View File

@ -1,4 +0,0 @@
.vscode/**
.vscode-test/**
.gitignore
vsc-extension-quickstart.md

View File

@ -1,9 +0,0 @@
# Change Log
All notable changes to the "mclang" extension will be documented in this file.
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
## [Unreleased]
- Initial release

View File

View File

@ -1,17 +0,0 @@
# mclang README
Code highlghting for mclang 1 and 2
## Known Issues
None
## Release Notes
Users appreciate release notes as you update your extension.
### 1.0.0
Initial release of mclang
**Enjoy!**

View File

@ -1,30 +0,0 @@
{
"comments": {
// symbol used for single line comment. Remove this entry if your language does not support line comments
"lineComment": "//",
// symbols used for start and end a block comment. Remove this entry if your language does not support block comments
"blockComment": [ "/*", "*/" ]
},
// symbols used as brackets
"brackets": [
["{", "}"],
["[", "]"],
["(", ")"]
],
// symbols that are auto closed when typing
"autoClosingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""],
["'", "'"]
],
// symbols that can be used to surround a selection
"surroundingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["\"", "\""],
["'", "'"]
]
}

Binary file not shown.

View File

@ -1,42 +0,0 @@
{
"name": "mclang",
"displayName": "mclang",
"description": "Code highlighting for mclang",
"version": "0.0.3",
"repository": {
"type": "git",
"url": "git@github.com:mc-lang/mclang2.git"
},
"engines": {
"vscode": "^1.54.0"
},
"categories": [
"Programming Languages"
],
"contributes": {
"languages": [
{
"id": "mclang",
"aliases": [
"MCLang",
"mclang"
],
"extensions": [
".mcl"
],
"configuration": "./language-configuration.json"
}
],
"grammars": [
{
"language": "mclang",
"scopeName": "source.mcl",
"path": "./syntaxes/mclang.tmLanguage.json"
}
]
},
"dependencies": {
"generator-code": "^1.7.4",
"@vscode/vsce": "^2.15.0"
}
}

View File

@ -1,147 +0,0 @@
{
"name": "MCLang",
"fileTypes": [
"mcl"
],
"scopeName": "source.mcl",
"patterns": [
{
"include": "#errors"
},
{
"include": "#keywords"
},
{
"include": "#definitions"
},
{
"include": "#placeholders"
},
{
"include": "#strings"
},
{
"include": "#comments"
},
{
"include": "#intrinsics"
},
{
"include": "#constants-and-special-vars"
}
],
"repository": {
"errors": {
"patterns": [
{
"name": "invalid.illegal",
"match": "(?<=^|\\s)(?:const|memory)\\s+(end)(?:$|\\s)"
},
{
"name": "invalid.illegal",
"match": "(?<=^|\\s)(?:fn)\\s+(done)(?:$|\\s)"
},
{
"name": "invalid.illegal",
"match": "(?<=^|\\s)(memory|const)\\s+\\S*(\\s+|$)end(?:\n|\\s)"
},
{
"name": "invalid.illegal",
"match": "(?<=^|\\s)(inline)\\s+(?!fn(\\s|$))"
}
]
},
"keywords": {
"patterns": [
{
"name": "keyword.declaration.mclang",
"match": "(?<=\\s|^)(macro|memory|fn|const|in|inline|include|assert|offset|addr-of|call-like|reset|let|peek|with|returns)(?:\\s|$)"
},
{
"name": "keyword.control.mclang",
"match": "(?<=\\s|^)(if|else|elif|end|done|then|while|do|if\\*)(?:\\s|$)"
}
]
},
"definitions": {
"patterns": [
{
"name": "support.class.mclang",
"match": "(?<=(macro|memory|fn|const)\\s+)(\\S*)"
},
{
"name": "support.class.mclang",
"match": "(?<=(let|peek)\\s+)\\S+.*(?=\\s+(in))"
}
]
},
"placeholders": {
"patterns": [
{
"name": "markup.italic.mclang",
"match": "(?<=(\\s|^))_[\\S]*_(?:(\\s|$))"
}
]
},
"strings": {
"patterns": [
{
"name": "string.quoted.double.mclang",
"begin": "\"",
"end": "\"",
"patterns": [
{
"name": "constant.character.escape.mclang",
"match": "\\\\."
}
]
},
{
"name": "string.quoted.single.mclang",
"begin": "'",
"end": "'",
"patterns": [
{
"name": "constant.character.escape.mclang",
"match": "\\\\."
}
]
}
]
},
"comments": {
"patterns": [
{
"name": "constant.other.character-class.regexp",
"match": "(?://\\s*)(TODO(O*)|FIXME).*"
},
{
"name": "comment.line.double-slash.mclang",
"match": "(//(?!\\s?(TODO(O*)|FIXME)(\\s|:|$)).*|//\\s*)"
}
]
},
"intrinsics": {
"patterns": [
{
"name": "variable.name.source.mclang",
"match": "(?<=^|\\s)(\\+|-|\\*|int|ptr|bool|addr|any|void|max|divmod|_dbg_print|=|>|<|>=|<=|!=|>>|<<|\\||&|not|dup|swap|drop|over|rot|argc|argv|here|syscall0|syscall1|syscall2|syscall3|syscall4|syscall5|syscall6|\\?\\?\\?)(?=>$|\\s)"
}
]
},
"constants-and-special-vars": {
"patterns": [
{
"name": "constant.numeric.mclang",
"match": "\\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\\.?[0-9]*)|(\\.[0-9]+))((e|E)(\\+|-)?[0-9]+)?)\\b(?!\\$)"
},
{
"name": "entity.name.function.mclang",
"match": "(?<=^|\\s)(NULL|true|false|cast(ptr)|cast(int)|cast(bool)|sizeof\\(u64\\)|sizeof\\(u32\\)|sizeof\\(ptr\\)|sizeof\\(bool\\)|sizeof\\(int\\)|sizeof\\(addr\\)|STDIN|STDOUT|STDERR|@ptr|@@ptr|@bool|@int|@addr|!bool|!ptr|!int|!addr|AT_FDCWD|O_RDONLY|O_WRONLY|O_RDWR|O_CREAT|O_TRUNC|O_NONBLOCK|F_SETFL|F_GETFL|EAGAIN|CLOCK_MONOTONIC|TIMER_ABSTIME|MAP_PRIVATE|MAP_ANONYMOUS|PROT_READ|PROT_WRITE|SIGQUIT|timespec\\.tv_sec|timespec\\.tv_nsec|sizeof\\(timespec\\)|ptr\\+|ptr-|ptr!=|ptr=|ptr<|\\+ptr|ptr-diff|sizeof\\(stat\\)|stat\\.st_dev|stat\\.st_ino|stat\\.st_mode|stat\\.st_nlink|stat\\.st_uid|stat\\.st_gid|stat\\.st_rdev|stat\\.st_size|@stat\\.st_size|stat\\.st_blksize|stat\\.st_blocks|stat\\.st_atim|stat\\.st_mtim|stat\\.st_ctim|sizeof\\(stat\\.st_dev\\)|sizeof\\(stat\\.st_ino\\)|sizeof\\(stat\\.st_mode\\)|sizeof\\(stat\\.st_nlink\\)|sizeof\\(stat\\.st_uid\\)|sizeof\\(stat\\.st_gid\\)|sizeof\\(stat\\.st_rdev\\)|sizeof\\(stat\\.st_size\\)|sizeof\\(stat\\.st_blksize\\)|sizeof\\(stat\\.st_blocks\\)|sizeof\\(stat\\.st_atim\\)|sizeof\\(stat\\.st_mtim\\)|sizeof\\(stat\\.st_ctim\\)|write|read|openat|fstat|stat|close|exit|mmap|clock_nanosleep|clock_gettime|fork|getpid|execve|wait4|rename|fcntl|kill|dup2|/|%|mod|div|imod|idiv|emod|nth_argv|lnot|land|lor|inc64-by|inc64|dec64|inc32|dec32|inc8|dec8|swap64|cstrlen|cstreq|cstr-to-str|fputs|puts|eputs|WIFSTOPPED|WIFCONTINUED|WIFSIGNALED|WTERMSIG|WIFEXITED|WEXITSTATUS|offsetof\\(Str\\.count\\)|offsetof\\(Str\\.data\\)|sizeof\\(Str\\)|Str\\.count|Str\\.data|@Str\\.count|@Str\\.data|!Str\\.count|!Str\\.data|@Str|!Str|str-chop-one-left|str-chop-one-right|\\?space|str-trim-left|str-chop-by-predicate|str-chop-by-delim|str-starts-with|\\?str-empty|streq|\\?digit|isdigit|\\?alpha|isalpha|\\?alnum|isalnum|try-parse-int|PUTU_BUFFER_CAP|fputu|fput0u|putu|put0u|eputu|memcpy|memset|srand|RAND_A|RAND_C|rand|getenv|TMP_CAP|tmp-clean|tmp-end|tmp-rewind|tmp-alloc|tmp-str-to-cstr|tmp-append|tmp-append-ptr|execvp|append-item|tmp-utos|map-file|\\?file-exist|\\?shell-safe-char|\\?shell-safe-str|shell-escape|timeit/from-here|1e9|timeit/to-here|str-rfind|dirname|putch|remove-ext|cmd-echoed)(?:\\s|$)"
}
]
}
}
}

View File

@ -1,29 +0,0 @@
# Welcome to your VS Code Extension
## What's in the folder
* This folder contains all of the files necessary for your extension.
* `package.json` - this is the manifest file in which you declare your language support and define the location of the grammar file that has been copied into your extension.
* `syntaxes/mclang.tmLanguage.json` - this is the Text mate grammar file that is used for tokenization.
* `language-configuration.json` - this is the language configuration, defining the tokens that are used for comments and brackets.
## Get up and running straight away
* Make sure the language configuration settings in `language-configuration.json` are accurate.
* Press `F5` to open a new window with your extension loaded.
* Create a new file with a file name suffix matching your language.
* Verify that syntax highlighting works and that the language configuration settings are working.
## Make changes
* You can relaunch the extension from the debug toolbar after making changes to the files listed above.
* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
## Add more language features
* To add features such as IntelliSense, hovers and validators check out the VS Code extenders documentation at https://code.visualstudio.com/docs
## Install your extension
* To start using your extension with Visual Studio Code copy it into the `<user home>/.vscode/extensions` folder and restart Code.
* To share your extension with the world, read on https://code.visualstudio.com/docs about publishing an extension.

File diff suppressed because it is too large Load Diff

3
examples/.gitignore vendored
View File

@ -1,3 +0,0 @@
/*
!/.gitignore
!/*.mcl

View File

@ -1,4 +0,0 @@
include "std.mcl"
"Henlo World! :3\n" puts

View File

@ -1,39 +0,0 @@
include "std.mcl"
macro BOARD_SIZE 100 end
mem BOARD_SIZE 2 - + 1 @8
0 while dup BOARD_SIZE 2 - < do
0 while dup BOARD_SIZE < do
dup mem + !8 if
dup mem + BOARD_SIZE + '*' @8
else
dup mem + BOARD_SIZE + ' ' @8
end
1 +
end
mem + BOARD_SIZE + '\n' @8
BOARD_SIZE 1 + mem BOARD_SIZE + puts
// pattern
mem !8 1 shl
mem 1 + !8
bor
1 while dup BOARD_SIZE 2 - < do
swap 1 shl 7 band
over mem + 1 + !8 bor
2dup 110 swap shr 1 band
swap mem + swap @8
swap
1 +
end
drop drop
1 +
end
drop

View File

@ -1,5 +0,0 @@
0 while dup 100 < do
dup print
1 +
end

1
include Submodule

@ -0,0 +1 @@
Subproject commit 33cccedea1ed7b86531f250127654d1973d8e36a

View File

@ -1 +0,0 @@
// todo: add some sort of macrow

View File

@ -1,29 +0,0 @@
const FS_O_RDONLY 0 end
const FS_O_WRONLY 1 end
const FS_O_RDWR 2 end
const FS_O_APPEND 1024 end // append to existing file
const FS_O_TRUNC 512 end // if file exists, ovewrite it (careful!)
const FS_O_CREAT 64 end // create file if it doesnt exist
const FS_O_ASYNC 8192 end // use signal-driven IO
const FS_O_CLOEXEC 524288 end // use close-on-exec (avoid race conditions and lock contentions)
const FS_O_DIRECT 16384 end // bypass cache (slower)
const FS_O_DIRECTORY 65536 end // fail if pathname isnt a directory
const FS_O_DSYNC 4096 end // ensure output is sent to hardware and metadata written before return
const FS_O_EXCL 128 end // ensure creation of file
const FS_O_LARGEFILE 0 end // allows use of file sizes represented by off64_t
const FS_O_NOATIME 262144 end // do not increment access time upon open
const FS_O_NOCTTY 256 end // if pathname is a terminal device, dont become controlling terminal
const FS_O_NOFOLLOW 131072 end // fail if pathname is symbolic link
const FS_O_NONBLOCK 2048 end // if possible, open file with non-blocking IO
const FS_O_NDELAY 2048 end // same as O_NONBLOCK
const FS_O_PATH 2097152 end // open descriptor for obtaining permissions and status of a file but does not allow read/write operations
const FS_O_SYNC 1052672 end // wait for IO to complete before returning
const FS_O_TMPFILE 4259840 end // create an unnamed, unreachable (via any other open call) temporary file
fn fs_read_to_string with int ptr returns int ptr then
done

View File

@ -1,20 +0,0 @@
const NULL 0 end
const false 0 end
const true 1 end
inline fn div with int int returns int then divmod drop done
inline fn mod with int int returns int then divmod swap drop done
inline fn dup2 with any any returns any any any any then over over done
inline fn drop2 with any any returns void then drop drop done
const sizeof(u64) 8 end
const sizeof(u32) 4 end
const sizeof(u16) 2 end
const sizeof(u8) 1 end
const u64 8 end
const u32 4 end
const u16 2 end
const u8 1 end

View File

@ -1,61 +0,0 @@
// Write to a file descriptor using the SYS_write syscall
// args: [buff_size, buff_ptr, fd]
// @arg buff_size: Int - number of bytes to write
// @arg buff_ptr: Ptr - pointer to the buffer to write
// @arg fd: Int - file descriptor
// @ret Int
inline fn fwrite with int ptr int returns int then
SYS_write syscall3
done
// Write to a file descriptor using the SYS_write syscall
// args: [buff_size, buff_ptr, fd]
// @arg buff_size: Int - number of bytes to write
// @arg buff_ptr: Ptr - pointer to the buffer to write
// @arg fd: Int - file descriptor
// @ret Int
inline fn fread with int ptr int returns int then
SYS_read syscall3
done
// Write to a file descriptor using the SYS_write syscall
// args: [buff_ptr, flags, mode]
// @arg buff_ptr: Ptr - File to open
// @arg flags: Int - Flags
// @arg mode: Int - Mode
// @ret Int - Fd
inline fn fopen with int ptr int returns int then
SYS_open syscall3
done
// Print a string to STDOUT
// args: [str_size, str_ptr]
// @arg buff_size: Int - number of bytes to write
// @arg buff_ptr: Ptr - pointer to the buffer to write
// @ret NULL
inline fn puts with int ptr returns void then
STDOUT fwrite drop
done
// Print a string to STDERR
// args: [str_size, str_ptr]
// @arg buff_size: Int - number of bytes to write
// @arg buff_ptr: Ptr - pointer to the buffer to write
// @ret NULL
inline fn eputs with int ptr returns void then
STDOUT fwrite drop
done
// TODO: make putc, eputc, putd, and eputd after we make local mem
// Exit the program with exit_code
// args: [exit_code]
// @arg exit_code: Int
// @ret NULL/NEVER
inline fn exit with int returns void then
SYS_exit syscall1 drop
done

View File

@ -1,322 +0,0 @@
// file descriptors
const STDIN 0 end
const STDOUT 1 end
const STDERR 2 end
// syscalls
const SYS_read 0 end
const SYS_write 1 end
const SYS_open 2 end
const SYS_close 3 end
const SYS_stat 4 end
const SYS_fstat 5 end
const SYS_lstat 6 end
const SYS_poll 7 end
const SYS_lseek 8 end
const SYS_mmap 9 end
const SYS_mprotect 10 end
const SYS_munmap 11 end
const SYS_brk 12 end
const SYS_rt_sigaction 13 end
const SYS_rt_sigprocmask 14 end
const SYS_rt_sigreturn 15 end
const SYS_ioctl 16 end
const SYS_pread64 17 end
const SYS_pwrite64 18 end
const SYS_readv 19 end
const SYS_writev 20 end
const SYS_access 21 end
const SYS_pipe 22 end
const SYS_select 23 end
const SYS_sched_yield 24 end
const SYS_mremap 25 end
const SYS_msync 26 end
const SYS_mincore 27 end
const SYS_madvise 28 end
const SYS_shmget 29 end
const SYS_shmat 30 end
const SYS_shmctl 31 end
const SYS_dup 32 end
const SYS_dup2 33 end
const SYS_pause 34 end
const SYS_nanosleep 35 end
const SYS_getitimer 36 end
const SYS_alarm 37 end
const SYS_setitimer 38 end
const SYS_getpid 39 end
const SYS_sendfile 40 end
const SYS_socket 41 end
const SYS_connect 42 end
const SYS_accept 43 end
const SYS_sendto 44 end
const SYS_recvfrom 45 end
const SYS_sendmsg 46 end
const SYS_recvmsg 47 end
const SYS_shutdown 48 end
const SYS_bind 49 end
const SYS_listen 50 end
const SYS_getsockname 51 end
const SYS_getpeername 52 end
const SYS_socketpair 53 end
const SYS_setsockopt 54 end
const SYS_getsockopt 55 end
const SYS_clone 56 end
const SYS_fork 57 end
const SYS_vfork 58 end
const SYS_execve 59 end
const SYS_exit 60 end
const SYS_wait4 61 end
const SYS_kill 62 end
const SYS_uname 63 end
const SYS_semget 64 end
const SYS_semop 65 end
const SYS_semctl 66 end
const SYS_shmdt 67 end
const SYS_msgget 68 end
const SYS_msgsnd 69 end
const SYS_msgrcv 70 end
const SYS_msgctl 71 end
const SYS_fcntl 72 end
const SYS_flock 73 end
const SYS_fsync 74 end
const SYS_fdatasync 75 end
const SYS_truncate 76 end
const SYS_ftruncate 77 end
const SYS_getdents 78 end
const SYS_getcwd 79 end
const SYS_chdir 80 end
const SYS_fchdir 81 end
const SYS_rename 82 end
const SYS_mkdir 83 end
const SYS_rmdir 84 end
const SYS_creat 85 end
const SYS_link 86 end
const SYS_unlink 87 end
const SYS_symlink 88 end
const SYS_readlink 89 end
const SYS_chmod 90 end
const SYS_fchmod 91 end
const SYS_chown 92 end
const SYS_fchown 93 end
const SYS_lchown 94 end
const SYS_umask 95 end
const SYS_gettimeofday 96 end
const SYS_getrlimit 97 end
const SYS_getrusage 98 end
const SYS_sysinfo 99 end
const SYS_times 100 end
const SYS_ptrace 101 end
const SYS_getuid 102 end
const SYS_syslog 103 end
const SYS_getgid 104 end
const SYS_setuid 105 end
const SYS_setgid 106 end
const SYS_geteuid 107 end
const SYS_getegid 108 end
const SYS_setpgid 109 end
const SYS_getppid 110 end
const SYS_getpgrp 111 end
const SYS_setsid 112 end
const SYS_setreuid 113 end
const SYS_setregid 114 end
const SYS_getgroups 115 end
const SYS_setgroups 116 end
const SYS_setresuid 117 end
const SYS_getresuid 118 end
const SYS_setresgid 119 end
const SYS_getresgid 120 end
const SYS_getpgid 121 end
const SYS_setfsuid 122 end
const SYS_setfsgid 123 end
const SYS_getsid 124 end
const SYS_capget 125 end
const SYS_capset 126 end
const SYS_rt_sigpending 127 end
const SYS_rt_sigtimedwait 128 end
const SYS_rt_sigqueueinfo 129 end
const SYS_rt_sigsuspend 130 end
const SYS_sigaltstack 131 end
const SYS_utime 132 end
const SYS_mknod 133 end
const SYS_uselib 134 end
const SYS_personality 135 end
const SYS_ustat 136 end
const SYS_statfs 137 end
const SYS_fstatfs 138 end
const SYS_sysfs 139 end
const SYS_getpriority 140 end
const SYS_setpriority 141 end
const SYS_sched_setparam 142 end
const SYS_sched_getparam 143 end
const SYS_sched_setscheduler 144 end
const SYS_sched_getscheduler 145 end
const SYS_sched_get_priority_max 146 end
const SYS_sched_get_priority_min 147 end
const SYS_sched_rr_get_interval 148 end
const SYS_mlock 149 end
const SYS_munlock 150 end
const SYS_mlockall 151 end
const SYS_munlockall 152 end
const SYS_vhangup 153 end
const SYS_modify_ldt 154 end
const SYS_pivot_root 155 end
const SYS__sysctl 156 end
const SYS_prctl 157 end
const SYS_arch_prctl 158 end
const SYS_adjtimex 159 end
const SYS_setrlimit 160 end
const SYS_chroot 161 end
const SYS_sync 162 end
const SYS_acct 163 end
const SYS_settimeofday 164 end
const SYS_mount 165 end
const SYS_umount2 166 end
const SYS_swapon 167 end
const SYS_swapoff 168 end
const SYS_reboot 169 end
const SYS_sethostname 170 end
const SYS_setdomainname 171 end
const SYS_iopl 172 end
const SYS_ioperm 173 end
const SYS_create_module 174 end
const SYS_init_module 175 end
const SYS_delete_module 176 end
const SYS_get_kernel_syms 177 end
const SYS_query_module 178 end
const SYS_quotactl 179 end
const SYS_nfsservctl 180 end
const SYS_getpmsg 181 end
const SYS_putpmsg 182 end
const SYS_afs_syscall 183 end
const SYS_tuxcall 184 end
const SYS_security 185 end
const SYS_gettid 186 end
const SYS_readahead 187 end
const SYS_setxattr 188 end
const SYS_lsetxattr 189 end
const SYS_fsetxattr 190 end
const SYS_getxattr 191 end
const SYS_lgetxattr 192 end
const SYS_fgetxattr 193 end
const SYS_listxattr 194 end
const SYS_llistxattr 195 end
const SYS_flistxattr 196 end
const SYS_removexattr 197 end
const SYS_lremovexattr 198 end
const SYS_fremovexattr 199 end
const SYS_tkill 200 end
const SYS_time 201 end
const SYS_futex 202 end
const SYS_sched_setaffinity 203 end
const SYS_sched_getaffinity 204 end
const SYS_set_thread_area 205 end
const SYS_io_setup 206 end
const SYS_io_destroy 207 end
const SYS_io_getevents 208 end
const SYS_io_submit 209 end
const SYS_io_cancel 210 end
const SYS_get_thread_area 211 end
const SYS_lookup_dcookie 212 end
const SYS_epoll_create 213 end
const SYS_epoll_ctl_old 214 end
const SYS_epoll_wait_old 215 end
const SYS_remap_file_pages 216 end
const SYS_getdents64 217 end
const SYS_set_tid_address 218 end
const SYS_restart_syscall 219 end
const SYS_semtimedop 220 end
const SYS_fadvise64 221 end
const SYS_timer_create 222 end
const SYS_timer_settime 223 end
const SYS_timer_gettime 224 end
const SYS_timer_getoverrun 225 end
const SYS_timer_delete 226 end
const SYS_clock_settime 227 end
const SYS_clock_gettime 228 end
const SYS_clock_getres 229 end
const SYS_clock_nanosleep 230 end
const SYS_exit_group 231 end
const SYS_epoll_wait 232 end
const SYS_epoll_ctl 233 end
const SYS_tgkill 234 end
const SYS_utimes 235 end
const SYS_vserver 236 end
const SYS_mbind 237 end
const SYS_set_mempolicy 238 end
const SYS_get_mempolicy 239 end
const SYS_mq_open 240 end
const SYS_mq_unlink 241 end
const SYS_mq_timedsend 242 end
const SYS_mq_timedreceive 243 end
const SYS_mq_notify 244 end
const SYS_mq_getsetattr 245 end
const SYS_kexec_load 246 end
const SYS_waitid 247 end
const SYS_add_key 248 end
const SYS_request_key 249 end
const SYS_keyctl 250 end
const SYS_ioprio_set 251 end
const SYS_ioprio_get 252 end
const SYS_inotify_init 253 end
const SYS_inotify_add_watch 254 end
const SYS_inotify_rm_watch 255 end
const SYS_migrate_pages 256 end
const SYS_openat 257 end
const SYS_mkdirat 258 end
const SYS_mknodat 259 end
const SYS_fchownat 260 end
const SYS_futimesat 261 end
const SYS_newfstatat 262 end
const SYS_unlinkat 263 end
const SYS_renameat 264 end
const SYS_linkat 265 end
const SYS_symlinkat 266 end
const SYS_readlinkat 267 end
const SYS_fchmodat 268 end
const SYS_faccessat 269 end
const SYS_pselect6 270 end
const SYS_ppoll 271 end
const SYS_unshare 272 end
const SYS_set_robust_list 273 end
const SYS_get_robust_list 274 end
const SYS_splice 275 end
const SYS_tee 276 end
const SYS_sync_file_range 277 end
const SYS_vmsplice 278 end
const SYS_move_pages 279 end
const SYS_utimensat 280 end
const SYS_epoll_pwait 281 end
const SYS_signalfd 282 end
const SYS_timerfd_create 283 end
const SYS_eventfd 284 end
const SYS_fallocate 285 end
const SYS_timerfd_settime 286 end
const SYS_timerfd_gettime 287 end
const SYS_accept4 288 end
const SYS_signalfd4 289 end
const SYS_eventfd2 290 end
const SYS_epoll_create1 291 end
const SYS_dup3 292 end
const SYS_pipe2 293 end
const SYS_inotify_init1 294 end
const SYS_preadv 295 end
const SYS_pwritev 296 end
const SYS_rt_tgsigqueueinfo 297 end
const SYS_perf_event_open 298 end
const SYS_recvmmsg 299 end
const SYS_fanotify_init 300 end
const SYS_fanotify_mark 301 end
const SYS_prlimit64 302 end
const SYS_name_to_handle_at 303 end
const SYS_open_by_handle_at 304 end
const SYS_clock_adjtime 305 end
const SYS_syncfs 306 end
const SYS_sendmmsg 307 end
const SYS_setns 308 end
const SYS_getcpu 309 end
const SYS_process_vm_readv 310 end
const SYS_process_vm_writev 311 end
const SYS_kcmp 312 end
const SYS_finit_module 313 end

View File

@ -1 +0,0 @@

View File

@ -1,6 +0,0 @@
include "linux.mcl"
include "io.mcl"
include "util.mcl"
include "int.mcl"
include "fs.mcl"
include "compat.mcl"

View File

@ -1,5 +0,0 @@
fn cstr_len with ptr returns int then
dup while dup load8 '\0' != do 1 + end swap -
done

View File

@ -1,15 +0,0 @@
// Assert implementation
// args: [condition, str_len, str_ptr]
// @arg condition: Bool
// @arg str_len: Int
// @arg str_ptr: Ptr
// @ret NULL/NEVER
fn assert with bool int ptr returns void then
rot
if else
"Assert failed: \"" eputs eputs
"\". Exiting!\n" eputs
1 exit
end
done

View File

@ -1,4 +0,0 @@
/*
!/.gitignore
!/*.mcl
!/mclangc

View File

@ -1,13 +0,0 @@
#!/usr/bin/bash
#? This is just a passthrough for the compiled mclangc binary for ease of use
#? It also compiles mclangc every time its ran
pushd ../ > /dev/null
cargo build --release -q
popd > /dev/null
../target/release/mclangc -I../include ${@:1}

View File

@ -1,35 +0,0 @@
include "std.mcl"
struct StatDef do
val -> u32
val2 -> i8
end
alloc Stat StatDef end
fn main with int ptr returns void then
// p l
"Hello!\n" puts
Stat.val 69 write32
Stat.val read32 _dbg_print
Stat.__size read32 _dbg_print
// memory fd 4 end
// NULL
// FS_O_RDWR
// c"/home/mcorange/@Projects/rust/programming_languages/mclang/mclangc/playground/test.mcl"
// fopen
// dup _dbg_print
// fd swap write32
done

View File

@ -1,171 +0,0 @@
use std::path::{PathBuf, Path};
use std::process::Stdio;
use std::{process, fs};
use clap::Parser;
use anyhow::{Result, bail};
pub mod color {
#![allow(dead_code)]
pub const NONE: &str = "\x1b[0m";
pub const RESET: &str = "\x1b[0m";
pub const BRIGHT: &str = "\x1b[1m";
pub const DIM: &str = "\x1b[2m";
pub const UNDERSCORE: &str = "\x1b[4m";
pub const BLINK: &str = "\x1b[5m";
pub const REVERSE: &str = "\x1b[7m";
pub const HIDDEN: &str = "\x1b[8m";
pub const FG_BLACK: &str = "\x1b[30m";
pub const FG_RED: &str = "\x1b[31m";
pub const FG_GREEN: &str = "\x1b[32m";
pub const FG_YELLOW: &str = "\x1b[33m";
pub const FG_BLUE: &str = "\x1b[34m";
pub const FG_MAGENTA: &str = "\x1b[35m";
pub const FG_CYAN: &str = "\x1b[36m";
pub const FG_WHITE: &str = "\x1b[37m";
pub const BG_BLACK: &str = "\x1b[40m";
pub const BG_RED: &str = "\x1b[41m";
pub const BG_GREEN: &str = "\x1b[42m";
pub const BG_YELLOW: &str = "\x1b[43m";
pub const BG_BLUE: &str = "\x1b[44m";
pub const BG_MAGENTA: &str = "\x1b[45m";
pub const BG_CYAN: &str = "\x1b[46m";
pub const BG_WHITE: &str = "\x1b[47m";
}
#[allow(dead_code)]
#[derive(Debug)]
struct TestOutput {
stdout: String,
stderr: String,
stdin: String,
status: i32
}
fn run_test<P: Into<PathBuf> + std::convert::AsRef<std::ffi::OsStr>>(f_in: PathBuf, f_out: &PathBuf, compiler: P, compile_mode: bool, stdin: String) -> Result<TestOutput> {
let mut command = process::Command::new(compiler);
command.stdout(Stdio::piped());
command.stderr(Stdio::piped());
if compile_mode {
command.arg("-cqr");
} else {
command.arg("-sq");
}
command.arg("-i");
command.arg(f_in);
command.arg("-o");
command.arg(f_out);
let child = command.spawn()?;
let out = child.wait_with_output()?;
let stdout = out.stdout.iter().map(|c| {
char::from_u32(u32::from(*c)).expect("Failed to parse stdout char").to_string()
}).collect::<String>();
let stderr = out.stderr.iter().map(|c| {
char::from_u32(u32::from(*c)).expect("Failed to parse stderr char").to_string()
}).collect::<String>();
Ok(TestOutput {
stdout,
stderr,
stdin,
status: out.status.code().unwrap()
})
}
fn run_tests(args: Args) -> Result<()>{
let files = fs::read_dir(args.input)?;
for file in files {
let file = file?;
let f_name = file.file_name().to_string_lossy().to_string();
let f_out = PathBuf::from(&args.output).join(f_name);
let intp = run_test(file.path(), &f_out, &args.compiler_path, false, String::new())?;
let comp = run_test(file.path(), &f_out, &args.compiler_path, true, String::new())?;
compare_results(&intp, &comp, &file.path())?;
}
Ok(())
}
fn compare_results(intp: &TestOutput, comp: &TestOutput, f_in: &Path) -> Result<()> {
if intp.stdout != comp.stdout {
println!("{b}[ {r}ERR{rs}{b} ]{rs} {f} compiled and interpreted stdout versions differ", r=color::FG_RED, rs=color::RESET, b=color::BRIGHT, f=f_in.display());
println!("compiled:\n{}", comp.stdout);
println!("interpreted:\n{}", intp.stdout);
bail!("Testing failed");
}
if intp.stderr != comp.stderr {
println!("{b}[ {r}ERR{rs}{b} ]{rs} {f} compiled and interpreted stderr versions differ", r=color::FG_RED, rs=color::RESET, b=color::BRIGHT, f=f_in.display());
println!("compiled:\n{}", comp.stderr);
println!("interpreted:\n{}", intp.stderr);
bail!("Testing failed");
}
if intp.status != comp.status {
println!("{b}[ {r}ERR{rs}{b} ]{rs} {f} compiled and interpreted status codes differ", r=color::FG_RED, rs=color::RESET, b=color::BRIGHT, f=f_in.display());
println!("compiled:\n{}", comp.status);
println!("interpreted:\n{}", intp.status);
bail!("Testing failed");
}
println!("{b}[ {g}OK{rs}{b} ]{rs} {f} ", g=color::FG_GREEN, rs=color::RESET, b=color::BRIGHT, f=f_in.display());
Ok(())
}
#[derive(Parser, Debug, Clone)]
#[command(author, version, about, long_about = None)]
struct Args {
/// Mode, allowed modes: test, record
#[arg(long, short)]
mode: String,
/// Use compile mode
#[arg(long, short)]
compile: bool,
/// Use interpret mode
#[arg(long, short='s')]
interpret: bool,
/// Output folder
#[arg(long, short, default_value_t=String::from("./target/mcl_test_dev"))]
output: String,
/// Input folder
#[arg(long, short, default_value_t=String::from("./tests"))]
input: String,
/// Compiler path
#[arg(long, short, default_value_t=String::from("./target/release/mclangc"))]
compiler_path: String
}
fn main() -> Result<()> {
let args = Args::parse();
fs::create_dir_all(&args.output)?;
match args.mode.as_str() {
"test" => run_tests(args),
"record" => todo!("Implement test result recording"),
s => {
eprintln!("Unknown mode '{s}'");
bail!("Bad subcommand");
}
}?;
Ok(())
}

129
src/cli.rs Normal file
View File

@ -0,0 +1,129 @@
use clap::{builder::PossibleValue, Parser, ValueEnum};
use camino::Utf8PathBuf;
lazy_static::lazy_static! {
static ref DEFAULT_INCLUDE_PATHS: Vec<Utf8PathBuf> = vec![
Utf8PathBuf::from("./"),
Utf8PathBuf::from("./include"),
Utf8PathBuf::from("~/.mclang/include"),
];
}
#[derive(Debug, Parser)]
pub struct CliArgs {
/// Only compile, dont link
#[arg(long, short)]
pub compile: bool,
/// Verosity
/// -1 - Nothing
/// 0 - Only errors
/// 1 - Normal
/// 2 - Verbose
/// 3 - Tracing
#[arg(long, short, default_value_t=1)]
pub verbose: i8,
/// Runt the program after compilation
#[arg(long, short)]
pub run: bool,
/// Output execuable file path
#[arg(long, short, default_value="./a.out")]
pub output: Utf8PathBuf,
/// Paths to search for libraries
#[arg(long="include", short='I', default_values_t=DEFAULT_INCLUDE_PATHS.clone().into_iter())]
pub include_path: Vec<Utf8PathBuf>,
/// Target to compile to
#[arg(long, short='T', default_value_t=CompilationTarget::X86_64_linux_nasm)]
pub target: CompilationTarget,
/// Input code files
#[arg(required=true, num_args=1..)]
pub input: Vec<Utf8PathBuf>,
#[clap(skip)]
pub passthrough: Vec<String>
}
impl CliArgs {
pub fn parse_with_passthrough() -> Self {
let mut clap_args = Vec::new();
let mut pt_args = Vec::new();
let mut switch = false;
for arg in std::env::args() {
if arg == String::from("--") {
switch = true;
continue;
}
if !switch {
//clap args
clap_args.push(arg);
} else {
// passwthrough
pt_args.push(arg);
}
}
let mut cargs = Self::parse_from(clap_args);
cargs.passthrough = pt_args;
cargs
}
}
#[allow(non_camel_case_types)]
#[derive(Debug, Clone)]
pub enum CompilationTarget {
X86_64_linux_nasm
}
impl ValueEnum for CompilationTarget {
fn value_variants<'a>() -> &'a [Self] {
&[
Self::X86_64_linux_nasm
]
}
fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
match self {
CompilationTarget::X86_64_linux_nasm => Some(PossibleValue::new("x86_64-linux-nasm")),
}
}
}
impl std::fmt::Display for CompilationTarget {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let r = match self {
CompilationTarget::X86_64_linux_nasm => "x86_64-linux-nasm",
};
write!(f, "{}", r)
}
}
// impl From<CompilationTarget> for clap::builder::OsStr {
// fn from(value: CompilationTarget) -> Self {
// match value {
// CompilationTarget::X86_64_linux_nasm => "X86_64_linux_nasm".into()
// }
// }
// }
// impl TryFrom<&str> for CompilationTarget {
// type Error = anyhow::Error;
// fn try_from(value: &str) -> Result<Self, Error> {
// match value {
// "X86_64_linux_nasm" => Ok(CompilationTarget::X86_64_linux_nasm)
// _ => bail!("Unknown compilation target {value}")
// }
// }
// }

View File

@ -1,93 +0,0 @@
use std::path::{PathBuf, Path};
use std::process::{Command, Stdio};
use anyhow::Result;
use crate::{info, error};
pub fn linux_x86_64_compile_and_link(of_a: &Path, of_o: &Path, of_c: &Path, quiet: bool) -> Result<()> {
let nasm_args = [
"-felf64",
of_a.to_str().unwrap(),
"-o",
of_o.to_str().unwrap()
];
let ld_args = [
of_o.to_str().unwrap(),
"-o",
of_c.to_str().unwrap()
];
let mut proc = if cfg!(target_os = "windows") {
return Ok(());
} else {
let ret = Command::new("nasm")
.args(nasm_args)
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.spawn();
if ret.is_err() {
error!("Nasm not installed")
}
ret?
};
if !quiet {
info!("running 'nasm {}'", nasm_args.join(" "));
}
let exit = proc.wait()?;
if !quiet {
info!("nasm process exited with code {}", exit);
}
let mut proc2 = if cfg!(target_os = "windows") {
return Ok(());
} else {
Command::new("ld")
.args(ld_args)
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.spawn()?
};
if !quiet {
info!("running 'ld {}'", ld_args.join(" "));
}
let exit2 = proc2.wait()?;
if !quiet {
info!("ld process exited with code {}", exit2);
}
Ok(())
}
pub fn linux_x86_64_run(bin: &Path, args: &[String], quiet: bool) -> Result<i32> {
let bin = PathBuf::from(
format!("./{}", bin.to_string_lossy())
);
let mut proc = if cfg!(target_os = "windows") {
return Ok(0);
} else {
Command::new(bin.clone())
.args(args)
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.spawn()?
};
// println!("{}", quiet);
if !quiet {
info!("running {} {}", bin.to_string_lossy(), args.join(" "));
}
let exit = proc.wait()?;
if !quiet {
info!("{} process exited with code {}", bin.to_string_lossy(), exit);
}
Ok(exit.code().unwrap_or(0))
}

View File

@ -1,536 +0,0 @@
use std::{fs, path::PathBuf, io::{Write, BufWriter}, collections::HashMap};
use crate::{definitions::*, Args, warn, lerror};
use crate::compile::commands::linux_x86_64_compile_and_link;
use crate::definitions::InstructionType;
use super::{commands::linux_x86_64_run, Constant, Memory, Function};
use anyhow::{Result, bail};
pub fn compile(program: &Program, args: &Args) -> Result<i32>{
let debug = args.get_opt_level()? < 1;
let mut of_c = PathBuf::from(&args.out_file);
let (mut of_o, mut of_a) = if args.out_file == *crate::DEFAULT_OUT_FILE {
let of_o = PathBuf::from("/tmp/mclang_comp.o");
let of_a = PathBuf::from("/tmp/mclang_comp.nasm");
(of_o, of_a)
} else {
let of_o = PathBuf::from(&args.out_file);
let of_a = PathBuf::from(&args.out_file);
(of_o, of_a)
};
of_c.set_extension("");
of_o.set_extension("o");
of_a.set_extension("nasm");
let mut should_push_ret = false;
let file = fs::File::create(&of_a)?;
let mut writer = BufWriter::new(&file);
let mut memories: Vec<Memory> = Vec::new();
let mut constants: HashMap<String, Constant> = HashMap::new();
let mut functions: Vec<Function> = Vec::new();
let mut alloced_structs: Vec<(String, String)> = Vec::new();
// println!("{}", tokens.len());
let mut strings: Vec<String> = Vec::new();
writeln!(writer, "BITS 64")?;
writeln!(writer, "segment .text")?;
writeln!(writer, "{}", super::MACRO_DEFINITIONS)?;
writeln!(writer, "{}", super::DBG_PRINT)?;
if !crate::config::ENABLE_EXPORTED_FUNCTIONS && !args.lib_mode {
writeln!(writer, "global _start")?;
writeln!(writer, "_start:")?;
writeln!(writer, " lea rbp, [rel ret_stack]")?;
writeln!(writer, " call main")?;
writeln!(writer, " jmp end")?;
}
let mut ti = 0;
while ti < program.ops.len() {
let token = &program.ops[ti];
if debug {
writeln!(writer, "addr_{ti}:")?;
if token.typ == OpType::Instruction(InstructionType::PushInt) {
writeln!(writer, " ;; -- {:?} {}", token.typ, token.value)?;
} else if token.typ == OpType::Instruction(InstructionType::PushStr) {
writeln!(writer, " ;; -- {:?} {}", token.typ, token.text.escape_debug())?;
} else {
writeln!(writer, " ;; -- {:?}", token.typ)?;
}
} else {
if ti > 0 {
if program.ops[ti-1].typ == OpType::Keyword(KeywordType::Else) ||
program.ops[ti-1].typ == OpType::Keyword(KeywordType::End){
writeln!(writer, "addr_{ti}:")?;
}
}
if ti + 1 < program.ops.len() && program.ops[ti+1].typ == OpType::Keyword(KeywordType::End) {
writeln!(writer, "addr_{ti}:")?;
}
if let OpType::Keyword(keyword) = &token.typ {
match keyword {
&KeywordType::End |
&KeywordType::While => {
writeln!(writer, "addr_{ti}:")?;
}
_ => ()
}
}
}
match token.typ.clone() {
// stack
OpType::Instruction(instruction) => {
match instruction {
InstructionType::PushInt => {
writeln!(writer, " OP_PushInt {}", token.value)?;
ti += 1;
},
InstructionType::PushStr => {
writeln!(writer, " OP_PushStr {}, str_{}", token.text.len(), strings.len())?;
strings.push(token.text.clone());
ti += 1;
}
InstructionType::PushCStr => {
writeln!(writer, " OP_PushCStr str_{}", strings.len())?;
strings.push(token.text.clone());
ti += 1;
}
InstructionType::Drop => {
writeln!(writer, " OP_Drop")?;
ti += 1;
},
InstructionType::Print => {
writeln!(writer, " OP_Print")?;
ti += 1;
},
InstructionType::Dup => {
writeln!(writer, " OP_Dup")?;
ti += 1;
},
InstructionType::Rot => {
writeln!(writer, " OP_Rot")?;
ti += 1;
},
InstructionType::Swap => {
writeln!(writer, " OP_Swap")?;
ti += 1;
},
InstructionType::Over => {
writeln!(writer, " OP_Over")?;
ti += 1;
},
InstructionType::Read8 => {
writeln!(writer, " OP_Load8")?;
ti += 1;
}
InstructionType::Write8 => {
writeln!(writer, " OP_Store8")?;
ti += 1;
}
InstructionType::Read32 => {
writeln!(writer, " OP_Load32")?;
ti += 1;
}
InstructionType::Write32 => {
writeln!(writer, " OP_Store32")?;
ti += 1;
}
InstructionType::Read64 => {
writeln!(writer, " OP_Load64")?;
ti += 1;
}
InstructionType::Write64 => {
writeln!(writer, " OP_Store64")?;
ti += 1;
}
// math
InstructionType::Plus => {
writeln!(writer, " OP_Plus")?;
ti += 1;
},
InstructionType::Minus => {
writeln!(writer, " OP_Minus")?;
ti += 1;
},
InstructionType::Equals => {
writeln!(writer, " OP_Equals")?;
ti += 1;
},
InstructionType::Lt => {
writeln!(writer, " OP_Lt")?;
ti += 1;
},
InstructionType::Gt => {
writeln!(writer, " OP_Gt")?;
ti += 1;
},
InstructionType::NotEquals => {
writeln!(writer, " OP_NotEquals")?;
ti += 1;
},
InstructionType::Le => {
writeln!(writer, " OP_Le")?;
ti += 1;
},
InstructionType::Ge => {
writeln!(writer, " OP_Ge")?;
ti += 1;
},
InstructionType::Band => {
writeln!(writer, " OP_Band")?;
ti += 1;
},
InstructionType::Bor => {
writeln!(writer, " OP_Bor")?;
ti += 1;
},
InstructionType::Shr => {
writeln!(writer, " OP_Shr")?;
ti += 1;
},
InstructionType::Shl => {
writeln!(writer, " OP_Shl")?;
ti += 1;
},
InstructionType::DivMod => {
writeln!(writer, " OP_DivMod")?;
ti += 1;
},
InstructionType::Mul => {
writeln!(writer, " OP_Mul")?;
ti += 1;
},
InstructionType::Syscall0 => {
writeln!(writer, " OP_Syscall0")?;
ti += 1;
},
InstructionType::Syscall1 => {
writeln!(writer, " OP_Syscall1")?;
ti += 1;
},
InstructionType::Syscall2 => {
writeln!(writer, " OP_Syscall2")?;
ti += 1;
},
InstructionType::Syscall3 => {
writeln!(writer, " OP_Syscall3")?;
ti += 1;
},
InstructionType::Syscall4 => {
writeln!(writer, " OP_Syscall4")?;
ti += 1;
},
InstructionType::Syscall5 => {
writeln!(writer, " OP_Syscall5")?;
ti += 1;
},
InstructionType::Syscall6 => {
writeln!(writer, " OP_Syscall6")?;
ti += 1;
},
InstructionType::MemUse => {
writeln!(writer, " OP_MemUse {}", token.addr.unwrap())?;
ti += 1;
},
InstructionType::None => {
println!("{token:?}");
unreachable!()
},
InstructionType::FnCall => {
writeln!(writer, " OP_FnCall {}", token.text)?;
ti += 1;
},
InstructionType::Return => {
// Experimental feature exported functions
if crate::config::ENABLE_EXPORTED_FUNCTIONS && should_push_ret {
writeln!(writer, " pop rdx")?;
should_push_ret = false;
}
writeln!(writer, " sub rbp, 8")?;
writeln!(writer, " mov rbx, qword [rbp]")?;
writeln!(writer, " push rbx")?;
writeln!(writer, " ret")?;
ti += 1;
},
InstructionType::CastBool |
InstructionType::CastPtr |
InstructionType::CastInt |
InstructionType::CastVoid |
InstructionType::TypeBool |
InstructionType::TypePtr |
InstructionType::TypeInt |
InstructionType::TypeVoid |
InstructionType::TypeAny |
InstructionType::Returns |
InstructionType::With => {
ti += 1;
}
InstructionType::ConstUse => {
writeln!(writer, " OP_ConstUse {}", token.text)?;
let mut c = constants.get(&token.text).unwrap().clone();
c.used = true;
constants.remove(&token.text);
constants.insert(token.text.clone(), c);
ti += 1;
},
InstructionType::StructUse => {
writeln!(writer, " OP_StructUse {}", token.text)?;
ti += 1;
},
}
}
OpType::Keyword(keyword) => {
match keyword {
// block
KeywordType::If |
KeywordType::Do => {
writeln!(writer, " pop rax")?;
writeln!(writer, " test rax, rax")?;
writeln!(writer, " jz addr_{}", token.jmp)?;
ti += 1;
}
KeywordType::Else => {
writeln!(writer, " jmp addr_{}", token.jmp)?;
ti += 1;
},
KeywordType::While => {
ti += 1;
}
KeywordType::End => {
if ti + 1 != token.jmp {
writeln!(writer, " jmp addr_{}", token.jmp)?;
}
ti += 1;
},
KeywordType::Memory => {
memories.push(Memory { size: token.value, loc: token.loc.clone(), id: token.addr.unwrap() });
ti += 1;
}
KeywordType::ConstantDef => {
// TODO: after we add c style strings add supoort for them in constants
let a = args.get_opt_level()? < 1;
let c = Constant{
loc: token.loc.clone(),
name: token.text.clone(),
value_i: Some(token.value),
value_s: None,
used: a,
};
constants.insert(token.text.clone(), c);
ti += 1;
},
KeywordType::FunctionDef => {
writeln!(writer, "{}:", token.text)?;
writeln!(writer, " pop rbx")?;
writeln!(writer, " mov qword [rbp], rbx")?;
writeln!(writer, " add rbp, 8")?;
functions.push(Function { loc: token.loc.clone(), name: token.text.clone(), exter: false});
ti += 1;
},
KeywordType::FunctionDone => {
if crate::config::ENABLE_EXPORTED_FUNCTIONS && should_push_ret {
writeln!(writer, " pop rdx")?;
should_push_ret = false;
}
writeln!(writer, " sub rbp, 8")?;
writeln!(writer, " mov rbx, qword [rbp]")?;
writeln!(writer, " push rbx")?;
writeln!(writer, " ret")?;
ti += 1;
}
KeywordType::FunctionThen => ti += 1,
KeywordType::FunctionDefExported => {
if !crate::config::ENABLE_EXPORTED_FUNCTIONS {
lerror!(&token.loc, "Experimental feature 'exported functions' is not enabled");
bail!("");
}
writeln!(writer, "global {}", token.text)?;
writeln!(writer, "{}:", token.text)?;
writeln!(writer, " pop rbx")?;
writeln!(writer, " mov qword [rbp], rbx")?;
writeln!(writer, " add rbp, 8")?;
warn!("External functions are highly experimental and should be treated as such");
if token.types.0 == 0 {
writeln!(writer, " ; no arguments")?;
} else {
if token.types.0 >= 1 {
writeln!(writer, " push rdi")?;
}
if token.types.0 >= 2 {
writeln!(writer, " push rsi")?;
}
if token.types.0 >= 3 {
writeln!(writer, " push rdx")?;
}
if token.types.0 >= 4 {
writeln!(writer, " push rcx")?;
}
if token.types.0 >= 5 {
writeln!(writer, " push r8")?;
}
if token.types.0 >= 6 {
writeln!(writer, " push r9")?;
}
if token.types.0 >= 7 {
lerror!(&token.loc, "More than 6 arguments in an external function is not supported");
bail!("");
}
}
if token.types.1 == 1 {
should_push_ret = true;
} else if token.types.1 > 1 {
lerror!(&token.loc, "More than 1 return arguments in an external function is not supported");
bail!("");
}
functions.push(Function { loc: token.loc.clone(), name: token.text.clone(), exter: false});
ti += 1;
},
KeywordType::Function |
KeywordType::Include |
KeywordType::Inline |
KeywordType::Export |
KeywordType::Struct |
KeywordType::Constant => unreachable!(),
}
}
OpType::Internal(t) => {
match t {
InternalType::StructAlloc{name} => {
alloced_structs.push((name, token.text.clone()));
ti += 1;
},
InternalType::Arrow => panic!("{t:?}"),
}
},
}
}
writeln!(writer, "addr_{ti}:")?;
if !crate::config::ENABLE_EXPORTED_FUNCTIONS && !args.lib_mode {
writeln!(writer, "end:")?;
writeln!(writer, " mov rax, 60")?;
writeln!(writer, " mov rdi, 0")?;
writeln!(writer, " syscall")?;
}
writeln!(writer, "segment .data")?;
for (i, s) in strings.iter().enumerate() {
let s_chars = s.chars().map(|c| (c as u32).to_string()).collect::<Vec<String>>();
let s_list = s_chars.join(",");
writeln!(writer, " str_{}: db {} ; {}", i, s_list, s.escape_default())?;
}
for (_, c) in constants {
if !c.used {
continue;
}
if let Some(v) = &c.value_i {
writeln!(writer, " const_{}: dq {}", c.name, v)?;
} else if let Some(_v) = &c.value_s {
todo!();
} else {
unreachable!();
}
}
writeln!(writer, "segment .bss")?;
for m in memories {
writeln!(writer, " mem_{}: resb {}", m.id, m.size)?;
}
for s in alloced_structs {
let Some(st) = program.struct_defs.get(&s.0) else {
// TODO: Make better error
panic!("Couldn find struct in struct defs");
};
let name = &s.1;
let mut st_size = 0;
writeln!(writer, " struct_{name}:")?;
for f in &st.fields {
let size = f.1.get_size();
writeln!(writer, " struct_{name}.{}: resb {}", f.0, size)?;
st_size += size;
}
writeln!(writer, " struct_{name}.__size: db {}", st_size)?;
}
writeln!(writer, " ret_stack: resq 256")?;
// for t in tokens {
// println!("{t:?}");
// }
writer.flush()?;
pre_compile_steps(
String::from_utf8_lossy(writer.buffer()).to_string().as_str(),
functions
)?;
linux_x86_64_compile_and_link(&of_a, &of_o, &of_c, args.quiet);
if args.run {
let c = linux_x86_64_run(&of_c, &[], args.quiet)?;
return Ok(c);
}
Ok(0)
}
fn pre_compile_steps(_code: &str, functions: Vec<Function>) -> Result<()> {
let mut has_main = false;
for func in functions {
if func.name == "main" {
has_main = true;
}
}
if !has_main {
crate::errors::missing_main_fn();
bail!("");
}
Ok(())
}

View File

@ -1,362 +0,0 @@
use crate::definitions::Loc;
pub mod linux_x86_64;
pub mod commands;
#[derive(Debug, Clone)]
pub struct Constant {
pub loc: Loc,
pub name: String,
pub value_i: Option<usize>,
pub value_s: Option<String>,
pub used: bool
// extern: bool
}
#[derive(Debug, Clone)]
pub struct Memory {
pub size: usize,
pub loc: Loc,
pub id: usize
}
#[derive(Debug, Clone)]
pub struct Function {
pub loc: Loc,
pub name: String,
pub exter: bool,
}
const DBG_PRINT: &'static str = "
_dbg_print:
mov r9, -3689348814741910323
sub rsp, 40
mov BYTE [rsp+31], 10
lea rcx, [rsp+30]
.L2:
mov rax, rdi
lea r8, [rsp+32]
mul r9
mov rax, rdi
sub r8, rcx
shr rdx, 3
lea rsi, [rdx+rdx*4]
add rsi, rsi
sub rax, rsi
add eax, 48
mov BYTE [rcx], al
mov rax, rdi
mov rdi, rdx
mov rdx, rcx
sub rcx, 1
cmp rax, 9
ja .L2
lea rax, [rsp+32]
mov edi, 1
sub rdx, rax
xor eax, eax
lea rsi, [rsp+32+rdx]
mov rdx, r8
mov rax, 1
syscall
add rsp, 40
ret
";
const MACRO_DEFINITIONS: &'static str = "\
%macro OP_PushInt 1
mov rax, %1
push rax
%endmacro
; str_len, str_id
%macro OP_PushStr 2
mov rax, %1
push rax
mov rax, %2
push rax
%endmacro
; str_id
%macro OP_PushCStr 1
push rax
mov rax, %1
push rax
%endmacro
%macro OP_Drop 0
pop rax
%endmacro
%macro OP_Print 0
pop rdi
call _dbg_print
%endmacro
%macro OP_Dup 0
pop rax
push rax
push rax
%endmacro
%macro OP_Rot 0
pop rax
pop rbx
pop rcx
push rbx
push rax
push rcx
%endmacro
%macro OP_Swap 0
pop rax
pop rbx
push rax
push rbx
%endmacro
%macro OP_Over 0
pop rax
pop rbx
push rbx
push rax
push rbx
%endmacro
%macro OP_Load8 0
pop rax
xor rbx, rbx
mov bl, byte [rax]
push rbx
%endmacro
%macro OP_Store8 0
pop rbx
pop rax
mov byte [rax], bl
%endmacro
%macro OP_Load32 0
pop rax
xor rbx, rbx
mov ebx, dword [rax]
push rbx
%endmacro
%macro OP_Store32 0
pop rbx
pop rax
mov dword[rax], ebx
%endmacro
%macro OP_Load64 0
pop rax
xor rbx, rbx
mov rbx, qword [rax]
push rbx
%endmacro
%macro OP_Store64 0
pop rbx
pop rax
mov qword [rax], rbx
%endmacro
%macro OP_Plus 0
pop rax
pop rbx
add rax, rbx
push rax
%endmacro
%macro OP_Minus 0
pop rax
pop rbx
sub rbx, rax
push rbx
%endmacro
%macro OP_Equals 0
mov rcx, 0
mov rdx, 1
pop rax
pop rbx
cmp rax, rbx
cmove rcx, rdx
push rcx
%endmacro
%macro OP_Lt 0
mov rcx, 0
mov rdx, 1
pop rbx
pop rax
cmp rax, rbx
cmovl rcx, rdx
push rcx
%endmacro
%macro OP_Gt 0
mov rcx, 0
mov rdx, 1
pop rbx
pop rax
cmp rax, rbx
cmovg rcx, rdx
push rcx
%endmacro
%macro OP_NotEquals 0
mov rcx, 1
mov rdx, 0
pop rax
pop rbx
cmp rax, rbx
cmove rcx, rdx
push rcx
%endmacro
%macro OP_Le 0
mov rcx, 0
mov rdx, 1
pop rbx
pop rax
cmp rax, rbx
cmovle rcx, rdx
push rcx
%endmacro
%macro OP_Ge 0
mov rcx, 0
mov rdx, 1
pop rbx
pop rax
cmp rax, rbx
cmovge rcx, rdx
push rcx
%endmacro
%macro OP_Band 0
pop rax
pop rbx
and rbx, rax
push rbx
%endmacro
%macro OP_Bor 0
pop rax
pop rbx
or rbx, rax
push rbx
%endmacro
%macro OP_Shr 0
pop rcx
pop rbx
shr rbx, cl
push rbx
%endmacro
%macro OP_Shl 0
pop rcx
pop rbx
shl rbx, cl
push rbx
%endmacro
%macro OP_DivMod 0
xor rdx, rdx
pop rbx
pop rax
div rbx
push rax
push rdx
%endmacro
%macro OP_Mul 0
pop rax
pop rbx
mul rbx
push rax
%endmacro
%macro OP_Syscall0 0
pop rax
syscall
push rax
%endmacro
%macro OP_Syscall1 0
pop rax
pop rdi
syscall
push rax
%endmacro
%macro OP_Syscall2 0
pop rax
pop rdi
pop rsi
syscall
push rax
%endmacro
%macro OP_Syscall3 0
pop rax
pop rdi
pop rsi
pop rdx
syscall
push rax
%endmacro
%macro OP_Syscall4 0
pop rax
pop rdi
pop rsi
pop rdx
pop r10
syscall
push rax
%endmacro
%macro OP_Syscall5 0
pop rax
pop rdi
pop rsi
pop rdx
pop r10
pop r8
syscall
push rax
%endmacro
%macro OP_Syscall6 0
pop rax
pop rdi
pop rsi
pop rdx
pop r10
pop r8
pop r9
syscall
push rax
%endmacro
%macro OP_MemUse 1
push mem_%1
%endmacro
%macro OP_FnCall 1
call %1
%endmacro
%macro OP_ConstUse 1
mov rax, qword [const_%1]
push rax
%endmacro
%macro OP_StructUse 1
push struct_%1
%endmacro
";

104
src/compiler/mod.rs Normal file
View File

@ -0,0 +1,104 @@
mod x86_64_linux_nasm;
mod utils;
use anyhow::bail;
use crate::{cli::{CliArgs, CompilationTarget}, types::ast::Program};
use std::{collections::HashMap, fs::File, io::{BufWriter, Write}, path::{Path, PathBuf}, process::Command};
pub trait Compiler {
fn new() -> Self;
fn generate_asm(&mut self, prog: &Program, fd: &mut BufWriter<File>) -> anyhow::Result<()>;
fn compile(&mut self, asm_fp: &Path, obj: &Path) -> anyhow::Result<()>;
fn link(&mut self, obj_files: Vec<PathBuf>, bin_fp: &Path) -> anyhow::Result<()>;
/// Return programs that are needed
fn needed_dependencies(&mut self) -> Vec<&str>;
}
//NOTE: No bsd cause im not about to create 3 or 4 diffrent compilation targets
pub fn compile_program(cli_args: &CliArgs, prog_map: HashMap<&Path, Program>) -> anyhow::Result<()> {
let mut compiler = match cli_args.target {
CompilationTarget::X86_64_linux_nasm => x86_64_linux_nasm::X86_64LinuxNasmCompiler::new(),
};
let bin_p = cli_args.output.as_std_path();
let mut objs = Vec::new();
for (k, v) in prog_map {
let mut asm_p = k.to_path_buf();
let mut obj_p = k.to_path_buf();
asm_p.set_extension("s");
obj_p.set_extension("o");
if let Err(_) = compile_file(&mut compiler, cli_args, asm_p.as_path(), obj_p.as_path(), &v) {
error!("Failed to compile file {k:?}");
bail!("")
}
objs.push(obj_p.clone());
}
if let Err(e) = compiler.link(objs, bin_p) {
error!("Failed to link program: {e}");
bail!("")
}
info!("Finished building program");
if cli_args.run {
// run_cmd(format!("./{}", bin_p.to_string_lossy()), cli_args.passthrough.clone())?;
let bin = bin_p.to_string_lossy().to_string();
let mut cmd = Command::new(format!("./{}", bin.clone()));
let cmd = cmd.args(cli_args.passthrough.clone());
let child = match cmd.spawn() {
Ok(c) => c,
Err(e) => {
error!("Unable to run {cmd:?}: {e}");
bail!("");
}
};
let ret = child.wait_with_output().expect("fuck i know");
if !ret.status.success() {
error!("Process running {bin:?} exited abnormaly, run with -v 2 for more output");
bail!("")
} else {
info!("Process exited successfully")
}
}
Ok(())
}
pub fn compile_file<C: Compiler>(compiler: &mut C, _: &CliArgs, asm_file: &Path, obj_file: &Path, prog: &Program) -> anyhow::Result<()> {
let asm_fd = std::fs::File::options()
.write(true)
.write(true)
.create(true)
.truncate(true)
.append(false)
.open(asm_file);
let asm_fd = match asm_fd {
Ok(fd) => fd,
Err(e) => {
error!("Failed to open file {asm_file:?}: {e}");
bail!("");
}
};
let mut buf_asm_fd = BufWriter::new(asm_fd);
compiler.generate_asm(prog, &mut buf_asm_fd)?;
buf_asm_fd.flush()?;
compiler.compile(asm_file, obj_file)?;
Ok(())
}

36
src/compiler/utils.rs Normal file
View File

@ -0,0 +1,36 @@
use std::{fmt::Debug, process::{Command, Stdio}};
use anyhow::bail;
pub fn run_cmd<'a, S: Into<String> + Debug + Clone>(bin: S, args: Vec<String>) -> anyhow::Result<()> {
let debug = unsafe {
crate::logger::LOGGER.enabled(crate::logger::Level::Debug)
};
let mut cmd = Command::new(bin.clone().into());
let cmd = cmd.args(args);
let cmd = if debug {
cmd.stdout(Stdio::inherit())
} else {
cmd.stdout(Stdio::null())
};
let cmd = cmd.stderr(Stdio::inherit());
let child = match cmd.spawn() {
Ok(c) => c,
Err(e) => {
error!("Unable to run {cmd:?}: {e}");
bail!("");
}
};
let ret = child.wait_with_output().expect("fuck i know");
if !ret.status.success() {
error!("Process running {bin:?} exited abnormaly, run with -v 2 for more output");
bail!("")
}
Ok(())
}

View File

@ -0,0 +1,521 @@
mod utils;
use std::path::PathBuf;
use std::{fs::File, io::BufWriter, path::Path};
use std::io::Write;
use crate::types::ast::{AstNode, EscIdent, Function, MemSize, Module, Program};
use crate::types::token::{InstructionType, Token, TokenType};
use super::utils::run_cmd;
use super::Compiler;
pub struct X86_64LinuxNasmCompiler {
strings: Vec<String>,
// func_mem_i: Vec<usize>,
// func_mem_list: HashMap<String, usize>,
if_i: usize,
while_i: usize,
used_consts: Vec<String>
}
impl X86_64LinuxNasmCompiler {
fn handle_token(&mut self, fd: &mut BufWriter<File>, _: &Program, token: &Token) -> anyhow::Result<()> {
match &token.typ {
TokenType::Instruction(it) => {
match it {
InstructionType::PushInt(i) => {
writeln!(fd, " mov rax, {i} ; PUSHINT({i})")?;
writeln!(fd, " push rax")?;
},
InstructionType::PushStr(s) => {
writeln!(fd, " push {}", s.len())?;
writeln!(fd, " push str_{}; PUSHSTR({})", self.strings.len(), s.escape_debug())?;
self.strings.push(s.clone());
},
InstructionType::PushCStr(s) => {
writeln!(fd, " push str_{}; PUSHCSTR({})", self.strings.len(), s.escape_debug())?;
self.strings.push(s.clone());
},
InstructionType::PushChar(c) => {
writeln!(fd, " push {}; PUSHCHAR({})", *c as u8, c.escape_debug())?;
},
InstructionType::Drop => {
writeln!(fd, " pop rax ; DROP")?;
},
InstructionType::Print => {
writeln!(fd, " pop rdi")?;
writeln!(fd, " call _dbg_print ; _DBG_PRINT")?;
},
InstructionType::Dup => {
writeln!(fd, " pop rax ; DUP")?;
writeln!(fd, " push rax")?;
writeln!(fd, " push rax")?;
},
InstructionType::Rot => {
writeln!(fd, " pop rax ; ROT")?;
writeln!(fd, " pop rbx")?;
writeln!(fd, " pop rcx")?;
writeln!(fd, " push rbx")?;
writeln!(fd, " push rax")?;
writeln!(fd, " push rcx")?;
},
InstructionType::Over => {
writeln!(fd, " pop rax ; OVER")?;
writeln!(fd, " pop rbx")?;
writeln!(fd, " push rbx")?;
writeln!(fd, " push rax")?;
writeln!(fd, " push rbx")?;
},
InstructionType::Swap => {
writeln!(fd, " pop rax ; SWAP")?;
writeln!(fd, " pop rbx")?;
writeln!(fd, " push rax")?;
writeln!(fd, " push rbx")?;
}
InstructionType::Minus => {
writeln!(fd, " pop rax ; SUB")?;
writeln!(fd, " pop rbx")?;
writeln!(fd, " sub rbx, rax")?;
writeln!(fd, " push rbx")?;
},
InstructionType::Plus => {
writeln!(fd, " pop rax ; ADD")?;
writeln!(fd, " pop rbx")?;
writeln!(fd, " add rax, rbx")?;
writeln!(fd, " push rax")?;
},
InstructionType::Equals => {
writeln!(fd, " mov rcx, 0 ; EQ")?;
writeln!(fd, " mov rdx, 1")?;
writeln!(fd, " pop rax")?;
writeln!(fd, " pop rbx")?;
writeln!(fd, " cmp rax, rbx")?;
writeln!(fd, " cmove rcx, rdx")?;
writeln!(fd, " push rcx")?;
},
InstructionType::Gt => {
writeln!(fd, " mov rcx, 0 ; GT")?;
writeln!(fd, " mov rdx, 1")?;
writeln!(fd, " pop rbx")?;
writeln!(fd, " pop rax")?;
writeln!(fd, " cmp rax, rbx")?;
writeln!(fd, " cmovg rcx, rdx")?;
writeln!(fd, " push rcx")?;
},
InstructionType::Lt => {
writeln!(fd, " mov rcx, 0 ; LT")?;
writeln!(fd, " mov rdx, 1")?;
writeln!(fd, " pop rbx")?;
writeln!(fd, " pop rax")?;
writeln!(fd, " cmp rax, rbx")?;
writeln!(fd, " cmovl rcx, rdx")?;
writeln!(fd, " push rcx")?;
},
InstructionType::Ge => {
writeln!(fd, " mov rcx, 0 ; GE")?;
writeln!(fd, " mov rdx, 1")?;
writeln!(fd, " pop rbx")?;
writeln!(fd, " pop rax")?;
writeln!(fd, " cmp rax, rbx")?;
writeln!(fd, " cmovge rcx, rdx")?;
writeln!(fd, " push rcx")?;
},
InstructionType::Le => {
writeln!(fd, " mov rcx, 0 ; LE")?;
writeln!(fd, " mov rdx, 1")?;
writeln!(fd, " pop rbx")?;
writeln!(fd, " pop rax")?;
writeln!(fd, " cmp rax, rbx")?;
writeln!(fd, " cmovle rcx, rdx")?;
writeln!(fd, " push rcx")?;
},
InstructionType::NotEquals => {
writeln!(fd, " mov rdx, 1 ; NEQ")?;
writeln!(fd, " mov rcx, 0")?;
writeln!(fd, " pop rax")?;
writeln!(fd, " pop rbx")?;
writeln!(fd, " cmp rax, rbx")?;
writeln!(fd, " cmove rcx, rdx")?;
writeln!(fd, " push rcx")?;
},
InstructionType::Band => {
writeln!(fd, " pop rax ; BAND")?;
writeln!(fd, " pop rbx")?;
writeln!(fd, " and rbx, rax")?;
writeln!(fd, " push rbx")?;
},
InstructionType::Bor => {
writeln!(fd, " pop rax ; BOR")?;
writeln!(fd, " pop rbx")?;
writeln!(fd, " or rbx, rax")?;
writeln!(fd, " push rbx")?;
}
InstructionType::Shr => {
writeln!(fd, " pop rcx")?;
writeln!(fd, " pop rbx")?;
writeln!(fd, " shr rbx, cl")?;
writeln!(fd, " push rbx")?;
},
InstructionType::Shl => {
writeln!(fd, " pop rcx")?;
writeln!(fd, " pop rbx")?;
writeln!(fd, " shl rbx, cl")?;
writeln!(fd, " push rbx")?;
},
InstructionType::DivMod => {
writeln!(fd, " xor rdx, rdx")?;
writeln!(fd, " pop rbx")?;
writeln!(fd, " pop rax")?;
writeln!(fd, " div rbx")?;
writeln!(fd, " push rax")?;
writeln!(fd, " push rdx")?;
},
InstructionType::Mul => {
writeln!(fd, " pop rax ; MUL")?;
writeln!(fd, " pop rbx")?;
writeln!(fd, " mul rbx")?;
writeln!(fd, " push rax")?;
},
InstructionType::Read8 => {
writeln!(fd, " pop rax ; READ8")?;
writeln!(fd, " xor rbx, rbx")?;
writeln!(fd, " mov bl, byte [rax]")?;
writeln!(fd, " push rbx")?;
}
InstructionType::Write8 => {
writeln!(fd, " pop rax ; WRITE 8")?;
writeln!(fd, " xor rbx, rbx")?;
writeln!(fd, " mov ebx, dword [rax]")?;
writeln!(fd, " push rbx")?;
},
InstructionType::Read32 => {
writeln!(fd, " pop rax ; READ 32")?;
writeln!(fd, " xor rbx, rbx")?;
writeln!(fd, " mov ebx, dword [rax]")?;
writeln!(fd, " push rbx")?;
},
InstructionType::Write32 => {
writeln!(fd, " pop rbx ; WRITE 32")?;
writeln!(fd, " pop rax")?;
writeln!(fd, " mov dword[rax], ebx")?;
},
InstructionType::Read64 => {
writeln!(fd, " pop rax ; READ 64")?;
writeln!(fd, " xor rbx, rbx")?;
writeln!(fd, " mov rbx, qword [rax]")?;
writeln!(fd, " push rbx")?;
},
InstructionType::Write64 => {
writeln!(fd, " pop rbx ; WRITE 64")?;
writeln!(fd, " pop rax")?;
writeln!(fd, " mov qword[rax], rbx")?;
},
InstructionType::Syscall0 => {
writeln!(fd, " pop rax")?;
writeln!(fd, " syscall")?;
writeln!(fd, " push rax")?;
},
InstructionType::Syscall1 => {
writeln!(fd, " pop rax")?;
writeln!(fd, " pop rdi")?;
writeln!(fd, " syscall")?;
writeln!(fd, " push rax")?;
},
InstructionType::Syscall2 => {
writeln!(fd, " pop rax")?;
writeln!(fd, " pop rdi")?;
writeln!(fd, " pop rsi")?;
writeln!(fd, " syscall")?;
writeln!(fd, " push rax")?;
},
InstructionType::Syscall3 => {
writeln!(fd, " pop rax")?;
writeln!(fd, " pop rdi")?;
writeln!(fd, " pop rsi")?;
writeln!(fd, " pop rdx")?;
writeln!(fd, " syscall")?;
writeln!(fd, " push rax")?;
},
InstructionType::Syscall4 => {
writeln!(fd, " pop rax")?;
writeln!(fd, " pop rdi")?;
writeln!(fd, " pop rsi")?;
writeln!(fd, " pop rdx")?;
writeln!(fd, " pop r10")?;
writeln!(fd, " syscall")?;
writeln!(fd, " push rax")?;
},
InstructionType::Syscall5 => {
writeln!(fd, " pop rax")?;
writeln!(fd, " pop rdi")?;
writeln!(fd, " pop rsi")?;
writeln!(fd, " pop rdx")?;
writeln!(fd, " pop r10")?;
writeln!(fd, " pop r8")?;
writeln!(fd, " syscall")?;
writeln!(fd, " push rax")?;
},
InstructionType::Syscall6 => {
writeln!(fd, " pop rax")?;
writeln!(fd, " pop rdi")?;
writeln!(fd, " pop rsi")?;
writeln!(fd, " pop rdx")?;
writeln!(fd, " pop r10")?;
writeln!(fd, " pop r8")?;
writeln!(fd, " pop r9")?;
writeln!(fd, " syscall")?;
writeln!(fd, " push rax")?;
},
InstructionType::CastBool |
InstructionType::CastPtr |
InstructionType::CastInt |
InstructionType::CastVoid => (), //? Possibly have a use for this
InstructionType::FnCall |
InstructionType::MemUse |
InstructionType::StructPath(_) |
InstructionType::StructItem(_) |
InstructionType::ConstUse => unreachable!(),
InstructionType::Return => {
writeln!(fd, " sub rbp, 8")?;
writeln!(fd, " mov rbx, qword [rbp]")?;
writeln!(fd, " push rbx")?;
writeln!(fd, " ret")?;
},
}
},
TokenType::Keyword(_) |
TokenType::Type(_) |
TokenType::Unknown(_) => unreachable!(),
}
Ok(())
}
fn handle_module(&mut self, fd: &mut BufWriter<File>, prog: &Program, module: &Module) -> anyhow::Result<()> {
writeln!(fd, "; {} Module START", module.path.join("::"))?;
self.handle_ast_list(fd, prog, module.body.clone())?;
writeln!(fd, "; {} Module END", module.path.join("::"))?;
Ok(())
}
fn handle_function(&mut self, fd: &mut BufWriter<File>, prog: &Program, func: &Function) -> anyhow::Result<()> {
writeln!(fd, "{f}: ; fn {f}", f=func.get_ident_escaped())?;
writeln!(fd, " fn_setup")?;
self.handle_ast_list(fd, prog, func.body.clone())?;
writeln!(fd, " fn_cleanup")?;
writeln!(fd, " ret")?;
Ok(())
}
fn handle_ast_list(&mut self, fd: &mut BufWriter<File>, prog: &Program, ast: Vec<AstNode>) -> anyhow::Result<()> {
for node in ast {
match &node {
AstNode::Function(f) => self.handle_function(fd, prog, f)?,
AstNode::Constant(_) => (),
AstNode::If(i) => {
let id = self.if_i;
self.if_i += 1;
writeln!(fd, "; IF({id}) START")?;
self.handle_ast_list(fd, prog, i.test.clone())?;
writeln!(fd, " pop rax")?;
writeln!(fd, " test rax, rax")?;
writeln!(fd, " jz if_{id}_else")?;
writeln!(fd, "if_{id}_start:")?;
self.handle_ast_list(fd, prog, i.body.clone())?;
writeln!(fd, " jmp if_{id}_end")?;
writeln!(fd, "if_{id}_else:")?;
self.handle_ast_list(fd, prog, vec![Box::leak(i.els.clone()).clone()])?;
writeln!(fd, "if_{id}_end:")?;
writeln!(fd, "; IF({id}) END")?;
},
AstNode::While(w) => {
let id = self.while_i;
self.while_i += 1;
writeln!(fd, "; WHILE({id}) START")?;
writeln!(fd, "while_{id}_test:")?;
self.handle_ast_list(fd, prog, w.test.clone())?;
writeln!(fd, " pop rax")?;
writeln!(fd, " test rax, rax")?;
writeln!(fd, " jz while_{id}_exit")?;
writeln!(fd, "while_{id}_start:")?;
self.handle_ast_list(fd, prog, w.body.clone())?;
writeln!(fd, "while_{id}_end:")?;
writeln!(fd, " jmp while_{id}_test")?;
writeln!(fd, "while_{id}_exit:")?;
writeln!(fd, "; WHILE({id}) END")?;
},
AstNode::Module(m) => self.handle_module(fd, prog, m)?,
AstNode::Memory(_) => {
//? Possibly allow stack based allocation somehow
// if !m.statc {
// todo!()
// }
},
AstNode::MemUse(m) => {
let tmp = if let Some(disp) = m.disp {
format!("+{disp}")
} else {
String::new()
};
writeln!(fd, " push m_{}{}", m.get_ident_escaped(), tmp)?;
},
AstNode::ConstUse(c) => {
self.used_consts.push(c.get_ident_escaped());
writeln!(fd, " mov rax, qword [c_{}]", c.get_ident_escaped())?;
writeln!(fd, " push rax")?;
},
AstNode::FnCall(f)=> {
writeln!(fd, " call {f} ; FUNCTIONCALL({f:?})", f=f.get_ident_escaped())?;
},
AstNode::Block(b)=> {
writeln!(fd, "; BLOCK({}) START", b.comment)?;
self.handle_ast_list(fd, prog, b.body.clone())?;
writeln!(fd, "; BLOCK({}) END", b.comment)?;
},
AstNode::Token(t) => self.handle_token(fd, prog, t)?,
AstNode::Int(_, _) |
AstNode::Str(_, _) |
AstNode::CStr(_, _) |
AstNode::Char(_, _) => unreachable!(),
AstNode::StructDef(_) => (),
AstNode::StructDispPush { disp, ident, .. } => {
writeln!(fd, " mov rax, {} ; STRUCTDISPPUSH({})", disp, ident)?;
writeln!(fd, " push rax")?;
},
}
}
Ok(())
}
}
impl Compiler for X86_64LinuxNasmCompiler {
fn new() -> Self {
Self {
strings: Vec::new(),
used_consts: Vec::new(),
if_i: 0,
while_i: 0,
// func_mem_i: Vec::new(),
// func_mem_list: HashMap::new(),
}
}
fn generate_asm(&mut self, prog: &Program, fd: &mut BufWriter<File>) -> anyhow::Result<()> {
writeln!(fd, "BITS 64")?;
writeln!(fd, "segment .text")?;
writeln!(fd, "%macro fn_setup 0")?;
writeln!(fd, " pop rbx")?;
writeln!(fd, " mov qword [rbp], rbx")?;
writeln!(fd, " add rbp, 8")?;
writeln!(fd, "%endmacro")?;
writeln!(fd, "%macro fn_cleanup 0")?;
writeln!(fd, " sub rbp, 8")?;
writeln!(fd, " mov rbx, qword [rbp]")?;
writeln!(fd, " push rbx")?;
writeln!(fd, "%endmacro")?;
writeln!(fd, "{}", utils::DBG_PRINT)?;
writeln!(fd, "global _start")?;
writeln!(fd, "_start:")?;
writeln!(fd, " lea rbp, [rel ret_stack]")?;
writeln!(fd, " call main")?;
writeln!(fd, " jmp __MCL_END__")?;
match &prog.ast {
AstNode::Module(m) => {
self.handle_module(fd, prog, m)?;
},
_ => panic!()
}
writeln!(fd, "__MCL_END__:")?;
writeln!(fd, " mov rax, 60")?;
writeln!(fd, " mov rdi, 0")?;
writeln!(fd, " syscall")?;
writeln!(fd, "segment .data")?;
for (_, v) in prog.constants.iter() {
if !self.used_consts.contains(&v.get_ident_escaped()) {
continue;
}
match Box::leak(v.value.clone()) {
AstNode::Int(_, val) => {
writeln!(fd, " c_{}: dq {}", v.get_ident_escaped(), val)?;
}
AstNode::Str(_, val) |
AstNode::CStr(_, val) => {
let s_chars = val.chars().map(|c| (c as u32).to_string()).collect::<Vec<String>>();
let s_list = s_chars.join(",");
writeln!(fd, " c_{}: db {} ; {}", v.get_ident_escaped(), s_list, val.escape_debug())?;
}
AstNode::Char(_, val) => {
writeln!(fd, " c_{}: db {} ; '{}'", v.get_ident_escaped(), *val as u8, val)?;
}
c => panic!("{c:?}")
};
}
for (i, s) in self.strings.iter().enumerate() {
let s_chars = s.chars().map(|c| (c as u32).to_string()).collect::<Vec<String>>();
let s_list = s_chars.join(",");
writeln!(fd, " str_{i}: db {} ; STRDEF({})", s_list, s.escape_debug())?;
}
writeln!(fd, "segment .bss")?;
writeln!(fd, " ret_stack: resq 256")?;
for (_, v) in &prog.memories {
match &v.size {
MemSize::Size(s) => {
writeln!(fd, " m_{}: resb {}", v.get_ident_escaped(), s)?;
},
MemSize::Type(tt) => {
writeln!(fd, " m_{}: resb {} ; {:?}", v.get_ident_escaped(), tt.get_size(), tt)?;
},
}
}
Ok(())
}
fn compile(&mut self, asm_fp: &Path, obj_fp: &Path) -> anyhow::Result<()> {
run_cmd("nasm", vec![
String::from("-felf64"),
String::from("-o"),
obj_fp.to_string_lossy().to_string(),
asm_fp.to_string_lossy().to_string()
])
}
fn link(&mut self, obj_files: Vec<PathBuf>, bin_fp: &Path) -> anyhow::Result<()> {
let mut args = vec![
String::from("-o"),
bin_fp.to_string_lossy().to_string(),
];
for f in obj_files {
args.push(f.to_string_lossy().to_string())
}
run_cmd("ld", args)
}
fn needed_dependencies(&mut self) -> Vec<&str> {
vec![
"nasm",
"ld"
]
}
}

View File

@ -0,0 +1,37 @@
pub const DBG_PRINT: &'static str = "
_dbg_print:
mov r9, -3689348814741910323
sub rsp, 40
mov BYTE [rsp+31], 10
lea rcx, [rsp+30]
.L2:
mov rax, rdi
lea r8, [rsp+32]
mul r9
mov rax, rdi
sub r8, rcx
shr rdx, 3
lea rsi, [rdx+rdx*4]
add rsi, rsi
sub rax, rsi
add eax, 48
mov BYTE [rcx], al
mov rax, rdi
mov rdi, rdx
mov rdx, rcx
sub rcx, 1
cmp rax, 9
ja .L2
lea rax, [rsp+32]
mov edi, 1
sub rdx, rax
xor eax, eax
lea rsi, [rsp+32+rdx]
mov rdx, r8
mov rax, 1
syscall
add rsp, 40
ret
";

View File

@ -1,16 +0,0 @@
/**
* Prints out extra information
*/
pub const DEV_MODE: bool = true;
pub const DEFAULT_OUT_FILE: &str = "a.out";
pub const DEFAULT_INCLUDES: [&str;2] = [
"./include",
"~/.mclang/include",
];
/**
* Experimental options
*/
pub const ENABLE_EXPORTED_FUNCTIONS: bool = false;

View File

@ -1,283 +0,0 @@
#[derive(Debug, Clone, PartialEq)]
pub enum InstructionType {
// stack
PushInt,
PushStr,
PushCStr,
Drop,
Print,
Dup,
Rot, // a b c => b c a
Over, // a b => a b a
Swap, // a b => b a
// math
Minus,
Plus,
Equals,
Gt,
Lt,
Ge,
Le,
NotEquals,
Band, // &
Bor, // |
Shr, // >>
Shl, // <<
DivMod, // /
Mul,
// mem
Read8,
Write8,
Read32,
Write32,
Read64,
Write64,
// syscalls
Syscall0,
Syscall1,
Syscall2,
Syscall3,
Syscall4,
Syscall5,
Syscall6,
CastBool,
CastPtr,
CastInt,
CastVoid,
// typing
TypeBool,
TypePtr,
TypeInt,
TypeVoid,
// TypeStr,
TypeAny,
Returns,
With,
FnCall,
MemUse,
ConstUse,
Return,
None // Used for macros and any other non built in word definitions
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum KeywordType {
If,
Else,
End,
While,
Do,
Include,
Memory,
Constant,
ConstantDef,
Function,
FunctionDef,
FunctionDefExported,
FunctionThen,
FunctionDone,
Inline,
Export,
Struct
}
#[derive(Debug, Clone, PartialEq)]
pub enum OpType {
Keyword(KeywordType),
Instruction(InstructionType)
}
#[derive(Debug, Clone)]
pub struct Operator{
pub typ: OpType,
pub tok_typ: TokenType,
pub value: usize,
pub text: String, //? only used for OpType::PushStr
pub addr: Option<usize>, //? only used for OpType::PushStr
pub jmp: usize,
pub loc: Loc,
pub types: (usize, usize)
}
impl Operator {
pub fn new(typ: OpType, tok_typ: TokenType, value: usize, text: String, file: String, row: usize, col: usize) -> Self {
Self {
typ,
value,
jmp: 0,
addr: None,
text,
loc: (file, row, col),
tok_typ,
types: (0, 0)
}
}
pub fn set_addr(&mut self, addr: usize) -> Self {
self.addr = Some(addr);
(*self).clone()
}
// pub fn set_types(&mut self, args: usize, rets: usize) -> Self {
// self.types = (args, rets);
// (*self).clone()
// }
}
impl OpType {
pub fn human(&self) -> String {
match (*self).clone() {
OpType::Instruction(instruction) => {
match instruction {
InstructionType::PushInt => "Number",
InstructionType::PushStr => "String",
InstructionType::PushCStr => "CString",
InstructionType::Print => "_dbg_print",
InstructionType::Dup => "dup",
InstructionType::Drop => "drop",
InstructionType::Rot => "rot",
InstructionType::Over => "over",
InstructionType::Swap => "swap",
InstructionType::Plus => "+",
InstructionType::Minus => "-",
InstructionType::Equals => "=",
InstructionType::Gt => ">",
InstructionType::Lt => "<",
InstructionType::NotEquals => "!=",
InstructionType::Le => "<=",
InstructionType::Ge => ">=",
InstructionType::Band => "band",
InstructionType::Bor => "bor",
InstructionType::Shr => "shr",
InstructionType::Shl => "shl",
InstructionType::DivMod => "divmod",
InstructionType::Mul => "*",
InstructionType::Read8 => "read8",
InstructionType::Write8 => "write8",
InstructionType::Read32 => "read32",
InstructionType::Write32 => "write32",
InstructionType::Read64 => "read64",
InstructionType::Write64 => "write64",
InstructionType::Syscall0 => "syscall0",
InstructionType::Syscall1 => "syscall1",
InstructionType::Syscall2 => "syscall2",
InstructionType::Syscall3 => "syscall3",
InstructionType::Syscall4 => "syscall4",
InstructionType::Syscall5 => "syscall5",
InstructionType::Syscall6 => "syscall6",
InstructionType::CastBool => "cast(bool",
InstructionType::CastPtr => "cast(ptr)",
InstructionType::CastInt => "cast(int)",
InstructionType::CastVoid => "cast(void)",
InstructionType::None => "None",
InstructionType::MemUse => "Memory use (internal)",
InstructionType::FnCall => "Function Call (Internal)",
InstructionType::ConstUse => "Constant Use (Internal)",
InstructionType::Return => "return",
InstructionType::TypeBool => "bool",
InstructionType::TypePtr => "ptr",
InstructionType::TypeInt => "int",
InstructionType::TypeVoid => "void",
InstructionType::Returns => "returns",
InstructionType::With => "with",
InstructionType::TypeAny => "any",
}
}
OpType::Keyword(keyword) => {
match keyword {
KeywordType::If => "if",
KeywordType::Else => "else",
KeywordType::End => "end",
KeywordType::While => "while",
KeywordType::Do => "do",
KeywordType::Include => "include",
KeywordType::Memory => "memory",
KeywordType::Function => "fn",
KeywordType::Constant => "const",
KeywordType::FunctionThen => "then",
KeywordType::FunctionDone => "done",
KeywordType::ConstantDef => "constant Definition (internal)",
KeywordType::FunctionDef => "function definition (internal)",
KeywordType::FunctionDefExported => "extern function definition (internal)",
KeywordType::Inline => "inline",
KeywordType::Export => "export",
KeywordType::Struct => "struct",
}
}
}.to_string()
}
}
#[derive(Debug, Clone)]
pub struct Token {
pub file: String,
pub line: usize,
pub col: usize,
pub text: String,
pub typ: TokenType,
pub value: Option<usize>, //* only used for Memories
pub addr: Option<usize>, //* only used for Memories
pub op_typ: OpType //* only used for Memories
}
#[derive(Debug, Clone, PartialEq, Copy)]
pub enum TokenType {
Word,
Int,
String,
CString,
Char
}
impl Token {
pub fn loc(&self) -> Loc {
(
self.file.clone(),
self.line,
self.col
)
}
}
impl TokenType {
pub fn human(self) -> String {
match self {
TokenType::Word => "Word",
TokenType::Int => "Int",
TokenType::String => "String",
TokenType::CString => "CString",
TokenType::Char => "Char"
}.to_string()
}
}
pub type Loc = (String, usize, usize);
#[derive(Debug, PartialEq, Clone)]
pub enum Types {
Bool,
Ptr,
Int,
Void,
Any
// U8,
// U16,
// U32,
// U64,
// todo: add signed numbers since we dont have them yet lol
}

View File

@ -1,392 +0,0 @@
use std::collections::HashMap;
use anyhow::{Result, bail};
#[derive(Debug, Clone, PartialEq)]
pub enum InstructionType {
// stack
PushInt,
PushStr,
PushCStr,
Drop,
Print,
Dup,
Rot, // a b c => b c a
Over, // a b => a b a
Swap, // a b => b a
// math
Minus,
Plus,
Equals,
Gt,
Lt,
Ge,
Le,
NotEquals,
Band, // &
Bor, // |
Shr, // >>
Shl, // <<
DivMod, // /
Mul,
// mem
Read8,
Write8,
Read32,
Write32,
Read64,
Write64,
// syscalls
Syscall0,
Syscall1,
Syscall2,
Syscall3,
Syscall4,
Syscall5,
Syscall6,
CastBool,
CastPtr,
CastInt,
CastVoid,
// typing
TypeBool,
TypePtr,
TypeInt,
TypeVoid,
// TypeStr,
TypeAny,
Returns,
With,
FnCall,
MemUse,
ConstUse,
StructUse,
Return,
None // Used for macros and any other non built in word definitions
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum KeywordType {
If,
Else,
End,
While,
Do,
Include,
Memory,
Constant,
ConstantDef,
Function,
FunctionDef,
FunctionDefExported,
FunctionThen,
FunctionDone,
Inline,
Export,
Struct,
}
#[derive(Debug, Clone, PartialEq)]
pub enum InternalType {
Arrow,
StructAlloc {
name: String
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum OpType {
Keyword(KeywordType),
Instruction(InstructionType),
Internal(InternalType)
}
#[derive(Debug, Clone)]
pub struct Operator{
pub typ: OpType,
pub tok_typ: TokenType,
pub value: usize,
pub text: String, //? only used for OpType::PushStr
pub addr: Option<usize>, //? only used for OpType::PushStr
pub jmp: usize,
pub loc: Loc,
pub types: (usize, usize)
}
impl Operator {
pub fn new(typ: OpType, tok_typ: TokenType, value: usize, text: String, file: String, row: usize, col: usize) -> Self {
Self {
typ,
value,
jmp: 0,
addr: None,
text,
loc: (file, row, col),
tok_typ,
types: (0, 0)
}
}
pub fn set_addr(&mut self, addr: usize) -> Self {
self.addr = Some(addr);
(*self).clone()
}
// pub fn set_types(&mut self, args: usize, rets: usize) -> Self {
// self.types = (args, rets);
// (*self).clone()
// }
}
impl OpType {
pub fn human(&self) -> String {
match (*self).clone() {
OpType::Instruction(instruction) => {
match instruction {
InstructionType::PushInt => "Number",
InstructionType::PushStr => "String",
InstructionType::PushCStr => "CString",
InstructionType::Print => "_dbg_print",
InstructionType::Dup => "dup",
InstructionType::Drop => "drop",
InstructionType::Rot => "rot",
InstructionType::Over => "over",
InstructionType::Swap => "swap",
InstructionType::Plus => "+",
InstructionType::Minus => "-",
InstructionType::Equals => "=",
InstructionType::Gt => ">",
InstructionType::Lt => "<",
InstructionType::NotEquals => "!=",
InstructionType::Le => "<=",
InstructionType::Ge => ">=",
InstructionType::Band => "band",
InstructionType::Bor => "bor",
InstructionType::Shr => "shr",
InstructionType::Shl => "shl",
InstructionType::DivMod => "divmod",
InstructionType::Mul => "*",
InstructionType::Read8 => "read8",
InstructionType::Write8 => "write8",
InstructionType::Read32 => "read32",
InstructionType::Write32 => "write32",
InstructionType::Read64 => "read64",
InstructionType::Write64 => "write64",
InstructionType::Syscall0 => "syscall0",
InstructionType::Syscall1 => "syscall1",
InstructionType::Syscall2 => "syscall2",
InstructionType::Syscall3 => "syscall3",
InstructionType::Syscall4 => "syscall4",
InstructionType::Syscall5 => "syscall5",
InstructionType::Syscall6 => "syscall6",
InstructionType::CastBool => "cast(bool",
InstructionType::CastPtr => "cast(ptr)",
InstructionType::CastInt => "cast(int)",
InstructionType::CastVoid => "cast(void)",
InstructionType::None => "None",
InstructionType::MemUse => "Memory use (internal)",
InstructionType::FnCall => "Function Call (Internal)",
InstructionType::ConstUse => "Constant Use (Internal)",
InstructionType::StructUse => "Struct Use (Internal)",
InstructionType::Return => "return",
InstructionType::TypeBool => "bool",
InstructionType::TypePtr => "ptr",
InstructionType::TypeInt => "int",
InstructionType::TypeVoid => "void",
InstructionType::Returns => "returns",
InstructionType::With => "with",
InstructionType::TypeAny => "any",
}
}
OpType::Keyword(keyword) => {
match keyword {
KeywordType::If => "if",
KeywordType::Else => "else",
KeywordType::End => "end",
KeywordType::While => "while",
KeywordType::Do => "do",
KeywordType::Include => "include",
KeywordType::Memory => "memory",
KeywordType::Function => "fn",
KeywordType::Constant => "const",
KeywordType::FunctionThen => "then",
KeywordType::FunctionDone => "done",
KeywordType::ConstantDef => "constant Definition (internal)",
KeywordType::FunctionDef => "function definition (internal)",
KeywordType::FunctionDefExported => "extern function definition (internal)",
KeywordType::Inline => "inline",
KeywordType::Export => "export",
KeywordType::Struct => "struct",
}
}
OpType::Internal(t) => {
match t {
InternalType::Arrow => "->",
InternalType::StructAlloc{..} => "Struct alloc ( internal )",
}
},
}.to_string()
}
}
#[derive(Debug, Clone)]
pub struct Token {
pub file: String,
pub line: usize,
pub col: usize,
pub text: String,
pub typ: TokenType,
pub value: Option<usize>, //* only used for Memories
pub addr: Option<usize>, //* only used for Memories
pub op_typ: OpType //* only used for Memories
}
#[derive(Debug, Clone, PartialEq, Copy)]
pub enum TokenType {
Word,
Int,
String,
CString,
Char
}
impl Token {
pub fn loc(&self) -> Loc {
(
self.file.clone(),
self.line,
self.col
)
}
}
impl TokenType {
pub fn human(self) -> String {
match self {
TokenType::Word => "Word",
TokenType::Int => "Int",
TokenType::String => "String",
TokenType::CString => "CString",
TokenType::Char => "Char"
}.to_string()
}
}
pub type Loc = (String, usize, usize);
#[derive(Debug, PartialEq, Clone)]
pub enum Types {
Any,
Bool,
Ptr,
Void,
U8,
U16,
U32,
U64,
I8,
I16,
I32,
I64,
#[allow(dead_code)] //TODO: Implement custom types
Custom{
size: u64 // in bytes
},
// todo: add signed numbers since we dont have them yet lol
}
impl Types {
pub fn get_size(&self) -> u64 {
match *self {
Types::Any => 0, // any cant be a known size
Types::Void => 0,
Types::Bool => 1,
Types::U8 |
Types::I8 => 1,
Types::U16 |
Types::I16 => 2,
Types::U32 |
Types::I32 => 4,
Types::Ptr |
Types::U64 |
Types::I64 => 8,
Types::Custom { size } => size,
}
}
pub fn from_string<S: Into<String> + std::fmt::Display>(s: &S) -> Result<Self> {
match s.to_string().as_str() {
"any" => Ok(Types::Any),
"void" => Ok(Types::Void),
"bool" => Ok(Types::Bool),
"u8" => Ok(Types::U8),
"i8" => Ok(Types::I8),
"u16" => Ok(Types::U16),
"i16" => Ok(Types::I16),
"u32" => Ok(Types::U32),
"i32" => Ok(Types::I32),
"ptr" => Ok(Types::Ptr),
"u64" => Ok(Types::U64),
"i64" => Ok(Types::I64),
_ => bail!("Unknown type {s}")
}
}
}
#[derive(Debug, Clone)]
pub struct Function {
pub loc: Loc,
pub name: String,
pub inline: bool,
pub tokens: Option<Vec<Operator>>
}
#[derive(Debug, Clone)]
pub struct Constant {
pub loc: Loc,
pub name: String
}
#[derive(Debug, Clone)]
pub struct Memory {
pub loc: Loc,
pub id: usize
}
#[derive(Debug, Clone)]
pub struct StructDef {
pub loc: Loc,
pub name: String,
pub fields: Vec<(String, Types)>
}
pub type Functions = HashMap<String, Function>;
pub type Memories = HashMap<String, Memory>;
pub type Constants = HashMap<String, Constant>;
pub type StructDefs = HashMap<String, StructDef>;
#[derive(Debug, Clone)]
pub struct Program {
pub ops: Vec<Operator>,
pub functions: Functions,
pub memories: Memories,
pub constants: Constants,
pub struct_defs: StructDefs,
pub struct_allocs: HashMap<String, String>
}

View File

@ -1,18 +0,0 @@
use crate::{error, help, code_block};
pub fn missing_main_fn() {
error!("Main function not found, please create one lol");
help!("Heres a basic main function with code that prints hello world:\n{}",
code_block!(
concat!(
"include \"std.mcl\"\n",
"\n",
"fn main with void retuns void then\n",
" \"Hello world!\\n\" puts\n",
"done\n"
)
)
);
}

View File

@ -1,442 +0,0 @@
use std::collections::HashMap;
use crate::{constants::{OpType, Loc, InstructionType, KeywordType, Operator}, lerror, error};
// use crate::util::logger;
use color_eyre::Result;
use eyre::eyre;
use super::{Memory, Function, Constant};
mod syscalls;
fn stack_pop(stack: &mut Vec<usize>, pos: &Loc) -> Result<usize> {
if let Some(i) = stack.pop() { Ok(i) } else {
lerror!(&pos.clone(), "Stack underflow");
Err(eyre!("Stack underflow"))
}
}
pub fn run(ops: &[crate::constants::Operator]) -> Result<i32>{
let mut stack: Vec<usize> = Vec::new();
let mut mem: Vec<u64> = vec![0; crate::MEM_SZ + crate::STRING_SZ];
let mut string_idx = 0;
let prerunned = pre_run(ops);
let functions = prerunned.functions;
let constants = prerunned.constants;
let memories = prerunned.memories;
let mut ret_stack: Vec<usize> = Vec::new();
// for token in &tokens {
// println!("{{typ: \"{:?}\", val: {}, jmp: {}}}", token.typ, token.value, token.jmp);
// }
// jump to main func
let mut ip = if let Some(i) = functions.get("main") {i.id} else {
crate::errors::missing_main_fn();
return Err(eyre!(""));
};
while ip < ops.len() {
let op = &ops[ip];
let pos = op.loc.clone();
match op.typ.clone() {
OpType::Instruction(instruction) => {
match instruction {
InstructionType::PushInt => {
stack.push(op.value);
ip += 1;
},
InstructionType::PushStr => {
if op.addr.is_none() {
stack.push(op.text.len()); // string len
stack.push(string_idx + crate::MEM_SZ);
for c in op.text.bytes() {
mem[crate::MEM_SZ + string_idx] = u64::from(c);
string_idx += 1;
}
} else {
stack.push(op.text.len());
if let Some(addr) = op.addr {
stack.push(addr);
}
}
ip += 1;
},
InstructionType::PushCStr => {
if op.addr.is_none() {
stack.push(string_idx + crate::MEM_SZ);
for c in op.text.bytes() {
mem[crate::MEM_SZ + string_idx] = u64::from(c);
string_idx += 1;
}
} else {
if let Some(addr) = op.addr {
stack.push(addr);
}
}
ip += 1;
},
InstructionType::Drop => {
stack.pop();
ip += 1;
},
InstructionType::Dup => {
let a = stack_pop(&mut stack, &pos)?;
stack.push(a);
stack.push(a);
ip += 1;
},
InstructionType::Rot => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
let c = stack_pop(&mut stack, &pos)?;
stack.push(b);
stack.push(a);
stack.push(c);
ip += 1;
}
InstructionType::Swap => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(a);
stack.push(b);
ip += 1;
}
InstructionType::Over => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(b);
stack.push(a);
stack.push(b);
ip += 1;
}
InstructionType::Print => {
let a = stack_pop(&mut stack, &pos)?;
println!("{a}");
// let _ = io::stdout().flush();
ip += 1;
},
#[allow(clippy::cast_possible_truncation)]
InstructionType::Read8 |
InstructionType::Read32 |
InstructionType::Read64 => {
let a = stack_pop(&mut stack, &pos)?;
if a > crate::MEM_SZ + crate::STRING_SZ {
lerror!(&op.loc, "Invalid memory address {a}");
return Ok(1);
}
let byte = mem[a];
stack.push(byte as usize);
ip += 1;
}
#[allow(clippy::cast_possible_truncation)]
InstructionType::Write8 => {
let val = stack_pop(&mut stack, &pos)?;
let addr = stack_pop(&mut stack, &pos)?;
if addr > crate::MEM_SZ {
lerror!(&op.loc, "Invalid memory address {addr}");
return Ok(1);
}
mem[addr] = u64::from(val as u8);
ip += 1;
}
#[allow(clippy::cast_possible_truncation)]
InstructionType::Write32 => {
let val = stack_pop(&mut stack, &pos)?;
let addr = stack_pop(&mut stack, &pos)?;
if addr > crate::MEM_SZ {
lerror!(&op.loc, "Invalid memory address {addr}");
return Ok(1);
}
mem[addr] = u64::from(val as u32);
ip += 1;
}
#[allow(clippy::cast_possible_truncation)]
InstructionType::Write64 => {
let val = stack_pop(&mut stack, &pos)?;
let addr = stack_pop(&mut stack, &pos)?;
if addr > crate::MEM_SZ {
lerror!(&op.loc, "Invalid memory address {addr}");
return Ok(1);
}
mem[addr] = val as u64;
ip += 1;
}
// math
InstructionType::Plus => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(b + a);
ip += 1;
},
InstructionType::Minus => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(b - a);
ip += 1;
},
InstructionType::Equals => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(usize::from(b == a));
ip += 1;
},
InstructionType::Gt => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(usize::from(b > a));
ip += 1;
},
InstructionType::Lt => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(usize::from(b < a));
ip += 1;
},
InstructionType::NotEquals => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(usize::from(b != a));
ip += 1;
},
InstructionType::Ge => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(usize::from(b >= a));
ip += 1;
},
InstructionType::Le => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(usize::from(b <= a));
ip += 1;
},
InstructionType::Band => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(a & b);
ip += 1;
}
InstructionType::Bor => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(a | b);
ip += 1;
}
InstructionType::Shr => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(b >> a);
ip += 1;
}
InstructionType::Shl => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(b << a);
ip += 1;
}
InstructionType::DivMod => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(b / a);
stack.push(b % a);
ip += 1;
}
InstructionType::Mul => {
let a = stack_pop(&mut stack, &pos)?;
let b = stack_pop(&mut stack, &pos)?;
stack.push(b * a);
ip += 1;
}
InstructionType::Syscall0 => {
todo!();
// ti += 1;
},
InstructionType::Syscall1 => {
todo!();
// ti += 1;
},
InstructionType::Syscall2 => {
todo!();
// ti += 1;
},
InstructionType::Syscall3 => {
let rax = stack_pop(&mut stack, &pos)?;
let rdi = stack_pop(&mut stack, &pos)?;
let rsi = stack_pop(&mut stack, &pos)?;
let rdx = stack_pop(&mut stack, &pos)?;
// println!("yes");
let ret = match rax {
1 => syscalls::sys_write(rax, rdi, rsi, rdx, &mem),
0 => 0, //? temp, so clippy doesnt complain
_ => {
error!("Syscall(3) #{} is not implemented", rax);
return Err(eyre!("Syscall not implemented"));
}
};
stack.push(ret);
// println!("{}", stack.len());
ip += 1;
},
InstructionType::Syscall4 => {
todo!();
// ti += 1;
},
InstructionType::Syscall5 => {
todo!();
// ti += 1;
},
InstructionType::Syscall6 => {
todo!();
// ti += 1;
},
InstructionType::MemUse => {
let m = memories.get(&op.addr.unwrap()).unwrap();
stack.push(m.id);
ip += 1;
},
InstructionType::FnCall => {
ret_stack.push(ip);
let f = functions.get(&op.text).unwrap();
ip = f.id;
}
InstructionType::Return => {
ip = ret_stack.pop().unwrap();
ip += 1;
}
InstructionType::ConstUse => {
let a = constants.get(&op.text).unwrap();
if let Some(i) = a.value_i {
stack.push(i);
} else if let Some(_s) = a.value_s.clone() {
unimplemented!();
}
ip += 1;
},
InstructionType::CastBool |
InstructionType::CastPtr |
InstructionType::CastInt |
InstructionType::CastVoid |
InstructionType::TypeBool |
InstructionType::TypePtr |
InstructionType::TypeInt |
InstructionType::TypeVoid |
InstructionType::TypeAny |
InstructionType::Returns |
InstructionType::With => ip += 1,
InstructionType::None => unreachable!(),
}
}
OpType::Keyword(k) => {
match k {
// blocks
KeywordType::If => {
let a = stack_pop(&mut stack, &pos)?;
if a == 0 {
// println!("If({ti}) => t: {:?} j: {}", tokens[token.jmp as usize].typ, token.jmp);
ip = op.jmp;
} else {
ip += 1;
}
},
KeywordType::Else | KeywordType::End => {
ip = op.jmp;
}
KeywordType::Do => {
let a = stack.pop().unwrap();
if a == 0 {
ip = op.jmp;
} else {
ip += 1;
}
}
KeywordType::While | //* exept this one, this one should just skip over
KeywordType::Memory |
KeywordType::FunctionDef |
KeywordType::FunctionDefExported |
KeywordType::ConstantDef => {
//? Disabled since we now pre run the whole program
// constants.insert(op.text.clone(), Constant { loc: op.loc.clone(), name: op.text.clone(), value_i: Some(op.value), value_s: None, used: false });
ip += 1;
},
KeywordType::FunctionDone => {
if let Some(i) = ret_stack.pop() {
ip = i + 1;
} else {
break;
}
},
KeywordType::FunctionThen => ip += 1,
KeywordType::Constant |
KeywordType::Function |
KeywordType::Inline |
KeywordType::Export |
KeywordType::Include => unreachable!(),
}
}
}
}
Ok(0)
}
pub struct Defineds {
pub memories: HashMap<usize, Memory>,
pub functions: HashMap<String, Function>,
pub constants: HashMap<String, Constant>
}
pub fn pre_run(ops: &[Operator]) -> Defineds {
let mut defineds = Defineds{
memories: HashMap::new(),
functions: HashMap::new(),
constants: HashMap::new(),
};
for (ip, op) in ops.iter().enumerate() {
match op.typ {
OpType::Keyword(KeywordType::Memory) => {
defineds.memories.insert(op.addr.unwrap(), Memory { size: op.value, loc: op.loc.clone(), id: op.addr.unwrap() });
},
OpType::Keyword(KeywordType::FunctionDefExported) => {
}
OpType::Keyword(KeywordType::FunctionDef) => {
defineds.functions.insert(op.text.clone(), Function { loc: op.loc.clone(), name: op.text.clone(), id: ip });
},
OpType::Keyword(KeywordType::ConstantDef) => {
defineds.constants.insert(op.text.clone(), Constant { loc: op.loc.clone(), name: op.text.clone(), value_i: Some(op.value), value_s: None, used: false });
},
_ => ()
}
}
defineds
}

View File

@ -1,22 +0,0 @@
#[allow(clippy::cast_possible_truncation)]
pub fn sys_write(sys_n: usize, fd: usize, buff: usize, count: usize, mem: &Vec<u64> ) -> usize {
let mem = (*mem).clone();
// println!("{:?}", &mem[buff..(buff + count)]);
// return 0 ;
let s = &mem[buff..(buff + count)].iter().map(|i| {
char::from_u32(u32::from(*i as u8)).unwrap_or('_').to_string()
}).collect::<String>();
match fd {
1 => {
print!("{s}");
},
2 => {
eprint!("{s}");
},
_ => panic!("Unknown file {fd}")
};
let _ = std::io::Write::flush(&mut std::io::stdout());
let _ = std::io::Write::flush(&mut std::io::stderr());
sys_n
}

View File

@ -1,27 +0,0 @@
use crate::constants::Loc;
pub mod linux_x86_64;
#[derive(Debug, Clone)]
pub struct Constant {
pub loc: Loc,
pub name: String,
pub value_i: Option<usize>,
pub value_s: Option<String>,
pub used: bool
// extern: bool
}
#[derive(Debug, Clone)]
pub struct Memory {
pub size: usize,
pub loc: Loc,
pub id: usize
}
#[derive(Debug, Clone)]
pub struct Function {
pub loc: Loc,
pub name: String,
pub id: usize
}

View File

@ -1,147 +0,0 @@
use crate::{definitions::{Token, TokenType}, Args};
fn lex_word(s: String, tok_type: TokenType) -> (TokenType, String) {
match s {
s if s.parse::<u64>().is_ok() && tok_type == TokenType::Word => { // negative numbers not yet implemented
(TokenType::Int, s)
},
s if tok_type == TokenType::Word => {
(TokenType::Word, s)
},
s if tok_type == TokenType::String => {
(TokenType::String, s)
}
s if tok_type == TokenType::CString => {
(TokenType::CString, s)
}
s if tok_type == TokenType::Char => {
(TokenType::Char, s)
}
_ => unreachable!()
}
}
pub fn find_col<F>(text: &str, mut col: usize, predicate: F) -> usize where F: Fn(char, char) -> bool {
let mut last = '\0';
while col < text.len() && !predicate(text.chars().nth(col).unwrap(), last) {
last = text.chars().nth(col).unwrap();
col += 1;
}
col
}
// TODO: Implement multiline strings
fn lex_line(text: &str) -> Vec<(usize, String, TokenType)> {
let mut tokens: Vec<(usize, String, TokenType)> = Vec::new();
let mut col = find_col(text, 0, |x, _| !x.is_whitespace());
let mut col_end: usize = 0;
while col_end < text.to_string().len() {
if (text.len() - col) < 1 {
return tokens;
}
if &text[col..=col] == "\"" {
col_end = find_col(text, col + 1, |x, x2| x == '"' && x2 != '\\');
let t = &text[(col + 1)..col_end];
let t = t.replace("\\n", "\n")
.replace("\\t", "\t")
.replace("\\r", "\r")
.replace("\\\'", "\'")
.replace("\\\"", "\"")
.replace("\\0", "\0");
if !t.is_empty() {
tokens.push((col, t.to_string(), TokenType::String));
}
col = find_col(text, col_end + 1, |x, _| !x.is_whitespace());
} else if &text[col..=col] == "'"{
col_end = find_col(text, col + 1, |x, x2| x == '\'' && x2 != '\\');
let t = &text[(col + 1)..col_end];
let t = t.replace("\\n", "\n")
.replace("\\t", "\t")
.replace("\\r", "\r")
.replace("\\\'", "\'")
.replace("\\\"", "\"")
.replace("\\0", "\0");
if !t.is_empty() {
tokens.push((col, t.to_string(), TokenType::Char));
}
col = find_col(text, col_end + 1, |x, _| !x.is_whitespace());
} else {
if &text[col..=col] == "c" && text.len() - 1 + col > 0 && &text[col+1..=col+1] == "\"" {
col_end = find_col(text, col + 2, |x, x2| x == '"' && x2 != '\\');
let t = &text[(col + 2)..col_end];
let mut t = t.replace("\\n", "\n")
.replace("\\t", "\t")
.replace("\\r", "\r")
.replace("\\\'", "\'")
.replace("\\\"", "\"")
.replace("\\0", "\0");
if !t.is_empty() {
t.push('\0');
tokens.push((col, t.to_string(), TokenType::CString));
}
col = find_col(text, col_end + 1, |x, _| !x.is_whitespace());
} else {
col_end = find_col(text, col, |x, _| x.is_whitespace());
let t = &text[col..col_end];
if t == "//" {
return tokens;
}
if !t.is_empty() {
tokens.push((col, t.to_string(), TokenType::Word));
}
col = find_col(text, col_end, |x, _| !x.is_whitespace());
}
}
}
tokens
}
pub fn lex(code: &str, file: &str, _args: &Args) -> Vec<Token> {
let lines: Vec<(usize, &str)> = code
.split(['\n', '\r'])
.enumerate()
.collect();
let lines: Vec<(usize, String)> = lines.iter().map(|i| (i.0, i.1.to_string())).collect();
let mut tokens: Vec<Token> = Vec::new();
for (row, line) in lines {
let lt = lex_line(&line);
for (col, tok, tok_type) in lt {
let (tok_type, tok) = lex_word(tok, tok_type);
let t = Token{
file: file.to_string(),
line: row + 1,
col,
text: tok,
typ: tok_type,
value: None,
addr: None,
op_typ: crate::definitions::OpType::Instruction(crate::definitions::InstructionType::None)
};
tokens.push(t);
}
}
// println!("{}", tokens.len());
// for token in tokens.clone() {
// println!("tok: {:?}", token.text);
// }
tokens
}

389
src/lexer/mod.rs Normal file
View File

@ -0,0 +1,389 @@
use std::path::Path;
use anyhow::bail;
use crate::{error, types::{common::Loc, token::{InstructionType, KeywordType, Token, TokenType, TypeType}}};
pub struct Lexer {
pub loc: Loc,
pub tokens: Vec<Token>
}
impl Lexer {
pub fn new() -> Self {
Self {
loc: Default::default(),
tokens: Default::default(),
}
}
pub fn lex(&mut self, file: &Path) -> anyhow::Result<&mut Self> {
self.reset(file);
let chars = match std::fs::read_to_string(file) {
Ok(c) => c,
Err(e) => {
error!("Failed to open file {file:?} : {e}");
bail!("");
}
}.chars().collect::<Vec<char>>();
let mut idx = 0;
let mut buf = String::new();
let mut is_searching = false;
if let Err(_) = self.go_to_first_char(&chars, &mut idx) {
return Ok(self);
}
let mut start_loc = self.loc.clone();
while idx < chars.len() {
match chars[idx] {
'c' if chars.get(idx + 1) == Some(&'"') => {
start_loc = self.loc.clone();
is_searching = true;
idx += 2; // skip c and "
self.loc.col += 2;
if !buf.is_empty() {
debug!({loc => self.loc() }, "buffer was not empty, intresting");
}
loop {
if chars[idx] == '"' && chars[idx-1] != '\\' {
break;
}
buf.push(chars[idx]);
if chars[idx] == '\n' {
self.loc.inc_line()
}
self.loc.inc_col();
idx += 1;
}
buf.push('\0');
let str = self.unescape(&&buf);
self.loc.inc_col();
self.tokens.push(Token::new(TokenType::Instruction(InstructionType::PushCStr(str)), self.loc(), buf.clone()));
buf.clear();
}
'"' => {
start_loc = self.loc.clone();
is_searching = true;
idx += 1; // skip "
self.loc.col += 1;
if !buf.is_empty() {
debug!({loc => self.loc() }, "buffer was not empty, intresting ({buf:?})");
}
// while chars.get(idx+1) != Some(&'"') && chars[idx] != '\\' && chars.get(idx+1).is_some() {
loop {
if chars[idx] == '"' && chars[idx-1] != '\\' {
break;
}
buf.push(chars[idx]);
if chars[idx] == '\n' {
self.loc.inc_line()
}
self.loc.inc_col();
idx += 1;
}
let str = self.unescape(&buf);
self.loc.inc_col();
self.tokens.push(Token::new(TokenType::Instruction(InstructionType::PushStr(str)), start_loc.clone(), buf.clone()));
buf.clear();
}
'\'' => {
start_loc = self.loc.clone();
is_searching = true;
idx += 1; // skip '
self.loc.col += 1;
if !buf.is_empty() {
debug!({loc => self.loc() }, "buffer was not empty, intresting ({buf})");
}
loop {
if chars[idx] == '"' && chars[idx-1] != '\\' {
break;
}
buf.push(chars[idx]);
if chars[idx] == '\n' {
self.loc.inc_line()
}
self.loc.inc_col();
idx += 1;
}
let str = self.unescape(&&&buf);
if str.len() > 1 {
error!({loc => self.loc()}, "Chars can only have 1 char");
bail!("")
}
self.loc.inc_col();
self.tokens.push(Token::new(TokenType::Instruction(InstructionType::PushStr(str)), self.loc(), buf.clone()));
buf.clear();
}
':' if chars.get(idx + 1) == Some(&':') => {
let mut p_buf = vec![buf.clone()];
buf.clear();
idx += 2; // skip ::
self.loc.col += 2;
while idx < chars.len() {
match chars[idx] {
' ' | '\n' | '\r' => {
if !p_buf.is_empty() {
p_buf.push(buf.clone());
}
self.tokens.push(Token::new(TokenType::Instruction(InstructionType::StructPath(p_buf.clone())), start_loc.clone(), p_buf.clone().join("::")));
buf.clear();
break;
}
c @ ('\'' | '"') => {
error!({loc => self.loc()}, "Invalid char in struct path token, expected /a-z|A-Z|0-9|_|-/ got {c}");
bail!("")
}
':' if chars.get(idx + 1) == Some(&':') => {
if buf.is_empty() {
error!({loc => self.loc()}, "Invalid char in struct path token, expected /a-z|A-Z|0-9|_|-/ got '.'");
bail!("")
}
idx += 2; // skip ::
self.loc.col += 2;
p_buf.push(buf.clone());
buf.clear();
}
c => {
buf.push(c);
idx += 1;
self.loc.inc_col();
}
}
}
}
'.' if !buf.is_empty() => {
let mut p_buf = vec![buf.clone()];
buf.clear();
idx += 1; // skip .
self.loc.inc_col();
while idx < chars.len() {
match chars[idx] {
' ' | '\n' | '\r' => {
if !p_buf.is_empty() {
p_buf.push(buf.clone());
}
self.tokens.push(Token::new(TokenType::Instruction(InstructionType::StructItem(p_buf.clone())), start_loc.clone(), p_buf.clone().join(".")));
buf.clear();
break;
}
c @ ('\'' | '"') => {
error!({loc => self.loc()}, "Invalid char in struct access token, expected /a-z|A-Z|0-9|_|-/ got {c}");
bail!("")
}
'.' => {
if buf.is_empty() {
error!({loc => self.loc()}, "Invalid char in struct access token, expected /a-z|A-Z|0-9|_|-/ got '.'");
bail!("")
}
idx += 1; // skip .
self.loc.col += 1;
p_buf.push(buf.clone());
buf.clear();
}
c => {
buf.push(c);
idx += 1;
self.loc.inc_col();
}
}
}
}
ch @ (' ' | '\n' | '\r') => {
if ch == '\n' {
self.loc.inc_line();
} else {
self.loc.inc_col();
}
if !buf.is_empty() {
//TODO: Implement signed ints
if let Ok(int) = parse_int::parse::<usize>(&buf) {
self.tokens.push(Token::new(TokenType::Instruction(InstructionType::PushInt(int)), start_loc.clone(), buf.clone()));
} else {
let token_type = self.match_token_type(&buf);
self.tokens.push(Token::new(token_type, start_loc.clone(), buf.clone()));
}
buf.clear();
is_searching = true;
}
}
'/' if chars.get(idx + 1) == Some(&'/') => {
let mut c = chars.get(idx);
while c.is_some() && c != Some(&'\n') {
self.loc.inc_col();
idx += 1;
c = chars.get(idx);
}
self.loc.inc_line();
}
ch => {
if is_searching {
is_searching = false;
start_loc = self.loc.clone();
}
buf.push(ch);
self.loc.inc_col();
}
}
idx += 1;
}
//? Add last token
//TODO: Implement signed ints
if !buf.is_empty() {
if let Ok(int) = parse_int::parse::<usize>(&buf) {
self.tokens.push(Token::new(TokenType::Instruction(InstructionType::PushInt(int)), start_loc.clone(), buf.clone()));
} else {
let token_type = self.match_token_type(&buf);
self.tokens.push(Token::new(token_type, start_loc.clone(), buf.clone()));
}
}
// for t in &self.tokens {
// debug!({loc => t.loc.clone()}, "token: {:?}", t.typ);
// }
Ok(self)
}
fn go_to_first_char(&mut self, chars: &Vec<char>, idx: &mut usize) -> anyhow::Result<()> {
loop {
if let Some(c) = chars.get(*idx) {
match c {
' ' | '\r' => self.loc.inc_col(),
'\n' => self.loc.inc_line(),
_ => break,
}
*idx += 1;
} else {
warn!("Empty program");
bail!("")
}
}
Ok(())
}
fn match_token_type(&self, s: &str) -> TokenType {
match s {
"if" => TokenType::Keyword(KeywordType::If),
"else" => TokenType::Keyword(KeywordType::Else),
"end" => TokenType::Keyword(KeywordType::End),
"while" => TokenType::Keyword(KeywordType::While),
"do" => TokenType::Keyword(KeywordType::Do),
"include" => TokenType::Keyword(KeywordType::Include),
"memory" => TokenType::Keyword(KeywordType::Memory),
"const" => TokenType::Keyword(KeywordType::Constant),
"fn" => TokenType::Keyword(KeywordType::Function),
"then" => TokenType::Keyword(KeywordType::Then),
"done" => TokenType::Keyword(KeywordType::Done),
"typedef" => TokenType::Keyword(KeywordType::TypeDef),
"structdef" => TokenType::Keyword(KeywordType::StructDef),
"inline" => TokenType::Keyword(KeywordType::Inline),
"export" => TokenType::Keyword(KeywordType::Export),
"extern" => TokenType::Keyword(KeywordType::Extern),
"returns" => TokenType::Keyword(KeywordType::Returns),
"with" => TokenType::Keyword(KeywordType::With),
"drop" => TokenType::Instruction(InstructionType::Drop),
"_dbg_print" => TokenType::Instruction(InstructionType::Print),
"dup" => TokenType::Instruction(InstructionType::Dup),
"rot" => TokenType::Instruction(InstructionType::Rot),
"over" => TokenType::Instruction(InstructionType::Over),
"swap" => TokenType::Instruction(InstructionType::Swap),
"sub" => TokenType::Instruction(InstructionType::Minus),
"add" => TokenType::Instruction(InstructionType::Plus),
"eq" => TokenType::Instruction(InstructionType::Equals),
"gt" => TokenType::Instruction(InstructionType::Gt),
"lt" => TokenType::Instruction(InstructionType::Lt),
"ge" => TokenType::Instruction(InstructionType::Ge),
"le" => TokenType::Instruction(InstructionType::Le),
"neq" => TokenType::Instruction(InstructionType::NotEquals),
"band" => TokenType::Instruction(InstructionType::Band),
"bor" => TokenType::Instruction(InstructionType::Bor),
"shr" => TokenType::Instruction(InstructionType::Shr),
"shl" => TokenType::Instruction(InstructionType::Shl),
"divmod" => TokenType::Instruction(InstructionType::DivMod),
"mul" => TokenType::Instruction(InstructionType::Mul),
"read8" => TokenType::Instruction(InstructionType::Read8),
"write8" => TokenType::Instruction(InstructionType::Write8),
"read32" => TokenType::Instruction(InstructionType::Read32),
"write32" => TokenType::Instruction(InstructionType::Write32),
"read64" => TokenType::Instruction(InstructionType::Read64),
"write64" => TokenType::Instruction(InstructionType::Write64),
"syscall0" => TokenType::Instruction(InstructionType::Syscall0),
"syscall1" => TokenType::Instruction(InstructionType::Syscall1),
"syscall2" => TokenType::Instruction(InstructionType::Syscall2),
"syscall3" => TokenType::Instruction(InstructionType::Syscall3),
"syscall4" => TokenType::Instruction(InstructionType::Syscall4),
"syscall5" => TokenType::Instruction(InstructionType::Syscall5),
"syscall6" => TokenType::Instruction(InstructionType::Syscall6),
"(bool)" => TokenType::Instruction(InstructionType::CastBool),
"(ptr)" => TokenType::Instruction(InstructionType::CastPtr),
"(int)" => TokenType::Instruction(InstructionType::CastInt),
"(void)" => TokenType::Instruction(InstructionType::CastVoid),
"return" => TokenType::Instruction(InstructionType::Return),
"ptr" => TokenType::Type(TypeType::Ptr),
"u8" => TokenType::Type(TypeType::U8),
"u16" => TokenType::Type(TypeType::U16),
"u32" => TokenType::Type(TypeType::U32),
"u64" => TokenType::Type(TypeType::U64),
"void" => TokenType::Type(TypeType::Void),
"any" => TokenType::Type(TypeType::Any),
t => TokenType::Unknown(t.to_string())
}
}
pub fn reset(&mut self, file: &Path) -> &mut Self {
self.loc.file = file.to_string_lossy().to_string();
self.loc.line = 1;
self.loc.col = 0;
self.tokens = Vec::new();
self
}
fn loc(&self) -> Loc {
self.loc.clone()
}
fn unescape(&self, s: &String) -> String {
//TODO: add more escapes
s
.replace("\\n", "\n")
.replace("\\0", "\0")
}
}

32
src/logger/colors.rs Normal file
View File

@ -0,0 +1,32 @@
#![allow(dead_code)]
pub const RESET: &str = "\x1b[0m";
pub const BOLD: &str = "\x1b[1m";
pub const ITALIC: &str = "\x1b[3m";
pub const UNDERLINE: &str = "\x1b[4m";
pub const BLINK: &str = "\x1b[5m";
pub const BLINK2: &str = "\x1b[6m";
pub const SELECTED: &str = "\x1b[7m";
pub const BLACK: &str = "\x1b[30m";
pub const RED: &str = "\x1b[31m";
pub const GREEN: &str = "\x1b[32m";
pub const YELLOW: &str = "\x1b[33m";
pub const BLUE: &str = "\x1b[34m";
pub const MAGENTA: &str = "\x1b[35m";
pub const BEIGE: &str = "\x1b[36m";
pub const WHITE: &str = "\x1b[37m";
pub const BLACKBG: &str = "\x1b[40m";
pub const REDBG: &str = "\x1b[41m";
pub const GREENBG: &str = "\x1b[42m";
pub const YELLOWBG: &str = "\x1b[43m";
pub const BLUEBG: &str = "\x1b[44m";
pub const MAGENTABG: &str = "\x1b[45m";
pub const BEIGEBG: &str = "\x1b[46m";
pub const WHITEBG: &str = "\x1b[47m";
pub const GREY: &str = "\x1b[90m";
pub const RED2: &str = "\x1b[91m";
pub const GREEN2: &str = "\x1b[92m";
pub const YELLOW2: &str = "\x1b[93m";
pub const BLUE2: &str = "\x1b[94m";
pub const MAGENTA2: &str = "\x1b[95m";
pub const BEIGE2: &str = "\x1b[96m";
pub const WHITE2: &str = "\x1b[97m";

106
src/logger/macros.rs Normal file
View File

@ -0,0 +1,106 @@
#[macro_export]
macro_rules! log {
({$($k: expr => $v: expr),* $(,)? }, $lvl:expr, $($arg:tt),+) => {
crate::log_tagged!({$($k => $v,)*}, crate::logger::Level::Info, $($arg)+)
};
(module: $module:expr, $lvl:expr, $($arg:tt)+) => {
unsafe {
crate::logger::LOGGER.log(
crate::logger::LogEvent {
level: $lvl,
module_path: $module.to_string(),
message: format!($($arg)+),
tags: std::collections::HashMap::new()
}
)
}
};
($lvl:expr, $($arg:tt)+) => {
crate::log!(module: module_path!(), $lvl, $($arg)+)
};
}
#[macro_export]
macro_rules! log_tagged {
({$($k: expr => $v: expr),* $(,)? }, $module:expr, $lvl:expr, $($arg:tt)+) => {
unsafe {
crate::logger::LOGGER.log(
crate::logger::LogEvent {
level: $lvl,
module_path: $module.to_string(),
message: format!($($arg)+),
tags: map_macro::hash_map!{$(stringify!($k).to_string() => Box::new($v) as Box<dyn core::any::Any>,)*}
}
)
}
};
}
#[macro_export]
macro_rules! debug {
(module: $module:expr, $($arg:tt)+) => {
crate::log!(module: $module, crate::logger::Level::Debug, $($arg:tt)+)
};
({$($k: expr => $v: expr),* $(,)? }, $($arg:tt)+) => {
crate::log_tagged!({$($k => $v,)*}, module_path!(), crate::logger::Level::Debug, $($arg)+)
};
({$($k: expr => $v: expr),* $(,)? }, module: $module:expr, $($arg:tt)+) => {
crate::log_tagged!({$($k => $v,)*}, $module, crate::logger::Level::Debug, $($arg)+)
};
($($arg:tt)+) => {
crate::log!(crate::logger::Level::Debug, $($arg)+)
};
}
#[macro_export]
macro_rules! info {
(module: $module:expr, $($arg:tt)+) => {
crate::log!(module: $module, crate::logger::Level::Info, $($arg:tt)+)
};
({$($k: expr => $v: expr),* $(,)? }, $($arg:tt)+) => {
crate::log_tagged!({$($k => $v,)*}, module_path!(), crate::logger::Level::Info, $($arg)+)
};
({$($k: expr => $v: expr),* $(,)? }, module: $module:expr, $($arg:tt)+) => {
crate::log_tagged!({$($k => $v,)*}, $module, crate::logger::Level::Info, $($arg)+)
};
($($arg:tt)+) => {
crate::log!(crate::logger::Level::Info, $($arg)+)
};
}
#[macro_export]
macro_rules! warn {
(module: $module:expr, $($arg:tt)+) => {
crate::log!(module: $module, crate::logger::Level::Warn, $($arg:tt)+)
};
({$($k: expr => $v: expr),* $(,)? }, $($arg:tt)+) => {
crate::log_tagged!({$($k => $v,)*}, module_path!(), crate::logger::Level::Warn, $($arg)+)
};
({$($k: expr => $v: expr),* $(,)? }, module: $module:expr, $($arg:tt)+) => {
crate::log_tagged!({$($k => $v,)*}, $module, crate::logger::Level::Warn, $($arg)+)
};
($($arg:tt)+) => {
crate::log!(crate::logger::Level::Warn, $($arg)+)
};
}
#[macro_export]
macro_rules! error {
(module: $module:expr, $($arg:tt)+) => {
crate::log!(module: $module, crate::logger::Level::Error, $($arg:tt)+)
};
({$($k: expr => $v: expr),* $(,)? }, $($arg:tt)+) => {
crate::log_tagged!({$($k => $v,)*}, module_path!(), crate::logger::Level::Error, $($arg)+)
};
({$($k: expr => $v: expr),* $(,)? }, module: $module:expr, $($arg:tt)+) => {
crate::log_tagged!({$($k => $v,)*}, $module, crate::logger::Level::Error, $($arg)+)
};
($($arg:tt)+) => {
crate::log!(crate::logger::Level::Error, $($arg)+)
};
}

83
src/logger/mod.rs Normal file
View File

@ -0,0 +1,83 @@
// use log::{Level, LevelFilter, Metadata, Record, SetLoggerError};
use std::ops::Deref;
use crate::{cli::CliArgs, types::common::Loc};
mod types;
mod colors;
#[macro_use]
pub mod macros;
pub use types::{Level, LogEvent, LOGGER};
use types::*;
pub struct Logger{
pub level: i8
}
impl Logger {
pub fn new(args: &CliArgs) -> Box<Self> {
Box::new(Self {
level: args.verbose
})
}
pub fn init(args: &CliArgs) -> anyhow::Result<()>{
unsafe {
types::LOGGER = Box::leak(
Self::new(args)
);
}
Ok(())
}
fn get_prefix(&self, level: Level) -> String {
use colors::{BOLD, RESET, RED, YELLOW, BLUE, GREEN, MAGENTA};
match level {
Level::Error => format!("{BOLD}{RED}error{RESET}", ),
Level::Warn => format!("{BOLD}{YELLOW}warn{RESET}", ),
Level::Info => format!("{BOLD}{GREEN}info{RESET}", ),
Level::Debug => format!("{BOLD}{BLUE}debug{RESET}", ),
Level::Trace => format!("{BOLD}{MAGENTA}trace{RESET}", ),
}
}
}
impl Log for Logger {
fn enabled(&self, level: Level) -> bool {
match level {
Level::Error if self.level >= 0 => true,
Level::Warn |
Level::Info if self.level >= 1 => true,
Level::Debug if self.level >= 2 => true,
Level::Trace if self.level >= 3 => true,
_ => false
}
}
fn log(&self, event: LogEvent) {
if self.enabled(event.level) {
let modpath = if event.level > Level::Info {
format!(" [{}]", event.module_path)
} else {
String::new()
};
if let Some(loc) = event.tags.get("loc") {
let loc: String = (*loc.deref()).downcast_ref::<Loc>()
.map_or(String::from("INVALID"), |l| l.to_string());
println!("{} {}{modpath}: {}", loc, self.get_prefix(event.level), event.message);
} else {
println!("{}{modpath}: {}", self.get_prefix(event.level), event.message);
}
}
}
fn level(&self) -> i8 {
self.level
}
}

40
src/logger/types.rs Normal file
View File

@ -0,0 +1,40 @@
use std::{any::Any, collections::HashMap, fmt::Debug };
pub static mut LOGGER: &dyn Log = &NopLogger;
struct NopLogger;
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum Level {
Error = 1,
Warn,
Info,
Debug,
Trace,
}
// pub trait Tag: Display + Debug + Any {}
pub struct LogEvent {
pub level: Level,
pub module_path: String,
pub message: String,
pub tags: HashMap<String, Box<dyn Any>>
}
impl Log for NopLogger {
fn enabled(&self, _: Level) -> bool {false}
fn level(&self) -> i8 {0}
fn log(&self, _: LogEvent) {}
}
pub trait Log {
fn enabled(&self, level: Level) -> bool;
fn log(&self, event: LogEvent);
fn level(&self) -> i8;
}

View File

@ -1,127 +1,47 @@
#![allow(clippy::wildcard_imports)]
#![allow(clippy::too_many_lines)]
mod definitions;
mod util;
mod compile;
mod parser;
use std::collections::HashMap;
#[macro_use]
mod logger;
mod cli;
mod types;
mod lexer;
mod preprocessor;
mod typechecker;
mod precompiler;
mod config;
mod errors;
pub mod test;
use config::*;
use std::{fs, collections::HashMap};
pub mod parser;
mod compiler;
use clap::Parser;
use anyhow::{Result, bail};
fn main() {
let cli_args = cli::CliArgs::parse_with_passthrough();
logger::Logger::init(&cli_args).expect("Failed to init logger");
#[derive(Parser, Debug, Clone)]
#[command(author=env!("CARGO_PKG_AUTHORS"), version=env!("CARGO_PKG_VERSION"), about=env!("CARGO_PKG_DESCRIPTION"), long_about=env!("CARGO_PKG_DESCRIPTION"))]
pub struct Args {
/// Input source file
in_file: String,
let mut prog_map = HashMap::new();
for file in &cli_args.input {
let mut lexer = lexer::Lexer::new();
/// Output compiled file
#[arg(long, short, default_value_t=String::from(DEFAULT_OUT_FILE))]
out_file: String,
/// Interpert
#[arg(long, short='s')]
interpret: bool,
/// Run the compiled executable
#[arg(long, short)]
run: bool,
/// Dont print any output exept the actual running codes output
#[arg(long, short)]
quiet: bool,
/// Add an include directory [default: ["./include", "~/.mclang/include"]]
#[arg(long, short='I')]
include: Vec<String>,
/// Unsafe mode, disables typechecking
#[arg(long="unsafe", default_value_t = false)]
unsaf: bool,
/// Optimisation level, available levels: 'D': debug, '0': No optimisations
#[arg(long, short='O', default_value_t=String::from("0"))]
optimisation: String,
// disables the main function
#[arg(long="lib")]
lib_mode: bool
//#[arg(long, short='F')]
//features: Vec<String>,
}
impl Args {
/// Get optimisation level
/// 0 => no optimisations
/// 1 => slight optimisations, mostly size ones
/// # Errors
///
/// Throws when the opt level is not known
pub fn get_opt_level(&self) -> Result<usize>{
match self.optimisation.as_str() {
"D" | "d" => Ok(0),
"0" | "" => Ok(1),
o => {
error!("Unknown optimisation level {o}");
bail!("")
}
info!("Lexing file {file:?}");
if let Err(_) = lexer.lex(file.as_std_path()) {
error!("Lexing failed, exiting");
return;
}
// for t in &lexer.tokens {
// info!({loc => t.loc.clone()}, "{:?}", t.typ);
// }
// dbg!(&lexer.tokens);
info!("Parsing file {file:?}");
let prog = match parser::parse(&cli_args, &mut lexer.tokens) {
Ok(r) => r,
Err(_) => {
error!("Parsing failed, exiting");
return;
}
};
prog_map.insert(file.as_std_path(), prog);
}
}
fn main() -> Result<()>{
let args = Args::parse();
let Ok(code) = fs::read_to_string(&args.in_file) else {
error!("Failed to read file {}, exiting!", &args.in_file);
return Ok(());
};
let tokens = lexer::lex(&code, args.in_file.as_str(), &args);
let mut parser = parser::Parser::new(tokens, &args, None);
let program = match parser.parse(){
Ok(t) => t,
Err(e) => {
error!("Parsing failed, exiting!");
if crate::DEV_MODE {
return Err(e)
}
return Ok(());
}
};
match typechecker::typecheck(program.ops.clone(), &args, None, HashMap::new(), HashMap::new()) {
Ok(_) => (),
Err(e) => {
error!("Typechecking failed, exiting!");
if crate::DEV_MODE {
return Err(e);
}
return Ok(());
}
};
let c =match compile::linux_x86_64::compile(&program, &args) {
Ok(c) => c,
Err(e) => {
error!("Compilation failed, exiting!");
println!("{e}");
1
}
};
std::process::exit(c);
if let Err(_) = compiler::compile_program(&cli_args, prog_map) {
error!("Failed to compile program, exiting");
}
}

View File

@ -1,229 +0,0 @@
use std::ops::Deref;
use crate::{definitions::{Operator, OpType, Token, TokenType, Loc, KeywordType, InstructionType, InternalType, Program}, lerror, preprocessor::Preprocessor, Args};
use anyhow::{Result, bail};
pub fn cross_ref(mut program: Vec<Operator>) -> Result<Vec<Operator>> {
let mut stack: Vec<usize> = Vec::new();
for ip in 0..program.len() {
let op = &program.clone()[ip];
// println!("{op:?}");
match op.typ {
// OpType::Keyword(KeywordType::FunctionDef) |
OpType::Keyword(KeywordType::If | KeywordType::While) => {
stack.push(ip);
}
OpType::Keyword(KeywordType::Else) => {
let Some(if_ip) = stack.pop() else {
lerror!(&op.loc, "Unclosed-if else block");
bail!("Cross referencing")
};
if program[if_ip].typ != OpType::Keyword(KeywordType::If) {
lerror!(&op.clone().loc,"'else' can only close 'if' blocks");
bail!("Bad block")
}
program[if_ip].jmp = ip + 1;
stack.push(ip);
},
OpType::Keyword(KeywordType::End) => {
let Some(block_ip) = stack.pop() else {
lerror!(&op.loc, "Unclosed if, if-else, while-do, function, memory, or constant");
bail!("Cross referencing")
};
match &program[block_ip].typ {
OpType::Keyword(KeywordType::If | KeywordType::Else) => {
program[block_ip].jmp = ip;
program[ip].jmp = ip + 1;
}
OpType::Keyword(KeywordType::Do) => {
program[ip].jmp = program[block_ip].jmp;
program[block_ip].jmp = ip + 1;
}
OpType::Keyword(KeywordType::Memory | KeywordType::Constant) => (),
a => {
println!("{a:?}");
lerror!(&op.clone().loc,"'end' can only close if, if-else, while-do, function, memory, or constant blocks");
bail!("")
}
}
}
OpType::Keyword(KeywordType::Do) => {
let Some(block_ip) = stack.pop() else {
lerror!(&op.loc, "Unclosed while-do block");
bail!("Cross referencing")
};
program[ip].jmp = block_ip;
stack.push(ip);
}
_ => ()
}
}
if !stack.is_empty() {
// println!("{:?}", stack);
let i = stack.pop().expect("Empy stack");
lerror!(&program[i].clone().loc,"Unclosed block, {:?}", program[i].clone());
bail!("Unclosed block")
}
Ok(program.clone())
}
pub struct Parser<'a> {
tokens: Vec<Token>,
pub preprocessor: Preprocessor<'a>,
#[allow(dead_code)]
args: &'a Args
}
impl<'a> Parser<'a> {
pub fn new(file: Vec<Token>, args: &'a Args, p: Option<Preprocessor<'a>>) -> Self {
let pre = if let Some(p) = p {p} else {
Preprocessor::new(Vec::new(), args)
};
Self{
tokens: file,
preprocessor: pre,
args
}
}
pub fn parse(&mut self) -> Result<Program> {
let mut tokens = Vec::new();
for token in &self.tokens {
if token.text.is_empty() {
continue;
}
let pos = (token.file.clone(), token.line, token.col);
match token.typ {
TokenType::Word => {
let word_type = if token.op_typ == OpType::Instruction(InstructionType::MemUse) {
OpType::Instruction(InstructionType::MemUse)
} else {
lookup_word(&token.text, &pos)
};
tokens.push(Operator::new(word_type, token.typ, token.value.unwrap_or(0), token.text.clone(), token.file.clone(), token.line, token.col).set_addr(token.addr.unwrap_or(0)));
},
TokenType::Int => {// negative numbers not yet implemented
tokens.push(Operator::new(OpType::Instruction(InstructionType::PushInt), token.typ, token.text.parse::<usize>()?, String::new(), token.file.clone(), token.line, token.col));
},
TokenType::String => {
tokens.push(Operator::new(OpType::Instruction(InstructionType::PushStr), token.typ, 0, token.text.clone(), token.file.clone(), token.line, token.col));
},
TokenType::CString => {
tokens.push(Operator::new(OpType::Instruction(InstructionType::PushCStr), token.typ, 0, token.text.clone(), token.file.clone(), token.line, token.col));
},
TokenType::Char => {
let c = token.text.clone();
if c.len() != 1 {
lerror!(&token.loc(), "Chars can only be of lenght 1, got {}", c.len());
bail!("")
}
tokens.push(Operator::new(OpType::Instruction(InstructionType::PushInt), token.typ, token.text.chars().next().unwrap() as usize, String::new(), token.file.clone(), token.line, token.col));
}
};
}
self.preprocessor.program.ops = tokens;
let mut t = self.preprocessor.preprocess()?.get_program();
t.ops = cross_ref(t.ops)?;
Ok(t)
}
}
pub fn lookup_word<P: Deref<Target = Loc>>(s: &str, _pos: P) -> OpType {
let n = s.parse::<usize>();
if n.is_ok() {
return OpType::Instruction(InstructionType::PushInt);
}
match s {
//stack
"_dbg_print" => OpType::Instruction(InstructionType::Print),
"dup" => OpType::Instruction(InstructionType::Dup),
"drop" => OpType::Instruction(InstructionType::Drop),
"rot" => OpType::Instruction(InstructionType::Rot),
"over" => OpType::Instruction(InstructionType::Over),
"swap" => OpType::Instruction(InstructionType::Swap),
// comp and math
"+" => OpType::Instruction(InstructionType::Plus),
"-" => OpType::Instruction(InstructionType::Minus),
"=" => OpType::Instruction(InstructionType::Equals),
"!=" => OpType::Instruction(InstructionType::NotEquals),
">" => OpType::Instruction(InstructionType::Gt),
"<" => OpType::Instruction(InstructionType::Lt),
">=" => OpType::Instruction(InstructionType::Ge),
"<=" => OpType::Instruction(InstructionType::Le),
"band" => OpType::Instruction(InstructionType::Band),
"bor" => OpType::Instruction(InstructionType::Bor),
"shr" => OpType::Instruction(InstructionType::Shr),
"shl" => OpType::Instruction(InstructionType::Shl),
"divmod" => OpType::Instruction(InstructionType::DivMod),
"*" => OpType::Instruction(InstructionType::Mul),
// mem
"read8" => OpType::Instruction(InstructionType::Read8),
"write8" => OpType::Instruction(InstructionType::Write8),
"read32" => OpType::Instruction(InstructionType::Read32),
"write32" => OpType::Instruction(InstructionType::Write32),
"read64" => OpType::Instruction(InstructionType::Read64),
"write64" => OpType::Instruction(InstructionType::Write64),
"syscall0" => OpType::Instruction(InstructionType::Syscall0),
"syscall1" => OpType::Instruction(InstructionType::Syscall1),
"syscall2" => OpType::Instruction(InstructionType::Syscall2),
"syscall3" => OpType::Instruction(InstructionType::Syscall3),
"syscall4" => OpType::Instruction(InstructionType::Syscall4),
"syscall5" => OpType::Instruction(InstructionType::Syscall5),
"syscall6" => OpType::Instruction(InstructionType::Syscall6),
"cast(bool)" => OpType::Instruction(InstructionType::CastBool),
"cast(ptr)" => OpType::Instruction(InstructionType::CastPtr),
"cast(int)" => OpType::Instruction(InstructionType::CastInt),
"cast(void)" => OpType::Instruction(InstructionType::CastVoid),
// block
"if" => OpType::Keyword(KeywordType::If),
"else" => OpType::Keyword(KeywordType::Else),
"end" => OpType::Keyword(KeywordType::End),
"while" => OpType::Keyword(KeywordType::While),
"do" => OpType::Keyword(KeywordType::Do),
"include" => OpType::Keyword(KeywordType::Include),
"memory" => OpType::Keyword(KeywordType::Memory),
"const" => OpType::Keyword(KeywordType::Constant),
"fn" => OpType::Keyword(KeywordType::Function),
"then" => OpType::Keyword(KeywordType::FunctionThen),
"done" => OpType::Keyword(KeywordType::FunctionDone),
"inline" => OpType::Keyword(KeywordType::Inline),
"export" => OpType::Keyword(KeywordType::Export),
"struct" => OpType::Keyword(KeywordType::Struct),
"return" => OpType::Instruction(InstructionType::Return),
"returns" => OpType::Instruction(InstructionType::Returns),
"bool" => OpType::Instruction(InstructionType::TypeBool),
"int" => OpType::Instruction(InstructionType::TypeInt),
"ptr" => OpType::Instruction(InstructionType::TypePtr),
"void" => OpType::Instruction(InstructionType::TypeVoid),
"any" => OpType::Instruction(InstructionType::TypeAny),
"with" => OpType::Instruction(InstructionType::With),
"->" => OpType::Internal(InternalType::Arrow),
_ => OpType::Instruction(InstructionType::None)
}
}

46
src/parser/builtin.rs Normal file
View File

@ -0,0 +1,46 @@
use std::collections::HashMap;
use lazy_static::lazy_static;
use crate::types::{ast::{AstNode, Constant, Module, Program}, common::Loc};
lazy_static!(
static ref DEFAULT_CONSTANTS: HashMap<&'static str, AstNode> = {
let mut h = HashMap::new();
// No bsd cause im not about to create 3 or 4 diffrent compilation targets
h.insert("__WINDOWS", AstNode::Int(Loc::default(), cfg!(target_os = "windows") as usize));
h.insert("__LINUX", AstNode::Int(Loc::default(), cfg!(target_os = "linux") as usize));
h.insert("__ENDIAN_LITTLE", AstNode::Int(Loc::default(), cfg!(target_endian="little") as usize));
h.insert("__ENDIAN_BIG", AstNode::Int(Loc::default(), cfg!(target_endian="big") as usize));
h
};
);
pub fn get_builtin_symbols(prog: &mut Program) -> AstNode {
let mut md = Module {
loc: Loc::new(String::from("BUILTIN"), 0, 0),
path: vec![String::from("builtin")],
ident: String::from("BUILTIN"),
body: Vec::new(),
};
for (k, v) in DEFAULT_CONSTANTS.iter() {
let c = Constant {
loc: Loc::default(),
ident: k.to_string(),
value: Box::from(v.clone()),
};
prog.constants.insert(k.to_string(), c.clone());
md.body.push(AstNode::Constant(c));
}
AstNode::Module(md)
}

787
src/parser/mod.rs Normal file
View File

@ -0,0 +1,787 @@
mod utils;
mod precompiler;
mod builtin;
use std::{collections::HashMap, path::Path};
use anyhow::{bail, Result};
use crate::{cli::CliArgs, lexer::Lexer, types::{ast::{AstNode, Block, ConstUse, Constant, FnCall, Function, If, MemSize, MemUse, Memory, Module, Program, StructDef, While}, common::Loc, token::{InstructionType, KeywordType, Token, TokenType, TypeType}}};
use self::{builtin::get_builtin_symbols, precompiler::{precompile_const, precompile_mem}, utils::{expect, peek_check, peek_check_multiple, PeekResult}};
bitflags::bitflags! {
struct Flags: u8 {
const EXTERN = 1 << 0;
const EXPORT = 1 << 1;
const INLINE = 1 << 2;
const ALLOW_TYPES = 1 << 3;
}
}
//TODO: Implement Module paths
pub fn parse(cli_args: &CliArgs, tokens: &mut Vec<Token>) -> Result<Program> {
tokens.reverse();
let module = Module {
loc: Loc::new(&tokens[0].loc.file, 0, 0),
ident: Path::new(&tokens[0].loc.file).file_stem().expect("Something went horribly wrong").to_string_lossy().to_string(),
body: Vec::new(),
path: vec![],
};
let mut prog = Program {
ast: AstNode::Module(module.clone()),
functions: HashMap::new(),
constants: HashMap::new(),
memories: HashMap::new(),
struct_defs: HashMap::new()
};
let syms = get_builtin_symbols(&mut prog);
match &mut prog.ast {
AstNode::Module(module) => {
module.body.push(syms)
}
_ => unreachable!()
}
while !tokens.is_empty() {
let node = parse_next(cli_args, &mut prog, tokens, Flags::empty(), true)?;
match &mut prog.ast {
AstNode::Module(module) => {
module.body.push(node);
}
_ => unreachable!()
}
}
// prog.ast = module;
Ok(prog)
}
fn parse_next(cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>, flags: Flags, is_module_root: bool) -> Result<AstNode> {
let token = tokens.pop().expect("We broke reality!");
// debug!({loc => token.loc.clone()}, "t: {:?}", token.typ);
let ret = match &token.typ {
TokenType::Keyword(kw) => {
match kw {
KeywordType::If => parse_if(&token, cli_args, prog, tokens)?,
KeywordType::While => parse_while(&token, cli_args, prog, tokens)?,
KeywordType::Include => parse_include(&token, cli_args, prog, tokens)?,
KeywordType::Memory => parse_memory(&token, cli_args, prog, tokens, is_module_root)?,
KeywordType::Constant => parse_const(&token, cli_args, prog, tokens)?,
KeywordType::Function => parse_function(&token, cli_args, prog, tokens, flags)?,
KeywordType::StructDef => parse_struct(&token, cli_args, prog, tokens)?,
KeywordType::TypeDef => todo!(),
KeywordType::Inline => parse_inline(&token, cli_args, prog, tokens, flags)?,
KeywordType::Export => parse_export(&token, cli_args, prog, tokens, flags)?,
KeywordType::Extern => parse_extern(&token, cli_args, prog, tokens, flags)?,
kw => {
dbg!(&prog.constants);
error!({loc => token.loc}, "Unexpected token {kw:?}");
bail!("")
}
}
},
TokenType::Instruction(it) => {
if is_module_root {
error!({loc => token.loc}, "Unexpected token {it:?}, please create a main function, this is not a scripting language");
bail!("")
} else {
match it {
InstructionType::StructPath(p) => parse_struct_path(&token, prog, p)?,
InstructionType::StructItem(p) => parse_struct_item(&token, prog, p)?,
_ => AstNode::Token(token)
}
}
},
TokenType::Unknown(ut) => {
if is_module_root {
error!({loc => token.loc}, "Unexpected token {ut:?}, please create a main function, this is not a scripting language");
bail!("")
} else {
// AstNode::Token(token)
parse_unknown(&token, cli_args, prog, tokens, flags)?
}
},
TokenType::Type(t) => {
if flags.contains(Flags::ALLOW_TYPES) {
AstNode::Token(token)
} else {
error!({loc => token.loc}, "Unexpected type {t:?}");
bail!("")
}
},
};
Ok(ret)
}
fn parse_struct_item(org: &Token, prog: &mut Program, p: &Vec<String>) -> Result<AstNode> {
fn find_disp(strct: &StructDef, disp: &mut usize, path: &[String]) {
let Some(p) = path.get(0) else {
return
};
for item in &strct.body {
if p == &item.0 {
match &item.2 {
TypeType::Struct(strct) => {
*disp += item.1;
find_disp(strct, disp, &path[1..])
},
_ => {
*disp += item.1;
}
}
}
}
}
if let Some(mem) = prog.memories.get(&p[0].to_string()) {
match &mem.size {
MemSize::Size(_) => {
error!({loc => org.loc()}, "You can only access items in structs");
bail!("")
},
MemSize::Type(t) => {
match t {
TypeType::Struct(s) => {
let mut disp = 0;
find_disp(&s, &mut disp, &p[1..]);
return Ok(AstNode::MemUse(MemUse{
ident: p[0].clone(),
loc: org.loc(),
disp: Some(disp)
}));
},
_ => {
error!({loc => org.loc()}, "You can only access items in structs");
bail!("")
}
}
},
}
}
error!("Failed to find memory {}", p[0]);
bail!("")
}
fn parse_struct_path(org: &Token, prog: &mut Program, p: &Vec<String>) -> Result<AstNode> {
fn find_disp(strct: &StructDef, disp: &mut usize, path: &[String]) {
let Some(p) = path.get(0) else {
return
};
for item in &strct.body {
if p == &item.0 {
match &item.2 {
TypeType::Struct(strct) => {
*disp += item.1;
find_disp(strct, disp, &path[1..])
},
_ => {
*disp += item.1;
}
}
}
}
}
let mut disp = 0;
if let Some(strct) = prog.struct_defs.get(&p[0].to_string()) {
find_disp(strct, &mut disp, &p[1..]);
return Ok(AstNode::StructDispPush{
ident: org.lexem.clone(),
loc: org.loc(),
disp
});
}
error!("Failed to find struct {}", p[0]);
bail!("")
}
fn parse_struct(org: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>) -> Result<AstNode> {
let ident = expect(tokens, TokenType::Unknown(String::new()))?;
expect(tokens, TokenType::Keyword(KeywordType::Do))?;
let mut body: Vec<(String, usize, TypeType)> = Vec::new();
let mut size = 0;
loop {
let ident = expect(tokens, TokenType::Unknown(String::new()))?;
expect(tokens, TokenType::Keyword(KeywordType::Do))?;
let typ = parse_next(cli_args, prog, tokens, Flags::ALLOW_TYPES, false)?;
let (typ, disp) = match &typ {
AstNode::Token(t) => {
match &t.typ {
TokenType::Type(t) => {
let disp = size;
size += t.get_size();
(t, disp)
}
_ => {
error!({loc => t.loc()}, "Expected type, got {t:?}");
bail!("")
}
}
},
t => {
error!({loc => typ.loc()}, "Expected type, got {t:?}");
bail!("")
}
};
expect(tokens, TokenType::Keyword(KeywordType::End))?;
body.push((ident.lexem, disp, typ.clone()));
if peek_check(tokens, TokenType::Keyword(KeywordType::Done)).correct(){
tokens.pop();
break;
}
// if peek_check(tokens, TokenType::Keyword(KeywordType::End)).correct()
};
let def = StructDef{
loc: org.loc(),
ident: ident.lexem.clone(),
body,
size,
};
prog.struct_defs.insert(ident.lexem, def.clone());
Ok(AstNode::StructDef(def))
}
fn parse_memory(org: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>, is_module_root: bool) -> Result<AstNode> {
let name = expect(tokens, TokenType::Unknown(String::new()))?;
let mut body = Vec::new();
loop {
let t = peek_check(tokens, TokenType::Keyword(KeywordType::End));
match t {
PeekResult::Correct(_) => break,
PeekResult::Wrong(_) => (),
PeekResult::None => panic!("idk what to do herre"),
}
body.push(parse_next(cli_args, prog, tokens, Flags::ALLOW_TYPES, false)?);
}
expect(tokens, TokenType::Keyword(KeywordType::End))?;
let val = precompile_mem(prog, body)?;
let name = name.lexem.clone()
.replace("(", "_OPRN_")
.replace(")", "_CPRN_");
let def = Memory{
loc: org.loc(),
ident: name.clone(),
size: val,
statc: is_module_root,
};
prog.memories.insert(name, def.clone());
Ok(AstNode::Memory(def))
}
// TODO: Extern functions
fn parse_function(org: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>, flags: Flags ) -> Result<AstNode> {
let name = expect(tokens, TokenType::Unknown(String::new()))?;
expect(tokens, TokenType::Keyword(KeywordType::With))?;
let mut args = Vec::new();
loop {
if let PeekResult::Correct(t) = peek_check_multiple(tokens, vec![
TokenType::Type(TypeType::Any),
TokenType::Type(TypeType::U8),
TokenType::Type(TypeType::U16),
TokenType::Type(TypeType::U32),
TokenType::Type(TypeType::U64),
TokenType::Type(TypeType::Ptr),
TokenType::Type(TypeType::Void),
TokenType::Type(TypeType::Custom(Vec::new())),
]) {
match &t.typ {
TokenType::Type(tt) => {
args.push(tt.clone());
}
_ => unreachable!()
}
} else {
break;
}
tokens.pop();
}
expect(tokens, TokenType::Keyword(KeywordType::Returns))?;
let mut ret_args = Vec::new();
loop {
if let PeekResult::Correct(t) = peek_check_multiple(tokens, vec![
TokenType::Type(TypeType::Any),
TokenType::Type(TypeType::U8),
TokenType::Type(TypeType::U16),
TokenType::Type(TypeType::U32),
TokenType::Type(TypeType::U64),
TokenType::Type(TypeType::Ptr),
TokenType::Type(TypeType::Void),
TokenType::Type(TypeType::Custom(Vec::new())),
]) {
match &t.typ {
TokenType::Type(tt) => {
ret_args.push(tt.clone());
}
_ => unreachable!()
}
} else {
break;
}
tokens.pop();
}
expect(tokens, TokenType::Keyword(KeywordType::Then))?;
let mut body = Vec::new();
loop {
let fn_got = peek_check(tokens, TokenType::Keyword(KeywordType::Done));
match fn_got {
PeekResult::Correct(_) => break,
PeekResult::Wrong(_) => (),
PeekResult::None => panic!("idk what to do herre"),
}
body.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
}
expect(tokens, TokenType::Keyword(KeywordType::Done))?;
let fn_def = Function {
loc: org.loc(),
inline: flags.contains(Flags::INLINE),
extrn: flags.contains(Flags::EXTERN),
export: flags.contains(Flags::EXPORT),
ident: name.lexem.clone(),
arg_types: args,
ret_types: ret_args,
body,
};
//TODO: Support module paths without double definitions
// let mut mp = match &prog.ast {
// AstNode::Module(m) => {
// m.path.clone()
// }
// _ => panic!("")
// };
// mp.push(name.lexem.clone());
// let mp = mp.join("::");
// prog.function_aliases.insert(mp, name.lexem.clone());
prog.functions.insert(name.lexem.clone(), fn_def.clone());
Ok(AstNode::Function(fn_def))
}
fn parse_if(org: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>) -> Result<AstNode> {
let mut test: Vec<AstNode> = Vec::new();
let mut body: Vec<AstNode> = Vec::new();
let mut els: Vec<AstNode> = Vec::new();
loop {
test.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
match peek_check(tokens, TokenType::Keyword(KeywordType::Do)) {
PeekResult::Correct(_) => break,
PeekResult::Wrong(w) => {
match w.typ {
TokenType::Keyword(KeywordType::Then) => {
warn!({loc => w.loc()}, "If is defined as `if ... do ... done`");
}
_ => ()
}
},
PeekResult::None => panic!("idk what to do herre"),
}
}
expect(tokens, TokenType::Keyword(KeywordType::Do))?;
loop {
body.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
match peek_check_multiple(tokens, vec![
TokenType::Keyword(KeywordType::Else),
TokenType::Keyword(KeywordType::Done),
]) {
PeekResult::Correct(_) => break,
PeekResult::Wrong(_) => (),
PeekResult::None => panic!("idk what to do herre"),
}
}
let els_t = tokens.last().expect("IMPOSSIBLEEE!!!!!!111").clone();
let els = match els_t.typ.clone() {
TokenType::Keyword(kw) => {
match kw {
KeywordType::Done => {
expect(tokens, TokenType::Keyword(KeywordType::Done))?;
AstNode::Block(Block{
comment: String::new(),
loc: els_t.loc,
body: Vec::new(),
})
},
KeywordType::Else => {
expect(tokens, TokenType::Keyword(KeywordType::Else))?;
if peek_check(tokens, TokenType::Keyword(KeywordType::If)).correct() {
let if_org =expect(tokens, TokenType::Keyword(KeywordType::If))?;
parse_if(&if_org, cli_args, prog, tokens)?
} else {
loop {
els.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
match peek_check(tokens, TokenType::Keyword(KeywordType::Done)) {
PeekResult::Correct(_) => break,
PeekResult::Wrong(w) => {
match w.typ {
TokenType::Keyword(KeywordType::Then) => {
warn!("If is defined as `if ... do ... done`");
}
_ => ()
}
},
PeekResult::None => panic!("idk what to do herre"),
}
}
expect(tokens, TokenType::Keyword(KeywordType::Done))?;
AstNode::Block(Block{
comment: String::new(),
loc: els_t.loc,
body: els,
})
}
},
e => {
error!({loc => els_t.loc.clone()}, "Expected {:?} or {:?} but got {:?}", KeywordType::Done, KeywordType::Else, e);
bail!("");
}
}
},
e => {
error!({loc => els_t.loc.clone()}, "Expected {:?} or {:?} but got {:?}", KeywordType::Done, KeywordType::Else, e);
bail!("");
}
};
Ok(AstNode::If(If{
test,
body,
els: Box::new(els),
loc: org.loc(),
}))
}
fn parse_while(org: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>) -> Result<AstNode> {
let mut test: Vec<AstNode> = Vec::new();
let mut body: Vec<AstNode> = Vec::new();
loop {
test.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
match peek_check(tokens, TokenType::Keyword(KeywordType::Do)) {
PeekResult::Correct(_) => break,
PeekResult::Wrong(w) => {
match w.typ {
TokenType::Keyword(KeywordType::Then) => {
warn!("while is defined as `while ... do ... done`");
}
_ => ()
}
},
PeekResult::None => panic!("idk what to do herre"),
}
}
expect(tokens, TokenType::Keyword(KeywordType::Do))?;
loop {
body.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
match peek_check_multiple(tokens, vec![
TokenType::Keyword(KeywordType::Else),
TokenType::Keyword(KeywordType::Done),
]) {
PeekResult::Correct(_) => break,
PeekResult::Wrong(_) => (),
PeekResult::None => panic!("idk what to do herre"),
}
}
expect(tokens, TokenType::Keyword(KeywordType::Done))?;
Ok(AstNode::While(While{
test,
body,
loc: org.loc(),
}))
}
fn parse_inline(_: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>, flags: Flags) -> Result<AstNode> {
let allowed_tokens = vec!{
TokenType::Keyword(KeywordType::Function)
};
let Some(t) = tokens.last() else {
error!("Expected one of {:?} after {:?} but found nothing", allowed_tokens, TokenType::Keyword(KeywordType::Inline));
bail!("")
};
let mut found = false;
for at in &allowed_tokens {
if utils::cmp(at, &t.typ) {
found = true;
}
}
if !found {
error!({loc => t.loc.clone()}, "Expected one of {:?} after {:?} but found {:?}", allowed_tokens, TokenType::Keyword(KeywordType::Inline), t.typ);
bail!("");
}
parse_next(cli_args, prog, tokens, flags | Flags::INLINE, false)
}
fn parse_extern(_: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>, flags: Flags) -> Result<AstNode> {
let allowed_tokens = vec!{
TokenType::Keyword(KeywordType::Function),
TokenType::Keyword(KeywordType::Constant),
TokenType::Keyword(KeywordType::Memory),
};
let Some(t) = tokens.last() else {
error!("Expected one of {:?} after {:?} but found nothing", allowed_tokens, TokenType::Keyword(KeywordType::Extern));
bail!("")
};
let mut found = false;
for at in &allowed_tokens {
if utils::cmp(at, &t.typ) {
found = true;
}
}
if !found {
error!({loc => t.loc.clone()}, "Expected one of {:?} after {:?} but found {:?}", allowed_tokens, TokenType::Keyword(KeywordType::Extern), t.typ);
bail!("");
}
parse_next(cli_args, prog, tokens, flags | Flags::EXTERN, false)
}
fn parse_export(_: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>, flags: Flags) -> Result<AstNode> {
let allowed_tokens = vec!{
TokenType::Keyword(KeywordType::Function),
TokenType::Keyword(KeywordType::Constant),
TokenType::Keyword(KeywordType::Memory),
};
let Some(t) = tokens.last() else {
error!("Expected one of {:?} after {:?} but found nothing", allowed_tokens, TokenType::Keyword(KeywordType::Export));
bail!("")
};
let mut found = false;
for at in &allowed_tokens {
if utils::cmp(at, &t.typ) {
found = true;
}
}
if !found {
error!({loc => t.loc.clone()}, "Expected one of {:?} after {:?} but found {:?}", allowed_tokens, TokenType::Keyword(KeywordType::Export), t.typ);
bail!("");
}
parse_next(cli_args, prog, tokens, flags | Flags::EXPORT, false)
}
fn parse_include(_: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>) -> Result<AstNode> {
let path = expect(tokens,
TokenType::Instruction(
InstructionType::PushStr(
String::new()
)
)
)?;
for ip in &cli_args.include_path {
let p = ip.join(&path.lexem).to_path_buf();
if p.exists() {
info!({loc => path.loc.clone()}, "Lexing file {}", path.lexem.clone());
let mut lexer = Lexer::new();
lexer.lex(p.as_std_path())?;
let mut mod_tokens = lexer.tokens;
mod_tokens.reverse();
let mut mp = match &prog.ast {
AstNode::Module(m) => {
m.path.clone()
}
_ => panic!("")
};
mp.push(p.file_stem().unwrap().to_string());
let module = Module {
loc: Loc::new(path.loc.file.clone(), 0, 0),
ident: Path::new(&path.loc.file).file_stem().expect("Something went horribly wrong").to_string_lossy().to_string(),
body: Vec::new(),
path: mp,
};
let mut mod_prog = Program {
ast: AstNode::Module(module),
functions: prog.functions.clone(),
constants: prog.constants.clone(),
memories: prog.memories.clone(),
struct_defs: prog.struct_defs.clone(),
};
info!({loc => path.loc.clone()}, "Parsing file {}", path.lexem.clone());
while !mod_tokens.is_empty() {
let node = parse_next(cli_args, &mut mod_prog, &mut mod_tokens, Flags::empty(), true)?;
match &mut mod_prog.ast {
AstNode::Module(module) => {
module.body.push(node);
}
_ => unreachable!()
}
}
prog.constants = mod_prog.constants;
prog.functions = mod_prog.functions;
prog.memories = mod_prog.memories;
return Ok(mod_prog.ast)
}
};
error!("Could not find file {:?} in these locations: {:?}", path.lexem, cli_args.include_path);
bail!("")
}
fn parse_const(org: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>) -> Result<AstNode> {
let name = expect(tokens, TokenType::Unknown(String::new()))?;
let mut body = Vec::new();
loop {
let t = peek_check(tokens, TokenType::Keyword(KeywordType::End));
match t {
PeekResult::Correct(_) => break,
PeekResult::Wrong(_) => (),
PeekResult::None => panic!("idk what to do herre"),
}
body.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
}
expect(tokens, TokenType::Keyword(KeywordType::End))?;
let val = precompile_const(prog, body, &mut Vec::new())?;
let def = Constant{
loc: org.loc(),
ident: name.lexem.clone(),
value: Box::new(val),
};
prog.constants.insert(name.lexem, def.clone());
Ok(AstNode::Constant(def))
}
fn parse_unknown(org: &Token, _: &CliArgs, prog: &mut Program, _: &mut Vec<Token>, _: Flags ) -> Result<AstNode> {
//TODO: Typing?
if let Some(func) = prog.functions.get(&org.lexem.clone()) {
if func.inline {
return Ok(AstNode::Block(Block{ loc: org.loc.clone(), body: func.body.clone(), comment: format!("inline fn {}", func.ident) }))
} else {
return Ok(AstNode::FnCall(FnCall{ loc: org.loc.clone(), ident: org.lexem.clone() }));
}
}
if let Some(_) = prog.constants.get(&org.lexem.clone()) {
return Ok(AstNode::ConstUse(ConstUse{ loc: org.loc.clone(), ident: org.lexem.clone() }));
}
if let Some(_) = prog.memories.get(&org.lexem.clone()) {
return Ok(AstNode::MemUse(MemUse{ loc: org.loc.clone(), ident: org.lexem.clone(), disp: None }));
}
if let Some(t) = prog.struct_defs.get(&org.lexem.clone()) {
return Ok(AstNode::Token(Token {
typ: TokenType::Type(TypeType::Struct(t.clone())),
loc: org.loc(),
lexem: org.lexem.clone(),
}));
}
// if org.lexem.clone().contains("::") {
// let pth = org.lexem.clone();
// let pth = pth.split("::").collect::<Vec<&str>>();
// dbg!(prog.struct_defs.clone());
// if let Some(t) = prog.struct_defs.get(&pth[0].to_string()) {
// if let Some(i) = t.body.iter().find(|i| i.0 == pth[1].to_string()) {
// return Ok(AstNode::StructDispPush{
// ident: org.lexem.clone(),
// loc: org.loc(),
// disp: i.1
// });
// }
// }
// }
// dbg!(&prog.constants);
debug!({loc => org.loc.clone()}, "Unknown token");
error!({loc => org.loc.clone()}, "Unknown token {:?}", org);
bail!("")
}

213
src/parser/precompiler.rs Normal file
View File

@ -0,0 +1,213 @@
use anyhow::bail;
use crate::types::{ast::{AstNode, MemSize, Program}, common::Loc, token::{InstructionType, TokenType, TypeType}};
pub fn precompile_mem(prog: &Program, ast: Vec<AstNode> ) -> anyhow::Result<MemSize> {
match &ast[0] {
AstNode::Token(t) => {
match &t.typ {
TokenType::Type(_) => {
let mut buf = vec![];
let mut i = 0;
while ast.len() > i {
match &ast[i] {
AstNode::Token(t) => {
match &t.typ {
TokenType::Type(t) => {
match t {
TypeType::Struct(s) => {
return Ok(MemSize::Type(TypeType::Struct(s.clone())));
},
_ => ()
}
buf.push(t.clone());
i += 1;
}
_ => {
error!({loc => t.loc()}, "Cannot use a type and a number as a memory size at the same time");
bail!("")
}
}
},
_ => {
error!({loc => t.loc()}, "Cannot use a type and a number as a memory size at the same time");
bail!("")
}
}
}
return Ok(MemSize::Type(TypeType::Custom(buf)));
}
_ => ()
}
},
_ => (),
}
match precompile_const(prog, ast, &mut Vec::new()) {
Ok(v) => {
match v {
AstNode::Int(_, i) => {
return Ok(MemSize::Size(i))
}
_ => {
error!({loc => v.loc()}, "Can only have a type or a number as a memory size");
bail!("")
}
}
},
Err(e) => bail!(e),
}
}
pub fn precompile_const(prog: &Program, ast: Vec<AstNode>, stack: &mut Vec<usize> ) -> anyhow::Result<AstNode> {
for node in ast.clone() {
match &node {
AstNode::ConstUse(c) => {
let Some(val) = prog.constants.get(&c.ident) else {
error!({loc => c.loc.clone()}, "Unknown constant {:?}", c.ident) ;
bail!("")
};
match Box::leak(val.value.clone()) {
t @ AstNode::Int(..) => {
return Ok(t.clone());
}
t @ AstNode::Str(..) => {
return Ok(t.clone());
}
t @ AstNode::CStr(..) => {
return Ok(t.clone());
}
t @ AstNode::Char(..) => {
return Ok(t.clone());
}
// AstNode::Token(t) => {
// match t.typ.clone() {
// TokenType::Instruction(it) => {
// match it {
// InstructionType::PushInt(i) => stack.push(i),
// InstructionType::PushCStr(_) => {
// //TODO: Handle this better
// return Ok(AstNode::Token(t.clone()));
// },
// InstructionType::PushChar(_) => {
// //TODO: Handle this better
// return Ok(AstNode::Token(t.clone()));
// },
// _ => panic!()
// }
// },
// _ => panic!()
// }
// },
_ => panic!()
}
},
AstNode::Token(t) => {
match t.typ.clone() {
TokenType::Keyword(_) => {
error!({loc => t.loc.clone()}, "Unsupported token {t:?}, we dont support precompilation of this") ;
bail!("")
},
TokenType::Instruction(it) => {
match it {
InstructionType::PushInt(i) => {
stack.push(i);
},
InstructionType::PushCStr(s) => {
//TODO: Handle this better
return Ok(AstNode::CStr(t.loc.clone(), s));
},
InstructionType::PushStr(s) => {
//TODO: Handle this better
return Ok(AstNode::Str(t.loc.clone(), s));
},
InstructionType::PushChar(c) => {
//TODO: Handle this better
return Ok(AstNode::Char(t.loc.clone(), c));
},
InstructionType::Minus => {
let a = stack_pop(stack, &t.loc)?;
let b = stack_pop(stack, &t.loc)?;
stack.push(b - a);
},
InstructionType::Plus => {
let a = stack_pop(stack, &t.loc)?;
let b = stack_pop(stack, &t.loc)?;
stack.push(b + a);
},
InstructionType::DivMod => {
let a = stack_pop(stack, &t.loc)?;
let b = stack_pop(stack, &t.loc)?;
stack.push(b / a);
stack.push(b % a);
},
InstructionType::Mul => {
let a = stack_pop(stack, &t.loc)?;
let b = stack_pop(stack, &t.loc)?;
stack.push(b * a);
},
InstructionType::Drop => {
stack_pop(stack, &t.loc)?;
},
//TODO: Support these later
// InstructionType::Dup => todo!(),
// InstructionType::Rot => todo!(),
// InstructionType::Over => todo!(),
// InstructionType::Swap => todo!(),
// InstructionType::Equals => todo!(),
// InstructionType::Gt => todo!(),
// InstructionType::Lt => todo!(),
// InstructionType::Ge => todo!(),
// InstructionType::Le => todo!(),
// InstructionType::NotEquals => todo!(),
// InstructionType::Band => todo!(),
// InstructionType::Bor => todo!(),
// InstructionType::Shr => todo!(),
// InstructionType::Shl => todo!(),
//TODO: Support this when we have types
// InstructionType::CastBool => todo!(),
// InstructionType::CastPtr => todo!(),
// InstructionType::CastInt => todo!(),
// InstructionType::CastVoid => todo!(),
InstructionType::ConstUse => unreachable!(),
_ => {
error!({loc => t.loc.clone()}, "Unsupported token {t:?}, we dont support precompilation of this") ;
bail!("")
}
}
},
TokenType::Unknown(_) => unreachable!(),
TokenType::Type(_) => {
error!({loc => t.loc()}, "Cannot use a type and a number as a memory size at the same time");
bail!("")
},
}
},
//TODO: Implement these
t @ AstNode::If { .. } |
t @ AstNode::While { .. } |
t => {
error!({loc => t.loc()}, "Unsupported token {t:?}, we dont support precompilation of this") ;
bail!("")
}
}
}
Ok(AstNode::Int(ast[0].loc(), stack[0]))
}
fn stack_pop(stack: &mut Vec<usize>, loc: &Loc) -> anyhow::Result<usize> {
match stack.pop() {
Some(i) => Ok(i),
None => {
error!({loc => loc.clone()}, "Failed to precompile tokens, failed to pop from stack");
bail!("")
},
}
}

103
src/parser/utils.rs Normal file
View File

@ -0,0 +1,103 @@
use anyhow::{bail, Result};
use crate::types::token::{Token, TokenType};
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum PeekResult<T> {
Correct(T),
Wrong(T),
None
}
impl<T> PeekResult<T> {
pub fn correct(&self) -> bool{
match self {
PeekResult::Correct(_) => true,
_ => false
}
}
#[allow(dead_code)]
pub fn wrong(&self) -> bool{
match self {
PeekResult::Wrong(_) => true,
_ => false
}
}
#[allow(dead_code)]
pub fn none(&self) -> bool{
match self {
PeekResult::None => true,
_ => false
}
}
}
pub fn cmp(lhs: &TokenType, rhs: &TokenType) -> bool {
match (lhs, rhs) {
(TokenType::Keyword(lhs), TokenType::Keyword(rhs)) => {
std::mem::discriminant(lhs) == std::mem::discriminant(rhs)
},
(TokenType::Instruction(lhs), TokenType::Instruction(rhs)) => {
std::mem::discriminant(lhs) == std::mem::discriminant(rhs)
},
(TokenType::Type(lhs), TokenType::Type(rhs)) => {
std::mem::discriminant(lhs) == std::mem::discriminant(rhs)
},
(TokenType::Unknown(_), TokenType::Unknown(_)) => true,
_ => false
}
}
pub fn peek_check_multiple(tokens: &Vec<Token>, typs: Vec<TokenType>) -> PeekResult<&Token>{
let t = tokens.last();
if let Some(t) = t {
for tt in typs.clone() {
if cmp(&t.typ, &tt) {
return PeekResult::Correct(t);
}
}
PeekResult::Wrong(t)
} else {
PeekResult::None
}
}
pub fn peek_check(tokens: &Vec<Token>, typ: TokenType) -> PeekResult<&Token> {
let t = tokens.last();
match t {
Some(t) => {
//? Source: https://doc.rust-lang.org/std/mem/fn.discriminant.html
if cmp(&t.typ, &typ) {
PeekResult::Correct(t)
} else {
PeekResult::Wrong(t)
}
},
None => {
PeekResult::None
}
}
}
pub fn expect(tokens: &mut Vec<Token>, typ: TokenType) -> Result<Token> {
let t = tokens.pop();
match t {
Some(t) => {
//? Source: https://doc.rust-lang.org/std/mem/fn.discriminant.html
if std::mem::discriminant(&t.typ) != std::mem::discriminant(&typ) {
error!({loc => t.loc()}, "Expected {:?}, but got {:?}", typ, t.typ);
bail!("")
}
Ok(t)
},
None => {
error!("Expected {:?}, but found nothing", typ);
bail!("")
}
}
}

View File

@ -1,147 +0,0 @@
use anyhow::{Result, bail};
use crate::{definitions::{ OpType, InstructionType, Loc, Operator}, lerror};
fn stack_pop(stack: &mut Vec<usize>, loc: &Loc) -> Result<usize> {
if let Some(i) = stack.pop() { Ok(i) } else {
lerror!(&loc.clone(), "Stack underflow");
bail!("Stack underflow")
}
}
pub fn precompile(tokens: &Vec<Operator>) -> Result<Vec<usize>>{
let mut stack: Vec<usize> = Vec::new();
for token in tokens.iter() {
match token.typ.clone() {
OpType::Instruction(i) => {
let loc = token.loc.clone();
match i {
InstructionType::PushInt => {
stack.push(token.value);
},
InstructionType::Plus => {
let a = stack_pop(&mut stack, &loc)?;
let b = stack_pop(&mut stack, &loc)?;
stack.push(b + a);
},
InstructionType::Minus => {
let a = stack_pop(&mut stack, &loc)?;
let b = stack_pop(&mut stack, &loc)?;
stack.push(b - a);
},
InstructionType::Equals => {
let a = stack_pop(&mut stack, &loc)?;
let b = stack_pop(&mut stack, &loc)?;
stack.push(usize::from(b == a));
},
InstructionType::Gt => {
let a = stack_pop(&mut stack, &loc)?;
let b = stack_pop(&mut stack, &loc)?;
stack.push(usize::from(b > a));
},
InstructionType::Lt => {
let a = stack_pop(&mut stack, &loc)?;
let b = stack_pop(&mut stack, &loc)?;
stack.push(usize::from(b < a));
},
InstructionType::NotEquals => {
let a = stack_pop(&mut stack, &loc)?;
let b = stack_pop(&mut stack, &loc)?;
stack.push(usize::from(b != a));
},
InstructionType::Ge => {
let a = stack_pop(&mut stack, &loc)?;
let b = stack_pop(&mut stack, &loc)?;
stack.push(usize::from(b >= a));
},
InstructionType::Le => {
let a = stack_pop(&mut stack, &loc)?;
let b = stack_pop(&mut stack, &loc)?;
stack.push(usize::from(b <= a));
},
InstructionType::Band => {
let a = stack_pop(&mut stack, &loc)?;
let b = stack_pop(&mut stack, &loc)?;
stack.push(a & b);
}
InstructionType::Bor => {
let a = stack_pop(&mut stack, &loc)?;
let b = stack_pop(&mut stack, &loc)?;
stack.push(a | b);
}
InstructionType::Shr => {
let a = stack_pop(&mut stack, &loc)?;
let b = stack_pop(&mut stack, &loc)?;
stack.push(b >> a);
}
InstructionType::Shl => {
let a = stack_pop(&mut stack, &loc)?;
let b = stack_pop(&mut stack, &loc)?;
stack.push(b << a);
}
InstructionType::DivMod => {
let a = stack_pop(&mut stack, &loc)?;
let b = stack_pop(&mut stack, &loc)?;
stack.push(b / a);
stack.push(b % a);
}
InstructionType::Mul => {
let a = stack_pop(&mut stack, &loc)?;
let b = stack_pop(&mut stack, &loc)?;
stack.push(b * a);
}
InstructionType::Drop => {
stack.pop();
},
InstructionType::Dup => {
let a = stack_pop(&mut stack, &loc)?;
stack.push(a);
stack.push(a);
},
InstructionType::Rot => {
let a = stack_pop(&mut stack, &loc)?;
let b = stack_pop(&mut stack, &loc)?;
let c = stack_pop(&mut stack, &loc)?;
stack.push(b);
stack.push(a);
stack.push(c);
}
InstructionType::Swap => {
let a = stack_pop(&mut stack, &loc)?;
let b = stack_pop(&mut stack, &loc)?;
stack.push(a);
stack.push(b);
}
InstructionType::Over => {
let a = stack_pop(&mut stack, &loc)?;
let b = stack_pop(&mut stack, &loc)?;
stack.push(b);
stack.push(a);
stack.push(b);
}
_ => {
lerror!(&token.loc, "Unsupported precompiler instruction {:?}", i);
dbg!(tokens);
bail!("");
}
}
}
OpType::Keyword(_) => {
lerror!(&token.loc, "Unsupported precompiler keyword {:?}", token.typ);
dbg!(tokens);
bail!("");
}
OpType::Internal(t) => panic!("{t:?}"),
}
}
Ok(stack)
}

View File

@ -1,754 +0,0 @@
use std::collections::HashMap;
use std::path::{PathBuf, Path};
use anyhow::{Result, bail};
use crate::definitions::*;
use crate::lexer::lex;
use crate::precompiler::precompile;
use crate::{lerror, Args, warn, linfo, parser};
use crate::parser::lookup_word;
#[derive(Debug, Clone)]
pub struct Preprocessor<'a> {
pub program: Program,
in_function: Option<String>,
args: &'a Args,
f_inline: bool,
f_export: bool,
}
impl<'a> Preprocessor<'a> {
pub fn new(prog: Vec<Operator>, args: &'a Args) -> Self {
Self {
args,
program: Program {
ops: prog,
functions: HashMap::new(),
memories: HashMap::new(),
constants: HashMap::new(),
struct_defs: HashMap::new(),
struct_allocs: HashMap::new()
},
in_function: None,
f_inline: false,
f_export: false,
}
}
pub fn preprocess(&mut self) -> Result<&mut Preprocessor<'a>>{
// println!("pre: has do tokens: {:?}", self.program.iter().map(|t| if t.typ == OpType::Keyword(KeywordType::Do) {Some(t)} else {None} ).collect::<Vec<Option<&Operator>>>());
let mut program: Vec<Operator> = Vec::new();
let mut rtokens = self.program.ops.clone();
rtokens.reverse();
while !rtokens.is_empty() {
let mut op = rtokens.pop().unwrap();
// println!("{token:?}");
let op_type = op.typ.clone();
match op_type {
OpType::Keyword(KeywordType::Include) => self.handle_include(&mut rtokens, &mut op)?,
OpType::Keyword(KeywordType::Memory) => self.handle_memory(&mut rtokens, &mut op, &mut program)?,
OpType::Keyword(KeywordType::Function) => self.handle_function(&mut rtokens, &mut op, &mut program)?,
OpType::Keyword(KeywordType::Constant) => self.handle_constant(&mut rtokens, &mut op, &mut program)?,
OpType::Keyword(KeywordType::Struct) => self.handle_struct(&mut rtokens, &mut op, &mut program)?,
OpType::Keyword(KeywordType::Inline) => {
if self.f_export {
lerror!(&op.loc, "Function is already marked as exported, function cannot be inline and exported at the same time");
bail!("");
} else if self.f_inline {
lerror!(&op.loc, "Function is already marked as inline, remove this inline Keyword");
bail!("");
} else {
self.f_inline = true;
}
}
OpType::Keyword(KeywordType::Export) => {
if !crate::config::ENABLE_EXPORTED_FUNCTIONS {
lerror!(&op.loc, "Experimental feature Exported functions not enabled");
bail!("");
}
if self.f_inline {
lerror!(&op.loc, "Function is already marked as inline, function cannot be inline and exported at the same time");
bail!("");
} else if self.f_export {
lerror!(&op.loc, "Function is already marked as extern, remove this extern Keyword");
bail!("");
} else {
self.f_export = true;
}
}
_ => {
program.push(op);
}
}
}
self.program.ops = program;
// println!("has do tokens: {:?}", self.program.iter().map(|t| if t.typ == OpType::Keyword(KeywordType::Do) {Some(t)} else {None} ).collect::<Vec<Option<&Operator>>>());
//* Feel free to fix this horrifying shit
//* i wanna kms
let mut times = 0;
// dbg!(program.clone());
while self.program.ops.iter().map(|f| {
if f.tok_typ == TokenType::Word {
match f.typ {
OpType::Instruction(InstructionType::FnCall) |
OpType::Instruction(InstructionType::MemUse) |
OpType::Instruction(InstructionType::StructUse) |
OpType::Keyword(KeywordType::FunctionDef) |
OpType::Keyword(KeywordType::FunctionDefExported)|
OpType::Keyword(KeywordType::ConstantDef) |
OpType::Internal(InternalType::StructAlloc{..}) |
OpType::Instruction(InstructionType::ConstUse) => OpType::Instruction(InstructionType::PushInt),
_ => {
lookup_word(&f.text, &f.loc)
}
}
} else {
OpType::Instruction(InstructionType::PushInt) // i hate myself, this is a randomly picked optype so its happy and works
}
}).collect::<Vec<OpType>>().contains(&OpType::Instruction(InstructionType::None)){
if times >= 50 {
warn!("File import depth maxed out, if the program crashes try reducing the import depth, good luck youll need it");
break
}
self.expand()?;
times += 1;
}
Ok(self)
}
fn handle_include(&mut self, rtokens: &mut Vec<Operator>, op: &mut Operator) -> Result<()> {
if rtokens.is_empty() {
lerror!(&op.loc, "Include path not found, expected {} but found nothing", TokenType::String.human());
bail!("");
}
let include_path = rtokens.pop().unwrap();
if include_path.tok_typ != TokenType::String {
lerror!(&include_path.loc, "Bad include path, expected {} but found {}", TokenType::String.human(), include_path.typ.human());
bail!("");
}
let mut in_paths = self.args.include.clone();
in_paths.append(&mut crate::DEFAULT_INCLUDES.to_vec().clone().iter().map(|f| (*f).to_string()).collect::<Vec<String>>());
let mut include_code = String::new();
let mut pth = PathBuf::new();
if include_path.text.chars().next().unwrap() == '.' {
let p = Path::new(include_path.loc.0.as_str());
let p = p.parent().unwrap();
let p = p.join(&include_path.text);
pth = p.clone();
include_code = std::fs::read_to_string(p)?;
} else {
for path in in_paths {
let p = PathBuf::from(path);
let p = p.join(&include_path.text);
pth = p.clone();
if p.exists() {
include_code = std::fs::read_to_string(p)?;
break;
}
}
}
if include_code.is_empty() {
lerror!(&include_path.loc, "Include file in path '{}' was not found or is empty", include_path.text);
bail!("");
}
let a = pth.to_str().unwrap().to_string();
let code = lex(&include_code, a.as_str(), self.args);
let mut p = parser::Parser::new(code, self.args, Some(self.clone()));
let mut code = p.parse()?;
self.set_constants(p.preprocessor.get_constants());
self.set_functions(p.preprocessor.get_functions());
self.set_memories(p.preprocessor.get_memories());
code.ops.reverse();
rtokens.append(&mut code.ops);
Ok(())
}
fn handle_memory(&mut self, rtokens: &mut Vec<Operator>, op: &mut Operator, program: &mut Vec<Operator>) -> Result<()> {
if rtokens.is_empty() {
lerror!(&op.loc, "Memory name not found, expected {} but found nothing", TokenType::String.human());
bail!("");
}
let name = rtokens.pop().unwrap();
self.is_word_available(&name, KeywordType::Memory)?;
let mut code: Vec<Operator> = Vec::new();
let mut depth = 0;
while !rtokens.is_empty() {
let t = rtokens.pop().unwrap();
let typ = t.typ.clone();
if typ == OpType::Keyword(KeywordType::End) && depth == 0 {
break;
} else if typ == OpType::Keyword(KeywordType::End) && depth != 0 {
depth -= 1;
code.push(t);
} else if typ == OpType::Keyword(KeywordType::If) || typ == OpType::Keyword(KeywordType::Do) {
code.push(t);
depth += 1;
} else {
code.push(t);
}
}
let res = precompile(&code)?;
if res.len() != 1 {
lerror!(&op.loc, "Expected 1 number, got {:?}", res);
bail!("");
}
op.value = res[0];
op.addr = Some(self.program.memories.len());
program.push(op.clone());
self.program.memories.insert(name.text, Memory { loc: op.loc.clone(), id: self.program.memories.len() });
Ok(())
}
fn handle_function(&mut self, rtokens: &mut Vec<Operator>, op: &mut Operator, program: &mut Vec<Operator>) -> Result<()> {
if rtokens.is_empty() {
lerror!(&op.loc, "Function name not found, expected {} but found nothing", TokenType::Word.human());
bail!("");
}
let mut name = rtokens.pop().unwrap();
if let '0'..='9' = name.text.chars().next().unwrap() {
lerror!(&name.loc, "Function name starts with a number which is not allowed");
bail!("");
}
// let mut should_warn = false;
for c in name.text.clone().chars() {
match c {
'a'..='z' |
'A'..='Z' |
'0'..='9' |
'-' | '_' => (),
'(' | ')' => {
name.text = name.text.clone().replace('(', "__OP_PAREN__").replace(')', "__CL_PAREN__");
}
_ => {
lerror!(&name.loc, "Function name contains '{c}', which is unsupported");
bail!("");
}
}
}
// if should_warn {
//TODO: add -W option in cli args to enable more warnings
//lwarn!(&function_name.loc, "Function name contains '(' or ')', this character is not supported but will be replaced with '__OP_PAREN__' or '__CL_PAREN__' respectively ");
// }
self.is_word_available(&name, KeywordType::Function)?;
if self.f_inline {
self.f_inline = false;
let mut prog: Vec<Operator> = Vec::new();
let mut depth = -1;
while !rtokens.is_empty() {
let op = rtokens.pop().unwrap();
match op.typ.clone() {
OpType::Instruction(i) => {
match i {
InstructionType::TypeAny |
InstructionType::TypeBool |
InstructionType::TypeInt |
InstructionType::TypePtr |
InstructionType::With |
InstructionType::Returns |
InstructionType::TypeVoid => {
if depth >= 0 {
prog.push(op);
}
},
_ => prog.push(op)
}
}
OpType::Keyword(k) => {
match k {
KeywordType::Inline |
KeywordType::Include => {
todo!("make error")
},
KeywordType::FunctionThen => {
if depth >= 0 {
prog.push(op);
}
depth += 1;
},
KeywordType::FunctionDone => {
if depth == 0 {
break;
}
depth -= 1;
},
_ => prog.push(op)
}
}
_ => prog.push(op)
}
}
let mut pre = self.clone();
pre.program.ops = prog;
if name.text.chars().next().unwrap() == '.' {
pre.in_function = Some(name.text[1..].to_string());
}
pre.preprocess()?;
prog = pre.get_ops();
self.program.functions.insert(name.text.clone(), Function{
loc: name.loc.clone(),
name: name.text.clone(),
inline: true,
tokens: Some(prog)
});
} else if self.f_export {
self.f_export = false;
self.program.functions.insert(name.text.clone(), Function{
loc: name.loc.clone(),
name: name.text.clone(),
inline: false,
tokens: None
});
let mut a: Vec<Operator> = Vec::new();
let mut fn_def = op.clone();
a.push(rtokens.pop().unwrap());
let mut ret = false;
while !rtokens.is_empty() {
let op = rtokens.pop().unwrap();
// println!("{:?}",op);
a.push(op.clone());
if op.typ == OpType::Instruction(InstructionType::Returns) {
ret = true;
}
if op.typ == OpType::Keyword(KeywordType::FunctionThen) {
break;
}
if op.typ == OpType::Instruction(InstructionType::TypeBool) ||
op.typ == OpType::Instruction(InstructionType::TypeInt) ||
op.typ == OpType::Instruction(InstructionType::TypePtr) {
if ret {
fn_def.types.1 += 1;
} else {
fn_def.types.0 += 1;
}
}
}
fn_def.typ = OpType::Keyword(KeywordType::FunctionDefExported);
fn_def.text = name.text;
// fn_def.set_types(args, rets);
// println!("{:?}", fn_def.types);
program.push(fn_def);
program.append(&mut a);
} else {
self.program.functions.insert(name.text.clone(), Function{
loc: name.loc.clone(),
name: name.text.clone(),
inline: false,
tokens: None
});
let mut fn_def = op.clone();
fn_def.typ = OpType::Keyword(KeywordType::FunctionDef);
fn_def.text = name.text;
// println!("{:?}", token);
program.push(fn_def);
}
Ok(())
}
fn handle_constant(&mut self, rtokens: &mut Vec<Operator>, op: &mut Operator, program: &mut Vec<Operator>) -> Result<()> {
let Some(mut name) = rtokens.pop() else {
lerror!(&op.loc, "Constant name not found, expected {} but found nothing", TokenType::Word.human());
bail!("");
};
if let '0'..='9' | '.' = name.text.chars().next().unwrap() {
lerror!(&name.loc, "Constant name starts with a number or dot which is not allowed");
bail!("");
}
for c in name.text.clone().chars() {
match c {
'a'..='z' |
'A'..='Z' |
'0'..='9' |
'-' | '_' => (),
'(' | ')' => {
// should_warn = true;
name.text = name.text.clone().replace('(', "__OP_PAREN__").replace(')', "__CL_PAREN__");
}
_ => {
lerror!(&name.loc, "Constant name contains '{c}', which is unsupported");
bail!("");
}
}
}
// if should_warn {
//TODO: add -W option in cli args to enable more warnings
//lwarn!(&name.loc, "Constant name contains '(' or ')', this character is not supported but will be replaced with '__OP_PAREN__' or '__CL_PAREN__' respectively ");
// }
self.is_word_available(&name, KeywordType::Constant)?;
self.program.constants.insert(name.text.clone(), Constant{
loc: name.loc.clone(),
name: name.text.clone(),
});
// println!("{:?}", self.program.constants);
let mut const_def = op.clone();
const_def.typ = OpType::Keyword(KeywordType::ConstantDef);
const_def.text = name.text;
let item = rtokens.pop().unwrap();
if item.tok_typ == TokenType::Int {
const_def.value = item.value;
} else {
lerror!(&op.loc, "For now only {:?} is allowed in constants", TokenType::Int);
bail!("");
}
let posibly_end = rtokens.pop();
// println!("end: {posibly_end:?}");
if posibly_end.is_none() || posibly_end.unwrap().typ != OpType::Keyword(KeywordType::End) {
lerror!(&op.loc, "Constant was not closed with an 'end' instruction, expected 'end' but found nothing");
bail!("");
}
// token.value =
program.push(const_def);
Ok(())
}
fn handle_struct(&mut self, rtokens: &mut Vec<Operator>, op: &mut Operator, program: &mut Vec<Operator>) -> Result<()> {
let Some(name) = rtokens.pop() else {
lerror!(&op.loc, "Struct name not found, expected {} but found nothing", TokenType::Word.human());
bail!("");
};
if let '0'..='9' | '.' = name.text.chars().next().unwrap() {
lerror!(&name.loc, "Struct name starts with a number or dot which is not allowed");
bail!("");
}
self.is_word_available(&name, KeywordType::Struct)?;
if let Some(kw_do) = rtokens.pop() {
if kw_do.typ != OpType::Keyword(KeywordType::Do) {
lerror!(&name.loc, "Expected keyword 'do' but found {:?}", kw_do.typ);
bail!("");
}
} else {
lerror!(&name.loc, "Expected keyword 'do' but found nothing");
bail!("");
}
let mut structure = StructDef{
loc: name.loc,
name: name.text,
fields: vec![],
};
loop {
let fl_name = rtokens.pop().unwrap();
if fl_name.typ == OpType::Keyword(KeywordType::End) {
break;
}
if let '0'..='9' = fl_name.text.chars().next().unwrap() {
lerror!(&fl_name.loc, "Struct field name starts with a number which is not allowed");
bail!("");
}
// let mut should_warn = false;
for c in fl_name.text.clone().chars() {
match c {
'a'..='z' |
'A'..='Z' |
'0'..='9' |
'_' => (),
_ => {
lerror!(&fl_name.loc, "Struct field name contains '{c}', which is unsupported");
bail!("");
}
}
}
if let Some(arrow) = rtokens.pop() {
if arrow.typ != OpType::Internal(InternalType::Arrow) {
lerror!(&arrow.loc, "Expected '->' but found {:?}", arrow.typ);
bail!("");
}
} else {
lerror!(&fl_name.loc, "Expected '->' but found nothing");
bail!("");
}
let Some(typ) = rtokens.pop() else {
lerror!(&fl_name.loc, "Expected a type but found nothing");
bail!("");
};
let Ok(typ) = Types::from_string(&typ.text) else {
lerror!(&typ.loc, "Expected a type but found {:?}", typ.text);
bail!("");
};
structure.fields.push((fl_name.text, typ));
}
self.program.struct_defs.insert(structure.name.clone(), structure.clone());
if let Some(def_name) = rtokens.pop() {
if def_name.typ == OpType::Instruction(InstructionType::None){
let mut def = def_name.clone();
def.typ = OpType::Internal(InternalType::StructAlloc {
name: structure.name.clone()
});
self.program.struct_allocs.insert(def_name.text, structure.name);
program.push(def);
} else {
rtokens.push(def_name);
}
}
Ok(())
}
pub fn expand(&mut self) -> Result<()> {
let mut program: Vec<Operator> = Vec::new();
// println!("{:?}", self.program.functions);
let mut rtokens = self.program.ops.clone();
rtokens.reverse();
'main_loop: while !rtokens.is_empty() {
let op = rtokens.pop().unwrap();
let op_type = op.typ.clone();
if op.tok_typ == TokenType::Word {
match op_type {
OpType::Instruction(InstructionType::None) => {
let m = self.program.functions.get(&op.text.clone().replace('(', "__OP_PAREN__").replace(')', "__CL_PAREN__"));
let mem = self.program.memories.get(&op.text);
let cons = self.program.constants.get(&op.text.clone().replace('(', "__OP_PAREN__").replace(')', "__CL_PAREN__"));
if let Some(m) = m {
if m.inline {
program.append(&mut m.tokens.clone().unwrap());
} else {
let mut t = op.clone();
t.typ = OpType::Instruction(InstructionType::FnCall);
t.text = m.name.clone();
program.push(t.clone());
}
// println!("##### {:?}", t);
} else if let Some(mem) = mem {
let mut t = op.clone();
t.addr = Some(mem.id);
t.typ = OpType::Instruction(InstructionType::MemUse);
program.push(t);
} else if let Some(cons) = cons {
let mut t = op.clone();
t.text = cons.name.clone();
t.typ = OpType::Instruction(InstructionType::ConstUse);
program.push(t);
} else {
let mut t = op.clone();
let parts = op.text.split('.').map(|f| f.to_string()).collect::<Vec<String>>();
let alc = self.program.struct_allocs.get(&parts[0]);
if let Some(alc) = alc {
if let Some(def) = self.program.struct_defs.get(alc) {
// if def.fields.iter().for_each(|f| f.0 == parts[1])
println!("{:?}", def.fields);
if def.fields.iter().find(|f| f.0 == parts[1]).is_some() || parts.len() < 2{
t.typ = OpType::Instruction(InstructionType::StructUse);
program.push(t);
continue 'main_loop;
}
}
}
lerror!(&op.loc, "Preprocess: Unknown word '{}'", op.text.clone());
bail!("");
}
}
_ => {
program.push(op.clone());
}
}
} else {
program.push(op.clone());
}
// if op.typ == OpType::Keyword(KeywordType::Do) {
// println!("expand: {:?}", op);
// program.push(op.clone());
// }
}
// println!("expand: has do tokens: {:?}", program.iter().map(|t| if t.typ == OpType::Keyword(KeywordType::Do) {Some(t)} else {None} ).collect::<Vec<Option<&Operator>>>());
self.program.ops = program;
// println!("{:#?}", self.program);
// println!("{:?}", self.program.last().unwrap());
Ok(())
}
pub fn get_ops(&mut self) -> Vec<Operator> {
self.program.ops.clone()
}
pub fn is_word_available(&self, word: &Operator, typ: KeywordType) -> Result<bool> {
match typ {
KeywordType::Memory |
KeywordType::Constant |
KeywordType::Struct |
KeywordType::Function => (),
_ => panic!()
}
if word.tok_typ != TokenType::Word {
lerror!(&word.loc, "Bad {typ:?}, expected {} but found {}", TokenType::Word.human(), word.typ.human());
if crate::DEV_MODE {println!("{word:?}")}
bail!("");
}
let w = lookup_word(&word.text, &word.loc);
if w != OpType::Instruction(InstructionType::None) {
lerror!(&word.loc, "Bad {typ:?}, {typ:?} definition cannot be builtin word, got {:?}", word.text);
if crate::DEV_MODE {println!("{word:?}")}
bail!("");
}
let m = self.program.memories.get(&word.text);
if let Some(m) = m {
if typ == KeywordType::Memory {
lerror!(&word.loc, "Memories cannot be redefined, got {}", word.text);
linfo!(&m.loc, "first definition here");
if crate::DEV_MODE {println!("{word:?}")}
bail!("");
}
lerror!(&word.loc, "{typ:?} cannot replace memory, got {}", word.text);
linfo!(&m.loc, "first definition here");
if crate::DEV_MODE {println!("{word:?}")}
bail!("");
}
let f = self.program.functions.get(&word.text);
if let Some(f) = f {
if typ == KeywordType::Function {
lerror!(&word.loc, "Functions cannot be redefined, got {}", word.text);
linfo!(&f.loc, "first definition here");
if crate::DEV_MODE {println!("{word:?}")}
bail!("");
}
lerror!(&word.loc, "{typ:?} cannot replace function, got {}", word.text);
linfo!(&f.loc, "first definition here");
if crate::DEV_MODE {println!("{word:?}")}
bail!("");
}
let c = self.program.constants.get(&word.text);
if let Some(c) = c {
if typ == KeywordType::Constant {
lerror!(&word.loc, "Constants cannot be redefined, got {}", word.text);
linfo!(&c.loc, "first definition here");
if crate::DEV_MODE {println!("{word:?}")}
bail!("");
}
lerror!(&word.loc, "{typ:?} cannot replace constant, got {}", word.text);
linfo!(&c.loc, "first definition here");
if crate::DEV_MODE {println!("{word:?}")}
bail!("");
}
let s = self.program.struct_defs.get(&word.text);
if let Some(s) = s {
if typ == KeywordType::Constant {
lerror!(&word.loc, "Structs cannot be redefined, got {}", word.text);
linfo!(&s.loc, "first definition here");
if crate::DEV_MODE {println!("{word:?}")}
bail!("");
}
lerror!(&word.loc, "{typ:?} cannot replace struct, got {}", word.text);
linfo!(&s.loc, "first definition here");
if crate::DEV_MODE {println!("{word:?}")}
bail!("");
}
Ok(true)
}
pub fn set_functions(&mut self, f: Functions) {
self.program.functions = f;
}
pub fn set_constants(&mut self, f: Constants) {
self.program.constants = f;
}
pub fn set_memories(&mut self, f: Memories) {
self.program.memories = f;
}
pub fn get_functions(&mut self) -> Functions {
self.program.functions.clone()
}
pub fn get_constants(&mut self) -> Constants {
self.program.constants.clone()
}
pub fn get_memories(&mut self) -> Memories{
self.program.memories.clone()
}
pub fn get_program(&mut self) -> Program {
self.program.clone()
}
}

View File

@ -1,408 +0,0 @@
use std::collections::HashMap;
use crate::{definitions::{Operator, Types, OpType, KeywordType, InstructionType, Loc}, Args, lerror, warn};
use anyhow::{Result, bail};
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct Function {
loc: Loc,
args: Vec<Types>,
returns: Vec<Types>,
}
#[derive(Debug, Clone)]
pub struct Constant {
#[allow(dead_code)]
loc: Loc,
types: Vec<Types>,
}
impl Function {
#[allow(dead_code)]
pub fn default() -> Self {
Self {
args: Vec::new(),
returns: Vec::new(),
loc: (String::new(), 0, 0)
}
}
}
type Functions = HashMap<String, Function>;
type Constants = HashMap<String, Constant>;
pub fn typecheck(ops: Vec<Operator>, args: &Args, init_types: Option<Vec<Types>>, funcs: HashMap<String, Function>, consts: HashMap<String, Constant>) -> Result<(Vec<Types>, Functions, Constants)>{
if args.unsaf {
if !args.quiet {
warn!("Unsafe mode enabled, disabling typechecker, goodluck");
}
return Ok((Vec::new(), HashMap::new(), HashMap::new()));
}
let mut functions: HashMap<String, Function> = funcs;
let mut constants: HashMap<String, Constant> = consts;
// let mut in_function: (String, Function, Loc) = (String::new(), Function::default(), (String::new(), 0, 0));
let mut stack: Vec<Types> = if let Some(i) = init_types {i} else {Vec::new()};
let mut stack_snapshots: Vec<Vec<Types>> = Vec::new();
let mut rtokens = ops;
rtokens.reverse();
// println!("{:#?}", ops);
while !rtokens.is_empty() {
let op = rtokens.pop().unwrap();
// println!("{:?}", stack.clone());
// println!("{:?}", op);
// println!("{}", ops.len());
match op.typ.clone() {
OpType::Keyword(keyword) => {
match keyword {
KeywordType::If |
KeywordType::Do => {
stack_pop(&mut stack, &op, &[Types::Bool])?;
},
KeywordType::FunctionDefExported |
KeywordType::FunctionDef => {
let name = op.text.clone();
// println!("{:?}", name);
if let Some(p) = rtokens.pop() {
if p.typ != OpType::Instruction(InstructionType::With){
lerror!(&op.loc, "Expected {:?}, got {:?}", OpType::Instruction(InstructionType::With), p.typ);
bail!("");
}
} else {
lerror!(&op.loc, "Expected {:?}, got nothing", OpType::Instruction(InstructionType::With));
bail!("");
}
let mut p = rtokens.pop();
let mut func = Function {
args: Vec::new(),
returns: Vec::new(),
loc: op.loc
};
let mut return_args = false;
while p.as_ref().is_some() {
let op = p.as_ref().unwrap();
if op.typ == OpType::Instruction(InstructionType::TypeBool) ||
op.typ == OpType::Instruction(InstructionType::TypeInt) ||
op.typ == OpType::Instruction(InstructionType::TypePtr) ||
op.typ == OpType::Instruction(InstructionType::TypeAny) ||
op.typ == OpType::Instruction(InstructionType::TypeVoid) {
let t = if op.typ == OpType::Instruction(InstructionType::TypeInt) {
Types::U64
} else if op.typ == OpType::Instruction(InstructionType::TypeBool) {
Types::Bool
} else if op.typ == OpType::Instruction(InstructionType::TypePtr) {
Types::Ptr
} else if op.typ == OpType::Instruction(InstructionType::TypeVoid) {
if return_args {
func.returns = vec![Types::Void];
} else {
func.args = vec![Types::Void];
return_args = true;
continue;
}
Types::Void
} else if op.typ == OpType::Instruction(InstructionType::TypeAny) {
Types::Any
} else {
panic!()
};
if return_args {
func.returns.push(t);
} else {
func.args.push(t);
}
}
if op.typ == OpType::Instruction(InstructionType::Returns) {
return_args = true;
}
if op.typ == OpType::Keyword(KeywordType::FunctionThen) {
break;
}
p = rtokens.pop();
};
let mut code: Vec<Operator> = Vec::new();
while !rtokens.is_empty() {
let op = rtokens.pop().unwrap();
if op.typ == OpType::Keyword(KeywordType::FunctionDone) {
break;
}
code.push(op);
}
let ts = if func.args.clone() == vec![Types::Void] {
Vec::new()
} else {
func.args.clone()
};
if ts.contains(&Types::Void) {
continue;
}
functions.insert(name.clone(), func.clone());
let (ret_typs, _, _) = typecheck(code, args, Some(ts.clone()), functions.clone(), constants.clone())?;
if ret_typs != func.returns && !func.returns.contains(&Types::Void){
lerror!(&func.loc, "Expected {:?}, but got {:?}", func.returns, ret_typs);
bail!("");
}
if !func.args.contains(&Types::Void) {
stack.append(&mut func.args);
}
stack_snapshots.push(stack.clone());
}
KeywordType::Else |
KeywordType::End |
KeywordType::While |
KeywordType::Include |
KeywordType::Constant |
KeywordType::Memory => (),
KeywordType::ConstantDef => {
// println!("defined constant");
constants.insert(op.text, Constant { loc: op.loc.clone(), types: vec![Types::U64] });
},
KeywordType::FunctionThen |
KeywordType::FunctionDone |
KeywordType::Inline |
KeywordType::Export |
KeywordType::Function => {
println!("{:?}", op);
unreachable!()
},
KeywordType::Struct => todo!(),
}
},
OpType::Instruction(instruction) => {
match instruction {
InstructionType::PushInt => {
stack.push(Types::U64);
},
InstructionType::PushStr => {
stack.push(Types::U64);
stack.push(Types::Ptr);
},
InstructionType::PushCStr => {
stack.push(Types::U64);
stack.push(Types::Ptr);
},
InstructionType::Drop => {
stack_pop(&mut stack, &op, &[Types::Any])?;
},
InstructionType::Print => {
stack_pop(&mut stack, &op, &[Types::U64])?;
},
InstructionType::Dup => {
let a = stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(a);
},
InstructionType::Rot => {
let a = stack_pop(&mut stack, &op, &[Types::Any])?;
let b = stack_pop(&mut stack, &op, &[Types::Any])?;
let c = stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(b);
stack.push(a);
stack.push(c);
},
InstructionType::Over => {
let a = stack_pop(&mut stack, &op, &[Types::Any])?;
let b = stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(b.clone());
stack.push(a);
stack.push(b);
},
InstructionType::Swap => {
let a = stack_pop(&mut stack, &op, &[Types::Any])?;
let b = stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(a);
stack.push(b);
},
InstructionType::Minus |
InstructionType::Plus |
InstructionType::Band |
InstructionType::Bor |
InstructionType::Shr |
InstructionType::Shl |
InstructionType::Mul => {
stack_pop(&mut stack, &op, &[Types::U64])?;
stack_pop(&mut stack, &op, &[Types::U64])?;
stack.push(Types::U64);
},
InstructionType::Equals |
InstructionType::Gt |
InstructionType::Lt |
InstructionType::Ge |
InstructionType::Le |
InstructionType::NotEquals => {
stack_pop(&mut stack, &op, &[Types::U64])?;
stack_pop(&mut stack, &op, &[Types::U64])?;
stack.push(Types::Bool);
},
InstructionType::DivMod => {
stack_pop(&mut stack, &op, &[Types::U64])?;
stack_pop(&mut stack, &op, &[Types::U64])?;
stack.push(Types::U64);
stack.push(Types::U64);
},
InstructionType::Read8 |
InstructionType::Read32 |
InstructionType::Read64 => {
stack_pop(&mut stack, &op, &[Types::Ptr])?;
stack.push(Types::U64);
},
InstructionType::Write8 |
InstructionType::Write32 |
InstructionType::Write64 => {
stack_pop(&mut stack, &op, &[Types::U64])?;
stack_pop(&mut stack, &op, &[Types::Ptr])?;
},
InstructionType::Syscall0 => {
stack_pop(&mut stack, &op, &[Types::U64])?;
stack.push(Types::U64);
},
InstructionType::Syscall1 => {
stack_pop(&mut stack, &op, &[Types::U64])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(Types::U64);
},
InstructionType::Syscall2 => {
stack_pop(&mut stack, &op, &[Types::U64])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(Types::U64);
},
InstructionType::Syscall3 => {
stack_pop(&mut stack, &op, &[Types::U64])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(Types::U64);
},
InstructionType::Syscall4 => {
stack_pop(&mut stack, &op, &[Types::U64])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(Types::U64);
},
InstructionType::Syscall5 => {
stack_pop(&mut stack, &op, &[Types::U64])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(Types::U64);
},
InstructionType::Syscall6 => {
stack_pop(&mut stack, &op, &[Types::U64])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(Types::U64);
},
InstructionType::CastBool => {
stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(Types::Bool);
},
InstructionType::CastPtr => {
stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(Types::Ptr);
},
InstructionType::CastInt => {
stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(Types::U64);
},
InstructionType::CastVoid => {
stack_pop(&mut stack, &op, &[Types::Any])?;
stack.push(Types::Any);
},
InstructionType::MemUse => {
stack.push(Types::Ptr);
},
InstructionType::FnCall => {
stack_snapshots.push(stack.clone());
let f = if let Some(f) = functions.get(&op.text) {f} else {
lerror!(&op.loc, "Could not find function {}", op.text);
bail!("");
};
// in_function = (op.text.clone(), f.clone(), op.loc.clone());
let mut s = stack.clone();
let mut a = f.args.clone();
// s.reverse();
a.reverse();
for t in a{
if let Some(s2) = s.pop(){
if t != s2 {
lerror!(&op.loc, "Expected {:?}, but got {:?}", t, s2);
bail!("");
}
} else {
lerror!(&op.loc, "Expected {:?}, but got nothing", t);
bail!("");
}
}
}
InstructionType::Return |
InstructionType::None |
InstructionType::TypeBool |
InstructionType::TypePtr |
InstructionType::TypeInt |
InstructionType::TypeVoid |
InstructionType::TypeAny |
InstructionType::Returns |
InstructionType::With => (),
InstructionType::ConstUse => {
// println!("{constants:?}");
let mut c = constants.get(&op.text).unwrap().clone();
stack.append(&mut c.types);
},
InstructionType::StructUse => todo!(),
}
},
OpType::Internal(t) => panic!("{t:?}"),
}
}
Ok((stack, functions, constants))
}
fn stack_pop(v: &mut Vec<Types>, op: &Operator, t: &[Types]) -> Result<Types> {
if v.is_empty() {
lerror!(&op.loc, "Expected {:?}, but got nothing", t);
bail!("");
}
let r = v.pop().unwrap();
if !t.contains(&r) && t[0] != Types::Any {
lerror!(&op.loc, "Expected {:?}, but got {:?}", t, r);
bail!("");
}
Ok(r)
}

View File

@ -1,392 +0,0 @@
use std::collections::{HashMap, HashSet};
use eyre::bail;
#[derive(Debug, Clone, PartialEq)]
pub enum InstructionType {
// stack
PushInt,
PushStr,
PushCStr,
Drop,
Print,
Dup,
Rot, // a b c => b c a
Over, // a b => a b a
Swap, // a b => b a
// math
Minus,
Plus,
Equals,
Gt,
Lt,
Ge,
Le,
NotEquals,
Band, // &
Bor, // |
Shr, // >>
Shl, // <<
DivMod, // /
Mul,
// mem
Read8,
Write8,
Read32,
Write32,
Read64,
Write64,
// syscalls
Syscall0,
Syscall1,
Syscall2,
Syscall3,
Syscall4,
Syscall5,
Syscall6,
CastBool,
CastPtr,
CastInt,
CastVoid,
// typing
TypeBool,
TypePtr,
TypeInt,
TypeVoid,
// TypeStr,
TypeAny,
Returns,
With,
FnCall,
MemUse,
ConstUse,
Return,
None // Used for macros and any other non built in word definitions
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum KeywordType {
If,
Else,
End,
While,
Do,
Include,
Memory,
Constant,
ConstantDef,
Function,
FunctionDef,
FunctionDefExported,
FunctionThen,
FunctionDone,
Inline,
Export,
Struct,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum InternalType {
Arrow
}
#[derive(Debug, Clone, PartialEq)]
pub enum OpType {
Keyword(KeywordType),
Instruction(InstructionType),
Internal(InternalType)
}
#[derive(Debug, Clone)]
pub struct Operator{
pub typ: OpType,
pub tok_typ: TokenType,
pub value: usize,
pub text: String, //? only used for OpType::PushStr
pub addr: Option<usize>, //? only used for OpType::PushStr
pub jmp: usize,
pub loc: Loc,
pub types: (usize, usize)
}
impl Operator {
pub fn new(typ: OpType, tok_typ: TokenType, value: usize, text: String, file: String, row: usize, col: usize) -> Self {
Self {
typ,
value,
jmp: 0,
addr: None,
text,
loc: (file, row, col),
tok_typ,
types: (0, 0)
}
}
pub fn set_addr(&mut self, addr: usize) -> Self {
self.addr = Some(addr);
(*self).clone()
}
// pub fn set_types(&mut self, args: usize, rets: usize) -> Self {
// self.types = (args, rets);
// (*self).clone()
// }
}
impl OpType {
pub fn human(&self) -> String {
match (*self).clone() {
OpType::Instruction(instruction) => {
match instruction {
InstructionType::PushInt => "Number",
InstructionType::PushStr => "String",
InstructionType::PushCStr => "CString",
InstructionType::Print => "_dbg_print",
InstructionType::Dup => "dup",
InstructionType::Drop => "drop",
InstructionType::Rot => "rot",
InstructionType::Over => "over",
InstructionType::Swap => "swap",
InstructionType::Plus => "+",
InstructionType::Minus => "-",
InstructionType::Equals => "=",
InstructionType::Gt => ">",
InstructionType::Lt => "<",
InstructionType::NotEquals => "!=",
InstructionType::Le => "<=",
InstructionType::Ge => ">=",
InstructionType::Band => "band",
InstructionType::Bor => "bor",
InstructionType::Shr => "shr",
InstructionType::Shl => "shl",
InstructionType::DivMod => "divmod",
InstructionType::Mul => "*",
InstructionType::Read8 => "read8",
InstructionType::Write8 => "write8",
InstructionType::Read32 => "read32",
InstructionType::Write32 => "write32",
InstructionType::Read64 => "read64",
InstructionType::Write64 => "write64",
InstructionType::Syscall0 => "syscall0",
InstructionType::Syscall1 => "syscall1",
InstructionType::Syscall2 => "syscall2",
InstructionType::Syscall3 => "syscall3",
InstructionType::Syscall4 => "syscall4",
InstructionType::Syscall5 => "syscall5",
InstructionType::Syscall6 => "syscall6",
InstructionType::CastBool => "cast(bool",
InstructionType::CastPtr => "cast(ptr)",
InstructionType::CastInt => "cast(int)",
InstructionType::CastVoid => "cast(void)",
InstructionType::None => "None",
InstructionType::MemUse => "Memory use (internal)",
InstructionType::FnCall => "Function Call (Internal)",
InstructionType::ConstUse => "Constant Use (Internal)",
InstructionType::Return => "return",
InstructionType::TypeBool => "bool",
InstructionType::TypePtr => "ptr",
InstructionType::TypeInt => "int",
InstructionType::TypeVoid => "void",
InstructionType::Returns => "returns",
InstructionType::With => "with",
InstructionType::TypeAny => "any",
}
}
OpType::Keyword(keyword) => {
match keyword {
KeywordType::If => "if",
KeywordType::Else => "else",
KeywordType::End => "end",
KeywordType::While => "while",
KeywordType::Do => "do",
KeywordType::Include => "include",
KeywordType::Memory => "memory",
KeywordType::Function => "fn",
KeywordType::Constant => "const",
KeywordType::FunctionThen => "then",
KeywordType::FunctionDone => "done",
KeywordType::ConstantDef => "constant Definition (internal)",
KeywordType::FunctionDef => "function definition (internal)",
KeywordType::FunctionDefExported => "extern function definition (internal)",
KeywordType::Inline => "inline",
KeywordType::Export => "export",
KeywordType::Struct => "struct",
}
}
OpType::Internal(t) => panic!("{t:?}"),
}.to_string()
}
}
#[derive(Debug, Clone)]
pub struct Token {
pub file: String,
pub line: usize,
pub col: usize,
pub text: String,
pub typ: TokenType,
pub value: Option<usize>, //* only used for Memories
pub addr: Option<usize>, //* only used for Memories
pub op_typ: OpType //* only used for Memories
}
#[derive(Debug, Clone, PartialEq, Copy)]
pub enum TokenType {
Word,
Int,
String,
CString,
Char
}
impl Token {
pub fn loc(&self) -> Loc {
(
self.file.clone(),
self.line,
self.col
)
}
}
impl TokenType {
pub fn human(self) -> String {
match self {
TokenType::Word => "Word",
TokenType::Int => "Int",
TokenType::String => "String",
TokenType::CString => "CString",
TokenType::Char => "Char"
}.to_string()
}
}
pub type Loc = (String, usize, usize);
#[derive(Debug, PartialEq, Clone)]
pub enum Types {
Any,
Bool,
Ptr,
Void,
U8,
U16,
U32,
U64,
I8,
I16,
I32,
I64,
Custom{
size: u64 // in bytes
},
// todo: add signed numbers since we dont have them yet lol
}
impl Types {
pub fn get_size(&self) -> u64 {
match *self {
Types::Any => 0, // any cant be a known size
Types::Void => 0,
Types::Bool => 1,
Types::U8 |
Types::I8 => 1,
Types::U16 |
Types::I16 => 2,
Types::U32 |
Types::I32 => 4,
Types::Ptr |
Types::U64 |
Types::I64 => 8,
Types::Custom { size } => size,
}
}
}
impl TryInto<Types> for &str {
type Error = color_eyre::eyre::Error;
fn try_into(self) -> Result<Types, Self::Error> {
match self {
"Any" => Ok(Types::Any),
"Void" => Ok(Types::Void),
"Bool" => Ok(Types::Bool),
"U8" => Ok(Types::U8),
"I8" => Ok(Types::I8),
"U16" => Ok(Types::U16),
"I16" => Ok(Types::I16),
"U32" => Ok(Types::U32),
"I32" => Ok(Types::I32),
"Ptr" => Ok(Types::Ptr),
"U64" => Ok(Types::U64),
"I64" => Ok(Types::I64),
_ => bail!("Unknown type {self}")
}
}
}
impl TryInto<Types> for String {
type Error = color_eyre::eyre::Error;
fn try_into(self) -> Result<Types, Self::Error> {
self.into()
}
}
#[derive(Debug, Clone)]
pub struct Function {
pub loc: Loc,
pub name: String,
pub inline: bool,
pub tokens: Option<Vec<Operator>>
}
#[derive(Debug, Clone)]
pub struct Constant {
pub loc: Loc,
pub name: String
}
#[derive(Debug, Clone)]
pub struct Memory {
pub loc: Loc,
pub id: usize
}
#[derive(Debug, Clone)]
pub struct StructDef {
pub loc: Loc,
pub name: String,
pub fields: HashSet<(String, Types)>
}
pub type Functions = HashMap<String, Function>;
pub type Memories = HashMap<String, Memory>;
pub type Constants = HashMap<String, Constant>;
pub type StructDefs = HashMap<String, StructDef>;
#[derive(Debug, Clone)]
pub struct Program {
pub ops: Vec<Operator>,
pub functions: Functions,
pub memories: Memories,
pub constants: Constants,
pub struct_defs: StructDefs
}

215
src/types/ast/mod.rs Normal file
View File

@ -0,0 +1,215 @@
use std::collections::HashMap;
use super::{common::Loc, token::{Token, TypeType}};
//TODO: Implement missing stuff
#[derive(Debug, Clone)]
pub enum AstNode {
Int(Loc, usize),
Str(Loc, String),
CStr(Loc, String),
Char(Loc, char),
// ExternFnDef {
// loc: Loc,
// ident: String,
// arg_types: Vec<TokenType>,
// ret_type: TokenType,
// },
Function(Function),
Constant(Constant),
// ExternConstantDef{
// loc: Loc,
// ident: String,
// value: InstructionType
// },
// StructDef{
// loc: Loc,
// extrn: bool,
// ident: String,
// body: Vec<(String, usize)> // (field ident, size in bytes)
// },
StructDef(StructDef),
StructDispPush{
loc: Loc,
disp: usize,
ident: String,
},
// StructItemPush{
// loc: Loc,
// disp: usize,
// ident: String,
// },
If(If),
While(While),
Module(Module),
Memory(Memory),
MemUse(MemUse),
ConstUse(ConstUse),
FnCall(FnCall),
Block(Block),
Token(Token),
}
impl AstNode {
pub fn loc(&self) -> Loc {
match self {
AstNode::Function(f) => f.loc.clone(),
AstNode::Constant(c) => c.loc.clone(),
AstNode::If(t)=> t.loc.clone(),
AstNode::While(t)=> t.loc.clone(),
AstNode::Module(m) => m.loc.clone(),
AstNode::Memory(m) => m.loc.clone(),
AstNode::MemUse(t)=> t.loc.clone(),
AstNode::ConstUse(t)=> t.loc.clone(),
AstNode::FnCall(t)=> t.loc.clone(),
AstNode::Block(t)=> t.loc.clone(),
AstNode::Token(tok) => tok.loc.clone(),
AstNode::Int(loc, _) => loc.clone(),
AstNode::Str(loc, _) => loc.clone(),
AstNode::CStr(loc, _) => loc.clone(),
AstNode::Char(loc, _) => loc.clone(),
AstNode::StructDef(s) => s.loc.clone(),
AstNode::StructDispPush { loc, ..} => loc.clone(),
// AstNode::StructItemPush { loc, .. } => loc.clone(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct StructDef {
pub loc: Loc,
pub ident: String,
pub body: Vec<(String, usize, TypeType)>, // (field ident, size in bytes)
pub size: usize
}
#[derive(Debug, Clone)]
pub struct MemUse {
pub loc: Loc,
pub ident: String,
pub disp: Option<usize>
}
#[derive(Debug, Clone)]
pub struct ConstUse {
pub loc: Loc,
pub ident: String,
}
#[derive(Debug, Clone)]
pub struct FnCall {
pub loc: Loc,
pub ident: String,
}
#[derive(Debug, Clone)]
pub struct Block {
pub comment: String,
pub loc: Loc,
pub body: Vec<AstNode>
}
#[derive(Debug, Clone)]
pub struct While {
pub loc: Loc,
pub test: Vec<AstNode>,
pub body: Vec<AstNode>,
}
#[derive(Debug, Clone)]
pub struct If {
pub loc: Loc,
pub test: Vec<AstNode>,
pub body: Vec<AstNode>,
pub els: Box<AstNode>,
}
#[derive(Debug, Clone)]
pub struct Module {
pub loc: Loc,
pub path: Vec<String>,
pub ident: String,
pub body: Vec<AstNode>
}
#[derive(Debug, Clone)]
pub struct Function {
pub loc: Loc,
pub ident: String,
pub inline: bool,
pub extrn: bool,
pub export: bool,
pub arg_types: Vec<TypeType>,
pub ret_types: Vec<TypeType>,
pub body: Vec<AstNode>
}
#[derive(Debug, Clone)]
pub struct Constant {
pub loc: Loc,
pub ident: String,
pub value: Box<AstNode>
}
#[derive(Debug, Clone)]
pub struct Memory {
pub loc: Loc,
pub ident: String,
pub statc: bool,
pub size: MemSize // bytes
}
#[derive(Debug, Clone)]
pub struct Program {
pub ast: AstNode,
pub functions: HashMap<String, Function>,
pub constants: HashMap<String, Constant>,
pub memories: HashMap<String, Memory>,
pub struct_defs: HashMap<String, StructDef>,
}
#[derive(Debug, Clone)]
pub enum MemSize {
Size(usize),
Type(TypeType)
}
impl EscIdent for FnCall {
fn ident(&self) -> String {
self.ident.clone()
}
}
impl EscIdent for ConstUse {
fn ident(&self) -> String {
self.ident.clone()
}
}
impl EscIdent for MemUse {
fn ident(&self) -> String {
self.ident.clone()
}
}
impl EscIdent for Constant {
fn ident(&self) -> String {
self.ident.clone()
}
}
impl EscIdent for Memory {
fn ident(&self) -> String {
self.ident.clone()
}
}
impl EscIdent for Function {
fn ident(&self) -> String {
self.ident.clone()
}
}
pub trait EscIdent {
fn ident(&self) -> String;
fn get_ident_escaped(&self) -> String {
self.ident().replace("(", "_OPRN_")
.replace(")", "_CPRN_")
}
}

37
src/types/common.rs Normal file
View File

@ -0,0 +1,37 @@
use std::fmt::Display;
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Loc {
pub file: String,
pub line: usize,
pub col: usize
}
impl Loc {
pub fn new<T: Into<String>>(f: T, line: usize, col: usize) -> Self {
Self {
file: f.into(),
line,
col,
}
}
pub fn inc_line(&mut self) {
self.line += 1;
self.col = 0;
}
pub fn inc_col(&mut self) {
self.col += 1;
}
}
impl Display for Loc {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}:{}", self.file, self.line, self.col)?;
Ok(())
}
}

View File

@ -1,29 +0,0 @@
pub struct Loc {
pub ln: usize,
pub col: usize,
pub file: String
}
impl Loc {
pub fn new(file: String, ln: usize, col: usize) -> Self {
Self {
ln,
col,
file,
}
}
}
impl ToString for Loc {
fn to_string(&self) -> String {
format!("{}:{}:{}", self.file, self.ln, self.col)
}
}
impl Into<String> for Loc {
fn into(self) -> String {
self.to_string()
}
}

View File

@ -1,4 +0,0 @@
mod loc;
pub use loc::*;

View File

@ -1,4 +1,3 @@
pub mod common;
pub mod token;
pub mod ast;

View File

@ -1,65 +0,0 @@
use super::common::Loc;
struct Token {
loc: Loc,
typ: TokenType,
val: TokenValue
}
pub enum TokenValue {
Int(usize),
Str(String)
}
pub enum TokenType {
DbgPrint,
Keyword(TokenKeyword),
Syscall(u8),
//? Literal
PushInt,
PushStr,
//? Stack manipulation
Dup,
Rot, // a b c => b c a
Over, // a b => a b a
Swap, // a b => b a
//? Math
Plus,
Minus,
Mul,
Div,
Mod,
//? Logical
And,
Or,
Eq,
Gt,
Lt,
Ge,
Le,
Ne,
//? Bitwise
Shr,
Shl,
Bor,
Band,
}
pub enum TokenKeyword {
Function,
If,
Else,
End,
Done,
Macro,
While,
Do
}

162
src/types/token/mod.rs Normal file
View File

@ -0,0 +1,162 @@
#![allow(dead_code)]
use super::{ast::StructDef, common::Loc};
#[derive(Debug, Clone, PartialEq)]
pub enum InstructionType {
// stack
PushInt(usize),
PushStr(String),
PushCStr(String),
PushChar(char),
StructPath(Vec<String>), // foo::bar
StructItem(Vec<String>), // foo.bar
Drop,
Print,
Dup,
Rot, // a b c => b c a
Over, // a b => a b a
Swap, // a b => b a
// math
Minus,
Plus,
Equals,
Gt,
Lt,
Ge,
Le,
NotEquals,
Band, // &
Bor, // |
Shr, // >>
Shl, // <<
DivMod, // /
Mul,
// mem
Read8,
Write8,
Read32,
Write32,
Read64,
Write64,
// syscalls
Syscall0,
Syscall1,
Syscall2,
Syscall3,
Syscall4,
Syscall5,
Syscall6,
CastBool,
CastPtr,
CastInt,
CastVoid,
FnCall,
MemUse,
ConstUse,
Return,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum KeywordType {
If,
Else,
End,
While,
Do,
Include,
Memory,
Constant,
Function,
Then,
Done,
StructDef,
TypeDef,
Inline,
Export,
Extern,
Returns,
With,
}
#[derive(Clone, PartialEq)]
pub enum TypeType {
Ptr,
U8,
U16,
U32,
U64,
Void,
Any,
Custom(Vec<TypeType>),
Struct(StructDef)
}
impl TypeType {
pub fn get_size(&self) -> usize {
match self {
TypeType::Ptr => std::mem::size_of::<*const ()>(),
TypeType::U8 => 1,
TypeType::U16 => 2,
TypeType::U32 => 4,
TypeType::U64 => 8,
TypeType::Void => 0,
TypeType::Any => 0,
TypeType::Custom(ts) => ts.iter().map(|f| f.get_size()).sum(),
TypeType::Struct(s) => s.size,
}
}
}
impl std::fmt::Debug for TypeType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Ptr => write!(f, "Ptr"),
Self::U8 => write!(f, "U8"),
Self::U16 => write!(f, "U16"),
Self::U32 => write!(f, "U32"),
Self::U64 => write!(f, "U64"),
Self::Void => write!(f, "Void"),
Self::Any => write!(f, "Any"),
Self::Custom(arg0) => f.debug_tuple("Custom").field(arg0).finish(),
Self::Struct(arg0) => write!(f, "{} {}{:?}", arg0.size, arg0.ident, arg0.body),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum TokenType {
Keyword(KeywordType),
Type(TypeType),
Instruction(InstructionType),
Unknown(String)
}
#[derive(Debug, Clone, PartialEq)]
pub struct Token {
pub typ: TokenType,
pub loc: Loc,
pub lexem: String,
}
impl Token {
pub fn new(typ: TokenType, loc: Loc, lexem: String) -> Self {
Self {
typ,
loc,
lexem,
}
}
pub fn loc(&self) -> Loc {
self.loc.clone()
}
}

View File

@ -1,117 +0,0 @@
// use color_eyre::Result;
pub mod color {
#![allow(dead_code)]
pub const NONE: &str = "\x1b[0m";
pub const RESET: &str = "\x1b[0m";
pub const BRIGHT: &str = "\x1b[1m";
pub const DIM: &str = "\x1b[2m";
pub const UNDERSCORE: &str = "\x1b[4m";
pub const BLINK: &str = "\x1b[5m";
pub const REVERSE: &str = "\x1b[7m";
pub const HIDDEN: &str = "\x1b[8m";
pub const FG_BLACK: &str = "\x1b[30m";
pub const FG_RED: &str = "\x1b[31m";
pub const FG_GREEN: &str = "\x1b[32m";
pub const FG_YELLOW: &str = "\x1b[33m";
pub const FG_BLUE: &str = "\x1b[34m";
pub const FG_MAGENTA: &str = "\x1b[35m";
pub const FG_CYAN: &str = "\x1b[36m";
pub const FG_WHITE: &str = "\x1b[37m";
pub const BG_BLACK: &str = "\x1b[40m";
pub const BG_RED: &str = "\x1b[41m";
pub const BG_GREEN: &str = "\x1b[42m";
pub const BG_YELLOW: &str = "\x1b[43m";
pub const BG_BLUE: &str = "\x1b[44m";
pub const BG_MAGENTA: &str = "\x1b[45m";
pub const BG_CYAN: &str = "\x1b[46m";
pub const BG_WHITE: &str = "\x1b[47m";
}
pub mod logger {
#![allow(dead_code)]
use std::ops::Deref;
use crate::{util::color, definitions::Loc};
pub fn error(msg: &str) {
println!("{red}error{r}: {msg}", red=color::FG_RED, r=color::RESET);
}
pub fn warn(msg: &str) {
println!("{yellow}warn{r}: {msg}", yellow=color::FG_YELLOW, r=color::RESET);
}
pub fn info(msg: &str) {
println!("{green}info{r}: {msg}", green=color::FG_GREEN, r=color::RESET);
}
pub fn note(msg: &str) {
println!("{blue}note{r}: {msg}", blue=color::FG_BLUE, r=color::RESET);
}
pub fn lerror<P: Deref<Target = Loc>>(loc: P, msg: &str) {
println!("{f}:{r}:{c} {red}error{rs}: {msg}", red=color::FG_RED, rs=color::RESET, f=loc.0, r=loc.1, c=loc.2);
}
pub fn lwarn<P: Deref<Target = Loc>>(loc: P, msg: &str) {
println!("{f}:{r}:{c} {yellow}warn{rs}: {msg}", yellow=color::FG_YELLOW, rs=color::RESET, f=loc.0, r=loc.1, c=loc.2);
}
pub fn linfo<P: Deref<Target = Loc>>(loc: P, msg: &str) {
println!("{f}:{r}:{c} {green}info{rs}: {msg}", green=color::FG_GREEN, rs=color::RESET, f=loc.0, r=loc.1, c=loc.2);
}
pub fn lnote<P: Deref<Target = Loc>>(loc: P, msg: &str) {
println!("{f}:{r}:{c} {blue}note{rs}: {msg}", blue=color::FG_BLUE, rs=color::RESET, f=loc.0, r=loc.1, c=loc.2);
}
pub fn help(msg: &str) {
println!("{blue}help{r}: {msg}", blue=color::FG_CYAN, r=color::RESET);
}
pub fn code_block(code: &str) -> String {
let mut ret = String::new();
let lines = code.lines();
for (i, line) in lines.enumerate() {
use std::fmt::Write;
writeln!(ret, "{}{} | {}{}",color::FG_BLUE, i + 1, line, color::RESET).unwrap();
}
ret
}
pub mod macros {
#[macro_export] macro_rules! error { ($($arg:tt)*) => { $crate::util::logger::error(std::format_args!($($arg)*).to_string().as_str()) }; }
#[macro_export] macro_rules! warn { ($($arg:tt)*) => { $crate::util::logger::warn( std::format_args!($($arg)*).to_string().as_str()) }; }
#[macro_export] macro_rules! info { ($($arg:tt)*) => { $crate::util::logger::info( std::format_args!($($arg)*).to_string().as_str()) }; }
#[macro_export] macro_rules! note { ($($arg:tt)*) => { $crate::util::logger::note( std::format_args!($($arg)*).to_string().as_str()) }; }
#[macro_export] macro_rules! lerror { ($dst:expr, $($arg:tt)*) => { $crate::util::logger::lerror($dst, std::format_args!($($arg)*).to_string().as_str()) }; }
#[macro_export] macro_rules! lwarn { ($dst:expr, $($arg:tt)*) => { $crate::util::logger::lwarn($dst, std::format_args!($($arg)*).to_string().as_str()) }; }
#[macro_export] macro_rules! linfo { ($dst:expr, $($arg:tt)*) => { $crate::util::logger::linfo($dst, std::format_args!($($arg)*).to_string().as_str()) }; }
#[macro_export] macro_rules! lnote { ($dst:expr, $($arg:tt)*) => { $crate::util::logger::lnote($dst, std::format_args!($($arg)*).to_string().as_str()) }; }
#[macro_export] macro_rules! help { ($($arg:tt)*) => { $crate::util::logger::help( std::format_args!($($arg)*).to_string().as_str()) }; }
#[macro_export] macro_rules! code_block { ($($arg:tt)*) => { $crate::util::logger::code_block( std::format_args!($($arg)*).to_string().as_str()) }; }
}
}
// pub trait StringExtra{
// fn find_idx(&self, pat: char, start: u32) -> Result<u32, ()>;
// }
// impl StringExtra for String {
// fn find_idx(&self, pat: char, start: u32) -> Result<u32, ()> {
// let mut col = start;
// for c in (*self).chars() {
// if c == pat {
// return Ok(col);
// }
// col += 1;
// }
// Err(())
// }
// }

View File

@ -1,13 +1,49 @@
include "std.mcl"
fn main with int ptr returns void then
// p l
"Hello!\n" puts
// memory fd 4 end
// c"./test.mcl" FS_O_SYNC 0 fopen
// dup _dbg_print
// fd swap write32
structdef Uwu do
owo do u64 end
twt do u64 end
done
structdef Foo do
buz do u64 end
uwu do Uwu end
done
memory s_foo Foo end
//? Comments :3
// extern fn a with void returns void then done
// inline fn b with void returns void then done
// export fn c with void returns void then done
// fn putd with int returns void then drop done
fn main with void returns void then
s_foo.uwu.twt 69 write64
s_foo.uwu.twt read64 _dbg_print
// 1 2 add
// 69 _dbg_print
// "Hewo\n" puts
// if 3 4 eq do
// "omg what impossible!\n"
// else if 1 1 eq do
// "whaaaaaaaaa\n"
// else
// "finally, some good soup\n"
// done
// puts
// 10
// while dup 0 gt do
// "uwu " puts
// dup _dbg_print
// 1 sub
// done
done

View File

@ -1 +0,0 @@
gftdesd5ryutfgyhibugtf6r4

View File

@ -1,7 +0,0 @@
34 35 + print
800 380 - print
10 5 * print
40 5 / print

4
tools/.gitignore vendored
View File

@ -1,4 +0,0 @@
/*
!/.gitignore
!/*.c
!/build_tools.sh

View File

@ -1,3 +0,0 @@
#!/usr/bin/bash
gcc intcnv.c -o intcnv

View File

@ -1,47 +0,0 @@
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char** argv) {
if (argc < 3) {
printf("Usage: intcnv i32 134\n");
return 1;
}
const char* typ = argv[1];
__uint64_t num = atoi(argv[2]);
if (strcmp(typ, "u8") == 0) {
__uint8_t num2 = (__uint8_t)num;
printf("%d\n", num2);
}
if (strcmp(typ, "i8") == 0) {
__int8_t num2 = (__int8_t)num;
printf("%d\n", num2);
}
if (strcmp(typ, "u16") == 0) {
__uint16_t num2 = (__uint16_t)num;
printf("%d\n", num2);
}
if (strcmp(typ, "i16") == 0) {
__int16_t num2 = (__int16_t)num;
printf("%d\n", num2);
}
if (strcmp(typ, "u32") == 0) {
__uint32_t num2 = (__uint32_t)num;
printf("%d\n", num2);
}
if (strcmp(typ, "i32") == 0) {
__int32_t num2 = (__int32_t)num;
printf("%d\n", num2);
}
if (strcmp(typ, "u64") == 0) {
__uint64_t num2 = (__uint64_t)num;
printf("%d\n", num2);
}
if (strcmp(typ, "i64") == 0) {
__int64_t num2 = (__int64_t)num;
printf("%d\n", num2);
}
}