Compare commits

...

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

67 changed files with 1214 additions and 7721 deletions

View File

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

17
.gitignore vendored
View File

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

363
Cargo.lock generated
View File

@ -2,47 +2,111 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "anstream"
version = "0.6.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
[[package]]
name = "anstyle-parse"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "anyhow"
version = "1.0.79"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "bitflags"
version = "1.3.2"
name = "autocfg"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "cc"
version = "1.0.79"
name = "camino"
version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239"
[[package]]
name = "clap"
version = "4.1.8"
version = "4.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5"
checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3"
dependencies = [
"bitflags",
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"is-terminal",
"once_cell",
"strsim",
"termcolor",
]
[[package]]
name = "clap_derive"
version = "4.1.8"
version = "4.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0"
checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
@ -50,243 +114,194 @@ dependencies = [
[[package]]
name = "clap_lex"
version = "0.3.2"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09"
dependencies = [
"os_str_bytes",
]
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
[[package]]
name = "errno"
version = "0.2.8"
name = "colorchoice"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
]
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
[[package]]
name = "errno-dragonfly"
name = "env_filter"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab"
dependencies = [
"cc",
"libc",
"log",
"regex",
]
[[package]]
name = "env_logger"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d"
dependencies = [
"anstream",
"anstyle",
"env_filter",
"humantime",
"log",
]
[[package]]
name = "heck"
version = "0.4.1"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.3.1"
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "io-lifetimes"
version = "1.0.6"
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3"
dependencies = [
"libc",
"windows-sys",
]
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "is-terminal"
version = "0.4.4"
name = "log"
version = "0.4.22"
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"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "mclangc"
version = "0.1.0"
dependencies = [
"anyhow",
"camino",
"clap",
"env_logger",
"log",
"parse_int",
]
[[package]]
name = "once_cell"
version = "1.17.1"
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "os_str_bytes"
version = "6.4.1"
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
"autocfg",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
name = "parse_int"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
checksum = "2d695b79916a2c08bcff7be7647ab60d1402885265005a6658ffe6d763553c5a"
dependencies = [
"proc-macro2",
"quote",
"version_check",
"num-traits",
]
[[package]]
name = "proc-macro2"
version = "1.0.51"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.23"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustix"
version = "0.36.9"
name = "regex"
version = "1.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc"
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "strsim"
version = "0.10.0"
name = "regex-automata"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "1.0.109"
version = "2.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "termcolor"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
dependencies = [
"winapi-util",
]
[[package]]
name = "unicode-ident"
version = "1.0.8"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "version_check"
version = "0.9.4"
name = "utf8parse"
version = "0.2.2"
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"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "windows-sys"
version = "0.45.0"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.1"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
@ -295,42 +310,48 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.1"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.1"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.42.1"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.42.1"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.1"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.1"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.1"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

View File

@ -1,13 +1,14 @@
[package]
name = "mclangc"
description="The McLang Programming language compiler"
version = "0.1.0"
edition = "2021"
authors=[
"MCorange <mcorangecodes@gmail.com> (https://mcorangehq.xyz/)"
]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.79"
clap = { version = "4.1.8", features = ["derive"] }
anyhow = "1.0.86"
camino = "1.1.7"
clap = { version = "4.5.11", features = ["derive"] }
env_logger = "0.11.5"
log = "0.4.22"
parse_int = "0.6.0"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

View File

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

View File

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

Binary file not shown.

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

3
examples/.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1 +0,0 @@

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

164
src/ast.rs Normal file
View File

@ -0,0 +1,164 @@
use std::collections::HashMap;
use camino::Utf8PathBuf;
use crate::{common::Loc, token::Token};
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub struct Ident {
pub text: String,
pub loc: Loc,
}
impl Ident {
pub fn from_tok(t: Token) -> Self {
let text;
match t.typ {
crate::token::TokenType::Ident(s) => text = s,
_ => unreachable!(),
}
Self { text, loc: t.loc.clone() }
}
}
#[derive(Debug, Clone)]
pub struct Type {
pub name: Ident,
pub inner: Option<Box<Type>>,
}
#[derive(Debug, Clone)]
pub struct Program {
pub file: Utf8PathBuf,
pub items: Vec<Stat>
}
#[derive(Debug, Clone)]
pub struct Stat {
pub typ: StatType,
pub loc: Loc,
}
#[derive(Debug, Clone)]
pub enum StatType {
Struct(StructStat),
Enum(EnumStat),
Var(VarStat),
Set(SetStat),
Match(MatchStat),
Not(UnaryStat),
Eq(BinaryStat),
Neq(BinaryStat),
Lt(BinaryStat),
Gt(BinaryStat),
Le(BinaryStat),
Ge(BinaryStat),
Add(BinaryStat),
Sub(BinaryStat),
Mul(BinaryStat),
Div(BinaryStat),
Mod(BinaryStat),
Shr(BinaryStat),
Shl(BinaryStat),
Band(BinaryStat),
Bor(BinaryStat),
Bnot(UnaryStat),
And(BinaryStat),
Or(BinaryStat),
Xor(BinaryStat),
Inc(UnaryStat),
Dec(UnaryStat),
Ref(UnaryStat),
Export(ExportStat),
Function(FnStat),
FunctionCall(FnCallStat),
Return(ReturnStat),
If(IfStat),
While(WhileStat),
Block(BlockStat),
Literal(ValueType)
}
#[derive(Debug, Clone)]
pub enum ValueType {
Float(f64),
Int(i64),
Bool(bool),
String(String),
Char(char),
}
#[derive(Debug, Clone)]
pub struct BlockStat(pub Vec<Stat>);
#[derive(Debug, Clone)]
pub struct UnaryStat(pub Box<Stat>);
#[derive(Debug, Clone)]
pub struct BinaryStat(pub Box<Stat>, pub Box<Stat>);
#[derive(Debug, Clone)]
pub struct MatchStat {
pub val: Ident,
pub cases: HashMap<ValueType, BlockStat>
}
#[derive(Debug, Clone)]
pub struct SetStat {
pub name: Ident,
pub val: Box<Stat>,
}
#[derive(Debug, Clone)]
pub struct VarStat {
pub name: Ident,
pub typ: Option<Type>,
pub val: Option<Box<Stat>>,
}
#[derive(Debug, Clone)]
pub struct EnumStat {
pub name: Ident,
pub fields: Vec<Ident>
}
#[derive(Debug, Clone)]
pub struct StructStat {
pub name: Ident,
pub fields: HashMap<Ident, Type>,
}
#[derive(Debug, Clone)]
pub struct FnStat {
pub name: Ident,
pub args: HashMap<Ident, Type>,
pub body: BlockStat,
}
#[derive(Debug, Clone)]
pub struct FnCallStat {
pub name: Ident,
pub args: Vec<Ident>,
}
#[derive(Debug, Clone)]
pub struct ReturnStat {
pub arg: Option<Box<Stat>>,
}
#[derive(Debug, Clone)]
pub struct IfStat {
pub condition: Box<Stat>,
pub body: BlockStat,
pub els: Box<Stat>
}
#[derive(Debug, Clone)]
pub struct WhileStat {
pub condition: Box<Stat>,
pub body: BlockStat,
}
#[derive(Debug, Clone)]
pub struct ExportStat {
pub name: Ident,
}

View File

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

24
src/cliargs.rs Normal file
View File

@ -0,0 +1,24 @@
use camino::Utf8PathBuf;
use clap::Parser;
use log::LevelFilter;
#[derive(Debug, Parser)]
pub struct CliArgs {
#[arg(long, short, default_value="./a.out")]
pub output: Utf8PathBuf,
#[arg(long, short)]
pub debug: bool,
pub input: Vec<Utf8PathBuf>,
}
impl CliArgs {
pub fn get_logger_filter(&self) -> LevelFilter {
if self.debug {
LevelFilter::Debug
} else {
LevelFilter::Info
}
}
}

26
src/common.rs Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,127 +1,32 @@
#![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;
use anyhow::{Result, bail};
use log::LevelFilter;
#[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,
mod ast;
mod cliargs;
mod parser;
mod token;
mod tokeniser;
mod common;
/// Output compiled file
#[arg(long, short, default_value_t=String::from(DEFAULT_OUT_FILE))]
out_file: String,
/// Interpert
#[arg(long, short='s')]
interpret: bool,
fn main() {
let cliargs = cliargs::CliArgs::parse();
env_logger::builder()
.format_timestamp(None)
.filter(None, cliargs.get_logger_filter())
.init();
/// Run the compiled executable
#[arg(long, short)]
run: bool,
/// Dont print any output exept the actual running codes output
#[arg(long, short)]
quiet: bool,
/// Add an include directory [default: ["./include", "~/.mclang/include"]]
#[arg(long, short='I')]
include: Vec<String>,
/// Unsafe mode, disables typechecking
#[arg(long="unsafe", default_value_t = false)]
unsaf: bool,
/// Optimisation level, available levels: 'D': debug, '0': No optimisations
#[arg(long, short='O', default_value_t=String::from("0"))]
optimisation: String,
// disables the main function
#[arg(long="lib")]
lib_mode: bool
//#[arg(long, short='F')]
//features: Vec<String>,
}
impl Args {
/// Get optimisation level
/// 0 => no optimisations
/// 1 => slight optimisations, mostly size ones
/// # Errors
///
/// Throws when the opt level is not known
pub fn get_opt_level(&self) -> Result<usize>{
match self.optimisation.as_str() {
"D" | "d" => Ok(0),
"0" | "" => Ok(1),
o => {
error!("Unknown optimisation level {o}");
bail!("")
}
let mut tokeniser = tokeniser::Tokeniser::new();
let mut parser = parser::Parser::new();
for file in &cliargs.input {
if let Err(_) = tokeniser.tokenise(file.as_path()) {
break;
}
let tokens = tokeniser.tokens();
let Ok(ast) = parser.parse(file, tokens) else {
break;
};
dbg!(ast);
}
}
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);
}

View File

@ -1,229 +1,423 @@
use std::ops::Deref;
use std::collections::HashMap;
use crate::{definitions::{Operator, OpType, Token, TokenType, Loc, KeywordType, InstructionType, InternalType, Program}, lerror, preprocessor::Preprocessor, Args};
use anyhow::{Result, bail};
use anyhow::{bail, Result};
use camino::Utf8Path;
pub fn cross_ref(mut program: Vec<Operator>) -> Result<Vec<Operator>> {
let mut stack: Vec<usize> = Vec::new();
use crate::{ast::{self, StructStat}, common::Loc, token::{KeywordType, Token, TokenType}};
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> {
pub struct Parser {
tokens: Vec<Token>,
pub preprocessor: Preprocessor<'a>,
#[allow(dead_code)]
args: &'a Args
loc: Loc,
is_in_func: bool,
}
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)
impl Parser {
pub fn new() -> Self {
Self {
tokens: Vec::new(),
loc: Loc::new(""),
is_in_func: false,
}
}
pub fn parse(&mut self, file: &Utf8Path, _tokens: &Vec<Token>) -> Result<ast::Program> {
self.tokens = _tokens.to_vec();
self.tokens.reverse();
let mut prog = ast::Program {
file: file.into(),
items: Vec::new()
};
Self{
tokens: file,
preprocessor: pre,
args
while !self.tokens.is_empty() {
prog.items.push(self.parse_stat()?);
}
Ok(prog)
}
pub fn parse(&mut self) -> Result<Program> {
let mut tokens = Vec::new();
for token in &self.tokens {
if token.text.is_empty() {
continue;
fn parse_stat(&mut self) -> Result<ast::Stat> {
log::debug!("parse_stat");
self.expect_next(TokenType::ParenL)?;
log::debug!("parse_stat.2");
let Some(main) = self.tokens.pop() else {
log::error!("Expected function, struct, enum definitions, but found nothing");
bail!("");
};
self.loc = main.loc;
let ret = match main.typ {
TokenType::ParenL => {
ast::Stat {
loc: self.loc.clone(),
typ: ast::StatType::Block(self.parse_block_stat()?),
}
}
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!("")
TokenType::Keyword(kw) => {
log::debug!("kw: {kw:?}");
match kw {
KeywordType::Eq | KeywordType::Neq |
KeywordType::Lt | KeywordType::Gt |
KeywordType::Ge | KeywordType::Le |
KeywordType::Or | KeywordType::And |
KeywordType::Shr | KeywordType::Shl |
KeywordType::Bor | KeywordType::Band |
KeywordType::Xor | KeywordType::Add |
KeywordType::Sub | KeywordType::Div |
KeywordType::Mod | KeywordType::Mul => {
if !self.is_in_func {
log::error!("Can only use {:?} in a function", kw);
bail!("");
}
self.parse_binary_stat(kw)?
}
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));
KeywordType::Not | KeywordType::Bnot |
KeywordType::Inc | KeywordType::Dec |
KeywordType::Ref => {
if !self.is_in_func {
log::error!("Can only use {:?} in a function", kw);
bail!("");
}
self.parse_unary_stat(kw)?
}
KeywordType::Var | KeywordType::Set |
KeywordType::If | KeywordType::While |
KeywordType::Match if !self.is_in_func => {
log::error!("Can only use {:?} in a function", kw);
bail!("");
}
// TODO: Dont allow structs exports, and enums in functions
KeywordType::Struct => self.parse_struct_stat()?,
KeywordType::Fn => self.parse_fn_stat()?,
KeywordType::Export => self.parse_export_stat()?,
KeywordType::Enum => self.parse_enum_stat()?,
KeywordType::Call if self.is_in_func => self.parse_call_stat()?,
KeywordType::Var if self.is_in_func => self.parse_var_stat()?,
KeywordType::Set if self.is_in_func => self.parse_set_stat()?,
KeywordType::If if self.is_in_func => self.parse_if_stat()?,
KeywordType::While if self.is_in_func => self.parse_while_stat()?,
KeywordType::Match if self.is_in_func => self.parse_match_stat()?,
KeywordType::Return if self.is_in_func => self.parse_return_stat()?,
KeywordType::Else => {
log::error!("Keyword else is only suppost to be put after an else statement");
bail!("");
}
v => unreachable!("{v:?}")
}
};
}
_ => {
log::error!("Expected function, struct, enum definitions, but found {:?}", main.typ);
bail!("");
}
};
Ok(ret)
}
fn parse_binary_stat(&mut self, kw: KeywordType) -> Result<ast::Stat> {
let bs = ast::BinaryStat(
Box::new(self.parse_stat()?),
Box::new(self.parse_stat()?)
);
let typ = match kw {
KeywordType::Eq => ast::StatType::Eq(bs),
KeywordType::Neq => ast::StatType::Neq(bs),
KeywordType::Lt => ast::StatType::Lt(bs),
KeywordType::Gt => ast::StatType::Gt(bs),
KeywordType::Ge => ast::StatType::Ge(bs),
KeywordType::Le => ast::StatType::Le(bs),
KeywordType::Or => ast::StatType::Or(bs),
KeywordType::And => ast::StatType::And(bs),
KeywordType::Add => ast::StatType::Add(bs),
KeywordType::Sub => ast::StatType::Sub(bs),
KeywordType::Div => ast::StatType::Div(bs),
KeywordType::Mul => ast::StatType::Mul(bs),
KeywordType::Mod => ast::StatType::Mod(bs),
KeywordType::Shr => ast::StatType::Shr(bs),
KeywordType::Shl => ast::StatType::Shl(bs),
KeywordType::Bor => ast::StatType::Bor(bs),
KeywordType::Band => ast::StatType::Band(bs),
KeywordType::Xor => ast::StatType::Xor(bs),
_ => unreachable!("{kw:?}")
};
self.expect_next(TokenType::ParenR)?;
Ok(ast::Stat {
loc: self.loc.clone(),
typ,
})
}
fn parse_unary_stat(&mut self, kw: KeywordType) -> Result<ast::Stat> {
let us = ast::UnaryStat(Box::new(self.parse_stat()?));
let typ = match kw {
KeywordType::Not => ast::StatType::Not(us),
KeywordType::Bnot => ast::StatType::Bnot(us),
KeywordType::Inc => ast::StatType::Inc(us),
KeywordType::Dec => ast::StatType::Dec(us),
KeywordType::Ref => ast::StatType::Ref(us),
_ => unreachable!("{kw:?}")
};
self.expect_next(TokenType::ParenR)?;
Ok(ast::Stat { typ, loc: self.loc.clone() })
}
fn parse_block_stat(&mut self) -> Result<ast::BlockStat> {
log::debug!("parse_block_stat.1");
self.expect_next(TokenType::ParenL)?;
let mut bs = Vec::new();
while let Some(t) = self.tokens.last() {
match &t.typ {
TokenType::ParenL => {
log::debug!("parse_block_stat.2");
bs.push(self.parse_stat()?);
}
TokenType::ParenR => {
log::debug!("parse_block_stat.3");
self.expect_next(TokenType::ParenR)?;
break;
}
typ => {
log::debug!("parse_block_stat.4");
log::error!("{}: Expected '(' found {typ:?}", self.loc);
bail!("");
}
}
}
self.preprocessor.program.ops = tokens;
let mut t = self.preprocessor.preprocess()?.get_program();
t.ops = cross_ref(t.ops)?;
Ok(t)
Ok(ast::BlockStat(bs))
}
fn parse_struct_stat(&mut self) -> Result<ast::Stat> {
todo!();
}
fn parse_fn_stat(&mut self) -> Result<ast::Stat> {
self.is_in_func = true;
let name = self.expect_next(TokenType::Ident(String::new()))?;
let mut args: HashMap<ast::Ident, ast::Type> = HashMap::new();
log::debug!("parse_fn_stat.1");
self.expect_next(TokenType::ParenL)?;
log::debug!("parse_fn_stat.2");
while self.try_next(TokenType::ParenR).is_none() {
log::debug!("parse_fn_stat.3");
self.expect_next(TokenType::ParenL)?;
log::debug!("parse_fn_stat.4");
let arg_name = self.expect_next(TokenType::Ident(String::new()))?;
let typ = self.parse_type()?;
self.expect_next(TokenType::ParenR)?;
args.insert(ast::Ident::from_tok(arg_name), typ);
}
log::debug!("parse_fn_stat.5");
self.expect_next(TokenType::ParenR)?;
let body = self.parse_block_stat()?;
log::debug!("parse_fn_stat.6");
let fs = ast::FnStat {
name: ast::Ident::from_tok(name.clone()),
args,
body
};
self.is_in_func = false;
self.expect_next(TokenType::ParenR)?;
Ok(ast::Stat {
loc: name.loc.clone(),
typ: ast::StatType::Function(fs)
})
}
fn parse_type(&mut self) -> Result<ast::Type> { // (args (arr string_t))
let has_another = self.try_next(TokenType::ParenL).is_some();
if has_another {
self.tokens.pop();
}
let name = self.expect_next(TokenType::Ident(String::new()))?;
let mut typ = ast::Type {
name: ast::Ident::from_tok(name),
inner: None,
};
if has_another {
typ.inner = Some(Box::new(self.parse_type()?));
self.expect_next(TokenType::ParenR)?;
}
Ok(typ)
}
fn parse_enum_stat(&mut self) -> Result<ast::Stat> {
todo!();
}
fn parse_var_stat(&mut self) -> Result<ast::Stat> {
let name = self.expect_next(TokenType::Ident(String::new()))?;
let typ;
let typ_tok = self.expect_several(&[
TokenType::Ident(String::new()),
TokenType::Underscore
])?;
match &typ_tok.typ {
TokenType::Underscore => {
typ = None;
}
TokenType::Ident(_) => {
self.tokens.push(typ_tok);
typ = Some(self.parse_type()?);
}
_ => unreachable!()
}
let val;
if self.try_next(TokenType::ParenR).is_some() {
val = None;
} else {
val = Some(Box::new(self.parse_val()?));
}
self.expect_next(TokenType::ParenR)?;
Ok(ast::Stat {
loc: name.loc.clone(),
typ: ast::StatType::Var(ast::VarStat {
name: ast::Ident::from_tok(name),
typ,
val,
})
})
}
fn parse_val(&mut self) -> Result<ast::Stat> {
if self.try_next(TokenType::ParenL).is_some() {
Ok(self.parse_stat()?)
} else {
let tok = self.expect_several(&[
TokenType::String(String::new()),
TokenType::Int(0),
TokenType::Float(0.0),
TokenType::Char('\0'),
])?;
let vt = match tok.typ {
TokenType::String(s) => ast::ValueType::String(s),
TokenType::Int(i) => ast::ValueType::Int(i),
TokenType::Float(f) => ast::ValueType::Float(f),
TokenType::Char(c) => ast::ValueType::Char(c),
_ => unreachable!("")
};
Ok(ast::Stat {
loc: tok.loc.clone(),
typ: ast::StatType::Literal(vt)
})
}
}
fn parse_set_stat(&mut self) -> Result<ast::Stat> {
let name = self.expect_next(TokenType::Ident(String::new()))?;
let val = self.parse_val()?;
self.expect_next(TokenType::ParenR)?;
Ok(ast::Stat {
loc: name.loc.clone(),
typ: ast::StatType::Set(ast::SetStat {
name: ast::Ident::from_tok(name),
val: Box::new(val),
})
})
}
fn parse_call_stat(&mut self) -> Result<ast::Stat> {
let name = self.expect_next(TokenType::Ident(String::new()))?;
let mut args = Vec::new();
while self.try_next(TokenType::ParenR).is_none() {
args.push(ast::Ident::from_tok(self.expect_next(TokenType::Ident(String::new()))?));
}
self.expect_next(TokenType::ParenR)?;
Ok(ast::Stat {
loc: name.loc.clone(),
typ: ast::StatType::FunctionCall(ast::FnCallStat {
name: ast::Ident::from_tok(name),
args
})
})
}
fn parse_return_stat(&mut self) -> Result<ast::Stat> {
let loc = self.loc.clone();
let val;
if self.try_next(TokenType::ParenR).is_some() {
val = None;
} else {
val = Some(Box::new(self.parse_val()?));
}
self.expect_next(TokenType::ParenR)?;
Ok(ast::Stat {
loc,
typ: ast::StatType::Return(ast::ReturnStat {
arg: val,
})
})
}
fn parse_if_stat(&mut self) -> Result<ast::Stat> {
todo!();
}
fn parse_while_stat(&mut self) -> Result<ast::Stat> {
todo!();
}
fn parse_match_stat(&mut self) -> Result<ast::Stat> {
todo!();
}
fn parse_export_stat(&mut self) -> Result<ast::Stat> {
todo!();
}
fn expect_next(&mut self, tt: TokenType) -> Result<Token> {
if let Some(t) = self.tokens.pop() {
log::debug!("en: {:?}", t.typ);
if std::mem::discriminant(&t.typ) == std::mem::discriminant(&tt) {
self.loc = t.loc.clone();
return Ok(t);
} else {
log::error!("{}: Expected {:?}, got {:?}", t.loc, tt, t.typ);
bail!("")
}
}
log::error!("{}: Expected {:?}, got Nothing", self.loc, tt);
bail!("")
}
fn expect_several(&mut self, tt: &[TokenType]) -> Result<Token> {
let tt_text = tt
.into_iter()
.map(|v| format!("{:?}", v))
.collect::<Vec<String>>()
.join(", ");
if let Some(t) = self.tokens.pop() {
for typ in tt {
if std::mem::discriminant(&t.typ) == std::mem::discriminant(&typ) {
self.loc = t.loc.clone();
return Ok(t);
}
}
log::error!("{}: Expected {}, got {:?}", t.loc, tt_text, t.typ);
bail!("")
}
log::error!("{}: Expected {}, got Nothing", self.loc, tt_text);
bail!("")
}
fn try_next(&mut self, tt: TokenType) -> Option<&Token> {
if let Some(t) = self.tokens.last() {
if std::mem::discriminant(&t.typ) == std::mem::discriminant(&tt) {
return Some(t);
}
}
None
}
}
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)
}
}

View File

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

View File

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

124
src/token.rs Normal file
View File

@ -0,0 +1,124 @@
use anyhow::{anyhow, bail};
use crate::common::Loc;
#[derive(Debug, Clone)]
pub struct Token {
pub typ: TokenType,
pub loc: Loc,
}
impl Token {
pub fn new(typ: TokenType, loc: Loc) -> Self {
Self {typ, loc}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum TokenType {
Ident(String),
Keyword(KeywordType),
Int(i64),
Float(f64),
String(String),
Char(char),
Underscore,
ParenR,
ParenL,
Dot,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum KeywordType {
Struct,
Enum,
Fn,
Var,
Set,
If,
While,
Match,
Not,
Eq,
Neq,
Lt,
Gt,
Le,
Ge,
Add,
Sub,
Div,
Mod,
Mul,
Shr,
Shl,
Band,
Bor,
Bnot,
And,
Or,
Xor,
Inc,
Dec,
Ref,
Export,
Else,
Call,
Return,
StructInit,
}
impl TryFrom<&str> for KeywordType {
type Error = anyhow::Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"fn" => Ok(Self::Fn),
"var" => Ok(Self::Var),
"set" => Ok(Self::Set),
"if" => Ok(Self::If),
"while" => Ok(Self::While),
"match" => Ok(Self::Match),
"not" => Ok(Self::Not),
"eq" => Ok(Self::Eq),
"neq" => Ok(Self::Neq),
"lt" => Ok(Self::Lt),
"gt" => Ok(Self::Gt),
"le" => Ok(Self::Le),
"ge" => Ok(Self::Ge),
"add" => Ok(Self::Add),
"sub" => Ok(Self::Sub),
"div" => Ok(Self::Div),
"mod" => Ok(Self::Mod),
"mul" => Ok(Self::Mul),
"shr" => Ok(Self::Shr),
"shl" => Ok(Self::Shl),
"band" => Ok(Self::Band),
"bor" => Ok(Self::Bor),
"bnot" => Ok(Self::Bnot),
"and" => Ok(Self::And),
"or" => Ok(Self::Or),
"xor" => Ok(Self::Xor),
"inc" => Ok(Self::Inc),
"dec" => Ok(Self::Dec),
"ref" => Ok(Self::Ref),
"struct" => Ok(Self::Struct),
"enum" => Ok(Self::Enum),
"export" => Ok(Self::Export),
"else" => Ok(Self::Else),
"call" => Ok(Self::Call),
"return" => Ok(Self::Return),
"structinit" => Ok(Self::StructInit),
_ => bail!("Unknown keyword: {}", value)
}
}
}
impl TryFrom<String> for KeywordType {
type Error = anyhow::Error;
fn try_from(value: String) -> Result<Self, Self::Error> {
value.as_str().try_into()
}
}

226
src/tokeniser.rs Normal file
View File

@ -0,0 +1,226 @@
use std::io::BufRead;
use anyhow::bail;
use camino::Utf8Path;
use crate::{common::Loc, token::{KeywordType, Token, TokenType}};
pub struct Tokeniser {
loc: Loc,
tokens: Vec<Token>,
}
impl Tokeniser {
pub fn new() -> Self {
Self {
loc: Loc::new(""),
tokens: Vec::new(),
}
}
pub fn tokenise(&mut self, file: &Utf8Path) -> anyhow::Result<()>{
self.reset();
self.loc.file = file.into();
let Ok(f) = std::fs::File::open(file) else {
log::error!("Failed to open file {file}, permissions?");
bail!("");
};
let mut f = std::io::BufReader::new(f);
let mut line = String::new();
loop {
match f.read_line(&mut line) {
Ok(c) if c == 0 => break,
Ok(_) => (),
Err(e) => {
log::error!("Failed to read line from file: {e}");
bail!("");
}
}
let mut chars = line.chars().into_iter().peekable();
'line: while let Some(c) = chars.next() {
match c {
'(' => self.tokens.push(Token::new(TokenType::ParenL, self.loc.clone())),
')' => self.tokens.push(Token::new(TokenType::ParenR, self.loc.clone())),
'.' => self.tokens.push(Token::new(TokenType::Dot, self.loc.clone())),
';' => break 'line,
'-' | '+' | '0'..='9' => {
let loc = self.loc.clone();
let mut buf = String::from(c);
let mut is_float = false;
while let Some(c) = chars.peek() {
match c {
'0'..='9' => {
buf.push(*c);
chars.next();
}
'.' => {
if is_float {
log::error!("{}: Invalid float", loc);
bail!("");
} else {
is_float = true;
buf.push(*c);
chars.next();
}
}
'x' | 'b' | 'o' => {
if buf.len() != 1 {
log::error!("{}: Invalid int", loc);
bail!("");
}
buf.push(*c);
chars.next();
}
_ => break
}
self.loc.col += 1;
}
if is_float {
let v;
match parse_int::parse::<f64>(&buf) {
Ok(f) => v = f,
Err(e) => {
log::error!("{}: Failed to parse float: {e}", loc);
bail!("");
}
}
self.tokens.push(Token::new(TokenType::Float(v), loc))
} else {
let v;
match parse_int::parse::<i64>(&buf) {
Ok(i) => v = i,
Err(e) => {
log::error!("{}: Failed to parse float: {e}", loc);
bail!("");
}
}
self.tokens.push(Token::new(TokenType::Int(v), loc))
}
}
'a'..='z' | 'A'..='Z' | '_' => {
let loc = self.loc.clone();
let mut buf = String::from(c);
while let Some(c) = chars.peek() {
if !matches!(c, 'a'..='z' | 'A'..='Z' | '_' | '0'..='9') {
self.loc.col -= 1;
break;
}
buf.push(*c);
chars.next();
self.loc.col += 1;
}
if buf == "_" {
self.tokens.push(Token::new(TokenType::Underscore, self.loc.clone()));
} else if let Ok(kw) = KeywordType::try_from(buf.clone()) {
self.tokens.push(Token::new(TokenType::Keyword(kw), loc));
} else {
self.tokens.push(Token::new(TokenType::Ident(buf), loc));
}
}
'"' => {
let loc = self.loc.clone();
let mut buf = String::new();
let mut escaped = false;
while let Some(c) = chars.next() {
match c {
'"' if escaped => {
buf.push(c);
escaped = false;
}
'"' if !escaped => break,
'\\' if !escaped => escaped = true,
'\\' if escaped => {
buf.push(c);
escaped = false;
}
'n' if escaped => {
buf.push('\n');
escaped = false;
}
'r' if escaped => {
buf.push('\r');
escaped = false;
}
_ if !escaped => buf.push(c),
_ if escaped => {
log::warn!("{}: Unknown escape \\{}, skipping and putting it as literal", self.loc, c);
buf.push('\\');
buf.push(c);
}
_ => unreachable!()
}
self.loc.col += 1;
}
self.tokens.push(Token::new(TokenType::String(buf), loc));
}
'\'' => {
let loc = self.loc.clone();
let mut buf = String::new();
let mut escaped = false;
while let Some(c) = chars.next() {
match c {
'\'' if escaped => {
buf.push(c);
escaped = false;
}
'\'' if !escaped => break,
'\\' if !escaped => escaped = true,
'\\' if escaped => {
buf.push(c);
escaped = false;
}
'n' if escaped => {
buf.push('\n');
escaped = false;
}
'r' if escaped => {
buf.push('\r');
escaped = false;
}
_ if !escaped => buf.push(c),
_ if escaped => {
log::warn!("{}: Unknown escape \\{}, skipping and putting it as literal", self.loc, c);
buf.push('\\');
buf.push(c);
}
_ => unreachable!()
}
self.loc.col += 1;
}
if buf.len() > 1 {
log::error!("{}: Chars can only have 1 character", loc);
bail!("");
}
self.tokens.push(Token::new(TokenType::Char(buf.chars().nth(0).unwrap()), loc))
}
_ => ()
}
self.loc.col += 1;
}
self.loc.line += 1;
self.loc.col = 1;
line.clear();
}
Ok(())
}
fn reset(&mut self) {
self.loc.col = 1;
self.loc.line = 1;
// self.loc.file = "".into();
self.tokens.clear();
}
pub fn tokens(&self) -> &Vec<Token> {
&self.tokens
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +0,0 @@
pub mod common;
pub mod token;

View File

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

View File

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

View File

@ -1,13 +1,30 @@
include "std.mcl"
; vim: set ft=commonlisp:
(fn main ((args (arr_t string_t))) (
(var a i32 35)
(var b i32_t)
(set b 34)
(set a (call uwu a b))
;(match a (
; (69 (
; print "ooooo"
; ))
; (420 (
; print "aaaaaaaa"
; ))
;))
fn main with int ptr returns void then
// p l
"Hello!\n" puts
;(if (eq a b) (
; print "yes"
;))
;(while (not (eq a b)) (
; (set_add (ref b) 1)
;))
))
// memory fd 4 end
// c"./test.mcl" FS_O_SYNC 0 fopen
// dup _dbg_print
// fd swap write32
done
;(fn uwu((a i32_t) (b i32_t)) (
; (return (add a b))
;))
;(fn set_add ((a (ref i32_t)) (b i32_t)) (
; (ref_set a (add (deref a) b)
;))

View File

@ -1 +0,0 @@
gftdesd5ryutfgyhibugtf6r4

View File

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

4
tools/.gitignore vendored
View File

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

View File

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

View File

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