owO?
This commit is contained in:
parent
0e18a5cdb8
commit
e2430e599c
10
.gitignore
vendored
10
.gitignore
vendored
|
@ -1,9 +1 @@
|
||||||
/*
|
/target
|
||||||
!/.vscode
|
|
||||||
!/.gitignore
|
|
||||||
!/.gitkeep
|
|
||||||
!/src
|
|
||||||
!/include
|
|
||||||
!/Cargo*
|
|
||||||
!/REF.md
|
|
||||||
!/*.mcl
|
|
||||||
|
|
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +0,0 @@
|
||||||
[submodule "include"]
|
|
||||||
path = include
|
|
||||||
url = git@github.com:mc-lang/libmc.git
|
|
11
.vscode/extensions.json
vendored
11
.vscode/extensions.json
vendored
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"recommendations": [
|
|
||||||
"aaron-bond.better-comments",
|
|
||||||
"usernamehw.errorlens",
|
|
||||||
"tamasfe.even-better-toml",
|
|
||||||
"platformio.platformio-ide",
|
|
||||||
"1YiB.rust-bundle",
|
|
||||||
"tamasfe.even-better-toml",
|
|
||||||
"ms-vscode.cpptools"
|
|
||||||
]
|
|
||||||
}
|
|
45
.vscode/launch.json
vendored
45
.vscode/launch.json
vendored
|
@ -1,45 +0,0 @@
|
||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"type": "lldb",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "Debug executable 'mclangc-v2'",
|
|
||||||
"cargo": {
|
|
||||||
"args": [
|
|
||||||
"build",
|
|
||||||
"--bin=mclangc-v2",
|
|
||||||
"--package=mclangc-v2"
|
|
||||||
],
|
|
||||||
"filter": {
|
|
||||||
"name": "mclangc-v2",
|
|
||||||
"kind": "bin"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"args": ["-o", "test", "test.mcl", "-r"],
|
|
||||||
"cwd": "${workspaceFolder}"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "lldb",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "Debug unit tests in executable 'mclangc-v2'",
|
|
||||||
"cargo": {
|
|
||||||
"args": [
|
|
||||||
"test",
|
|
||||||
"--no-run",
|
|
||||||
"--bin=mclangc-v2",
|
|
||||||
"--package=mclangc-v2"
|
|
||||||
],
|
|
||||||
"filter": {
|
|
||||||
"name": "mclangc-v2",
|
|
||||||
"kind": "bin"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"args": [],
|
|
||||||
"cwd": "${workspaceFolder}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"files.associations": {
|
|
||||||
"*.s": "platformio-debug.asm"
|
|
||||||
}
|
|
||||||
}
|
|
280
Cargo.lock
generated
280
Cargo.lock
generated
|
@ -3,48 +3,58 @@
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "aho-corasick"
|
||||||
version = "0.6.13"
|
version = "1.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.6.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"anstyle-parse",
|
"anstyle-parse",
|
||||||
"anstyle-query",
|
"anstyle-query",
|
||||||
"anstyle-wincon",
|
"anstyle-wincon",
|
||||||
"colorchoice",
|
"colorchoice",
|
||||||
|
"is_terminal_polyfill",
|
||||||
"utf8parse",
|
"utf8parse",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle"
|
name = "anstyle"
|
||||||
version = "1.0.6"
|
version = "1.0.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-parse"
|
name = "anstyle-parse"
|
||||||
version = "0.2.3"
|
version = "0.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
|
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"utf8parse",
|
"utf8parse",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-query"
|
name = "anstyle-query"
|
||||||
version = "1.0.2"
|
version = "1.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
|
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-wincon"
|
name = "anstyle-wincon"
|
||||||
version = "3.0.2"
|
version = "3.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
|
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
|
@ -52,33 +62,27 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.80"
|
version = "1.0.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
|
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "2.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "camino"
|
name = "camino"
|
||||||
version = "1.1.6"
|
version = "1.1.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c"
|
checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.2"
|
version = "4.5.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b230ab84b0ffdf890d5a10abdbc8b83ae1c4918275daea1ab8801f71536b2651"
|
checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
|
@ -86,9 +90,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.2"
|
version = "4.5.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
|
checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
|
@ -98,9 +102,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.0"
|
version = "4.5.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
|
checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -110,54 +114,86 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.7.0"
|
version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
version = "1.0.0"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "env_filter"
|
||||||
version = "0.4.1"
|
version = "0.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lazy_static"
|
|
||||||
version = "1.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "map-macro"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fb950a42259642e5a3483115aca87eebed2a64886993463af9c9739c205b8d3a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mclangc-v2"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"log",
|
||||||
"bitflags",
|
"regex",
|
||||||
"camino",
|
|
||||||
"clap",
|
|
||||||
"clap_derive",
|
|
||||||
"lazy_static",
|
|
||||||
"map-macro",
|
|
||||||
"parse_int",
|
|
||||||
"snailquote",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "env_logger"
|
||||||
version = "0.2.18"
|
version = "0.11.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
|
checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"env_filter",
|
||||||
|
"humantime",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_terminal_polyfill"
|
||||||
|
version = "1.70.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mclangc"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"camino",
|
||||||
|
"clap",
|
||||||
|
"env_logger",
|
||||||
|
"log",
|
||||||
|
"parse_int",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
@ -173,86 +209,79 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.78"
|
version = "1.0.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.35"
|
version = "1.0.36"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "snailquote"
|
name = "regex"
|
||||||
version = "0.3.1"
|
version = "1.10.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec62a949bda7f15800481a711909f946e1204f2460f89210eaf7f57730f88f86"
|
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror",
|
"aho-corasick",
|
||||||
"unicode_categories",
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "regex-automata"
|
||||||
version = "0.11.0"
|
version = "0.4.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
|
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]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.52"
|
version = "2.0.72"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
|
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thiserror"
|
|
||||||
version = "1.0.58"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
|
|
||||||
dependencies = [
|
|
||||||
"thiserror-impl",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thiserror-impl"
|
|
||||||
version = "1.0.58"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.12"
|
version = "1.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode_categories"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.1"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
|
@ -265,13 +294,14 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.4"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm",
|
"windows_aarch64_gnullvm",
|
||||||
"windows_aarch64_msvc",
|
"windows_aarch64_msvc",
|
||||||
"windows_i686_gnu",
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
"windows_i686_msvc",
|
"windows_i686_msvc",
|
||||||
"windows_x86_64_gnu",
|
"windows_x86_64_gnu",
|
||||||
"windows_x86_64_gnullvm",
|
"windows_x86_64_gnullvm",
|
||||||
|
@ -280,42 +310,48 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.52.4"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.52.4"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.52.4"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.52.4"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.52.4"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.52.4"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.52.4"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
17
Cargo.toml
17
Cargo.toml
|
@ -1,19 +1,14 @@
|
||||||
[package]
|
[package]
|
||||||
name = "mclangc-v2"
|
name = "mclangc"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.80"
|
anyhow = "1.0.86"
|
||||||
bitflags = "2.4.2"
|
camino = "1.1.7"
|
||||||
camino = "1.1.6"
|
clap = { version = "4.5.11", features = ["derive"] }
|
||||||
clap = { version = "4.5.2", features = ["derive"] }
|
env_logger = "0.11.5"
|
||||||
clap_derive = "4.5.0"
|
log = "0.4.22"
|
||||||
lazy_static = "1.4.0"
|
|
||||||
map-macro = "0.3.0"
|
|
||||||
parse_int = "0.6.0"
|
parse_int = "0.6.0"
|
||||||
# serde = { version = "1.0.197", features = ["derive"] }
|
|
||||||
# regex = "1.10.3"
|
|
||||||
snailquote = "0.3.1"
|
|
||||||
|
|
53
REF.md
53
REF.md
|
@ -1,53 +0,0 @@
|
||||||
# Reference
|
|
||||||
|
|
||||||
```mclang
|
|
||||||
typedef str do int ptr end // [int, ptr]
|
|
||||||
|
|
||||||
include "std.mcl"
|
|
||||||
|
|
||||||
const sizeof(u8) 1 end
|
|
||||||
const sizeof(u16) 2 end
|
|
||||||
const sizeof(u32) 4 end
|
|
||||||
const sizeof(u64) 8 end
|
|
||||||
|
|
||||||
structdef Foo do
|
|
||||||
buz do sizeof(u64) end
|
|
||||||
baz do sizeof(u64) end
|
|
||||||
done
|
|
||||||
|
|
||||||
memory s_foo Foo end
|
|
||||||
|
|
||||||
//? Comments :3
|
|
||||||
|
|
||||||
extern fn a with void returns void then done
|
|
||||||
inline fn b with void returns void then done
|
|
||||||
export fn c with void returns void then done
|
|
||||||
|
|
||||||
fn puts with str returns void then drop drop done
|
|
||||||
// fn putd with int returns void then drop done
|
|
||||||
|
|
||||||
fn main with int ptr returns int then
|
|
||||||
// 1 2 add
|
|
||||||
69 _dbg_print
|
|
||||||
"Hewo" puts
|
|
||||||
|
|
||||||
if 3 4 eq do
|
|
||||||
"omg what impossible!\n"
|
|
||||||
else if 1 1 eq do
|
|
||||||
"whaaaaaaaaa\n"
|
|
||||||
else
|
|
||||||
"finally, some good soup\n"
|
|
||||||
done
|
|
||||||
puts
|
|
||||||
|
|
||||||
10
|
|
||||||
while dup 0 gt do
|
|
||||||
"uwu" puts
|
|
||||||
dup _dbg_print
|
|
||||||
1
|
|
||||||
done
|
|
||||||
|
|
||||||
done
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
1
include
1
include
|
@ -1 +0,0 @@
|
||||||
Subproject commit 33cccedea1ed7b86531f250127654d1973d8e36a
|
|
164
src/ast.rs
Normal file
164
src/ast.rs
Normal 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,
|
||||||
|
}
|
||||||
|
|
129
src/cli.rs
129
src/cli.rs
|
@ -1,129 +0,0 @@
|
||||||
use clap::{builder::PossibleValue, Parser, ValueEnum};
|
|
||||||
use camino::Utf8PathBuf;
|
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
|
||||||
static ref DEFAULT_INCLUDE_PATHS: Vec<Utf8PathBuf> = vec![
|
|
||||||
Utf8PathBuf::from("./"),
|
|
||||||
Utf8PathBuf::from("./include"),
|
|
||||||
Utf8PathBuf::from("~/.mclang/include"),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
|
||||||
pub struct CliArgs {
|
|
||||||
/// Only compile, dont link
|
|
||||||
#[arg(long, short)]
|
|
||||||
pub compile: bool,
|
|
||||||
|
|
||||||
/// Verosity
|
|
||||||
/// -1 - Nothing
|
|
||||||
/// 0 - Only errors
|
|
||||||
/// 1 - Normal
|
|
||||||
/// 2 - Verbose
|
|
||||||
/// 3 - Tracing
|
|
||||||
#[arg(long, short, default_value_t=1)]
|
|
||||||
pub verbose: i8,
|
|
||||||
|
|
||||||
/// Runt the program after compilation
|
|
||||||
#[arg(long, short)]
|
|
||||||
pub run: bool,
|
|
||||||
|
|
||||||
/// Output execuable file path
|
|
||||||
#[arg(long, short, default_value="./a.out")]
|
|
||||||
pub output: Utf8PathBuf,
|
|
||||||
|
|
||||||
/// Paths to search for libraries
|
|
||||||
#[arg(long="include", short='I', default_values_t=DEFAULT_INCLUDE_PATHS.clone().into_iter())]
|
|
||||||
pub include_path: Vec<Utf8PathBuf>,
|
|
||||||
|
|
||||||
/// Target to compile to
|
|
||||||
#[arg(long, short='T', default_value_t=CompilationTarget::X86_64_linux_nasm)]
|
|
||||||
pub target: CompilationTarget,
|
|
||||||
|
|
||||||
/// Input code files
|
|
||||||
#[arg(required=true, num_args=1..)]
|
|
||||||
pub input: Vec<Utf8PathBuf>,
|
|
||||||
|
|
||||||
#[clap(skip)]
|
|
||||||
pub passthrough: Vec<String>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl CliArgs {
|
|
||||||
pub fn parse_with_passthrough() -> Self {
|
|
||||||
let mut clap_args = Vec::new();
|
|
||||||
let mut pt_args = Vec::new();
|
|
||||||
let mut switch = false;
|
|
||||||
for arg in std::env::args() {
|
|
||||||
if arg == String::from("--") {
|
|
||||||
switch = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !switch {
|
|
||||||
//clap args
|
|
||||||
clap_args.push(arg);
|
|
||||||
} else {
|
|
||||||
// passwthrough
|
|
||||||
pt_args.push(arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut cargs = Self::parse_from(clap_args);
|
|
||||||
cargs.passthrough = pt_args;
|
|
||||||
|
|
||||||
cargs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum CompilationTarget {
|
|
||||||
X86_64_linux_nasm
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ValueEnum for CompilationTarget {
|
|
||||||
fn value_variants<'a>() -> &'a [Self] {
|
|
||||||
&[
|
|
||||||
Self::X86_64_linux_nasm
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
|
|
||||||
match self {
|
|
||||||
CompilationTarget::X86_64_linux_nasm => Some(PossibleValue::new("x86_64-linux-nasm")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for CompilationTarget {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
let r = match self {
|
|
||||||
CompilationTarget::X86_64_linux_nasm => "x86_64-linux-nasm",
|
|
||||||
};
|
|
||||||
write!(f, "{}", r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// impl From<CompilationTarget> for clap::builder::OsStr {
|
|
||||||
// fn from(value: CompilationTarget) -> Self {
|
|
||||||
// match value {
|
|
||||||
// CompilationTarget::X86_64_linux_nasm => "X86_64_linux_nasm".into()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl TryFrom<&str> for CompilationTarget {
|
|
||||||
// type Error = anyhow::Error;
|
|
||||||
// fn try_from(value: &str) -> Result<Self, Error> {
|
|
||||||
// match value {
|
|
||||||
// "X86_64_linux_nasm" => Ok(CompilationTarget::X86_64_linux_nasm)
|
|
||||||
// _ => bail!("Unknown compilation target {value}")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
24
src/cliargs.rs
Normal file
24
src/cliargs.rs
Normal 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
26
src/common.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,104 +0,0 @@
|
||||||
mod x86_64_linux_nasm;
|
|
||||||
mod utils;
|
|
||||||
|
|
||||||
use anyhow::bail;
|
|
||||||
|
|
||||||
use crate::{cli::{CliArgs, CompilationTarget}, types::ast::Program};
|
|
||||||
use std::{collections::HashMap, fs::File, io::{BufWriter, Write}, path::{Path, PathBuf}, process::Command};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub trait Compiler {
|
|
||||||
fn new() -> Self;
|
|
||||||
fn generate_asm(&mut self, prog: &Program, fd: &mut BufWriter<File>) -> anyhow::Result<()>;
|
|
||||||
fn compile(&mut self, asm_fp: &Path, obj: &Path) -> anyhow::Result<()>;
|
|
||||||
fn link(&mut self, obj_files: Vec<PathBuf>, bin_fp: &Path) -> anyhow::Result<()>;
|
|
||||||
/// Return programs that are needed
|
|
||||||
fn needed_dependencies(&mut self) -> Vec<&str>;
|
|
||||||
}
|
|
||||||
|
|
||||||
//NOTE: No bsd cause im not about to create 3 or 4 diffrent compilation targets
|
|
||||||
|
|
||||||
pub fn compile_program(cli_args: &CliArgs, prog_map: HashMap<&Path, Program>) -> anyhow::Result<()> {
|
|
||||||
let mut compiler = match cli_args.target {
|
|
||||||
CompilationTarget::X86_64_linux_nasm => x86_64_linux_nasm::X86_64LinuxNasmCompiler::new(),
|
|
||||||
};
|
|
||||||
let bin_p = cli_args.output.as_std_path();
|
|
||||||
let mut objs = Vec::new();
|
|
||||||
for (k, v) in prog_map {
|
|
||||||
let mut asm_p = k.to_path_buf();
|
|
||||||
let mut obj_p = k.to_path_buf();
|
|
||||||
|
|
||||||
asm_p.set_extension("s");
|
|
||||||
obj_p.set_extension("o");
|
|
||||||
|
|
||||||
|
|
||||||
if let Err(_) = compile_file(&mut compiler, cli_args, asm_p.as_path(), obj_p.as_path(), &v) {
|
|
||||||
error!("Failed to compile file {k:?}");
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
objs.push(obj_p.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(e) = compiler.link(objs, bin_p) {
|
|
||||||
error!("Failed to link program: {e}");
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("Finished building program");
|
|
||||||
|
|
||||||
if cli_args.run {
|
|
||||||
// run_cmd(format!("./{}", bin_p.to_string_lossy()), cli_args.passthrough.clone())?;
|
|
||||||
let bin = bin_p.to_string_lossy().to_string();
|
|
||||||
let mut cmd = Command::new(format!("./{}", bin.clone()));
|
|
||||||
let cmd = cmd.args(cli_args.passthrough.clone());
|
|
||||||
|
|
||||||
let child = match cmd.spawn() {
|
|
||||||
Ok(c) => c,
|
|
||||||
Err(e) => {
|
|
||||||
error!("Unable to run {cmd:?}: {e}");
|
|
||||||
bail!("");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let ret = child.wait_with_output().expect("fuck i know");
|
|
||||||
|
|
||||||
|
|
||||||
if !ret.status.success() {
|
|
||||||
error!("Process running {bin:?} exited abnormaly, run with -v 2 for more output");
|
|
||||||
bail!("")
|
|
||||||
} else {
|
|
||||||
info!("Process exited successfully")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn compile_file<C: Compiler>(compiler: &mut C, _: &CliArgs, asm_file: &Path, obj_file: &Path, prog: &Program) -> anyhow::Result<()> {
|
|
||||||
|
|
||||||
let asm_fd = std::fs::File::options()
|
|
||||||
.write(true)
|
|
||||||
.write(true)
|
|
||||||
.create(true)
|
|
||||||
.truncate(true)
|
|
||||||
.append(false)
|
|
||||||
.open(asm_file);
|
|
||||||
|
|
||||||
let asm_fd = match asm_fd {
|
|
||||||
Ok(fd) => fd,
|
|
||||||
Err(e) => {
|
|
||||||
error!("Failed to open file {asm_file:?}: {e}");
|
|
||||||
bail!("");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut buf_asm_fd = BufWriter::new(asm_fd);
|
|
||||||
|
|
||||||
compiler.generate_asm(prog, &mut buf_asm_fd)?;
|
|
||||||
buf_asm_fd.flush()?;
|
|
||||||
|
|
||||||
compiler.compile(asm_file, obj_file)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
use std::{fmt::Debug, process::{Command, Stdio}};
|
|
||||||
|
|
||||||
use anyhow::bail;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub fn run_cmd<'a, S: Into<String> + Debug + Clone>(bin: S, args: Vec<String>) -> anyhow::Result<()> {
|
|
||||||
let debug = unsafe {
|
|
||||||
crate::logger::LOGGER.enabled(crate::logger::Level::Debug)
|
|
||||||
};
|
|
||||||
let mut cmd = Command::new(bin.clone().into());
|
|
||||||
let cmd = cmd.args(args);
|
|
||||||
let cmd = if debug {
|
|
||||||
cmd.stdout(Stdio::inherit())
|
|
||||||
} else {
|
|
||||||
cmd.stdout(Stdio::null())
|
|
||||||
};
|
|
||||||
let cmd = cmd.stderr(Stdio::inherit());
|
|
||||||
|
|
||||||
let child = match cmd.spawn() {
|
|
||||||
Ok(c) => c,
|
|
||||||
Err(e) => {
|
|
||||||
error!("Unable to run {cmd:?}: {e}");
|
|
||||||
bail!("");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let ret = child.wait_with_output().expect("fuck i know");
|
|
||||||
|
|
||||||
|
|
||||||
if !ret.status.success() {
|
|
||||||
error!("Process running {bin:?} exited abnormaly, run with -v 2 for more output");
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
|
@ -1,521 +0,0 @@
|
||||||
mod utils;
|
|
||||||
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::{fs::File, io::BufWriter, path::Path};
|
|
||||||
use std::io::Write;
|
|
||||||
use crate::types::ast::{AstNode, EscIdent, Function, MemSize, Module, Program};
|
|
||||||
use crate::types::token::{InstructionType, Token, TokenType};
|
|
||||||
|
|
||||||
use super::utils::run_cmd;
|
|
||||||
use super::Compiler;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub struct X86_64LinuxNasmCompiler {
|
|
||||||
strings: Vec<String>,
|
|
||||||
// func_mem_i: Vec<usize>,
|
|
||||||
// func_mem_list: HashMap<String, usize>,
|
|
||||||
if_i: usize,
|
|
||||||
while_i: usize,
|
|
||||||
used_consts: Vec<String>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl X86_64LinuxNasmCompiler {
|
|
||||||
fn handle_token(&mut self, fd: &mut BufWriter<File>, _: &Program, token: &Token) -> anyhow::Result<()> {
|
|
||||||
match &token.typ {
|
|
||||||
TokenType::Instruction(it) => {
|
|
||||||
match it {
|
|
||||||
InstructionType::PushInt(i) => {
|
|
||||||
writeln!(fd, " mov rax, {i} ; PUSHINT({i})")?;
|
|
||||||
writeln!(fd, " push rax")?;
|
|
||||||
},
|
|
||||||
InstructionType::PushStr(s) => {
|
|
||||||
writeln!(fd, " push {}", s.len())?;
|
|
||||||
writeln!(fd, " push str_{}; PUSHSTR({})", self.strings.len(), s.escape_debug())?;
|
|
||||||
self.strings.push(s.clone());
|
|
||||||
},
|
|
||||||
InstructionType::PushCStr(s) => {
|
|
||||||
writeln!(fd, " push str_{}; PUSHCSTR({})", self.strings.len(), s.escape_debug())?;
|
|
||||||
self.strings.push(s.clone());
|
|
||||||
},
|
|
||||||
InstructionType::PushChar(c) => {
|
|
||||||
writeln!(fd, " push {}; PUSHCHAR({})", *c as u8, c.escape_debug())?;
|
|
||||||
},
|
|
||||||
InstructionType::Drop => {
|
|
||||||
writeln!(fd, " pop rax ; DROP")?;
|
|
||||||
},
|
|
||||||
InstructionType::Print => {
|
|
||||||
writeln!(fd, " pop rdi")?;
|
|
||||||
writeln!(fd, " call _dbg_print ; _DBG_PRINT")?;
|
|
||||||
},
|
|
||||||
InstructionType::Dup => {
|
|
||||||
writeln!(fd, " pop rax ; DUP")?;
|
|
||||||
writeln!(fd, " push rax")?;
|
|
||||||
writeln!(fd, " push rax")?;
|
|
||||||
},
|
|
||||||
InstructionType::Rot => {
|
|
||||||
writeln!(fd, " pop rax ; ROT")?;
|
|
||||||
writeln!(fd, " pop rbx")?;
|
|
||||||
writeln!(fd, " pop rcx")?;
|
|
||||||
writeln!(fd, " push rbx")?;
|
|
||||||
writeln!(fd, " push rax")?;
|
|
||||||
writeln!(fd, " push rcx")?;
|
|
||||||
},
|
|
||||||
InstructionType::Over => {
|
|
||||||
writeln!(fd, " pop rax ; OVER")?;
|
|
||||||
writeln!(fd, " pop rbx")?;
|
|
||||||
writeln!(fd, " push rbx")?;
|
|
||||||
writeln!(fd, " push rax")?;
|
|
||||||
writeln!(fd, " push rbx")?;
|
|
||||||
},
|
|
||||||
InstructionType::Swap => {
|
|
||||||
writeln!(fd, " pop rax ; SWAP")?;
|
|
||||||
writeln!(fd, " pop rbx")?;
|
|
||||||
writeln!(fd, " push rax")?;
|
|
||||||
writeln!(fd, " push rbx")?;
|
|
||||||
}
|
|
||||||
InstructionType::Minus => {
|
|
||||||
writeln!(fd, " pop rax ; SUB")?;
|
|
||||||
writeln!(fd, " pop rbx")?;
|
|
||||||
writeln!(fd, " sub rbx, rax")?;
|
|
||||||
writeln!(fd, " push rbx")?;
|
|
||||||
},
|
|
||||||
InstructionType::Plus => {
|
|
||||||
writeln!(fd, " pop rax ; ADD")?;
|
|
||||||
writeln!(fd, " pop rbx")?;
|
|
||||||
writeln!(fd, " add rax, rbx")?;
|
|
||||||
writeln!(fd, " push rax")?;
|
|
||||||
},
|
|
||||||
InstructionType::Equals => {
|
|
||||||
writeln!(fd, " mov rcx, 0 ; EQ")?;
|
|
||||||
writeln!(fd, " mov rdx, 1")?;
|
|
||||||
writeln!(fd, " pop rax")?;
|
|
||||||
writeln!(fd, " pop rbx")?;
|
|
||||||
writeln!(fd, " cmp rax, rbx")?;
|
|
||||||
writeln!(fd, " cmove rcx, rdx")?;
|
|
||||||
writeln!(fd, " push rcx")?;
|
|
||||||
},
|
|
||||||
InstructionType::Gt => {
|
|
||||||
writeln!(fd, " mov rcx, 0 ; GT")?;
|
|
||||||
writeln!(fd, " mov rdx, 1")?;
|
|
||||||
writeln!(fd, " pop rbx")?;
|
|
||||||
writeln!(fd, " pop rax")?;
|
|
||||||
writeln!(fd, " cmp rax, rbx")?;
|
|
||||||
writeln!(fd, " cmovg rcx, rdx")?;
|
|
||||||
writeln!(fd, " push rcx")?;
|
|
||||||
},
|
|
||||||
InstructionType::Lt => {
|
|
||||||
writeln!(fd, " mov rcx, 0 ; LT")?;
|
|
||||||
writeln!(fd, " mov rdx, 1")?;
|
|
||||||
writeln!(fd, " pop rbx")?;
|
|
||||||
writeln!(fd, " pop rax")?;
|
|
||||||
writeln!(fd, " cmp rax, rbx")?;
|
|
||||||
writeln!(fd, " cmovl rcx, rdx")?;
|
|
||||||
writeln!(fd, " push rcx")?;
|
|
||||||
},
|
|
||||||
InstructionType::Ge => {
|
|
||||||
writeln!(fd, " mov rcx, 0 ; GE")?;
|
|
||||||
writeln!(fd, " mov rdx, 1")?;
|
|
||||||
writeln!(fd, " pop rbx")?;
|
|
||||||
writeln!(fd, " pop rax")?;
|
|
||||||
writeln!(fd, " cmp rax, rbx")?;
|
|
||||||
writeln!(fd, " cmovge rcx, rdx")?;
|
|
||||||
writeln!(fd, " push rcx")?;
|
|
||||||
},
|
|
||||||
InstructionType::Le => {
|
|
||||||
writeln!(fd, " mov rcx, 0 ; LE")?;
|
|
||||||
writeln!(fd, " mov rdx, 1")?;
|
|
||||||
writeln!(fd, " pop rbx")?;
|
|
||||||
writeln!(fd, " pop rax")?;
|
|
||||||
writeln!(fd, " cmp rax, rbx")?;
|
|
||||||
writeln!(fd, " cmovle rcx, rdx")?;
|
|
||||||
writeln!(fd, " push rcx")?;
|
|
||||||
},
|
|
||||||
InstructionType::NotEquals => {
|
|
||||||
writeln!(fd, " mov rdx, 1 ; NEQ")?;
|
|
||||||
writeln!(fd, " mov rcx, 0")?;
|
|
||||||
writeln!(fd, " pop rax")?;
|
|
||||||
writeln!(fd, " pop rbx")?;
|
|
||||||
writeln!(fd, " cmp rax, rbx")?;
|
|
||||||
writeln!(fd, " cmove rcx, rdx")?;
|
|
||||||
writeln!(fd, " push rcx")?;
|
|
||||||
},
|
|
||||||
InstructionType::Band => {
|
|
||||||
writeln!(fd, " pop rax ; BAND")?;
|
|
||||||
writeln!(fd, " pop rbx")?;
|
|
||||||
writeln!(fd, " and rbx, rax")?;
|
|
||||||
writeln!(fd, " push rbx")?;
|
|
||||||
},
|
|
||||||
InstructionType::Bor => {
|
|
||||||
writeln!(fd, " pop rax ; BOR")?;
|
|
||||||
writeln!(fd, " pop rbx")?;
|
|
||||||
writeln!(fd, " or rbx, rax")?;
|
|
||||||
writeln!(fd, " push rbx")?;
|
|
||||||
}
|
|
||||||
InstructionType::Shr => {
|
|
||||||
writeln!(fd, " pop rcx")?;
|
|
||||||
writeln!(fd, " pop rbx")?;
|
|
||||||
writeln!(fd, " shr rbx, cl")?;
|
|
||||||
writeln!(fd, " push rbx")?;
|
|
||||||
},
|
|
||||||
InstructionType::Shl => {
|
|
||||||
writeln!(fd, " pop rcx")?;
|
|
||||||
writeln!(fd, " pop rbx")?;
|
|
||||||
writeln!(fd, " shl rbx, cl")?;
|
|
||||||
writeln!(fd, " push rbx")?;
|
|
||||||
},
|
|
||||||
InstructionType::DivMod => {
|
|
||||||
writeln!(fd, " xor rdx, rdx")?;
|
|
||||||
writeln!(fd, " pop rbx")?;
|
|
||||||
writeln!(fd, " pop rax")?;
|
|
||||||
writeln!(fd, " div rbx")?;
|
|
||||||
writeln!(fd, " push rax")?;
|
|
||||||
writeln!(fd, " push rdx")?;
|
|
||||||
},
|
|
||||||
InstructionType::Mul => {
|
|
||||||
writeln!(fd, " pop rax ; MUL")?;
|
|
||||||
writeln!(fd, " pop rbx")?;
|
|
||||||
writeln!(fd, " mul rbx")?;
|
|
||||||
writeln!(fd, " push rax")?;
|
|
||||||
},
|
|
||||||
InstructionType::Read8 => {
|
|
||||||
writeln!(fd, " pop rax ; READ8")?;
|
|
||||||
writeln!(fd, " xor rbx, rbx")?;
|
|
||||||
writeln!(fd, " mov bl, byte [rax]")?;
|
|
||||||
writeln!(fd, " push rbx")?;
|
|
||||||
}
|
|
||||||
InstructionType::Write8 => {
|
|
||||||
writeln!(fd, " pop rax ; WRITE 8")?;
|
|
||||||
writeln!(fd, " xor rbx, rbx")?;
|
|
||||||
writeln!(fd, " mov ebx, dword [rax]")?;
|
|
||||||
writeln!(fd, " push rbx")?;
|
|
||||||
},
|
|
||||||
InstructionType::Read32 => {
|
|
||||||
writeln!(fd, " pop rax ; READ 32")?;
|
|
||||||
writeln!(fd, " xor rbx, rbx")?;
|
|
||||||
writeln!(fd, " mov ebx, dword [rax]")?;
|
|
||||||
writeln!(fd, " push rbx")?;
|
|
||||||
},
|
|
||||||
InstructionType::Write32 => {
|
|
||||||
writeln!(fd, " pop rbx ; WRITE 32")?;
|
|
||||||
writeln!(fd, " pop rax")?;
|
|
||||||
writeln!(fd, " mov dword[rax], ebx")?;
|
|
||||||
},
|
|
||||||
InstructionType::Read64 => {
|
|
||||||
writeln!(fd, " pop rax ; READ 64")?;
|
|
||||||
writeln!(fd, " xor rbx, rbx")?;
|
|
||||||
writeln!(fd, " mov rbx, qword [rax]")?;
|
|
||||||
writeln!(fd, " push rbx")?;
|
|
||||||
},
|
|
||||||
InstructionType::Write64 => {
|
|
||||||
writeln!(fd, " pop rbx ; WRITE 64")?;
|
|
||||||
writeln!(fd, " pop rax")?;
|
|
||||||
writeln!(fd, " mov qword[rax], rbx")?;
|
|
||||||
},
|
|
||||||
InstructionType::Syscall0 => {
|
|
||||||
writeln!(fd, " pop rax")?;
|
|
||||||
writeln!(fd, " syscall")?;
|
|
||||||
writeln!(fd, " push rax")?;
|
|
||||||
},
|
|
||||||
InstructionType::Syscall1 => {
|
|
||||||
writeln!(fd, " pop rax")?;
|
|
||||||
writeln!(fd, " pop rdi")?;
|
|
||||||
writeln!(fd, " syscall")?;
|
|
||||||
writeln!(fd, " push rax")?;
|
|
||||||
},
|
|
||||||
InstructionType::Syscall2 => {
|
|
||||||
writeln!(fd, " pop rax")?;
|
|
||||||
writeln!(fd, " pop rdi")?;
|
|
||||||
writeln!(fd, " pop rsi")?;
|
|
||||||
writeln!(fd, " syscall")?;
|
|
||||||
writeln!(fd, " push rax")?;
|
|
||||||
},
|
|
||||||
InstructionType::Syscall3 => {
|
|
||||||
writeln!(fd, " pop rax")?;
|
|
||||||
writeln!(fd, " pop rdi")?;
|
|
||||||
writeln!(fd, " pop rsi")?;
|
|
||||||
writeln!(fd, " pop rdx")?;
|
|
||||||
writeln!(fd, " syscall")?;
|
|
||||||
writeln!(fd, " push rax")?;
|
|
||||||
},
|
|
||||||
InstructionType::Syscall4 => {
|
|
||||||
writeln!(fd, " pop rax")?;
|
|
||||||
writeln!(fd, " pop rdi")?;
|
|
||||||
writeln!(fd, " pop rsi")?;
|
|
||||||
writeln!(fd, " pop rdx")?;
|
|
||||||
writeln!(fd, " pop r10")?;
|
|
||||||
writeln!(fd, " syscall")?;
|
|
||||||
writeln!(fd, " push rax")?;
|
|
||||||
},
|
|
||||||
InstructionType::Syscall5 => {
|
|
||||||
writeln!(fd, " pop rax")?;
|
|
||||||
writeln!(fd, " pop rdi")?;
|
|
||||||
writeln!(fd, " pop rsi")?;
|
|
||||||
writeln!(fd, " pop rdx")?;
|
|
||||||
writeln!(fd, " pop r10")?;
|
|
||||||
writeln!(fd, " pop r8")?;
|
|
||||||
writeln!(fd, " syscall")?;
|
|
||||||
writeln!(fd, " push rax")?;
|
|
||||||
},
|
|
||||||
InstructionType::Syscall6 => {
|
|
||||||
writeln!(fd, " pop rax")?;
|
|
||||||
writeln!(fd, " pop rdi")?;
|
|
||||||
writeln!(fd, " pop rsi")?;
|
|
||||||
writeln!(fd, " pop rdx")?;
|
|
||||||
writeln!(fd, " pop r10")?;
|
|
||||||
writeln!(fd, " pop r8")?;
|
|
||||||
writeln!(fd, " pop r9")?;
|
|
||||||
writeln!(fd, " syscall")?;
|
|
||||||
writeln!(fd, " push rax")?;
|
|
||||||
},
|
|
||||||
InstructionType::CastBool |
|
|
||||||
InstructionType::CastPtr |
|
|
||||||
InstructionType::CastInt |
|
|
||||||
InstructionType::CastVoid => (), //? Possibly have a use for this
|
|
||||||
InstructionType::FnCall |
|
|
||||||
InstructionType::MemUse |
|
|
||||||
InstructionType::StructPath(_) |
|
|
||||||
InstructionType::StructItem(_) |
|
|
||||||
InstructionType::ConstUse => unreachable!(),
|
|
||||||
InstructionType::Return => {
|
|
||||||
writeln!(fd, " sub rbp, 8")?;
|
|
||||||
writeln!(fd, " mov rbx, qword [rbp]")?;
|
|
||||||
writeln!(fd, " push rbx")?;
|
|
||||||
writeln!(fd, " ret")?;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
TokenType::Keyword(_) |
|
|
||||||
TokenType::Type(_) |
|
|
||||||
TokenType::Unknown(_) => unreachable!(),
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_module(&mut self, fd: &mut BufWriter<File>, prog: &Program, module: &Module) -> anyhow::Result<()> {
|
|
||||||
writeln!(fd, "; {} Module START", module.path.join("::"))?;
|
|
||||||
self.handle_ast_list(fd, prog, module.body.clone())?;
|
|
||||||
writeln!(fd, "; {} Module END", module.path.join("::"))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_function(&mut self, fd: &mut BufWriter<File>, prog: &Program, func: &Function) -> anyhow::Result<()> {
|
|
||||||
writeln!(fd, "{f}: ; fn {f}", f=func.get_ident_escaped())?;
|
|
||||||
writeln!(fd, " fn_setup")?;
|
|
||||||
|
|
||||||
self.handle_ast_list(fd, prog, func.body.clone())?;
|
|
||||||
|
|
||||||
writeln!(fd, " fn_cleanup")?;
|
|
||||||
writeln!(fd, " ret")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_ast_list(&mut self, fd: &mut BufWriter<File>, prog: &Program, ast: Vec<AstNode>) -> anyhow::Result<()> {
|
|
||||||
for node in ast {
|
|
||||||
match &node {
|
|
||||||
AstNode::Function(f) => self.handle_function(fd, prog, f)?,
|
|
||||||
AstNode::Constant(_) => (),
|
|
||||||
AstNode::If(i) => {
|
|
||||||
let id = self.if_i;
|
|
||||||
self.if_i += 1;
|
|
||||||
|
|
||||||
writeln!(fd, "; IF({id}) START")?;
|
|
||||||
self.handle_ast_list(fd, prog, i.test.clone())?;
|
|
||||||
writeln!(fd, " pop rax")?;
|
|
||||||
writeln!(fd, " test rax, rax")?;
|
|
||||||
writeln!(fd, " jz if_{id}_else")?;
|
|
||||||
writeln!(fd, "if_{id}_start:")?;
|
|
||||||
self.handle_ast_list(fd, prog, i.body.clone())?;
|
|
||||||
writeln!(fd, " jmp if_{id}_end")?;
|
|
||||||
writeln!(fd, "if_{id}_else:")?;
|
|
||||||
self.handle_ast_list(fd, prog, vec![Box::leak(i.els.clone()).clone()])?;
|
|
||||||
writeln!(fd, "if_{id}_end:")?;
|
|
||||||
writeln!(fd, "; IF({id}) END")?;
|
|
||||||
},
|
|
||||||
AstNode::While(w) => {
|
|
||||||
let id = self.while_i;
|
|
||||||
self.while_i += 1;
|
|
||||||
writeln!(fd, "; WHILE({id}) START")?;
|
|
||||||
writeln!(fd, "while_{id}_test:")?;
|
|
||||||
self.handle_ast_list(fd, prog, w.test.clone())?;
|
|
||||||
writeln!(fd, " pop rax")?;
|
|
||||||
writeln!(fd, " test rax, rax")?;
|
|
||||||
writeln!(fd, " jz while_{id}_exit")?;
|
|
||||||
writeln!(fd, "while_{id}_start:")?;
|
|
||||||
self.handle_ast_list(fd, prog, w.body.clone())?;
|
|
||||||
writeln!(fd, "while_{id}_end:")?;
|
|
||||||
writeln!(fd, " jmp while_{id}_test")?;
|
|
||||||
writeln!(fd, "while_{id}_exit:")?;
|
|
||||||
writeln!(fd, "; WHILE({id}) END")?;
|
|
||||||
},
|
|
||||||
AstNode::Module(m) => self.handle_module(fd, prog, m)?,
|
|
||||||
AstNode::Memory(_) => {
|
|
||||||
//? Possibly allow stack based allocation somehow
|
|
||||||
// if !m.statc {
|
|
||||||
// todo!()
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
AstNode::MemUse(m) => {
|
|
||||||
let tmp = if let Some(disp) = m.disp {
|
|
||||||
format!("+{disp}")
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
};
|
|
||||||
writeln!(fd, " push m_{}{}", m.get_ident_escaped(), tmp)?;
|
|
||||||
},
|
|
||||||
AstNode::ConstUse(c) => {
|
|
||||||
self.used_consts.push(c.get_ident_escaped());
|
|
||||||
writeln!(fd, " mov rax, qword [c_{}]", c.get_ident_escaped())?;
|
|
||||||
writeln!(fd, " push rax")?;
|
|
||||||
},
|
|
||||||
AstNode::FnCall(f)=> {
|
|
||||||
writeln!(fd, " call {f} ; FUNCTIONCALL({f:?})", f=f.get_ident_escaped())?;
|
|
||||||
},
|
|
||||||
AstNode::Block(b)=> {
|
|
||||||
writeln!(fd, "; BLOCK({}) START", b.comment)?;
|
|
||||||
self.handle_ast_list(fd, prog, b.body.clone())?;
|
|
||||||
writeln!(fd, "; BLOCK({}) END", b.comment)?;
|
|
||||||
},
|
|
||||||
AstNode::Token(t) => self.handle_token(fd, prog, t)?,
|
|
||||||
AstNode::Int(_, _) |
|
|
||||||
AstNode::Str(_, _) |
|
|
||||||
AstNode::CStr(_, _) |
|
|
||||||
AstNode::Char(_, _) => unreachable!(),
|
|
||||||
AstNode::StructDef(_) => (),
|
|
||||||
AstNode::StructDispPush { disp, ident, .. } => {
|
|
||||||
writeln!(fd, " mov rax, {} ; STRUCTDISPPUSH({})", disp, ident)?;
|
|
||||||
writeln!(fd, " push rax")?;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl Compiler for X86_64LinuxNasmCompiler {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
strings: Vec::new(),
|
|
||||||
used_consts: Vec::new(),
|
|
||||||
if_i: 0,
|
|
||||||
while_i: 0,
|
|
||||||
// func_mem_i: Vec::new(),
|
|
||||||
// func_mem_list: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_asm(&mut self, prog: &Program, fd: &mut BufWriter<File>) -> anyhow::Result<()> {
|
|
||||||
|
|
||||||
writeln!(fd, "BITS 64")?;
|
|
||||||
writeln!(fd, "segment .text")?;
|
|
||||||
|
|
||||||
writeln!(fd, "%macro fn_setup 0")?;
|
|
||||||
writeln!(fd, " pop rbx")?;
|
|
||||||
writeln!(fd, " mov qword [rbp], rbx")?;
|
|
||||||
writeln!(fd, " add rbp, 8")?;
|
|
||||||
writeln!(fd, "%endmacro")?;
|
|
||||||
writeln!(fd, "%macro fn_cleanup 0")?;
|
|
||||||
writeln!(fd, " sub rbp, 8")?;
|
|
||||||
writeln!(fd, " mov rbx, qword [rbp]")?;
|
|
||||||
writeln!(fd, " push rbx")?;
|
|
||||||
writeln!(fd, "%endmacro")?;
|
|
||||||
|
|
||||||
writeln!(fd, "{}", utils::DBG_PRINT)?;
|
|
||||||
writeln!(fd, "global _start")?;
|
|
||||||
writeln!(fd, "_start:")?;
|
|
||||||
writeln!(fd, " lea rbp, [rel ret_stack]")?;
|
|
||||||
writeln!(fd, " call main")?;
|
|
||||||
writeln!(fd, " jmp __MCL_END__")?;
|
|
||||||
|
|
||||||
match &prog.ast {
|
|
||||||
AstNode::Module(m) => {
|
|
||||||
self.handle_module(fd, prog, m)?;
|
|
||||||
},
|
|
||||||
_ => panic!()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
writeln!(fd, "__MCL_END__:")?;
|
|
||||||
writeln!(fd, " mov rax, 60")?;
|
|
||||||
writeln!(fd, " mov rdi, 0")?;
|
|
||||||
writeln!(fd, " syscall")?;
|
|
||||||
|
|
||||||
writeln!(fd, "segment .data")?;
|
|
||||||
for (_, v) in prog.constants.iter() {
|
|
||||||
|
|
||||||
if !self.used_consts.contains(&v.get_ident_escaped()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
match Box::leak(v.value.clone()) {
|
|
||||||
AstNode::Int(_, val) => {
|
|
||||||
writeln!(fd, " c_{}: dq {}", v.get_ident_escaped(), val)?;
|
|
||||||
}
|
|
||||||
AstNode::Str(_, val) |
|
|
||||||
AstNode::CStr(_, val) => {
|
|
||||||
let s_chars = val.chars().map(|c| (c as u32).to_string()).collect::<Vec<String>>();
|
|
||||||
let s_list = s_chars.join(",");
|
|
||||||
writeln!(fd, " c_{}: db {} ; {}", v.get_ident_escaped(), s_list, val.escape_debug())?;
|
|
||||||
}
|
|
||||||
AstNode::Char(_, val) => {
|
|
||||||
writeln!(fd, " c_{}: db {} ; '{}'", v.get_ident_escaped(), *val as u8, val)?;
|
|
||||||
}
|
|
||||||
c => panic!("{c:?}")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i, s) in self.strings.iter().enumerate() {
|
|
||||||
let s_chars = s.chars().map(|c| (c as u32).to_string()).collect::<Vec<String>>();
|
|
||||||
let s_list = s_chars.join(",");
|
|
||||||
writeln!(fd, " str_{i}: db {} ; STRDEF({})", s_list, s.escape_debug())?;
|
|
||||||
}
|
|
||||||
writeln!(fd, "segment .bss")?;
|
|
||||||
writeln!(fd, " ret_stack: resq 256")?;
|
|
||||||
|
|
||||||
for (_, v) in &prog.memories {
|
|
||||||
match &v.size {
|
|
||||||
MemSize::Size(s) => {
|
|
||||||
writeln!(fd, " m_{}: resb {}", v.get_ident_escaped(), s)?;
|
|
||||||
},
|
|
||||||
MemSize::Type(tt) => {
|
|
||||||
writeln!(fd, " m_{}: resb {} ; {:?}", v.get_ident_escaped(), tt.get_size(), tt)?;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn compile(&mut self, asm_fp: &Path, obj_fp: &Path) -> anyhow::Result<()> {
|
|
||||||
run_cmd("nasm", vec![
|
|
||||||
String::from("-felf64"),
|
|
||||||
String::from("-o"),
|
|
||||||
obj_fp.to_string_lossy().to_string(),
|
|
||||||
asm_fp.to_string_lossy().to_string()
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn link(&mut self, obj_files: Vec<PathBuf>, bin_fp: &Path) -> anyhow::Result<()> {
|
|
||||||
let mut args = vec![
|
|
||||||
String::from("-o"),
|
|
||||||
bin_fp.to_string_lossy().to_string(),
|
|
||||||
];
|
|
||||||
|
|
||||||
for f in obj_files {
|
|
||||||
args.push(f.to_string_lossy().to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
run_cmd("ld", args)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn needed_dependencies(&mut self) -> Vec<&str> {
|
|
||||||
vec![
|
|
||||||
"nasm",
|
|
||||||
"ld"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
pub const DBG_PRINT: &'static str = "
|
|
||||||
_dbg_print:
|
|
||||||
mov r9, -3689348814741910323
|
|
||||||
sub rsp, 40
|
|
||||||
mov BYTE [rsp+31], 10
|
|
||||||
lea rcx, [rsp+30]
|
|
||||||
.L2:
|
|
||||||
mov rax, rdi
|
|
||||||
lea r8, [rsp+32]
|
|
||||||
mul r9
|
|
||||||
mov rax, rdi
|
|
||||||
sub r8, rcx
|
|
||||||
shr rdx, 3
|
|
||||||
lea rsi, [rdx+rdx*4]
|
|
||||||
add rsi, rsi
|
|
||||||
sub rax, rsi
|
|
||||||
add eax, 48
|
|
||||||
mov BYTE [rcx], al
|
|
||||||
mov rax, rdi
|
|
||||||
mov rdi, rdx
|
|
||||||
mov rdx, rcx
|
|
||||||
sub rcx, 1
|
|
||||||
cmp rax, 9
|
|
||||||
ja .L2
|
|
||||||
lea rax, [rsp+32]
|
|
||||||
mov edi, 1
|
|
||||||
sub rdx, rax
|
|
||||||
xor eax, eax
|
|
||||||
lea rsi, [rsp+32+rdx]
|
|
||||||
mov rdx, r8
|
|
||||||
mov rax, 1
|
|
||||||
syscall
|
|
||||||
add rsp, 40
|
|
||||||
ret
|
|
||||||
";
|
|
389
src/lexer/mod.rs
389
src/lexer/mod.rs
|
@ -1,389 +0,0 @@
|
||||||
use std::path::Path;
|
|
||||||
use anyhow::bail;
|
|
||||||
|
|
||||||
use crate::{error, types::{common::Loc, token::{InstructionType, KeywordType, Token, TokenType, TypeType}}};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub struct Lexer {
|
|
||||||
pub loc: Loc,
|
|
||||||
pub tokens: Vec<Token>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Lexer {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
loc: Default::default(),
|
|
||||||
tokens: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub fn lex(&mut self, file: &Path) -> anyhow::Result<&mut Self> {
|
|
||||||
self.reset(file);
|
|
||||||
|
|
||||||
let chars = match std::fs::read_to_string(file) {
|
|
||||||
Ok(c) => c,
|
|
||||||
Err(e) => {
|
|
||||||
error!("Failed to open file {file:?} : {e}");
|
|
||||||
bail!("");
|
|
||||||
}
|
|
||||||
}.chars().collect::<Vec<char>>();
|
|
||||||
|
|
||||||
|
|
||||||
let mut idx = 0;
|
|
||||||
let mut buf = String::new();
|
|
||||||
let mut is_searching = false;
|
|
||||||
|
|
||||||
if let Err(_) = self.go_to_first_char(&chars, &mut idx) {
|
|
||||||
return Ok(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut start_loc = self.loc.clone();
|
|
||||||
while idx < chars.len() {
|
|
||||||
|
|
||||||
match chars[idx] {
|
|
||||||
|
|
||||||
'c' if chars.get(idx + 1) == Some(&'"') => {
|
|
||||||
start_loc = self.loc.clone();
|
|
||||||
is_searching = true;
|
|
||||||
idx += 2; // skip c and "
|
|
||||||
self.loc.col += 2;
|
|
||||||
|
|
||||||
if !buf.is_empty() {
|
|
||||||
debug!({loc => self.loc() }, "buffer was not empty, intresting");
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if chars[idx] == '"' && chars[idx-1] != '\\' {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
buf.push(chars[idx]);
|
|
||||||
if chars[idx] == '\n' {
|
|
||||||
self.loc.inc_line()
|
|
||||||
}
|
|
||||||
self.loc.inc_col();
|
|
||||||
idx += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.push('\0');
|
|
||||||
let str = self.unescape(&&buf);
|
|
||||||
self.loc.inc_col();
|
|
||||||
self.tokens.push(Token::new(TokenType::Instruction(InstructionType::PushCStr(str)), self.loc(), buf.clone()));
|
|
||||||
buf.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
'"' => {
|
|
||||||
start_loc = self.loc.clone();
|
|
||||||
is_searching = true;
|
|
||||||
idx += 1; // skip "
|
|
||||||
self.loc.col += 1;
|
|
||||||
|
|
||||||
if !buf.is_empty() {
|
|
||||||
debug!({loc => self.loc() }, "buffer was not empty, intresting ({buf:?})");
|
|
||||||
}
|
|
||||||
|
|
||||||
// while chars.get(idx+1) != Some(&'"') && chars[idx] != '\\' && chars.get(idx+1).is_some() {
|
|
||||||
loop {
|
|
||||||
if chars[idx] == '"' && chars[idx-1] != '\\' {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
buf.push(chars[idx]);
|
|
||||||
if chars[idx] == '\n' {
|
|
||||||
self.loc.inc_line()
|
|
||||||
}
|
|
||||||
self.loc.inc_col();
|
|
||||||
idx += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let str = self.unescape(&buf);
|
|
||||||
self.loc.inc_col();
|
|
||||||
self.tokens.push(Token::new(TokenType::Instruction(InstructionType::PushStr(str)), start_loc.clone(), buf.clone()));
|
|
||||||
buf.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
'\'' => {
|
|
||||||
start_loc = self.loc.clone();
|
|
||||||
is_searching = true;
|
|
||||||
idx += 1; // skip '
|
|
||||||
self.loc.col += 1;
|
|
||||||
|
|
||||||
if !buf.is_empty() {
|
|
||||||
debug!({loc => self.loc() }, "buffer was not empty, intresting ({buf})");
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if chars[idx] == '"' && chars[idx-1] != '\\' {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
buf.push(chars[idx]);
|
|
||||||
if chars[idx] == '\n' {
|
|
||||||
self.loc.inc_line()
|
|
||||||
}
|
|
||||||
self.loc.inc_col();
|
|
||||||
idx += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let str = self.unescape(&&&buf);
|
|
||||||
if str.len() > 1 {
|
|
||||||
error!({loc => self.loc()}, "Chars can only have 1 char");
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
|
|
||||||
self.loc.inc_col();
|
|
||||||
self.tokens.push(Token::new(TokenType::Instruction(InstructionType::PushStr(str)), self.loc(), buf.clone()));
|
|
||||||
buf.clear();
|
|
||||||
}
|
|
||||||
':' if chars.get(idx + 1) == Some(&':') => {
|
|
||||||
let mut p_buf = vec![buf.clone()];
|
|
||||||
buf.clear();
|
|
||||||
idx += 2; // skip ::
|
|
||||||
self.loc.col += 2;
|
|
||||||
|
|
||||||
while idx < chars.len() {
|
|
||||||
match chars[idx] {
|
|
||||||
' ' | '\n' | '\r' => {
|
|
||||||
if !p_buf.is_empty() {
|
|
||||||
p_buf.push(buf.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.tokens.push(Token::new(TokenType::Instruction(InstructionType::StructPath(p_buf.clone())), start_loc.clone(), p_buf.clone().join("::")));
|
|
||||||
buf.clear();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
c @ ('\'' | '"') => {
|
|
||||||
error!({loc => self.loc()}, "Invalid char in struct path token, expected /a-z|A-Z|0-9|_|-/ got {c}");
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
|
|
||||||
':' if chars.get(idx + 1) == Some(&':') => {
|
|
||||||
if buf.is_empty() {
|
|
||||||
error!({loc => self.loc()}, "Invalid char in struct path token, expected /a-z|A-Z|0-9|_|-/ got '.'");
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
idx += 2; // skip ::
|
|
||||||
self.loc.col += 2;
|
|
||||||
p_buf.push(buf.clone());
|
|
||||||
buf.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
c => {
|
|
||||||
buf.push(c);
|
|
||||||
idx += 1;
|
|
||||||
self.loc.inc_col();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
'.' if !buf.is_empty() => {
|
|
||||||
let mut p_buf = vec![buf.clone()];
|
|
||||||
buf.clear();
|
|
||||||
idx += 1; // skip .
|
|
||||||
self.loc.inc_col();
|
|
||||||
|
|
||||||
while idx < chars.len() {
|
|
||||||
match chars[idx] {
|
|
||||||
' ' | '\n' | '\r' => {
|
|
||||||
if !p_buf.is_empty() {
|
|
||||||
p_buf.push(buf.clone());
|
|
||||||
}
|
|
||||||
self.tokens.push(Token::new(TokenType::Instruction(InstructionType::StructItem(p_buf.clone())), start_loc.clone(), p_buf.clone().join(".")));
|
|
||||||
buf.clear();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
c @ ('\'' | '"') => {
|
|
||||||
error!({loc => self.loc()}, "Invalid char in struct access token, expected /a-z|A-Z|0-9|_|-/ got {c}");
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
|
|
||||||
'.' => {
|
|
||||||
if buf.is_empty() {
|
|
||||||
error!({loc => self.loc()}, "Invalid char in struct access token, expected /a-z|A-Z|0-9|_|-/ got '.'");
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
idx += 1; // skip .
|
|
||||||
self.loc.col += 1;
|
|
||||||
p_buf.push(buf.clone());
|
|
||||||
buf.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
c => {
|
|
||||||
buf.push(c);
|
|
||||||
idx += 1;
|
|
||||||
self.loc.inc_col();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ch @ (' ' | '\n' | '\r') => {
|
|
||||||
if ch == '\n' {
|
|
||||||
self.loc.inc_line();
|
|
||||||
} else {
|
|
||||||
self.loc.inc_col();
|
|
||||||
}
|
|
||||||
if !buf.is_empty() {
|
|
||||||
//TODO: Implement signed ints
|
|
||||||
if let Ok(int) = parse_int::parse::<usize>(&buf) {
|
|
||||||
self.tokens.push(Token::new(TokenType::Instruction(InstructionType::PushInt(int)), start_loc.clone(), buf.clone()));
|
|
||||||
} else {
|
|
||||||
let token_type = self.match_token_type(&buf);
|
|
||||||
self.tokens.push(Token::new(token_type, start_loc.clone(), buf.clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.clear();
|
|
||||||
is_searching = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
'/' if chars.get(idx + 1) == Some(&'/') => {
|
|
||||||
let mut c = chars.get(idx);
|
|
||||||
while c.is_some() && c != Some(&'\n') {
|
|
||||||
self.loc.inc_col();
|
|
||||||
idx += 1;
|
|
||||||
c = chars.get(idx);
|
|
||||||
}
|
|
||||||
self.loc.inc_line();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ch => {
|
|
||||||
if is_searching {
|
|
||||||
is_searching = false;
|
|
||||||
start_loc = self.loc.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.push(ch);
|
|
||||||
self.loc.inc_col();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
idx += 1;
|
|
||||||
}
|
|
||||||
//? Add last token
|
|
||||||
//TODO: Implement signed ints
|
|
||||||
if !buf.is_empty() {
|
|
||||||
if let Ok(int) = parse_int::parse::<usize>(&buf) {
|
|
||||||
self.tokens.push(Token::new(TokenType::Instruction(InstructionType::PushInt(int)), start_loc.clone(), buf.clone()));
|
|
||||||
} else {
|
|
||||||
let token_type = self.match_token_type(&buf);
|
|
||||||
self.tokens.push(Token::new(token_type, start_loc.clone(), buf.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// for t in &self.tokens {
|
|
||||||
// debug!({loc => t.loc.clone()}, "token: {:?}", t.typ);
|
|
||||||
// }
|
|
||||||
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn go_to_first_char(&mut self, chars: &Vec<char>, idx: &mut usize) -> anyhow::Result<()> {
|
|
||||||
loop {
|
|
||||||
if let Some(c) = chars.get(*idx) {
|
|
||||||
match c {
|
|
||||||
' ' | '\r' => self.loc.inc_col(),
|
|
||||||
'\n' => self.loc.inc_line(),
|
|
||||||
_ => break,
|
|
||||||
}
|
|
||||||
*idx += 1;
|
|
||||||
} else {
|
|
||||||
warn!("Empty program");
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn match_token_type(&self, s: &str) -> TokenType {
|
|
||||||
match s {
|
|
||||||
"if" => TokenType::Keyword(KeywordType::If),
|
|
||||||
"else" => TokenType::Keyword(KeywordType::Else),
|
|
||||||
"end" => TokenType::Keyword(KeywordType::End),
|
|
||||||
"while" => TokenType::Keyword(KeywordType::While),
|
|
||||||
"do" => TokenType::Keyword(KeywordType::Do),
|
|
||||||
"include" => TokenType::Keyword(KeywordType::Include),
|
|
||||||
"memory" => TokenType::Keyword(KeywordType::Memory),
|
|
||||||
"const" => TokenType::Keyword(KeywordType::Constant),
|
|
||||||
"fn" => TokenType::Keyword(KeywordType::Function),
|
|
||||||
"then" => TokenType::Keyword(KeywordType::Then),
|
|
||||||
"done" => TokenType::Keyword(KeywordType::Done),
|
|
||||||
"typedef" => TokenType::Keyword(KeywordType::TypeDef),
|
|
||||||
"structdef" => TokenType::Keyword(KeywordType::StructDef),
|
|
||||||
"inline" => TokenType::Keyword(KeywordType::Inline),
|
|
||||||
"export" => TokenType::Keyword(KeywordType::Export),
|
|
||||||
"extern" => TokenType::Keyword(KeywordType::Extern),
|
|
||||||
"returns" => TokenType::Keyword(KeywordType::Returns),
|
|
||||||
"with" => TokenType::Keyword(KeywordType::With),
|
|
||||||
"drop" => TokenType::Instruction(InstructionType::Drop),
|
|
||||||
"_dbg_print" => TokenType::Instruction(InstructionType::Print),
|
|
||||||
"dup" => TokenType::Instruction(InstructionType::Dup),
|
|
||||||
"rot" => TokenType::Instruction(InstructionType::Rot),
|
|
||||||
"over" => TokenType::Instruction(InstructionType::Over),
|
|
||||||
"swap" => TokenType::Instruction(InstructionType::Swap),
|
|
||||||
"sub" => TokenType::Instruction(InstructionType::Minus),
|
|
||||||
"add" => TokenType::Instruction(InstructionType::Plus),
|
|
||||||
"eq" => TokenType::Instruction(InstructionType::Equals),
|
|
||||||
"gt" => TokenType::Instruction(InstructionType::Gt),
|
|
||||||
"lt" => TokenType::Instruction(InstructionType::Lt),
|
|
||||||
"ge" => TokenType::Instruction(InstructionType::Ge),
|
|
||||||
"le" => TokenType::Instruction(InstructionType::Le),
|
|
||||||
"neq" => TokenType::Instruction(InstructionType::NotEquals),
|
|
||||||
"band" => TokenType::Instruction(InstructionType::Band),
|
|
||||||
"bor" => TokenType::Instruction(InstructionType::Bor),
|
|
||||||
"shr" => TokenType::Instruction(InstructionType::Shr),
|
|
||||||
"shl" => TokenType::Instruction(InstructionType::Shl),
|
|
||||||
"divmod" => TokenType::Instruction(InstructionType::DivMod),
|
|
||||||
"mul" => TokenType::Instruction(InstructionType::Mul),
|
|
||||||
"read8" => TokenType::Instruction(InstructionType::Read8),
|
|
||||||
"write8" => TokenType::Instruction(InstructionType::Write8),
|
|
||||||
"read32" => TokenType::Instruction(InstructionType::Read32),
|
|
||||||
"write32" => TokenType::Instruction(InstructionType::Write32),
|
|
||||||
"read64" => TokenType::Instruction(InstructionType::Read64),
|
|
||||||
"write64" => TokenType::Instruction(InstructionType::Write64),
|
|
||||||
"syscall0" => TokenType::Instruction(InstructionType::Syscall0),
|
|
||||||
"syscall1" => TokenType::Instruction(InstructionType::Syscall1),
|
|
||||||
"syscall2" => TokenType::Instruction(InstructionType::Syscall2),
|
|
||||||
"syscall3" => TokenType::Instruction(InstructionType::Syscall3),
|
|
||||||
"syscall4" => TokenType::Instruction(InstructionType::Syscall4),
|
|
||||||
"syscall5" => TokenType::Instruction(InstructionType::Syscall5),
|
|
||||||
"syscall6" => TokenType::Instruction(InstructionType::Syscall6),
|
|
||||||
"(bool)" => TokenType::Instruction(InstructionType::CastBool),
|
|
||||||
"(ptr)" => TokenType::Instruction(InstructionType::CastPtr),
|
|
||||||
"(int)" => TokenType::Instruction(InstructionType::CastInt),
|
|
||||||
"(void)" => TokenType::Instruction(InstructionType::CastVoid),
|
|
||||||
"return" => TokenType::Instruction(InstructionType::Return),
|
|
||||||
"ptr" => TokenType::Type(TypeType::Ptr),
|
|
||||||
"u8" => TokenType::Type(TypeType::U8),
|
|
||||||
"u16" => TokenType::Type(TypeType::U16),
|
|
||||||
"u32" => TokenType::Type(TypeType::U32),
|
|
||||||
"u64" => TokenType::Type(TypeType::U64),
|
|
||||||
"void" => TokenType::Type(TypeType::Void),
|
|
||||||
"any" => TokenType::Type(TypeType::Any),
|
|
||||||
t => TokenType::Unknown(t.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset(&mut self, file: &Path) -> &mut Self {
|
|
||||||
self.loc.file = file.to_string_lossy().to_string();
|
|
||||||
self.loc.line = 1;
|
|
||||||
self.loc.col = 0;
|
|
||||||
self.tokens = Vec::new();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn loc(&self) -> Loc {
|
|
||||||
self.loc.clone()
|
|
||||||
}
|
|
||||||
fn unescape(&self, s: &String) -> String {
|
|
||||||
//TODO: add more escapes
|
|
||||||
s
|
|
||||||
.replace("\\n", "\n")
|
|
||||||
.replace("\\0", "\0")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
#![allow(dead_code)]
|
|
||||||
pub const RESET: &str = "\x1b[0m";
|
|
||||||
pub const BOLD: &str = "\x1b[1m";
|
|
||||||
pub const ITALIC: &str = "\x1b[3m";
|
|
||||||
pub const UNDERLINE: &str = "\x1b[4m";
|
|
||||||
pub const BLINK: &str = "\x1b[5m";
|
|
||||||
pub const BLINK2: &str = "\x1b[6m";
|
|
||||||
pub const SELECTED: &str = "\x1b[7m";
|
|
||||||
pub const BLACK: &str = "\x1b[30m";
|
|
||||||
pub const RED: &str = "\x1b[31m";
|
|
||||||
pub const GREEN: &str = "\x1b[32m";
|
|
||||||
pub const YELLOW: &str = "\x1b[33m";
|
|
||||||
pub const BLUE: &str = "\x1b[34m";
|
|
||||||
pub const MAGENTA: &str = "\x1b[35m";
|
|
||||||
pub const BEIGE: &str = "\x1b[36m";
|
|
||||||
pub const WHITE: &str = "\x1b[37m";
|
|
||||||
pub const BLACKBG: &str = "\x1b[40m";
|
|
||||||
pub const REDBG: &str = "\x1b[41m";
|
|
||||||
pub const GREENBG: &str = "\x1b[42m";
|
|
||||||
pub const YELLOWBG: &str = "\x1b[43m";
|
|
||||||
pub const BLUEBG: &str = "\x1b[44m";
|
|
||||||
pub const MAGENTABG: &str = "\x1b[45m";
|
|
||||||
pub const BEIGEBG: &str = "\x1b[46m";
|
|
||||||
pub const WHITEBG: &str = "\x1b[47m";
|
|
||||||
pub const GREY: &str = "\x1b[90m";
|
|
||||||
pub const RED2: &str = "\x1b[91m";
|
|
||||||
pub const GREEN2: &str = "\x1b[92m";
|
|
||||||
pub const YELLOW2: &str = "\x1b[93m";
|
|
||||||
pub const BLUE2: &str = "\x1b[94m";
|
|
||||||
pub const MAGENTA2: &str = "\x1b[95m";
|
|
||||||
pub const BEIGE2: &str = "\x1b[96m";
|
|
||||||
pub const WHITE2: &str = "\x1b[97m";
|
|
|
@ -1,106 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! log {
|
|
||||||
({$($k: expr => $v: expr),* $(,)? }, $lvl:expr, $($arg:tt),+) => {
|
|
||||||
crate::log_tagged!({$($k => $v,)*}, crate::logger::Level::Info, $($arg)+)
|
|
||||||
};
|
|
||||||
(module: $module:expr, $lvl:expr, $($arg:tt)+) => {
|
|
||||||
unsafe {
|
|
||||||
crate::logger::LOGGER.log(
|
|
||||||
crate::logger::LogEvent {
|
|
||||||
level: $lvl,
|
|
||||||
module_path: $module.to_string(),
|
|
||||||
message: format!($($arg)+),
|
|
||||||
tags: std::collections::HashMap::new()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
($lvl:expr, $($arg:tt)+) => {
|
|
||||||
crate::log!(module: module_path!(), $lvl, $($arg)+)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! log_tagged {
|
|
||||||
({$($k: expr => $v: expr),* $(,)? }, $module:expr, $lvl:expr, $($arg:tt)+) => {
|
|
||||||
unsafe {
|
|
||||||
crate::logger::LOGGER.log(
|
|
||||||
crate::logger::LogEvent {
|
|
||||||
level: $lvl,
|
|
||||||
module_path: $module.to_string(),
|
|
||||||
message: format!($($arg)+),
|
|
||||||
tags: map_macro::hash_map!{$(stringify!($k).to_string() => Box::new($v) as Box<dyn core::any::Any>,)*}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! debug {
|
|
||||||
(module: $module:expr, $($arg:tt)+) => {
|
|
||||||
crate::log!(module: $module, crate::logger::Level::Debug, $($arg:tt)+)
|
|
||||||
};
|
|
||||||
({$($k: expr => $v: expr),* $(,)? }, $($arg:tt)+) => {
|
|
||||||
crate::log_tagged!({$($k => $v,)*}, module_path!(), crate::logger::Level::Debug, $($arg)+)
|
|
||||||
};
|
|
||||||
({$($k: expr => $v: expr),* $(,)? }, module: $module:expr, $($arg:tt)+) => {
|
|
||||||
crate::log_tagged!({$($k => $v,)*}, $module, crate::logger::Level::Debug, $($arg)+)
|
|
||||||
};
|
|
||||||
($($arg:tt)+) => {
|
|
||||||
crate::log!(crate::logger::Level::Debug, $($arg)+)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! info {
|
|
||||||
(module: $module:expr, $($arg:tt)+) => {
|
|
||||||
crate::log!(module: $module, crate::logger::Level::Info, $($arg:tt)+)
|
|
||||||
};
|
|
||||||
({$($k: expr => $v: expr),* $(,)? }, $($arg:tt)+) => {
|
|
||||||
crate::log_tagged!({$($k => $v,)*}, module_path!(), crate::logger::Level::Info, $($arg)+)
|
|
||||||
};
|
|
||||||
({$($k: expr => $v: expr),* $(,)? }, module: $module:expr, $($arg:tt)+) => {
|
|
||||||
crate::log_tagged!({$($k => $v,)*}, $module, crate::logger::Level::Info, $($arg)+)
|
|
||||||
};
|
|
||||||
($($arg:tt)+) => {
|
|
||||||
crate::log!(crate::logger::Level::Info, $($arg)+)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! warn {
|
|
||||||
(module: $module:expr, $($arg:tt)+) => {
|
|
||||||
crate::log!(module: $module, crate::logger::Level::Warn, $($arg:tt)+)
|
|
||||||
};
|
|
||||||
({$($k: expr => $v: expr),* $(,)? }, $($arg:tt)+) => {
|
|
||||||
crate::log_tagged!({$($k => $v,)*}, module_path!(), crate::logger::Level::Warn, $($arg)+)
|
|
||||||
};
|
|
||||||
({$($k: expr => $v: expr),* $(,)? }, module: $module:expr, $($arg:tt)+) => {
|
|
||||||
crate::log_tagged!({$($k => $v,)*}, $module, crate::logger::Level::Warn, $($arg)+)
|
|
||||||
};
|
|
||||||
($($arg:tt)+) => {
|
|
||||||
crate::log!(crate::logger::Level::Warn, $($arg)+)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! error {
|
|
||||||
(module: $module:expr, $($arg:tt)+) => {
|
|
||||||
crate::log!(module: $module, crate::logger::Level::Error, $($arg:tt)+)
|
|
||||||
};
|
|
||||||
({$($k: expr => $v: expr),* $(,)? }, $($arg:tt)+) => {
|
|
||||||
crate::log_tagged!({$($k => $v,)*}, module_path!(), crate::logger::Level::Error, $($arg)+)
|
|
||||||
};
|
|
||||||
({$($k: expr => $v: expr),* $(,)? }, module: $module:expr, $($arg:tt)+) => {
|
|
||||||
crate::log_tagged!({$($k => $v,)*}, $module, crate::logger::Level::Error, $($arg)+)
|
|
||||||
};
|
|
||||||
($($arg:tt)+) => {
|
|
||||||
crate::log!(crate::logger::Level::Error, $($arg)+)
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,83 +0,0 @@
|
||||||
|
|
||||||
// use log::{Level, LevelFilter, Metadata, Record, SetLoggerError};
|
|
||||||
|
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use crate::{cli::CliArgs, types::common::Loc};
|
|
||||||
|
|
||||||
mod types;
|
|
||||||
mod colors;
|
|
||||||
#[macro_use]
|
|
||||||
pub mod macros;
|
|
||||||
pub use types::{Level, LogEvent, LOGGER};
|
|
||||||
use types::*;
|
|
||||||
|
|
||||||
|
|
||||||
pub struct Logger{
|
|
||||||
pub level: i8
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl Logger {
|
|
||||||
pub fn new(args: &CliArgs) -> Box<Self> {
|
|
||||||
Box::new(Self {
|
|
||||||
level: args.verbose
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(args: &CliArgs) -> anyhow::Result<()>{
|
|
||||||
unsafe {
|
|
||||||
types::LOGGER = Box::leak(
|
|
||||||
Self::new(args)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_prefix(&self, level: Level) -> String {
|
|
||||||
use colors::{BOLD, RESET, RED, YELLOW, BLUE, GREEN, MAGENTA};
|
|
||||||
match level {
|
|
||||||
Level::Error => format!("{BOLD}{RED}error{RESET}", ),
|
|
||||||
Level::Warn => format!("{BOLD}{YELLOW}warn{RESET}", ),
|
|
||||||
Level::Info => format!("{BOLD}{GREEN}info{RESET}", ),
|
|
||||||
Level::Debug => format!("{BOLD}{BLUE}debug{RESET}", ),
|
|
||||||
Level::Trace => format!("{BOLD}{MAGENTA}trace{RESET}", ),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Log for Logger {
|
|
||||||
fn enabled(&self, level: Level) -> bool {
|
|
||||||
match level {
|
|
||||||
Level::Error if self.level >= 0 => true,
|
|
||||||
Level::Warn |
|
|
||||||
Level::Info if self.level >= 1 => true,
|
|
||||||
Level::Debug if self.level >= 2 => true,
|
|
||||||
Level::Trace if self.level >= 3 => true,
|
|
||||||
_ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn log(&self, event: LogEvent) {
|
|
||||||
|
|
||||||
if self.enabled(event.level) {
|
|
||||||
let modpath = if event.level > Level::Info {
|
|
||||||
format!(" [{}]", event.module_path)
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(loc) = event.tags.get("loc") {
|
|
||||||
let loc: String = (*loc.deref()).downcast_ref::<Loc>()
|
|
||||||
.map_or(String::from("INVALID"), |l| l.to_string());
|
|
||||||
println!("{} {}{modpath}: {}", loc, self.get_prefix(event.level), event.message);
|
|
||||||
} else {
|
|
||||||
println!("{}{modpath}: {}", self.get_prefix(event.level), event.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn level(&self) -> i8 {
|
|
||||||
self.level
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
use std::{any::Any, collections::HashMap, fmt::Debug };
|
|
||||||
|
|
||||||
|
|
||||||
pub static mut LOGGER: &dyn Log = &NopLogger;
|
|
||||||
|
|
||||||
struct NopLogger;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
|
||||||
pub enum Level {
|
|
||||||
Error = 1,
|
|
||||||
Warn,
|
|
||||||
Info,
|
|
||||||
Debug,
|
|
||||||
Trace,
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub trait Tag: Display + Debug + Any {}
|
|
||||||
|
|
||||||
|
|
||||||
pub struct LogEvent {
|
|
||||||
pub level: Level,
|
|
||||||
pub module_path: String,
|
|
||||||
pub message: String,
|
|
||||||
pub tags: HashMap<String, Box<dyn Any>>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl Log for NopLogger {
|
|
||||||
fn enabled(&self, _: Level) -> bool {false}
|
|
||||||
fn level(&self) -> i8 {0}
|
|
||||||
fn log(&self, _: LogEvent) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub trait Log {
|
|
||||||
fn enabled(&self, level: Level) -> bool;
|
|
||||||
fn log(&self, event: LogEvent);
|
|
||||||
fn level(&self) -> i8;
|
|
||||||
}
|
|
59
src/main.rs
59
src/main.rs
|
@ -1,47 +1,32 @@
|
||||||
|
use clap::Parser;
|
||||||
|
use log::LevelFilter;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
mod ast;
|
||||||
|
mod cliargs;
|
||||||
|
mod parser;
|
||||||
|
mod token;
|
||||||
|
mod tokeniser;
|
||||||
|
mod common;
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod logger;
|
|
||||||
mod cli;
|
|
||||||
mod types;
|
|
||||||
mod lexer;
|
|
||||||
pub mod parser;
|
|
||||||
mod compiler;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let cli_args = cli::CliArgs::parse_with_passthrough();
|
let cliargs = cliargs::CliArgs::parse();
|
||||||
logger::Logger::init(&cli_args).expect("Failed to init logger");
|
env_logger::builder()
|
||||||
|
.format_timestamp(None)
|
||||||
|
.filter(None, cliargs.get_logger_filter())
|
||||||
|
.init();
|
||||||
|
|
||||||
let mut prog_map = HashMap::new();
|
let mut tokeniser = tokeniser::Tokeniser::new();
|
||||||
for file in &cli_args.input {
|
let mut parser = parser::Parser::new();
|
||||||
let mut lexer = lexer::Lexer::new();
|
for file in &cliargs.input {
|
||||||
|
if let Err(_) = tokeniser.tokenise(file.as_path()) {
|
||||||
info!("Lexing file {file:?}");
|
break;
|
||||||
if let Err(_) = lexer.lex(file.as_std_path()) {
|
|
||||||
error!("Lexing failed, exiting");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// for t in &lexer.tokens {
|
let tokens = tokeniser.tokens();
|
||||||
// info!({loc => t.loc.clone()}, "{:?}", t.typ);
|
let Ok(ast) = parser.parse(file, tokens) else {
|
||||||
// }
|
break;
|
||||||
// dbg!(&lexer.tokens);
|
|
||||||
|
|
||||||
info!("Parsing file {file:?}");
|
|
||||||
let prog = match parser::parse(&cli_args, &mut lexer.tokens) {
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(_) => {
|
|
||||||
error!("Parsing failed, exiting");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
dbg!(ast);
|
||||||
prog_map.insert(file.as_std_path(), prog);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
if let Err(_) = compiler::compile_program(&cli_args, prog_map) {
|
|
||||||
error!("Failed to compile program, exiting");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
423
src/parser.rs
Normal file
423
src/parser.rs
Normal file
|
@ -0,0 +1,423 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
use camino::Utf8Path;
|
||||||
|
|
||||||
|
use crate::{ast::{self, StructStat}, common::Loc, token::{KeywordType, Token, TokenType}};
|
||||||
|
|
||||||
|
|
||||||
|
pub struct Parser {
|
||||||
|
tokens: Vec<Token>,
|
||||||
|
loc: Loc,
|
||||||
|
is_in_func: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
};
|
||||||
|
|
||||||
|
while !self.tokens.is_empty() {
|
||||||
|
prog.items.push(self.parse_stat()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(prog)
|
||||||
|
}
|
||||||
|
|
||||||
|
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()?),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)?
|
||||||
|
}
|
||||||
|
|
||||||
|
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!("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
|
|
||||||
use crate::types::{ast::{AstNode, Constant, Module, Program}, common::Loc};
|
|
||||||
|
|
||||||
|
|
||||||
lazy_static!(
|
|
||||||
static ref DEFAULT_CONSTANTS: HashMap<&'static str, AstNode> = {
|
|
||||||
let mut h = HashMap::new();
|
|
||||||
// No bsd cause im not about to create 3 or 4 diffrent compilation targets
|
|
||||||
h.insert("__WINDOWS", AstNode::Int(Loc::default(), cfg!(target_os = "windows") as usize));
|
|
||||||
h.insert("__LINUX", AstNode::Int(Loc::default(), cfg!(target_os = "linux") as usize));
|
|
||||||
h.insert("__ENDIAN_LITTLE", AstNode::Int(Loc::default(), cfg!(target_endian="little") as usize));
|
|
||||||
h.insert("__ENDIAN_BIG", AstNode::Int(Loc::default(), cfg!(target_endian="big") as usize));
|
|
||||||
|
|
||||||
|
|
||||||
h
|
|
||||||
};
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub fn get_builtin_symbols(prog: &mut Program) -> AstNode {
|
|
||||||
let mut md = Module {
|
|
||||||
loc: Loc::new(String::from("BUILTIN"), 0, 0),
|
|
||||||
path: vec![String::from("builtin")],
|
|
||||||
ident: String::from("BUILTIN"),
|
|
||||||
body: Vec::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for (k, v) in DEFAULT_CONSTANTS.iter() {
|
|
||||||
let c = Constant {
|
|
||||||
loc: Loc::default(),
|
|
||||||
ident: k.to_string(),
|
|
||||||
value: Box::from(v.clone()),
|
|
||||||
};
|
|
||||||
prog.constants.insert(k.to_string(), c.clone());
|
|
||||||
md.body.push(AstNode::Constant(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
AstNode::Module(md)
|
|
||||||
}
|
|
|
@ -1,787 +0,0 @@
|
||||||
mod utils;
|
|
||||||
mod precompiler;
|
|
||||||
mod builtin;
|
|
||||||
|
|
||||||
use std::{collections::HashMap, path::Path};
|
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
|
||||||
|
|
||||||
use crate::{cli::CliArgs, lexer::Lexer, types::{ast::{AstNode, Block, ConstUse, Constant, FnCall, Function, If, MemSize, MemUse, Memory, Module, Program, StructDef, While}, common::Loc, token::{InstructionType, KeywordType, Token, TokenType, TypeType}}};
|
|
||||||
|
|
||||||
use self::{builtin::get_builtin_symbols, precompiler::{precompile_const, precompile_mem}, utils::{expect, peek_check, peek_check_multiple, PeekResult}};
|
|
||||||
|
|
||||||
|
|
||||||
bitflags::bitflags! {
|
|
||||||
struct Flags: u8 {
|
|
||||||
const EXTERN = 1 << 0;
|
|
||||||
const EXPORT = 1 << 1;
|
|
||||||
const INLINE = 1 << 2;
|
|
||||||
const ALLOW_TYPES = 1 << 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: Implement Module paths
|
|
||||||
pub fn parse(cli_args: &CliArgs, tokens: &mut Vec<Token>) -> Result<Program> {
|
|
||||||
tokens.reverse();
|
|
||||||
let module = Module {
|
|
||||||
loc: Loc::new(&tokens[0].loc.file, 0, 0),
|
|
||||||
ident: Path::new(&tokens[0].loc.file).file_stem().expect("Something went horribly wrong").to_string_lossy().to_string(),
|
|
||||||
body: Vec::new(),
|
|
||||||
path: vec![],
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
let mut prog = Program {
|
|
||||||
ast: AstNode::Module(module.clone()),
|
|
||||||
functions: HashMap::new(),
|
|
||||||
constants: HashMap::new(),
|
|
||||||
memories: HashMap::new(),
|
|
||||||
struct_defs: HashMap::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
let syms = get_builtin_symbols(&mut prog);
|
|
||||||
match &mut prog.ast {
|
|
||||||
AstNode::Module(module) => {
|
|
||||||
module.body.push(syms)
|
|
||||||
}
|
|
||||||
_ => unreachable!()
|
|
||||||
}
|
|
||||||
|
|
||||||
while !tokens.is_empty() {
|
|
||||||
let node = parse_next(cli_args, &mut prog, tokens, Flags::empty(), true)?;
|
|
||||||
match &mut prog.ast {
|
|
||||||
AstNode::Module(module) => {
|
|
||||||
module.body.push(node);
|
|
||||||
}
|
|
||||||
_ => unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// prog.ast = module;
|
|
||||||
|
|
||||||
Ok(prog)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_next(cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>, flags: Flags, is_module_root: bool) -> Result<AstNode> {
|
|
||||||
let token = tokens.pop().expect("We broke reality!");
|
|
||||||
// debug!({loc => token.loc.clone()}, "t: {:?}", token.typ);
|
|
||||||
let ret = match &token.typ {
|
|
||||||
TokenType::Keyword(kw) => {
|
|
||||||
match kw {
|
|
||||||
KeywordType::If => parse_if(&token, cli_args, prog, tokens)?,
|
|
||||||
KeywordType::While => parse_while(&token, cli_args, prog, tokens)?,
|
|
||||||
KeywordType::Include => parse_include(&token, cli_args, prog, tokens)?,
|
|
||||||
KeywordType::Memory => parse_memory(&token, cli_args, prog, tokens, is_module_root)?,
|
|
||||||
KeywordType::Constant => parse_const(&token, cli_args, prog, tokens)?,
|
|
||||||
KeywordType::Function => parse_function(&token, cli_args, prog, tokens, flags)?,
|
|
||||||
KeywordType::StructDef => parse_struct(&token, cli_args, prog, tokens)?,
|
|
||||||
KeywordType::TypeDef => todo!(),
|
|
||||||
KeywordType::Inline => parse_inline(&token, cli_args, prog, tokens, flags)?,
|
|
||||||
KeywordType::Export => parse_export(&token, cli_args, prog, tokens, flags)?,
|
|
||||||
KeywordType::Extern => parse_extern(&token, cli_args, prog, tokens, flags)?,
|
|
||||||
kw => {
|
|
||||||
dbg!(&prog.constants);
|
|
||||||
error!({loc => token.loc}, "Unexpected token {kw:?}");
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
TokenType::Instruction(it) => {
|
|
||||||
if is_module_root {
|
|
||||||
error!({loc => token.loc}, "Unexpected token {it:?}, please create a main function, this is not a scripting language");
|
|
||||||
bail!("")
|
|
||||||
} else {
|
|
||||||
match it {
|
|
||||||
InstructionType::StructPath(p) => parse_struct_path(&token, prog, p)?,
|
|
||||||
InstructionType::StructItem(p) => parse_struct_item(&token, prog, p)?,
|
|
||||||
_ => AstNode::Token(token)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
TokenType::Unknown(ut) => {
|
|
||||||
if is_module_root {
|
|
||||||
error!({loc => token.loc}, "Unexpected token {ut:?}, please create a main function, this is not a scripting language");
|
|
||||||
bail!("")
|
|
||||||
} else {
|
|
||||||
// AstNode::Token(token)
|
|
||||||
parse_unknown(&token, cli_args, prog, tokens, flags)?
|
|
||||||
}
|
|
||||||
},
|
|
||||||
TokenType::Type(t) => {
|
|
||||||
if flags.contains(Flags::ALLOW_TYPES) {
|
|
||||||
AstNode::Token(token)
|
|
||||||
} else {
|
|
||||||
error!({loc => token.loc}, "Unexpected type {t:?}");
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Ok(ret)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_struct_item(org: &Token, prog: &mut Program, p: &Vec<String>) -> Result<AstNode> {
|
|
||||||
fn find_disp(strct: &StructDef, disp: &mut usize, path: &[String]) {
|
|
||||||
let Some(p) = path.get(0) else {
|
|
||||||
return
|
|
||||||
};
|
|
||||||
|
|
||||||
for item in &strct.body {
|
|
||||||
if p == &item.0 {
|
|
||||||
match &item.2 {
|
|
||||||
TypeType::Struct(strct) => {
|
|
||||||
*disp += item.1;
|
|
||||||
find_disp(strct, disp, &path[1..])
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
*disp += item.1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if let Some(mem) = prog.memories.get(&p[0].to_string()) {
|
|
||||||
match &mem.size {
|
|
||||||
MemSize::Size(_) => {
|
|
||||||
error!({loc => org.loc()}, "You can only access items in structs");
|
|
||||||
bail!("")
|
|
||||||
},
|
|
||||||
MemSize::Type(t) => {
|
|
||||||
match t {
|
|
||||||
TypeType::Struct(s) => {
|
|
||||||
|
|
||||||
let mut disp = 0;
|
|
||||||
find_disp(&s, &mut disp, &p[1..]);
|
|
||||||
return Ok(AstNode::MemUse(MemUse{
|
|
||||||
ident: p[0].clone(),
|
|
||||||
loc: org.loc(),
|
|
||||||
disp: Some(disp)
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
error!({loc => org.loc()}, "You can only access items in structs");
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
error!("Failed to find memory {}", p[0]);
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_struct_path(org: &Token, prog: &mut Program, p: &Vec<String>) -> Result<AstNode> {
|
|
||||||
|
|
||||||
fn find_disp(strct: &StructDef, disp: &mut usize, path: &[String]) {
|
|
||||||
let Some(p) = path.get(0) else {
|
|
||||||
return
|
|
||||||
};
|
|
||||||
|
|
||||||
for item in &strct.body {
|
|
||||||
if p == &item.0 {
|
|
||||||
match &item.2 {
|
|
||||||
TypeType::Struct(strct) => {
|
|
||||||
*disp += item.1;
|
|
||||||
find_disp(strct, disp, &path[1..])
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
*disp += item.1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
let mut disp = 0;
|
|
||||||
if let Some(strct) = prog.struct_defs.get(&p[0].to_string()) {
|
|
||||||
find_disp(strct, &mut disp, &p[1..]);
|
|
||||||
return Ok(AstNode::StructDispPush{
|
|
||||||
ident: org.lexem.clone(),
|
|
||||||
loc: org.loc(),
|
|
||||||
disp
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
error!("Failed to find struct {}", p[0]);
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_struct(org: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>) -> Result<AstNode> {
|
|
||||||
let ident = expect(tokens, TokenType::Unknown(String::new()))?;
|
|
||||||
expect(tokens, TokenType::Keyword(KeywordType::Do))?;
|
|
||||||
|
|
||||||
|
|
||||||
let mut body: Vec<(String, usize, TypeType)> = Vec::new();
|
|
||||||
let mut size = 0;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let ident = expect(tokens, TokenType::Unknown(String::new()))?;
|
|
||||||
expect(tokens, TokenType::Keyword(KeywordType::Do))?;
|
|
||||||
let typ = parse_next(cli_args, prog, tokens, Flags::ALLOW_TYPES, false)?;
|
|
||||||
let (typ, disp) = match &typ {
|
|
||||||
AstNode::Token(t) => {
|
|
||||||
match &t.typ {
|
|
||||||
TokenType::Type(t) => {
|
|
||||||
let disp = size;
|
|
||||||
size += t.get_size();
|
|
||||||
(t, disp)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
error!({loc => t.loc()}, "Expected type, got {t:?}");
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
t => {
|
|
||||||
error!({loc => typ.loc()}, "Expected type, got {t:?}");
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
expect(tokens, TokenType::Keyword(KeywordType::End))?;
|
|
||||||
|
|
||||||
body.push((ident.lexem, disp, typ.clone()));
|
|
||||||
|
|
||||||
if peek_check(tokens, TokenType::Keyword(KeywordType::Done)).correct(){
|
|
||||||
tokens.pop();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// if peek_check(tokens, TokenType::Keyword(KeywordType::End)).correct()
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let def = StructDef{
|
|
||||||
loc: org.loc(),
|
|
||||||
ident: ident.lexem.clone(),
|
|
||||||
body,
|
|
||||||
size,
|
|
||||||
};
|
|
||||||
|
|
||||||
prog.struct_defs.insert(ident.lexem, def.clone());
|
|
||||||
|
|
||||||
Ok(AstNode::StructDef(def))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_memory(org: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>, is_module_root: bool) -> Result<AstNode> {
|
|
||||||
let name = expect(tokens, TokenType::Unknown(String::new()))?;
|
|
||||||
|
|
||||||
|
|
||||||
let mut body = Vec::new();
|
|
||||||
loop {
|
|
||||||
|
|
||||||
let t = peek_check(tokens, TokenType::Keyword(KeywordType::End));
|
|
||||||
match t {
|
|
||||||
PeekResult::Correct(_) => break,
|
|
||||||
PeekResult::Wrong(_) => (),
|
|
||||||
PeekResult::None => panic!("idk what to do herre"),
|
|
||||||
}
|
|
||||||
body.push(parse_next(cli_args, prog, tokens, Flags::ALLOW_TYPES, false)?);
|
|
||||||
}
|
|
||||||
expect(tokens, TokenType::Keyword(KeywordType::End))?;
|
|
||||||
|
|
||||||
let val = precompile_mem(prog, body)?;
|
|
||||||
|
|
||||||
let name = name.lexem.clone()
|
|
||||||
.replace("(", "_OPRN_")
|
|
||||||
.replace(")", "_CPRN_");
|
|
||||||
|
|
||||||
let def = Memory{
|
|
||||||
loc: org.loc(),
|
|
||||||
ident: name.clone(),
|
|
||||||
size: val,
|
|
||||||
statc: is_module_root,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
prog.memories.insert(name, def.clone());
|
|
||||||
|
|
||||||
Ok(AstNode::Memory(def))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Extern functions
|
|
||||||
fn parse_function(org: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>, flags: Flags ) -> Result<AstNode> {
|
|
||||||
|
|
||||||
|
|
||||||
let name = expect(tokens, TokenType::Unknown(String::new()))?;
|
|
||||||
expect(tokens, TokenType::Keyword(KeywordType::With))?;
|
|
||||||
let mut args = Vec::new();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if let PeekResult::Correct(t) = peek_check_multiple(tokens, vec![
|
|
||||||
TokenType::Type(TypeType::Any),
|
|
||||||
TokenType::Type(TypeType::U8),
|
|
||||||
TokenType::Type(TypeType::U16),
|
|
||||||
TokenType::Type(TypeType::U32),
|
|
||||||
TokenType::Type(TypeType::U64),
|
|
||||||
TokenType::Type(TypeType::Ptr),
|
|
||||||
TokenType::Type(TypeType::Void),
|
|
||||||
TokenType::Type(TypeType::Custom(Vec::new())),
|
|
||||||
]) {
|
|
||||||
match &t.typ {
|
|
||||||
TokenType::Type(tt) => {
|
|
||||||
args.push(tt.clone());
|
|
||||||
}
|
|
||||||
_ => unreachable!()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
tokens.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(tokens, TokenType::Keyword(KeywordType::Returns))?;
|
|
||||||
|
|
||||||
let mut ret_args = Vec::new();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if let PeekResult::Correct(t) = peek_check_multiple(tokens, vec![
|
|
||||||
TokenType::Type(TypeType::Any),
|
|
||||||
TokenType::Type(TypeType::U8),
|
|
||||||
TokenType::Type(TypeType::U16),
|
|
||||||
TokenType::Type(TypeType::U32),
|
|
||||||
TokenType::Type(TypeType::U64),
|
|
||||||
TokenType::Type(TypeType::Ptr),
|
|
||||||
TokenType::Type(TypeType::Void),
|
|
||||||
TokenType::Type(TypeType::Custom(Vec::new())),
|
|
||||||
]) {
|
|
||||||
match &t.typ {
|
|
||||||
TokenType::Type(tt) => {
|
|
||||||
ret_args.push(tt.clone());
|
|
||||||
}
|
|
||||||
_ => unreachable!()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
tokens.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
expect(tokens, TokenType::Keyword(KeywordType::Then))?;
|
|
||||||
let mut body = Vec::new();
|
|
||||||
loop {
|
|
||||||
|
|
||||||
let fn_got = peek_check(tokens, TokenType::Keyword(KeywordType::Done));
|
|
||||||
match fn_got {
|
|
||||||
PeekResult::Correct(_) => break,
|
|
||||||
PeekResult::Wrong(_) => (),
|
|
||||||
PeekResult::None => panic!("idk what to do herre"),
|
|
||||||
}
|
|
||||||
body.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
|
|
||||||
}
|
|
||||||
expect(tokens, TokenType::Keyword(KeywordType::Done))?;
|
|
||||||
|
|
||||||
let fn_def = Function {
|
|
||||||
loc: org.loc(),
|
|
||||||
inline: flags.contains(Flags::INLINE),
|
|
||||||
extrn: flags.contains(Flags::EXTERN),
|
|
||||||
export: flags.contains(Flags::EXPORT),
|
|
||||||
ident: name.lexem.clone(),
|
|
||||||
arg_types: args,
|
|
||||||
ret_types: ret_args,
|
|
||||||
body,
|
|
||||||
};
|
|
||||||
//TODO: Support module paths without double definitions
|
|
||||||
// let mut mp = match &prog.ast {
|
|
||||||
// AstNode::Module(m) => {
|
|
||||||
// m.path.clone()
|
|
||||||
// }
|
|
||||||
// _ => panic!("")
|
|
||||||
// };
|
|
||||||
// mp.push(name.lexem.clone());
|
|
||||||
// let mp = mp.join("::");
|
|
||||||
|
|
||||||
// prog.function_aliases.insert(mp, name.lexem.clone());
|
|
||||||
prog.functions.insert(name.lexem.clone(), fn_def.clone());
|
|
||||||
Ok(AstNode::Function(fn_def))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_if(org: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>) -> Result<AstNode> {
|
|
||||||
let mut test: Vec<AstNode> = Vec::new();
|
|
||||||
let mut body: Vec<AstNode> = Vec::new();
|
|
||||||
let mut els: Vec<AstNode> = Vec::new();
|
|
||||||
loop {
|
|
||||||
test.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
|
|
||||||
match peek_check(tokens, TokenType::Keyword(KeywordType::Do)) {
|
|
||||||
PeekResult::Correct(_) => break,
|
|
||||||
PeekResult::Wrong(w) => {
|
|
||||||
match w.typ {
|
|
||||||
TokenType::Keyword(KeywordType::Then) => {
|
|
||||||
warn!({loc => w.loc()}, "If is defined as `if ... do ... done`");
|
|
||||||
}
|
|
||||||
_ => ()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PeekResult::None => panic!("idk what to do herre"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(tokens, TokenType::Keyword(KeywordType::Do))?;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
loop {
|
|
||||||
body.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
|
|
||||||
match peek_check_multiple(tokens, vec![
|
|
||||||
TokenType::Keyword(KeywordType::Else),
|
|
||||||
TokenType::Keyword(KeywordType::Done),
|
|
||||||
]) {
|
|
||||||
PeekResult::Correct(_) => break,
|
|
||||||
PeekResult::Wrong(_) => (),
|
|
||||||
PeekResult::None => panic!("idk what to do herre"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let els_t = tokens.last().expect("IMPOSSIBLEEE!!!!!!111").clone();
|
|
||||||
let els = match els_t.typ.clone() {
|
|
||||||
TokenType::Keyword(kw) => {
|
|
||||||
match kw {
|
|
||||||
KeywordType::Done => {
|
|
||||||
expect(tokens, TokenType::Keyword(KeywordType::Done))?;
|
|
||||||
AstNode::Block(Block{
|
|
||||||
comment: String::new(),
|
|
||||||
loc: els_t.loc,
|
|
||||||
body: Vec::new(),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
KeywordType::Else => {
|
|
||||||
expect(tokens, TokenType::Keyword(KeywordType::Else))?;
|
|
||||||
if peek_check(tokens, TokenType::Keyword(KeywordType::If)).correct() {
|
|
||||||
let if_org =expect(tokens, TokenType::Keyword(KeywordType::If))?;
|
|
||||||
parse_if(&if_org, cli_args, prog, tokens)?
|
|
||||||
} else {
|
|
||||||
loop {
|
|
||||||
els.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
|
|
||||||
match peek_check(tokens, TokenType::Keyword(KeywordType::Done)) {
|
|
||||||
PeekResult::Correct(_) => break,
|
|
||||||
PeekResult::Wrong(w) => {
|
|
||||||
match w.typ {
|
|
||||||
TokenType::Keyword(KeywordType::Then) => {
|
|
||||||
warn!("If is defined as `if ... do ... done`");
|
|
||||||
}
|
|
||||||
_ => ()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PeekResult::None => panic!("idk what to do herre"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expect(tokens, TokenType::Keyword(KeywordType::Done))?;
|
|
||||||
|
|
||||||
AstNode::Block(Block{
|
|
||||||
comment: String::new(),
|
|
||||||
loc: els_t.loc,
|
|
||||||
body: els,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
e => {
|
|
||||||
error!({loc => els_t.loc.clone()}, "Expected {:?} or {:?} but got {:?}", KeywordType::Done, KeywordType::Else, e);
|
|
||||||
bail!("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
e => {
|
|
||||||
error!({loc => els_t.loc.clone()}, "Expected {:?} or {:?} but got {:?}", KeywordType::Done, KeywordType::Else, e);
|
|
||||||
bail!("");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(AstNode::If(If{
|
|
||||||
test,
|
|
||||||
body,
|
|
||||||
els: Box::new(els),
|
|
||||||
loc: org.loc(),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn parse_while(org: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>) -> Result<AstNode> {
|
|
||||||
let mut test: Vec<AstNode> = Vec::new();
|
|
||||||
let mut body: Vec<AstNode> = Vec::new();
|
|
||||||
loop {
|
|
||||||
test.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
|
|
||||||
match peek_check(tokens, TokenType::Keyword(KeywordType::Do)) {
|
|
||||||
PeekResult::Correct(_) => break,
|
|
||||||
PeekResult::Wrong(w) => {
|
|
||||||
match w.typ {
|
|
||||||
TokenType::Keyword(KeywordType::Then) => {
|
|
||||||
warn!("while is defined as `while ... do ... done`");
|
|
||||||
}
|
|
||||||
_ => ()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PeekResult::None => panic!("idk what to do herre"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(tokens, TokenType::Keyword(KeywordType::Do))?;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
loop {
|
|
||||||
body.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
|
|
||||||
match peek_check_multiple(tokens, vec![
|
|
||||||
TokenType::Keyword(KeywordType::Else),
|
|
||||||
TokenType::Keyword(KeywordType::Done),
|
|
||||||
]) {
|
|
||||||
PeekResult::Correct(_) => break,
|
|
||||||
PeekResult::Wrong(_) => (),
|
|
||||||
PeekResult::None => panic!("idk what to do herre"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
expect(tokens, TokenType::Keyword(KeywordType::Done))?;
|
|
||||||
|
|
||||||
Ok(AstNode::While(While{
|
|
||||||
test,
|
|
||||||
body,
|
|
||||||
loc: org.loc(),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_inline(_: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>, flags: Flags) -> Result<AstNode> {
|
|
||||||
let allowed_tokens = vec!{
|
|
||||||
TokenType::Keyword(KeywordType::Function)
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
let Some(t) = tokens.last() else {
|
|
||||||
error!("Expected one of {:?} after {:?} but found nothing", allowed_tokens, TokenType::Keyword(KeywordType::Inline));
|
|
||||||
bail!("")
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
let mut found = false;
|
|
||||||
|
|
||||||
for at in &allowed_tokens {
|
|
||||||
if utils::cmp(at, &t.typ) {
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
error!({loc => t.loc.clone()}, "Expected one of {:?} after {:?} but found {:?}", allowed_tokens, TokenType::Keyword(KeywordType::Inline), t.typ);
|
|
||||||
bail!("");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
parse_next(cli_args, prog, tokens, flags | Flags::INLINE, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_extern(_: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>, flags: Flags) -> Result<AstNode> {
|
|
||||||
let allowed_tokens = vec!{
|
|
||||||
TokenType::Keyword(KeywordType::Function),
|
|
||||||
TokenType::Keyword(KeywordType::Constant),
|
|
||||||
TokenType::Keyword(KeywordType::Memory),
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
let Some(t) = tokens.last() else {
|
|
||||||
error!("Expected one of {:?} after {:?} but found nothing", allowed_tokens, TokenType::Keyword(KeywordType::Extern));
|
|
||||||
bail!("")
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
let mut found = false;
|
|
||||||
|
|
||||||
for at in &allowed_tokens {
|
|
||||||
if utils::cmp(at, &t.typ) {
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
error!({loc => t.loc.clone()}, "Expected one of {:?} after {:?} but found {:?}", allowed_tokens, TokenType::Keyword(KeywordType::Extern), t.typ);
|
|
||||||
bail!("");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
parse_next(cli_args, prog, tokens, flags | Flags::EXTERN, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_export(_: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>, flags: Flags) -> Result<AstNode> {
|
|
||||||
let allowed_tokens = vec!{
|
|
||||||
TokenType::Keyword(KeywordType::Function),
|
|
||||||
TokenType::Keyword(KeywordType::Constant),
|
|
||||||
TokenType::Keyword(KeywordType::Memory),
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
let Some(t) = tokens.last() else {
|
|
||||||
error!("Expected one of {:?} after {:?} but found nothing", allowed_tokens, TokenType::Keyword(KeywordType::Export));
|
|
||||||
bail!("")
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
let mut found = false;
|
|
||||||
|
|
||||||
for at in &allowed_tokens {
|
|
||||||
if utils::cmp(at, &t.typ) {
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
error!({loc => t.loc.clone()}, "Expected one of {:?} after {:?} but found {:?}", allowed_tokens, TokenType::Keyword(KeywordType::Export), t.typ);
|
|
||||||
bail!("");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
parse_next(cli_args, prog, tokens, flags | Flags::EXPORT, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn parse_include(_: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>) -> Result<AstNode> {
|
|
||||||
let path = expect(tokens,
|
|
||||||
TokenType::Instruction(
|
|
||||||
InstructionType::PushStr(
|
|
||||||
String::new()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)?;
|
|
||||||
|
|
||||||
for ip in &cli_args.include_path {
|
|
||||||
let p = ip.join(&path.lexem).to_path_buf();
|
|
||||||
if p.exists() {
|
|
||||||
info!({loc => path.loc.clone()}, "Lexing file {}", path.lexem.clone());
|
|
||||||
let mut lexer = Lexer::new();
|
|
||||||
lexer.lex(p.as_std_path())?;
|
|
||||||
|
|
||||||
let mut mod_tokens = lexer.tokens;
|
|
||||||
|
|
||||||
mod_tokens.reverse();
|
|
||||||
|
|
||||||
let mut mp = match &prog.ast {
|
|
||||||
AstNode::Module(m) => {
|
|
||||||
m.path.clone()
|
|
||||||
}
|
|
||||||
_ => panic!("")
|
|
||||||
};
|
|
||||||
|
|
||||||
mp.push(p.file_stem().unwrap().to_string());
|
|
||||||
|
|
||||||
let module = Module {
|
|
||||||
loc: Loc::new(path.loc.file.clone(), 0, 0),
|
|
||||||
ident: Path::new(&path.loc.file).file_stem().expect("Something went horribly wrong").to_string_lossy().to_string(),
|
|
||||||
body: Vec::new(),
|
|
||||||
path: mp,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
let mut mod_prog = Program {
|
|
||||||
ast: AstNode::Module(module),
|
|
||||||
functions: prog.functions.clone(),
|
|
||||||
constants: prog.constants.clone(),
|
|
||||||
memories: prog.memories.clone(),
|
|
||||||
struct_defs: prog.struct_defs.clone(),
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
info!({loc => path.loc.clone()}, "Parsing file {}", path.lexem.clone());
|
|
||||||
while !mod_tokens.is_empty() {
|
|
||||||
let node = parse_next(cli_args, &mut mod_prog, &mut mod_tokens, Flags::empty(), true)?;
|
|
||||||
match &mut mod_prog.ast {
|
|
||||||
AstNode::Module(module) => {
|
|
||||||
module.body.push(node);
|
|
||||||
}
|
|
||||||
_ => unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prog.constants = mod_prog.constants;
|
|
||||||
prog.functions = mod_prog.functions;
|
|
||||||
prog.memories = mod_prog.memories;
|
|
||||||
return Ok(mod_prog.ast)
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
error!("Could not find file {:?} in these locations: {:?}", path.lexem, cli_args.include_path);
|
|
||||||
bail!("")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_const(org: &Token, cli_args: &CliArgs, prog: &mut Program, tokens: &mut Vec<Token>) -> Result<AstNode> {
|
|
||||||
let name = expect(tokens, TokenType::Unknown(String::new()))?;
|
|
||||||
|
|
||||||
|
|
||||||
let mut body = Vec::new();
|
|
||||||
loop {
|
|
||||||
|
|
||||||
let t = peek_check(tokens, TokenType::Keyword(KeywordType::End));
|
|
||||||
match t {
|
|
||||||
PeekResult::Correct(_) => break,
|
|
||||||
PeekResult::Wrong(_) => (),
|
|
||||||
PeekResult::None => panic!("idk what to do herre"),
|
|
||||||
}
|
|
||||||
body.push(parse_next(cli_args, prog, tokens, Flags::empty(), false)?);
|
|
||||||
}
|
|
||||||
expect(tokens, TokenType::Keyword(KeywordType::End))?;
|
|
||||||
|
|
||||||
let val = precompile_const(prog, body, &mut Vec::new())?;
|
|
||||||
|
|
||||||
|
|
||||||
let def = Constant{
|
|
||||||
loc: org.loc(),
|
|
||||||
ident: name.lexem.clone(),
|
|
||||||
value: Box::new(val),
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
prog.constants.insert(name.lexem, def.clone());
|
|
||||||
|
|
||||||
Ok(AstNode::Constant(def))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_unknown(org: &Token, _: &CliArgs, prog: &mut Program, _: &mut Vec<Token>, _: Flags ) -> Result<AstNode> {
|
|
||||||
//TODO: Typing?
|
|
||||||
if let Some(func) = prog.functions.get(&org.lexem.clone()) {
|
|
||||||
if func.inline {
|
|
||||||
return Ok(AstNode::Block(Block{ loc: org.loc.clone(), body: func.body.clone(), comment: format!("inline fn {}", func.ident) }))
|
|
||||||
} else {
|
|
||||||
return Ok(AstNode::FnCall(FnCall{ loc: org.loc.clone(), ident: org.lexem.clone() }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(_) = prog.constants.get(&org.lexem.clone()) {
|
|
||||||
return Ok(AstNode::ConstUse(ConstUse{ loc: org.loc.clone(), ident: org.lexem.clone() }));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(_) = prog.memories.get(&org.lexem.clone()) {
|
|
||||||
return Ok(AstNode::MemUse(MemUse{ loc: org.loc.clone(), ident: org.lexem.clone(), disp: None }));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(t) = prog.struct_defs.get(&org.lexem.clone()) {
|
|
||||||
return Ok(AstNode::Token(Token {
|
|
||||||
typ: TokenType::Type(TypeType::Struct(t.clone())),
|
|
||||||
loc: org.loc(),
|
|
||||||
lexem: org.lexem.clone(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// if org.lexem.clone().contains("::") {
|
|
||||||
// let pth = org.lexem.clone();
|
|
||||||
// let pth = pth.split("::").collect::<Vec<&str>>();
|
|
||||||
// dbg!(prog.struct_defs.clone());
|
|
||||||
// if let Some(t) = prog.struct_defs.get(&pth[0].to_string()) {
|
|
||||||
// if let Some(i) = t.body.iter().find(|i| i.0 == pth[1].to_string()) {
|
|
||||||
// return Ok(AstNode::StructDispPush{
|
|
||||||
// ident: org.lexem.clone(),
|
|
||||||
// loc: org.loc(),
|
|
||||||
// disp: i.1
|
|
||||||
// });
|
|
||||||
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// dbg!(&prog.constants);
|
|
||||||
debug!({loc => org.loc.clone()}, "Unknown token");
|
|
||||||
error!({loc => org.loc.clone()}, "Unknown token {:?}", org);
|
|
||||||
bail!("")
|
|
||||||
}
|
|
|
@ -1,213 +0,0 @@
|
||||||
use anyhow::bail;
|
|
||||||
|
|
||||||
use crate::types::{ast::{AstNode, MemSize, Program}, common::Loc, token::{InstructionType, TokenType, TypeType}};
|
|
||||||
|
|
||||||
|
|
||||||
pub fn precompile_mem(prog: &Program, ast: Vec<AstNode> ) -> anyhow::Result<MemSize> {
|
|
||||||
match &ast[0] {
|
|
||||||
AstNode::Token(t) => {
|
|
||||||
match &t.typ {
|
|
||||||
TokenType::Type(_) => {
|
|
||||||
let mut buf = vec![];
|
|
||||||
let mut i = 0;
|
|
||||||
while ast.len() > i {
|
|
||||||
match &ast[i] {
|
|
||||||
AstNode::Token(t) => {
|
|
||||||
match &t.typ {
|
|
||||||
TokenType::Type(t) => {
|
|
||||||
match t {
|
|
||||||
TypeType::Struct(s) => {
|
|
||||||
return Ok(MemSize::Type(TypeType::Struct(s.clone())));
|
|
||||||
},
|
|
||||||
_ => ()
|
|
||||||
}
|
|
||||||
buf.push(t.clone());
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
error!({loc => t.loc()}, "Cannot use a type and a number as a memory size at the same time");
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
error!({loc => t.loc()}, "Cannot use a type and a number as a memory size at the same time");
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Ok(MemSize::Type(TypeType::Custom(buf)));
|
|
||||||
}
|
|
||||||
_ => ()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
match precompile_const(prog, ast, &mut Vec::new()) {
|
|
||||||
Ok(v) => {
|
|
||||||
match v {
|
|
||||||
AstNode::Int(_, i) => {
|
|
||||||
return Ok(MemSize::Size(i))
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
error!({loc => v.loc()}, "Can only have a type or a number as a memory size");
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(e) => bail!(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn precompile_const(prog: &Program, ast: Vec<AstNode>, stack: &mut Vec<usize> ) -> anyhow::Result<AstNode> {
|
|
||||||
for node in ast.clone() {
|
|
||||||
match &node {
|
|
||||||
AstNode::ConstUse(c) => {
|
|
||||||
let Some(val) = prog.constants.get(&c.ident) else {
|
|
||||||
error!({loc => c.loc.clone()}, "Unknown constant {:?}", c.ident) ;
|
|
||||||
bail!("")
|
|
||||||
};
|
|
||||||
match Box::leak(val.value.clone()) {
|
|
||||||
t @ AstNode::Int(..) => {
|
|
||||||
return Ok(t.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
t @ AstNode::Str(..) => {
|
|
||||||
return Ok(t.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
t @ AstNode::CStr(..) => {
|
|
||||||
return Ok(t.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
t @ AstNode::Char(..) => {
|
|
||||||
return Ok(t.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// AstNode::Token(t) => {
|
|
||||||
// match t.typ.clone() {
|
|
||||||
// TokenType::Instruction(it) => {
|
|
||||||
// match it {
|
|
||||||
// InstructionType::PushInt(i) => stack.push(i),
|
|
||||||
// InstructionType::PushCStr(_) => {
|
|
||||||
// //TODO: Handle this better
|
|
||||||
// return Ok(AstNode::Token(t.clone()));
|
|
||||||
// },
|
|
||||||
// InstructionType::PushChar(_) => {
|
|
||||||
// //TODO: Handle this better
|
|
||||||
// return Ok(AstNode::Token(t.clone()));
|
|
||||||
// },
|
|
||||||
// _ => panic!()
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// _ => panic!()
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
_ => panic!()
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
AstNode::Token(t) => {
|
|
||||||
match t.typ.clone() {
|
|
||||||
TokenType::Keyword(_) => {
|
|
||||||
error!({loc => t.loc.clone()}, "Unsupported token {t:?}, we dont support precompilation of this") ;
|
|
||||||
bail!("")
|
|
||||||
},
|
|
||||||
TokenType::Instruction(it) => {
|
|
||||||
match it {
|
|
||||||
InstructionType::PushInt(i) => {
|
|
||||||
stack.push(i);
|
|
||||||
},
|
|
||||||
InstructionType::PushCStr(s) => {
|
|
||||||
//TODO: Handle this better
|
|
||||||
return Ok(AstNode::CStr(t.loc.clone(), s));
|
|
||||||
},
|
|
||||||
InstructionType::PushStr(s) => {
|
|
||||||
//TODO: Handle this better
|
|
||||||
return Ok(AstNode::Str(t.loc.clone(), s));
|
|
||||||
},
|
|
||||||
InstructionType::PushChar(c) => {
|
|
||||||
//TODO: Handle this better
|
|
||||||
return Ok(AstNode::Char(t.loc.clone(), c));
|
|
||||||
},
|
|
||||||
InstructionType::Minus => {
|
|
||||||
let a = stack_pop(stack, &t.loc)?;
|
|
||||||
let b = stack_pop(stack, &t.loc)?;
|
|
||||||
stack.push(b - a);
|
|
||||||
},
|
|
||||||
InstructionType::Plus => {
|
|
||||||
let a = stack_pop(stack, &t.loc)?;
|
|
||||||
let b = stack_pop(stack, &t.loc)?;
|
|
||||||
stack.push(b + a);
|
|
||||||
},
|
|
||||||
InstructionType::DivMod => {
|
|
||||||
let a = stack_pop(stack, &t.loc)?;
|
|
||||||
let b = stack_pop(stack, &t.loc)?;
|
|
||||||
stack.push(b / a);
|
|
||||||
stack.push(b % a);
|
|
||||||
},
|
|
||||||
InstructionType::Mul => {
|
|
||||||
let a = stack_pop(stack, &t.loc)?;
|
|
||||||
let b = stack_pop(stack, &t.loc)?;
|
|
||||||
stack.push(b * a);
|
|
||||||
},
|
|
||||||
InstructionType::Drop => {
|
|
||||||
stack_pop(stack, &t.loc)?;
|
|
||||||
},
|
|
||||||
//TODO: Support these later
|
|
||||||
// InstructionType::Dup => todo!(),
|
|
||||||
// InstructionType::Rot => todo!(),
|
|
||||||
// InstructionType::Over => todo!(),
|
|
||||||
// InstructionType::Swap => todo!(),
|
|
||||||
// InstructionType::Equals => todo!(),
|
|
||||||
// InstructionType::Gt => todo!(),
|
|
||||||
// InstructionType::Lt => todo!(),
|
|
||||||
// InstructionType::Ge => todo!(),
|
|
||||||
// InstructionType::Le => todo!(),
|
|
||||||
// InstructionType::NotEquals => todo!(),
|
|
||||||
// InstructionType::Band => todo!(),
|
|
||||||
// InstructionType::Bor => todo!(),
|
|
||||||
// InstructionType::Shr => todo!(),
|
|
||||||
// InstructionType::Shl => todo!(),
|
|
||||||
//TODO: Support this when we have types
|
|
||||||
// InstructionType::CastBool => todo!(),
|
|
||||||
// InstructionType::CastPtr => todo!(),
|
|
||||||
// InstructionType::CastInt => todo!(),
|
|
||||||
// InstructionType::CastVoid => todo!(),
|
|
||||||
InstructionType::ConstUse => unreachable!(),
|
|
||||||
_ => {
|
|
||||||
error!({loc => t.loc.clone()}, "Unsupported token {t:?}, we dont support precompilation of this") ;
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
TokenType::Unknown(_) => unreachable!(),
|
|
||||||
TokenType::Type(_) => {
|
|
||||||
error!({loc => t.loc()}, "Cannot use a type and a number as a memory size at the same time");
|
|
||||||
bail!("")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
//TODO: Implement these
|
|
||||||
t @ AstNode::If { .. } |
|
|
||||||
t @ AstNode::While { .. } |
|
|
||||||
t => {
|
|
||||||
error!({loc => t.loc()}, "Unsupported token {t:?}, we dont support precompilation of this") ;
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(AstNode::Int(ast[0].loc(), stack[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stack_pop(stack: &mut Vec<usize>, loc: &Loc) -> anyhow::Result<usize> {
|
|
||||||
match stack.pop() {
|
|
||||||
Some(i) => Ok(i),
|
|
||||||
None => {
|
|
||||||
error!({loc => loc.clone()}, "Failed to precompile tokens, failed to pop from stack");
|
|
||||||
bail!("")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,103 +0,0 @@
|
||||||
use anyhow::{bail, Result};
|
|
||||||
|
|
||||||
use crate::types::token::{Token, TokenType};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
|
||||||
pub enum PeekResult<T> {
|
|
||||||
Correct(T),
|
|
||||||
Wrong(T),
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> PeekResult<T> {
|
|
||||||
pub fn correct(&self) -> bool{
|
|
||||||
match self {
|
|
||||||
PeekResult::Correct(_) => true,
|
|
||||||
_ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn wrong(&self) -> bool{
|
|
||||||
match self {
|
|
||||||
PeekResult::Wrong(_) => true,
|
|
||||||
_ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn none(&self) -> bool{
|
|
||||||
match self {
|
|
||||||
PeekResult::None => true,
|
|
||||||
_ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cmp(lhs: &TokenType, rhs: &TokenType) -> bool {
|
|
||||||
match (lhs, rhs) {
|
|
||||||
(TokenType::Keyword(lhs), TokenType::Keyword(rhs)) => {
|
|
||||||
std::mem::discriminant(lhs) == std::mem::discriminant(rhs)
|
|
||||||
},
|
|
||||||
(TokenType::Instruction(lhs), TokenType::Instruction(rhs)) => {
|
|
||||||
std::mem::discriminant(lhs) == std::mem::discriminant(rhs)
|
|
||||||
},
|
|
||||||
(TokenType::Type(lhs), TokenType::Type(rhs)) => {
|
|
||||||
std::mem::discriminant(lhs) == std::mem::discriminant(rhs)
|
|
||||||
},
|
|
||||||
(TokenType::Unknown(_), TokenType::Unknown(_)) => true,
|
|
||||||
_ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn peek_check_multiple(tokens: &Vec<Token>, typs: Vec<TokenType>) -> PeekResult<&Token>{
|
|
||||||
let t = tokens.last();
|
|
||||||
|
|
||||||
if let Some(t) = t {
|
|
||||||
for tt in typs.clone() {
|
|
||||||
if cmp(&t.typ, &tt) {
|
|
||||||
return PeekResult::Correct(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PeekResult::Wrong(t)
|
|
||||||
} else {
|
|
||||||
PeekResult::None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn peek_check(tokens: &Vec<Token>, typ: TokenType) -> PeekResult<&Token> {
|
|
||||||
let t = tokens.last();
|
|
||||||
|
|
||||||
match t {
|
|
||||||
Some(t) => {
|
|
||||||
//? Source: https://doc.rust-lang.org/std/mem/fn.discriminant.html
|
|
||||||
if cmp(&t.typ, &typ) {
|
|
||||||
PeekResult::Correct(t)
|
|
||||||
} else {
|
|
||||||
PeekResult::Wrong(t)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
PeekResult::None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn expect(tokens: &mut Vec<Token>, typ: TokenType) -> Result<Token> {
|
|
||||||
let t = tokens.pop();
|
|
||||||
|
|
||||||
match t {
|
|
||||||
Some(t) => {
|
|
||||||
//? Source: https://doc.rust-lang.org/std/mem/fn.discriminant.html
|
|
||||||
if std::mem::discriminant(&t.typ) != std::mem::discriminant(&typ) {
|
|
||||||
error!({loc => t.loc()}, "Expected {:?}, but got {:?}", typ, t.typ);
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
Ok(t)
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
error!("Expected {:?}, but found nothing", typ);
|
|
||||||
bail!("")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
124
src/token.rs
Normal file
124
src/token.rs
Normal 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
226
src/tokeniser.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,215 +0,0 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use super::{common::Loc, token::{Token, TypeType}};
|
|
||||||
|
|
||||||
|
|
||||||
//TODO: Implement missing stuff
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum AstNode {
|
|
||||||
Int(Loc, usize),
|
|
||||||
Str(Loc, String),
|
|
||||||
CStr(Loc, String),
|
|
||||||
Char(Loc, char),
|
|
||||||
// ExternFnDef {
|
|
||||||
// loc: Loc,
|
|
||||||
// ident: String,
|
|
||||||
// arg_types: Vec<TokenType>,
|
|
||||||
// ret_type: TokenType,
|
|
||||||
// },
|
|
||||||
Function(Function),
|
|
||||||
Constant(Constant),
|
|
||||||
// ExternConstantDef{
|
|
||||||
// loc: Loc,
|
|
||||||
// ident: String,
|
|
||||||
// value: InstructionType
|
|
||||||
// },
|
|
||||||
// StructDef{
|
|
||||||
// loc: Loc,
|
|
||||||
// extrn: bool,
|
|
||||||
// ident: String,
|
|
||||||
// body: Vec<(String, usize)> // (field ident, size in bytes)
|
|
||||||
// },
|
|
||||||
StructDef(StructDef),
|
|
||||||
StructDispPush{
|
|
||||||
loc: Loc,
|
|
||||||
disp: usize,
|
|
||||||
ident: String,
|
|
||||||
},
|
|
||||||
// StructItemPush{
|
|
||||||
// loc: Loc,
|
|
||||||
// disp: usize,
|
|
||||||
// ident: String,
|
|
||||||
// },
|
|
||||||
If(If),
|
|
||||||
While(While),
|
|
||||||
Module(Module),
|
|
||||||
Memory(Memory),
|
|
||||||
MemUse(MemUse),
|
|
||||||
ConstUse(ConstUse),
|
|
||||||
FnCall(FnCall),
|
|
||||||
Block(Block),
|
|
||||||
Token(Token),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AstNode {
|
|
||||||
pub fn loc(&self) -> Loc {
|
|
||||||
match self {
|
|
||||||
AstNode::Function(f) => f.loc.clone(),
|
|
||||||
AstNode::Constant(c) => c.loc.clone(),
|
|
||||||
AstNode::If(t)=> t.loc.clone(),
|
|
||||||
AstNode::While(t)=> t.loc.clone(),
|
|
||||||
AstNode::Module(m) => m.loc.clone(),
|
|
||||||
AstNode::Memory(m) => m.loc.clone(),
|
|
||||||
AstNode::MemUse(t)=> t.loc.clone(),
|
|
||||||
AstNode::ConstUse(t)=> t.loc.clone(),
|
|
||||||
AstNode::FnCall(t)=> t.loc.clone(),
|
|
||||||
AstNode::Block(t)=> t.loc.clone(),
|
|
||||||
AstNode::Token(tok) => tok.loc.clone(),
|
|
||||||
AstNode::Int(loc, _) => loc.clone(),
|
|
||||||
AstNode::Str(loc, _) => loc.clone(),
|
|
||||||
AstNode::CStr(loc, _) => loc.clone(),
|
|
||||||
AstNode::Char(loc, _) => loc.clone(),
|
|
||||||
AstNode::StructDef(s) => s.loc.clone(),
|
|
||||||
AstNode::StructDispPush { loc, ..} => loc.clone(),
|
|
||||||
// AstNode::StructItemPush { loc, .. } => loc.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct StructDef {
|
|
||||||
pub loc: Loc,
|
|
||||||
pub ident: String,
|
|
||||||
pub body: Vec<(String, usize, TypeType)>, // (field ident, size in bytes)
|
|
||||||
pub size: usize
|
|
||||||
}
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct MemUse {
|
|
||||||
pub loc: Loc,
|
|
||||||
pub ident: String,
|
|
||||||
pub disp: Option<usize>
|
|
||||||
}
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ConstUse {
|
|
||||||
pub loc: Loc,
|
|
||||||
pub ident: String,
|
|
||||||
}
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct FnCall {
|
|
||||||
pub loc: Loc,
|
|
||||||
pub ident: String,
|
|
||||||
}
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Block {
|
|
||||||
pub comment: String,
|
|
||||||
pub loc: Loc,
|
|
||||||
pub body: Vec<AstNode>
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct While {
|
|
||||||
pub loc: Loc,
|
|
||||||
pub test: Vec<AstNode>,
|
|
||||||
pub body: Vec<AstNode>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct If {
|
|
||||||
pub loc: Loc,
|
|
||||||
pub test: Vec<AstNode>,
|
|
||||||
pub body: Vec<AstNode>,
|
|
||||||
pub els: Box<AstNode>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Module {
|
|
||||||
pub loc: Loc,
|
|
||||||
pub path: Vec<String>,
|
|
||||||
pub ident: String,
|
|
||||||
pub body: Vec<AstNode>
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Function {
|
|
||||||
pub loc: Loc,
|
|
||||||
pub ident: String,
|
|
||||||
pub inline: bool,
|
|
||||||
pub extrn: bool,
|
|
||||||
pub export: bool,
|
|
||||||
pub arg_types: Vec<TypeType>,
|
|
||||||
pub ret_types: Vec<TypeType>,
|
|
||||||
pub body: Vec<AstNode>
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Constant {
|
|
||||||
pub loc: Loc,
|
|
||||||
pub ident: String,
|
|
||||||
pub value: Box<AstNode>
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Memory {
|
|
||||||
pub loc: Loc,
|
|
||||||
pub ident: String,
|
|
||||||
pub statc: bool,
|
|
||||||
pub size: MemSize // bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Program {
|
|
||||||
pub ast: AstNode,
|
|
||||||
pub functions: HashMap<String, Function>,
|
|
||||||
pub constants: HashMap<String, Constant>,
|
|
||||||
pub memories: HashMap<String, Memory>,
|
|
||||||
pub struct_defs: HashMap<String, StructDef>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum MemSize {
|
|
||||||
Size(usize),
|
|
||||||
Type(TypeType)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EscIdent for FnCall {
|
|
||||||
fn ident(&self) -> String {
|
|
||||||
self.ident.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EscIdent for ConstUse {
|
|
||||||
fn ident(&self) -> String {
|
|
||||||
self.ident.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EscIdent for MemUse {
|
|
||||||
fn ident(&self) -> String {
|
|
||||||
self.ident.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EscIdent for Constant {
|
|
||||||
fn ident(&self) -> String {
|
|
||||||
self.ident.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl EscIdent for Memory {
|
|
||||||
fn ident(&self) -> String {
|
|
||||||
self.ident.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl EscIdent for Function {
|
|
||||||
fn ident(&self) -> String {
|
|
||||||
self.ident.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait EscIdent {
|
|
||||||
fn ident(&self) -> String;
|
|
||||||
fn get_ident_escaped(&self) -> String {
|
|
||||||
self.ident().replace("(", "_OPRN_")
|
|
||||||
.replace(")", "_CPRN_")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq)]
|
|
||||||
pub struct Loc {
|
|
||||||
pub file: String,
|
|
||||||
pub line: usize,
|
|
||||||
pub col: usize
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl Loc {
|
|
||||||
pub fn new<T: Into<String>>(f: T, line: usize, col: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
file: f.into(),
|
|
||||||
line,
|
|
||||||
col,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn inc_line(&mut self) {
|
|
||||||
self.line += 1;
|
|
||||||
self.col = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inc_col(&mut self) {
|
|
||||||
self.col += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Loc {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}:{}:{}", self.file, self.line, self.col)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
pub mod common;
|
|
||||||
pub mod token;
|
|
||||||
pub mod ast;
|
|
|
@ -1,162 +0,0 @@
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use super::{ast::StructDef, common::Loc};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum InstructionType {
|
|
||||||
|
|
||||||
// stack
|
|
||||||
PushInt(usize),
|
|
||||||
PushStr(String),
|
|
||||||
PushCStr(String),
|
|
||||||
PushChar(char),
|
|
||||||
StructPath(Vec<String>), // foo::bar
|
|
||||||
StructItem(Vec<String>), // foo.bar
|
|
||||||
Drop,
|
|
||||||
Print,
|
|
||||||
Dup,
|
|
||||||
Rot, // a b c => b c a
|
|
||||||
Over, // a b => a b a
|
|
||||||
Swap, // a b => b a
|
|
||||||
|
|
||||||
// math
|
|
||||||
Minus,
|
|
||||||
Plus,
|
|
||||||
Equals,
|
|
||||||
Gt,
|
|
||||||
Lt,
|
|
||||||
Ge,
|
|
||||||
Le,
|
|
||||||
NotEquals,
|
|
||||||
Band, // &
|
|
||||||
Bor, // |
|
|
||||||
Shr, // >>
|
|
||||||
Shl, // <<
|
|
||||||
DivMod, // /
|
|
||||||
Mul,
|
|
||||||
|
|
||||||
|
|
||||||
// mem
|
|
||||||
Read8,
|
|
||||||
Write8,
|
|
||||||
Read32,
|
|
||||||
Write32,
|
|
||||||
Read64,
|
|
||||||
Write64,
|
|
||||||
|
|
||||||
// syscalls
|
|
||||||
Syscall0,
|
|
||||||
Syscall1,
|
|
||||||
Syscall2,
|
|
||||||
Syscall3,
|
|
||||||
Syscall4,
|
|
||||||
Syscall5,
|
|
||||||
Syscall6,
|
|
||||||
|
|
||||||
CastBool,
|
|
||||||
CastPtr,
|
|
||||||
CastInt,
|
|
||||||
CastVoid,
|
|
||||||
|
|
||||||
FnCall,
|
|
||||||
MemUse,
|
|
||||||
ConstUse,
|
|
||||||
|
|
||||||
Return,
|
|
||||||
}
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
pub enum KeywordType {
|
|
||||||
If,
|
|
||||||
Else,
|
|
||||||
End,
|
|
||||||
While,
|
|
||||||
Do,
|
|
||||||
Include,
|
|
||||||
Memory,
|
|
||||||
Constant,
|
|
||||||
Function,
|
|
||||||
Then,
|
|
||||||
Done,
|
|
||||||
StructDef,
|
|
||||||
TypeDef,
|
|
||||||
Inline,
|
|
||||||
Export,
|
|
||||||
Extern,
|
|
||||||
Returns,
|
|
||||||
With,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
|
||||||
pub enum TypeType {
|
|
||||||
Ptr,
|
|
||||||
U8,
|
|
||||||
U16,
|
|
||||||
U32,
|
|
||||||
U64,
|
|
||||||
Void,
|
|
||||||
Any,
|
|
||||||
Custom(Vec<TypeType>),
|
|
||||||
Struct(StructDef)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TypeType {
|
|
||||||
pub fn get_size(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
TypeType::Ptr => std::mem::size_of::<*const ()>(),
|
|
||||||
TypeType::U8 => 1,
|
|
||||||
TypeType::U16 => 2,
|
|
||||||
TypeType::U32 => 4,
|
|
||||||
TypeType::U64 => 8,
|
|
||||||
TypeType::Void => 0,
|
|
||||||
TypeType::Any => 0,
|
|
||||||
TypeType::Custom(ts) => ts.iter().map(|f| f.get_size()).sum(),
|
|
||||||
TypeType::Struct(s) => s.size,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for TypeType {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Ptr => write!(f, "Ptr"),
|
|
||||||
Self::U8 => write!(f, "U8"),
|
|
||||||
Self::U16 => write!(f, "U16"),
|
|
||||||
Self::U32 => write!(f, "U32"),
|
|
||||||
Self::U64 => write!(f, "U64"),
|
|
||||||
Self::Void => write!(f, "Void"),
|
|
||||||
Self::Any => write!(f, "Any"),
|
|
||||||
Self::Custom(arg0) => f.debug_tuple("Custom").field(arg0).finish(),
|
|
||||||
Self::Struct(arg0) => write!(f, "{} {}{:?}", arg0.size, arg0.ident, arg0.body),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum TokenType {
|
|
||||||
Keyword(KeywordType),
|
|
||||||
Type(TypeType),
|
|
||||||
Instruction(InstructionType),
|
|
||||||
Unknown(String)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct Token {
|
|
||||||
pub typ: TokenType,
|
|
||||||
pub loc: Loc,
|
|
||||||
pub lexem: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Token {
|
|
||||||
pub fn new(typ: TokenType, loc: Loc, lexem: String) -> Self {
|
|
||||||
Self {
|
|
||||||
typ,
|
|
||||||
loc,
|
|
||||||
lexem,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn loc(&self) -> Loc {
|
|
||||||
self.loc.clone()
|
|
||||||
}
|
|
||||||
}
|
|
73
test.mcl
73
test.mcl
|
@ -1,49 +1,30 @@
|
||||||
include "std.mcl"
|
; vim: set ft=commonlisp:
|
||||||
|
|
||||||
structdef Uwu do
|
(fn main ((args (arr_t string_t))) (
|
||||||
owo do u64 end
|
(var a i32 35)
|
||||||
twt do u64 end
|
(var b i32_t)
|
||||||
done
|
(set b 34)
|
||||||
|
(set a (call uwu a b))
|
||||||
|
;(match a (
|
||||||
|
; (69 (
|
||||||
|
; print "ooooo"
|
||||||
|
; ))
|
||||||
|
; (420 (
|
||||||
|
; print "aaaaaaaa"
|
||||||
|
; ))
|
||||||
|
;))
|
||||||
|
|
||||||
structdef Foo do
|
;(if (eq a b) (
|
||||||
buz do u64 end
|
; print "yes"
|
||||||
uwu do Uwu end
|
;))
|
||||||
done
|
;(while (not (eq a b)) (
|
||||||
|
; (set_add (ref b) 1)
|
||||||
|
;))
|
||||||
|
))
|
||||||
|
|
||||||
|
;(fn uwu((a i32_t) (b i32_t)) (
|
||||||
memory s_foo Foo end
|
; (return (add a b))
|
||||||
|
;))
|
||||||
//? Comments :3
|
;(fn set_add ((a (ref i32_t)) (b i32_t)) (
|
||||||
|
; (ref_set a (add (deref a) b)
|
||||||
// extern fn a with void returns void then done
|
;))
|
||||||
// inline fn b with void returns void then done
|
|
||||||
// export fn c with void returns void then done
|
|
||||||
|
|
||||||
// fn putd with int returns void then drop done
|
|
||||||
|
|
||||||
fn main with void returns void then
|
|
||||||
|
|
||||||
s_foo.uwu.twt 69 write64
|
|
||||||
|
|
||||||
s_foo.uwu.twt read64 _dbg_print
|
|
||||||
// 1 2 add
|
|
||||||
// 69 _dbg_print
|
|
||||||
// "Hewo\n" puts
|
|
||||||
|
|
||||||
// if 3 4 eq do
|
|
||||||
// "omg what impossible!\n"
|
|
||||||
// else if 1 1 eq do
|
|
||||||
// "whaaaaaaaaa\n"
|
|
||||||
// else
|
|
||||||
// "finally, some good soup\n"
|
|
||||||
// done
|
|
||||||
// puts
|
|
||||||
|
|
||||||
// 10
|
|
||||||
// while dup 0 gt do
|
|
||||||
// "uwu " puts
|
|
||||||
// dup _dbg_print
|
|
||||||
// 1 sub
|
|
||||||
// done
|
|
||||||
|
|
||||||
done
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user