diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c41cc9e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..1e9ba2d --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/whython-8.iml b/.idea/whython-8.iml new file mode 100644 index 0000000..bbe0a70 --- /dev/null +++ b/.idea/whython-8.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..4acd214 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,512 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anstream" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "b-box" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79a7873ba5be66516cc71605969989bea6de94f2a3b5215bcd8c60169ce2e268" + +[[package]] +name = "brownstone" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5839ee4f953e811bfdcf223f509cb2c6a3e1447959b0bff459405575bc17f22" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "bytecount" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" + +[[package]] +name = "clap" +version = "4.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "color-print" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a858372ff14bab9b1b30ea504f2a4bc534582aee3e42ba2d41d2a7baba63d5d" +dependencies = [ + "color-print-proc-macro", +] + +[[package]] +name = "color-print-proc-macro" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57e37866456a721d0a404439a1adae37a31be4e0055590d053dfe6981e05003f" +dependencies = [ + "nom", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indent_write" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cfe9645a18782869361d9c8732246be7b410ad4e919d3609ebabdac00ba12c3" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "joinery" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72167d68f5fce3b8655487b8038691a3c9984ee769590f93f2a631f4ad64e4f5" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nom-supreme" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd3ae6c901f1959588759ff51c95d24b491ecb9ff91aa9c2ef4acc5b1dcab27" +dependencies = [ + "brownstone", + "indent_write", + "joinery", + "memchr", + "nom", +] + +[[package]] +name = "nom_locate" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" +dependencies = [ + "bytecount", + "memchr", + "nom", +] + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + +[[package]] +name = "strum" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" + +[[package]] +name = "strum_macros" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.53", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" +dependencies = [ + "proc-macro2", + "quote", + "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 2.0.53", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unique-type-id" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0462fbf31a136eb58d768f742ff34d9de41fe8b167aed5821f59163a9527a6b" +dependencies = [ + "unique-type-id-derive", +] + +[[package]] +name = "unique-type-id-derive" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "407e1420003f468eb3b9b7dafbed105a2add06b6a18e1291ea00ec81ee2de406" +dependencies = [ + "fs2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "whython-7" +version = "0.1.0" +dependencies = [ + "b-box", + "clap", + "color-print", + "either", + "itertools", + "lazy_static", + "nom", + "nom-supreme", + "nom_locate", + "same-file", + "strum", + "strum_macros", + "thiserror", + "unique-type-id", + "walkdir", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..68ae409 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "whython-7" +version = "0.1.0" +edition = "2021" + + +[dependencies] +clap = { version = "4.5.3", features = ["derive"] } +thiserror = "1.0.58" +itertools = "0.12.1" +strum = "0.26.2" +strum_macros = "0.26.2" +unique-type-id = "1.3.0" +either = "1.10.0" +lazy_static = "1.4.0" +b-box = "0.1.0" +same-file = "1.0.6" +walkdir = "2.5.0" +color-print = "0.3.5" +nom = "7.1.3" +nom_locate = "4.2.0" +nom-supreme = "0.8.0" + +[profile.release] +opt-level = 3 +codegen-units = 1 +lto = "fat" +panic = "abort" diff --git a/README.md b/README.md new file mode 100644 index 0000000..bcf247c --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# Whython-8 + +Requires `link.exe` (MSVC) and `nasm.exe` to be in `PATH`. `main.why` will be compiled +into the binary found in `out.exe`. + +When on Linux, `gcc` will be used for linking and `wine` for execution (both of which need to be in `PATH`). +`nasm.exe` is still required. + +Designed to work on Windows linking with `kernel32.lib` for system calls. + +The `master` branch should now contain a semi-stable version whereas +the latest version can be found on `dev`. I also use `dev` to sync +my work so expect constant breaking changes on that branch. \ No newline at end of file diff --git a/build/out.asm b/build/out.asm new file mode 100644 index 0000000..569b3e0 --- /dev/null +++ b/build/out.asm @@ -0,0 +1,222 @@ + global main + extern ExitProcess + extern GetStdHandle + extern WriteFile + extern HeapAlloc + extern HeapFree + extern GetProcessHeap + extern printf + section .text + +__2: ; printi + push rbp + mov rbp, rsp + sub rsp, 16 + ; [inline asm] + mov dword [rbp-4], 0x000a + mov dword [rbp-8], 0x646C6C25 + mov rcx, rbp + sub rcx, 8 + mov rdx, qword [rbp+16] + sub rsp, 40 + call printf + add rsp, 40 + leave + ret + +__3: ; printb + push rbp + mov rbp, rsp + sub rsp, 32 + ; [inline asm] + mov dword [rbp-8], 0x65757274 + mov dword [rbp-4], 0x0D0A + mov rax, qword [rbp+16] + cmp rax, 0 + jz ._3.true + mov dword [rbp-8], 0x736C6166 + mov dword [rbp-4], 0x0D0A65 + ._3.true: + mov rcx, rbp + sub rcx, 8 + mov rdx, qword [rbp+16] + sub rsp, 40 + call printf + add rsp, 40 + leave + ret + +__37: ; printf + push rbp + mov rbp, rsp + sub rsp, 16 + ; [inline asm] + mov dword [rbp-4], 0x00 + mov dword [rbp-8], 0x0a664C25 + mov rcx, rbp + sub rcx, 8 + movsd xmm1, qword [rbp+16] + mov rdx, qword [rbp+16] + sub rsp, 40 + call printf + add rsp, 40 + leave + ret + +main: ; main + push rbp + mov rbp, rsp + sub rsp, 160 + ; ' printb(true);' + ; [inline asm] + mov qword [rbp-8], 0 + ; [no return call] -3 , [(-8, 8)] + sub rsp, 8 + mov rax, qword [rbp-8] + mov qword [rbp-168], rax + call __3 + add rsp, 8 + ; ' printb(true);' + ; [inline asm] + mov qword [rbp-16], 0 + ; [no return call] -3 , [(-16, 8)] + sub rsp, 8 + mov rax, qword [rbp-16] + mov qword [rbp-168], rax + call __3 + add rsp, 8 + ; ' printb(false);' + ; [inline asm] + mov qword [rbp-24], 1 + ; [no return call] -3 , [(-24, 8)] + sub rsp, 8 + mov rax, qword [rbp-24] + mov qword [rbp-168], rax + call __3 + add rsp, 8 + ; ' printb(false);' + ; [inline asm] + mov qword [rbp-32], 1 + ; [no return call] -3 , [(-32, 8)] + sub rsp, 8 + mov rax, qword [rbp-32] + mov qword [rbp-168], rax + call __3 + add rsp, 8 + ; ' printi(9223372036854775808);' + ; [inline asm] + mov dword [rbp-40], 0x00000000 + mov dword [rbp-36], 0x80000000 + ; [no return call] -2 , [(-40, 8)] + sub rsp, 8 + mov rax, qword [rbp-40] + mov qword [rbp-168], rax + call __2 + add rsp, 8 + ; ' printi(-9223372036854775807);' + ; [inline asm] + mov dword [rbp-48], 0xffffffff + mov dword [rbp-44], 0x7fffffff + ; [inline asm] + mov dword [rbp-56], 0x00000000 + mov dword [rbp-52], 0x00000000 + ; [inline asm] + ; [inline asm] + mov rax, qword [rbp-56] + sub rax, [rbp-48] + mov [rbp-64], rax + ; [no return call] -2 , [(-64, 8)] + sub rsp, 8 + mov rax, qword [rbp-64] + mov qword [rbp-168], rax + call __2 + add rsp, 8 + ; ' printf(1.12351234123);' + ; [inline asm] + mov rax, __float64__(1.12351234123) + mov qword [rbp-72], rax + ; [no return call] -37 , [(-72, 8)] + sub rsp, 8 + mov rax, qword [rbp-72] + mov qword [rbp-168], rax + call __37 + add rsp, 8 + ; ' printf(1.12351234124);' + ; [inline asm] + mov rax, __float64__(1.12351234124) + mov qword [rbp-80], rax + ; [no return call] -37 , [(-80, 8)] + sub rsp, 8 + mov rax, qword [rbp-80] + mov qword [rbp-168], rax + call __37 + add rsp, 8 + ; ' printf(-1.12351234123);' + ; [inline asm] + mov rax, __float64__(1.12351234123) + mov qword [rbp-88], rax + ; [inline asm] + mov rax, __float64__(0.0) + mov qword [rbp-96], rax + ; [inline asm] + ; [inline asm] + movsd xmm0, qword [rbp-96] + subsd xmm0, qword [rbp-88] + movsd qword [rbp-104], xmm0 + ; [no return call] -37 , [(-104, 8)] + sub rsp, 8 + mov rax, qword [rbp-104] + mov qword [rbp-168], rax + call __37 + add rsp, 8 + ; '' + ; ' printb(1.12351234123 == 1.12351234124);' + ; [inline asm] + mov rax, __float64__(1.12351234123) + mov qword [rbp-112], rax + ; [inline asm] + mov rax, __float64__(1.12351234124) + mov qword [rbp-120], rax + ; [inline asm] + ; [inline asm] + movsd xmm0, qword [rbp-112] + ucomisd xmm0, qword [rbp-120] + mov qword [rbp-128], 0 + setne [rbp-128] + ; [no return call] -3 , [(-128, 8)] + sub rsp, 8 + mov rax, qword [rbp-128] + mov qword [rbp-168], rax + call __3 + add rsp, 8 + ; ' printb(1.12351234123 == 1.12351234123);' + ; [inline asm] + mov rax, __float64__(1.12351234123) + mov qword [rbp-136], rax + ; [inline asm] + mov rax, __float64__(1.12351234123) + mov qword [rbp-144], rax + ; [inline asm] + ; [inline asm] + movsd xmm0, qword [rbp-136] + ucomisd xmm0, qword [rbp-144] + mov qword [rbp-152], 0 + setne [rbp-152] + ; [no return call] -3 , [(-152, 8)] + sub rsp, 8 + mov rax, qword [rbp-152] + mov qword [rbp-168], rax + call __3 + add rsp, 8 + ; '' + ; '' + ; ' return 7;' + ; [inline asm] + mov dword [rbp-160], 0x00000007 + mov dword [rbp-156], 0x00000000 + ; [return] Some((-160, 8)) + mov rcx, qword [rbp-160] + call ExitProcess + +formatStr: + db `The int is %d\n`,0 \ No newline at end of file diff --git a/build/out.exe b/build/out.exe new file mode 100755 index 0000000..97a2203 Binary files /dev/null and b/build/out.exe differ diff --git a/build/out.obj b/build/out.obj new file mode 100644 index 0000000..5d2cb3c Binary files /dev/null and b/build/out.obj differ diff --git a/hex_formatter.py b/hex_formatter.py new file mode 100644 index 0000000..d3b2e6f --- /dev/null +++ b/hex_formatter.py @@ -0,0 +1,11 @@ +from ast import literal_eval +from itertools import batched + +i = input("Enter string: ") +string = literal_eval("'" + i + "'") +hex = ["".join(x) for x in batched(string.encode('ascii').hex().upper(), 2)] +hex_groups = batched(hex, 4) + +print("In groups of reversed 8s:") +for group in hex_groups: + print("0x" + "".join(reversed(group))) diff --git a/libs/kernel32.lib b/libs/kernel32.lib new file mode 100644 index 0000000..a5db285 Binary files /dev/null and b/libs/kernel32.lib differ diff --git a/libs/legacy_stdio_definitions.lib b/libs/legacy_stdio_definitions.lib new file mode 100644 index 0000000..adbd07b Binary files /dev/null and b/libs/legacy_stdio_definitions.lib differ diff --git a/libs/legacy_stdio_wide_specifiers.lib b/libs/legacy_stdio_wide_specifiers.lib new file mode 100644 index 0000000..0de905f Binary files /dev/null and b/libs/legacy_stdio_wide_specifiers.lib differ diff --git a/libs/msvcrt.lib b/libs/msvcrt.lib new file mode 100644 index 0000000..5f58764 Binary files /dev/null and b/libs/msvcrt.lib differ diff --git a/libs/ucrt.lib b/libs/ucrt.lib new file mode 100644 index 0000000..fd88b49 Binary files /dev/null and b/libs/ucrt.lib differ diff --git a/libs/vcruntime.lib b/libs/vcruntime.lib new file mode 100644 index 0000000..2c2ba22 Binary files /dev/null and b/libs/vcruntime.lib differ diff --git a/main.why b/main.why new file mode 100644 index 0000000..5c08917 --- /dev/null +++ b/main.why @@ -0,0 +1,17 @@ +fn main() ~ int { + printb(true); + printb(true); + printb(false); + printb(false); + printi(9223372036854775808); + printi(-9223372036854775807); + printf(1.12351234123); + printf(1.12351234124); + printf(-1.12351234123); + + printb(1.12351234123 == 1.12351234124); + printb(1.12351234123 == 1.12351234123); + + + return 7; +} \ No newline at end of file diff --git a/parse_test.why b/parse_test.why new file mode 100644 index 0000000..f768d4a --- /dev/null +++ b/parse_test.why @@ -0,0 +1,4 @@ +struct A { + a: A::B.C +} + diff --git a/reference/currently working b/reference/currently working new file mode 100644 index 0000000..3f74e6a --- /dev/null +++ b/reference/currently working @@ -0,0 +1,22 @@ +Working +- struct definition +- functions +- static struct functions +- non-static struct functions +- return +- while +- break +- if/elif/else +- int type w/ most operators +- bool type w/ some operator +- variable initialisation +- variable assignments +- recursion +- showing + +Silent Fail +- None (yet :D) + +Error +- instantiating custom structs +- comments (for the weak) \ No newline at end of file diff --git a/reference/e.why b/reference/e.why new file mode 100644 index 0000000..b8605f5 --- /dev/null +++ b/reference/e.why @@ -0,0 +1,31 @@ +fn fact(x: float) ~ float { + if (x == 0.0) { + return 1.0; + }; + + let f: float = 1.0; + let i: float = 1.0; + + while (i < (x + 0.99)) { + f *= i; + i += 1.0; + }; + + return f; +} + +fn main() ~ int { + let e: float = 0.0; + + let i: float = 0.0; + while (i < 10.0) { + e += 1.0 / fact(i); + printf(e); + i += 1.0; + }; + + printf(e); + + return 7; +} + diff --git a/reference/example 2.why b/reference/example 2.why new file mode 100644 index 0000000..2bf7c16 --- /dev/null +++ b/reference/example 2.why @@ -0,0 +1,34 @@ +struct Fib { + a: int, + b: int, + n: int +} + +impl Fib { + fn start(self) { + if ((*self.n) == 0) { + printi(*self.a); + } + else { + let temp: int = (*self.a) + (*self.b); + *self.a = *self.b; + *self.b = temp; + *self.n -= 1; + self.start(); + }; + } +} + + +fn main() ~ int { + let i: int = 1; + + while (i < 10) { + let f: Fib = @ Fib { 0, 1, i }; + f.start(); + i += 1; + }; + + return 0; +} + diff --git a/reference/example.why b/reference/example.why new file mode 100644 index 0000000..3fe316f --- /dev/null +++ b/reference/example.why @@ -0,0 +1,31 @@ +mod test.why; + +fn main() ~ int { + printi(true + false); + let a: bool = true; + printi(a.add(false)); + printi(bool#add(true, false)); + + extern(); + + let i: int = 0; + while (i < 90) { + printi(fibb(0, 1, i)); + i += 1; + }; + return fibb(0, 1, 20); +} + +fn fibb(a: int, b: int, n: int) ~ int { + if (n <= 0) { return a; }; + return fibb(b, a + b, n - 1); +} + +impl bool { + fn add(lhs: bool, rhs: bool) ~ int { + let out: int = 0; + if (lhs) { out += 1; }; + if (rhs) { out += 1; }; + return out; + } +} \ No newline at end of file diff --git a/reference/linked_list_example.why b/reference/linked_list_example.why new file mode 100644 index 0000000..27c7c55 --- /dev/null +++ b/reference/linked_list_example.why @@ -0,0 +1,75 @@ +struct Node { + cur: int, + last: bool, + next: $Node +} + +struct LL { + base: $Node, + has_first: bool +} + +impl LL { + fn new() ~ LL { + return @LL { + 0, + false + }; + } + + fn add(self, next: int) { + let new_node: Node = @Node { + next, + true, + 0 + }; + + if (!(*self.has_first)) { + *self.base = ^new_node; + *self.has_first = true; + return; + }; + + let curr: $Node = *self.base; + while (!(*curr.last)) { + curr = *curr.next; + }; + + *curr.last = false; + *curr.next = ^new_node; + } + + fn print(self) { + if (!(*self.has_first)) { + return; + }; + + let curr: $Node = *self.base; + printi(*curr.cur); + while (!(*curr.last)) { + curr = *curr.next; + printi(*curr.cur); + }; + } +} + +fn test_two() { + let ll: LL = LL#new(); + + ll.add(12); + + ll.add(18); + + ll.print(); + printi(123123); +} + +fn test() { + test_two(); +} + +fn main() ~ int { + test(); + + return 7; +} \ No newline at end of file diff --git a/reference/main - c.why b/reference/main - c.why new file mode 100644 index 0000000..e046ff9 --- /dev/null +++ b/reference/main - c.why @@ -0,0 +1,81 @@ +struct Node { + cur: int, + last: bool, + next: $Node +} + +struct LL { + base: $Node, + has_first: bool +} + +impl LL { + fn new() ~ LL { + return @LL { + 0, + false + }; + } + + fn add(self, next: int) { + printb(true); + + let new_node: Node = @Node { + next, + true, + 0 + }; + + printb(false); + + if (!(*self.has_first)) { + printb(false); + + self.base = ^new_node; + return; + }; + + printb(true); + + let curr: $Node = self.base; + while (!(*curr.last)) { + curr = curr.next; + }; + + *curr.last = false; + curr.next = ^new_node; + } + + fn print(self) { + if (!(*self.has_first)) { + return; + }; + + let curr: $Node = self.base; + printi(*curr.cur); + while (!(*curr.last)) { + curr = curr.next; + printi(*curr.cur); + }; + } +} + +fn main() ~ int { + let ll: LL = LL#new(); + + printb(true); + + ll.add(12); + + printb(false); + + ll.add(18); + + printb(true); + + ll.print(); + + printb(false); + + return 7; +} \ No newline at end of file diff --git a/reference/out3.asm b/reference/out3.asm new file mode 100644 index 0000000..06d31cf --- /dev/null +++ b/reference/out3.asm @@ -0,0 +1,51 @@ + global main + extern ExitProcess + extern GetStdHandle + extern WriteFile + extern WriteConsoleA + extern WriteConsoleW + section .text + +__14: + push rbp + mov rbp, rsp + sub rsp, 64 + + mov qword [rbp-8], 0D0Ah + mov qword [rbp-16], "true" + mov rax, [rbp+16] + cmp rax, 0 + jz ._14.true + mov qword [rbp-8], `0D0A0065h` + mov qword [rbp-16], "fals" + ._14.true: + + mov ecx, -11 + call GetStdHandle + + ; You have to reserve space for these despite not being on the stack! + mov rcx, rax ; STD Handle + mov rdx, rbp ; Data pointer + sub rdx, 16 ; cont. + mov r8, 16 ; Bytes to write + mov qword [rbp - 24], 0 ; optional out bytes written + mov r9, rbp + sub r9, 24 ; contd. + mov qword [rbp - 32], 0 ; optional lpOverlapped + call WriteFile + leave + ret + +main: + push rbp + mov rbp, rsp + sub rsp, 16 + mov qword [rbp-8], 1 + mov rax, qword [rbp-8] + push qword 0 + push rax + call __14 + add rsp, 16 + mov qword [rbp-16], 1 + mov rcx, [rbp-16] + call ExitProcess \ No newline at end of file diff --git a/reference/out4.asm b/reference/out4.asm new file mode 100644 index 0000000..5a5b1b7 --- /dev/null +++ b/reference/out4.asm @@ -0,0 +1,70 @@ + global main + extern ExitProcess + extern GetStdHandle + extern WriteFile + extern WriteConsoleA + extern WriteConsoleW + section .text + +__4: + push rbp + mov rbp, rsp + sub rsp, 80 + + mov rcx, rbp + mov rax, qword [rbp+16] + mov qword [rbp-24], "" + mov qword [rbp-16], "" + mov qword [rbp-8], "" + cmp rax, 0 + jg ._test + mov dword [rbp-20], "-" + mov r8, rax + mov rax, 0 + sub rax, r8 + ._test + mov rbx, 10 + mov r8, rbp + sub r8, 24 + ._4.loop: + xor rdx, rdx + div rbx + dec rcx + add rdx, '0' + mov [rcx], dl + cmp rcx, r8 + jz ._4.loop + test rax, rax + jnz ._4.loop + + mov ecx, -11 + call GetStdHandle + + ; You have to reserve space for these despite not being on the stack! + mov rcx, rax ; STD Handle + mov rdx, rbp ; Data pointer + sub rdx, 24 ; cont. + mov r8, 24 ; Bytes to write + mov qword [rbp - 40], 0 ; optional out bytes written + mov r9, rbp + sub r9, 24 ; contd. + mov qword [rbp - 48], 0 ; optional lpOverlapped + call WriteFile + + leave + ret + +main: + push rbp + mov rbp, rsp + sub rsp, 16 + mov rax, qword -18446744073709515615 + mov qword [rbp-8], rax + push qword 0 + mov rax, qword [rbp-8] + push rax + call __4 + add rsp, 8 + mov qword [rbp-16], 1 + mov rcx, [rbp-8] + call ExitProcess diff --git a/reference/print.asm b/reference/print.asm new file mode 100644 index 0000000..ec94494 --- /dev/null +++ b/reference/print.asm @@ -0,0 +1,29 @@ + +; set up the .text segment for the code +section .text + +; global main is the entry point +global main +; note that there is no _ before printf here, unlike in OS X +extern printf + +main: + mov rcx, qword textformat + movq xmm0, qword [pi] + mov rax, qword 1 ; need to tell printf how many floats + call printf + + ; note next step - this puts a zero in rax + xor rax, rax + ret ; this returns to the OS based on how Windows calls programs. + ; this return causes a delay then the program exits. + +textformat: + db "hello, %lf!",0x0a, 0x00 ; friendly greeting + + +; data segment +section .data + +pi dq __float64__(3.14159) + diff --git a/reference/print2.asm b/reference/print2.asm new file mode 100644 index 0000000..cfe2e0c --- /dev/null +++ b/reference/print2.asm @@ -0,0 +1,9 @@ +mov dword [rbp-44], 0x00 + mov dword [rbp-48], 0x0a666C25 + mov rcx, rbp + sub rcx, 48 + movq xmm1, qword [rbp-32] + movq rdx, xmm1 ; duplicate into the integer register + sub rsp, 40 ; allocate shadow space and alignment (32+8) + call printf + add rsp, 40 ; restore stack \ No newline at end of file diff --git a/reference/reference implementation.txt b/reference/reference implementation.txt new file mode 100644 index 0000000..7a8e2b3 --- /dev/null +++ b/reference/reference implementation.txt @@ -0,0 +1,10 @@ + - Have refs as special cases for types with same underlying type - how + - Cast from **type to *type follows ref + - Cast from *type to type copies + - Impls accept *self + - Impl non-static functions only accessible through *type + - *type.attrib accessed through fixed offset from r9 i.e. mov r9, qword [rbp - *type addr]; mov rax, qword [r9 + x] + +Implementing special case: + - Have first part of names (and types) store indirection amount + diff --git a/reference/test.asm b/reference/test.asm new file mode 100644 index 0000000..fbf0873 --- /dev/null +++ b/reference/test.asm @@ -0,0 +1,34 @@ + global _main + extern _GetStdHandle@4 + extern _WriteFile@20 + extern _ExitProcess@4 + + section .text +_main: + ; DWORD bytes; + mov ebp, esp + sub esp, 4 + + ; hStdOut = GetstdHandle( STD_OUTPUT_HANDLE) + push -11 + call _GetStdHandle@4 + mov ebx, eax + + ; WriteFile( hstdOut, message, length(message), &bytes, 0); + push 0 + lea eax, [ebp-4] + push eax + push (message_end - message) + push message + push ebx + call _WriteFile@20 + + ; ExitProcess(0) + push 0 + call _ExitProcess@4 + + ; never here + hlt +message: + db 'Hello, World', 10 +message_end: \ No newline at end of file diff --git a/reference/test.why b/reference/test.why new file mode 100644 index 0000000..5bb873a --- /dev/null +++ b/reference/test.why @@ -0,0 +1,3 @@ +fn extern() { + printi(101); +} \ No newline at end of file diff --git a/reference/test2.asm b/reference/test2.asm new file mode 100644 index 0000000..f98e9c1 --- /dev/null +++ b/reference/test2.asm @@ -0,0 +1,27 @@ +global main +extern GetStdHandle +extern WriteFile + +section .text +main: + sub rsp, 40 ; reserve shadow spaceand align the stack by 16 + mov ecx, -11 ; GetStdHandle takes a DWORD arg, write it as 32-bit. This is STD_OUTPUT_HANDLE + call GetStdHandle + + mov rcx, rax + mov rdx, NtlpBuffer ; or better, lea rdx, [rel NtlpBuffer] + mov r8, [NtnNBytesToWrite] ; or better, make it an EQU constant for mov r8d, bytes_to_write + mov r9, NtlpNBytesWritten ; first 4 args in regs + mov qword [rsp + 32], 00h ; fifth arg on the stack above the shadow space. Also, this is a pointer so it needs to be a qword store. + call WriteFile + add rsp, 40 +ExitProgram: + xor eax, eax + ret + +section .data +NtlpBuffer: db 'Hello, World!', 00h +NtnNBytesToWrite: dq 0eh + +section .bss +NtlpNBytesWritten: resd 01h \ No newline at end of file diff --git a/reference/test2.exe b/reference/test2.exe new file mode 100644 index 0000000..00843a4 Binary files /dev/null and b/reference/test2.exe differ diff --git a/reference/test2.obj b/reference/test2.obj new file mode 100644 index 0000000..7fc30bd Binary files /dev/null and b/reference/test2.obj differ diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..dec16f3 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1 @@ +pub mod root; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..ea21d62 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,5 @@ +mod root; + +fn main() { + root::main(); +} diff --git a/src/root.rs b/src/root.rs new file mode 100644 index 0000000..9343927 --- /dev/null +++ b/src/root.rs @@ -0,0 +1,142 @@ +use std::fs; +use std::io::ErrorKind; +use crate::root::assembler::assemble::generate_assembly; +use crate::root::parser::parse::parse; +use crate::root::name_resolver::processor::process; +use crate::time; +use clap::Parser; +use std::path::PathBuf; +use color_print::cprintln; +use runner::assemble; +use crate::root::utils::AnyError; + +#[cfg(target_os = "windows")] +use runner::link; +#[cfg(target_os = "windows")] +use crate::root::runner::run; + +#[cfg(target_os = "linux")] +use runner::link_gcc_experimental; +#[cfg(target_os = "linux")] +use crate::root::runner::run_wine_experimental; + +mod assembler; +mod ast; +mod basic_ast; +mod compiler; +mod custom; +mod parser; +mod nom_parser; +mod name_resolver; +mod utils; +mod runner; + +/// Compiler for Whython files (.why) +#[derive(Parser)] +#[command(version, about, long_about = None)] +pub struct Args { + /// Main input file + #[arg(short, long, default_value = "main.why")] + pub input: String, + /// Output files name without extension + /// Main input file + #[arg(short, long, default_value = "build/out")] + pub output: String, + /// Only build - don't run + #[arg(short, long)] + pub build: bool, +} + +pub fn main() { + nom_parser::parse::parse(PathBuf::from("parse_test.why")).ok(); + return; + + // assemble("build/out").unwrap(); + // link_gcc_experimental("build/out").unwrap(); + // run_wine_experimental("build/out").unwrap(); + // return; + + let args = Args::parse(); + let _ = main_args(args); +} + +pub fn main_args(args: Args) -> Result<(), AnyError> { + if let Some(path) = PathBuf::from(&args.output).parent() { + if let Err(e) = fs::create_dir_all(path) { + if !matches!(e.kind(), ErrorKind::AlreadyExists) { + cprintln!("Failed to create directories for output files"); + return Err(AnyError::Other); + } + } + } + + let mut asts = Vec::new(); + let mut files_followed = Vec::new(); + print!("Parsing... "); + time!( + if let Err(e) = parse(PathBuf::from(&args.input), &mut asts, &mut files_followed) { + cprintln!("\n{}", e); + return Err(e.into()); + } + ); + + print!("Processing... "); + time!( + let functions = match process(asts) { + Err(e) => { + cprintln!("\n{}", e); + return Err(e.into()); + } + Ok(functions) => functions + }; + ); + + print!("Compiling... "); + time!(generate_assembly(&args.output, functions);); + + print!("Assembling (NASM)... "); + time!( + if assemble(&args.output).is_err() { + return Err(AnyError::Other); + } + ); + + #[cfg(target_os = "windows")] + { + println!("Linking (MSVC - link.exe)... "); + time!( + if link(&args.output).is_err() { + return Err(AnyError::Other); + } + ); + if args.build { + println!("Skipping execution") + } else { + println!("Executing... "); + run(&args.output); + } + } + #[cfg(target_os = "linux")] + { + cprintln!("Compilation and execution on Linux may be buggy!"); + println!("Linking (gcc)... "); + time!( + let res = link_gcc_experimental(&args.output); + if res.is_err() { + return Err(AnyError::Other); + } + ); + + if args.build { + println!("Skipping execution") + } else { + println!("Executing (wine)... "); + if run_wine_experimental(&args.output).is_err() { + return Err(AnyError::Other); + } + } + } + + cprintln!("Done!"); + Ok(()) +} diff --git a/src/root/assembler/assemble.rs b/src/root/assembler/assemble.rs new file mode 100644 index 0000000..1047bdc --- /dev/null +++ b/src/root/assembler/assemble.rs @@ -0,0 +1,25 @@ +use crate::root::compiler::compile_functions::Function; +use std::fs; + +pub fn generate_assembly(output: &str, functions: Vec>) { + let mut out = String::from( + " global main + extern ExitProcess + extern GetStdHandle + extern WriteFile + extern HeapAlloc + extern HeapFree + extern GetProcessHeap + extern printf + section .text\n", + ); + for f in functions { + out += "\r\n"; + out += &(f.get_asm()); + } + + out += "\nformatStr: + db `The int is %d\\n`,0"; + + fs::write(format!("{output}.asm"), out).expect("Failed to write assembly to file"); +} diff --git a/src/root/assembler/mod.rs b/src/root/assembler/mod.rs new file mode 100644 index 0000000..3c372be --- /dev/null +++ b/src/root/assembler/mod.rs @@ -0,0 +1 @@ +pub mod assemble; diff --git a/src/root/ast/builtin.rs b/src/root/ast/builtin.rs new file mode 100644 index 0000000..d7c5ded --- /dev/null +++ b/src/root/ast/builtin.rs @@ -0,0 +1,2 @@ +#[derive(PartialEq, Clone, strum_macros::Display, Debug)] +pub enum Builtin {} diff --git a/src/root/ast/keywords.rs b/src/root/ast/keywords.rs new file mode 100644 index 0000000..5cc5828 --- /dev/null +++ b/src/root/ast/keywords.rs @@ -0,0 +1,37 @@ +#[derive(PartialEq, Copy, Clone, strum_macros::Display, Debug, strum_macros::EnumIter)] +pub enum Keyword { + Mod, + Struct, + Impl, + Fn, + If, + Elif, + Else, + While, + Break, + Continue, + Let, + Return, +} + +pub const MOD_KEYWORD: &str = "mod"; + +impl Keyword { + pub fn get_enum(name: &str) -> Option { + match name { + MOD_KEYWORD => Some(Keyword::Mod), + "struct" => Some(Keyword::Struct), + "impl" => Some(Keyword::Impl), + "fn" => Some(Keyword::Fn), + "if" => Some(Keyword::If), + "elif" => Some(Keyword::Elif), + "else" => Some(Keyword::Else), + "while" => Some(Keyword::While), + "break" => Some(Keyword::Break), + "continue" => Some(Keyword::Continue), + "let" => Some(Keyword::Let), + "return" => Some(Keyword::Return), + _ => None, + } + } +} diff --git a/src/root/ast/literals.rs b/src/root/ast/literals.rs new file mode 100644 index 0000000..fa113c5 --- /dev/null +++ b/src/root/ast/literals.rs @@ -0,0 +1,38 @@ +use crate::root::basic_ast::symbol::BasicSymbol; +use crate::root::compiler::local_variable::TypeInfo; +use crate::root::custom::types::bool::Bool; +use crate::root::custom::types::float::Float; +use crate::root::custom::types::int::Int; +use crate::root::parser::line_info::LineInfo; +use crate::root::name_resolver::processor::ProcessorError; +use crate::root::name_resolver::type_builder::TypeTable; + +#[derive(Clone, strum_macros::Display, Debug)] +#[allow(dead_code)] +pub enum Literal { + String(String), + Char(char), + Int(i128), + Bool(bool), + Float(f64), + Initialiser(String, Vec>), + Null, + None, +} + +impl Literal { + pub fn get_type_id(&self, type_table: &TypeTable, line_info: &LineInfo) -> Result { + Ok(match &self { + Literal::Int(_) => TypeInfo::new(Int::get_id(), 0), + Literal::Bool(_) => TypeInfo::new(Bool::get_id(), 0), + Literal::Float(_) => TypeInfo::new(Float::get_id(), 0), + Literal::Null => TypeInfo::new(-1, 1), + Literal::Initialiser(name, _) => TypeInfo::new( + type_table.get_id_by_name(name).ok_or_else(|| + ProcessorError::TypeNotFound(line_info.clone(), name.clone()) + )?, + 0), + _ => todo!(), + }) + } +} diff --git a/src/root/ast/mod.rs b/src/root/ast/mod.rs new file mode 100644 index 0000000..0cdef6c --- /dev/null +++ b/src/root/ast/mod.rs @@ -0,0 +1,5 @@ +pub mod builtin; +pub mod keywords; +pub mod literals; +pub mod operators; +mod symbol; diff --git a/src/root/ast/operators.rs b/src/root/ast/operators.rs new file mode 100644 index 0000000..572b8f4 --- /dev/null +++ b/src/root/ast/operators.rs @@ -0,0 +1,47 @@ +#[derive(PartialEq, Clone, strum_macros::Display, Debug)] +pub enum Operator { + Add, + Subtract, + Product, + Divide, + Greater, + Less, + GreaterEqual, + LessEqual, + Equal, + NotEqual, + Modulo, + Or, + And, + Not, + HeapAlloc, + HeapDealloc, +} + +pub const ALL_SYMBOLS: [char; 13] = [ + '+', '-', '*', '/', '>', '<', '=', '|', '&', '!', '%', '^', '¬', +]; + +impl Operator { + pub fn get_operator(string: &str) -> Option { + match string { + "+" => Some(Operator::Add), + "-" => Some(Operator::Subtract), + "*" => Some(Operator::Product), + "/" => Some(Operator::Divide), + ">" => Some(Operator::Greater), + "<" => Some(Operator::Less), + ">=" => Some(Operator::GreaterEqual), + "<=" => Some(Operator::LessEqual), + "==" => Some(Operator::Equal), + "!=" => Some(Operator::NotEqual), + "|" => Some(Operator::Or), + "&" => Some(Operator::And), + "!" => Some(Operator::Not), + "%" => Some(Operator::Modulo), + "^" => Some(Operator::HeapAlloc), + "¬" => Some(Operator::HeapDealloc), + _ => None, + } + } +} diff --git a/src/root/ast/symbol.rs b/src/root/ast/symbol.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/root/ast/symbol.rs @@ -0,0 +1 @@ + diff --git a/src/root/basic_ast/mod.rs b/src/root/basic_ast/mod.rs new file mode 100644 index 0000000..7e10413 --- /dev/null +++ b/src/root/basic_ast/mod.rs @@ -0,0 +1,2 @@ +pub mod punctuation; +pub mod symbol; diff --git a/src/root/basic_ast/punctuation.rs b/src/root/basic_ast/punctuation.rs new file mode 100644 index 0000000..71b2ab4 --- /dev/null +++ b/src/root/basic_ast/punctuation.rs @@ -0,0 +1,7 @@ +#[derive(PartialEq, Clone, strum_macros::Display, Debug)] +pub enum Punctuation { + ListSeparator, + Colon, + Semicolon, + Tilda, +} diff --git a/src/root/basic_ast/symbol.rs b/src/root/basic_ast/symbol.rs new file mode 100644 index 0000000..85e6402 --- /dev/null +++ b/src/root/basic_ast/symbol.rs @@ -0,0 +1,70 @@ +use crate::root::ast::keywords::Keyword; +use crate::root::ast::literals::Literal; +use crate::root::ast::operators::Operator; +use crate::root::basic_ast::punctuation::Punctuation; +use crate::root::parser::line_info::LineInfo; + +pub type BasicAbstractSyntaxTree = Vec<(BasicSymbol, LineInfo)>; + +#[derive(PartialEq, Clone, strum_macros::Display, Debug)] +pub enum NameAccessType { + Base, + Static, + Normal, +} + +#[derive(Clone, strum_macros::Display, Debug)] +pub enum NameType { + Normal, + Function(Vec>), +} + +#[derive(Clone, strum_macros::Display, Debug)] +pub enum BasicSymbol { + AbstractSyntaxTree(Vec<(BasicSymbol, LineInfo)>), + Literal(Literal), + Operator(Operator), + Assigner(Option), + BracedSection(Vec<(BasicSymbol, LineInfo)>), + BracketedSection(Vec<(BasicSymbol, LineInfo)>), + SquareBracketedSection(Vec<(BasicSymbol, LineInfo)>), + Punctuation(Punctuation), + Keyword(Keyword), + Name(Vec<(String, NameAccessType, NameType, usize)>), +} + +pub const NAME_VALID_CHARS: [char; 63] = [ + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', + 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_', '1', '2', '3', '4', + '5', '6', '7', '8', '9', '0', +]; + +impl BasicSymbol { + pub fn get_name_contents(&self) -> &Vec<(String, NameAccessType, NameType, usize)> { + match self { + BasicSymbol::Name(inside) => inside, + _ => panic!(), + } + } +} + +impl BasicSymbol { + #[allow(dead_code)] + pub fn instead_found(&self) -> String { + match &self { + BasicSymbol::AbstractSyntaxTree(_) => panic!(), + BasicSymbol::Literal(_literal) => "Literal (or initialiser)".to_string(), + BasicSymbol::Operator(_) => "Operator".to_string(), + BasicSymbol::Assigner(_) => "Assigner".to_string(), + BasicSymbol::BracedSection(_) => "BracedSection".to_string(), + BasicSymbol::BracketedSection(_) => "BracketedSection".to_string(), + BasicSymbol::SquareBracketedSection(_) => "SquareBracketedSection".to_string(), + BasicSymbol::Punctuation(punctuation) => { + format!("{punctuation}") + } + BasicSymbol::Name(_) => "Name".to_string(), + BasicSymbol::Keyword(_) => "Keyword".to_string(), + } + } +} diff --git a/src/root/compiler/compile_functions.rs b/src/root/compiler/compile_functions.rs new file mode 100644 index 0000000..b9fa9d7 --- /dev/null +++ b/src/root/compiler/compile_functions.rs @@ -0,0 +1,164 @@ +mod assignment; +mod call_function; +mod evaluate; +mod evaluate_symbol; +mod instantiate_literal; +mod name_handler; +mod operators; +mod process_lines; +mod reference; + +use crate::root::basic_ast::symbol::BasicSymbol; +use crate::root::custom::get::{ + get_custom_function_implementations, get_custom_function_signatures, +}; +use crate::root::compiler::generate_asm::compile_user_function; +use crate::root::parser::line_info::LineInfo; +use crate::root::name_resolver::processor::ProcessorError; +use crate::root::name_resolver::type_builder::{TypeTable, TypedFunction}; +use name_handler::NameHandler; +use std::collections::HashMap; +use crate::root::compiler::local_variable::TypeInfo; + +pub enum Line { + ReturnCall(isize, isize, Vec<(isize, usize)>, usize, isize), + NoReturnCall(isize, isize, Vec<(isize, usize)>, usize), + Copy(isize, isize, usize), + DynFromCopy(isize, isize, usize), + DynToCopy(isize, isize, usize), + Return(Option<(isize, usize)>), + HeapAlloc(usize, isize), + HeapDealloc(isize, isize), + InlineAsm(Vec), + #[cfg(debug_assertions)] + Annotation(String), +} + +pub struct UserFunction { + pub id: isize, + pub name: String, + pub local_variable_size: usize, + pub arg_count: usize, + pub lines: Vec, +} + +impl Function for UserFunction { + fn get_asm(&self) -> String { + compile_user_function(self) + } + + fn get_id(&self) -> isize { + self.id + } +} + +pub trait Function { + fn get_asm(&self) -> String; + fn get_id(&self) -> isize; +} + +pub struct FunctionHolder { + functions: HashMap>, + functions_table: HashMap, HashMap>, +} + +impl FunctionHolder { + pub fn new( + functions: HashMap>, + functions_table: HashMap, HashMap>, + ) -> FunctionHolder { + FunctionHolder { + functions, + functions_table, + } + } + + pub fn get_function( + &self, + _type: Option, + name: &str, + ) -> Option<&Box> { + self.functions_table + .get(&_type.map(|x| x.type_id)) + .and_then(|x| x.get(name).map(|x| self.functions.get(x).unwrap())) + } + + pub fn functions(&self) -> &HashMap> { + &self.functions + } + + pub fn functions_table(&self) -> &HashMap, HashMap> { + &self.functions_table + } +} + +pub fn compile_functions( + mut function_name_map: HashMap, HashMap>, + mut functions: HashMap>, + type_table: TypeTable, +) -> Result>, ProcessorError> { + let mut function_contents: HashMap> = HashMap::new(); + for (id, func) in &mut functions { + function_contents.insert(*id, func.take_contents()); + } + for (t, f) in get_custom_function_signatures() { + if function_name_map.get_mut(&t).is_none() { + function_name_map.insert(t, HashMap::new()); + } + function_name_map + .get_mut(&t) + .unwrap() + .insert(f.get_name().to_string(), f.get_id()); + functions.insert(f.get_id(), f); + } + + let function_holder = FunctionHolder::new(functions, function_name_map); + let mut name_handler = NameHandler::new(type_table); + let mut processed_functions = get_custom_function_implementations(); + name_handler.use_function_id(0); + + for (id, contents) in function_contents { + let function = function_holder.functions.get(&id).unwrap(); + let name = function.get_name().to_string(); + name_handler.reset(); + name_handler.set_args(function.get_args_positioned(name_handler.type_table())?); + let return_type = function.get_return_type(); + let mut lines = Vec::new(); + + let last_return = process_lines::process_lines( + &contents, + id, + return_type, + &mut lines, + &mut name_handler, + &function_holder, + None, + )?; + + // TODO: Don't call if there is a return on the last line + name_handler.destroy_local_variables(&mut lines)?; + + if return_type.is_some() && !last_return { + return Err(ProcessorError::NoReturnStatement(function.get_line())); + } + + processed_functions.push(Box::new(UserFunction { + id, + local_variable_size: name_handler.local_variable_space(), + arg_count: function_holder + .functions() + .get(&id) + .unwrap() + .get_args() + .len(), + lines, + name, + })); + } + + let processed_functions = processed_functions + .into_iter() + .filter(|f| name_handler.used_functions().contains(&f.get_id())) + .collect(); + Ok(processed_functions) +} diff --git a/src/root/compiler/compile_functions/assignment.rs b/src/root/compiler/compile_functions/assignment.rs new file mode 100644 index 0000000..88e0267 --- /dev/null +++ b/src/root/compiler/compile_functions/assignment.rs @@ -0,0 +1,91 @@ +use crate::root::basic_ast::symbol::BasicSymbol; +use crate::root::compiler::compile_functions::name_handler::NameHandler; +use crate::root::compiler::compile_functions::{evaluate, operators, FunctionHolder, Line}; +use crate::root::parser::line_info::LineInfo; +use crate::root::name_resolver::processor::ProcessorError; +use either::Left; +use crate::root::compiler::local_variable::{LocalVariable, TypeInfo}; + +pub fn process_assignment( + lines: &mut Vec, + name_handler: &mut NameHandler, + function_holder: &FunctionHolder, + line: &[(BasicSymbol, LineInfo)], + is_ref: bool, +) -> Result<(), ProcessorError> { + if line.len() < 2 { + panic!() + } + + match &line[1].0 { + BasicSymbol::Assigner(assigner) => { + let name = match &line[0].0 { + BasicSymbol::Name(name) => name, + _ => return Err(ProcessorError::NonNameAssignment(line[0].1.clone())), + }; + let Left(variable) = + name_handler.resolve_name(function_holder, name, &line[0].1, lines)? + else { + return Err(ProcessorError::AssignToNonVariable(line[0].1.clone())); + }; + + let (original_variable, variable) = if is_ref { + if variable.type_info.reference_depth == 0 { + return Err(ProcessorError::CantDerefNonRef(line[0].1.clone())); + } + + let new_type = TypeInfo::new(variable.type_info.type_id, variable.type_info.reference_depth - 1); + let non_ref = name_handler + .add_local_variable(None, new_type, lines) + .unwrap(); + lines.push(Line::DynFromCopy( + variable.offset, + non_ref, + name_handler.type_table().get_type_size(new_type)?, + )); + (Some(variable), LocalVariable::from_type_info(non_ref, new_type)) + } else { + (None, variable) + }; + + if line.len() < 3 { + return Err(ProcessorError::NoAssignmentRHS(line[1].1.clone())); + } + if let Some(assigner) = assigner { + let result = + evaluate::evaluate(&line[2..], lines, name_handler, function_holder, None)? + .ok_or(ProcessorError::DoesntEvaluate(line[2].1.clone()))?; + operators::evaluate_operation( + variable, + (assigner, &line[1].1), + Some(result), + lines, + name_handler, + function_holder, + Some(variable), + )?; + } else { + evaluate::evaluate( + &line[2..], + lines, + name_handler, + function_holder, + Some(variable), + )?; + } + + if is_ref { + lines.push(Line::DynToCopy( + variable.offset, + original_variable.unwrap().offset, + name_handler.type_table().get_type_size(variable.type_info)?, + )); + } + + Ok(()) + } + _ => { + panic!() + } + } +} diff --git a/src/root/compiler/compile_functions/call_function.rs b/src/root/compiler/compile_functions/call_function.rs new file mode 100644 index 0000000..0927933 --- /dev/null +++ b/src/root/compiler/compile_functions/call_function.rs @@ -0,0 +1,189 @@ +use crate::root::ast::operators::Operator; +use crate::root::basic_ast::symbol::BasicSymbol; +use crate::root::compiler::compile_functions::name_handler::NameHandler; +use crate::root::compiler::compile_functions::operators::evaluate_operation; +use crate::root::compiler::compile_functions::{evaluate, FunctionHolder, Line}; +use crate::root::compiler::local_variable::LocalVariable; +use crate::root::parser::line_info::LineInfo; +use crate::root::name_resolver::processor::ProcessorError; +use crate::root::name_resolver::type_builder::TypedFunction; + +pub fn call_function( + function: &Box, + start_line: &LineInfo, + default_arg: Option, + args: &Vec>, + lines: &mut Vec, + name_handler: &mut NameHandler, + function_holder: &FunctionHolder, + return_into: Option, +) -> Result, ProcessorError> { + name_handler.use_function(function); + let target_args = function.get_args(); + let mut args_len = args.len(); + if default_arg.is_some() { + args_len += 1; + } + + if args_len > target_args.len() { + return Err(ProcessorError::BadArgCount( + // TODO: Bad line location + args[target_args.len() - (args_len - args.len())][0] + .1 + .clone(), + target_args.len(), + args_len, + function.get_line(), + )); + } + if args_len < target_args.len() { + if args.is_empty() { + return Err(ProcessorError::BadArgCount( + // TODO: Bad line location + start_line.clone(), + target_args.len(), + args_len, + function.get_line(), + )); + } else { + return Err(ProcessorError::BadArgCount( + // TODO: Bad line location + args[args.len() - 1].last().unwrap().1.clone(), + target_args.len(), + args_len, + function.get_line(), + )); + } + } + + let mut call_args = Vec::new(); + if let Some(default_arg) = default_arg { + let default_arg = if default_arg.type_info.reference_depth == 0 && target_args[0].1.reference_depth == 1 { + // TODO: Bad operator line + let into = name_handler.add_local_variable(None, target_args[0].1, lines)?; + evaluate_operation( + default_arg, + (&Operator::And, start_line), + None, + lines, + name_handler, + function_holder, + Some(LocalVariable::from_type_info(into, target_args[0].1)), + )? + .unwrap() + } else { + default_arg + }; + if default_arg.type_info != target_args[0].1 { + panic!("Default arg doesn't match first target arg") + } + call_args.push(( + default_arg.offset, + name_handler.type_table().get_type_size(default_arg.type_info)?, + )); + } + for arg in args { + let evaluated = evaluate::evaluate(arg, lines, name_handler, function_holder, None)?; + // println!("{:?}", evaluated); + if evaluated.is_none() { + return Err(ProcessorError::DoesntEvaluate(arg[0].1.clone())); + } + let evaluated = evaluated.unwrap(); + if evaluated.type_info != target_args[call_args.len()].1 { + return Err(ProcessorError::BadArgType( + arg[0].1.clone(), + name_handler + .type_table() + .get_type(target_args[call_args.len()].1.type_id) + .unwrap() + .get_indirect_name(target_args[call_args.len()].1.reference_depth) + .to_string(), + name_handler + .type_table() + .get_type(evaluated.type_info.type_id) + .unwrap() + .get_indirect_name(evaluated.type_info.reference_depth) + .to_string(), + function.get_line(), + )); + } + call_args.push(( + evaluated.offset, + name_handler.type_table().get_type_size(evaluated.type_info)?, + )); + } + + Ok(if let Some(return_type) = function.get_return_type() { + if return_into.is_some() && return_into.unwrap().type_info != return_type { + return Err(ProcessorError::BadEvaluatedType( + start_line.clone(), + name_handler + .type_table() + .get_type(return_into.unwrap().type_info.type_id) + .unwrap() + .get_indirect_name(return_into.unwrap().type_info.reference_depth) + .to_string(), + name_handler + .type_table() + .get_type(return_type.type_id) + .unwrap() + .get_indirect_name(return_type.reference_depth) + .to_string(), + )); + } + let return_into = if let Some(return_into) = return_into { + ( + return_into.offset, + name_handler.type_table().get_type_size(return_type)?, + ) + } else { + ( + name_handler.add_local_variable(None, return_type, lines)?, + name_handler.type_table().get_type_size(return_type)?, + ) + }; + + if function.is_inline() { + let mut inline_args: Vec<_> = call_args.into_iter().map(|x| x.0).collect(); + inline_args.push(return_into.0); + lines.push(Line::InlineAsm(function.get_inline(inline_args))); + } else { + lines.push(Line::ReturnCall( + function.get_id(), + -(name_handler.local_variable_space() as isize), + call_args, + name_handler.type_table().get_type_size(return_type)?, + return_into.0, + )) + } + + Some(LocalVariable::from_type_info(return_into.0, return_type)) + } else { + if let Some(return_into) = return_into { + return Err(ProcessorError::BadEvaluatedType( + start_line.clone(), + name_handler + .type_table() + .get_type(return_into.type_info.type_id) + .unwrap() + .get_indirect_name(return_into.type_info.reference_depth) + .to_string(), + "None".to_string(), + )); + } + + if function.is_inline() { + let inline_args: Vec<_> = call_args.into_iter().map(|x| x.0).collect(); + lines.push(Line::InlineAsm(function.get_inline(inline_args))); + } else { + lines.push(Line::NoReturnCall( + function.get_id(), + -(name_handler.local_variable_space() as isize), + call_args, + 0, + )) + } + + None + }) +} diff --git a/src/root/compiler/compile_functions/evaluate.rs b/src/root/compiler/compile_functions/evaluate.rs new file mode 100644 index 0000000..1fa0efb --- /dev/null +++ b/src/root/compiler/compile_functions/evaluate.rs @@ -0,0 +1,79 @@ +use crate::root::basic_ast::symbol::BasicSymbol; +use crate::root::compiler::compile_functions::name_handler::NameHandler; +use crate::root::compiler::compile_functions::{evaluate_symbol, operators, FunctionHolder, Line}; +use crate::root::compiler::local_variable::LocalVariable; +use crate::root::parser::line_info::LineInfo; +use crate::root::name_resolver::processor::ProcessorError; + +pub fn evaluate( + section: &[(BasicSymbol, LineInfo)], + lines: &mut Vec, + name_handler: &mut NameHandler, + function_holder: &FunctionHolder, + return_into: Option, +) -> Result, ProcessorError> { + // addr, type + Ok(if section.len() == 1 { + evaluate_symbol::evaluate_symbol( + §ion[0], + lines, + name_handler, + function_holder, + return_into, + )? + } else if section.len() == 2 { + let op = operators::evaluate_operator(§ion[0])?; + let Some(value) = evaluate_symbol::evaluate_symbol( + §ion[1], + lines, + name_handler, + function_holder, + None, + )? + else { + return Err(ProcessorError::DoesntEvaluate(section[1].1.clone())); + }; + operators::evaluate_operation( + value, + (op, §ion[1].1), + None, + lines, + name_handler, + function_holder, + return_into, + )? + } else if section.len() == 3 { + let Some(lhs) = evaluate_symbol::evaluate_symbol( + §ion[0], + lines, + name_handler, + function_holder, + None, + )? + else { + return Err(ProcessorError::DoesntEvaluate(section[0].1.clone())); + }; + let op = operators::evaluate_operator(§ion[1])?; + let Some(rhs) = evaluate_symbol::evaluate_symbol( + §ion[2], + lines, + name_handler, + function_holder, + None, + )? + else { + return Err(ProcessorError::DoesntEvaluate(section[2].1.clone())); + }; + operators::evaluate_operation( + lhs, + (op, §ion[1].1), + Some(rhs), + lines, + name_handler, + function_holder, + return_into, + )? + } else { + return Err(ProcessorError::BadEvaluableLayout(section[3].1.clone())); + }) +} diff --git a/src/root/compiler/compile_functions/evaluate_symbol.rs b/src/root/compiler/compile_functions/evaluate_symbol.rs new file mode 100644 index 0000000..f025b0e --- /dev/null +++ b/src/root/compiler/compile_functions/evaluate_symbol.rs @@ -0,0 +1,82 @@ +use crate::root::basic_ast::symbol::BasicSymbol; +use crate::root::compiler::compile_functions::name_handler::NameHandler; +use crate::root::compiler::compile_functions::{ + call_function, evaluate, instantiate_literal, FunctionHolder, Line, +}; +use crate::root::parser::line_info::LineInfo; +use crate::root::name_resolver::processor::ProcessorError; +use either::{Left, Right}; +use crate::root::compiler::local_variable::LocalVariable; + +pub fn evaluate_symbol( + symbol: &(BasicSymbol, LineInfo), + lines: &mut Vec, + name_handler: &mut NameHandler, + function_holder: &FunctionHolder, + return_into: Option, +) -> Result, ProcessorError> { + // println!("{:?}", symbol); + Ok(match &symbol.0 { + BasicSymbol::AbstractSyntaxTree(_) => panic!(), + BasicSymbol::Operator(_) => { + return Err(ProcessorError::BadEvaluableLayout(symbol.1.clone())) + } + BasicSymbol::Literal(literal) => Some(instantiate_literal::instantiate_variable( + Left((literal, &symbol.1)), + lines, + name_handler, + function_holder, + return_into, + )?), + BasicSymbol::BracketedSection(inner) => { + evaluate::evaluate(inner, lines, name_handler, function_holder, return_into)? + } + BasicSymbol::Name(name) => { + // println!("{:?}", name); + match name_handler.resolve_name(function_holder, name, &symbol.1, lines)? { + Left(new_variable) => { + if let Some(return_into) = return_into { + if return_into.type_info != new_variable.type_info { + return Err(ProcessorError::BadEvaluatedType( + symbol.1.clone(), + name_handler + .type_table() + .get_type(return_into.type_info.type_id) + .unwrap() + .get_indirect_name(return_into.type_info.reference_depth) + .to_string(), + name_handler + .type_table() + .get_type(new_variable.type_info.type_id) + .unwrap() + .get_indirect_name(new_variable.type_info.reference_depth) + .to_string(), + )); + } + + lines.push(Line::Copy( + new_variable.offset, + return_into.offset, + name_handler.type_table().get_type_size(return_into.type_info)?, + )); + + Some(return_into) + } else { + Some(new_variable) + } + } + Right((function, default_args, args)) => call_function::call_function( + function, + &symbol.1, + default_args, + args, + lines, + name_handler, + function_holder, + return_into, + )?, + } + } + _other => return Err(ProcessorError::BadEvaluableLayout(symbol.1.clone())), + }) +} diff --git a/src/root/compiler/compile_functions/instantiate_literal.rs b/src/root/compiler/compile_functions/instantiate_literal.rs new file mode 100644 index 0000000..c1a1fd9 --- /dev/null +++ b/src/root/compiler/compile_functions/instantiate_literal.rs @@ -0,0 +1,113 @@ +use crate::root::ast::literals::Literal; +use crate::root::compiler::compile_functions::evaluate::evaluate; +use crate::root::compiler::compile_functions::name_handler::NameHandler; +use crate::root::compiler::compile_functions::{FunctionHolder, Line}; +use crate::root::parser::line_info::LineInfo; +use crate::root::name_resolver::processor::ProcessorError; +use either::{Either, Left, Right}; +use crate::root::compiler::local_variable::{LocalVariable, TypeInfo}; +use crate::root::custom::types::int::Int; + +#[allow(dead_code)] +fn try_instantiate_literal( + literal: Either, + lines: &mut Vec, + name_handler: &mut NameHandler, + function_holder: &FunctionHolder, + return_into: Option, +) -> Result { + match literal { + Left(r) => Ok(r), + Right(literal) => instantiate_variable( + Left(literal), + lines, + name_handler, + function_holder, + return_into, + ), + } +} + +pub fn instantiate_variable( + literal: Either<(&Literal, &LineInfo), TypeInfo>, + lines: &mut Vec, + name_handler: &mut NameHandler, + function_holder: &FunctionHolder, + return_into: Option, +) -> Result { + let variable = if let Some(variable) = return_into { + variable + } else { + let id = match &literal { + Left((literal, info)) => literal.get_type_id(name_handler.type_table(), info)?, + Right(id) => *id, + }; + LocalVariable::from_type_info(name_handler.add_local_variable(None, id, lines)?, id) + }; + + // Indirect + let true_id = variable.type_info.type_id; + let indirection = variable.type_info.reference_depth; + let id = if indirection != 0 { + Int::get_id() + } else { + variable.type_info.type_id + }; + let _type = name_handler.type_table().get_type(id).unwrap(); + let asm = match &literal { + Left((Literal::Initialiser(name, attributes), line_info)) => { + let line_info = *line_info; + let _id = name_handler.type_table().get_id_by_name(name).ok_or( + ProcessorError::TypeNotFound(line_info.clone(), name.clone()), + )?; + + if id != _id { + return Err(ProcessorError::BadEvaluatedType( + line_info.clone(), + _type.get_name().to_string(), + name.clone(), + )); + } + + if id < 0 { + return Err(ProcessorError::AttemptedBuiltinInitialiser( + line_info.clone(), + )); + } + + // let size = _type.get_size(name_handler.type_table(), None)?; // ! Ensures non-circularity + let attribute_types = _type.get_user_type().unwrap().get_attribute_types(); + if attributes.len() != attribute_types.len() { + return Err(ProcessorError::IncorrectAttribCount( + line_info.clone(), + attribute_types.len(), + attributes.len(), + )); + } + + let mut addr_counter = variable.offset; + for (attribute, attribute_type) in attributes.iter().zip(attribute_types) { + evaluate( + attribute, + lines, + name_handler, + function_holder, + Some(LocalVariable::from_type_info(addr_counter, attribute_type)), + )?; + + addr_counter += name_handler.type_table().get_type_size(attribute_type)? as isize; + } + + Vec::new() + } + Left((literal, line_info)) => { + if literal.get_type_id(name_handler.type_table(), line_info)? != TypeInfo::new(id, 0) { + return Err(ProcessorError::BadLiteralType()); + } + _type.instantiate(Some(literal), variable.offset)? + } + Right(_id) => _type.instantiate(None, variable.offset)?, + }; + lines.push(Line::InlineAsm(asm)); + Ok(LocalVariable::new(variable.offset, true_id, indirection)) +} diff --git a/src/root/compiler/compile_functions/name_handler.rs b/src/root/compiler/compile_functions/name_handler.rs new file mode 100644 index 0000000..81b401d --- /dev/null +++ b/src/root/compiler/compile_functions/name_handler.rs @@ -0,0 +1,248 @@ +use crate::root::basic_ast::symbol::{BasicSymbol, NameAccessType, NameType}; +use crate::root::compiler::compile_functions::{FunctionHolder, Line}; +use crate::root::parser::line_info::LineInfo; +use crate::root::name_resolver::processor::ProcessorError; +use crate::root::name_resolver::type_builder::{TypeTable, TypedFunction}; +use crate::root::utils::align; +use either::{Either, Left, Right}; +use std::collections::HashSet; +use crate::root::compiler::local_variable::{LocalVariable, TypeInfo}; +use crate::root::custom::types::int::Int; + +pub struct NameHandler { + type_table: TypeTable, + args: Vec<(String, LocalVariable)>, + local_variables: Vec<(String, LocalVariable)>, + local_variables_size: usize, + used_functions: HashSet, + uid: usize, +} + +impl NameHandler { + pub fn new(type_table: TypeTable) -> NameHandler { + NameHandler { + type_table, + args: Vec::new(), + local_variables: Vec::new(), + local_variables_size: 0, + used_functions: HashSet::new(), + uid: 0, + } + } + + pub fn set_args(&mut self, args: Vec<(String, LocalVariable)>) { + self.args = args; + } + + pub fn reset(&mut self) { + self.uid = 0; + self.args.clear(); + self.local_variables.clear(); + self.local_variables_size = 0; + } + + pub fn get_uid(&mut self) -> usize { + self.uid += 1; + self.uid - 1 + } + + pub fn type_table(&self) -> &TypeTable { + &self.type_table + } + + pub fn local_variable_space(&self) -> usize { + self.local_variables_size + } + + pub fn add_local_variable( + &mut self, + name: Option, + _type: TypeInfo, + _lines: &mut Vec, + ) -> Result { + let size = align(self.type_table.get_type_size(_type)?, 8); + let addr = -(self.local_variables_size as isize) - size as isize; + self.local_variables_size += size; + // parse_fn.push(Line::InlineAsm(vec![format!("sub rsp, {}", size)])); + if let Some(name) = name { + self.local_variables.push((name, LocalVariable::from_type_info(addr, _type))); + } + Ok(addr) + } + + pub fn destroy_local_variables(&mut self, lines: &mut Vec) -> Result<(), ProcessorError> { + // return Ok(()) + + for (_name, variable) in self.local_variables.clone() { + if variable.type_info.reference_depth != 0 { + continue; + } + let t = self.type_table.get_type(variable.type_info.type_id).unwrap(); + if let Some(destructor) = t.get_destructor() { + let ref_ = self.add_local_variable(None, TypeInfo::new(variable.type_info.type_id, 1), lines)?; + + lines.push(Line::InlineAsm(Int::instantiate_local_ref(variable.offset, ref_))); + + lines.push(Line::NoReturnCall( + destructor, + -(self.local_variable_space() as isize), + vec![(ref_, self.type_table.get_type_size(TypeInfo::new(variable.type_info.type_id, 1))?)], + 0, + )); + + self.use_function_id(destructor); + } + } + + Ok(()) + } + + pub fn name_variable(&mut self, name: String, addr: isize, _type: TypeInfo) { + self.local_variables.push((name, LocalVariable::from_type_info(addr, _type))); + } + + pub fn resolve_name<'b>( + &mut self, + function_holder: &'b FunctionHolder, + name: &'b Vec<(String, NameAccessType, NameType, usize)>, + line: &LineInfo, + lines: &mut Vec, + ) -> Result< + Either< + LocalVariable, + ( + &'b Box, + Option, + &'b Vec>, + ), + >, + ProcessorError, + > { + let mut current_type: Option = None; + let mut current_variable = None; + let mut return_func: Option<(&Box, Option, &Vec>)> = None; + + for (name, access_type, name_type, indirection) in name { + if return_func.is_some() { + // TODO + return Err(ProcessorError::NotImplemented( + line.clone(), + "Using '.' or '#' after a function call".to_string(), + )); + } + + match name_type { + NameType::Normal => { + if current_type.is_some() && current_variable.is_some() { + let user_type = self + .type_table + .get_type(current_type.unwrap().type_id) + .unwrap() + .get_user_type() + .ok_or(ProcessorError::AttributeDoesntExist( + line.clone(), + self.type_table + .get_type(current_type.unwrap().type_id) + .unwrap() + .get_name() + .to_string(), + name.clone(), + ))?; + + let t = user_type + .get_attribute_offset_and_type(name, &self.type_table)? + .ok_or(ProcessorError::AttributeDoesntExist( + line.clone(), + self.type_table + .get_type(current_type.unwrap().type_id) + .unwrap() + .get_name() + .to_string(), + name.clone(), + ))?; + + if current_type.unwrap().reference_depth > 0 { + let ref_addr = self + .add_local_variable(None, TypeInfo::new(t.type_info.type_id, current_type.unwrap().reference_depth), lines) + .unwrap(); + lines.push(Line::InlineAsm(Int::instantiate_ref( + current_variable.unwrap(), + t.offset, + ref_addr, + ))); + current_variable = Some(ref_addr); + current_type = Some(TypeInfo::new(t.type_info.type_id, current_type.unwrap().reference_depth + t.type_info.reference_depth)); + } else { + current_variable = Some(current_variable.unwrap() + t.offset); + current_type = Some(t.type_info); + } + } else if current_type.is_some() { + return Err(ProcessorError::AttemptedTypeAttribAccess(line.clone())); + } else if let Some((_, variable)) = self + .local_variables + .iter() + .rev() + .chain(self.args.iter()) + .find(|(n, _)| n == name) + { + // println!("{}, {}", addr, _type); + current_variable = Some(variable.offset); + current_type = Some(variable.type_info); + } else if let Some(_type) = self.type_table.get_id_by_name(name) { + current_variable = None; + current_type = Some(TypeInfo::new(_type, *indirection)); + } else { + return Err(ProcessorError::NameNotFound(line.clone(), name.clone())); + } + } + NameType::Function(contents) => { + if let Some(func) = function_holder + .functions_table() + .get(¤t_type.map(|x| x.type_id)) + .unwrap() + .get(name) + { + let default_arg = if matches!(access_type, NameAccessType::Normal) { + if current_variable.is_none() { + return Err(ProcessorError::TypeNonStaticFunctionCall( + line.clone(), + )); + } + Some(LocalVariable::from_type_info(current_variable.unwrap(), current_type.unwrap())) + } else { + None + }; + return_func = Some(( + function_holder.functions().get(func).unwrap(), + default_arg, + contents, + )); + } + } + } + } + + if let Some(return_func) = return_func { + return Ok(Right(return_func)); + } + + Ok(Left(LocalVariable::from_type_info( + current_variable.ok_or(ProcessorError::StandaloneType(line.clone()))?, + current_type.unwrap(), + ))) + } + + pub fn use_function_id(&mut self, id: isize) { + self.used_functions.insert(id); + } + + pub fn use_function(&mut self, func: &Box) { + if !func.is_inline() { + self.used_functions.insert(func.get_id()); + } + } + + pub fn used_functions(&self) -> &HashSet { + &self.used_functions + } +} diff --git a/src/root/compiler/compile_functions/operators.rs b/src/root/compiler/compile_functions/operators.rs new file mode 100644 index 0000000..386fda4 --- /dev/null +++ b/src/root/compiler/compile_functions/operators.rs @@ -0,0 +1,388 @@ +use crate::root::ast::literals::Literal; +use crate::root::ast::operators::Operator; +use crate::root::basic_ast::symbol::BasicSymbol; +use crate::root::compiler::compile_functions::instantiate_literal::instantiate_variable; +use crate::root::compiler::compile_functions::name_handler::NameHandler; +use crate::root::compiler::compile_functions::{FunctionHolder, Line}; +use crate::root::parser::line_info::LineInfo; +use crate::root::name_resolver::processor::ProcessorError; +use either::{Left, Right}; +use crate::root::compiler::local_variable::{LocalVariable, TypeInfo}; +use crate::root::custom::types::bool::Bool; +use crate::root::custom::types::float::Float; +use crate::root::custom::types::int::Int; + +pub fn evaluate_operator(symbol: &(BasicSymbol, LineInfo)) -> Result<&Operator, ProcessorError> { + match &symbol.0 { + BasicSymbol::Operator(operator) => Ok(operator), + _ => Err(ProcessorError::BadEvaluableLayout(symbol.1.clone())), + } +} + +pub fn evaluate_operation( + lhs: LocalVariable, + op: (&Operator, &LineInfo), + rhs: Option, + lines: &mut Vec, + name_handler: &mut NameHandler, + function_holder: &FunctionHolder, + return_into: Option, +) -> Result, ProcessorError> { + Ok(Some(match &op.0 { + Operator::Not => { + let func = function_holder.get_function(Some(lhs.type_info), "not").ok_or( + ProcessorError::SingleOpFunctionNotFound( + op.1.clone(), + "not".to_string(), + name_handler + .type_table() + .get_type(lhs.type_info.type_id) + .unwrap() + .get_indirect_name(lhs.type_info.reference_depth) + .to_string(), + ), + )?; + name_handler.use_function(func); + let func_args = func.get_args(); + let func_id = func.get_id(); + if func_args.len() != 1 { + return Err(ProcessorError::SingleOpFunctionNotFound( + op.1.clone(), + "not".to_string(), + name_handler + .type_table() + .get_type(lhs.type_info.type_id) + .unwrap() + .get_indirect_name(lhs.type_info.reference_depth) + .to_string(), + )); + } + let output = if let Some(return_into) = return_into { + return_into + } else { + instantiate_variable( + Right( + func.get_return_type() + .ok_or(ProcessorError::SingleOpFunctionNotFound( + op.1.clone(), + "not".to_string(), + name_handler + .type_table() + .get_type(lhs.type_info.type_id) + .unwrap() + .get_indirect_name(lhs.type_info.reference_depth) + .to_string(), + ))?, + ), + lines, + name_handler, + function_holder, + None, + )? + }; + let func = function_holder.functions().get(&func_id).unwrap(); + if func.is_inline() { + lines.push(Line::InlineAsm(func.get_inline(vec![lhs.offset, output.offset]))); + } else { + lines.push(Line::ReturnCall( + func.get_id(), + -(name_handler.local_variable_space() as isize), + vec![(lhs.offset, name_handler.type_table().get_type_size(lhs.type_info)?)], + name_handler.type_table().get_type_size(output.type_info)?, + output.offset, + )); + } + output + } + op_ => { + if matches!(op_, Operator::And) && rhs.is_none() { + let return_into = if let Some(return_into) = return_into { + if return_into.type_info.type_id != lhs.type_info.type_id || return_into.type_info.reference_depth != lhs.type_info.reference_depth + 1 { + return Err(ProcessorError::BadEvaluatedType( + op.1.clone(), + name_handler + .type_table() + .get_type(return_into.type_info.type_id) + .unwrap() + .get_indirect_name(return_into.type_info.reference_depth) + .to_string(), + name_handler + .type_table() + .get_type(lhs.type_info.type_id) + .unwrap() + .get_indirect_name(lhs.type_info.reference_depth + 1) + .to_string(), + )); + } + return_into + } else { + LocalVariable::new( + name_handler.add_local_variable(None, TypeInfo::new(lhs.type_info.type_id, lhs.type_info.reference_depth + 1), lines)?, + lhs.type_info.type_id, lhs.type_info.reference_depth + 1 + ) + }; + lines.push(Line::InlineAsm(Int::instantiate_local_ref( + lhs.offset, + return_into.offset, + ))); + return Ok(Some(return_into)); + } + + // Get ref + if matches!(op_, Operator::Product) && rhs.is_none() { + let return_into = if let Some(return_into) = return_into { + if lhs.type_info.reference_depth == 0 { + return Err(ProcessorError::CantDerefNonRef(op.1.clone())); + } + if return_into.type_info.type_id != lhs.type_info.type_id || return_into.type_info.reference_depth != lhs.type_info.reference_depth - 1 { + return Err(ProcessorError::BadEvaluatedType( + op.1.clone(), + name_handler + .type_table() + .get_type(return_into.type_info.type_id) + .unwrap() + .get_indirect_name(return_into.type_info.reference_depth) + .to_string(), + name_handler + .type_table() + .get_type(lhs.type_info.type_id) + .unwrap() + .get_indirect_name(lhs.type_info.reference_depth - 1) + .to_string(), + )); + } + return_into + } else { + LocalVariable::new( + name_handler.add_local_variable(None, TypeInfo::new(lhs.type_info.type_id, lhs.type_info.reference_depth + 1), lines)?, + lhs.type_info.type_id, lhs.type_info.reference_depth - 1, + ) + }; + + lines.push(Line::DynFromCopy( + lhs.offset, + return_into.offset, + name_handler.type_table().get_type_size(return_into.type_info)?, + )); + return Ok(Some(return_into)); + } + + // Heap alloc + if matches!(op_, Operator::HeapAlloc) && rhs.is_none() { + let return_into = if let Some(return_into) = return_into { + if return_into.type_info.type_id != lhs.type_info.type_id || return_into.type_info.reference_depth != lhs.type_info.reference_depth + 1 { + return Err(ProcessorError::BadEvaluatedType( + op.1.clone(), + name_handler + .type_table() + .get_type(return_into.type_info.type_id) + .unwrap() + .get_indirect_name(return_into.type_info.reference_depth) + .to_string(), + name_handler + .type_table() + .get_type(lhs.type_info.type_id) + .unwrap() + .get_indirect_name(lhs.type_info.reference_depth + 1) + .to_string(), + )); + } + return_into + } else { + LocalVariable::new( + name_handler.add_local_variable(None, TypeInfo::new(lhs.type_info.type_id, lhs.type_info.reference_depth + 1), lines)?, + lhs.type_info.type_id, lhs.type_info.reference_depth + 1, + ) + }; + let size = name_handler.type_table().get_type_size(lhs.type_info)?; + lines.push(Line::HeapAlloc(size, return_into.offset)); + lines.push(Line::DynToCopy(lhs.offset, return_into.offset, size)); + return Ok(Some(return_into)); + } + + // Heap dealloc + if matches!(op_, Operator::HeapDealloc) && rhs.is_none() { + let return_into = if let Some(return_into) = return_into { + if return_into.type_info != TypeInfo::new(Bool::get_id(), 0) { + return Err(ProcessorError::BadEvaluatedType( + op.1.clone(), + name_handler + .type_table() + .get_type(return_into.type_info.type_id) + .unwrap() + .get_indirect_name(return_into.type_info.reference_depth) + .to_string(), + name_handler + .type_table() + .get_type(Bool::get_id()) + .unwrap() + .get_indirect_name(0) + .to_string(), + )); + } + return_into + } else { + LocalVariable::new( + name_handler.add_local_variable(None, TypeInfo::new(Bool::get_id(), 0), lines)?, + Bool::get_id(), 0, + ) + }; + + if lhs.type_info.reference_depth == 0 { + return Err(ProcessorError::CantDeallocateNonRef(op.1.clone())); + } + + lines.push(Line::HeapDealloc(lhs.offset, return_into.offset)); + return Ok(Some(return_into)); + } + + // Negate int + let (lhs, rhs) = if matches!(op_, Operator::Subtract) + && rhs.is_none() && (lhs.type_info == TypeInfo::new(Int::get_id(), 0) || lhs.type_info == TypeInfo::new(Float::get_id(), 0)) + { + ( + instantiate_variable( + if lhs.type_info.type_id == Int::get_id() { Left((&Literal::Int(0), &op.1)) } else { Left((&Literal::Float(0.0), &op.1)) }, + lines, + name_handler, + function_holder, + None, + ) + .unwrap(), + lhs, + ) + } else { + ( + lhs, + rhs.ok_or(ProcessorError::BadOperatorPosition( + op.1.clone(), + op.0.clone(), + ))?, + ) + }; + + let func_name = match op_ { + Operator::Add => "add", + Operator::Subtract => "sub", + Operator::Product => "mul", + Operator::Divide => "div", + Operator::Modulo => "mod", + Operator::Greater => "gt", + Operator::Less => "lt", + Operator::GreaterEqual => "ge", + Operator::LessEqual => "le", + Operator::Equal => "eq", + Operator::NotEqual => "ne", + Operator::Or => "or", + Operator::And => "and", + Operator::HeapAlloc => "heap allocate", + Operator::HeapDealloc => "heap deallocate", + Operator::Not => panic!(), + }; + + // println!("{}vs{} {} {}vs{}", lhs.1.0, Float::get_id(), func_name, rhs.1.1, Float::get_id()); + + let func = function_holder.get_function(Some(lhs.type_info), func_name).ok_or( + ProcessorError::OpFunctionNotFound( + op.1.clone(), + func_name.to_string(), + name_handler + .type_table() + .get_type(lhs.type_info.type_id) + .unwrap() + .get_indirect_name(lhs.type_info.reference_depth) + .to_string(), + name_handler + .type_table() + .get_type(rhs.type_info.type_id) + .unwrap() + .get_indirect_name(rhs.type_info.reference_depth) + .to_string(), + ), + )?; + name_handler.use_function(func); + let func_args = func.get_args(); + let func_id = func.get_id(); + + if func_args.len() != 2 || func_args[1].1 != rhs.type_info { + return Err(ProcessorError::OpFunctionNotFound( + op.1.clone(), + func_name.to_string(), + name_handler + .type_table() + .get_type(lhs.type_info.type_id) + .unwrap() + .get_indirect_name(lhs.type_info.reference_depth) + .to_string(), + name_handler + .type_table() + .get_type(rhs.type_info.type_id) + .unwrap() + .get_indirect_name(rhs.type_info.reference_depth) + .to_string(), + )); + } + + let ret_type = func + .get_return_type() + .ok_or(ProcessorError::OpFunctionNotFound( + op.1.clone(), + func_name.to_string(), + name_handler + .type_table() + .get_type(lhs.type_info.type_id) + .unwrap() + .get_indirect_name(lhs.type_info.reference_depth) + .to_string(), + name_handler + .type_table() + .get_type(rhs.type_info.type_id) + .unwrap() + .get_indirect_name(rhs.type_info.reference_depth) + .to_string(), + ))?; + + let output = if let Some(return_into) = return_into { + if return_into.type_info != ret_type { + return Err(ProcessorError::BadEvaluatedType( + op.1.clone(), + name_handler + .type_table() + .get_type(return_into.type_info.type_id) + .unwrap() + .get_indirect_name(return_into.type_info.reference_depth) + .to_string(), + name_handler + .type_table() + .get_type(ret_type.type_id) + .unwrap() + .get_indirect_name(ret_type.reference_depth) + .to_string(), + )); + } + return_into + } else { + instantiate_variable(Right(ret_type), lines, name_handler, function_holder, None)? + }; + + let func = function_holder.functions().get(&func_id).unwrap(); + if func.is_inline() { + lines.push(Line::InlineAsm( + func.get_inline(vec![lhs.offset, rhs.offset, output.offset]), + )); + } else { + lines.push(Line::ReturnCall( + func.get_id(), + -(name_handler.local_variable_space() as isize), + vec![ + (lhs.offset, name_handler.type_table().get_type_size(lhs.type_info)?), + (rhs.offset, name_handler.type_table().get_type_size(rhs.type_info)?), + ], + name_handler.type_table().get_type_size(output.type_info)?, + output.offset, + )); + } + output + } + })) +} diff --git a/src/root/compiler/compile_functions/process_lines.rs b/src/root/compiler/compile_functions/process_lines.rs new file mode 100644 index 0000000..1fa9b49 --- /dev/null +++ b/src/root/compiler/compile_functions/process_lines.rs @@ -0,0 +1,420 @@ +use crate::root::ast::keywords::Keyword; +use crate::root::ast::operators::Operator; +use crate::root::basic_ast::punctuation::Punctuation; +use crate::root::basic_ast::symbol::{BasicSymbol, NameType}; +use crate::root::compiler::compile_functions::assignment::process_assignment; +use crate::root::compiler::compile_functions::name_handler::NameHandler; +use crate::root::compiler::compile_functions::{evaluate, FunctionHolder, Line}; +use crate::root::compiler::generate_asm::{get_function_sublabel, get_local_address}; +use crate::root::parser::line_info::LineInfo; +use crate::root::name_resolver::processor::ProcessorError; +use crate::root::name_resolver::type_builder::Type; + +#[cfg(debug_assertions)] +use itertools::Itertools; +#[cfg(debug_assertions)] +use std::fs; +use crate::root::compiler::local_variable::{LocalVariable, TypeInfo}; +use crate::root::custom::types::bool::Bool; + +pub fn process_lines( + section: &[(BasicSymbol, LineInfo)], + current_id: isize, + return_type: Option, + lines: &mut Vec, + name_handler: &mut NameHandler, + function_holder: &FunctionHolder, + mut break_label: Option, +) -> Result { + let mut last_return = false; + + #[cfg(debug_assertions)] + let mut prev_line = None; + for line in section.split(|x| matches!(x.0, BasicSymbol::Punctuation(Punctuation::Semicolon))) { + if line.is_empty() { + continue; + } + + #[cfg(debug_assertions)] + { + let new_line = line.last().unwrap().1.line(); + let file = line.last().unwrap().1.file().unwrap(); + let start = if let Some(prev_line) = prev_line { + prev_line + } else { + line.first().unwrap().1.line() + }; + + let text = fs::read_to_string(file.as_path()).unwrap(); + let text = &text.lines().collect_vec()[(start - 1)..(new_line)]; + + for l in text { + lines.push(Line::Annotation(l.to_string())); + } + + prev_line = Some(new_line + 1); + } + + last_return = false; + + if line.len() > 2 + && matches!(&line[0].0, BasicSymbol::Operator(Operator::Product)) + && matches!(&line[2].0, BasicSymbol::Assigner(_)) + { + process_assignment(lines, name_handler, function_holder, &line[1..], true)?; + continue; + } + + if line.len() > 1 && matches!(&line[1].0, BasicSymbol::Assigner(_)) { + process_assignment(lines, name_handler, function_holder, line, false)?; + continue; + } + + match &line[0].0 { + BasicSymbol::Keyword(Keyword::Return) => { + last_return = true; + if line.len() == 1 { + if return_type.is_none() { + name_handler.destroy_local_variables(lines)?; + lines.push(Line::Return(None)); + continue; + } else { + return Err(ProcessorError::NoneReturnOnTypedFunction(line[0].1.clone())); + } + } else if return_type.is_none() { + return Err(ProcessorError::TypeReturnOnVoidFunction(line[1].1.clone())); + } + let return_type = return_type.unwrap(); + + let return_into = name_handler.add_local_variable(None, return_type, lines)?; + let return_value = evaluate::evaluate( + &line[1..], + lines, + name_handler, + function_holder, + Some(LocalVariable::from_type_info(return_into, return_type)), + )?; + if return_value.is_none() { + return Err(ProcessorError::DoesntEvaluate(line[1].1.clone())); + } + let return_value = return_value.unwrap(); + if return_type != return_value.type_info { + return Err(ProcessorError::BadReturnType( + line[1].1.clone(), + name_handler + .type_table() + .get_type(return_type.type_id) + .unwrap() + .get_indirect_name(return_type.reference_depth) + .to_string(), + name_handler + .type_table() + .get_type(return_value.type_info.type_id) + .unwrap() + .get_indirect_name(return_value.type_info.reference_depth) + .to_string(), + )); + } + name_handler.destroy_local_variables(lines)?; + lines.push(Line::Return(Some(( + return_value.offset, + name_handler.type_table().get_type_size(return_value.type_info)?, + )))); + } + BasicSymbol::Keyword(Keyword::Let) => { + if line.len() < 2 { + return Err(ProcessorError::LetNoName(line[0].1.clone())); + } + let BasicSymbol::Name(name) = &line[1].0 else { + return Err(ProcessorError::LetNoName(line[1].1.clone())); + }; + + if name.len() > 1 { + return Err(ProcessorError::MultipartNameDef(line[1].1.clone())); + } + let name = &name[0]; + if name.3 != 0 { + return Err(ProcessorError::NameWithRefPrefix(line[0].1.clone())); + } + + if !matches!(&name.2, NameType::Normal) { + return Err(ProcessorError::LetNoName(line[0].1.clone())); + } + let name = &name.0; + + if line.len() < 4 { + return Err(ProcessorError::NameTypeNotDefined(line[1].1.clone())); + } + if !matches!(&line[2].0, BasicSymbol::Punctuation(Punctuation::Colon)) { + return Err(ProcessorError::NameTypeNotDefined(line[2].1.clone())); + } + + let BasicSymbol::Name(type_name) = &line[3].0 else { + return Err(ProcessorError::NameTypeNotDefined(line[3].1.clone())); + }; + + if type_name.len() > 1 { + return Err(ProcessorError::MultipartTypeName(line[3].1.clone())); + } + let type_name = &type_name[0]; + + if !matches!(&type_name.2, NameType::Normal) { + return Err(ProcessorError::NameTypeNotDefined(line[3].1.clone())); + } + + let type_id = name_handler + .type_table() + .get_id_by_name(&type_name.0) + .ok_or(ProcessorError::TypeNotFound( + line[3].1.clone(), + type_name.0.clone(), + ))?; + let addr = name_handler.add_local_variable(None, TypeInfo::new(type_id, type_name.3), lines)?; + + if line.len() < 6 { + return Err(ProcessorError::LetNoValue(line[3].1.clone())); + } + if !matches!(&line[4].0, BasicSymbol::Assigner(None)) { + return Err(ProcessorError::LetNoValue(line[4].1.clone())); + } + + evaluate::evaluate( + &line[5..], + lines, + name_handler, + function_holder, + Some(LocalVariable::new(addr, type_id, type_name.3)), + )?; + + name_handler.name_variable(name.clone(), addr, TypeInfo::new(type_id, type_name.3)); + } + BasicSymbol::Keyword(Keyword::While) => { + if line.len() < 2 { + return Err(ProcessorError::WhileNoBrackets(line[0].1.clone())); + } + + let BasicSymbol::BracketedSection(expr) = &line[1].0 else { + return Err(ProcessorError::WhileNoBrackets(line[1].1.clone())); + }; + let start_label = + get_function_sublabel(current_id, &name_handler.get_uid().to_string()); + let end_label = + get_function_sublabel(current_id, &name_handler.get_uid().to_string()); + break_label = Some(end_label.clone()); + + lines.push(Line::InlineAsm(vec![format!("{}:", start_label)])); + let evaluated = + evaluate::evaluate(expr, lines, name_handler, function_holder, None)? + .ok_or(ProcessorError::DoesntEvaluate(line[1].1.clone()))?; + lines.push(Line::InlineAsm(vec![ + format!("mov rax, qword [{}]", get_local_address(evaluated.offset)), + "cmp rax, 0".to_string(), + format!("jnz {}", end_label), + ])); + + if evaluated.type_info != TypeInfo::new(Bool::new().get_id(), 0) { + return Err(ProcessorError::BadConditionType( + line[1].1.clone(), + name_handler + .type_table() + .get_type(evaluated.type_info.type_id) + .unwrap() + .get_indirect_name(evaluated.type_info.reference_depth) + .to_string(), + )); + } + if line.len() < 3 { + return Err(ProcessorError::WhileNoBraces(line[1].1.clone())); + } + let BasicSymbol::BracedSection(inner) = &line[2].0 else { + return Err(ProcessorError::WhileNoBraces(line[2].1.clone())); + }; + process_lines( + inner, + current_id, + return_type, + lines, + name_handler, + function_holder, + break_label.clone(), + )?; + lines.push(Line::InlineAsm(vec![ + format!("jmp {}", start_label), + format!("{}:", end_label), + ])); + + if line.len() > 3 { + return Err(ProcessorError::WhileMoreAfterBraces(line[3].1.clone())); + } + } + BasicSymbol::Keyword(Keyword::If) => { + if line.len() < 2 { + return Err(ProcessorError::IfElifNoBrackets(line[0].1.clone())); + } + + let BasicSymbol::BracketedSection(expr) = &line[1].0 else { + return Err(ProcessorError::IfElifNoBrackets(line[1].1.clone())); + }; + let mut next_label = + get_function_sublabel(current_id, &name_handler.get_uid().to_string()); + let end_label = + get_function_sublabel(current_id, &name_handler.get_uid().to_string()); + + let evaluated = + evaluate::evaluate(expr, lines, name_handler, function_holder, None)? + .ok_or(ProcessorError::DoesntEvaluate(line[1].1.clone()))?; + if evaluated.type_info != TypeInfo::new(Bool::new().get_id(), 0) { + return Err(ProcessorError::BadConditionType( + line[1].1.clone(), + name_handler + .type_table() + .get_type(evaluated.type_info.type_id) + .unwrap() + .get_indirect_name(evaluated.type_info.reference_depth) + .to_string(), + )); + } + lines.push(Line::InlineAsm(vec![ + format!("mov rax, qword [{}]", get_local_address(evaluated.offset)), + "cmp rax, 0".to_string(), + format!("jnz {}", next_label), + ])); + if line.len() < 3 { + return Err(ProcessorError::IfElifElseNoBraces(line[1].1.clone())); + } + let BasicSymbol::BracedSection(inner) = &line[2].0 else { + return Err(ProcessorError::IfElifElseNoBraces(line[2].1.clone())); + }; + process_lines( + inner, + current_id, + return_type, + lines, + name_handler, + function_holder, + break_label.clone(), + )?; + + let mut i = 3; + let mut ended = false; + while line.len() > i { + lines.push(Line::InlineAsm(vec![ + format!("jmp {}", end_label), + format!("{}:", next_label), + ])); + + next_label = + get_function_sublabel(current_id, &name_handler.get_uid().to_string()); + + match &line[i].0 { + BasicSymbol::Keyword(Keyword::Elif) => { + if ended { + return Err(ProcessorError::IfElifAfterElse(line[i].1.clone())); + } + i += 1; + if line.len() <= i { + return Err(ProcessorError::IfElifNoBrackets( + line[i - 1].1.clone(), + )); + } + + let BasicSymbol::BracketedSection(expr) = &line[i].0 else { + return Err(ProcessorError::IfElifNoBrackets(line[i].1.clone())); + }; + + let evaluated = evaluate::evaluate( + expr, + lines, + name_handler, + function_holder, + None, + )? + .ok_or(ProcessorError::DoesntEvaluate(line[i].1.clone()))?; + if evaluated.type_info != TypeInfo::new(Bool::new().get_id(), 0) { + return Err(ProcessorError::BadConditionType( + line[i].1.clone(), + name_handler + .type_table() + .get_type(evaluated.type_info.type_id) + .unwrap() + .get_indirect_name(evaluated.type_info.reference_depth) + .to_string(), + )); + } + lines.push(Line::InlineAsm(vec![ + format!("mov rax, qword [{}]", get_local_address(evaluated.offset)), + "cmp rax, 0".to_string(), + format!("jnz {}", next_label), + ])); + + i += 1; + if line.len() <= i { + return Err(ProcessorError::IfElifElseNoBraces( + line[i - 1].1.clone(), + )); + } + let BasicSymbol::BracedSection(inner) = &line[i].0 else { + return Err(ProcessorError::IfElifElseNoBraces(line[i].1.clone())); + }; + process_lines( + inner, + current_id, + return_type, + lines, + name_handler, + function_holder, + break_label.clone(), + )?; + i += 1; + } + BasicSymbol::Keyword(Keyword::Else) => { + ended = true; + i += 1; + if line.len() <= i { + return Err(ProcessorError::IfElifElseNoBraces( + line[i - 1].1.clone(), + )); + } + let BasicSymbol::BracedSection(inner) = &line[i].0 else { + return Err(ProcessorError::IfElifElseNoBraces(line[i].1.clone())); + }; + process_lines( + inner, + current_id, + return_type, + lines, + name_handler, + function_holder, + break_label.clone(), + )?; + i += 1; + } + _ => return Err(ProcessorError::ElseMoreAfterBraces(line[i].1.clone())), + } + } + + lines.push(Line::InlineAsm(vec![ + format!("{}:", next_label), + format!("{}:", end_label), + ])); + } + BasicSymbol::Keyword(Keyword::Elif | Keyword::Else) => { + return Err(ProcessorError::RawElifElse(line[0].1.clone())) + } + BasicSymbol::Keyword(Keyword::Break) => { + if line.len() > 1 { + return Err(ProcessorError::BreakLineNotEmpty(line[1].1.clone())); + } + let Some(break_label) = &break_label else { + return Err(ProcessorError::NothingToBreak(line[0].1.clone())); + }; + lines.push(Line::InlineAsm(vec![format!("jmp {}", break_label)])); + } + _ => { + evaluate::evaluate(line, lines, name_handler, function_holder, None)?; + } + }; + } + + Ok(last_return) +} diff --git a/src/root/compiler/compile_functions/reference.rs b/src/root/compiler/compile_functions/reference.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/root/compiler/compile_functions/reference.rs @@ -0,0 +1 @@ + diff --git a/src/root/compiler/generate_asm.rs b/src/root/compiler/generate_asm.rs new file mode 100644 index 0000000..4c9c53b --- /dev/null +++ b/src/root/compiler/generate_asm.rs @@ -0,0 +1,342 @@ +use crate::root::compiler::compile_functions::{Line, UserFunction}; +use crate::root::utils::align; + +pub struct Output { + inner: String, +} + +impl Default for Output { + fn default() -> Self { + Self::new() + } +} + +impl Output { + pub fn new() -> Output { + Output { + inner: String::new(), + } + } + + pub fn new_with_name(id: isize, name: &str) -> Output { + Output { + inner: format!("{}: ; {}\n", get_function_name(id), name), + } + } + + pub fn push(&mut self, string: &str) { + self.inner.push('\t'); + self.inner += string; + self.inner.push('\n'); + } +} + +impl From for String { + fn from(value: Output) -> Self { + value.inner + } +} + +pub fn get_function_name(id: isize) -> String { + if id == 0 { + return "main".to_string(); + } + let sign = if id < 0 { "__" } else { "_" }; + format!("{sign}{}", id.abs()) +} + +pub fn get_function_sublabel(id: isize, label: &str) -> String { + let mut base = if id == 0 { + "main".to_string() + } else { + let sign = if id < 0 { "_" } else { "" }; + format!(".{sign}{}", id.abs()) + }; + + base.push('.'); + base += label; + base +} + +pub fn get_local_address(addr: isize) -> String { + let sign = if addr >= 0 { "+" } else { "" }; + format!("rbp{sign}{addr}") +} + +pub fn compile_user_function(c_function: &UserFunction) -> String { + let mut output = Output::new_with_name(c_function.id, &c_function.name); + output.push("push rbp"); + output.push("mov rbp, rsp"); + let aligned_local_size = align(c_function.local_variable_size, 16); + + output.push(&format!("sub rsp, {}", aligned_local_size)); + + let mut last_return = false; + for line in &c_function.lines { + last_return = false; + match line { + Line::ReturnCall(function, _start_addr, local_args, ret_size, return_addr) => { + #[cfg(debug_assertions)] + output.push(&format!( + "; [return call] {} , {:?}, {}", + *function, local_args, *return_addr + )); + + let sum = + local_args.iter().map(|x| align(x.1, 8)).sum::() + align(*ret_size, 8); + let mut t = 0usize; + + // Ensure 16-byte alignment + // #[cfg(debug_assertions)] + // output.push("; alignment"); + // if sum % 16 != 0 { + // let adjustment = 16 - (sum % 16); + // output.push(&format!("sub rsp, {adjustment}")); + // sum += adjustment; + // } + + // Push args to stack + for (local_addr, size) in local_args.iter().rev() { + let mut local_addr = *local_addr; + let mut size = *size as isize; + local_addr += size; + local_addr -= 8; + while size > 0 { + t += 8; + output.push("sub rsp, 8"); + output.push(&format!( + "mov rax, qword [{}]", + get_local_address(local_addr) + )); + output.push(&format!( + "mov qword [{}], rax", + get_local_address(-(aligned_local_size as isize) - t as isize) + )); + local_addr -= 8; + size -= 8; + } + } + + // Allocate return space + for _ in 0..ret_size.div_ceil(8) { + output.push("sub rsp, 8"); + t += 8; + } + + // Call + output.push(&format!("call {}", get_function_name(*function))); + + // Move return value + local_copy( + &mut output, + -(aligned_local_size as isize) - t as isize, + *return_addr, + *ret_size, + ); + + // Release stack space used + output.push(&format!("add rsp, {}", sum)); + } + Line::NoReturnCall(function, _start_addr, local_args, ret_size) => { + #[cfg(debug_assertions)] + output.push(&format!( + "; [no return call] {} , {:?}", + *function, local_args + )); + + let sum = + local_args.iter().map(|x| align(x.1, 8)).sum::() + align(*ret_size, 8); + let mut t = 0usize; + + // Ensure 16-byte alignment + // #[cfg(debug_assertions)] + // output.push("; alignment"); + // if sum % 16 != 0 { + // let adjustment = 16 - (sum % 16); + // output.push(&format!("sub rsp, {adjustment}")); + // sum += adjustment; + // } + + // Push args to stack + for (local_addr, size) in local_args.iter().rev() { + let mut local_addr = *local_addr; + let mut size = *size as isize; + local_addr += size; + local_addr -= 8; + while size > 0 { + t += 8; + output.push("sub rsp, 8"); + output.push(&format!( + "mov rax, qword [{}]", + get_local_address(local_addr) + )); + output.push(&format!( + "mov qword [{}], rax", + get_local_address(-(aligned_local_size as isize) - t as isize) + )); + local_addr -= 8; + size -= 8; + } + } + + // Allocate return space + for _ in 0..ret_size.div_ceil(8) { + output.push("sub rsp, 8"); + t += 8; + } + + // Call + output.push(&format!("call {}", get_function_name(*function))); + + // Move return value + // local_copy(&mut output, -(c_function.local_variable_size as isize) - sum as isize, *return_addr, *ret_size); + + // Release stack space used + output.push(&format!("add rsp, {}", sum)); + } + Line::Copy(local_from, local_to, amount) => { + local_copy(&mut output, *local_from, *local_to, *amount); + } + Line::DynFromCopy(local_dyn_from, local_to, amount) => { + #[cfg(debug_assertions)] + output.push(&format!( + "; [dyn from copy] {} , {}, {}", + *local_dyn_from, *local_to, *amount + )); + let mut done = 0; + output.push(&format!( + "mov r9, qword [{}]", + get_local_address(*local_dyn_from) + )); + while done < *amount { + output.push(&format!("mov rax, qword [r9+{}]", done)); + output.push(&format!( + "mov qword [{}], rax", + get_local_address(*local_to + (done as isize)) + )); + done += 8; + } + } + Line::DynToCopy(local_from, local_dyn_to, amount) => { + #[cfg(debug_assertions)] + output.push(&format!( + "; [dyn to copy] {} , {}, {}", + *local_from, *local_dyn_to, *amount + )); + let mut done = 0; + output.push(&format!( + "mov r9, qword [{}]", + get_local_address(*local_dyn_to) + )); + while done < *amount { + output.push(&format!( + "mov rax, qword [{}]", + get_local_address(*local_from + (done as isize)) + )); + output.push(&format!("mov qword [r9+{}], rax", done)); + done += 8; + } + } + Line::Return(local_return_val) => { + #[cfg(debug_assertions)] + output.push(&format!("; [return] {:?}", *local_return_val)); + last_return = true; + if c_function.id == 0 { + output.push(&format!( + "mov rcx, qword [{}]", + get_local_address(local_return_val.unwrap().0) + )); + output.push("call ExitProcess"); + } else { + if let Some(val) = local_return_val { + local_copy(&mut output, val.0, 16, val.1); + } + output.push("leave"); + output.push("ret"); + } + } + Line::HeapAlloc(amount, local_ref_addr) => { + #[cfg(debug_assertions)] + output.push(&format!("; [heap alloc] {} , {}", *amount, *local_ref_addr)); + output.push("sub rsp, 32"); + output.push("call GetProcessHeap"); // Get process heap + output.push("mov rcx, rax"); // Heap handle + output.push("mov rdx, 0"); // Flags + output.push(&format!("mov r8, {}", *amount)); + output.push("call HeapAlloc"); + output.push(&format!( + "mov qword [{}], rax", + get_local_address(*local_ref_addr) + )); + output.push("add rsp, 32"); + } + Line::HeapDealloc(local_ref_addr, local_success_bool) => { + #[cfg(debug_assertions)] + output.push(&format!( + "; [heap dealloc] {} , {}", + *local_ref_addr, *local_success_bool + )); + output.push("call GetProcessHeap"); // Get process heap + output.push("mov rcx, rax"); // Heap handle + output.push("mov rdx, 0"); // Flags + output.push(&format!( + "mov r8, qword [{}]", + get_local_address(*local_ref_addr) + )); + output.push("call HeapFree"); + output.push("cmp rax, 0"); + output.push("mov rcx, 0"); + output.push("setz cl"); + output.push(&format!( + "mov qword [{}], rcx", + get_local_address(*local_success_bool) + )) + } + Line::InlineAsm(asm) => { + #[cfg(debug_assertions)] + output.push("; [inline asm]"); + for line in asm { + output.push(line); + } + } + #[cfg(debug_assertions)] + Line::Annotation(annotation) => { + output.push(&format!("; '{}'", annotation)); + } + } + } + + if last_return { + return output.into(); + } + + if c_function.id == 0 { + output.push("mov rcx, 0"); + output.push("call ExitProcess"); + output.into() + } else { + output.push("leave"); + output.push("ret"); + output.into() + } +} + +fn local_copy(output: &mut Output, local_from: isize, local_to: isize, amount: usize) { + #[cfg(debug_assertions)] + output.push(&format!( + "; [local copy] {} , {}, {}", + local_from, local_to, amount + )); + let mut done = 0; + while done < amount { + output.push(&format!( + "mov rax, qword [{}]", + get_local_address(local_from + (done as isize)) + )); + output.push(&format!( + "mov qword [{}], rax", + get_local_address(local_to + (done as isize)) + )); + done += 8; + } +} diff --git a/src/root/compiler/local_variable.rs b/src/root/compiler/local_variable.rs new file mode 100644 index 0000000..1a215f1 --- /dev/null +++ b/src/root/compiler/local_variable.rs @@ -0,0 +1,27 @@ +#[derive(Eq, PartialEq, Clone, Copy, Debug)] +pub struct TypeInfo { + pub type_id: isize, + pub reference_depth: usize +} + +impl TypeInfo { + pub fn new(type_id: isize, reference_depth: usize) -> TypeInfo { + TypeInfo { type_id, reference_depth } + } +} + +#[derive(Eq, PartialEq, Clone, Copy, Debug)] +pub struct LocalVariable { + pub offset: isize, + pub type_info: TypeInfo +} + +impl LocalVariable { + pub fn new(offset: isize, type_id: isize, reference_depth: usize) -> LocalVariable { + LocalVariable { offset, type_info: TypeInfo { type_id, reference_depth } } + } + + pub fn from_type_info(offset: isize, type_info: TypeInfo) -> LocalVariable { + LocalVariable { offset, type_info } + } +} \ No newline at end of file diff --git a/src/root/compiler/mod.rs b/src/root/compiler/mod.rs new file mode 100644 index 0000000..4eb98c8 --- /dev/null +++ b/src/root/compiler/mod.rs @@ -0,0 +1,3 @@ +pub mod compile_functions; +pub mod generate_asm; +pub mod local_variable; diff --git a/src/root/custom/functions/mod.rs b/src/root/custom/functions/mod.rs new file mode 100644 index 0000000..858758c --- /dev/null +++ b/src/root/custom/functions/mod.rs @@ -0,0 +1,2 @@ +pub mod windows; +pub mod print; \ No newline at end of file diff --git a/src/root/custom/functions/print.rs b/src/root/custom/functions/print.rs new file mode 100644 index 0000000..80c13a9 --- /dev/null +++ b/src/root/custom/functions/print.rs @@ -0,0 +1,236 @@ +use lazy_static::lazy_static; +use unique_type_id::UniqueTypeId; + +use crate::root::basic_ast::symbol::BasicSymbol; +use crate::root::compiler::compile_functions::{Function, Line, UserFunction}; +use crate::root::compiler::generate_asm::{compile_user_function, get_function_sublabel}; +use crate::root::compiler::local_variable::TypeInfo; +use crate::root::custom::types::bool::Bool; +use crate::root::custom::types::float::Float; +use crate::root::custom::types::int::Int; +use crate::root::parser::line_info::LineInfo; +use crate::root::name_resolver::type_builder::TypedFunction; + +pub fn add_function_signatures(existing: &mut Vec<(Option, Box)>) { + let signatures: [(Option, Box); 3] = [ + (None, Box::new(PrintI {})), + (None, Box::new(PrintB {})), + (None, Box::new(PrintF {})), + ]; + + for s in signatures { + existing.push(s); + } +} + +pub fn add_function_implementations(existing: &mut Vec>) { + let functions: [Box; 3] = [ + Box::new(PrintI {}), + Box::new(PrintB {}), + Box::new(PrintF{}) + ]; + + for s in functions { + existing.push(s); + } +} + + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct PrintI {} +lazy_static! { + static ref PRINT_I_ARGS: [(String, TypeInfo); 1] = + [(String::from("integer"), TypeInfo::new(Int::get_id(), 0))]; +} +impl TypedFunction for PrintI { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "printi" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + PRINT_I_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + None + } + + fn is_inline(&self) -> bool { + false + } + + fn contents(&self) -> &Vec<(BasicSymbol, LineInfo)> { + panic!() + } + + fn take_contents(&mut self) -> Vec<(BasicSymbol, LineInfo)> { + panic!() + } + + fn get_inline(&self, _args: Vec) -> Vec { + panic!() + } +} + +impl Function for PrintI { + fn get_asm(&self) -> String { + compile_user_function(&UserFunction { + id: TypedFunction::get_id(self), + local_variable_size: 8, + arg_count: 1, + lines: vec![Line::InlineAsm(vec![ + "mov dword [rbp-4], 0x000a".to_string(), + "mov dword [rbp-8], 0x646C6C25".to_string(), + "mov rcx, rbp".to_string(), + "sub rcx, 8".to_string(), + "mov rdx, qword [rbp+16]".to_string(), + "sub rsp, 40".to_string(), + "call printf".to_string(), + "add rsp, 40".to_string() + ])], + name: "printi".to_string(), + }) + } + + fn get_id(&self) -> isize { + TypedFunction::get_id(self) + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct PrintB {} +lazy_static! { + static ref PRINT_B_ARGS: [(String, TypeInfo); 1] = + [(String::from("bool"), TypeInfo::new(Bool::get_id(), 0))]; +} +impl TypedFunction for PrintB { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "printb" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + PRINT_B_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + None + } + + fn is_inline(&self) -> bool { + false + } +} + +impl Function for PrintB { + fn get_asm(&self) -> String { + compile_user_function(&UserFunction { + id: TypedFunction::get_id(self), + local_variable_size: 32, + arg_count: 1, + lines: vec![Line::InlineAsm(vec![ + "mov dword [rbp-8], 0x65757274".to_string(), + "mov dword [rbp-4], 0x0D0A".to_string(), + "mov rax, qword [rbp+16]".to_string(), + "cmp rax, 0".to_string(), + format!( + "jz {}", + get_function_sublabel(TypedFunction::get_id(self), "true") + ), + "mov dword [rbp-8], 0x736C6166".to_string(), + "mov dword [rbp-4], 0x0D0A65".to_string(), + format!( + "{}:", + get_function_sublabel(TypedFunction::get_id(self), "true") + ), + "mov rcx, rbp".to_string(), + "sub rcx, 8".to_string(), + "mov rdx, qword [rbp+16]".to_string(), + "sub rsp, 40".to_string(), + "call printf".to_string(), + "add rsp, 40".to_string() + ])], + name: "printb".to_string(), + }) + } + + fn get_id(&self) -> isize { + TypedFunction::get_id(self) + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct PrintF {} +lazy_static! { + static ref PRINT_F_ARGS: [(String, TypeInfo); 1] = + [(String::from("float"), TypeInfo::new(Float::get_id(), 0))]; +} +impl TypedFunction for PrintF { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "printf" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + PRINT_F_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + None + } + + fn is_inline(&self) -> bool { + false + } +} + +impl Function for PrintF { + fn get_asm(&self) -> String { + compile_user_function(&UserFunction { + id: TypedFunction::get_id(self), + local_variable_size: 8, + arg_count: 1, + lines: vec![Line::InlineAsm(vec![ + "mov dword [rbp-4], 0x00".to_string(), + "mov dword [rbp-8], 0x0a664C25".to_string(), + "mov rcx, rbp".to_string(), + "sub rcx, 8".to_string(), + "movsd xmm1, qword [rbp+16]".to_string(), + "mov rdx, qword [rbp+16]".to_string(), + "sub rsp, 40".to_string(), + "call printf".to_string(), + "add rsp, 40".to_string() + ])], + name: "printf".to_string(), + }) + } + + fn get_id(&self) -> isize { + TypedFunction::get_id(self) + } +} diff --git a/src/root/custom/functions/windows.rs b/src/root/custom/functions/windows.rs new file mode 100644 index 0000000..3aea3a6 --- /dev/null +++ b/src/root/custom/functions/windows.rs @@ -0,0 +1,67 @@ +use lazy_static::lazy_static; +use unique_type_id::UniqueTypeId; +use crate::root::basic_ast::symbol::BasicSymbol; +use crate::root::compiler::generate_asm::get_local_address; +use crate::root::compiler::local_variable::TypeInfo; +use crate::root::custom::types::int::Int; +use crate::root::parser::line_info::LineInfo; +use crate::root::name_resolver::type_builder::TypedFunction; + +pub fn add_function_signatures(existing: &mut Vec<(Option, Box)>) { + let signatures: [(Option, Box); 1] = [ + (None, Box::new(WindowsExit {})), + ]; + + for s in signatures { + existing.push(s); + } +} + + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct WindowsExit {} +lazy_static! { + static ref WINDOWS_EXIT_ARGS: [(String, TypeInfo); 1] = + [(String::from("exit_code"), TypeInfo::new(Int::get_id(), 0))]; +} +impl TypedFunction for WindowsExit { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "exit" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + WINDOWS_EXIT_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + None + } + + fn is_inline(&self) -> bool { + true + } + + fn contents(&self) -> &Vec<(BasicSymbol, LineInfo)> { + panic!() + } + + fn take_contents(&mut self) -> Vec<(BasicSymbol, LineInfo)> { + panic!() + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("mov rcx, qword [{}]", get_local_address(args[0])), + "call ExitProcess".to_string(), + ] + } +} diff --git a/src/root/custom/get.rs b/src/root/custom/get.rs new file mode 100644 index 0000000..3a942b3 --- /dev/null +++ b/src/root/custom/get.rs @@ -0,0 +1,25 @@ +use crate::root::compiler::compile_functions::Function; +use crate::root::custom::functions::{print, windows}; +use crate::root::custom::types::{bool, float, int}; +use crate::root::name_resolver::type_builder::TypedFunction; + +pub fn get_custom_function_signatures() -> Vec<(Option, Box)> { + let mut signatures = Vec::new(); + + bool::add_function_signatures(&mut signatures); + int::add_function_signatures(&mut signatures); + float::add_function_signatures(&mut signatures); + + windows::add_function_signatures(&mut signatures); + print::add_function_signatures(&mut signatures); + + signatures +} + +pub fn get_custom_function_implementations() -> Vec> { + let mut functions = Vec::new(); + + print::add_function_implementations(&mut functions); + + functions +} diff --git a/src/root/custom/mod.rs b/src/root/custom/mod.rs new file mode 100644 index 0000000..0a805a4 --- /dev/null +++ b/src/root/custom/mod.rs @@ -0,0 +1,3 @@ +pub mod types; +mod functions; +pub mod get; diff --git a/src/root/custom/types/bool.rs b/src/root/custom/types/bool.rs new file mode 100644 index 0000000..b1ce6db --- /dev/null +++ b/src/root/custom/types/bool.rs @@ -0,0 +1,208 @@ +use lazy_static::lazy_static; +use unique_type_id::UniqueTypeId; + +use crate::root::ast::literals::Literal; +use crate::root::compiler::generate_asm::get_local_address; +use crate::root::compiler::local_variable::TypeInfo; +use crate::root::parser::line_info::LineInfo; +use crate::root::name_resolver::processor::ProcessorError; +use crate::root::name_resolver::type_builder::{Type, TypedFunction, TypeTable}; + +pub fn add_function_signatures(existing: &mut Vec<(Option, Box)>) { + let signatures: [(Option, Box); 3] = [ + (Some(Bool::get_id()), Box::new(BoolNot {})), + (Some(Bool::get_id()), Box::new(BoolEQ {})), + (Some(Bool::get_id()), Box::new(BoolNE {})) + ]; + for s in signatures { + existing.push(s); + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct Bool {} + +impl Bool { + pub fn new() -> Bool { + Bool {} + } + pub fn get_id() -> isize { + -(Self::id().0 as isize) - 1 + } +} + +impl Type for Bool { + fn get_id(&self) -> isize { + Self::get_id() + } + + fn get_name(&self) -> &str { + "bool" + } + + fn get_size( + &self, + _type_table: &TypeTable, + _path: Option>, + ) -> Result { + Ok(8) + } + + fn instantiate( + &self, + literal: Option<&Literal>, + local_address: isize, + ) -> Result, ProcessorError> { + if literal.is_none() { + return Ok(vec![]); + } + let Literal::Bool(val) = literal.unwrap() else { + panic!() + }; + + if *val { + Ok(vec![format!( + "mov qword [{}], 0", + get_local_address(local_address) + )]) + } else { + Ok(vec![format!( + "mov qword [{}], 1", + get_local_address(local_address) + )]) + } + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct BoolNot {} +lazy_static! { + static ref BOOL_NOT_ARGS: [(String, TypeInfo); 1] = + [(String::from("lhs"), TypeInfo::new(Bool::get_id(), 0))]; +} +impl TypedFunction for BoolNot { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "not" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + BOOL_NOT_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Bool::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("mov rax, qword [{}]", get_local_address(args[0])), + "cmp rax, 0".to_string(), + "setz al".to_string(), + format!("mov qword [{}], rax", get_local_address(args[1])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct BoolEQ {} +lazy_static! { + static ref BOOL_EQ_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Bool::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Bool::get_id(), 0)) + ]; +} +impl TypedFunction for BoolEQ { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "eq" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + BOOL_EQ_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Bool::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("mov rax, qword [{}]", get_local_address(args[0])), + format!("mov rcx, [{}]", get_local_address(args[1])), + "cmp rcx, rax".to_string(), + format!("mov qword [{}], 0", get_local_address(args[2])), + format!("setnz [{}]", get_local_address(args[2])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct BoolNE {} +lazy_static! { + static ref BOOL_NE_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Bool::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Bool::get_id(), 0)) + ]; +} +impl TypedFunction for BoolNE { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "ne" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + BOOL_NE_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Bool::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("mov rax, qword [{}]", get_local_address(args[0])), + format!("mov rcx, [{}]", get_local_address(args[1])), + "cmp rcx, rax".to_string(), + format!("mov qword [{}], 0", get_local_address(args[2])), + format!("setz [{}]", get_local_address(args[2])), + ] + } +} diff --git a/src/root/custom/types/float.rs b/src/root/custom/types/float.rs new file mode 100644 index 0000000..3d0ed23 --- /dev/null +++ b/src/root/custom/types/float.rs @@ -0,0 +1,514 @@ +use lazy_static::lazy_static; +use unique_type_id::UniqueTypeId; + +use crate::root::ast::literals::Literal; +use crate::root::compiler::generate_asm::get_local_address; +use crate::root::compiler::local_variable::TypeInfo; +use crate::root::custom::types::bool::Bool; +use crate::root::parser::line_info::LineInfo; +use crate::root::name_resolver::processor::ProcessorError; +use crate::root::name_resolver::type_builder::{Type, TypedFunction, TypeTable}; + +pub fn add_function_signatures(existing: &mut Vec<(Option, Box)>) { + let signatures: [(Option, Box); 10] = [ + (Some(Float::get_id()), Box::new(FloatAdd {})), + (Some(Float::get_id()), Box::new(FloatSub {})), + (Some(Float::get_id()), Box::new(FloatMul {})), + (Some(Float::get_id()), Box::new(FloatDiv {})), + (Some(Float::get_id()), Box::new(FloatLT {})), + (Some(Float::get_id()), Box::new(FloatGT {})), + (Some(Float::get_id()), Box::new(FloatLE {})), + (Some(Float::get_id()), Box::new(FloatGE {})), + (Some(Float::get_id()), Box::new(FloatEQ {})), + (Some(Float::get_id()), Box::new(FloatNE {})) + ]; + for s in signatures { + existing.push(s); + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct Float {} + +impl Float { + pub fn new() -> Float { + Float {} + } + + pub fn get_id() -> isize { + -(Self::id().0 as isize) - 1 + } +} + +impl Type for Float { + fn get_id(&self) -> isize { + Self::get_id() + } + + fn get_name(&self) -> &str { + "float" + } + + fn get_size( + &self, + _type_table: &TypeTable, + _path: Option>, + ) -> Result { + Ok(8) + } + + fn instantiate( + &self, + literal: Option<&Literal>, + local_address: isize, + ) -> Result, ProcessorError> { + if literal.is_none() { + return Ok(vec![]); + } + let Literal::Float(val) = literal.unwrap() else { + panic!() + }; + + Ok(vec![ + format!("mov rax, __float64__({:?})", *val), + format!("mov qword [{}], rax", get_local_address(local_address)), + ]) + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct FloatAdd {} +lazy_static! { + static ref FLOAT_ADD_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Float::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Float::get_id(), 0)) + ]; +} +impl TypedFunction for FloatAdd { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "add" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + FLOAT_ADD_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Float::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("movsd xmm0, qword [{}]", get_local_address(args[0])), + format!("addsd xmm0, qword [{}]", get_local_address(args[1])), + format!("movsd qword [{}], xmm0", get_local_address(args[2])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct FloatSub {} +lazy_static! { + static ref FLOAT_SUB_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Float::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Float::get_id(), 0)) + ]; +} +impl TypedFunction for FloatSub { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "sub" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + FLOAT_SUB_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Float::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("movsd xmm0, qword [{}]", get_local_address(args[0])), + format!("subsd xmm0, qword [{}]", get_local_address(args[1])), + format!("movsd qword [{}], xmm0", get_local_address(args[2])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct FloatMul {} +lazy_static! { + static ref FLOAT_MUL_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Float::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Float::get_id(), 0)) + ]; +} +impl TypedFunction for FloatMul { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "mul" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + FLOAT_SUB_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Float::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("movsd xmm0, qword [{}]", get_local_address(args[0])), + format!("mulsd xmm0, qword [{}]", get_local_address(args[1])), + format!("movsd qword [{}], xmm0", get_local_address(args[2])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct FloatDiv {} +lazy_static! { + static ref FLOAT_DIV_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Float::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Float::get_id(), 0)) + ]; +} +impl TypedFunction for FloatDiv { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "div" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + FLOAT_SUB_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Float::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("movsd xmm0, qword [{}]", get_local_address(args[0])), + format!("divsd xmm0, qword [{}]", get_local_address(args[1])), + format!("movsd qword [{}], xmm0", get_local_address(args[2])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct FloatLT {} +lazy_static! { + static ref FLOAT_LT_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Float::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Float::get_id(), 0)) + ]; +} +impl TypedFunction for FloatLT { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "lt" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + FLOAT_LT_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Bool::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("movsd xmm0, qword [{}]", get_local_address(args[0])), + format!("ucomisd xmm0, qword [{}]", get_local_address(args[1])), + format!("mov qword [{}], 0", get_local_address(args[2])), + format!("seta [{}]", get_local_address(args[2])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct FloatGT {} +lazy_static! { + static ref FLOAT_GT_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Float::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Float::get_id(), 0)) + ]; +} +impl TypedFunction for FloatGT { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "gt" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + FLOAT_GT_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Bool::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("movsd xmm0, qword [{}]", get_local_address(args[0])), + format!("ucomisd xmm0, qword [{}]", get_local_address(args[1])), + format!("mov qword [{}], 0", get_local_address(args[2])), + format!("setb [{}]", get_local_address(args[2])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct FloatLE {} +lazy_static! { + static ref FLOAT_LE_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Float::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Float::get_id(), 0)) + ]; +} +impl TypedFunction for FloatLE { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "le" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + FLOAT_LE_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Bool::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("movsd xmm0, qword [{}]", get_local_address(args[0])), + format!("ucomisd xmm0, qword [{}]", get_local_address(args[1])), + format!("mov qword [{}], 0", get_local_address(args[2])), + format!("setae [{}]", get_local_address(args[2])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct FloatGE {} +lazy_static! { + static ref FLOAT_GE_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Float::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Float::get_id(), 0)) + ]; +} +impl TypedFunction for FloatGE { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "ge" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + FLOAT_GE_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Bool::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("movsd xmm0, qword [{}]", get_local_address(args[0])), + format!("ucomisd xmm0, qword [{}]", get_local_address(args[1])), + format!("mov qword [{}], 0", get_local_address(args[2])), + format!("setbe [{}]", get_local_address(args[2])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct FloatEQ {} +lazy_static! { + static ref FLOAT_EQ_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Float::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Float::get_id(), 0)) + ]; +} +impl TypedFunction for FloatEQ { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "eq" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + FLOAT_EQ_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Bool::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("movsd xmm0, qword [{}]", get_local_address(args[0])), + format!("ucomisd xmm0, qword [{}]", get_local_address(args[1])), + format!("mov qword [{}], 0", get_local_address(args[2])), + format!("setne [{}]", get_local_address(args[2])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct FloatNE {} +lazy_static! { + static ref FLOAT_NE_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Float::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Float::get_id(), 0)) + ]; +} +impl TypedFunction for FloatNE { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "ne" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + FLOAT_NE_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Bool::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("movsd xmm0, qword [{}]", get_local_address(args[0])), + format!("ucomisd xmm0, qword [{}]", get_local_address(args[1])), + format!("mov qword [{}], 0", get_local_address(args[2])), + format!("sete [{}]", get_local_address(args[2])), + ] + } +} \ No newline at end of file diff --git a/src/root/custom/types/int.rs b/src/root/custom/types/int.rs new file mode 100644 index 0000000..5e7e430 --- /dev/null +++ b/src/root/custom/types/int.rs @@ -0,0 +1,594 @@ +use lazy_static::lazy_static; +use unique_type_id::UniqueTypeId; + +use crate::root::ast::literals::Literal; +use crate::root::compiler::generate_asm::get_local_address; +use crate::root::compiler::local_variable::TypeInfo; +use crate::root::custom::types::bool::Bool; +use crate::root::parser::line_info::LineInfo; +use crate::root::name_resolver::processor::ProcessorError; +use crate::root::name_resolver::type_builder::{Type, TypedFunction, TypeTable}; + +pub fn add_function_signatures(existing: &mut Vec<(Option, Box)>) { + let signatures: [(Option, Box); 11] = [ + (Some(Int::get_id()), Box::new(IntAdd {})), + (Some(Int::get_id()), Box::new(IntSub {})), + (Some(Int::get_id()), Box::new(IntMul {})), + (Some(Int::get_id()), Box::new(IntDiv {})), + (Some(Int::get_id()), Box::new(IntMod {})), + (Some(Int::get_id()), Box::new(IntLT {})), + (Some(Int::get_id()), Box::new(IntGT {})), + (Some(Int::get_id()), Box::new(IntLE {})), + (Some(Int::get_id()), Box::new(IntGE {})), + (Some(Int::get_id()), Box::new(IntEQ {})), + (Some(Int::get_id()), Box::new(IntNE {})) + ]; + + for s in signatures { + existing.push(s); + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct Int {} + +impl Int { + pub fn new() -> Int { + Int {} + } + + pub fn get_id() -> isize { + -(Self::id().0 as isize) - 1 + } +} + +impl Int { + pub fn instantiate_local_ref(offset: isize, local_address: isize) -> Vec { + vec![ + "mov rax, rbp".to_string(), + format!("add rax, {offset}"), + format!("mov qword [{}], rax", get_local_address(local_address),), + ] + } + + pub fn instantiate_ref(base_variable: isize, offset: isize, ref_address: isize) -> Vec { + vec![ + format!("mov rax, qword [{}]", get_local_address(base_variable)), + format!("add rax, {offset}"), + format!("mov qword [{}], rax", get_local_address(ref_address),), + ] + } +} + +impl Type for Int { + fn get_id(&self) -> isize { + Self::get_id() + } + + fn get_name(&self) -> &str { + "int" + } + + fn get_size( + &self, + _type_table: &TypeTable, + _path: Option>, + ) -> Result { + Ok(8) + } + + fn instantiate( + &self, + literal: Option<&Literal>, + local_address: isize, + ) -> Result, ProcessorError> { + if literal.is_none() { + return Ok(vec![]); + } + let Literal::Int(val) = literal.unwrap() else { + panic!() + }; + + // ? Hacky workaround as NASM doesn't appear to support 64-bit literals + let hex_str = format!("{:016x}", *val as i64); + let upper = &hex_str[..8]; + let lower = &hex_str[8..]; + + Ok(vec![ + format!("mov dword [{}], 0x{}", get_local_address(local_address), lower), + format!("mov dword [{}], 0x{}", get_local_address(local_address + 4), upper), + ]) + } +} + + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct IntAdd {} +lazy_static! { + static ref INT_ADD_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Int::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Int::get_id(), 0)) + ]; +} +impl TypedFunction for IntAdd { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "add" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + INT_ADD_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Int::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("mov rax, qword [{}]", get_local_address(args[0])), + format!("add rax, [{}]", get_local_address(args[1])), + format!("mov qword [{}], rax", get_local_address(args[2])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct IntSub {} +lazy_static! { + static ref INT_SUB_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Int::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Int::get_id(), 0)) + ]; +} +impl TypedFunction for IntSub { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "sub" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + INT_SUB_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Int::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("mov rax, qword [{}]", get_local_address(args[0])), + format!("sub rax, [{}]", get_local_address(args[1])), + format!("mov [{}], rax", get_local_address(args[2])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct IntMul {} +lazy_static! { + static ref INT_MUL_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Int::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Int::get_id(), 0)) + ]; +} +impl TypedFunction for IntMul { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "mul" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + INT_SUB_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Int::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("mov rax, qword [{}]", get_local_address(args[0])), + format!("mov rcx, [{}]", get_local_address(args[1])), + "mul rcx".to_string(), + format!("mov [{}], rax", get_local_address(args[2])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct IntDiv {} +lazy_static! { + static ref INT_DIV_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Int::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Int::get_id(), 0)) + ]; +} +impl TypedFunction for IntDiv { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "div" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + INT_SUB_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Int::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("mov rax, qword [{}]", get_local_address(args[0])), + format!("mov rcx, [{}]", get_local_address(args[1])), + "cqo".to_string(), + "idiv rcx".to_string(), + format!("mov [{}], rax", get_local_address(args[2])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct IntMod {} +lazy_static! { + static ref INT_MOD_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Int::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Int::get_id(), 0)) + ]; +} +impl TypedFunction for IntMod { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "mod" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + INT_MOD_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Int::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("mov rax, qword [{}]", get_local_address(args[0])), + format!("mov rcx, [{}]", get_local_address(args[1])), + "cqo".to_string(), + "idiv rcx".to_string(), + format!("mov [{}], rdx", get_local_address(args[2])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct IntLT {} +lazy_static! { + static ref INT_LT_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Int::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Int::get_id(), 0)) + ]; +} +impl TypedFunction for IntLT { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "lt" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + INT_LT_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Bool::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("mov rax, qword [{}]", get_local_address(args[0])), + format!("mov rcx, [{}]", get_local_address(args[1])), + "cmp rcx, rax".to_string(), + format!("mov qword [{}], 0", get_local_address(args[2])), + format!("setle [{}]", get_local_address(args[2])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct IntGT {} +lazy_static! { + static ref INT_GT_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Int::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Int::get_id(), 0)) + ]; +} +impl TypedFunction for IntGT { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "gt" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + INT_GT_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Bool::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("mov rax, qword [{}]", get_local_address(args[0])), + format!("mov rcx, [{}]", get_local_address(args[1])), + "cmp rax, rcx".to_string(), + format!("mov qword [{}], 0", get_local_address(args[2])), + format!("setle [{}]", get_local_address(args[2])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct IntLE {} +lazy_static! { + static ref INT_LE_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Int::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Int::get_id(), 0)) + ]; +} +impl TypedFunction for IntLE { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "le" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + INT_LE_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Bool::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("mov rax, qword [{}]", get_local_address(args[0])), + format!("mov rcx, [{}]", get_local_address(args[1])), + "cmp rax, rcx".to_string(), + format!("mov qword [{}], 0", get_local_address(args[2])), + format!("setnle [{}]", get_local_address(args[2])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct IntGE {} +lazy_static! { + static ref INT_GE_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Int::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Int::get_id(), 0)) + ]; +} +impl TypedFunction for IntGE { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "ge" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + INT_GE_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Bool::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("mov rax, qword [{}]", get_local_address(args[0])), + format!("mov rcx, [{}]", get_local_address(args[1])), + "cmp rcx, rax".to_string(), + format!("mov qword [{}], 0", get_local_address(args[2])), + format!("setnle [{}]", get_local_address(args[2])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct IntEQ {} +lazy_static! { + static ref INT_EQ_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Int::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Int::get_id(), 0)) + ]; +} +impl TypedFunction for IntEQ { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "eq" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + INT_EQ_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Bool::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("mov rax, qword [{}]", get_local_address(args[0])), + format!("mov rcx, [{}]", get_local_address(args[1])), + "cmp rcx, rax".to_string(), + format!("mov qword [{}], 0", get_local_address(args[2])), + format!("setnz [{}]", get_local_address(args[2])), + ] + } +} + +#[derive(UniqueTypeId)] +#[UniqueTypeIdType = "u16"] +pub struct IntNE {} +lazy_static! { + static ref INT_NE_ARGS: [(String, TypeInfo); 2] = [ + (String::from("lhs"), TypeInfo::new(Int::get_id(), 0)), + (String::from("rhs"), TypeInfo::new(Int::get_id(), 0)) + ]; +} +impl TypedFunction for IntNE { + fn get_id(&self) -> isize { + -(Self::id().0 as isize) - 1 + } + + fn get_name(&self) -> &str { + "ne" + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + INT_NE_ARGS.as_ref() + } + + fn get_line(&self) -> LineInfo { + LineInfo::builtin() + } + + fn get_return_type(&self) -> Option { + Some(TypeInfo::new(Bool::get_id(), 0)) + } + + fn is_inline(&self) -> bool { + true + } + + fn get_inline(&self, args: Vec) -> Vec { + vec![ + format!("mov rax, qword [{}]", get_local_address(args[0])), + format!("mov rcx, [{}]", get_local_address(args[1])), + "cmp rcx, rax".to_string(), + format!("mov qword [{}], 0", get_local_address(args[2])), + format!("setz [{}]", get_local_address(args[2])), + ] + } +} diff --git a/src/root/custom/types/mod.rs b/src/root/custom/types/mod.rs new file mode 100644 index 0000000..6a37252 --- /dev/null +++ b/src/root/custom/types/mod.rs @@ -0,0 +1,3 @@ +pub mod float; +pub mod bool; +pub mod int; \ No newline at end of file diff --git a/src/root/name_resolver/custom_types.rs b/src/root/name_resolver/custom_types.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/root/name_resolver/custom_types.rs @@ -0,0 +1 @@ + diff --git a/src/root/name_resolver/mod.rs b/src/root/name_resolver/mod.rs new file mode 100644 index 0000000..02c2bd5 --- /dev/null +++ b/src/root/name_resolver/mod.rs @@ -0,0 +1,5 @@ +pub mod custom_types; +mod preprocess; +pub mod processor; +pub mod type_builder; +pub mod user_type; diff --git a/src/root/name_resolver/preprocess.rs b/src/root/name_resolver/preprocess.rs new file mode 100644 index 0000000..cd4533a --- /dev/null +++ b/src/root/name_resolver/preprocess.rs @@ -0,0 +1,353 @@ +use crate::root::ast::keywords::Keyword; +use crate::root::basic_ast::punctuation::Punctuation; +use crate::root::basic_ast::symbol::{BasicAbstractSyntaxTree, BasicSymbol, NameType}; + +use crate::root::parser::line_info::LineInfo; +use crate::root::name_resolver::processor::ProcessorError; + +use std::vec::IntoIter; + +pub type PreProcessFunction = ( + String, + Vec<(String, LineInfo, String, usize, LineInfo)>, + Option<((String, usize), LineInfo)>, + Vec<(BasicSymbol, LineInfo)>, +); + +#[derive(Clone, strum_macros::Display, Debug)] +pub enum PreprocessSymbol { + Struct( + LineInfo, + String, + Vec<(String, LineInfo, (String, usize), LineInfo)>, + ), + Impl(LineInfo, String, Vec<(PreProcessFunction, LineInfo)>), + Fn(LineInfo, PreProcessFunction), +} + +pub fn preprocess( + ast: Vec, +) -> Result, ProcessorError> { + let mut output = Vec::new(); + + for tree in ast { + let mut tree = tree.into_iter(); + loop { + let next = tree.next(); + if next.is_none() { + break; + } + let (first_symbol, first_line) = next.unwrap(); + + match first_symbol { + BasicSymbol::Keyword(keyword) => match keyword { + Keyword::Struct => { + output.push(parse_struct(first_line, &mut tree)?); + } + Keyword::Impl => { + output.push(parse_impl(first_line, &mut tree)?); + } + Keyword::Fn => { + output.push(parse_fn(first_line, &mut tree, None)?); + } + _ => {} + }, + BasicSymbol::AbstractSyntaxTree(_) => panic!(), + _symbol => return Err(ProcessorError::BadTopLevelSymbol(first_line)), + } + } + } + + Ok(output) +} + +fn parse_struct( + start_line_info: LineInfo, + tree: &mut IntoIter<(BasicSymbol, LineInfo)>, +) -> Result { + let (name, name_line) = tree + .next() + .ok_or(ProcessorError::StructNoName(start_line_info.clone()))?; + let mut name = match name { + BasicSymbol::Name(name) => name, + _ => return Err(ProcessorError::StructNoName(name_line)), + }; + + if name.len() > 1 { + return Err(ProcessorError::MultipartNameDef(name_line)); + } + let name = name.remove(0); + + let (contents, contents_line) = tree + .next() + .ok_or(ProcessorError::StructNoBraces(name_line))?; + let contents = match contents { + BasicSymbol::BracedSection(contents) => contents, + _ => return Err(ProcessorError::StructNoBraces(contents_line)), + }; + let mut contents = contents.into_iter(); + + let mut attributes = Vec::new(); + let mut first = true; + + loop { + let mut first_item = contents.next(); + if first_item.is_none() { + break; + } + + if !first { + let (tmp_first_item, tmp_line) = first_item.unwrap(); + if !matches!( + tmp_first_item, + BasicSymbol::Punctuation(Punctuation::ListSeparator) + ) { + return Err(ProcessorError::StructNoAttrSeparator(tmp_line)); + } + first_item = contents.next(); + if first_item.is_none() { + break; + } + } + + let (attr_name, attr_name_line) = first_item.unwrap(); + let attr_name = match attr_name { + BasicSymbol::Name(mut name) => { + if name.len() > 1 { + return Err(ProcessorError::MultipartNameDef(attr_name_line)); + } + name.remove(0) + } + _ => return Err(ProcessorError::StructExpectedAttributeName(attr_name_line)), + }; + if attr_name.3 != 0 { + return Err(ProcessorError::NameWithRefPrefix(attr_name_line)); + } + let Some((colon, colon_line)) = contents.next() else { + return Err(ProcessorError::NameTypeNotDefined(attr_name_line)); + }; + if !matches!(colon, BasicSymbol::Punctuation(Punctuation::Colon)) { + return Err(ProcessorError::NameTypeNotDefined(colon_line)); + } + + let Some((attr_type, attr_type_line)) = contents.next() else { + return Err(ProcessorError::NameTypeNotDefined(colon_line)); + }; + let attr_type = match attr_type { + BasicSymbol::Name(mut name) => { + if name.len() > 1 { + return Err(ProcessorError::MultipartNameDef(attr_type_line)); + } + name.remove(0) + } + _ => return Err(ProcessorError::NameTypeNotDefined(attr_type_line)), + }; + + attributes.push(( + attr_name.0, + attr_name_line, + (attr_type.0, attr_type.3), + attr_type_line, + )); + first = false; + } + + Ok(PreprocessSymbol::Struct( + start_line_info, + name.0, + attributes, + )) +} + +fn parse_impl( + start_line_info: LineInfo, + tree: &mut IntoIter<(BasicSymbol, LineInfo)>, +) -> Result { + let (name, name_line) = tree + .next() + .ok_or(ProcessorError::ImplNoName(start_line_info.clone()))?; + let mut name = match name { + BasicSymbol::Name(name) => name, + _ => return Err(ProcessorError::ImplNoName(name_line)), + }; + + if name.len() > 1 { + return Err(ProcessorError::MultipartTypeName(name_line)); + } + let name = name.remove(0); + + let (contents, contents_line) = tree.next().ok_or(ProcessorError::ImplNoBraces(name_line))?; + let contents = match contents { + BasicSymbol::BracedSection(contents) => contents, + _ => return Err(ProcessorError::ImplNoBraces(contents_line)), + }; + let mut contents = contents.into_iter(); + + let mut functions = Vec::new(); + + loop { + let symbol = contents.next(); + if symbol.is_none() { + break; + } + let (symbol, symbol_line) = symbol.unwrap(); + match symbol { + BasicSymbol::Keyword(Keyword::Fn) => { + let function = + parse_fn(start_line_info.clone(), &mut contents, Some(name.0.clone()))?; + let function = match function { + PreprocessSymbol::Fn(_, function) => function, + _ => panic!(), + }; + functions.push((function, symbol_line)); + } + _ => return Err(ProcessorError::ImplNonFnContent(symbol_line)), + } + } + + Ok(PreprocessSymbol::Impl(start_line_info, name.0, functions)) +} + +fn parse_fn( + start_line_info: LineInfo, + tree: &mut IntoIter<(BasicSymbol, LineInfo)>, + mut self_type: Option, +) -> Result { + let (name, name_line) = tree + .next() + .ok_or(ProcessorError::FnNoName(start_line_info.clone()))?; + let mut name = match name { + BasicSymbol::Name(name) => name, + _ => return Err(ProcessorError::FnNoName(name_line)), + }; + + if name.len() > 1 { + return Err(ProcessorError::MultipartNameDef(name_line)); + } + let (name, _, name_type, indirection) = name.remove(0); + if indirection != 0 { + return Err(ProcessorError::NameWithRefPrefix(name_line)); + } + + let parameters = match name_type { + NameType::Normal => return Err(ProcessorError::FnNoBraces(name_line)), + NameType::Function(arguments) => arguments, + }; + + // let (arguments, arguments_line) = tree.next().ok_or()?; + // let arguments = match arguments { + // BasicSymbol::BracketedSection(contents) => contents, + // _ => { + // return Err(Syntax( + // path, + // arguments_line, + // "function name must be followed by brackets ('()')".to_string(), + // )) + // } + // }; + + let mut parameters_processed = Vec::new(); + let mut last_line = name_line; + + for parameter in parameters { + let mut parameter = parameter.into_iter(); + + let Some((first_item, first_line)) = parameter.next() else { + return Err(ProcessorError::FnParamsTrailingComma(last_line)); + }; + + let arg_name = first_item; + let arg_line = first_line; + let arg_name = match arg_name { + BasicSymbol::Name(mut name) => { + if name.len() > 1 { + return Err(ProcessorError::MultipartNameDef(arg_line)); + } + name.remove(0).0 + } + _ => return Err(ProcessorError::FnExpectedParameterName(arg_line)), + }; + let (param_type, param_type_line) = if arg_name == "self" { + if self_type.is_none() { + return Err(ProcessorError::FnBadSelf(arg_line)); + } + + ((self_type.take().unwrap(), 1), arg_line.clone()) + } else { + let Some((colon, colon_line)) = parameter.next() else { + return Err(ProcessorError::NameTypeNotDefined(arg_line)); + }; + + if !matches!(colon, BasicSymbol::Punctuation(Punctuation::Colon)) { + return Err(ProcessorError::NameTypeNotDefined(colon_line)); + } + + let Some((param_type, param_type_line)) = parameter.next() else { + return Err(ProcessorError::NameTypeNotDefined(colon_line)); + }; + let param_type = match param_type { + BasicSymbol::Name(mut name) => { + if name.len() > 1 { + return Err(ProcessorError::MultipartTypeName(param_type_line)); + } + let part = name.remove(0); + (part.0, part.3) + } + _ => return Err(ProcessorError::NameTypeNotDefined(param_type_line)), + }; + (param_type, param_type_line) + }; + + parameters_processed.push(( + arg_name, + arg_line, + param_type.0, + param_type.1, + param_type_line.clone(), + )); // TODO: + last_line = param_type_line; + } + + let (mut contents, mut contents_line) = tree + .next() + .ok_or(ProcessorError::FnNoBracesOrReturn(last_line))?; + + let return_type = if matches!(&contents, BasicSymbol::Punctuation(Punctuation::Tilda)) { + let Some((next_symbol, next_line)) = tree.next() else { + return Err(ProcessorError::FnExpectedReturnType(contents_line)); + }; + contents_line = next_line.clone(); + match next_symbol { + BasicSymbol::Name(mut name) => { + if name.len() > 1 { + return Err(ProcessorError::MultipartTypeName(next_line)); + } + Some((name.remove(0), next_line)) + } + _ => return Err(ProcessorError::FnExpectedReturnType(next_line)), + } + } else { + None + }; + + if return_type.is_some() { + (contents, contents_line) = tree + .next() + .ok_or(ProcessorError::FnNoBraces(contents_line))?; + } + + let contents = match contents { + BasicSymbol::BracedSection(contents) => contents, + _ => return Err(ProcessorError::FnNoBraces(contents_line)), + }; + + Ok(PreprocessSymbol::Fn( + start_line_info, + ( + name, + parameters_processed, + return_type.map(|x| ((x.0 .0, x.0 .3), x.1)), + contents, + ), + )) +} diff --git a/src/root/name_resolver/processor.rs b/src/root/name_resolver/processor.rs new file mode 100644 index 0000000..4a4f125 --- /dev/null +++ b/src/root/name_resolver/processor.rs @@ -0,0 +1,167 @@ +use crate::root::ast::operators::Operator; +use crate::root::basic_ast::symbol::BasicAbstractSyntaxTree; +use crate::root::compiler::compile_functions::{compile_functions, Function}; +use crate::root::parser::line_info::LineInfo; +use crate::root::name_resolver::preprocess::preprocess; +use crate::root::name_resolver::type_builder::build_types; + +use thiserror::Error; + +#[derive(Error, Debug)] +#[allow(dead_code)] +pub enum ProcessorError { + #[error("Error: Expected name after 'struct'\n{0}")] + StructNoName(LineInfo), + #[error("Error: Tried to define a name with multiple parts\n{0}")] + MultipartNameDef(LineInfo), + #[error("Error: Expected braces after struct name\n{0}")] + StructNoBraces(LineInfo), + #[error("Error: Struct attributes must be ',' separated\n{0}")] + StructNoAttrSeparator(LineInfo), + #[error("Error: Expected struct attribute name\n{0}")] + StructExpectedAttributeName(LineInfo), + #[error("Error: Expected `: [TYPE]` after name to define type\n{0}")] + NameTypeNotDefined(LineInfo), + #[error("Error: Expected 'struct', 'impl' or 'fn' at top level\n{0}")] + BadTopLevelSymbol(LineInfo), + #[error("Error: Expected name after 'impl'\n{0}")] + ImplNoName(LineInfo), + #[error("Error: Expected braces after impl name\n{0}")] + ImplNoBraces(LineInfo), + #[error("Error: Only function definitions are allowed within impls\n{0}")] + ImplNonFnContent(LineInfo), + #[error("Error: Expected name after 'fn'\n{0}")] + FnNoName(LineInfo), + #[error("Error: Function parameters cannot have a trailing ','\n{0}")] + FnParamsTrailingComma(LineInfo), + #[error("Error: Expected parameter name \n{0}")] + FnExpectedParameterName(LineInfo), + #[error("Error: Expected '~ [RETURN TYPE]' or braces after function parameters\n{0}")] + FnNoBracesOrReturn(LineInfo), + #[error("Error: Expected braces after function parameters\n{0}")] + FnNoBraces(LineInfo), + #[error("Error: Expected function return type after '~'\n{0}")] + FnExpectedReturnType(LineInfo), + #[error("Error: 'self' can only be used as the first parameter in an impl function with no type specifier\n{0}")] + FnBadSelf(LineInfo), + #[error("Error: Parameter name '{1}' already in use\n{0}")] + ParameterNameInUse(LineInfo, String), + #[error("[TODO] Error: Tried to use a type name with multiple parts\n{0}")] + MultipartTypeName(LineInfo), + #[error("Error: Type '{1}' not found\n{0}")] + TypeNotFound(LineInfo, String), + #[error("Error: Name '{1}' not found\n{0}")] // TODO: + NameNotFound(LineInfo, String), + // #[error("Error: Type '{1}' defined...\n{0}{2}")] + // TypeRedefinition(LineInfo, String, LineInfo), + #[error("Error: Type '{1}' has an infinite size [{2}]\n{0}")] + CircularType(LineInfo, String, String), + #[error("Error: Impl type not found\n{0}")] + BadImplType(LineInfo), + #[error("Error: No main function found")] + NoMainFunction, + #[error("Error: Main function cannot have parameters")] + MainFunctionParams, // TODO + #[error("Error: Main function must return 'int'")] + MainFunctionBadReturn, // TODO + // #[error("Error: Expected semicolon\n{0}")] + // ExpectedSemicolon(LineInfo), + #[error("Error: Bad operator position for '{1}'\n{0}")] + BadOperatorPosition(LineInfo, Operator), + #[error("Error: Standalone type\n{0}")] + StandaloneType(LineInfo), + // #[error("Error: Standalone operator\n{0}")] + // StandaloneOperator(LineInfo), + #[error("Error: This must evaluate to a value but doesn't\n{0}")] + DoesntEvaluate(LineInfo), + #[error("Error: Bad argument type for function - expected '{1}', found '{2}'. Called:\n{0}\nDefined:\n{3}")] + BadArgType(LineInfo, String, String, LineInfo), + #[error("Error: Wrong amount of arguments for function - expected {1}, found {2} (including automatic self passing where applicable). Called:\n{0}\nDefined:\n{3}")] + BadArgCount(LineInfo, usize, usize, LineInfo), + #[error( + "Error: Functions with a return type must have a return statement as their last line\n{0}" + )] + NoReturnStatement(LineInfo), + #[error("Error: You can only assign to names\n{0}")] + NonNameAssignment(LineInfo), + #[error("Error: Assignment operator must have value on RHS\n{0}")] + NoAssignmentRHS(LineInfo), + #[error("Error: Can't return nothing from a function with a return type\n{0}")] + NoneReturnOnTypedFunction(LineInfo), + #[error("Error: Can't return a value from a function with no return type\n{0}")] + TypeReturnOnVoidFunction(LineInfo), + #[error("Error: Returned type '{2}' doesn't match function return type '{1}\n{0}")] + BadReturnType(LineInfo, String, String), + #[error("Error: Can only assign to variables")] + AssignToNonVariable(LineInfo), + #[error("Error: 'let' must be followed by a variable name\n{0}")] + LetNoName(LineInfo), + #[error("Error: `let [NAME]: [TYPE]` must be followed by `= [VALUE]`")] + LetNoValue(LineInfo), + #[error("Error: `while` must be followed by brackets containing the condition\n{0}")] + WhileNoBrackets(LineInfo), + #[error("Error: Condition must evaluate to boolean (not '{1}')\n{0}")] + BadConditionType(LineInfo, String), + #[error("Error: While condition must be followed by braces containing the contents of the loop\n{0}")] + WhileNoBraces(LineInfo), + #[error("Error: While contents must be followed by semicolon\n{0}")] + WhileMoreAfterBraces(LineInfo), + #[error("Error: Evaluable layout must be `[VALUE]`, `[PREFIX OPERATOR] [VALUE]`, or `[VALUE] [POSTFIX OPERATOR] [OTHER VALUE]`\n{0}")] + BadEvaluableLayout(LineInfo), + #[error("Error: Expected evaluation to type '{1}' but found '{2}'\n{0}")] + BadEvaluatedType(LineInfo, String, String), + #[error("Error: Operator function '{1}' not found for type '{2}'\n{0}")] + SingleOpFunctionNotFound(LineInfo, String, String), + #[error("Error: Operator function '{1}' not found for type '{2}' (LHS) and '{3}' (RHS)\n{0}")] + OpFunctionNotFound(LineInfo, String, String, String), + #[error("Error: Nothing can follow a 'break'\n{0}")] + BreakLineNotEmpty(LineInfo), + #[error("Error: Nothing to break out of\n{0}")] + NothingToBreak(LineInfo), + #[error("Error: `elif` and `else` can only follow `if`\n{0}")] + RawElifElse(LineInfo), + #[error("Error: `if` and `elif` must be followed by brackets containing the condition\n{0}")] + IfElifNoBrackets(LineInfo), + #[error("Error: `if` and `elif` conditions, or `else` on its own must be followed by braces containing code to be executed conditionally\n{0}")] + IfElifElseNoBraces(LineInfo), + #[error("Error: An if/elif/else chain must be followed by semicolon\n{0}")] + ElseMoreAfterBraces(LineInfo), + #[error("Error: Can't have anything after an else in an if/elif/else\n{0}")] + IfElifAfterElse(LineInfo), + #[error("Error: Builtin types cannot be initialised with an explicit initialiser\n{0}")] + AttemptedBuiltinInitialiser(LineInfo), + #[error("Error: Incorrect number of attributes in initialise. Expected {1}, found {2}\n{0}")] + IncorrectAttribCount(LineInfo, usize, usize), + #[error("Error: Attempted to access attribute of a type, not an initialised variable of that type\n{0}")] + AttemptedTypeAttribAccess(LineInfo), + #[error("Error: Type '{1}' has no attribute '{2}'\n{0}")] + AttributeDoesntExist(LineInfo, String, String), + #[error("Error: Tried to call non-static function on a type, not an initialised variable of that type\n{0}")] + TypeNonStaticFunctionCall(LineInfo), + #[error("Error: Name cannot have a '$' prefix. Use `& [NAME]` to get a reference to a variable and `* [NAME]` to dereference\n{0}")] + NameWithRefPrefix(LineInfo), + #[error("Error: Can't dereference non-reference type\n{0}")] + CantDerefNonRef(LineInfo), + #[error("Error: Can't deallocate non-reference type\n{0}")] + CantDeallocateNonRef(LineInfo), + #[error("Error: Can't set the destructor of a built-in type\n{0}")] + CantSetBuiltinDestructor(LineInfo), + #[error("Error: Destructor must have only take `self` as a parameter\n{0}")] + BadDestructorSignature(LineInfo), + #[error("Error: Can only have one destructor\n{0}")] + MultipleDestructors(LineInfo), + #[error("TODO: Bad literal type")] + BadLiteralType(), + #[error("Error: Feature '{1}' not implemented yet\n{0}")] + NotImplemented(LineInfo, String), +} + +pub fn process( + ast: Vec, +) -> Result>, ProcessorError> { + let pre_ast = preprocess(ast)?; + // println!("Preprocessing Result:\n{:?}", pre_ast); + let (type_table, function_names, typed_functions) = build_types(pre_ast)?; + // println!("Typed functions:\n{:?}", typed_functions); + compile_functions(function_names, typed_functions, type_table) +} diff --git a/src/root/name_resolver/type_builder.rs b/src/root/name_resolver/type_builder.rs new file mode 100644 index 0000000..0e834f0 --- /dev/null +++ b/src/root/name_resolver/type_builder.rs @@ -0,0 +1,466 @@ +use crate::root::name_resolver::preprocess::{PreProcessFunction, PreprocessSymbol}; +use crate::root::name_resolver::processor::ProcessorError; + +use std::collections::HashMap; + +use crate::root::ast::literals::Literal; +use crate::root::basic_ast::symbol::BasicSymbol; +use crate::root::compiler::local_variable::{LocalVariable, TypeInfo}; +use crate::root::custom::types::bool::Bool; +use crate::root::custom::types::float::Float; +use crate::root::custom::types::int::Int; +use crate::root::parser::line_info::LineInfo; + +use crate::root::name_resolver::user_type::UserType; +use crate::root::utils::align; + +struct UninitialisedType { + pub path: LineInfo, + pub id: isize, + pub attributes: Vec<(String, Result)>, +} + +impl UninitialisedType { + pub fn new( + path: LineInfo, + id: isize, + attributes: Vec<(String, LineInfo, (String, usize), LineInfo)>, + ) -> UninitialisedType { + let mut attributes_processed = Vec::new(); + for (attr_name, _attr_line, attr_type, attr_type_line) in attributes { + attributes_processed.push((attr_name, Err((attr_type, attr_type_line)))); + } + + UninitialisedType { + path, + id, + attributes: attributes_processed, + } + } + + // pub fn to_initialised(self) -> Result { + // let mut attributes_processed = Vec::new(); + // for (attr_name, attr_type) in self.attributes { + // if attr_type.is_err() { + // let (attr_type, attr_type_line) = attr_type.unwrap_err(); + // return Err(TypeNotFoundError(self.path, attr_type_line, attr_type)); + // } + // + // attributes_processed.push((attr_name, attr_type.unwrap())) + // } + // + // Ok(UserType::new(self.name, self.id, attributes_processed)) + // } +} + +pub trait Type { + fn get_id(&self) -> isize; + + fn get_name(&self) -> &str; + + fn get_indirect_name(&self, indirection: usize) -> String { + let mut out = String::new(); + for _ in 0..indirection { + out.push('$'); + } + out += self.get_name(); + out + } + + fn get_size( + &self, + type_table: &TypeTable, + path: Option>, + ) -> Result; + + fn instantiate( + &self, + literal: Option<&Literal>, + local_address: isize, + ) -> Result, ProcessorError>; + + fn get_user_type(&self) -> Option<&UserType> { + None + } + + fn try_set_destructor( + &mut self, + line_info: &LineInfo, + _func: isize, + ) -> Result<(), ProcessorError> { + Err(ProcessorError::CantSetBuiltinDestructor(line_info.clone())) + } + + fn get_destructor(&self) -> Option { + None + } +} + +// pub struct TypeIdentifier { +// id: isize, +// indirections: usize, +// } + +pub struct TypeTable { + types: HashMap>, +} + +impl TypeTable { + pub fn new() -> TypeTable { + TypeTable { + types: HashMap::new(), + } + } + + pub fn add_builtin(mut self) -> TypeTable { + self.add_type(Int::get_id(), Box::new(Int::new())); + self.add_type(Bool::get_id(), Box::new(Bool::new())); + self.add_type(Float::get_id(), Box::new(Float::new())); + self + } + + pub fn add_type(&mut self, id: isize, type_: Box) { + if self.types.insert(id, type_).is_some() { + panic!("Attempted to override type") + } + } + + pub fn get_id_by_name(&self, name: &str) -> Option { + for (id, type_) in &self.types { + if type_.get_name() == name { + return Some(*id); + } + } + None + } + + pub fn get_type(&self, id: isize) -> Option<&Box> { + self.types.get(&id) + } + + pub fn get_type_mut(&mut self, id: isize) -> Option<&mut Box> { + self.types.get_mut(&id) + } + + pub fn get_type_size(&self, id: TypeInfo) -> Result { + if id.reference_depth != 0 { + return Ok(Int::get_size(&Int::new(), self, None)?); + } + self.types.get(&id.type_id).unwrap().get_size(self, None) + } +} + +#[derive(Debug)] +pub struct UserTypedFunction { + pub id: isize, + pub name: String, + pub line: LineInfo, + pub args: Vec<(String, TypeInfo)>, + pub return_type: Option, + pub contents: Option>, +} + +impl TypedFunction for UserTypedFunction { + fn get_id(&self) -> isize { + self.id + } + + fn get_name(&self) -> &str { + &self.name + } + + fn get_args(&self) -> &[(String, TypeInfo)] { + &self.args + } + + fn get_line(&self) -> LineInfo { + self.line.clone() + } + + fn get_return_type(&self) -> Option { + self.return_type + } + + fn is_inline(&self) -> bool { + false + } + + fn contents(&self) -> &Vec<(BasicSymbol, LineInfo)> { + self.contents.as_ref().unwrap() + } + + fn take_contents(&mut self) -> Vec<(BasicSymbol, LineInfo)> { + self.contents.take().unwrap() + } + + fn get_inline(&self, _args: Vec) -> Vec { + panic!() + } +} + +pub trait TypedFunction { + fn get_id(&self) -> isize { + panic!(); + } + fn get_name(&self) -> &str; + fn get_args(&self) -> &[(String, TypeInfo)]; + fn get_line(&self) -> LineInfo; + fn get_args_positioned( + &self, + type_table: &TypeTable, + ) -> Result, ProcessorError> { + let mut offset = 16isize; + if let Some(return_type) = self.get_return_type() { + offset += type_table.get_type_size(return_type)? as isize; + offset = align(offset, 8); + } + let mut output = Vec::new(); + + for (name, _type) in self.get_args() { + output.push((name.clone(), LocalVariable::from_type_info(offset, *_type))); + offset += type_table.get_type_size(*_type).unwrap() as isize; + offset = align(offset, 8); + } + + Ok(output) + } + fn get_return_type(&self) -> Option; + fn is_inline(&self) -> bool; + fn contents(&self) -> &Vec<(BasicSymbol, LineInfo)> { + panic!() + } + fn take_contents(&mut self) -> Vec<(BasicSymbol, LineInfo)> { + panic!() + } + fn get_inline(&self, _args: Vec) -> Vec { + panic!(); + } +} + +// #[derive(Debug)] +// pub enum TypedImplsFns { +// Impl(isize, Vec), +// Fn(TypedFunction) +// } + +pub fn build_types( + pre_ast: Vec, +) -> Result< + ( + TypeTable, + HashMap, HashMap>, + HashMap>, + ), + ProcessorError, +> { + let mut remaining_pre_ast = Vec::new(); + + let mut uninitialised_types: HashMap = HashMap::new(); + let mut type_counter = 0isize; + + let mut type_table = TypeTable::new().add_builtin(); + + for symbol in pre_ast { + match symbol { + PreprocessSymbol::Struct(line, name, args) => { + uninitialised_types.insert( + name.clone(), + UninitialisedType::new(line, type_counter, args), + ); + type_counter += 1; + } + other => remaining_pre_ast.push(other), + } + } + + let mut uninitialised_types: Vec<_> = uninitialised_types.into_iter().collect(); + + for i in 0..uninitialised_types.len() { + 'attr_loop: for a in 0..uninitialised_types[i].1.attributes.len() { + for j in 0..uninitialised_types.len() { + if uninitialised_types[i].1.attributes[a] + .1 + .as_ref() + .unwrap_err() + .0 + .0 + == uninitialised_types[j].0 + { + uninitialised_types[i].1.attributes[a].1 = Ok(TypeInfo::new( + uninitialised_types[j].1.id, + uninitialised_types[i].1.attributes[a] + .1 + .as_ref() + .unwrap_err() + .0 + .1, + )); + continue 'attr_loop; + } + } + + if let Some(id) = type_table.get_id_by_name( + &uninitialised_types[i].1.attributes[a] + .1 + .as_ref() + .unwrap_err() + .0 + .0, + ) { + uninitialised_types[i].1.attributes[a].1 = Ok(TypeInfo::new( + id, + uninitialised_types[i].1.attributes[a] + .1 + .as_ref() + .unwrap_err() + .0 + .1, + )); + continue 'attr_loop; + } + + let type_ = uninitialised_types.remove(i).1; + let (_line, mut attributes) = (type_.path, type_.attributes); + let (type_name, line) = attributes.remove(a).1.unwrap_err(); + return Err(ProcessorError::TypeNotFound(line, type_name.0)); + } + } + + for (name, type_) in uninitialised_types { + let (_id, line, attributes) = (type_.id, type_.path, type_.attributes); + + let mut attributes_processed = Vec::new(); + for (attr_name, attr_type) in attributes { + if attr_type.is_err() { + let (attr_type, attr_type_line) = attr_type.unwrap_err(); + return Err(ProcessorError::TypeNotFound(attr_type_line, attr_type.0)); + } + + attributes_processed.push((attr_name, attr_type.unwrap())) + } + + type_table.add_type( + type_.id, + Box::new(UserType::new( + name, + type_.id, + line, + attributes_processed, + None, + )), + ) + } + + let mut typed_fns = HashMap::new(); + let mut fn_name_map = HashMap::new(); + fn_name_map.insert(None, HashMap::new()); + let mut id_counter: isize = 1; + for symbol in remaining_pre_ast { + match symbol { + PreprocessSymbol::Impl(line, type_name, functions) => { + let type_id = type_table + .get_id_by_name(&type_name) + .ok_or(ProcessorError::BadImplType(line))?; + fn_name_map + .entry(Some(type_id)) + .or_insert_with(HashMap::new); + for (function, line) in functions { + fn_name_map + .get_mut(&Some(type_id)) + .unwrap() + .insert(function.0.clone(), id_counter); + + if function.0 == "destroy" { + type_table + .get_type_mut(type_id) + .unwrap() + .try_set_destructor(&line, id_counter)?; + } + + typed_fns.insert( + id_counter, + process_function(function, &type_table, id_counter, Some(type_id), line)?, + ); + id_counter += 1; + } + } + PreprocessSymbol::Fn(line, function) => { + let id = if &function.0 == "main" { + 0 + } else { + id_counter += 1; + id_counter - 1 + }; + fn_name_map + .get_mut(&None) + .unwrap() + .insert(function.0.clone(), id); + typed_fns.insert(id, process_function(function, &type_table, id, None, line)?); + id_counter += 1; + } + _ => panic!("Expected Impl of Functions"), + } + } + + if let Some(main) = typed_fns.get(&0) { + if !main.get_args().is_empty() { + return Err(ProcessorError::MainFunctionParams); + } + if main.get_return_type() != Some(TypeInfo::new(Int::get_id(), 0)) { + return Err(ProcessorError::MainFunctionBadReturn); + } + } else { + return Err(ProcessorError::NoMainFunction); + } + + Ok((type_table, fn_name_map, typed_fns)) +} + +fn process_function( + function: PreProcessFunction, + type_table: &TypeTable, + id: isize, + _impl_type: Option, + line: LineInfo, +) -> Result, ProcessorError> { + let (name, args, return_type, contents) = function; + + let mut args_processed: Vec<(String, TypeInfo)> = Vec::new(); + + for (param_name, param_line, type_name, indirection, type_line) in args { + for (existing_arg, _) in &args_processed { + if ¶m_name == existing_arg { + return Err(ProcessorError::ParameterNameInUse(param_line, param_name)); + } + } + args_processed.push(( + param_name, + TypeInfo::new( + type_table + .get_id_by_name(&type_name) + .ok_or(ProcessorError::TypeNotFound(type_line, type_name))?, + indirection, + ), + )); + } + + let return_type = if let Some((type_name, type_line)) = return_type { + Some(TypeInfo::new( + type_table + .get_id_by_name(&type_name.0) + .ok_or(ProcessorError::TypeNotFound(type_line, type_name.0))?, + type_name.1, + )) + } else { + None + }; + + Ok(Box::new(UserTypedFunction { + id, + name, + line, + args: args_processed, + return_type, + contents: Some(contents), + })) +} diff --git a/src/root/name_resolver/user_type.rs b/src/root/name_resolver/user_type.rs new file mode 100644 index 0000000..8c9f819 --- /dev/null +++ b/src/root/name_resolver/user_type.rs @@ -0,0 +1,137 @@ +use crate::root::ast::literals::Literal; +use crate::root::compiler::local_variable::{LocalVariable, TypeInfo}; +use crate::root::parser::line_info::LineInfo; +use crate::root::name_resolver::processor::ProcessorError; +use crate::root::name_resolver::type_builder::{Type, TypeTable}; + +pub struct UserType { + name: String, + id: isize, + path: LineInfo, + attributes: Vec<(String, TypeInfo)>, + destructor: Option, +} + +impl UserType { + pub fn new( + name: String, + id: isize, + path: LineInfo, + attributes: Vec<(String, TypeInfo)>, + destructor: Option, + ) -> UserType { + UserType { + name, + id, + path, + attributes, + destructor, + } + } + + pub fn get_attribute_offset_and_type( + &self, + name: &str, + type_table: &TypeTable, + ) -> Result, ProcessorError> { + let mut offset = 0; + for (attrib_name, attrib_type) in &self.attributes { + if name == attrib_name { + return Ok(Some(LocalVariable::from_type_info(offset, *attrib_type))); + } + offset += type_table.get_type_size(*attrib_type)? as isize; + } + Ok(None) + } + + pub fn get_attribute_types(&self) -> Vec { + let mut out = Vec::new(); + for (_, i) in &self.attributes { + out.push(*i); + } + out + } +} + +impl Type for UserType { + fn get_id(&self) -> isize { + self.id + } + + fn get_name(&self) -> &str { + &self.name + } + + fn get_size( + &self, + type_table: &TypeTable, + mut path: Option>, + ) -> Result { + if path.is_none() { + // path = Some(vec![self.get_id()]) + // ? + } else { + let mut failed_check = false; + for id in &**path.as_ref().unwrap() { + if *id == self.get_id() { + failed_check = true; + break; + } + } + + if failed_check { + let mut debug_str = String::new(); + for id in &**path.as_ref().unwrap() { + debug_str += &type_table.get_type(*id).unwrap().get_name(); + debug_str += "->"; + } + + debug_str += &self.get_name(); + + return Err(ProcessorError::CircularType( + self.path.clone(), + self.name.clone(), + debug_str, + )); + } + + path.as_mut().unwrap().push(self.get_id()); + }; + + let mut size = 0; + + for (_name, id) in &self.attributes { + size += type_table.get_type_size(*id)?; + } + + Ok(size) + } + + fn instantiate( + &self, + _literal: Option<&Literal>, + _local_address: isize, + ) -> Result, ProcessorError> { + panic!() + } + + fn get_user_type(&self) -> Option<&UserType> { + Some(self) + } + + fn try_set_destructor( + &mut self, + line_info: &LineInfo, + func: isize, + ) -> Result<(), ProcessorError> { + if self.destructor.is_some() { + return Err(ProcessorError::MultipleDestructors(line_info.clone())); + } + self.destructor = Some(func); + Ok(()) + } + + fn get_destructor(&self) -> Option { + self.destructor + } +} diff --git a/src/root/nom_parser/mod.rs b/src/root/nom_parser/mod.rs new file mode 100644 index 0000000..6c5eba6 --- /dev/null +++ b/src/root/nom_parser/mod.rs @@ -0,0 +1,12 @@ +pub mod parse; +mod parse_error; +mod parse_toplevel; +mod parse_struct; +mod parse_impl; +mod parse_fn; +mod parse_util; +mod parse_comments; +mod parse_blocks; +mod parse_parameters; +mod parse_name; +mod parse_arguments; \ No newline at end of file diff --git a/src/root/nom_parser/parse.rs b/src/root/nom_parser/parse.rs new file mode 100644 index 0000000..bded628 --- /dev/null +++ b/src/root/nom_parser/parse.rs @@ -0,0 +1,44 @@ +use std::fs; +use std::path::PathBuf; +use std::rc::Rc; +use nom::IResult; +use nom_locate::LocatedSpan; +use nom_supreme::error::GenericErrorTree; +use crate::root::nom_parser::parse_toplevel; + +pub type Span<'a> = LocatedSpan<&'a str, &'a Rc>; + +pub type ParseResult<'a, I=Span<'a>, O=Span<'a>, E=TypeErrorTree<'a>> = IResult; +pub type TypeErrorTree<'a> = GenericErrorTree, &'static str, &'static str, Box>; + +#[derive(Debug)] +pub struct Location { + path: Rc, + offset: usize, + line: u32 +} + +impl Location { + pub fn from_span(span: Span) -> Location { + Location { + path: span.extra.clone(), + offset: span.location_offset(), + line: span.location_line() + } + } +} + +pub fn parse(path: PathBuf) -> Result<(), ()> { + let text = fs::read_to_string(&path).unwrap(); + let path = Rc::new(path); + let base = Span::new_extra(&text, &path); + + + println!("{:?}", + parse_toplevel::parse_toplevel(base) + ); + + // parse_toplevel(Span::new_extra(&text, &path)).unwrap(); + + Ok(()) +} \ No newline at end of file diff --git a/src/root/nom_parser/parse_arguments.rs b/src/root/nom_parser/parse_arguments.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/root/nom_parser/parse_blocks.rs b/src/root/nom_parser/parse_blocks.rs new file mode 100644 index 0000000..8e730ba --- /dev/null +++ b/src/root/nom_parser/parse_blocks.rs @@ -0,0 +1,14 @@ +use nom::bytes::complete::take_until; +use nom::character::complete::char; +use nom::sequence::Tuple; +use nom_supreme::tag::complete::tag; +use crate::root::nom_parser::parse::{ParseResult, Span}; + +pub fn braced_section(s: Span) -> ParseResult { + ( + char('{'), + take_until("}"), + char('}') + ).parse(s) + .map(|(s, (_, y, _)): (Span, (char, Span, char))| (s, y)) +} \ No newline at end of file diff --git a/src/root/nom_parser/parse_comments.rs b/src/root/nom_parser/parse_comments.rs new file mode 100644 index 0000000..2ab6f72 --- /dev/null +++ b/src/root/nom_parser/parse_comments.rs @@ -0,0 +1,24 @@ +use nom_supreme::tag::complete::tag; +use nom::bytes::complete::{is_not, take_until}; +use nom::sequence::{pair, Tuple}; +use nom::Parser; +use crate::root::nom_parser::parse::{ParseResult, Span}; + +fn peol_comment(s: Span) -> ParseResult +{ + pair(tag("//"), is_not("\n\r")).parse(s) + .map(|(s, (_, y)): (Span, (Span, Span))| (s, y)) +} + +fn pinline_comment(s: Span) -> ParseResult { + ( + tag("/*"), + take_until("*/"), + tag("*/") + ).parse(s) + .map(|(s, (_, y, _)): (Span, (Span, Span, Span))| (s, y)) +} + +pub fn parse_comment(s: Span) -> ParseResult { + pinline_comment(s).or_else(|_| peol_comment(s)) +} diff --git a/src/root/nom_parser/parse_error.rs b/src/root/nom_parser/parse_error.rs new file mode 100644 index 0000000..13a59e0 --- /dev/null +++ b/src/root/nom_parser/parse_error.rs @@ -0,0 +1,34 @@ +use std::io; +use std::path::PathBuf; +use thiserror::Error; +use crate::root::parser::line_info::LineInfo; + +#[derive(Error, Debug)] +pub enum ParseError { + #[error("File read error on path '{0}'")] + FileRead(PathBuf, io::Error), + #[error("Error: {1}\n{0}")] + Nested(LineInfo, Box), + #[error("Error: Operator '{1}' not recognised\n{0}")] + OperatorNotRecognised(LineInfo, String), + #[error("Error: 'mod' must be followed by a path\n{0}")] + ModNotFollowedByPath(LineInfo), + #[error("Error: Keyword ('{1}') cannot be followed by . or #\n{0}")] + KeywordFollowed(LineInfo, String), + #[error("Error: Closing '{1}' not found (started on line {2})\n{0}")] + NotClosed(LineInfo, char, usize), + #[error("Error: Unknown escape code '{1}'\n{0}")] + UnknownEscapeCode(LineInfo, char), + #[error("Error: String literal started on line {1} not closed\n{0}")] + UnclosedString(LineInfo, usize), + #[error("Error: Closing '{1}' found with no corresponding opening bracket\n{0}")] + NoOpening(LineInfo, char), + #[error("Error: Names cannot contain character '{1}' (UTF-8 Code: {2:?})\n{0}")] + BadName(LineInfo, char, Vec), + #[error("Initialiser must specify a type after '@'")] + NoInitialiserType(LineInfo), + #[error("Error: Initialiser type must be followed by braces containing attribute values\n{0}")] + NoInitialiserContents(LineInfo), + #[error("Error: Attribute cannot be empty (must be a value between commas)\n{0}")] + NoInitialiserAttribute(LineInfo) +} \ No newline at end of file diff --git a/src/root/nom_parser/parse_fn.rs b/src/root/nom_parser/parse_fn.rs new file mode 100644 index 0000000..a01c347 --- /dev/null +++ b/src/root/nom_parser/parse_fn.rs @@ -0,0 +1,24 @@ +pub mod base; + +use nom_supreme::tag::complete::tag; +use nom::Parser; +use crate::root::nom_parser::parse::{Location, ParseResult, Span}; +use crate::root::nom_parser::parse_parameters::Parameters; +use crate::root::nom_parser::parse_toplevel::TopLevelTokens; + +#[derive(Debug)] +pub struct FunctionToken { + location: Location, + name: String, + return_type: String, + parameters: Parameters, + // contents: Vec> +} + +pub fn parse_function(s: Span) -> ParseResult { + todo!(); + + // println!("{:?}", s); + // tag("fn").parse(s).map(|(s, _)| (s, TopLevelTokens::Test)) +} + diff --git a/src/root/nom_parser/parse_fn/base.rs b/src/root/nom_parser/parse_fn/base.rs new file mode 100644 index 0000000..929ff49 --- /dev/null +++ b/src/root/nom_parser/parse_fn/base.rs @@ -0,0 +1,86 @@ +use crate::root::nom_parser::parse::{Location, ParseResult, Span}; +use crate::root::nom_parser::parse_name::NameToken; + +enum LineTokens<'a> { + Initialisation(InitialisationToken<'a>), + Assignment(AssignmentToken<'a>), + If(IfToken<'a>), + While(WhileToken<'a>), + Return(&'a str), + Break, + NoOp(EvaluableToken) +} + +#[derive(Debug)] +pub struct EvaluableToken { + location: Location, + tokens: Vec +} + +struct InitialisationToken<'a> { + location: Location, + name: &'a str, + type_name: &'a str, + value: EvaluableToken +} + +struct AssignmentOperatorToken { + location: Location, + assignment_operator: AssignmentOperatorTokens +} + +enum AssignmentOperatorTokens { + None, + Combination(OperatorTokens), +} + +struct AssignmentToken<'a> { + location: Location, + name: &'a str, + assignment_operator: AssignmentOperatorToken, + value: EvaluableToken +} + +struct IfToken<'a> { + location: Location, + if_condition: EvaluableToken, + if_contents: Vec>, + elif_condition_contents: Vec<(EvaluableToken, Vec>)>, + else_contents: Option>> +} + +struct WhileToken<'a> { + location: Location, + condition: EvaluableToken, + contents: Vec> +} + +#[derive(Debug)] +enum EvaluableTokens { + Name(NameToken), + Literal(LiteralTokens), + InfixOperator(EvaluableToken, OperatorToken, EvaluableToken), + PrefixOperator(OperatorToken, EvaluableToken) +} + +#[derive(Debug)] +struct OperatorToken { + location: Location, + operator: OperatorTokens +} + +#[derive(Debug)] +enum OperatorTokens { + Add, + Subtract, +} + +#[derive(Debug)] +enum LiteralTokens { + Bool(bool), + String(String) +} + +pub fn evaluable(s: Span) -> ParseResult<(), EvaluableToken> { + todo!() +} \ No newline at end of file diff --git a/src/root/nom_parser/parse_impl.rs b/src/root/nom_parser/parse_impl.rs new file mode 100644 index 0000000..3ffb2cb --- /dev/null +++ b/src/root/nom_parser/parse_impl.rs @@ -0,0 +1,49 @@ +use nom::Parser; +use nom_supreme::tag::complete::tag; +use crate::root::nom_parser::parse::{Location, ParseResult, Span}; +use crate::root::nom_parser::parse_blocks::braced_section; +use crate::root::nom_parser::parse_fn::{FunctionToken, parse_function}; +use crate::root::nom_parser::parse_name::parse_simple_name; +use crate::root::nom_parser::parse_parameters::parse_parameters; +use crate::root::nom_parser::parse_struct::StructToken; +use crate::root::nom_parser::parse_toplevel::TopLevelTokens; +use crate::root::nom_parser::parse_util::{discard_ignored, require_ignored}; + +#[derive(Debug)] +pub struct ImplToken { + location: Location, + name: String, + functions: Vec +} + +pub fn parse_impl(s: Span) -> ParseResult { + let location = Location::from_span(s); + let (s, _) = tag("impl").parse(s)?; + let (s, _) = require_ignored(s)?; + let (s, name) = parse_simple_name(s)?; + let (s, _) = discard_ignored(s); + let (s, contents) = braced_section(s)?; + + let mut functions = Vec::new(); + + let mut c = contents; + loop { + let (cs, _) = discard_ignored(c); + if cs.is_empty() { + break; + } + let (cs, function) = parse_function(cs)?; + + functions.push(function); + c = cs; + } + + Ok(( + s, + ImplToken { + location, + name: name.to_string(), + functions + } + )) +} \ No newline at end of file diff --git a/src/root/nom_parser/parse_name.rs b/src/root/nom_parser/parse_name.rs new file mode 100644 index 0000000..28dc430 --- /dev/null +++ b/src/root/nom_parser/parse_name.rs @@ -0,0 +1,103 @@ +use nom::bytes::complete::take_till; +use nom::Err::Error; +use nom::InputTake; +use nom_supreme::error::{BaseErrorKind, Expectation}; +use crate::root::nom_parser::parse::{Location, ParseResult, Span, TypeErrorTree}; +use crate::root::nom_parser::parse_fn::base::EvaluableToken; + +#[derive(Debug)] +enum NameConnectors { + NonStatic, + Static +} + +#[derive(Debug)] +pub struct NameToken { + location: Location, + base: String, + names: Vec<(NameConnectors, String)>, + function_call: Option> +} + +pub fn parse_full_name(s: Span) -> ParseResult { + // TODO: Handle function calls + + let location = Location::from_span(s); + + let (mut s, base_name) = parse_simple_name(s)?; + let mut names = Vec::new(); + + loop { + let ns; + let connector = if let Some(next) = s.chars().next() { + if next == '.' { + ns = s.take_split(1).0; + NameConnectors::NonStatic + } + else if next == ':' && s.chars().nth(1).is_some_and(|c| c == ':') { + ns = s.take_split(2).0; + NameConnectors::Static + } + else { + break; + } + } + else { + break; + }; + + let (ns, name) = parse_simple_name(ns)?; + + names.push(( + connector, + name.to_string()) + ); + + s = ns; + } + + Ok(( + s, + NameToken { + location, + base: base_name.to_string(), + names, + function_call: None, + } + )) +} + +pub fn parse_simple_name(s: Span) -> ParseResult { + let (s, n) = take_till(|c: char| { + c.is_whitespace() || + (!c.is_alphabetic() && c != '_') + })(s)?; + + if let Some(first) = s.chars().next() { + if first.is_ascii_digit() { + return Err(Error( + TypeErrorTree::Base { + location: s, + kind: BaseErrorKind::Expected( + Expectation::Space + ), + } + )) + } + } + + if n.is_empty() { + Err(Error( + TypeErrorTree::Base { + location: s, + kind: BaseErrorKind::Expected( + Expectation::Alpha + ), + } + )) + } + else { + Ok((s, n)) + } + +} diff --git a/src/root/nom_parser/parse_parameters.rs b/src/root/nom_parser/parse_parameters.rs new file mode 100644 index 0000000..fcfe047 --- /dev/null +++ b/src/root/nom_parser/parse_parameters.rs @@ -0,0 +1,33 @@ +use nom::character::complete::char; +use crate::root::nom_parser::parse::{ParseResult, Span}; +use crate::root::nom_parser::parse_name::{NameToken, parse_full_name, parse_simple_name}; +use crate::root::nom_parser::parse_util::discard_ignored; + +pub type Parameters = Vec<(String, NameToken)>; + +pub fn parse_parameters(s: Span) -> ParseResult<(), Parameters> { + let (mut s, _) = discard_ignored(s); + + let mut parameters = Vec::new(); + + while !s.is_empty() { + let (ns, name) = parse_simple_name(s)?; + let (ns, _) = discard_ignored(ns); + let (ns, _) = char(':')(ns)?; + let (ns, _) = discard_ignored(ns); + let (ns, name_token) = parse_full_name(ns)?; + let (ns, _) = discard_ignored(ns); + + parameters.push((name.to_string(), name_token)); + + if ns.is_empty() { + break; + } + + let (ns, _) = char(',')(ns)?; + let (ns, _) = discard_ignored(ns); + s = ns; + } + + Ok(((), parameters)) +} \ No newline at end of file diff --git a/src/root/nom_parser/parse_struct.rs b/src/root/nom_parser/parse_struct.rs new file mode 100644 index 0000000..b557e3b --- /dev/null +++ b/src/root/nom_parser/parse_struct.rs @@ -0,0 +1,34 @@ +use nom_supreme::tag::complete::tag; +use nom::Parser; +use crate::root::nom_parser::parse::{Location, ParseResult, Span}; +use crate::root::nom_parser::parse_blocks::braced_section; +use crate::root::nom_parser::parse_name::{NameToken, parse_simple_name}; +use crate::root::nom_parser::parse_parameters::{Parameters, parse_parameters}; +use crate::root::nom_parser::parse_toplevel::TopLevelTokens; +use crate::root::nom_parser::parse_util::{discard_ignored, require_ignored}; + +#[derive(Debug)] +pub struct StructToken { + location: Location, + name: String, + attributes: Parameters +} + +pub fn parse_struct(s: Span) -> ParseResult { + let location = Location::from_span(s); + let (s, _) = tag("struct").parse(s)?; + let (s, _) = require_ignored(s)?; + let (s, name) = parse_simple_name(s)?; + let (s, _) = discard_ignored(s); + let (s, contents) = braced_section(s)?; + let (_, parameters) = parse_parameters(contents)?; + + Ok(( + s, + StructToken { + location, + name: name.to_string(), + attributes: parameters + } + )) +} diff --git a/src/root/nom_parser/parse_toplevel.rs b/src/root/nom_parser/parse_toplevel.rs new file mode 100644 index 0000000..c74149d --- /dev/null +++ b/src/root/nom_parser/parse_toplevel.rs @@ -0,0 +1,39 @@ +use nom::branch::alt; +use nom::Parser; +use crate::root::nom_parser::parse::{ParseResult, Span}; +use crate::root::nom_parser::{parse_util}; +use crate::root::nom_parser::parse_fn::{FunctionToken, parse_function}; +use crate::root::nom_parser::parse_impl::{ImplToken, parse_impl}; +use crate::root::nom_parser::parse_struct::{parse_struct, StructToken}; + +#[derive(Debug)] +pub enum TopLevelTokens { + Struct(StructToken), + Impl(ImplToken), + Function(FunctionToken), +} + +pub fn parse_toplevel(s: Span) -> ParseResult> { + let mut s = s; + let mut tokens = Vec::new(); + + loop { + let ns = s; + let (ns, _) = parse_util::discard_ignored(ns); + + if ns.is_empty() { + return Ok((ns, tokens)) + } + + let (ns, token) = alt(( + |x| parse_impl(x).map(|(s, i)| (s, TopLevelTokens::Impl(i))), + |x| parse_function(x).map(|(s, f)| (s, TopLevelTokens::Function(f))), + |x| parse_struct(x).map(|(s, stru)| (s, TopLevelTokens::Struct(stru))), + )) + .parse(ns)?; + + tokens.push(token); + + s = ns; + } +} diff --git a/src/root/nom_parser/parse_util.rs b/src/root/nom_parser/parse_util.rs new file mode 100644 index 0000000..798d8ec --- /dev/null +++ b/src/root/nom_parser/parse_util.rs @@ -0,0 +1,54 @@ +use nom::sequence::Tuple; +use nom::bytes::complete::{take_till, take_while}; +use nom_supreme::error::{BaseErrorKind, Expectation}; +use nom::error::ParseError; +use nom::{InputTakeAtPosition, IResult, Parser}; +use crate::root::nom_parser::parse::{ParseResult, Span, TypeErrorTree}; +use crate::root::nom_parser::parse_comments; + +pub fn take_whitespace(s: Span) -> ParseResult { + take_while(|c: char| c.is_whitespace())(s) +} + +pub fn discard_ignored(s: Span) -> (Span, bool) { + let mut s = s; + let mut ever_found = false; + let mut found = true; + while found { + found = false; + if let Ok((ns, _)) = parse_comments::parse_comment(s) { + s = ns; + found = true; + ever_found = true; + } + if let Ok((ns, p)) = take_whitespace(s) { + if !p.is_empty() { + s = ns; + found = true; + ever_found = true; + } + } + } + + (s, ever_found) +} + +pub fn require_ignored(s: Span) -> ParseResult { + let (s, i) = discard_ignored(s); + if !i { + return Err(nom::Err::Error( + TypeErrorTree::Base { + location: s, + kind: BaseErrorKind::Expected(Expectation::Space), + } + )) + } + Ok((s, ())) +} + +pub fn take_till_whitespace>() -> impl Fn(Input) -> IResult + where + Input: InputTakeAtPosition +{ + take_till(|c: char| c.is_whitespace()) +} diff --git a/src/root/parser/escape_codes.rs b/src/root/parser/escape_codes.rs new file mode 100644 index 0000000..0344a90 --- /dev/null +++ b/src/root/parser/escape_codes.rs @@ -0,0 +1,7 @@ +pub fn get_escape_code(code: char) -> Option { + match code { + 'n' => Some('\n'), + '\\' => Some('\\'), + _ => None, + } +} diff --git a/src/root/parser/file_reader.rs b/src/root/parser/file_reader.rs new file mode 100644 index 0000000..67ef05d --- /dev/null +++ b/src/root/parser/file_reader.rs @@ -0,0 +1,181 @@ +use crate::root::parser::line_info::LineInfo; + +use std::path::PathBuf; +use std::rc::Rc; + +pub struct FileReader { + path: Rc, + data: String, + cursor: usize, + line_start: usize, + line: usize, + checkpoint: (usize, usize), +} + +#[allow(dead_code)] +impl FileReader { + pub fn new(path: PathBuf, data: String) -> FileReader { + FileReader { + path: Rc::new(path), + data, + cursor: 0, + line_start: 0, + line: 1, + checkpoint: (1, 0), + } + } + + pub fn get_line_info(&self) -> LineInfo { + LineInfo::new(self.path.clone(), self.checkpoint.0, self.checkpoint.1) + } + + pub fn get_line_info_changed(&self, line: usize, char_start: usize) -> LineInfo { + LineInfo::new(self.path.clone(), line, char_start) + } + + pub fn get_line_info_current(&self) -> LineInfo { + if self.cursor - self.line_start == 0 { + LineInfo::new(self.path.clone(), self.line, self.cursor - self.line_start) + } else { + LineInfo::new( + self.path.clone(), + self.line, + self.cursor - self.line_start - 1, + ) + } + } + + pub fn get_line_char(&self) -> (usize, usize) { + if self.cursor - self.line_start < 2 { + (self.line, self.cursor - self.line_start) + } else { + (self.line, self.cursor - self.line_start - 2) + } + } + + pub fn checkpoint(&mut self) -> (usize, usize) { + self.checkpoint = self.get_line_char(); + self.checkpoint + } + + pub fn line(&self) -> usize { + self.line + } + + pub fn get_path(&self) -> Rc { + self.path.clone() + } + + pub fn move_read_any(&mut self) -> Option { + let c = self.data.chars().nth(self.cursor); + if c.is_some() { + self.cursor += 1; + if c.unwrap() == '\n' { + self.line += 1; + self.line_start = self.cursor; + } + } + c + } + + pub fn read_until_char(&self, c: char) -> (String, bool) { + let mut out = String::new(); + + let chars = self.data.chars().skip(self.cursor); + + let mut eof = true; + + for char in chars { + if char == c { + eof = false; + break; + } + out.push(char); + } + + (out, eof) + } + + pub fn skip_until_newline(&mut self) -> (String, bool) { + let mut out = String::new(); + + let chars = self.data.chars().skip(self.cursor); + + let mut eof = true; + + let mut i = 0; + for char in chars { + i += 1; + if char == '\n' || char == '\r' { + eof = false; + break; + } + out.push(char); + } + + self.cursor += i; + + (out, eof) + } + + pub fn move_to_next_char(&mut self, c: char) { + let chars = self.data.chars().skip(self.cursor); + + for char in chars { + self.cursor += 1; + if char == '\n' { + self.line += 1; + self.line_start = self.cursor; + } + if char == c { + break; + } + } + } + + pub fn move_read_to_next_char(&mut self, c: char) -> (String, bool) { + let mut out = String::new(); + + let chars = self.data.chars().skip(self.cursor); + + let mut eof = true; + + for char in chars { + self.cursor += 1; + + if char == '\n' { + self.line += 1; + self.line_start = self.cursor; + } + + if char == c { + eof = false; + break; + } + out.push(char); + } + + (out, eof) + } + + pub fn skip_whitespace(&mut self) -> bool { + let chars = self.data.chars().skip(self.cursor); + + let mut eof = true; + + for char in chars { + if !char.is_whitespace() { + eof = false; + break; + } + + self.cursor += 1; + } + + eof + } + + pub fn all_read(&self) -> bool { + self.cursor >= self.data.len() + } +} diff --git a/src/root/parser/initialiser_parser.rs b/src/root/parser/initialiser_parser.rs new file mode 100644 index 0000000..4b813d9 --- /dev/null +++ b/src/root/parser/initialiser_parser.rs @@ -0,0 +1,73 @@ +use crate::root::ast::literals::Literal; +use crate::root::basic_ast::punctuation::Punctuation; +use crate::root::basic_ast::symbol::{BasicSymbol, NAME_VALID_CHARS}; +use crate::root::parser::file_reader::FileReader; +use crate::root::parser::line_info::LineInfo; +use crate::root::parser::normal_parser::parse_normal; +use crate::root::parser::parse::{BlockType, ParseError}; + +pub fn parse_initialiser( + symbols: &mut Vec<(BasicSymbol, LineInfo)>, + reader: &mut FileReader, +) -> Result<(), ParseError> { + let line_info = reader.checkpoint(); + let (name, eof) = reader.move_read_to_next_char('{'); + let name = name.trim(); + for c in name.chars() { + if !NAME_VALID_CHARS.contains(&c) { + let mut utf8 = vec![0; c.len_utf8()]; + c.encode_utf8(&mut utf8); + return Err(ParseError::BadName(reader.get_line_info(), c, utf8)); + } + } + if name.is_empty() { + return Err(ParseError::NoInitialiserType( + reader.get_line_info_current(), + )); + } + + if eof { + return Err(ParseError::NoInitialiserContents( + reader.get_line_info_current(), + )); + } + + let BasicSymbol::BracedSection(attributes) = parse_normal(reader, BlockType::Braces)? else { + panic!() + }; + let mut attributes: Vec> = + attributes.into_iter().fold(Vec::new(), |mut v, next| { + let last = if let Some(last) = v.last_mut() { + last + } else { + v.push(Vec::new()); + v.last_mut().unwrap() + }; + + if matches!(next.0, BasicSymbol::Punctuation(Punctuation::ListSeparator)) { + v.push(Vec::new()) + } else { + last.push(next) + } + v + }); + + for (i, attribute) in attributes.iter().enumerate() { + if attribute.is_empty() && i + 1 != attributes.len() { + return Err(ParseError::NoInitialiserAttribute( + reader.get_line_info_current(), + )); + } + } + + if attributes.last().is_some_and(|x| x.is_empty()) { + attributes.pop(); + } + + symbols.push(( + BasicSymbol::Literal(Literal::Initialiser(name.to_string(), attributes)), + reader.get_line_info_changed(line_info.0, line_info.1), + )); + + Ok(()) +} diff --git a/src/root/parser/line_info.rs b/src/root/parser/line_info.rs new file mode 100644 index 0000000..ab82d7f --- /dev/null +++ b/src/root/parser/line_info.rs @@ -0,0 +1,95 @@ +use std::fmt::{Display, Formatter}; +use std::fs; +use std::path::PathBuf; +use std::rc::Rc; + +#[derive(Clone, Debug)] +pub struct LineInfo { + file: Option>, + line: usize, + char_start: usize, +} + +#[allow(dead_code)] +impl LineInfo { + pub fn new(file: Rc, line: usize, char_start: usize) -> LineInfo { + LineInfo { + file: Some(file), + line, + char_start, + } + } + + pub fn builtin() -> LineInfo { + LineInfo { + file: None, + line: 0, + char_start: 0, + } + } + + pub fn line(&self) -> usize { + self.line + } + + pub fn file(&self) -> Option> { + self.file.clone() + } +} + +impl Display for LineInfo { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if self.file.is_none() { + writeln!(f, "In builtin function")?; + return Ok(()); + } + + writeln!(f, "In file: {:?}", self.file.as_ref().unwrap().as_os_str())?; + let line_text = self.line.to_string(); + write!(f, "{}| ", line_text)?; + let t = fs::read_to_string(self.file.as_ref().unwrap().as_ref()).unwrap(); + let line = t.split('\n').nth(self.line - 1).unwrap(); + + let mut changed_start = false; + let mut start = 0; + if self.char_start > 20 { + start = self.char_start - 10; + changed_start = true; + } + + let mut changed_end = false; + let mut end = line.chars().count() - 1; + if line.chars().count() - self.char_start > 10 { + end = self.char_start + 10; + changed_end = true; + } + + if changed_start { + write!(f, "... ")?; + } + + write!( + f, + "{}", + &line[line.char_indices().nth(start).unwrap().0 + ..line.char_indices().nth(end).unwrap().0] + )?; + if changed_end { + write!(f, " ...")?; + } + writeln!(f)?; + + let mut offset = line_text.len() + 2; + if changed_start { + offset += 4 + 10; + } else { + offset += self.char_start; + } + for _ in 0..offset { + write!(f, " ")?; + } + write!(f, "^ here")?; + + Ok(()) + } +} diff --git a/src/root/parser/mod.rs b/src/root/parser/mod.rs new file mode 100644 index 0000000..db715e9 --- /dev/null +++ b/src/root/parser/mod.rs @@ -0,0 +1,7 @@ +mod escape_codes; +mod file_reader; +mod initialiser_parser; +pub mod line_info; +mod normal_parser; +pub mod parse; +mod string_parser; diff --git a/src/root/parser/normal_parser.rs b/src/root/parser/normal_parser.rs new file mode 100644 index 0000000..5d960cd --- /dev/null +++ b/src/root/parser/normal_parser.rs @@ -0,0 +1,364 @@ +use crate::root::ast::keywords::Keyword; +use crate::root::ast::literals::Literal; +use crate::root::ast::operators; +use crate::root::ast::operators::Operator; +use crate::root::basic_ast::punctuation::Punctuation; +use crate::root::basic_ast::symbol::{BasicSymbol, NameAccessType, NameType, NAME_VALID_CHARS}; +use crate::root::parser::file_reader::FileReader; +use crate::root::parser::initialiser_parser::parse_initialiser; +use crate::root::parser::line_info::LineInfo; +use crate::root::parser::parse::{BlockType, ParseError}; +use crate::root::parser::string_parser::parse_string; + +pub fn parse_normal( + reader: &mut FileReader, + block_type: BlockType, +) -> Result { + let start_line = reader.line(); + + let mut buffer = String::new(); + + let mut operator_mode = false; + + let mut symbols: Vec<(BasicSymbol, LineInfo)> = Vec::new(); + + reader.checkpoint(); + loop { + let next = reader.move_read_any(); + reader.checkpoint(); + + // * EOF + if next.is_none() { + process_buffer(&mut buffer, &mut operator_mode, &mut symbols, reader)?; + return if matches!(block_type, BlockType::Base) { + Ok(BasicSymbol::AbstractSyntaxTree(symbols)) + } else { + let terminator = match block_type { + BlockType::Braces => Some('}'), + BlockType::Brackets => Some(')'), + BlockType::SquareBrackets => Some(']'), + BlockType::Base => None, + }; + + Err(ParseError::NotClosed( + reader.get_line_info(), + terminator.unwrap(), + start_line, + )) + }; + } + + let next = next.unwrap(); + + // * Opening/Closing blocks + match next { + '"' => { + process_buffer(&mut buffer, &mut operator_mode, &mut symbols, reader)?; + let _start = reader.line(); + reader.checkpoint(); + symbols.push((parse_string(reader)?, reader.get_line_info())); + continue; + } + c => { + let closed_block = match c { + '}' => Some(BlockType::Braces), + ')' => Some(BlockType::Brackets), + ']' => Some(BlockType::SquareBrackets), + _ => None, + }; + + if let Some(closed_block) = closed_block { + process_buffer(&mut buffer, &mut operator_mode, &mut symbols, reader)?; + return if closed_block != block_type { + Err(ParseError::NoOpening(reader.get_line_info_current(), c)) + } else { + Ok(match block_type { + BlockType::Braces => BasicSymbol::BracedSection(symbols), + BlockType::Brackets => BasicSymbol::BracketedSection(symbols), + BlockType::SquareBrackets => { + BasicSymbol::SquareBracketedSection(symbols) + } + _ => panic!(), + }) + }; + } + + let new_block = match c { + '{' => Some(BlockType::Braces), + '(' => Some(BlockType::Brackets), + '[' => Some(BlockType::SquareBrackets), + _ => None, + }; + + if let Some(new_block) = new_block { + process_buffer(&mut buffer, &mut operator_mode, &mut symbols, reader)?; + + reader.checkpoint(); + let parsed_pos = reader.get_line_info(); + let parsed = parse_normal(reader, new_block)?; + + if !symbols.is_empty() + && matches!(symbols.last().unwrap().0, BasicSymbol::Name(_)) + && matches!(&parsed, BasicSymbol::BracketedSection(_)) + && matches!( + symbols + .last() + .unwrap() + .0 + .get_name_contents() + .last() + .unwrap() + .2, + NameType::Normal + ) + { + let (s, _l) = &mut symbols.last_mut().unwrap(); + let BasicSymbol::Name(v) = s else { panic!() }; + + let mut arguments = vec![Vec::new()]; + + let BasicSymbol::BracketedSection(symbols) = parsed else { + panic!(); + }; + for (symbol, line) in symbols { + match symbol { + BasicSymbol::Punctuation(Punctuation::ListSeparator) => { + arguments.push(Vec::new()) + } + symbol => arguments.last_mut().unwrap().push((symbol, line)), + } + } + if arguments.len() == 1 && arguments.last().unwrap().is_empty() { + arguments.pop(); + } + + v.last_mut().unwrap().2 = NameType::Function(arguments); + } else { + symbols.push((parsed, parsed_pos)); + } + continue; + } + } + } + + // * Process buffer + if next == ' ' || next == '\t' || next == '\n' || next == '\r' { + if buffer.is_empty() { + continue; + } + process_buffer(&mut buffer, &mut operator_mode, &mut symbols, reader)?; + continue; + } + + if operators::ALL_SYMBOLS.contains(&next) { + if !operator_mode { + process_buffer(&mut buffer, &mut operator_mode, &mut symbols, reader)?; + buffer.push(next); + operator_mode = true; + continue; + } + } else if operator_mode { + process_buffer(&mut buffer, &mut operator_mode, &mut symbols, reader)?; + buffer.push(next); + operator_mode = false; + continue; + } + + if next == ';' { + process_buffer(&mut buffer, &mut operator_mode, &mut symbols, reader)?; + symbols.push(( + BasicSymbol::Punctuation(Punctuation::Semicolon), + reader.get_line_info_current(), + )); + continue; + } + + if next == ',' { + process_buffer(&mut buffer, &mut operator_mode, &mut symbols, reader)?; + symbols.push(( + BasicSymbol::Punctuation(Punctuation::ListSeparator), + reader.get_line_info_current(), + )); + continue; + } + + if next == '¬' { + process_buffer(&mut buffer, &mut operator_mode, &mut symbols, reader)?; + symbols.push(( + BasicSymbol::Operator(Operator::HeapDealloc), + reader.get_line_info_current(), + )); + continue; + } + + if next == ':' { + process_buffer(&mut buffer, &mut operator_mode, &mut symbols, reader)?; + symbols.push(( + BasicSymbol::Punctuation(Punctuation::Colon), + reader.get_line_info_current(), + )); + continue; + } + + if next == '~' { + process_buffer(&mut buffer, &mut operator_mode, &mut symbols, reader)?; + symbols.push(( + BasicSymbol::Punctuation(Punctuation::Tilda), + reader.get_line_info_current(), + )); + continue; + } + + if next == '@' && buffer.is_empty() { + parse_initialiser(&mut symbols, reader)?; + continue; + } + + if next == '\\' { + process_buffer(&mut buffer, &mut operator_mode, &mut symbols, reader)?; + reader.skip_until_newline(); + continue; + } + + if buffer.is_empty() { + reader.checkpoint(); + } + buffer.push(next) + } +} + +fn process_buffer( + buffer: &mut String, + operator_mode: &mut bool, + symbols: &mut Vec<(BasicSymbol, LineInfo)>, + reader: &FileReader, +) -> Result<(), ParseError> { + if buffer.is_empty() { + return Ok(()); + } + + if *operator_mode { + symbols.push(( + process_operator_buffer(buffer, reader)?, + reader.get_line_info(), + )); + *operator_mode = false; + buffer.clear(); + return Ok(()); + } + + if buffer == "true" { + symbols.push(( + BasicSymbol::Literal(Literal::Bool(true)), + reader.get_line_info(), + )); + buffer.clear(); + return Ok(()); + } + if buffer == "false" { + symbols.push(( + BasicSymbol::Literal(Literal::Bool(false)), + reader.get_line_info(), + )); + buffer.clear(); + return Ok(()); + } + + if let Ok(val) = buffer.parse() { + symbols.push(( + BasicSymbol::Literal(Literal::Int(val)), + reader.get_line_info(), + )); + buffer.clear(); + return Ok(()); + } + + if let Ok(val) = buffer.parse::() { + symbols.push(( + BasicSymbol::Literal(Literal::Float(val)), + reader.get_line_info(), + )); + buffer.clear(); + return Ok(()); + } + + let mut sections = Vec::new(); + let mut section_buffer = String::new(); + let mut section_type = NameType::Normal; + let mut last_separator = NameAccessType::Base; + let mut indirection: usize = 0; + for c in buffer.chars() { + if c == '.' { + sections.push((section_buffer, last_separator, section_type, indirection)); + section_buffer = String::new(); + section_type = NameType::Normal; + last_separator = NameAccessType::Normal; + indirection = 0; + continue; + } + if c == '#' { + sections.push((section_buffer, last_separator, section_type, indirection)); + section_buffer = String::new(); + section_type = NameType::Normal; + last_separator = NameAccessType::Static; + indirection = 0; + continue; + } + + if c == '$' && section_buffer.is_empty() { + indirection += 1; + continue; + } + + if !NAME_VALID_CHARS.contains(&c) { + let mut utf8 = Vec::with_capacity(c.len_utf8()); + for _ in 0..c.len_utf8() { + utf8.push(0); + } + c.encode_utf8(&mut utf8); + return Err(ParseError::BadName(reader.get_line_info(), c, utf8)); + } + section_buffer.push(c); + } + + sections.push((section_buffer, last_separator, section_type, indirection)); + + if let Some(kwd) = Keyword::get_enum(§ions.first().unwrap().0) { + if sections.len() > 1 { + return Err(ParseError::KeywordFollowed( + reader.get_line_info(), + sections.first().unwrap().0.clone(), + )); + } + symbols.push((BasicSymbol::Keyword(kwd), reader.get_line_info())); + buffer.clear(); + } else { + symbols.push((BasicSymbol::Name(sections), reader.get_line_info())); + buffer.clear(); + } + + Ok(()) +} + +fn process_operator_buffer( + buffer: &String, + reader: &FileReader, +) -> Result { + let operator = Operator::get_operator(buffer.as_str()); + if let Some(operator) = operator { + return Ok(BasicSymbol::Operator(operator)); + } else if buffer == "=" { + return Ok(BasicSymbol::Assigner(None)); + } else if buffer.ends_with('=') { + if let Some(operator) = + Operator::get_operator(&buffer[..buffer.char_indices().last().unwrap().0]) + { + return Ok(BasicSymbol::Assigner(Some(operator))); + } + } + + Err(ParseError::OperatorNotRecognised( + reader.get_line_info(), + buffer.clone(), + )) +} diff --git a/src/root/parser/parse.rs b/src/root/parser/parse.rs new file mode 100644 index 0000000..9f68d79 --- /dev/null +++ b/src/root/parser/parse.rs @@ -0,0 +1,110 @@ +use std::{fs, io}; +use std::path::PathBuf; + +use same_file::is_same_file; +use thiserror::Error; + +use ParseError::Nested; + +use crate::root::ast::keywords::MOD_KEYWORD; +use crate::root::basic_ast::symbol::{BasicAbstractSyntaxTree, BasicSymbol}; +use crate::root::parser::file_reader::FileReader; +use crate::root::parser::line_info::LineInfo; +use crate::root::parser::normal_parser::parse_normal; +use crate::root::parser::parse::ParseError::ModNotFollowedByPath; + +#[derive(Error, Debug)] +pub enum ParseError { + #[error("File read error on path '{0}'")] + FileRead(PathBuf, io::Error), + #[error("Error: {1}\n{0}")] + Nested(LineInfo, Box), + #[error("Error: Operator '{1}' not recognised\n{0}")] + OperatorNotRecognised(LineInfo, String), + #[error("Error: 'mod' must be followed by a path\n{0}")] + ModNotFollowedByPath(LineInfo), + #[error("Error: Keyword ('{1}') cannot be followed by . or #\n{0}")] + KeywordFollowed(LineInfo, String), + #[error("Error: Closing '{1}' not found (started on line {2})\n{0}")] + NotClosed(LineInfo, char, usize), + #[error("Error: Unknown escape code '{1}'\n{0}")] + UnknownEscapeCode(LineInfo, char), + #[error("Error: String literal started on line {1} not closed\n{0}")] + UnclosedString(LineInfo, usize), + #[error("Error: Closing '{1}' found with no corresponding opening bracket\n{0}")] + NoOpening(LineInfo, char), + #[error("Error: Names cannot contain character '{1}' (UTF-8 Code: {2:?})\n{0}")] + BadName(LineInfo, char, Vec), + #[error("Initialiser must specify a type after '@'")] + NoInitialiserType(LineInfo), + #[error("Error: Initialiser type must be followed by braces containing attribute values\n{0}")] + NoInitialiserContents(LineInfo), + #[error("Error: Attribute cannot be empty (must be a value between commas)\n{0}")] + NoInitialiserAttribute(LineInfo) +} + +pub fn parse(path: PathBuf, asts: &mut Vec, files_followed: &mut Vec) -> Result<(), ParseError> { + for other_path in & *files_followed { + if is_same_file(&path, other_path).map_err(|x| ParseError::FileRead(path.clone(), x))? { + return Ok(()) + } + } + + let data = fs::read_to_string(&path); + + if let Err(e) = data { + return Err(ParseError::FileRead(path, e)); + } + let mut reader = FileReader::new(path.clone(), data.unwrap()); + + files_followed.push(path); + + // * IMPORT PHASE + { + while reader.read_until_char(' ').0 == MOD_KEYWORD { + reader.move_to_next_char(' '); + + reader.checkpoint(); + let (file, _eof) = reader.move_read_to_next_char(';'); + let trimmed = file.trim(); + if trimmed.is_empty() { + return Err(ModNotFollowedByPath(reader.get_line_info())); + } + + if let Err(e) = parse(PathBuf::from(file), asts, files_followed) { + return Err(Nested(reader.get_line_info(), Box::new(e))); + } + } + } + + reader.checkpoint(); + let ast = parse_normal(&mut reader, BlockType::Base)?; + + let inner = match ast { + BasicSymbol::AbstractSyntaxTree(inner) => inner, + _ => panic!(), + }; + + asts.push(inner); + + Ok(()) +} + +#[derive(PartialEq)] +pub enum BlockType { + Base, + Braces, // start line + Brackets, // start line + SquareBrackets, // start line +} + +// fn recursively_parse_symbols(reader: &mut FileReader, block_type: BlockType) -> Result { +// match block_type { +// BlockType::String(start_line) => { +// parse_string(reader, start_line) +// } +// _ => { +// parse_normal(reader, block_type) +// } +// } +// } diff --git a/src/root/parser/string_parser.rs b/src/root/parser/string_parser.rs new file mode 100644 index 0000000..8e15d88 --- /dev/null +++ b/src/root/parser/string_parser.rs @@ -0,0 +1,57 @@ +use crate::root::ast::literals::Literal; +use crate::root::basic_ast::symbol::BasicSymbol; +use crate::root::parser::escape_codes::get_escape_code; +use crate::root::parser::file_reader::FileReader; +use crate::root::parser::parse::ParseError; +use crate::root::parser::parse::ParseError::UnclosedString; + +const ESCAPE_CHAR: char = '\\'; +const STRING_LITERAL_TERMINATOR: char = '"'; + +pub fn parse_string(reader: &mut FileReader) -> Result { + let start_line = reader.line(); + let mut string = String::new(); + + let mut escape = false; + + let mut eof = true; + + loop { + let next = reader.move_read_any(); + if next.is_none() { + break; + } + let next = next.unwrap(); + + if escape { + let char = get_escape_code(next); + if char.is_none() { + return Err(ParseError::UnknownEscapeCode( + reader.get_line_info_current(), + next, + )); + } + string.push(char.unwrap()); + escape = false; + continue; + } + + if next == ESCAPE_CHAR { + escape = true; + continue; + } + + if next == STRING_LITERAL_TERMINATOR { + eof = false; + break; + } + + string.push(next) + } + + if eof { + return Err(UnclosedString(reader.get_line_info_current(), start_line)); + } + + Ok(BasicSymbol::Literal(Literal::String(string))) +} diff --git a/src/root/runner.rs b/src/root/runner.rs new file mode 100644 index 0000000..ce6f3e3 --- /dev/null +++ b/src/root/runner.rs @@ -0,0 +1,111 @@ +use std::fs; +use std::process::Command; + +use color_print::cprintln; + +use crate::ret_time; +use crate::root::utils::try_run_program; + +#[cfg(target_os = "windows")] +pub fn run(output: &str) { + let time; + ret_time!(time, + let full = fs::canonicalize(format!("{output}.exe")).unwrap(); + let code = match Command::new(full).status() { + Ok(r) => { + match r.code() { + Some(c) => c, + None => { + cprintln!("\nProcess did not return an exit code. \ + This could be due to a forceful termination"); + return; + } + } + } + Err(e) => { + cprintln!("Starting process failed with error:\n{}", e); + return; + } + }; + ); + + // ? Here to circumvent some timing issues + thread::sleep(Duration::from_millis(100)); + println!("\nExited with return code {}", code); + cprintln!("Completed [{:?}]", time); +} + +#[cfg(target_os = "linux")] +pub fn run_wine_experimental(output: &str) -> Result<(), ()> { + let time; + ret_time!(time, + let full = fs::canonicalize(format!("{output}.exe")).unwrap(); + let code = try_run_program("wine", Command::new("wine").args([full]).status())? + .code() + .unwrap(); + ); + + println!( + "\nExited with return code {}", + code + ); + cprintln!("Completed [{:?}]", time); + Ok(()) +} + +pub fn assemble(output: &str) -> Result<(), ()> { + if !try_run_program("nasm", Command::new("nasm") + .args(["-f", "win64", format!("{output}.asm").as_str()]) + .status())? + .success() + { + cprintln!("NASM assembler step failed"); + return Err(()) + } + Ok(()) +} + +#[cfg(target_os = "windows")] +pub fn link(output: &str) -> Result<(), ()> { + if !try_run_program("link", Command::new("link") + .args([ + // "/entry:main", + format!("/out:{output}.exe").as_str(), + "/SUBSYSTEM:CONSOLE", + // "/LARGEADDRESSAWARE:NO", + format!("{output}.obj").as_str(), + ".\\libs\\kernel32.lib", + ".\\libs\\msvcrt.lib", + ".\\libs\\legacy_stdio_definitions.lib", + ".\\libs\\legacy_stdio_wide_specifiers.lib", + ".\\libs\\vcruntime.lib", + ".\\libs\\ucrt.lib", + ]) + .status())? + .success() + { + cprintln!("MSVC linking step failed"); + return Err(()) + } + + Ok(()) +} + +#[cfg(target_os = "linux")] +pub fn link_gcc_experimental(output: &str) -> Result<(), ()> { + if !try_run_program("x86_64-w64-mingw32-gcc", Command::new("x86_64-w64-mingw32-gcc") + .args([ + format!("{output}.obj").as_str(), + "./libs/kernel32.lib", + "-o", + format!("{output}.exe").as_str(), + ]) + .status())? + .success() + { + cprintln!("gcc linking step failed"); + return Err(()); + } + + Ok(()) +} diff --git a/src/root/utils.rs b/src/root/utils.rs new file mode 100644 index 0000000..1fff3e4 --- /dev/null +++ b/src/root/utils.rs @@ -0,0 +1,65 @@ +use std::io; +use std::io::ErrorKind; +use std::ops::{Add, Rem, Sub}; +use std::process::ExitStatus; +use crate::root::parser::parse::ParseError; +use crate::root::name_resolver::processor::ProcessorError; + +pub fn try_run_program(name: &str, exit_status: io::Result) -> Result { + match exit_status { + Ok(e) => Ok(e), + Err(e) => { + if matches!(e.kind(), ErrorKind::NotFound) { + println!("Program `{name}` not found. Check to make sure it is in your path"); + } + else { + println!("Running `{name}` failed with error:\n{e}") + } + Err(()) + } + } +} + +#[macro_export] +macro_rules! time { + ($($tts:tt)*) => { + let t = std::time::Instant::now(); + $($tts)* + let end = t.elapsed(); + color_print::cprintln!("Completed [{:?}]", end); + }; +} + +#[macro_export] +macro_rules! ret_time { + ($out: expr, $($tts:tt)*) => { + let t = std::time::Instant::now(); + $($tts)* + $out = t.elapsed(); + }; +} + +pub fn align + Rem + Add>( + num: T, + alignment: T, +) -> T { + num + (alignment - (num % alignment)) % alignment +} + +pub enum AnyError { + Parse(ParseError), + Processing(ProcessorError), + Other +} + +impl From for AnyError { + fn from(value: ParseError) -> Self { + AnyError::Parse(value) + } +} + +impl From for AnyError { + fn from(value: ProcessorError) -> Self { + AnyError::Processing(value) + } +} \ No newline at end of file diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..3602361 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1 @@ +temp \ No newline at end of file diff --git a/tests/inputs/control/If.why b/tests/inputs/control/If.why new file mode 100644 index 0000000..ae3e653 --- /dev/null +++ b/tests/inputs/control/If.why @@ -0,0 +1,21 @@ +fn main() ~ int { + let a: bool = true; + + if (a) { + printi(7); + } + else { + printi(9); + }; + + let b: bool = false; + + if (b) { + printi(7); + } + else { + printi(9); + }; + + return 0; +} \ No newline at end of file diff --git a/tests/inputs/print/SimplePrint.why b/tests/inputs/print/SimplePrint.why new file mode 100644 index 0000000..996fe13 --- /dev/null +++ b/tests/inputs/print/SimplePrint.why @@ -0,0 +1,7 @@ +fn main() ~ int { + printi(121); + printi(-1121); + printb(true); + printb(false); + return 0; +} \ No newline at end of file diff --git a/tests/outputs/control/If.why b/tests/outputs/control/If.why new file mode 100644 index 0000000..78bd770 --- /dev/null +++ b/tests/outputs/control/If.why @@ -0,0 +1,2 @@ +7 +9 \ No newline at end of file diff --git a/tests/outputs/print/SimplePrint.why b/tests/outputs/print/SimplePrint.why new file mode 100644 index 0000000..b967b12 --- /dev/null +++ b/tests/outputs/print/SimplePrint.why @@ -0,0 +1,4 @@ +121 +-1121 +true +false \ No newline at end of file diff --git a/tests/tests.rs b/tests/tests.rs new file mode 100644 index 0000000..2296b2c --- /dev/null +++ b/tests/tests.rs @@ -0,0 +1,66 @@ +use std::fs; +use std::path::PathBuf; +use std::process::Command; +use whython_7::root::main_args; +use whython_7::root::Args; + +#[test] +fn tests() { + fs::create_dir_all("tests/temp").unwrap(); + + for entry in get_files(PathBuf::from("tests/inputs")) { + let in_path = PathBuf::from("tests/inputs").join(entry.clone()); + + let out_path = PathBuf::from("tests/outputs").join(entry); + + compare( + in_path.into_os_string().into_string().unwrap(), + out_path.into_os_string().into_string().unwrap(), + ); + } +} + +fn get_files(base_dir: PathBuf) -> Vec { + let mut out = Vec::new(); + walk_dir(&base_dir, PathBuf::new(), &mut out); + out +} + +fn walk_dir(base_dir: &PathBuf, prefix: PathBuf, out: &mut Vec) { + for entry in base_dir.join(prefix.clone()).read_dir().unwrap() { + let entry = entry.unwrap(); + let path = entry.path(); + let emit = prefix.clone().join(path.file_name().unwrap()); + if path.is_dir() { + walk_dir(base_dir, emit, out); + continue; + } + + out.push(emit); + } +} + + +fn compare(in_path: String, out_path: String) { + println!("Testing `{}`", in_path); + + assert!(main_args(Args { + input: in_path, + output: String::from("tests/temp/out"), + build: true + }).is_ok()); + + let result = Command::new("tests/temp/out.exe").output().unwrap(); + + assert!(result.status.success()); + + let result = String::from_utf8(result.stdout) + .unwrap() + .replace('\0', "") + .replace("\r\n", "\n"); + + assert_eq!( + result.trim(), + fs::read_to_string(out_path).unwrap().replace("\r\n", "\n").trim() + ); +} diff --git a/types.toml b/types.toml new file mode 100644 index 0000000..7a3c3f5 --- /dev/null +++ b/types.toml @@ -0,0 +1,37 @@ +WindowsExit=0 +PrintI=1 +PrintB=2 +Bool=3 +BoolNot=4 +BoolEQ=5 +BoolNE=6 +Int=7 +IntAdd=8 +IntSub=9 +IntMul=10 +IntDiv=11 +IntMod=12 +IntLT=13 +IntGT=14 +IntLE=15 +IntGE=16 +IntEQ=17 +IntNE=18 +Float=19 +FloatAdd=20 +FloatSub=21 +FloatMul=22 +FloatDiv=23 +FloatMod=24 +FloatLT=25 +FloatGT=26 +FloatLE=27 +FloatGE=28 +FloatEQ=29 +FloatNE=30 +P=31 +Pr=32 +Pri=33 +Prin=34 +Print=35 +PrintF=36