Compare commits
No commits in common. "main" and "old" have entirely different histories.
26
.github/workflows/rust.yml
vendored
Normal file
26
.github/workflows/rust.yml
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
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
|
17
.gitignore
vendored
17
.gitignore
vendored
|
@ -1 +1,18 @@
|
|||
/target
|
||||
/*
|
||||
|
||||
# files
|
||||
!/.gitignore
|
||||
!/cargo.lock
|
||||
!/cargo.toml
|
||||
!/README.md
|
||||
|
||||
# folders
|
||||
!/.github
|
||||
!/editor
|
||||
!/examples
|
||||
!/include
|
||||
!/playground
|
||||
!/src
|
||||
!/tests
|
||||
!/tools
|
327
Cargo.lock
generated
327
Cargo.lock
generated
|
@ -1,103 +1,48 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
]
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.94"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
|
||||
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "camino"
|
||||
version = "1.1.9"
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.23"
|
||||
version = "4.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
|
||||
checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"is-terminal",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.18"
|
||||
version = "4.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||
checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
|
@ -105,92 +50,167 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.4"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
name = "errno"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
name = "hermit-abi"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[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"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"camino",
|
||||
"clap",
|
||||
"lazy_static",
|
||||
"parse_int",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
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"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parse_int"
|
||||
version = "0.6.0"
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d695b79916a2c08bcff7be7647ab60d1402885265005a6658ffe6d763553c5a"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.92"
|
||||
version = "1.0.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
name = "rustix"
|
||||
version = "0.36.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.90"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -198,36 +218,75 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.14"
|
||||
name = "termcolor"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
name = "unicode-ident"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
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"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
|
@ -236,48 +295,42 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -1,13 +1,13 @@
|
|||
[package]
|
||||
name = "mclangc"
|
||||
description="The McLang Programming language compiler"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
default-run = "mclangc"
|
||||
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.94"
|
||||
camino = "1.1.9"
|
||||
clap = { version = "4.5.23", features = ["derive"] }
|
||||
lazy_static = "1.5.0"
|
||||
parse_int = "0.6.0"
|
||||
anyhow = "1.0.79"
|
||||
clap = { version = "4.1.8", features = ["derive"] }
|
||||
|
|
31
README.md
Normal file
31
README.md
Normal file
|
@ -0,0 +1,31 @@
|
|||
# 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
|
2
editor/vscode/.gitignore
vendored
Normal file
2
editor/vscode/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
**/*.log
|
||||
**/node_modules/
|
17
editor/vscode/.vscode/launch.json
vendored
Normal file
17
editor/vscode/.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
// 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}"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
4
editor/vscode/.vscodeignore
Normal file
4
editor/vscode/.vscodeignore
Normal file
|
@ -0,0 +1,4 @@
|
|||
.vscode/**
|
||||
.vscode-test/**
|
||||
.gitignore
|
||||
vsc-extension-quickstart.md
|
9
editor/vscode/CHANGELOG.md
Normal file
9
editor/vscode/CHANGELOG.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# 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
|
0
editor/vscode/LICENSE
Normal file
0
editor/vscode/LICENSE
Normal file
17
editor/vscode/README.md
Normal file
17
editor/vscode/README.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
# 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!**
|
30
editor/vscode/language-configuration.json
Normal file
30
editor/vscode/language-configuration.json
Normal file
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"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": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
["\"", "\""],
|
||||
["'", "'"]
|
||||
]
|
||||
}
|
BIN
editor/vscode/mclang-0.0.3.vsix
Normal file
BIN
editor/vscode/mclang-0.0.3.vsix
Normal file
Binary file not shown.
42
editor/vscode/package.json
Normal file
42
editor/vscode/package.json
Normal file
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
147
editor/vscode/syntaxes/mclang.tmLanguage.json
Normal file
147
editor/vscode/syntaxes/mclang.tmLanguage.json
Normal file
|
@ -0,0 +1,147 @@
|
|||
{
|
||||
"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|$)"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
29
editor/vscode/vsc-extension-quickstart.md
Normal file
29
editor/vscode/vsc-extension-quickstart.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
# 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.
|
1784
editor/vscode/yarn.lock
Normal file
1784
editor/vscode/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
3
examples/.gitignore
vendored
Normal file
3
examples/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
/*
|
||||
!/.gitignore
|
||||
!/*.mcl
|
4
examples/hello_world.mcl
Normal file
4
examples/hello_world.mcl
Normal file
|
@ -0,0 +1,4 @@
|
|||
include "std.mcl"
|
||||
|
||||
"Henlo World! :3\n" puts
|
||||
|
39
examples/rule110.mcl
Executable file
39
examples/rule110.mcl
Executable file
|
@ -0,0 +1,39 @@
|
|||
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
|
5
examples/seq_100.mcl
Normal file
5
examples/seq_100.mcl
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
0 while dup 100 < do
|
||||
dup print
|
||||
1 +
|
||||
end
|
1
include/compat.mcl
Normal file
1
include/compat.mcl
Normal file
|
@ -0,0 +1 @@
|
|||
// todo: add some sort of macrow
|
29
include/fs.mcl
Normal file
29
include/fs.mcl
Normal file
|
@ -0,0 +1,29 @@
|
|||
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 doesn’t 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 isn’t 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, don’t 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
|
20
include/int.mcl
Normal file
20
include/int.mcl
Normal file
|
@ -0,0 +1,20 @@
|
|||
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
|
61
include/io.mcl
Normal file
61
include/io.mcl
Normal file
|
@ -0,0 +1,61 @@
|
|||
|
||||
// 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
|
||||
|
322
include/linux.mcl
Normal file
322
include/linux.mcl
Normal file
|
@ -0,0 +1,322 @@
|
|||
|
||||
// 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
|
1
include/mem.mcl
Normal file
1
include/mem.mcl
Normal file
|
@ -0,0 +1 @@
|
|||
|
6
include/std.mcl
Normal file
6
include/std.mcl
Normal file
|
@ -0,0 +1,6 @@
|
|||
include "linux.mcl"
|
||||
include "io.mcl"
|
||||
include "util.mcl"
|
||||
include "int.mcl"
|
||||
include "fs.mcl"
|
||||
include "compat.mcl"
|
5
include/string.mcl
Normal file
5
include/string.mcl
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
|
||||
fn cstr_len with ptr returns int then
|
||||
dup while dup load8 '\0' != do 1 + end swap -
|
||||
done
|
15
include/util.mcl
Normal file
15
include/util.mcl
Normal file
|
@ -0,0 +1,15 @@
|
|||
|
||||
// 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
|
4
playground/.gitignore
vendored
Normal file
4
playground/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
/*
|
||||
!/.gitignore
|
||||
!/*.mcl
|
||||
!/mclangc
|
13
playground/mclangc
Executable file
13
playground/mclangc
Executable file
|
@ -0,0 +1,13 @@
|
|||
#!/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}
|
35
playground/test.mcl
Normal file
35
playground/test.mcl
Normal file
|
@ -0,0 +1,35 @@
|
|||
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
|
171
src/bin/mcl_test_dev.rs
Normal file
171
src/bin/mcl_test_dev.rs
Normal file
|
@ -0,0 +1,171 @@
|
|||
|
||||
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(())
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
#[repr(u8)]
|
||||
#[derive(Debug, Default)]
|
||||
pub enum Level {
|
||||
Off = 0,
|
||||
Error,
|
||||
Warn,
|
||||
#[default]
|
||||
Info,
|
||||
Help,
|
||||
Debug
|
||||
}
|
||||
|
||||
const C_RESET: &'static str = "\x1B[0m";
|
||||
const C_ERROR: &'static str = "\x1B[1;31m";
|
||||
const C_WARN: &'static str = "\x1B[1;33m";
|
||||
const C_INFO: &'static str = "\x1B[1;32m";
|
||||
const C_DEBUG: &'static str = "\x1B[1;35m";
|
||||
const C_HELP: &'static str = "\x1B[1;36m";
|
||||
|
||||
pub fn _log(level: Level, str: &str) {
|
||||
match level {
|
||||
Level::Off => return,
|
||||
Level::Error => println!("{C_ERROR}error{C_RESET}: {str}"),
|
||||
Level::Warn => println!("{C_WARN}warn{C_RESET}: {str}"),
|
||||
Level::Info => println!("{C_INFO}info{C_RESET}: {str}"),
|
||||
Level::Help => println!("{C_HELP}help{C_RESET}: {str}"),
|
||||
Level::Debug => println!("{C_DEBUG}debug{C_RESET}: {str}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
pub mod log {
|
||||
#[macro_export]
|
||||
macro_rules! error {
|
||||
($($arg:tt)*) => {
|
||||
crate::logger::_log(crate::logger::Level::Error, &format!($($arg)*))
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! warn {
|
||||
($($arg:tt)*) => {
|
||||
crate::logger::_log(crate::logger::Level::Warn, &format!($($arg)*))
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! info {
|
||||
($($arg:tt)*) => {
|
||||
crate::logger::_log(crate::logger::Level::Info, &format!($($arg)*))
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! help {
|
||||
($($arg:tt)*) => {
|
||||
crate::logger::_log(crate::logger::Level::Help, &format!($($arg)*))
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! debug {
|
||||
($($arg:tt)*) => {
|
||||
crate::logger::_log(crate::logger::Level::Debug, &format!($($arg)*))
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,220 +0,0 @@
|
|||
use std::{collections::HashMap, ffi::OsStr, io::Write, os::unix::ffi::OsStrExt, path::{Path, PathBuf}, process::ExitCode};
|
||||
use camino::Utf8PathBuf;
|
||||
use clap::Parser;
|
||||
use mclangc;
|
||||
|
||||
#[macro_use]
|
||||
mod logger;
|
||||
|
||||
/// Testing program for mclangc, taken inspiration from porth, which was made by tsoding :3
|
||||
#[derive(Debug, clap::Parser)]
|
||||
#[command(version, about, long_about = None)]
|
||||
struct CliArgs {
|
||||
/// Path to the test folder
|
||||
#[arg(long, short, default_value="./tests")]
|
||||
path: Utf8PathBuf,
|
||||
#[clap(subcommand)]
|
||||
cmd: CliCmd
|
||||
}
|
||||
|
||||
#[derive(Debug, clap::Subcommand)]
|
||||
pub enum CliCmd {
|
||||
/// Run the tests
|
||||
Run,
|
||||
/// Run the tests and set the output as the expected output
|
||||
Compile
|
||||
}
|
||||
|
||||
struct CollectedFiles {
|
||||
tokeniser: HashMap<String, (String, ExpTyp)>,
|
||||
parser: HashMap<String, (String, ExpTyp)>,
|
||||
}
|
||||
|
||||
enum ExpTyp {
|
||||
Text((PathBuf, String)),
|
||||
Path(PathBuf),
|
||||
}
|
||||
|
||||
impl ExpTyp {
|
||||
pub fn path(&self) -> &Path {
|
||||
match self {
|
||||
Self::Text((p, _)) => p,
|
||||
Self::Path(p) => p,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_files_for_single_type(path: &Path) -> anyhow::Result<HashMap<String, (String, ExpTyp)>> {
|
||||
let mut files = HashMap::new();
|
||||
for file in path.read_dir()? {
|
||||
let file = file?;
|
||||
if file.file_type()?.is_file() {
|
||||
if file.path().extension() != Some(OsStr::from_bytes(b"mcl")) {
|
||||
continue;
|
||||
}
|
||||
let src = std::fs::read_to_string(file.path())?;
|
||||
let exp_p = file.path().with_extension("exp");
|
||||
let name = file.path().with_extension("").file_name().unwrap().to_string_lossy().to_string();
|
||||
if exp_p.exists() {
|
||||
let exp = std::fs::read_to_string(&exp_p)?;
|
||||
files.insert(name, (src, ExpTyp::Text((exp_p, exp))));
|
||||
} else {
|
||||
files.insert(name, (src, ExpTyp::Path(exp_p)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(files)
|
||||
}
|
||||
|
||||
fn collect_all_files(path: &Path) -> anyhow::Result<CollectedFiles> {
|
||||
let path = path.to_path_buf();
|
||||
let mut tkn = path.clone();
|
||||
tkn.push("tokeniser");
|
||||
let mut parser = path.clone();
|
||||
parser.push("parser");
|
||||
Ok(CollectedFiles {
|
||||
tokeniser: collect_files_for_single_type(&tkn)?,
|
||||
parser: collect_files_for_single_type(&parser)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn test_tokeniser(cf: &CollectedFiles, compile: bool) -> anyhow::Result<usize> {
|
||||
let mut err_count = 0;
|
||||
for (name, (src, expected)) in &cf.tokeniser {
|
||||
let tokens = match mclangc::tokeniser::tokenise(src, &format!("tokeniser/{name}.mcl")) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
crate::error!("Test tokeniser/{name} had an error: {e}");
|
||||
err_count += 1;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if compile {
|
||||
let path = expected.path();
|
||||
if path.exists() {
|
||||
crate::info!("Test tokeniser/{name} already has a *.exp file, overwriting");
|
||||
} else {
|
||||
crate::info!("Test tokeniser/{name} doesnt a *.exp file, creating");
|
||||
}
|
||||
let mut fp = std::fs::File::options()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.create(true)
|
||||
.open(path)?;
|
||||
write!(fp, "{tokens:#?}")?;
|
||||
} else {
|
||||
let ExpTyp::Text((_, exp)) = expected else {
|
||||
crate::warn!("Test tokeniser/{name} doesnt have a *.exp file, please make it by running 'test compile'");
|
||||
continue;
|
||||
};
|
||||
if format!("{tokens:#?}") == *exp {
|
||||
crate::info!("Test tokeniser/{name}: OK");
|
||||
} else {
|
||||
crate::error!("Test tokeniser/{name}: FAIL");
|
||||
crate::debug!("Expected: {exp}");
|
||||
crate::debug!("Got: {tokens:#?}");
|
||||
err_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(err_count)
|
||||
}
|
||||
|
||||
fn test_parser(cf: &CollectedFiles, compile: bool) -> anyhow::Result<usize> {
|
||||
let mut err_count = 0;
|
||||
for (name, (src, expected)) in &cf.parser {
|
||||
let tokens = match mclangc::tokeniser::tokenise(src, &format!("parser/{name}.mcl")) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
crate::error!("Test parser/{name} had an error: {e}");
|
||||
err_count += 1;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let ast = match mclangc::parser::parse_program(tokens) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
crate::error!("Test parser/{name} had an error: {e}");
|
||||
err_count += 1;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if compile {
|
||||
let path = expected.path();
|
||||
if path.exists() {
|
||||
crate::info!("Test parser/{name} already has a *.exp file, overwriting");
|
||||
} else {
|
||||
crate::info!("Test parser/{name} doesnt a *.exp file, creating");
|
||||
}
|
||||
let mut fp = std::fs::File::options()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.create(true)
|
||||
.open(path)?;
|
||||
write!(fp, "{ast:#?}")?;
|
||||
} else {
|
||||
let ExpTyp::Text((_, exp)) = expected else {
|
||||
crate::warn!("Test parser/{name} doesnt have a *.exp file, please make it by running 'test compile'");
|
||||
continue;
|
||||
};
|
||||
if format!("{ast:#?}") == *exp {
|
||||
crate::info!("Test parser/{name}: OK");
|
||||
} else {
|
||||
crate::error!("Test parser/{name}: FAIL");
|
||||
crate::debug!("Expected: {exp}");
|
||||
crate::debug!("Got: {ast:#?}");
|
||||
err_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(err_count)
|
||||
}
|
||||
|
||||
fn test(cf: &CollectedFiles, compile: bool) -> anyhow::Result<usize> {
|
||||
let mut err_count = test_tokeniser(&cf, compile)?;
|
||||
err_count += test_parser(&cf, compile)?;
|
||||
|
||||
Ok(err_count)
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let cli = CliArgs::parse();
|
||||
let cf = match collect_all_files(cli.path.as_std_path()) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
crate::error!("Failed to read directory '{}', do you have permission to read it?: {e}", cli.path);
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
};
|
||||
let ec = match cli.cmd {
|
||||
CliCmd::Run => {
|
||||
match test(&cf, false) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
crate::error!("Had an error: {e}");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
CliCmd::Compile => {
|
||||
match test(&cf, true) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
crate::error!("Had an error: {e}");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if ec > 0 {
|
||||
crate::error!("Testing FAILED, had {ec} errors");
|
||||
return ExitCode::FAILURE;
|
||||
} else {
|
||||
crate::info!("Testing SUCCEEDED, had 0 errors");
|
||||
}
|
||||
ExitCode::SUCCESS
|
||||
}
|
39
src/cli.rs
39
src/cli.rs
|
@ -1,39 +0,0 @@
|
|||
use clap::{error::ErrorKind, CommandFactory};
|
||||
|
||||
use crate::logger::Level;
|
||||
|
||||
|
||||
#[derive(Debug, clap::Parser)]
|
||||
pub struct CliArgs {
|
||||
/// Output more info, will get overwritten if -q|--quiet is specified
|
||||
#[arg(long, short)]
|
||||
verbose: bool,
|
||||
/// Output nothing, except errors, will overwrite -v|--verbose
|
||||
#[arg(long, short)]
|
||||
quiet: bool,
|
||||
/// Output file
|
||||
#[arg(long, short, default_value="a.out")]
|
||||
pub output: String,
|
||||
/// All input files
|
||||
#[clap(num_args = 1..)]
|
||||
pub input: Vec<String>
|
||||
}
|
||||
|
||||
impl CliArgs {
|
||||
pub fn set_log_level(&self) {
|
||||
if self.quiet {
|
||||
unsafe {
|
||||
crate::logger::LEVEL = Level::Error;
|
||||
}
|
||||
} else if self.verbose {
|
||||
unsafe {
|
||||
crate::logger::LEVEL = Level::Debug;
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn validate(&self) {
|
||||
if self.input.len() < 1 {
|
||||
CliArgs::command().error(ErrorKind::TooFewValues, "at least one value is required for '<INPUT>' but none was supplied").exit();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
use std::fmt::{Debug, Display};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
|
||||
pub struct Loc {
|
||||
file: String,
|
||||
line: usize,
|
||||
col: usize,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Loc {
|
||||
pub fn new(s: impl ToString, line: usize, col: usize) -> Self {
|
||||
Self {
|
||||
file: s.to_string(),
|
||||
line, col
|
||||
}
|
||||
}
|
||||
fn file(&self) -> &String {
|
||||
&self.file
|
||||
}
|
||||
fn line(&self) -> usize {
|
||||
self.line
|
||||
}
|
||||
fn col(&self) -> usize {
|
||||
self.col
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LocBox<T: Clone + Debug> {
|
||||
inner: T,
|
||||
loc: Loc
|
||||
}
|
||||
|
||||
impl<T: Clone + Debug> LocBox<T> {
|
||||
pub fn new(loc: &Loc, inner: T) -> Self {
|
||||
Self { loc: loc.clone(), inner }
|
||||
}
|
||||
pub fn inner(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
pub fn inner_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
pub fn loc(&self) -> &Loc {
|
||||
&self.loc
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Loc {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}:{}:{}", self.file, self.line, self.col)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Loc {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
line: 1,
|
||||
col: 1,
|
||||
file: Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait LocIncr {
|
||||
fn inc_line(&mut self);
|
||||
fn inc_col(&mut self);
|
||||
}
|
||||
|
||||
impl LocIncr for Loc {
|
||||
fn inc_line(&mut self) {
|
||||
self.line += 1;
|
||||
self.col = 1;
|
||||
}
|
||||
fn inc_col(&mut self) {
|
||||
self.col += 1;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
pub mod loc;
|
||||
pub use loc::Loc;
|
93
src/compile/commands.rs
Normal file
93
src/compile/commands.rs
Normal file
|
@ -0,0 +1,93 @@
|
|||
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))
|
||||
}
|
536
src/compile/linux_x86_64.rs
Normal file
536
src/compile/linux_x86_64.rs
Normal file
|
@ -0,0 +1,536 @@
|
|||
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(())
|
||||
}
|
362
src/compile/mod.rs
Normal file
362
src/compile/mod.rs
Normal file
|
@ -0,0 +1,362 @@
|
|||
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
|
||||
";
|
16
src/config.rs
Normal file
16
src/config.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* 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;
|
283
src/constants.rs
Normal file
283
src/constants.rs
Normal file
|
@ -0,0 +1,283 @@
|
|||
|
||||
|
||||
|
||||
#[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
|
||||
}
|
||||
|
392
src/definitions/mod.rs
Normal file
392
src/definitions/mod.rs
Normal file
|
@ -0,0 +1,392 @@
|
|||
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>
|
||||
}
|
18
src/errors/mod.rs
Normal file
18
src/errors/mod.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
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"
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
442
src/interpret/linux_x86_64/mod.rs
Normal file
442
src/interpret/linux_x86_64/mod.rs
Normal file
|
@ -0,0 +1,442 @@
|
|||
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
|
||||
}
|
22
src/interpret/linux_x86_64/syscalls.rs
Normal file
22
src/interpret/linux_x86_64/syscalls.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
#[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
|
||||
}
|
27
src/interpret/mod.rs
Normal file
27
src/interpret/mod.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
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
|
||||
}
|
147
src/lexer.rs
Normal file
147
src/lexer.rs
Normal file
|
@ -0,0 +1,147 @@
|
|||
|
||||
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
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
pub mod common;
|
||||
pub mod tokeniser;
|
||||
pub mod parser;
|
||||
pub mod cli;
|
||||
#[macro_use]
|
||||
pub mod logger;
|
||||
pub mod validator;
|
162
src/logger.rs
162
src/logger.rs
|
@ -1,162 +0,0 @@
|
|||
pub static mut LEVEL: Level = Level::Info;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Level {
|
||||
Off = 0,
|
||||
Error,
|
||||
Warn,
|
||||
#[default]
|
||||
Info,
|
||||
Help,
|
||||
Debug
|
||||
}
|
||||
|
||||
const C_RESET: &'static str = "\x1B[0m";
|
||||
const C_ERROR: &'static str = "\x1B[1;31m";
|
||||
const C_WARN: &'static str = "\x1B[1;33m";
|
||||
const C_INFO: &'static str = "\x1B[1;32m";
|
||||
const C_DEBUG: &'static str = "\x1B[1;35m";
|
||||
const C_HELP: &'static str = "\x1B[1;36m";
|
||||
|
||||
pub fn _log(dbg_loc: Option<(&'static str, u32, u32)>, level: Level, str: &str) {
|
||||
if level > unsafe { LEVEL }{
|
||||
return;
|
||||
}
|
||||
let dbg = if let Some((file, line, col)) = dbg_loc {
|
||||
format!("{file}:{line}:{col}: ")
|
||||
} else {String::new()};
|
||||
match level {
|
||||
Level::Off => return,
|
||||
Level::Error => println!("{dbg}{C_ERROR}error{C_RESET}: {str}"),
|
||||
Level::Warn => println!("{dbg}{C_WARN}warn{C_RESET}: {str}"),
|
||||
Level::Info => println!("{dbg}{C_INFO}info{C_RESET}: {str}"),
|
||||
Level::Help => println!("{dbg}{C_HELP}help{C_RESET}: {str}"),
|
||||
Level::Debug => println!("{dbg}{C_DEBUG}debug{C_RESET}: {str}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn _log_with_loc(dbg_loc: Option<(&'static str, u32, u32)>, loc: String, level: Level, str: &str) {
|
||||
if level > unsafe { LEVEL }{
|
||||
return;
|
||||
}
|
||||
let dbg = if let Some((file, line, col)) = dbg_loc {
|
||||
format!("{file}:{line}:{col}: ")
|
||||
} else {String::new()};
|
||||
match level {
|
||||
Level::Off => return,
|
||||
Level::Error => println!("{dbg}{loc}: {C_ERROR}error{C_RESET}: {str}"),
|
||||
Level::Warn => println!("{dbg}{loc}: {C_WARN}warn{C_RESET}: {str}"),
|
||||
Level::Info => println!("{dbg}{loc}: {C_INFO}info{C_RESET}: {str}"),
|
||||
Level::Help => println!("{dbg}{loc}: {C_HELP}help{C_RESET}: {str}"),
|
||||
Level::Debug => println!("{dbg}{loc}: {C_DEBUG}debug{C_RESET}: {str}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
pub mod log {
|
||||
#[macro_export]
|
||||
macro_rules! error {
|
||||
($($arg:tt)*) => {
|
||||
if cfg!(debug_assertions) {
|
||||
crate::logger::_log(Some((file!(), line!(), column!())), crate::logger::Level::Error, &format!($($arg)*))
|
||||
} else {
|
||||
crate::logger::_log(None, crate::logger::Level::Error, &format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! warn {
|
||||
($($arg:tt)*) => {
|
||||
if cfg!(debug_assertions) {
|
||||
crate::logger::_log(Some((file!(), line!(), column!())), crate::logger::Level::Warn, &format!($($arg)*))
|
||||
} else {
|
||||
crate::logger::_log(None, crate::logger::Level::Warn, &format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! info {
|
||||
($($arg:tt)*) => {
|
||||
if cfg!(debug_assertions) {
|
||||
crate::logger::_log(Some((file!(), line!(), column!())), crate::logger::Level::Info, &format!($($arg)*))
|
||||
} else {
|
||||
crate::logger::_log(None, crate::logger::Level::Info, &format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! help {
|
||||
($($arg:tt)*) => {
|
||||
if cfg!(debug_assertions) {
|
||||
crate::logger::_log(Some((file!(), line!(), column!())), crate::logger::Level::Help, &format!($($arg)*))
|
||||
} else {
|
||||
crate::logger::_log(None, crate::logger::Level::Help, &format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! debug {
|
||||
($($arg:tt)*) => {
|
||||
if cfg!(debug_assertions) {
|
||||
crate::logger::_log(Some((file!(), line!(), column!())), crate::logger::Level::Debug, &format!($($arg)*))
|
||||
} else {
|
||||
crate::logger::_log(None, crate::logger::Level::Debug, &format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! lerror {
|
||||
($loc:expr, $($arg:tt)*) => {
|
||||
if cfg!(debug_assertions) {
|
||||
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Error, &format!($($arg)*))
|
||||
} else {
|
||||
crate::logger::_log_with_loc(None, format!("{}", $loc), crate::logger::Level::Error, &format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! lwarn {
|
||||
($loc:expr, $($arg:tt)*) => {
|
||||
if cfg!(debug_assertions) {
|
||||
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Warn, &format!($($arg)*))
|
||||
} else {
|
||||
crate::logger::_log_with_loc(None, format!("{}", $loc), crate::logger::Level::Warn, &format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! linfo {
|
||||
($loc:expr, $($arg:tt)*) => {
|
||||
if cfg!(debug_assertions) {
|
||||
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Info, &format!($($arg)*))
|
||||
} else {
|
||||
crate::logger::_log_with_loc(None, format!("{}", $loc), crate::logger::Level::Info, &format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! lhelp {
|
||||
($loc:expr, $($arg:tt)*) => {
|
||||
if cfg!(debug_assertions) {
|
||||
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Help, &format!($($arg)*))
|
||||
} else {
|
||||
crate::logger::_log_with_loc(None, format!("{}", $loc), crate::logger::Level::Help, &format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! ldebug {
|
||||
($loc:expr, $($arg:tt)*) => {
|
||||
if cfg!(debug_assertions) {
|
||||
crate::logger::_log_with_loc(Some((file!(), line!(), column!())), format!("{}", $loc), crate::logger::Level::Debug, &format!($($arg)*))
|
||||
} else {
|
||||
crate::logger::_log_with_loc(None, format!("{}", $loc), crate::logger::Level::Debug, &format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
138
src/main.rs
138
src/main.rs
|
@ -1,31 +1,127 @@
|
|||
use std::{path::PathBuf, process::ExitCode};
|
||||
#![allow(clippy::wildcard_imports)]
|
||||
#![allow(clippy::too_many_lines)]
|
||||
mod definitions;
|
||||
mod util;
|
||||
mod compile;
|
||||
mod parser;
|
||||
mod lexer;
|
||||
mod preprocessor;
|
||||
mod typechecker;
|
||||
mod precompiler;
|
||||
mod config;
|
||||
mod errors;
|
||||
pub mod test;
|
||||
use config::*;
|
||||
use std::{fs, collections::HashMap};
|
||||
|
||||
use clap::Parser;
|
||||
// Importing logger here too cause the logger macros dont work outside the mclanc lib
|
||||
mod logger;
|
||||
use anyhow::{Result, bail};
|
||||
|
||||
#[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,
|
||||
|
||||
/// 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>,
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let cli = mclangc::cli::CliArgs::parse();
|
||||
cli.set_log_level();
|
||||
cli.validate();
|
||||
for file in &cli.input {
|
||||
let fp = PathBuf::from(file);
|
||||
if !fp.exists() {
|
||||
error!("File {fp:?} doesnt exits, exiting");
|
||||
anyhow::bail!("")
|
||||
}
|
||||
|
||||
let data = std::fs::read_to_string(fp).unwrap();
|
||||
info!("Tokenising {file}");
|
||||
let tokens = mclangc::tokeniser::tokenise(&data, &file)?;
|
||||
info!("Parsing {file}");
|
||||
let mut prog = mclangc::parser::parse_program(tokens)?;
|
||||
info!("Validating {file}");
|
||||
mclangc::validator::validate_code(&mut prog)?;
|
||||
dbg!(&prog);
|
||||
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!("")
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
229
src/parser.rs
Normal file
229
src/parser.rs
Normal file
|
@ -0,0 +1,229 @@
|
|||
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)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::{common::loc::LocBox, tokeniser::tokentype::*};
|
||||
|
||||
use super::{typ::Type, Ast};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Expr {
|
||||
// Comment(Comment),
|
||||
Group(Box<LocBox<Expr>>),
|
||||
UnOp {
|
||||
typ: Punctuation,
|
||||
right: Box<LocBox<Expr>>,
|
||||
},
|
||||
BinOp {
|
||||
typ: Punctuation,
|
||||
left: Box<LocBox<Expr>>,
|
||||
right: Box<LocBox<Expr>>,
|
||||
},
|
||||
Literal(super::literal::Literal),
|
||||
ArrayIndex {
|
||||
name: Box<LocBox<Expr>>,
|
||||
index: Box<LocBox<Expr>>,
|
||||
},
|
||||
Path(Path),
|
||||
Call {
|
||||
path: Box<LocBox<Expr>>,
|
||||
params: CallParams, // LocBox<Expr> ~ (, Expr)*
|
||||
},
|
||||
//MethodCall {
|
||||
// var_name: Box<LocBox<Expr>>,
|
||||
// method_name: Ident,
|
||||
// params: CallParams,
|
||||
//},
|
||||
|
||||
/// the left side only exists on the /.|->/ chain
|
||||
FieldAccess {
|
||||
left: Box<Option<LocBox<Expr>>>,
|
||||
right: Box<LocBox<Expr>>,
|
||||
},
|
||||
PtrFieldAccess {
|
||||
left: Box<Option<LocBox<Expr>>>,
|
||||
right: Box<LocBox<Expr>>,
|
||||
},
|
||||
ForLoop {
|
||||
init: Box<Ast>,
|
||||
test: Box<LocBox<Expr>>,
|
||||
on_loop: Box<LocBox<Expr>>,
|
||||
body: Block,
|
||||
},
|
||||
WhileLoop {
|
||||
test: Box<LocBox<Expr>>,
|
||||
body: Block,
|
||||
},
|
||||
InfLoop {
|
||||
body: Block,
|
||||
},
|
||||
If(IfExpr),
|
||||
Struct {
|
||||
path: Path,
|
||||
fields: HashMap<Ident, LocBox<Expr>>,
|
||||
},
|
||||
Return(Box<Option<LocBox<Expr>>>),
|
||||
Break,
|
||||
Continue,
|
||||
Cast {
|
||||
left: Box<LocBox<Expr>>,
|
||||
right: Box<LocBox<Type>>
|
||||
},
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
pub fn unwrap_path(&self) -> Path {
|
||||
let Expr::Path(p) = self else {panic!("Unwrapping")};
|
||||
p.clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CallParams(pub Vec<LocBox<Expr>>);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Block(pub Vec<Ast>);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Path(pub Vec<Ident>);
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IfExpr {
|
||||
pub test: Box<LocBox<Expr>>,
|
||||
pub body: Block,
|
||||
pub else_if: Option<IfBranchExpr>
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum IfBranchExpr {
|
||||
ElseIf(Box<IfExpr>),
|
||||
Else(Block)
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::{common::loc::LocBox, tokeniser::tokentype::*};
|
||||
|
||||
use super::{expr::Expr, typ::Type, Ast};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Literal {
|
||||
Number(Number),
|
||||
Ident(Ident),
|
||||
String(TString),
|
||||
Char(Char),
|
||||
Array(Vec<LocBox<Expr>>),
|
||||
ArrayRepeat {
|
||||
typ: Box<LocBox<Type>>,
|
||||
count: Box<LocBox<Expr>>,
|
||||
},
|
||||
Struct {
|
||||
name: Ident,
|
||||
fields: HashMap<Ident, Ast>
|
||||
},
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use statement::{Enum, Function, Struct};
|
||||
|
||||
use crate::{common::loc::LocBox, validator::predefined::TypeType};
|
||||
pub use crate::tokeniser::tokentype::*;
|
||||
|
||||
pub mod expr;
|
||||
pub mod literal;
|
||||
pub mod statement;
|
||||
pub mod typ;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Program {
|
||||
pub ast: expr::Block,
|
||||
pub structs: HashMap<Ident, LocBox<Struct>>,
|
||||
pub enums: HashMap<Ident, LocBox<Enum>>,
|
||||
pub types: HashMap<Ident, TypeType>,
|
||||
pub functions: HashMap<Ident, LocBox<Function>>,
|
||||
pub member_functions: HashMap<Ident, HashMap<Ident, LocBox<Function>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Ast {
|
||||
Expr(LocBox<expr::Expr>),
|
||||
Statement(LocBox<statement::Statement>),
|
||||
}
|
||||
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
|
||||
use crate::common::{loc::LocBox, Loc};
|
||||
|
||||
use super::{expr::{Block, Expr}, typ::Type, Ident, TString};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Statement {
|
||||
Fn(Function),
|
||||
TypeAlias(TypeAlias),
|
||||
Struct(Struct),
|
||||
Enum(Enum),
|
||||
ConstVar {
|
||||
name: Ident,
|
||||
typ: LocBox<Type>,
|
||||
val: LocBox<Expr>
|
||||
},
|
||||
StaticVar {
|
||||
name: Ident,
|
||||
typ: LocBox<Type>,
|
||||
val: LocBox<Expr>,
|
||||
},
|
||||
Let {
|
||||
name: Ident,
|
||||
typ: Option<LocBox<Type>>,
|
||||
val: Option<LocBox<Expr>>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TypeAlias {
|
||||
pub name: Ident,
|
||||
pub typ: LocBox<Type>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Struct {
|
||||
pub name: Ident,
|
||||
pub fields: Vec<(Ident, LocBox<Type>)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Enum {
|
||||
pub name: Ident,
|
||||
pub fields: Vec<Ident>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Function {
|
||||
pub struct_name: Option<Ident>,
|
||||
pub name: Ident,
|
||||
pub params: Vec<(Ident, LocBox<Type>)>,
|
||||
pub ret_type: Option<LocBox<Type>>,
|
||||
pub qual_const: bool,
|
||||
pub qual_extern: Option<TString>, // abi
|
||||
pub body: Option<Block>, // If None then its a type declaration
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
use crate::common::{loc::LocBox, Loc};
|
||||
|
||||
use super::{expr::Expr, Ident};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Type {
|
||||
Ref {
|
||||
inner: Box<Type>,
|
||||
mutable: bool,
|
||||
},
|
||||
Array {
|
||||
inner: Box<Type>,
|
||||
},
|
||||
ArrayRepeat {
|
||||
inner: Box<Type>,
|
||||
count: LocBox<Expr>,
|
||||
},
|
||||
Owned(Ident),
|
||||
}
|
|
@ -1,461 +0,0 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
use crate::{common::loc::LocBox, debug, error, lerror, parser::{typ::parse_type, Punctuation}, tokeniser::Token};
|
||||
|
||||
use super::{ast::{expr::{Block, CallParams, Expr, IfBranchExpr, IfExpr, Path}, literal::Literal, TokenType}, parse_item, utils, Delimiter, Keyword};
|
||||
|
||||
const BINOP_LIST: &[TokenType] = &[
|
||||
TokenType::Punct(Punctuation::Plus),
|
||||
TokenType::Punct(Punctuation::Minus),
|
||||
TokenType::Punct(Punctuation::Div),
|
||||
TokenType::Punct(Punctuation::Star),
|
||||
TokenType::Punct(Punctuation::Mod),
|
||||
TokenType::Punct(Punctuation::Shl),
|
||||
TokenType::Punct(Punctuation::Shr),
|
||||
TokenType::Punct(Punctuation::AndAnd),
|
||||
TokenType::Punct(Punctuation::OrOr),
|
||||
TokenType::Punct(Punctuation::Ampersand),
|
||||
TokenType::Punct(Punctuation::Or),
|
||||
TokenType::Punct(Punctuation::Xor),
|
||||
TokenType::Punct(Punctuation::AddEq),
|
||||
TokenType::Punct(Punctuation::SubEq),
|
||||
TokenType::Punct(Punctuation::DivEq),
|
||||
TokenType::Punct(Punctuation::MulEq),
|
||||
TokenType::Punct(Punctuation::ModEq),
|
||||
TokenType::Punct(Punctuation::ShlEq),
|
||||
TokenType::Punct(Punctuation::ShrEq),
|
||||
TokenType::Punct(Punctuation::AndEq),
|
||||
TokenType::Punct(Punctuation::OrEq),
|
||||
TokenType::Punct(Punctuation::XorEq),
|
||||
TokenType::Punct(Punctuation::Eq),
|
||||
TokenType::Punct(Punctuation::EqEq),
|
||||
TokenType::Punct(Punctuation::Lt),
|
||||
TokenType::Punct(Punctuation::Gt),
|
||||
TokenType::Punct(Punctuation::Le),
|
||||
TokenType::Punct(Punctuation::Ge),
|
||||
];
|
||||
|
||||
pub fn parse_expr(tokens: &mut Vec<Token>, precedence: usize, consume_semi: bool) -> Result<Option<LocBox<Expr>>> {
|
||||
let res = if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenL)) {
|
||||
Some(parse_group(tokens)?)
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::ident("")) {
|
||||
let p = parse_path(tokens)?;
|
||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
|
||||
Some(parse_struct_literal(tokens, p.inner().unwrap_path())?)
|
||||
} else {
|
||||
Some(p)
|
||||
}
|
||||
} else
|
||||
if let Some(_) = utils::check_from_many(tokens, &[
|
||||
TokenType::Punct(Punctuation::Not),
|
||||
TokenType::Punct(Punctuation::Plus),
|
||||
TokenType::Punct(Punctuation::Minus),
|
||||
TokenType::Punct(Punctuation::Ampersand),
|
||||
TokenType::Punct(Punctuation::Star),
|
||||
]) {
|
||||
Some(parse_unop(tokens)?)
|
||||
} else
|
||||
if let Some(_) = utils::check_from_many(tokens, &[
|
||||
TokenType::string("", false),
|
||||
TokenType::number(0, 0, false),
|
||||
TokenType::char('\0'),
|
||||
TokenType::Delim(Delimiter::SquareL),
|
||||
]) {
|
||||
Some(parse_literal(tokens)?)
|
||||
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::While)) {
|
||||
return Ok(Some(parse_while_loop(tokens)?));
|
||||
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::For)) {
|
||||
return Ok(Some(parse_for_loop(tokens)?));
|
||||
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Loop)) {
|
||||
return Ok(Some(parse_inf_loop(tokens)?));
|
||||
} else if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Return)) {
|
||||
return Ok(Some(parse_return(tokens)?));
|
||||
} else if let Some(kw) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Break)) {
|
||||
return Ok(Some(LocBox::new(kw.loc(), Expr::Break)));
|
||||
} else if let Some(kw) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Continue)) {
|
||||
return Ok(Some(LocBox::new(kw.loc(), Expr::Continue)));
|
||||
} else if let Some(kw) = utils::check(tokens, TokenType::Keyword(Keyword::If)) {
|
||||
return Ok(Some(LocBox::new(&kw.loc().clone(), Expr::If(parse_if(tokens)?))));
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
|
||||
if let Some(mut res) = res {
|
||||
if utils::check(tokens, TokenType::Punct(Punctuation::Fieldaccess)).is_some() {
|
||||
res = parse_field_access(tokens, res)?;
|
||||
}
|
||||
if utils::check(tokens, TokenType::Punct(Punctuation::Arrow)).is_some() {
|
||||
res =parse_ptr_field_access(tokens, res)?;
|
||||
}
|
||||
if utils::check(tokens, TokenType::Delim(Delimiter::ParenL)).is_some() {
|
||||
res = parse_fn_call(tokens, res)?;
|
||||
}
|
||||
if utils::check(tokens, TokenType::Keyword(Keyword::As)).is_some() {
|
||||
res = parse_cast(tokens, res)?;
|
||||
}
|
||||
if utils::check(tokens, TokenType::Delim(Delimiter::SquareL)).is_some() {
|
||||
res = parse_array_index(tokens, res)?;
|
||||
}
|
||||
|
||||
if let Some(_) = utils::check_from_many(tokens, BINOP_LIST) {
|
||||
return Ok(Some(parse_binop(tokens, res, precedence)?));
|
||||
} else {
|
||||
if consume_semi {
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "Expected ; at the end of the expression")?;
|
||||
}
|
||||
return Ok(Some(res));
|
||||
}
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn parse_return(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Return), "")?;
|
||||
let item = parse_expr(tokens, 0, true)?;
|
||||
Ok(LocBox::new(kw.loc(), Expr::Return(Box::new(item))))
|
||||
}
|
||||
|
||||
fn parse_cast(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::As), "")?;
|
||||
let typ = parse_type(tokens)?;
|
||||
Ok(LocBox::new(kw.loc(), Expr::Cast {
|
||||
left: Box::new(left),
|
||||
right: Box::new(typ)
|
||||
}))
|
||||
}
|
||||
fn parse_if(tokens: &mut Vec<Token>) -> Result<IfExpr> {
|
||||
let loc = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::If), "")?;
|
||||
let Some(test) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(loc.loc(), "Expected test for if statement, got nothing");
|
||||
bail!("")
|
||||
};
|
||||
let block = if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
|
||||
if let Some(_) = utils::check_2_last(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
||||
_ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR));
|
||||
Block(Vec::new())
|
||||
} else {
|
||||
parse_block(tokens)?
|
||||
}
|
||||
} else {
|
||||
lerror!(loc.loc(), "Expected '{{'");
|
||||
bail!("")
|
||||
};
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Else)) {
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::If)) {
|
||||
let branch = IfBranchExpr::ElseIf(Box::new(parse_if(tokens)?));
|
||||
Ok(IfExpr {
|
||||
test: Box::new(test),
|
||||
body: block,
|
||||
else_if: Some(branch)
|
||||
})
|
||||
} else {
|
||||
let branch = IfBranchExpr::Else(parse_block(tokens)?);
|
||||
Ok(IfExpr {
|
||||
test: Box::new(test),
|
||||
body: block,
|
||||
else_if: Some(branch)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Ok(IfExpr {
|
||||
test: Box::new(test),
|
||||
body: block,
|
||||
else_if: None
|
||||
})
|
||||
}
|
||||
}
|
||||
fn parse_while_loop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::While), "")?;
|
||||
let Some(test) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(kw.loc(), "Expected test comparrison for while loop, got nothing");
|
||||
bail!("")
|
||||
};
|
||||
let block = parse_block(tokens)?;
|
||||
Ok(LocBox::new(kw.loc(), Expr::WhileLoop {
|
||||
test: Box::new(test),
|
||||
body: block
|
||||
}))
|
||||
}
|
||||
fn parse_for_loop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::For), "")?;
|
||||
let Some(pre) = parse_item(tokens)? else {
|
||||
lerror!(kw.loc(), "Expected init stat for a for loop, got nothing");
|
||||
bail!("")
|
||||
};
|
||||
// Semicolon parsed out by parse_item above
|
||||
let Some(test) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(kw.loc(), "Expected test comparrison for a for loop, got nothing");
|
||||
bail!("")
|
||||
};
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "");
|
||||
let Some(post) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(kw.loc(), "Expected post expression (usually an index increment) for a for loop, got nothing");
|
||||
bail!("")
|
||||
};
|
||||
let block = parse_block(tokens)?;
|
||||
|
||||
Ok(LocBox::new(kw.loc(), Expr::ForLoop {
|
||||
init: Box::new(pre),
|
||||
test: Box::new(test),
|
||||
on_loop: Box::new(post),
|
||||
body: block
|
||||
}))
|
||||
}
|
||||
fn parse_inf_loop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Loop), "")?;
|
||||
let block = parse_block(tokens)?;
|
||||
Ok(LocBox::new(kw.loc(), Expr::InfLoop { body: block }))
|
||||
}
|
||||
fn parse_fn_call(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr>> {
|
||||
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
||||
let mut params = Vec::new();
|
||||
|
||||
while !tokens.is_empty() {
|
||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
|
||||
break;
|
||||
}
|
||||
let Some(param) = parse_expr(tokens, 0, false)? else {break};
|
||||
params.push(param);
|
||||
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
||||
if let None = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
|
||||
lerror!(&utils::get_last_loc(), "Expected ',' or ')' but didnt find either");
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "");
|
||||
Ok(LocBox::new(start.loc(), Expr::Call { path: Box::new(left), params: CallParams(params) }))
|
||||
}
|
||||
fn parse_array_index(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr>> {
|
||||
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareL), "")?;
|
||||
let Some(idx) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(start.loc(), "Expected index for in array index but found nothing.");
|
||||
bail!("")
|
||||
};
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "");
|
||||
Ok(LocBox::new(start.loc(), Expr::ArrayIndex {
|
||||
name: Box::new(left),
|
||||
index: Box::new(idx)
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_field_access(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr>> {
|
||||
let start = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Fieldaccess), "unreachable")?;
|
||||
|
||||
let right = if let Some(_) = utils::check_2_last(tokens, TokenType::Punct(Punctuation::Arrow)) {
|
||||
let right = parse_path(tokens)?;
|
||||
parse_ptr_field_access(tokens, right)?
|
||||
} else if let Some(_) = utils::check_2_last(tokens, TokenType::Punct(Punctuation::Fieldaccess)) {
|
||||
let right = parse_path(tokens)?;
|
||||
parse_field_access(tokens, right)?
|
||||
} else {
|
||||
parse_path(tokens)?
|
||||
};
|
||||
Ok(LocBox::new(start.loc(), Expr::FieldAccess {
|
||||
left: Box::new(Some(left)),
|
||||
right: Box::new(right)
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_ptr_field_access(tokens: &mut Vec<Token>, left: LocBox<Expr>) -> Result<LocBox<Expr>> {
|
||||
let start = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Arrow), "unreachable")?;
|
||||
let right = if let Some(_) = utils::check_2_last(tokens, TokenType::Punct(Punctuation::Arrow)) {
|
||||
let right = parse_path(tokens)?;
|
||||
parse_ptr_field_access(tokens, right)?
|
||||
} else if let Some(_) = utils::check_2_last(tokens, TokenType::Punct(Punctuation::Fieldaccess)) {
|
||||
let right = parse_path(tokens)?;
|
||||
parse_field_access(tokens, right)?
|
||||
} else {
|
||||
parse_path(tokens)?
|
||||
};
|
||||
Ok(LocBox::new(start.loc(), Expr::PtrFieldAccess {
|
||||
left: Box::new(Some(left)),
|
||||
right: Box::new(right)
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_literal(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
|
||||
if let Some(tkn) = utils::check_consume(tokens, TokenType::string("", false)) {
|
||||
let TokenType::String(str) = tkn.tt() else {unreachable!()};
|
||||
return Ok(LocBox::new(tkn.loc(), Expr::Literal(Literal::String(str.clone()))));
|
||||
} else
|
||||
if let Some(tkn) = utils::check_consume(tokens, TokenType::number(0, 0, false)) {
|
||||
let TokenType::Number(val) = tkn.tt() else {unreachable!()};
|
||||
return Ok(LocBox::new(tkn.loc(), Expr::Literal(Literal::Number(val.clone()))));
|
||||
} else
|
||||
if let Some(tkn) = utils::check_consume(tokens, TokenType::char('\0')) {
|
||||
let TokenType::Char(val) = tkn.tt() else {unreachable!()};
|
||||
return Ok(LocBox::new(tkn.loc(), Expr::Literal(Literal::Char(val.clone()))));
|
||||
} else
|
||||
if let Some(start) = utils::check_consume(tokens, TokenType::Delim(Delimiter::SquareL)) {
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Delim(Delimiter::SquareR)) {
|
||||
return Ok(LocBox::new(start.loc(), Expr::Literal(Literal::Array(Vec::new()))));
|
||||
}
|
||||
if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Comma) {
|
||||
let first = parse_expr(tokens, 0, false)?;
|
||||
let Some(first) = first else { unreachable!() };
|
||||
|
||||
let mut values = Vec::new();
|
||||
values.push(first);
|
||||
while !tokens.is_empty() {
|
||||
let Some(val) = parse_expr(tokens, 0, false)? else{break};
|
||||
|
||||
values.push(val);
|
||||
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
|
||||
return Ok(LocBox::new(start.loc(), Expr::Literal(Literal::Array(values))));
|
||||
} else if *tokens[tokens.len()-2].tt() == TokenType::Punct(Punctuation::Semi) {
|
||||
let typ = parse_type(tokens)?;
|
||||
let count = parse_expr(tokens, 0, false)?.unwrap();
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
|
||||
return Ok(LocBox::new(start.loc(), Expr::Literal(Literal::ArrayRepeat {
|
||||
typ: Box::new(typ),
|
||||
count: Box::new(count)
|
||||
})));
|
||||
} else {
|
||||
if let Some(curr) = tokens.last() {
|
||||
lerror!(start.loc(), "Expected a , or ; as a separator in a literal array (normal, or repeating, respectively), but found {}", curr.tt());
|
||||
} else {
|
||||
lerror!(start.loc(), "Expected a , or ; as a separator in a literal array (normal, or repeating, respectively), but found nothing");
|
||||
}
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn parse_struct_literal(tokens: &mut Vec<Token>, name: Path) -> Result<LocBox<Expr>> {
|
||||
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyL), "")?;
|
||||
let mut fields = HashMap::new();
|
||||
while !tokens.is_empty() {
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
||||
break;
|
||||
}
|
||||
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?;
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
|
||||
let typ = parse_expr(tokens, 0, false)?.unwrap();
|
||||
fields.insert(name.tt().unwrap_ident(), typ);
|
||||
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyR), "")?;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(LocBox::new(start.loc(), Expr::Struct { path: name, fields }))
|
||||
}
|
||||
|
||||
fn parse_group(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
|
||||
let start = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
||||
let Some(expr) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(start.loc(), "Expected expr found nothing");
|
||||
bail!("")
|
||||
};
|
||||
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
|
||||
Ok(LocBox::new(start.loc(), Expr::Group(Box::new(expr))))
|
||||
}
|
||||
|
||||
fn parse_path(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
|
||||
let mut buf = Vec::new();
|
||||
let part = utils::check_consume(tokens, TokenType::ident("")).unwrap();
|
||||
|
||||
|
||||
buf.push(part.tt().unwrap_ident());
|
||||
while let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Pathaccess)) {
|
||||
let Some(part) = utils::check_consume(tokens, TokenType::ident("")) else {
|
||||
break;
|
||||
};
|
||||
buf.push(part.tt().unwrap_ident());
|
||||
}
|
||||
|
||||
Ok(LocBox::new(part.loc(), Expr::Path(Path(buf))))
|
||||
}
|
||||
|
||||
fn parse_unop(tokens: &mut Vec<Token>) -> Result<LocBox<Expr>> {
|
||||
let typ = utils::check_consume_or_err_from_many(tokens, &[
|
||||
TokenType::Punct(Punctuation::Not),
|
||||
TokenType::Punct(Punctuation::Plus),
|
||||
TokenType::Punct(Punctuation::Minus),
|
||||
TokenType::Punct(Punctuation::Ampersand),
|
||||
TokenType::Punct(Punctuation::Star),
|
||||
], "")?;
|
||||
let loc = typ.loc().clone();
|
||||
let TokenType::Punct(typ) = typ.tt().clone() else {unreachable!()};
|
||||
|
||||
let Some(right) = parse_expr(tokens, 5, false)? else {
|
||||
lerror!(&loc, "Expected expression after unary token, found nothing");
|
||||
bail!("")
|
||||
};
|
||||
Ok(LocBox::new(&loc, Expr::UnOp {
|
||||
typ,
|
||||
right: Box::new(right)
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_binop(tokens: &mut Vec<Token>, mut lhs: LocBox<Expr>, precedence: usize) -> Result<LocBox<Expr>> {
|
||||
// TODO: https://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudocode
|
||||
|
||||
loop {
|
||||
let op_loc;
|
||||
let op = match tokens.last() {
|
||||
Some(op) if BINOP_LIST.contains(&op.tt()) => {
|
||||
op_loc = op.loc().clone();
|
||||
let TokenType::Punct(op) = op.tt() else {unreachable!()};
|
||||
op.clone()
|
||||
}
|
||||
Some(op) if [
|
||||
TokenType::Delim(Delimiter::ParenR),
|
||||
TokenType::Punct(Punctuation::Semi)
|
||||
].contains(&op.tt()) => {
|
||||
break
|
||||
}
|
||||
Some(op) if matches!(&op.tt(), TokenType::Ident(_)) => {
|
||||
lerror!(op.loc(), "Unexpected identifier, did you forget a semicolon? ';'");
|
||||
bail!("");
|
||||
}
|
||||
Some(_) |
|
||||
None => break,
|
||||
};
|
||||
debug!("OP: {op:?}");
|
||||
let (lp, rp) = op.precedence().unwrap();
|
||||
if lp < precedence {
|
||||
break
|
||||
}
|
||||
|
||||
_ = tokens.pop();
|
||||
let Some(rhs) = parse_expr(tokens, rp, false)? else {break;};
|
||||
lhs = LocBox::new(&op_loc, Expr::BinOp {
|
||||
typ: op,
|
||||
left: Box::new(lhs),
|
||||
right: Box::new(rhs)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Ok(lhs)
|
||||
|
||||
}
|
||||
|
||||
pub fn parse_block(tokens: &mut Vec<Token>) -> Result<Block> {
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyL), "")?;
|
||||
let mut items = Vec::new();
|
||||
while !tokens.is_empty() {
|
||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
||||
break;
|
||||
}
|
||||
if let Some(item) = parse_item(tokens)? {
|
||||
items.push(item);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::CurlyR), "")?;
|
||||
Ok(Block(items))
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use ast::{expr::Block, Ast, Program};
|
||||
|
||||
use crate::tokeniser::{Token, tokentype::*};
|
||||
|
||||
pub mod ast;
|
||||
mod expr;
|
||||
mod stat;
|
||||
mod utils;
|
||||
pub mod typ;
|
||||
|
||||
type Result<T> = anyhow::Result<T>;
|
||||
|
||||
pub fn parse_program(mut tokens: Vec<Token>) -> Result<Program> {
|
||||
let mut prog_body = Vec::new();
|
||||
|
||||
while !tokens.is_empty() {
|
||||
if let Some(item) = parse_item(&mut tokens)? {
|
||||
prog_body.push(item);
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(Program {
|
||||
ast: Block(prog_body),
|
||||
enums: HashMap::new(),
|
||||
functions: HashMap::new(),
|
||||
member_functions: HashMap::new(),
|
||||
types: HashMap::new(),
|
||||
structs: HashMap::new()
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_item(tokens: &mut Vec<Token>) -> Result<Option<Ast>> {
|
||||
if let Some(stat) = stat::parse_statement(tokens)? {
|
||||
return Ok(Some(Ast::Statement(stat)));
|
||||
}
|
||||
if let Some(expr) = expr::parse_expr(tokens, 0, true)? {
|
||||
return Ok(Some(Ast::Expr(expr)));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,216 +0,0 @@
|
|||
use anyhow::bail;
|
||||
|
||||
use crate::common::loc::LocBox;
|
||||
use crate::lerror;
|
||||
use crate::parser::ast::TokenType;
|
||||
use crate::parser::expr::parse_expr;
|
||||
use crate::parser::{Delimiter, Ident, Keyword, Punctuation};
|
||||
use crate::tokeniser::Token;
|
||||
use super::ast::typ::Type;
|
||||
use super::expr::parse_block;
|
||||
use super::typ::parse_type;
|
||||
use super::utils;
|
||||
use super::ast::statement::{Enum, Function, Statement, Struct, TypeAlias};
|
||||
|
||||
type Result<T> = anyhow::Result<T>;
|
||||
|
||||
pub fn parse_statement(tokens: &mut Vec<Token>) -> Result<Option<LocBox<Statement>>> {
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Fn)) {
|
||||
Ok(Some(parse_fn(tokens)?))
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Type)) {
|
||||
Ok(Some(parse_type_alias(tokens)?))
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Const)) {
|
||||
Ok(Some(parse_constant(tokens)?))
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Static)) {
|
||||
Ok(Some(parse_static(tokens)?))
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Struct)) {
|
||||
Ok(Some(parse_struct(tokens)?))
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Enum)) {
|
||||
Ok(Some(parse_enum(tokens)?))
|
||||
} else
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Let)) {
|
||||
Ok(Some(parse_let(tokens)?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_enum(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Enum), "")?;
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
_ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyL));
|
||||
let mut fields = Vec::new();
|
||||
while !tokens.is_empty() {
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
||||
break;
|
||||
}
|
||||
let field_name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?;
|
||||
let loc = field_name.loc().clone();
|
||||
let field_name = field_name.tt().unwrap_ident();
|
||||
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
||||
if let None = utils::check(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
||||
lerror!(&loc, "Expected comma after struct field");
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
fields.push(field_name);
|
||||
}
|
||||
|
||||
Ok(LocBox::new(kw.loc(), Statement::Enum(Enum { name, fields })))
|
||||
}
|
||||
|
||||
fn parse_struct(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Struct), "")?;
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
_ = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyL));
|
||||
let mut fields = Vec::new();
|
||||
while !tokens.is_empty() {
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
||||
break;
|
||||
}
|
||||
let field_name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?;
|
||||
let loc = field_name.loc().clone();
|
||||
let field_name = field_name.tt().unwrap_ident();
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
|
||||
let typ = parse_type(tokens)?;
|
||||
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
||||
if let None = utils::check(tokens, TokenType::Delim(Delimiter::CurlyR)) {
|
||||
lerror!(&loc, "Expected comma after struct field");
|
||||
bail!("")
|
||||
}
|
||||
}
|
||||
fields.push((field_name, typ));
|
||||
}
|
||||
|
||||
Ok(LocBox::new(kw.loc(), Statement::Struct(Struct { name, fields })))
|
||||
}
|
||||
|
||||
fn parse_static(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Static), "")?;
|
||||
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
|
||||
let typ = parse_type(tokens)?;
|
||||
let eq = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?;
|
||||
let Some(val) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(eq.loc(), "Expected expression found nothing");
|
||||
bail!("")
|
||||
};
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
||||
Ok(LocBox::new(kw.loc(), Statement::StaticVar { name, typ, val }))
|
||||
}
|
||||
|
||||
fn parse_let(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Let), "")?;
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
let mut typ = None;
|
||||
let mut val = None;
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Colon)) {
|
||||
typ = Some(parse_type(tokens)?);
|
||||
}
|
||||
if let Some(eq) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Eq)) {
|
||||
let Some(_val) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(eq.loc(), "Expected expression found nothing");
|
||||
bail!("")
|
||||
};
|
||||
val = Some(_val);
|
||||
}
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
||||
Ok(LocBox::new(kw.loc(), Statement::Let { name, typ, val }))
|
||||
}
|
||||
fn parse_constant(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Const), "")?;
|
||||
|
||||
if let Some(_) = utils::check(tokens, TokenType::Keyword(Keyword::Fn)) {
|
||||
unimplemented!()
|
||||
}
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
|
||||
let typ = parse_type(tokens)?;
|
||||
let eq = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?;
|
||||
let Some(val) = parse_expr(tokens, 0, false)? else {
|
||||
lerror!(eq.loc(), "Expected expression found nothing");
|
||||
bail!("")
|
||||
};
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
||||
Ok(LocBox::new(kw.loc(), Statement::ConstVar { name, typ, val }))
|
||||
}
|
||||
|
||||
fn parse_type_alias(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Type), "")?;
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Eq), "")?;
|
||||
let typ = parse_type(tokens)?;
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
||||
|
||||
Ok(LocBox::new(kw.loc(), Statement::TypeAlias(TypeAlias { name, typ })))
|
||||
}
|
||||
|
||||
fn parse_fn(tokens: &mut Vec<Token>) -> Result<LocBox<Statement>> {
|
||||
// Just remove the kw since we checked it before
|
||||
let kw = utils::check_consume_or_err(tokens, TokenType::Keyword(Keyword::Fn), "")?;
|
||||
|
||||
let mut struct_name = None;
|
||||
let mut name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
// Check if this is a struct method
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Fieldaccess)) {
|
||||
struct_name = Some(name);
|
||||
name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?.tt().unwrap_ident();
|
||||
}
|
||||
let params = parse_fn_params(tokens)?;
|
||||
|
||||
// Check for return type cause it optional
|
||||
let mut ret_type = None;
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Arrow)) {
|
||||
ret_type = Some(parse_type(tokens)?);
|
||||
}
|
||||
let body;
|
||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::CurlyL)) {
|
||||
body = Some(parse_block(tokens)?);
|
||||
} else {
|
||||
// Check if its just a declaration
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Semi), "")?;
|
||||
body = None;
|
||||
}
|
||||
|
||||
Ok(LocBox::new(kw.loc(), Statement::Fn(Function{
|
||||
struct_name,
|
||||
name,
|
||||
params,
|
||||
ret_type,
|
||||
qual_const: false,
|
||||
qual_extern: None,
|
||||
body,
|
||||
})))
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn parse_fn_params(tokens: &mut Vec<Token>) -> Result<Vec<(Ident, LocBox<Type>)>> {
|
||||
let mut args = Vec::new();
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenL), "")?;
|
||||
while !tokens.is_empty() {
|
||||
if let Some(_) = utils::check(tokens, TokenType::Delim(Delimiter::ParenR)) {
|
||||
break;
|
||||
}
|
||||
let name = utils::check_consume_or_err(tokens, TokenType::ident(""), "")?;
|
||||
utils::check_consume_or_err(tokens, TokenType::Punct(Punctuation::Colon), "")?;
|
||||
//dbg!(&name);
|
||||
let typ = parse_type(tokens)?;
|
||||
args.push((name.tt().unwrap_ident(), typ));
|
||||
|
||||
if let None = utils::check_consume(tokens, TokenType::Punct(Punctuation::Comma)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::ParenR), "")?;
|
||||
Ok(args)
|
||||
}
|
||||
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
use anyhow::Result;
|
||||
|
||||
use crate::{common::loc::LocBox, parser::Delimiter, tokeniser::Token};
|
||||
|
||||
use super::{ast::{typ::Type, TokenType}, expr::parse_expr, utils, Keyword, Punctuation};
|
||||
|
||||
pub fn parse_type(tokens: &mut Vec<Token>) -> Result<LocBox<Type>> {
|
||||
let mut ref_cnt = Vec::new();
|
||||
let mut loc = None;
|
||||
while let Some(tok) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Ampersand)) {
|
||||
if let None = loc {
|
||||
loc = Some(tok.loc().clone());
|
||||
}
|
||||
if let Some(tok) = utils::check_consume(tokens, TokenType::Keyword(Keyword::Mut)) {
|
||||
ref_cnt.push(tok.clone());
|
||||
} else {
|
||||
ref_cnt.push(tok.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let mut typ;
|
||||
if let Some(start) = utils::check_consume(tokens, TokenType::Delim(super::Delimiter::SquareL)) {
|
||||
if let None = loc {
|
||||
loc = Some(start.loc().clone());
|
||||
}
|
||||
let itm_typ = parse_type(tokens)?;
|
||||
if let Some(_) = utils::check_consume(tokens, TokenType::Punct(Punctuation::Semi)) {
|
||||
let count = parse_expr(tokens, 0, false)?.unwrap();
|
||||
typ = Type::ArrayRepeat {
|
||||
inner: Box::new(itm_typ.inner().clone()),
|
||||
count
|
||||
}
|
||||
} else {
|
||||
typ = Type::Array {
|
||||
inner: Box::new(itm_typ.inner().clone()),
|
||||
}
|
||||
}
|
||||
_ = utils::check_consume_or_err(tokens, TokenType::Delim(Delimiter::SquareR), "")?;
|
||||
} else {
|
||||
let ident = utils::check_consume_or_err(tokens, TokenType::ident(""), "a")?;
|
||||
typ = Type::Owned(ident.tt().unwrap_ident());
|
||||
if let None = loc {
|
||||
loc = Some(ident.loc().clone());
|
||||
}
|
||||
}
|
||||
while let Some(reft) = ref_cnt.pop() {
|
||||
match reft.tt() {
|
||||
TokenType::Keyword(Keyword::Mut) => {
|
||||
typ = Type::Ref {
|
||||
inner: Box::new(typ),
|
||||
mutable: true
|
||||
}
|
||||
},
|
||||
TokenType::Punct(Punctuation::Ampersand) => {
|
||||
typ = Type::Ref {
|
||||
inner: Box::new(typ),
|
||||
mutable: false
|
||||
}
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
Ok(LocBox::new(&loc.unwrap(), typ))
|
||||
}
|
|
@ -1,129 +0,0 @@
|
|||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use crate::{common::Loc, debug, lerror, tokeniser::Token};
|
||||
|
||||
use super::ast::TokenType;
|
||||
|
||||
|
||||
lazy_static!(
|
||||
static ref LAST_LOC: Arc<Mutex<Loc>> = Arc::new(Mutex::new(Loc::default()));
|
||||
);
|
||||
|
||||
pub fn check(tokens: &Vec<Token>, tt: TokenType) -> Option<&Token> {
|
||||
if let Some(tkn) = tokens.last() {
|
||||
if tkn.tt() == &tt ||
|
||||
// ignore internal values if searching for these
|
||||
matches!((tkn.tt(), &tt), (TokenType::Ident(_), TokenType::Ident(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::String(_), TokenType::String(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::Number(_), TokenType::Number(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::Char(_), TokenType::Char(_)))
|
||||
{
|
||||
debug!("check: {}", tkn);
|
||||
return Some(tkn);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn check_2_last(tokens: &Vec<Token>, tt: TokenType) -> Option<&Token> {
|
||||
if tokens.len() < 2 {
|
||||
return None
|
||||
}
|
||||
if let Some(tkn) = tokens.get(tokens.len() - 2) {
|
||||
if tkn.tt() == &tt ||
|
||||
// ignore internal values if searching for these
|
||||
matches!((tkn.tt(), &tt), (TokenType::Ident(_), TokenType::Ident(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::String(_), TokenType::String(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::Number(_), TokenType::Number(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::Char(_), TokenType::Char(_)))
|
||||
{
|
||||
return Some(tkn);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn check_consume(tokens: &mut Vec<Token>, tt: TokenType) -> Option<Token> {
|
||||
if let Some(tkn) = tokens.last() {
|
||||
if tkn.tt() == &tt ||
|
||||
// ignore internal values if searching for these
|
||||
matches!((tkn.tt(), &tt), (TokenType::Ident(_), TokenType::Ident(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::String(_), TokenType::String(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::Number(_), TokenType::Number(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::Char(_), TokenType::Char(_)))
|
||||
{
|
||||
*LAST_LOC.lock().expect("Could not lock LAST_LOC") = tkn.loc().clone();
|
||||
debug!("check_consume: {}", tokens.last()?);
|
||||
return Some(tokens.pop()?);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn check_consume_or_err(tokens: &mut Vec<Token>, tt: TokenType, err_msg: &'static str) -> anyhow::Result<Token> {
|
||||
if let Some(tkn) = tokens.last() {
|
||||
if tkn.tt() == &tt ||
|
||||
// ignore internal values if searching for these
|
||||
matches!((tkn.tt(), &tt), (TokenType::Ident(_), TokenType::Ident(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::String(_), TokenType::String(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::Number(_), TokenType::Number(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::Char(_), TokenType::Char(_)))
|
||||
{
|
||||
*LAST_LOC.lock().expect("Could not lock LAST_LOC") = tkn.loc().clone();
|
||||
return Ok(tokens.pop().expect("Unreachable"));
|
||||
} else {
|
||||
lerror!(tkn.loc(), "Expected: '{tt}', got: '{}': {err_msg}", tkn.tt());
|
||||
//anyhow::bail!(format!("{}: ERROR: Expected: '{tt:?}', got: '{:?}': {err_msg}", tkn.loc(), tkn.tt()))
|
||||
anyhow::bail!("")
|
||||
}
|
||||
}
|
||||
let loc = LAST_LOC.lock().expect("Could not lock LAST_LOC");
|
||||
lerror!(&loc, "Expected: '{tt}', got: '(empty)': {err_msg}");
|
||||
// anyhow::bail!(format!("{loc}: ERROR: Expected '{tt:?}', got (empty): {err_msg}"))
|
||||
anyhow::bail!("")
|
||||
}
|
||||
|
||||
pub fn check_consume_from_many(tokens: &mut Vec<Token>, tts: &[TokenType]) -> Option<Token> {
|
||||
for tt in tts {
|
||||
if let Some(tkn) = check_consume(tokens, tt.clone()) {
|
||||
return Some(tkn);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn check_from_many<'a>(tokens: &'a mut Vec<Token>, tts: &[TokenType]) -> Option<&'a Token> {
|
||||
for tt in tts {
|
||||
if let Some(tkn) = check(tokens, tt.clone()) {
|
||||
return Some(tkn);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn check_consume_or_err_from_many(tokens: &mut Vec<Token>, tts: &[TokenType], err_msg: &'static str) -> anyhow::Result<Token> {
|
||||
if let Some(tkn) = tokens.last() {
|
||||
for tt in tts {
|
||||
if tkn.tt() == tt ||
|
||||
// ignore internal values if searching for these
|
||||
matches!((tkn.tt(), &tt), (TokenType::Ident(_), TokenType::Ident(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::String(_), TokenType::String(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::Number(_), TokenType::Number(_))) ||
|
||||
matches!((tkn.tt(), &tt), (TokenType::Char(_), TokenType::Char(_)))
|
||||
{
|
||||
*LAST_LOC.lock().expect("Could not lock LAST_LOC") = tkn.loc().clone();
|
||||
return Ok(tokens.pop().expect("Unreachable"));
|
||||
}
|
||||
}
|
||||
lerror!(tkn.loc(), "Expected: '{tts:?}', got: '{}': {err_msg}", tkn.tt());
|
||||
anyhow::bail!("")
|
||||
}
|
||||
let loc = LAST_LOC.lock().expect("Could not lock LAST_LOC");
|
||||
lerror!(&loc, "Expected: '{tts:?}', got: '(empty)': {err_msg}");
|
||||
anyhow::bail!("")
|
||||
}
|
||||
|
||||
pub fn get_last_loc() -> Loc {
|
||||
LAST_LOC.lock().expect("Could not lock LAST_LOC").clone()
|
||||
}
|
147
src/precompiler.rs
Normal file
147
src/precompiler.rs
Normal file
|
@ -0,0 +1,147 @@
|
|||
|
||||
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)
|
||||
}
|
754
src/preprocessor.rs
Normal file
754
src/preprocessor.rs
Normal file
|
@ -0,0 +1,754 @@
|
|||
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()
|
||||
}
|
||||
}
|
|
@ -1,301 +0,0 @@
|
|||
use std::{collections::HashMap, fmt::Display};
|
||||
use anyhow::bail;
|
||||
use parse_int::parse;
|
||||
use crate::{common::{loc::LocIncr, Loc}, error, lerror};
|
||||
|
||||
pub mod tokentype;
|
||||
use tokentype::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Token {
|
||||
loc: Loc,
|
||||
tt: TokenType,
|
||||
}
|
||||
|
||||
impl Display for Token {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}: {:?}", self.loc(), self.tt())
|
||||
}
|
||||
}
|
||||
|
||||
impl Token {
|
||||
fn new(tt: TokenType, loc: &Loc) -> Self {
|
||||
Self {
|
||||
tt, loc: loc.clone()
|
||||
}
|
||||
}
|
||||
pub fn loc(&self) -> &Loc {
|
||||
&self.loc
|
||||
}
|
||||
pub fn tt(&self) -> &TokenType {
|
||||
&self.tt
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn tokenise(s: &str, file_p: &str) -> anyhow::Result<Vec<Token>> {
|
||||
let mut loc = Loc::new(file_p, 1, 1);
|
||||
let mut tokens = Vec::new();
|
||||
let chars: Vec<_> = s.chars().collect();
|
||||
let mut chars = chars.iter().peekable();
|
||||
while let Some(c) = chars.next() {
|
||||
loc.inc_col();
|
||||
match c {
|
||||
' ' | '\t' => (),
|
||||
'/' if chars.peek() == Some(&&'/') => {
|
||||
let mut buf = String::new();
|
||||
chars.next();
|
||||
while let Some(c) = chars.next_if(|c| !matches!(c, '\n' | '\r')) {
|
||||
loc.inc_col();
|
||||
buf.push(*c);
|
||||
}
|
||||
// tokens.push(Token::new(TokenType::Comment(Comment::Line(buf.clone())), &loc));
|
||||
},
|
||||
'/' if chars.peek() == Some(&&'*') => {
|
||||
let mut buf = String::new();
|
||||
chars.next();
|
||||
while let Some(c) = chars.peek() {
|
||||
if matches!(c, '\n' | '\r') {
|
||||
loc.inc_line();
|
||||
} else {
|
||||
loc.inc_col();
|
||||
}
|
||||
let c = *chars.next().expect("Unreachable");
|
||||
if c == '*' && matches!(chars.peek(), Some(&&'/') | None) {
|
||||
chars.next();
|
||||
break;
|
||||
}
|
||||
buf.push(c);
|
||||
}
|
||||
// tokens.push(Token::new(TokenType::Comment(Comment::Line(buf.clone())), &loc));
|
||||
}
|
||||
'\n' => loc.inc_line(),
|
||||
'"' => {
|
||||
let mut last = '\0';
|
||||
let mut buf = String::new();
|
||||
while let Some(c) = chars.next_if(|v| **v != '\n') {
|
||||
loc.inc_col();
|
||||
if *c == '"' && last != '\\' {
|
||||
break;
|
||||
}
|
||||
buf.push(*c);
|
||||
last = *c;
|
||||
}
|
||||
tokens.push(Token::new(TokenType::string(&buf, false), &loc));
|
||||
}
|
||||
'\'' => {
|
||||
let mut last = '\0';
|
||||
let mut buf = String::new();
|
||||
while let Some(c) = chars.next_if(|v| **v != '\n') {
|
||||
loc.inc_col();
|
||||
if *c == '\'' && last != '\\' {
|
||||
break;
|
||||
}
|
||||
buf.push(*c);
|
||||
last = *c;
|
||||
}
|
||||
let buf = buf
|
||||
.replace("\\n", "\n")
|
||||
.replace("\\r", "\r");
|
||||
if buf.len() > 1 {
|
||||
lerror!(&loc, "Chars can only have 1 byte");
|
||||
bail!("")
|
||||
}
|
||||
tokens.push(Token::new(TokenType::char(buf.chars().nth(0).unwrap()), &loc));
|
||||
}
|
||||
'c' if chars.peek() == Some(&&'"') => {
|
||||
chars.next();
|
||||
let mut last = '\0';
|
||||
let mut buf = String::new();
|
||||
while let Some(c) = chars.next_if(|v| **v != '\n') {
|
||||
loc.inc_col();
|
||||
if *c == '"' && last != '\\' {
|
||||
break;
|
||||
}
|
||||
buf.push(*c);
|
||||
last = *c;
|
||||
}
|
||||
tokens.push(Token::new(TokenType::string(&buf, true), &loc));
|
||||
}
|
||||
'a'..='z' | 'A'..='Z' | '_' => {
|
||||
let mut buf = String::new();
|
||||
buf.push(*c);
|
||||
while let Some(c) = chars.next_if(|v| matches!(**v, 'a'..='z' | 'A'..='Z' | '_' | '0'..='9')) {
|
||||
loc.inc_col();
|
||||
buf.push(*c);
|
||||
}
|
||||
if let Some(kw) = TokenType::from_str(&buf) {
|
||||
tokens.push(Token::new(kw, &loc));
|
||||
continue;
|
||||
}
|
||||
tokens.push(Token::new(TokenType::ident(&buf), &loc));
|
||||
buf.clear();
|
||||
},
|
||||
|
||||
'+' | '-' | '0'..='9'
|
||||
// Checks if its a number an not an operator in disguise
|
||||
if matches!(c, '0'..='9') || matches!(chars.peek(), Some('0'..='9')) => {
|
||||
let mut buf = String::new();
|
||||
buf.push(*c);
|
||||
let signed = *c == '-';
|
||||
let mut radix = 10;
|
||||
match chars.peek() {
|
||||
Some(v) => {
|
||||
match v {
|
||||
'x' => radix = 16,
|
||||
'b' => radix = 2,
|
||||
'o' => radix = 8,
|
||||
_ => (),
|
||||
}
|
||||
|
||||
},
|
||||
None => {
|
||||
tokens.push(Token::new(TokenType::number(parse(&buf).unwrap(), radix, signed), &loc));
|
||||
}
|
||||
}
|
||||
while let Some(c) = chars.next_if(|v| matches!(**v, '0'..='9' | '.' | 'a'..='f' | 'A'..='F' | 'x' | 'o')) {
|
||||
loc.inc_col();
|
||||
buf.push(*c);
|
||||
}
|
||||
match radix {
|
||||
2 => {
|
||||
if buf.strip_prefix("0b").expect("Unreachable")
|
||||
.chars().filter(|v| !matches!(v, '0' | '1')).collect::<Vec<_>>().len() > 0 {
|
||||
lerror!(&loc, "Invalid character in binary number");
|
||||
bail!("")
|
||||
}
|
||||
tokens.push(Token::new(TokenType::number(parse(&buf).unwrap(), radix, signed), &loc));
|
||||
}
|
||||
8 => {
|
||||
if buf.strip_prefix("0o").expect("Unreachable")
|
||||
.chars().filter(|v| !matches!(v, '0'..='7')).collect::<Vec<_>>().len() > 0 {
|
||||
lerror!(&loc, "Invalid character in octal number");
|
||||
bail!("")
|
||||
}
|
||||
tokens.push(Token::new(TokenType::number(parse(&buf).unwrap(), radix, false), &loc));
|
||||
}
|
||||
10 => {
|
||||
if buf.chars().filter(|v| !matches!(v, '0'..='9' | '.')).collect::<Vec<_>>().len() > 0 {
|
||||
lerror!(&loc, "Invalid character in decimal number");
|
||||
bail!("")
|
||||
}
|
||||
if buf.contains(".") {
|
||||
if buf.chars().filter(|v| *v == '.').collect::<Vec<_>>().len() > 1 {
|
||||
lerror!(&loc, "Floats cant have more than 1 dot");
|
||||
}
|
||||
todo!()
|
||||
}
|
||||
tokens.push(Token::new(TokenType::number(parse(&buf).unwrap(), radix, signed), &loc));
|
||||
}
|
||||
16 => {
|
||||
if buf.strip_prefix("0x").expect("Unreachable")
|
||||
.chars().filter(|v| !matches!(v, '0'..='9' | 'a'..='f' | 'A'..='F')).collect::<Vec<_>>().len() > 0 {
|
||||
lerror!(&loc, "Invalid character in hex number");
|
||||
bail!("")
|
||||
}
|
||||
tokens.push(Token::new(TokenType::number(parse(&buf).unwrap(), radix, false), &loc));
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
|
||||
buf.clear();
|
||||
},
|
||||
|
||||
_ => {
|
||||
let mut buf = String::new();
|
||||
buf.push(*c);
|
||||
while let Some(c) = chars.peek() {
|
||||
if let None = TokenType::from_str(&format!("{buf}{c}")) {
|
||||
break;
|
||||
}
|
||||
if let Some(c) = chars.next() {
|
||||
buf.push(*c);
|
||||
}
|
||||
}
|
||||
if let Some(tt) = TokenType::from_str(&buf) {
|
||||
tokens.push(Token::new(tt, &loc));
|
||||
} else {
|
||||
lerror!(&loc, "Unknown token: {buf}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tokens.reverse();
|
||||
Ok(tokens)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Lookup table for all tokens, fast for normal tokenisation,
|
||||
// but slower for reveres lookup (for like error messages)
|
||||
lazy_static::lazy_static!(
|
||||
static ref TT: HashMap<&'static str, TokenType> = [
|
||||
("fn", TokenType::Keyword(Keyword::Fn)),
|
||||
("if", TokenType::Keyword(Keyword::If)),
|
||||
("else", TokenType::Keyword(Keyword::Else)),
|
||||
("struct", TokenType::Keyword(Keyword::Struct)),
|
||||
("enum", TokenType::Keyword(Keyword::Enum)),
|
||||
("type", TokenType::Keyword(Keyword::Type)),
|
||||
("while", TokenType::Keyword(Keyword::While)),
|
||||
("for", TokenType::Keyword(Keyword::For)),
|
||||
("break", TokenType::Keyword(Keyword::Break)),
|
||||
("continue", TokenType::Keyword(Keyword::Continue)),
|
||||
("let", TokenType::Keyword(Keyword::Let)),
|
||||
("const", TokenType::Keyword(Keyword::Const)),
|
||||
("mut", TokenType::Keyword(Keyword::Mut)),
|
||||
("static", TokenType::Keyword(Keyword::Static)),
|
||||
("true", TokenType::Keyword(Keyword::True)),
|
||||
("false", TokenType::Keyword(Keyword::False)),
|
||||
("include", TokenType::Keyword(Keyword::Include)),
|
||||
("extern", TokenType::Keyword(Keyword::Extern)),
|
||||
("return", TokenType::Keyword(Keyword::Return)),
|
||||
("loop", TokenType::Keyword(Keyword::Loop)),
|
||||
("as", TokenType::Keyword(Keyword::As)),
|
||||
("{", TokenType::Delim(Delimiter::CurlyL)),
|
||||
("}", TokenType::Delim(Delimiter::CurlyR)),
|
||||
("[", TokenType::Delim(Delimiter::SquareL)),
|
||||
("]", TokenType::Delim(Delimiter::SquareR)),
|
||||
("(", TokenType::Delim(Delimiter::ParenL)),
|
||||
(")", TokenType::Delim(Delimiter::ParenR)),
|
||||
(";", TokenType::Punct(Punctuation::Semi)),
|
||||
(":", TokenType::Punct(Punctuation::Colon)),
|
||||
("::", TokenType::Punct(Punctuation::Pathsep)),
|
||||
("->", TokenType::Punct(Punctuation::Arrow)),
|
||||
("=>", TokenType::Punct(Punctuation::FatArrow)),
|
||||
("+", TokenType::Punct(Punctuation::Plus)),
|
||||
("-", TokenType::Punct(Punctuation::Minus)),
|
||||
(",", TokenType::Punct(Punctuation::Comma)),
|
||||
("&", TokenType::Punct(Punctuation::Ampersand)),
|
||||
("*", TokenType::Punct(Punctuation::Star)),
|
||||
("!", TokenType::Punct(Punctuation::Not)),
|
||||
("/", TokenType::Punct(Punctuation::Div)),
|
||||
("%", TokenType::Punct(Punctuation::Mod)),
|
||||
("<<", TokenType::Punct(Punctuation::Shl)),
|
||||
(">>", TokenType::Punct(Punctuation::Shr)),
|
||||
("&&", TokenType::Punct(Punctuation::AndAnd)),
|
||||
("||", TokenType::Punct(Punctuation::OrOr)),
|
||||
("|", TokenType::Punct(Punctuation::Or)),
|
||||
(">", TokenType::Punct(Punctuation::Gt)),
|
||||
("<", TokenType::Punct(Punctuation::Lt)),
|
||||
(">=", TokenType::Punct(Punctuation::Ge)),
|
||||
("<=", TokenType::Punct(Punctuation::Le)),
|
||||
("^", TokenType::Punct(Punctuation::Xor)),
|
||||
("+=", TokenType::Punct(Punctuation::AddEq)),
|
||||
("-=", TokenType::Punct(Punctuation::SubEq)),
|
||||
("/=", TokenType::Punct(Punctuation::DivEq)),
|
||||
("*=", TokenType::Punct(Punctuation::MulEq)),
|
||||
("%=", TokenType::Punct(Punctuation::ModEq)),
|
||||
("<<=", TokenType::Punct(Punctuation::ShlEq)),
|
||||
(">>=", TokenType::Punct(Punctuation::ShrEq)),
|
||||
("&=", TokenType::Punct(Punctuation::AndEq)),
|
||||
("|=", TokenType::Punct(Punctuation::OrEq)),
|
||||
("^=", TokenType::Punct(Punctuation::XorEq)),
|
||||
("=", TokenType::Punct(Punctuation::Eq)),
|
||||
("==", TokenType::Punct(Punctuation::EqEq)),
|
||||
("!=", TokenType::Punct(Punctuation::Neq)),
|
||||
(".", TokenType::Punct(Punctuation::Fieldaccess)),
|
||||
("::", TokenType::Punct(Punctuation::Pathaccess)),
|
||||
].into();
|
||||
);
|
||||
|
||||
|
|
@ -1,215 +0,0 @@
|
|||
use core::panic;
|
||||
use std::fmt::Display;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Ident(pub String);
|
||||
|
||||
impl ToString for Ident {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Number {
|
||||
pub val: usize,
|
||||
pub base: u8,
|
||||
pub signed: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct TString {
|
||||
pub val: String,
|
||||
pub cstr: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Char(char);
|
||||
|
||||
impl Into<char> for Char {
|
||||
fn into(self) -> char {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<char> for Char {
|
||||
fn from(value: char) -> Self {
|
||||
Char(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Keyword {
|
||||
Fn, If, Else, Struct, Enum,
|
||||
Type, While, For, Break, Continue,
|
||||
Let, Const, Mut, Static,
|
||||
True, False, Include, Extern, Return,
|
||||
As, Loop
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Delimiter {
|
||||
CurlyL, CurlyR,
|
||||
SquareL, SquareR,
|
||||
ParenL, ParenR,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Punctuation {
|
||||
Semi, Colon, Pathsep, Comma,
|
||||
Arrow, FatArrow, Plus, Minus,
|
||||
Ampersand, Star, Div,
|
||||
Mod, Shl, Shr, AndAnd,
|
||||
OrOr, Or, Xor, Not,
|
||||
AddEq, SubEq,
|
||||
DivEq, MulEq,
|
||||
ModEq, ShlEq,
|
||||
ShrEq, AndEq,
|
||||
OrEq, XorEq,
|
||||
Eq, EqEq, Fieldaccess,
|
||||
Pathaccess, Lt, Gt, Le, Ge, Neq
|
||||
|
||||
}
|
||||
|
||||
impl Punctuation {
|
||||
|
||||
// pls help
|
||||
pub fn precedence(&self) -> Option<(usize, usize)> {
|
||||
match self {
|
||||
Punctuation::AddEq |
|
||||
Punctuation::SubEq |
|
||||
Punctuation::DivEq |
|
||||
Punctuation::MulEq |
|
||||
Punctuation::ModEq |
|
||||
Punctuation::ShlEq |
|
||||
Punctuation::ShrEq |
|
||||
Punctuation::AndEq |
|
||||
Punctuation::OrEq |
|
||||
Punctuation::XorEq |
|
||||
Punctuation::Eq => Some((1, 2)),
|
||||
|
||||
Punctuation::EqEq |
|
||||
Punctuation::Neq => Some((3, 4)),
|
||||
|
||||
Punctuation::Div |
|
||||
Punctuation::Star |
|
||||
Punctuation::Mod => Some((5,6)),
|
||||
|
||||
Punctuation::Plus |
|
||||
Punctuation::Minus => Some((7,8)),
|
||||
|
||||
Punctuation::Shl |
|
||||
Punctuation::Shr => Some((9,10)),
|
||||
|
||||
Punctuation::Lt |
|
||||
Punctuation::Gt |
|
||||
Punctuation::Le |
|
||||
Punctuation::Ge => Some((11, 12)),
|
||||
|
||||
Punctuation::Ampersand => Some((13, 14)),
|
||||
Punctuation::Xor => Some((15, 16)),
|
||||
Punctuation::Or => Some((17, 18)),
|
||||
Punctuation::AndAnd => Some((19, 20)),
|
||||
Punctuation::OrOr => Some((21, 22)),
|
||||
|
||||
_ => None
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd, Ord, Eq)]
|
||||
pub enum TokenType {
|
||||
Ident(Ident),
|
||||
Number(Number),
|
||||
String(TString),
|
||||
Char(Char),
|
||||
Keyword(Keyword),
|
||||
Delim(Delimiter),
|
||||
Punct(Punctuation),
|
||||
Comment(Comment),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, PartialOrd, Ord, Eq)]
|
||||
pub enum Comment {
|
||||
Line(String),
|
||||
Block(String)
|
||||
}
|
||||
|
||||
impl TokenType {
|
||||
pub fn unwrap_ident(&self) -> Ident {
|
||||
match self {
|
||||
Self::Ident(i) => i.clone(),
|
||||
_ => panic!("Expected {}, got {self}", Self::ident(""))
|
||||
}
|
||||
}
|
||||
pub fn ident(s: &str) -> Self {
|
||||
Self::Ident(Ident(s.to_string()))
|
||||
}
|
||||
pub fn number(val: usize, base: u8, signed: bool) -> Self {
|
||||
Self::Number(Number { val, base, signed })
|
||||
}
|
||||
pub fn string(s: &str, cstr: bool) -> Self{
|
||||
Self::String(TString { val: s.to_string(), cstr })
|
||||
}
|
||||
pub fn char(v: char) -> Self {
|
||||
Self::Char(Char(v))
|
||||
}
|
||||
pub fn from_str(s: &str) -> Option<Self> {
|
||||
super::TT.get(s).cloned()
|
||||
}
|
||||
pub fn to_str(&self) -> String {
|
||||
for (k, v) in super::TT.iter() {
|
||||
if v == self {
|
||||
return k.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
match self {
|
||||
TokenType::Ident(s) => {
|
||||
return format!("Ident(\"{}\")", s.to_string());
|
||||
},
|
||||
TokenType::Number(num) => {
|
||||
match num.base {
|
||||
2 => {
|
||||
assert!(!num.signed, "base 2 (binary) numbers physically cannot be signed");
|
||||
format!("{:#b}", num.val)
|
||||
}
|
||||
8 => {
|
||||
assert!(!num.signed, "base 8 (octal) numbers physically cannot be signed");
|
||||
format!("{:#o}", num.val)
|
||||
}
|
||||
10 => {
|
||||
if num.signed {
|
||||
format!("{}", num.val as isize)
|
||||
} else {
|
||||
format!("{}", num.val)
|
||||
}
|
||||
}
|
||||
16 => {
|
||||
assert!(!num.signed, "base 16 (hex) numbers physically cannot be signed");
|
||||
format!("{:#x}", num.val)
|
||||
}
|
||||
_ => panic!("Invalid base for number, {}", num.base),
|
||||
}
|
||||
},
|
||||
TokenType::String(s) => {
|
||||
if s.cstr {
|
||||
format!("\"{}\\0\"", s.val)
|
||||
} else {
|
||||
format!("\"{}\"", s.val)
|
||||
}
|
||||
},
|
||||
TokenType::Char(c) => {
|
||||
format!("'{}'", c.0)
|
||||
}
|
||||
_ => unreachable!("Unreachable, did you add a new token and forget to add reverse lookup?"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for TokenType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.to_str())
|
||||
}
|
||||
}
|
408
src/typechecker.rs
Normal file
408
src/typechecker.rs
Normal file
|
@ -0,0 +1,408 @@
|
|||
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)
|
||||
}
|
392
src/types.rs
Normal file
392
src/types.rs
Normal file
|
@ -0,0 +1,392 @@
|
|||
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
|
||||
}
|
29
src/types/common/loc.rs
Normal file
29
src/types/common/loc.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
4
src/types/common/mod.rs
Normal file
4
src/types/common/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
mod loc;
|
||||
|
||||
|
||||
pub use loc::*;
|
4
src/types/mod.rs
Normal file
4
src/types/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
|
||||
pub mod common;
|
||||
pub mod token;
|
65
src/types/token.rs
Normal file
65
src/types/token.rs
Normal file
|
@ -0,0 +1,65 @@
|
|||
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
|
||||
}
|
117
src/util.rs
Normal file
117
src/util.rs
Normal file
|
@ -0,0 +1,117 @@
|
|||
// 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(())
|
||||
|
||||
// }
|
||||
// }
|
|
@ -1,74 +0,0 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::{common::loc::LocBox, parser::ast::{expr::Block, statement::{Statement, TypeAlias}, Ast, Program}};
|
||||
|
||||
pub mod predefined;
|
||||
|
||||
pub fn validate_code(prog: &mut Program) -> anyhow::Result<()> {
|
||||
let Block(items) = prog.ast.clone();
|
||||
predefined::load_builtin(prog);
|
||||
collect_types(prog, &items);
|
||||
//dbg!(&prog.types);
|
||||
//dbg!(&prog.structs);
|
||||
//dbg!(&prog.enums);
|
||||
//dbg!(&prog.member_functions);
|
||||
//dbg!(&prog.functions);
|
||||
for item in items {
|
||||
match item {
|
||||
Ast::Statement(stat) => {
|
||||
match stat.inner() {
|
||||
Statement::Fn(func) => {}
|
||||
Statement::Let { name, typ, val } => {}
|
||||
Statement::ConstVar { name, typ, val } => {}
|
||||
Statement::StaticVar { name, typ, val } => {}
|
||||
Statement::Enum(enm) => {}
|
||||
Statement::Struct(strct) => {}
|
||||
Statement::TypeAlias(alias) => {}
|
||||
}
|
||||
}
|
||||
Ast::Expr(_) => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn collect_types(prog: &mut Program, items: &Vec<Ast>) {
|
||||
for item in items {
|
||||
match item {
|
||||
Ast::Statement(stat) => {
|
||||
match stat.inner() {
|
||||
Statement::Fn(func)=> {
|
||||
if let Some(struct_name) = &func.struct_name {
|
||||
if let Some(v) = prog.member_functions.get_mut(&struct_name) {
|
||||
v.insert(func.name.clone(), LocBox::new(stat.loc(), func.clone()));
|
||||
} else {
|
||||
let mut v = HashMap::new();
|
||||
v.insert(func.name.clone(), LocBox::new(stat.loc(), func.clone()));
|
||||
prog.member_functions.insert(struct_name.clone(), v);
|
||||
}
|
||||
} else {
|
||||
prog.functions.insert(func.name.clone(), LocBox::new(stat.loc(), func.clone()));
|
||||
}
|
||||
}
|
||||
Statement::Enum(enm) => {
|
||||
prog.enums.insert(enm.name.clone(), LocBox::new(stat.loc(), enm.clone()));
|
||||
}
|
||||
Statement::Struct(strct) => {
|
||||
prog.structs.insert(strct.name.clone(), LocBox::new(stat.loc(), strct.clone()));
|
||||
}
|
||||
Statement::TypeAlias(alias) => {
|
||||
let typ = alias.clone().typ.inner().clone();
|
||||
prog.types.insert(alias.name.clone(), predefined::TypeType::Normal(LocBox::new(stat.loc(), typ)));
|
||||
}
|
||||
Statement::Let { .. } |
|
||||
Statement::ConstVar { .. } |
|
||||
Statement::StaticVar { .. } => (),
|
||||
}
|
||||
}
|
||||
Ast::Expr(_) => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
use std::collections::HashMap;
|
||||
use lazy_static::lazy_static;
|
||||
use crate::common::Loc;
|
||||
use crate::parser::typ::parse_type;
|
||||
use crate::{common::loc::LocBox, parser::ast::{statement::Function, typ::Type, Ident, Program}};
|
||||
|
||||
#[cfg(target_arch="x86_64")]
|
||||
const SIZE: usize = 8;
|
||||
#[cfg(target_arch="x86")]
|
||||
const SIZE: usize = 4;
|
||||
|
||||
lazy_static!(
|
||||
pub static ref TYPES_RAW: HashMap<&'static str, usize> = [
|
||||
("void", 0),
|
||||
("usize", SIZE),
|
||||
("isize", SIZE),
|
||||
("u8", 1),
|
||||
("u16", 2),
|
||||
("u32", 4),
|
||||
("u64", 8),
|
||||
("i8", 1),
|
||||
("i16", 2),
|
||||
("i32", 4),
|
||||
("i64", 8),
|
||||
].into();
|
||||
pub static ref FUNCTIONS: HashMap<&'static str, (Vec<(&'static str, &'static str)>, &'static str)> = [
|
||||
("syscall", (vec![
|
||||
("arg_count", "&u8"),
|
||||
("sc_num", "usize"),
|
||||
("args", "&[&void]")
|
||||
], "usize")),
|
||||
].into();
|
||||
);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TypeType {
|
||||
Normal(LocBox<Type>),
|
||||
Builtin(usize),
|
||||
}
|
||||
|
||||
pub fn load_builtin(prog: &mut Program) {
|
||||
for (name, size) in TYPES_RAW.iter() {
|
||||
prog.types.insert(Ident(name.to_string()), TypeType::Builtin(*size));
|
||||
}
|
||||
|
||||
for (name, (args, ret_typ)) in FUNCTIONS.iter() {
|
||||
let mut params = Vec::new();
|
||||
let mut ret_type = None;
|
||||
if ret_typ.len() > 0 {
|
||||
let mut ret_t_tokens = crate::tokeniser::tokenise(&ret_typ, "(internal)").unwrap();
|
||||
let typ = parse_type(&mut ret_t_tokens).unwrap();
|
||||
ret_type = Some(LocBox::new(&Loc::default(), typ.inner().clone()));
|
||||
}
|
||||
for (name, typ) in args {
|
||||
let mut tokens = crate::tokeniser::tokenise(&typ, "(internal)").unwrap();
|
||||
let typ = parse_type(&mut tokens).unwrap();
|
||||
params.push((Ident(name.to_string()), LocBox::new(&Loc::new("(internal)", 0, 0), typ.inner().clone())));
|
||||
}
|
||||
|
||||
let f = Function {
|
||||
struct_name: None,
|
||||
name: Ident(name.to_string()),
|
||||
params,
|
||||
ret_type,
|
||||
qual_const: false,
|
||||
qual_extern: None,
|
||||
body: None
|
||||
};
|
||||
prog.functions.insert(Ident(name.to_string()), LocBox::new(&Loc::new("(internal)", 0, 0), f));
|
||||
}
|
||||
}
|
30
test.mcl
30
test.mcl
|
@ -1,21 +1,13 @@
|
|||
//type str = [u8];
|
||||
//
|
||||
//struct Foo {
|
||||
// a: usize,
|
||||
// b: &str
|
||||
//}
|
||||
//
|
||||
//fn Foo.new(a: usize, b: &str) -> Foo {
|
||||
// return Foo {
|
||||
// a: a,
|
||||
// b: b
|
||||
// };
|
||||
//}
|
||||
//
|
||||
//
|
||||
//fn main() {
|
||||
// let obj = Foo::new();
|
||||
//
|
||||
//}
|
||||
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
|
||||
done
|
1
tests/fail_unknown_word.mcl
Normal file
1
tests/fail_unknown_word.mcl
Normal file
|
@ -0,0 +1 @@
|
|||
gftdesd5ryutfgyhibugtf6r4
|
7
tests/math.mcl
Normal file
7
tests/math.mcl
Normal file
|
@ -0,0 +1,7 @@
|
|||
34 35 + print
|
||||
|
||||
800 380 - print
|
||||
|
||||
10 5 * print
|
||||
|
||||
40 5 / print
|
|
@ -1,55 +0,0 @@
|
|||
Program {
|
||||
ast: Block(
|
||||
[
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Enum(
|
||||
Enum {
|
||||
name: Ident(
|
||||
"Foo",
|
||||
),
|
||||
fields: [],
|
||||
},
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/enumerations.mcl",
|
||||
line: 1,
|
||||
col: 5,
|
||||
},
|
||||
},
|
||||
),
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Enum(
|
||||
Enum {
|
||||
name: Ident(
|
||||
"Bar",
|
||||
),
|
||||
fields: [
|
||||
Ident(
|
||||
"A",
|
||||
),
|
||||
Ident(
|
||||
"B",
|
||||
),
|
||||
Ident(
|
||||
"C",
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/enumerations.mcl",
|
||||
line: 3,
|
||||
col: 5,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
structs: {},
|
||||
enums: {},
|
||||
types: {},
|
||||
functions: {},
|
||||
member_functions: {},
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
enum Foo {}
|
||||
|
||||
enum Bar {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
}
|
|
@ -1,908 +0,0 @@
|
|||
Program {
|
||||
ast: Block(
|
||||
[
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Let {
|
||||
name: Ident(
|
||||
"a",
|
||||
),
|
||||
typ: None,
|
||||
val: Some(
|
||||
LocBox {
|
||||
inner: BinOp {
|
||||
typ: EqEq,
|
||||
left: LocBox {
|
||||
inner: BinOp {
|
||||
typ: Star,
|
||||
left: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 1,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 1,
|
||||
col: 10,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 3,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 1,
|
||||
col: 14,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 1,
|
||||
col: 12,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 4,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 1,
|
||||
col: 18,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 1,
|
||||
col: 16,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 1,
|
||||
col: 4,
|
||||
},
|
||||
},
|
||||
),
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Let {
|
||||
name: Ident(
|
||||
"b",
|
||||
),
|
||||
typ: None,
|
||||
val: Some(
|
||||
LocBox {
|
||||
inner: BinOp {
|
||||
typ: EqEq,
|
||||
left: LocBox {
|
||||
inner: BinOp {
|
||||
typ: Div,
|
||||
left: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 3,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 2,
|
||||
col: 10,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 4,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 2,
|
||||
col: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 2,
|
||||
col: 11,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: UnOp {
|
||||
typ: Star,
|
||||
right: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"a",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 2,
|
||||
col: 17,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 2,
|
||||
col: 16,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 2,
|
||||
col: 14,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 2,
|
||||
col: 4,
|
||||
},
|
||||
},
|
||||
),
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Let {
|
||||
name: Ident(
|
||||
"c",
|
||||
),
|
||||
typ: None,
|
||||
val: Some(
|
||||
LocBox {
|
||||
inner: BinOp {
|
||||
typ: Div,
|
||||
left: LocBox {
|
||||
inner: Group(
|
||||
LocBox {
|
||||
inner: PtrFieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"a",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 11,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: FieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"b",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 13,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: PtrFieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"c",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 15,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"d",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 17,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 16,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 14,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 12,
|
||||
},
|
||||
},
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 10,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 2,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 22,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 20,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 3,
|
||||
col: 4,
|
||||
},
|
||||
},
|
||||
),
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Let {
|
||||
name: Ident(
|
||||
"d",
|
||||
),
|
||||
typ: None,
|
||||
val: Some(
|
||||
LocBox {
|
||||
inner: BinOp {
|
||||
typ: Div,
|
||||
left: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 2,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 4,
|
||||
col: 10,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: PtrFieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"a",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 4,
|
||||
col: 14,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: FieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"b",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 4,
|
||||
col: 16,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: PtrFieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"c",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 4,
|
||||
col: 18,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"d",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 4,
|
||||
col: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 4,
|
||||
col: 19,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 4,
|
||||
col: 17,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 4,
|
||||
col: 15,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 4,
|
||||
col: 12,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 4,
|
||||
col: 4,
|
||||
},
|
||||
},
|
||||
),
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Let {
|
||||
name: Ident(
|
||||
"e",
|
||||
),
|
||||
typ: None,
|
||||
val: Some(
|
||||
LocBox {
|
||||
inner: BinOp {
|
||||
typ: Div,
|
||||
left: LocBox {
|
||||
inner: PtrFieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"a",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 5,
|
||||
col: 10,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: FieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"b",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 5,
|
||||
col: 12,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: PtrFieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"c",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 5,
|
||||
col: 14,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"d",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 5,
|
||||
col: 16,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 5,
|
||||
col: 15,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 5,
|
||||
col: 13,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 5,
|
||||
col: 11,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 2,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 5,
|
||||
col: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 5,
|
||||
col: 18,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 5,
|
||||
col: 4,
|
||||
},
|
||||
},
|
||||
),
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Let {
|
||||
name: Ident(
|
||||
"f",
|
||||
),
|
||||
typ: None,
|
||||
val: Some(
|
||||
LocBox {
|
||||
inner: BinOp {
|
||||
typ: Div,
|
||||
left: LocBox {
|
||||
inner: FieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"a",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 6,
|
||||
col: 10,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: FieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"b",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 6,
|
||||
col: 12,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: FieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"c",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 6,
|
||||
col: 14,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"d",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 6,
|
||||
col: 16,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 6,
|
||||
col: 15,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 6,
|
||||
col: 13,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 6,
|
||||
col: 11,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 2,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 6,
|
||||
col: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 6,
|
||||
col: 18,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 6,
|
||||
col: 4,
|
||||
},
|
||||
},
|
||||
),
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Let {
|
||||
name: Ident(
|
||||
"g",
|
||||
),
|
||||
typ: None,
|
||||
val: Some(
|
||||
LocBox {
|
||||
inner: BinOp {
|
||||
typ: Star,
|
||||
left: LocBox {
|
||||
inner: ArrayIndex {
|
||||
name: LocBox {
|
||||
inner: FieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"a",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 10,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"b",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 11,
|
||||
},
|
||||
},
|
||||
index: LocBox {
|
||||
inner: UnOp {
|
||||
typ: Star,
|
||||
right: LocBox {
|
||||
inner: FieldAccess {
|
||||
left: Some(
|
||||
LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"a",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 15,
|
||||
},
|
||||
},
|
||||
),
|
||||
right: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"c",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 17,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 16,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 14,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 13,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 5,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 22,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 20,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/expressions.mcl",
|
||||
line: 7,
|
||||
col: 4,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
structs: {},
|
||||
enums: {},
|
||||
types: {},
|
||||
functions: {},
|
||||
member_functions: {},
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
let a = 1 * 3 == 4;
|
||||
let b = 3/4 == *a;
|
||||
let c = (a->b.c->d) / 2;
|
||||
let d = 2 / a->b.c->d;
|
||||
let e = a->b.c->d / 2;
|
||||
let f = a.b.c.d / 2;
|
||||
let g = a.b[*a.c] * 5;
|
||||
|
|
@ -1,306 +0,0 @@
|
|||
Program {
|
||||
ast: Block(
|
||||
[
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Fn(
|
||||
Function {
|
||||
struct_name: None,
|
||||
name: Ident(
|
||||
"main",
|
||||
),
|
||||
params: [
|
||||
(
|
||||
Ident(
|
||||
"argc",
|
||||
),
|
||||
LocBox {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"i32",
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 1,
|
||||
col: 18,
|
||||
},
|
||||
},
|
||||
),
|
||||
(
|
||||
Ident(
|
||||
"argv",
|
||||
),
|
||||
LocBox {
|
||||
inner: Ref {
|
||||
inner: Array {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"Str",
|
||||
),
|
||||
),
|
||||
},
|
||||
mutable: false,
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 1,
|
||||
col: 27,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
ret_type: Some(
|
||||
LocBox {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"i32",
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 1,
|
||||
col: 39,
|
||||
},
|
||||
},
|
||||
),
|
||||
qual_const: false,
|
||||
qual_extern: None,
|
||||
body: Some(
|
||||
Block(
|
||||
[
|
||||
Expr(
|
||||
LocBox {
|
||||
inner: Return(
|
||||
Some(
|
||||
LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 0,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 2,
|
||||
col: 13,
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 2,
|
||||
col: 11,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 1,
|
||||
col: 3,
|
||||
},
|
||||
},
|
||||
),
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Fn(
|
||||
Function {
|
||||
struct_name: Some(
|
||||
Ident(
|
||||
"Baz",
|
||||
),
|
||||
),
|
||||
name: Ident(
|
||||
"main",
|
||||
),
|
||||
params: [
|
||||
(
|
||||
Ident(
|
||||
"self",
|
||||
),
|
||||
LocBox {
|
||||
inner: Ref {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"Baz",
|
||||
),
|
||||
),
|
||||
mutable: true,
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 4,
|
||||
col: 20,
|
||||
},
|
||||
},
|
||||
),
|
||||
(
|
||||
Ident(
|
||||
"a",
|
||||
),
|
||||
LocBox {
|
||||
inner: Ref {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"Foo",
|
||||
),
|
||||
),
|
||||
mutable: false,
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 4,
|
||||
col: 33,
|
||||
},
|
||||
},
|
||||
),
|
||||
(
|
||||
Ident(
|
||||
"b",
|
||||
),
|
||||
LocBox {
|
||||
inner: Ref {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"Bar",
|
||||
),
|
||||
),
|
||||
mutable: true,
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 4,
|
||||
col: 42,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
ret_type: Some(
|
||||
LocBox {
|
||||
inner: Ref {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"Nya",
|
||||
),
|
||||
),
|
||||
mutable: false,
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 4,
|
||||
col: 54,
|
||||
},
|
||||
},
|
||||
),
|
||||
qual_const: false,
|
||||
qual_extern: None,
|
||||
body: None,
|
||||
},
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 4,
|
||||
col: 3,
|
||||
},
|
||||
},
|
||||
),
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Fn(
|
||||
Function {
|
||||
struct_name: Some(
|
||||
Ident(
|
||||
"Baz",
|
||||
),
|
||||
),
|
||||
name: Ident(
|
||||
"main",
|
||||
),
|
||||
params: [
|
||||
(
|
||||
Ident(
|
||||
"a",
|
||||
),
|
||||
LocBox {
|
||||
inner: Ref {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"Foo",
|
||||
),
|
||||
),
|
||||
mutable: false,
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 5,
|
||||
col: 17,
|
||||
},
|
||||
},
|
||||
),
|
||||
(
|
||||
Ident(
|
||||
"b",
|
||||
),
|
||||
LocBox {
|
||||
inner: Ref {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"Bar",
|
||||
),
|
||||
),
|
||||
mutable: true,
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 5,
|
||||
col: 26,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
ret_type: Some(
|
||||
LocBox {
|
||||
inner: Ref {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"Nya",
|
||||
),
|
||||
),
|
||||
mutable: false,
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 5,
|
||||
col: 38,
|
||||
},
|
||||
},
|
||||
),
|
||||
qual_const: false,
|
||||
qual_extern: None,
|
||||
body: None,
|
||||
},
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/functions.mcl",
|
||||
line: 5,
|
||||
col: 3,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
structs: {},
|
||||
enums: {},
|
||||
types: {},
|
||||
functions: {},
|
||||
member_functions: {},
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
fn main(argc: i32, argv: &[Str]) -> i32 {
|
||||
return 0;
|
||||
}
|
||||
fn Baz.main(self: &mut Baz, a: &Foo, b: &mut Bar) -> &Nya;
|
||||
fn Baz.main(a: &Foo, b: &mut Bar) -> &Nya;
|
|
@ -1,70 +0,0 @@
|
|||
Program {
|
||||
ast: Block(
|
||||
[
|
||||
Expr(
|
||||
LocBox {
|
||||
inner: If(
|
||||
IfExpr {
|
||||
test: LocBox {
|
||||
inner: BinOp {
|
||||
typ: Gt,
|
||||
left: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"i",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/if-statements.mcl",
|
||||
line: 2,
|
||||
col: 5,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 3,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/if-statements.mcl",
|
||||
line: 2,
|
||||
col: 9,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/if-statements.mcl",
|
||||
line: 2,
|
||||
col: 7,
|
||||
},
|
||||
},
|
||||
body: Block(
|
||||
[],
|
||||
),
|
||||
else_if: None,
|
||||
},
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/if-statements.mcl",
|
||||
line: 2,
|
||||
col: 3,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
structs: {},
|
||||
enums: {},
|
||||
types: {},
|
||||
functions: {},
|
||||
member_functions: {},
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
|
||||
if i > 3 {
|
||||
|
||||
}
|
||||
|
||||
if 1 {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
if 1 {
|
||||
|
||||
} else if a > 3 {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
|
@ -1,212 +0,0 @@
|
|||
Program {
|
||||
ast: Block(
|
||||
[
|
||||
Expr(
|
||||
LocBox {
|
||||
inner: ForLoop {
|
||||
init: Statement(
|
||||
LocBox {
|
||||
inner: Let {
|
||||
name: Ident(
|
||||
"i",
|
||||
),
|
||||
typ: None,
|
||||
val: Some(
|
||||
LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 0,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 1,
|
||||
col: 14,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 1,
|
||||
col: 8,
|
||||
},
|
||||
},
|
||||
),
|
||||
test: LocBox {
|
||||
inner: BinOp {
|
||||
typ: Lt,
|
||||
left: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"i",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 1,
|
||||
col: 17,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 10,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 1,
|
||||
col: 22,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 1,
|
||||
col: 19,
|
||||
},
|
||||
},
|
||||
on_loop: LocBox {
|
||||
inner: BinOp {
|
||||
typ: AddEq,
|
||||
left: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"i",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 1,
|
||||
col: 25,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 1,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 1,
|
||||
col: 29,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 1,
|
||||
col: 27,
|
||||
},
|
||||
},
|
||||
body: Block(
|
||||
[],
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 1,
|
||||
col: 4,
|
||||
},
|
||||
},
|
||||
),
|
||||
Expr(
|
||||
LocBox {
|
||||
inner: WhileLoop {
|
||||
test: LocBox {
|
||||
inner: BinOp {
|
||||
typ: Gt,
|
||||
left: LocBox {
|
||||
inner: Path(
|
||||
Path(
|
||||
[
|
||||
Ident(
|
||||
"i",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 2,
|
||||
col: 8,
|
||||
},
|
||||
},
|
||||
right: LocBox {
|
||||
inner: Literal(
|
||||
Number(
|
||||
Number {
|
||||
val: 3,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 2,
|
||||
col: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 2,
|
||||
col: 10,
|
||||
},
|
||||
},
|
||||
body: Block(
|
||||
[],
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 2,
|
||||
col: 6,
|
||||
},
|
||||
},
|
||||
),
|
||||
Expr(
|
||||
LocBox {
|
||||
inner: InfLoop {
|
||||
body: Block(
|
||||
[],
|
||||
),
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/loops.mcl",
|
||||
line: 3,
|
||||
col: 5,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
structs: {},
|
||||
enums: {},
|
||||
types: {},
|
||||
functions: {},
|
||||
member_functions: {},
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
for let i = 0; i < 10; i += 1 {}
|
||||
while i > 3 {}
|
||||
loop {}
|
|
@ -1,66 +0,0 @@
|
|||
Program {
|
||||
ast: Block(
|
||||
[
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Struct(
|
||||
Struct {
|
||||
name: Ident(
|
||||
"foo_t",
|
||||
),
|
||||
fields: [],
|
||||
},
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/structs.mcl",
|
||||
line: 2,
|
||||
col: 7,
|
||||
},
|
||||
},
|
||||
),
|
||||
Statement(
|
||||
LocBox {
|
||||
inner: Struct(
|
||||
Struct {
|
||||
name: Ident(
|
||||
"bar_t",
|
||||
),
|
||||
fields: [
|
||||
(
|
||||
Ident(
|
||||
"a",
|
||||
),
|
||||
LocBox {
|
||||
inner: Ref {
|
||||
inner: Owned(
|
||||
Ident(
|
||||
"bar_t",
|
||||
),
|
||||
),
|
||||
mutable: false,
|
||||
},
|
||||
loc: Loc {
|
||||
file: "parser/structs.mcl",
|
||||
line: 5,
|
||||
col: 9,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
loc: Loc {
|
||||
file: "parser/structs.mcl",
|
||||
line: 4,
|
||||
col: 7,
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
structs: {},
|
||||
enums: {},
|
||||
types: {},
|
||||
functions: {},
|
||||
member_functions: {},
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
|
||||
struct foo_t {}
|
||||
|
||||
struct bar_t {
|
||||
a: &bar_t
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
[]
|
|
@ -1,7 +0,0 @@
|
|||
|
||||
// Hello, this is a single line comment
|
||||
|
||||
/*
|
||||
And this is a multiline comment, which is
|
||||
useful for longer documentation
|
||||
*/
|
|
@ -1,62 +0,0 @@
|
|||
[
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/delimiters.mcl",
|
||||
line: 3,
|
||||
col: 4,
|
||||
},
|
||||
tt: Delim(
|
||||
ParenR,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/delimiters.mcl",
|
||||
line: 3,
|
||||
col: 2,
|
||||
},
|
||||
tt: Delim(
|
||||
ParenL,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/delimiters.mcl",
|
||||
line: 2,
|
||||
col: 4,
|
||||
},
|
||||
tt: Delim(
|
||||
CurlyR,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/delimiters.mcl",
|
||||
line: 2,
|
||||
col: 2,
|
||||
},
|
||||
tt: Delim(
|
||||
CurlyL,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/delimiters.mcl",
|
||||
line: 1,
|
||||
col: 4,
|
||||
},
|
||||
tt: Delim(
|
||||
SquareR,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/delimiters.mcl",
|
||||
line: 1,
|
||||
col: 2,
|
||||
},
|
||||
tt: Delim(
|
||||
SquareL,
|
||||
),
|
||||
},
|
||||
]
|
|
@ -1,3 +0,0 @@
|
|||
[ ]
|
||||
{ }
|
||||
( )
|
|
@ -1,212 +0,0 @@
|
|||
[
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 21,
|
||||
col: 5,
|
||||
},
|
||||
tt: Keyword(
|
||||
Loop,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 20,
|
||||
col: 3,
|
||||
},
|
||||
tt: Keyword(
|
||||
As,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 19,
|
||||
col: 7,
|
||||
},
|
||||
tt: Keyword(
|
||||
Return,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 18,
|
||||
col: 7,
|
||||
},
|
||||
tt: Keyword(
|
||||
Extern,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 17,
|
||||
col: 8,
|
||||
},
|
||||
tt: Keyword(
|
||||
Include,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 16,
|
||||
col: 6,
|
||||
},
|
||||
tt: Keyword(
|
||||
False,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 15,
|
||||
col: 5,
|
||||
},
|
||||
tt: Keyword(
|
||||
True,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 14,
|
||||
col: 7,
|
||||
},
|
||||
tt: Keyword(
|
||||
Static,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 13,
|
||||
col: 4,
|
||||
},
|
||||
tt: Keyword(
|
||||
Mut,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 12,
|
||||
col: 6,
|
||||
},
|
||||
tt: Keyword(
|
||||
Const,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 11,
|
||||
col: 4,
|
||||
},
|
||||
tt: Keyword(
|
||||
Let,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 10,
|
||||
col: 9,
|
||||
},
|
||||
tt: Keyword(
|
||||
Continue,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 9,
|
||||
col: 6,
|
||||
},
|
||||
tt: Keyword(
|
||||
Break,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 8,
|
||||
col: 4,
|
||||
},
|
||||
tt: Keyword(
|
||||
For,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 7,
|
||||
col: 6,
|
||||
},
|
||||
tt: Keyword(
|
||||
While,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 6,
|
||||
col: 5,
|
||||
},
|
||||
tt: Keyword(
|
||||
Type,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 5,
|
||||
col: 5,
|
||||
},
|
||||
tt: Keyword(
|
||||
Enum,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 4,
|
||||
col: 7,
|
||||
},
|
||||
tt: Keyword(
|
||||
Struct,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 3,
|
||||
col: 5,
|
||||
},
|
||||
tt: Keyword(
|
||||
Else,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 2,
|
||||
col: 3,
|
||||
},
|
||||
tt: Keyword(
|
||||
If,
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/keywords.mcl",
|
||||
line: 1,
|
||||
col: 3,
|
||||
},
|
||||
tt: Keyword(
|
||||
Fn,
|
||||
),
|
||||
},
|
||||
]
|
|
@ -1,21 +0,0 @@
|
|||
fn
|
||||
if
|
||||
else
|
||||
struct
|
||||
enum
|
||||
type
|
||||
while
|
||||
for
|
||||
break
|
||||
continue
|
||||
let
|
||||
const
|
||||
mut
|
||||
static
|
||||
true
|
||||
false
|
||||
include
|
||||
extern
|
||||
return
|
||||
as
|
||||
loop
|
|
@ -1,96 +0,0 @@
|
|||
[
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/literals.mcl",
|
||||
line: 7,
|
||||
col: 11,
|
||||
},
|
||||
tt: Number(
|
||||
Number {
|
||||
val: 173,
|
||||
base: 2,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/literals.mcl",
|
||||
line: 6,
|
||||
col: 8,
|
||||
},
|
||||
tt: Number(
|
||||
Number {
|
||||
val: 13633,
|
||||
base: 8,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/literals.mcl",
|
||||
line: 5,
|
||||
col: 9,
|
||||
},
|
||||
tt: Number(
|
||||
Number {
|
||||
val: 16759299,
|
||||
base: 16,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/literals.mcl",
|
||||
line: 4,
|
||||
col: 3,
|
||||
},
|
||||
tt: Number(
|
||||
Number {
|
||||
val: 21,
|
||||
base: 10,
|
||||
signed: false,
|
||||
},
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/literals.mcl",
|
||||
line: 3,
|
||||
col: 22,
|
||||
},
|
||||
tt: String(
|
||||
TString {
|
||||
val: "this is a c string!",
|
||||
cstr: true,
|
||||
},
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/literals.mcl",
|
||||
line: 2,
|
||||
col: 27,
|
||||
},
|
||||
tt: String(
|
||||
TString {
|
||||
val: "this is a normal string!",
|
||||
cstr: false,
|
||||
},
|
||||
),
|
||||
},
|
||||
Token {
|
||||
loc: Loc {
|
||||
file: "tokeniser/literals.mcl",
|
||||
line: 1,
|
||||
col: 4,
|
||||
},
|
||||
tt: Char(
|
||||
Char(
|
||||
'c',
|
||||
),
|
||||
),
|
||||
},
|
||||
]
|
|
@ -1,8 +0,0 @@
|
|||
'c'
|
||||
"this is a normal string!"
|
||||
c"this is a c string!"
|
||||
21
|
||||
0xFfbA03
|
||||
0o32501
|
||||
0b10101101
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user