mirror of
https://github.com/azur1s/bobbylisp.git
synced 2024-10-16 02:37:40 -05:00
:trollface:
This commit is contained in:
parent
7b108df8f5
commit
c82c3701ba
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -1,7 +1,4 @@
|
|||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
/target
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
@ -10,4 +7,4 @@ target/
|
|||
*.pdb
|
||||
|
||||
# Generated by the compiler
|
||||
/*.ts
|
||||
/*.js
|
161
Cargo.lock
generated
161
Cargo.lock
generated
|
@ -4,21 +4,20 @@ version = 3
|
|||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.3.8"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"const-random",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ariadne"
|
||||
version = "0.1.5"
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1cb2a2046bea8ce5e875551f5772024882de0b540c7f93dfc5d6cf1ca8b030c"
|
||||
dependencies = [
|
||||
"yansi",
|
||||
]
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
|
@ -28,65 +27,14 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
|
||||
[[package]]
|
||||
name = "chumsky"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d02796e4586c6c41aeb68eae9bfb4558a522c35f1430c14b40136c3706e09e4"
|
||||
checksum = "c4d619fba796986dd538d82660b76e0b9756c6e19b2e4d4559ba5a57f9f00810"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"hashbrown",
|
||||
"stacker",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "compiler"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"lower",
|
||||
"vm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-random"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368a7a772ead6ce7e1de82bfb04c485f3db8ec744f72925af5735e29a22cc18e"
|
||||
dependencies = [
|
||||
"const-random-macro",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-random-macro"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"proc-macro-hack",
|
||||
"tiny-keccak",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "entry"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"compiler",
|
||||
"lower",
|
||||
"parser",
|
||||
"vm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.8"
|
||||
|
@ -99,53 +47,60 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.138"
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||
|
||||
[[package]]
|
||||
name = "lower"
|
||||
version = "0.1.0"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
dependencies = [
|
||||
"parser",
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.16.0"
|
||||
name = "libc"
|
||||
version = "0.2.139"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||
|
||||
[[package]]
|
||||
name = "parser"
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "psm"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "renxi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ariadne",
|
||||
"chumsky",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.19"
|
||||
name = "stacker"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
|
||||
checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce"
|
||||
dependencies = [
|
||||
"crunchy",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"psm",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
|
@ -154,7 +109,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||
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-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
|
15
Cargo.toml
15
Cargo.toml
|
@ -1,8 +1,7 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"entry",
|
||||
"parser",
|
||||
"lower",
|
||||
"compiler",
|
||||
"vm",
|
||||
]
|
||||
[package]
|
||||
name = "renxi"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
chumsky = "0.9.0"
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
ko_fi: azur1s
|
176
LICENSE-APACHE
176
LICENSE-APACHE
|
@ -1,176 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
23
LICENSE-MIT
23
LICENSE-MIT
|
@ -1,23 +0,0 @@
|
|||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
13
README.md
13
README.md
|
@ -1,13 +0,0 @@
|
|||
!!! In development !!!
|
||||
|
||||
Update: There might be a stagnate in the development because my school is open and there will be a lot of assignments coming. Don't worry! I'm still learning more about compiler inner workings in my free time. I'll be back in a while.
|
||||
# Holymer
|
||||
A ML-inspired programming language for making back-end application.
|
||||
|
||||
## Contributing
|
||||
Please have [Rust programming language](https://github.com/rust-lang/rust) installed on your machine before building it.
|
||||
```shell
|
||||
$ git clone https://github.com/azur1s/holymer.git
|
||||
$ cd holymer
|
||||
# build with `cargo build` or just `cargo run -- filename`
|
||||
```
|
43
a.hlm
Normal file
43
a.hlm
Normal file
|
@ -0,0 +1,43 @@
|
|||
println("Hello, " + name + "!");
|
||||
|
||||
let a = 17, b = 35 in
|
||||
let c = a * 2 in
|
||||
println(b + c);
|
||||
|
||||
func foo (a: int, b: int) {
|
||||
let c = a * 2;
|
||||
|
||||
let res = b + c in
|
||||
return res + a;
|
||||
}
|
||||
|
||||
println((\x: int -> x + 1)(1));
|
||||
|
||||
──────────────────────────────────────────────────
|
||||
|
||||
(println (+ "Hello, " name "!"))
|
||||
|
||||
(let [a 17] [b 35]
|
||||
(let [c (* a 2)]
|
||||
(println (+ b c))))
|
||||
|
||||
(func foo [a int b int] (block
|
||||
(let [c (* a 2)])
|
||||
(let [res (+ b c)]
|
||||
(return (+ res a)))
|
||||
))
|
||||
|
||||
──────────────────────────────────────────────────
|
||||
|
||||
console.log("Hello, " + name + "!");
|
||||
|
||||
let a = 17;
|
||||
let b = 35;
|
||||
let c = a * 2;
|
||||
console.log(b + c);
|
||||
|
||||
const foo = (a, b) => {
|
||||
let c = a * 2;
|
||||
let res = b + c;
|
||||
return res + a;
|
||||
}
|
5
b.hlm
Normal file
5
b.hlm
Normal file
|
@ -0,0 +1,5 @@
|
|||
let foo : num = 1 in bar(foo) end
|
||||
|
||||
lambda (foo : num) -> unknown = bar(foo)
|
||||
|
||||
let x : t = e1 in e2 end
|
|
@ -1,8 +0,0 @@
|
|||
[package]
|
||||
name = "compiler"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
lower = { path = "../lower" }
|
||||
vm = { path = "../vm" }
|
|
@ -1,167 +0,0 @@
|
|||
#![allow(clippy::new_without_default)]
|
||||
#![allow(clippy::only_used_in_recursion)]
|
||||
use lower::model::{BinaryOp, Expr, Literal, Stmt, UnaryOp};
|
||||
use vm::model::Instr;
|
||||
|
||||
pub struct Compiler {}
|
||||
|
||||
impl Compiler {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn compile_expr(&mut self, expr: Expr) -> Vec<Instr> {
|
||||
match expr {
|
||||
Expr::Error => {
|
||||
println!("{:?}", expr);
|
||||
unreachable!()
|
||||
}
|
||||
Expr::Literal(x) => match x {
|
||||
Literal::Num(x) => vec![Instr::NumPush(x)],
|
||||
Literal::Bool(x) => vec![Instr::BoolPush(x)],
|
||||
Literal::Str(x) => vec![Instr::StrPush(x)],
|
||||
},
|
||||
Expr::Sym(name) => vec![Instr::Get(name)],
|
||||
Expr::Vec(xs) => {
|
||||
let mut instrs = vec![];
|
||||
let count = xs.len();
|
||||
for x in xs {
|
||||
instrs.extend(self.compile_expr(x));
|
||||
}
|
||||
instrs.push(Instr::ListMake(count));
|
||||
instrs
|
||||
}
|
||||
Expr::Unary(op, x) => {
|
||||
let mut instrs = self.compile_expr(*x);
|
||||
instrs.extend(match op {
|
||||
UnaryOp::Neg => vec![Instr::NumPush(-1), Instr::NumMul],
|
||||
UnaryOp::Not => vec![Instr::BoolNot],
|
||||
});
|
||||
instrs
|
||||
}
|
||||
Expr::Binary(op, x, y) => {
|
||||
let mut instrs = self.compile_expr(*y);
|
||||
instrs.extend(self.compile_expr(*x));
|
||||
instrs.push(match op {
|
||||
BinaryOp::Add => Instr::NumAdd,
|
||||
BinaryOp::Sub => Instr::NumSub,
|
||||
BinaryOp::Mul => Instr::NumMul,
|
||||
BinaryOp::Div => Instr::NumDiv,
|
||||
BinaryOp::Eq => Instr::NumEq,
|
||||
BinaryOp::Ne => Instr::NumNe,
|
||||
BinaryOp::Lt => Instr::NumLt,
|
||||
BinaryOp::Gt => Instr::NumGt,
|
||||
BinaryOp::Le => Instr::NumLe,
|
||||
BinaryOp::Ge => Instr::NumGe,
|
||||
BinaryOp::And => Instr::BoolAnd,
|
||||
BinaryOp::Or => Instr::BoolOr,
|
||||
BinaryOp::Pipe => todo!(),
|
||||
});
|
||||
instrs
|
||||
}
|
||||
Expr::Lambda(args, body) => {
|
||||
vec![Instr::FuncMake(args, self.compile_expr(*body))]
|
||||
}
|
||||
Expr::Call(f, xs) => {
|
||||
let mut instrs = vec![];
|
||||
for x in xs {
|
||||
instrs.extend(self.compile_expr(x));
|
||||
}
|
||||
match *f {
|
||||
Expr::Sym(ref fname) => match fname.as_str() {
|
||||
"print" => instrs.push(Instr::Print),
|
||||
"println" => instrs.push(Instr::PrintLn),
|
||||
_ => {
|
||||
instrs.extend(self.compile_expr(*f));
|
||||
instrs.push(Instr::FuncApply);
|
||||
}
|
||||
},
|
||||
Expr::Lambda(_, _) => {
|
||||
instrs.extend(self.compile_expr(*f));
|
||||
instrs.push(Instr::FuncApply);
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
instrs
|
||||
}
|
||||
Expr::Let(binds, body) => {
|
||||
let mut instrs = vec![];
|
||||
let binds = binds
|
||||
.into_iter()
|
||||
.flat_map(|(name, expr)| {
|
||||
let mut instrs = self.compile_expr(expr);
|
||||
instrs.extend(vec![Instr::Set(name)]);
|
||||
instrs
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if let Some(e) = body {
|
||||
// If there is a body then we put the bindings
|
||||
// inside the closure so it gets undefined outside
|
||||
// the scope
|
||||
instrs.extend(vec![
|
||||
Instr::FuncMake(
|
||||
vec![],
|
||||
binds.into_iter().chain(self.compile_expr(*e)).collect(),
|
||||
),
|
||||
Instr::FuncApply,
|
||||
]);
|
||||
} else {
|
||||
// If there is no body then we just push the bindings
|
||||
// to the global scope
|
||||
instrs.extend(binds);
|
||||
}
|
||||
instrs
|
||||
}
|
||||
Expr::If(c, t, f) => {
|
||||
let mut instrs = self.compile_expr(*c);
|
||||
let t = self.compile_expr(*t);
|
||||
if let Some(f) = f {
|
||||
let f = self.compile_expr(*f);
|
||||
instrs.push(Instr::JumpIfFalse(t.len() + 1));
|
||||
instrs.extend(t);
|
||||
instrs.push(Instr::Jump(f.len()));
|
||||
instrs.extend(f);
|
||||
} else {
|
||||
instrs.push(Instr::JumpIfFalse(t.len()));
|
||||
instrs.extend(t);
|
||||
}
|
||||
instrs
|
||||
}
|
||||
Expr::Do(es) => {
|
||||
let mut instrs = vec![];
|
||||
for e in es {
|
||||
instrs.extend(self.compile_expr(e));
|
||||
}
|
||||
instrs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile_stmt(&mut self, stmt: Stmt) -> Vec<Instr> {
|
||||
match stmt {
|
||||
Stmt::Fun(name, args, body) => {
|
||||
let is_main = name == "main";
|
||||
let mut instrs = match body {
|
||||
// If the body is a lambda then we don't have to compile
|
||||
// it into a function
|
||||
Expr::Lambda(_, _) => self.compile_expr(body),
|
||||
_ => vec![Instr::FuncMake(args, self.compile_expr(body))],
|
||||
};
|
||||
instrs.push(Instr::Set(name));
|
||||
if is_main {
|
||||
instrs.pop();
|
||||
instrs.push(Instr::FuncApply);
|
||||
}
|
||||
instrs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile_program(&mut self, stmts: Vec<Stmt>) -> Vec<Instr> {
|
||||
let mut instrs = vec![];
|
||||
for stmt in stmts {
|
||||
instrs.extend(self.compile_stmt(stmt));
|
||||
}
|
||||
instrs
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
[package]
|
||||
name = "entry"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
parser = { path = "../parser" }
|
||||
lower = { path = "../lower" }
|
||||
compiler = { path = "../compiler" }
|
||||
vm = { path = "../vm" }
|
||||
|
||||
[[bin]]
|
||||
name = "hmc"
|
||||
path = "src/main.rs"
|
|
@ -1,40 +0,0 @@
|
|||
use compiler::Compiler;
|
||||
use lower::{model::converts, Lower};
|
||||
use parser::{lex, parse, report};
|
||||
use vm::exec::Executor;
|
||||
|
||||
fn main() {
|
||||
let path = std::env::args().nth(1).expect("No file path provided");
|
||||
let src = std::fs::read_to_string(path).expect("Failed to read file");
|
||||
|
||||
let (tokens, lex_errors) = lex(src.to_string());
|
||||
let parse_errors = if let Some(tokens) = tokens {
|
||||
let (ast, parse_errors) = parse(tokens, src.len());
|
||||
|
||||
if let Some(ast) = ast {
|
||||
let stripped = converts(ast);
|
||||
let mut lower = Lower::new();
|
||||
let lowered = lower.opt_stmts(stripped);
|
||||
let mut compiler = Compiler::new();
|
||||
let instrs = compiler.compile_program(lowered);
|
||||
// instrs.iter().for_each(|i| println!("{:?}", i));
|
||||
let mut executor = Executor::new(instrs);
|
||||
match executor.run() {
|
||||
Ok(_) => {}
|
||||
Err(e) => println!("Runtime error: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
parse_errors
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
if !lex_errors.is_empty() || !parse_errors.is_empty() {
|
||||
lex_errors
|
||||
.into_iter()
|
||||
.map(|e| e.map(|c| c.to_string()))
|
||||
.chain(parse_errors.into_iter().map(|e| e.map(|t| t.to_string())))
|
||||
.for_each(|e| report(e, &src));
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
fun succ x = do
|
||||
let one = 1
|
||||
|
||||
let y = x + one
|
||||
in y
|
||||
end
|
||||
|
||||
fun double x = x * 2
|
||||
|
||||
fun main = do
|
||||
|
||||
1
|
||||
|> \a -> succ(a)
|
||||
|> \b -> double(b)
|
||||
|> \c -> println(c)
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
fun factorial n =
|
||||
let helper = \n acc ->
|
||||
if n > 0
|
||||
then helper(n - 1, acc * n)
|
||||
else acc
|
||||
in
|
||||
helper(n, 1)
|
||||
|
||||
fun main = println(factorial(20))
|
|
@ -1,45 +0,0 @@
|
|||
// This is just proof of concept on what the language
|
||||
// might look like in the future
|
||||
|
||||
import http from "http"
|
||||
|
||||
// Define a custom type to represent a user
|
||||
type User =
|
||||
id: number,
|
||||
name: string,
|
||||
end
|
||||
|
||||
// Define a function to handle incoming HTTP requests
|
||||
fun handle_request
|
||||
req: http.IncomingMessage,
|
||||
res: http.ServerResponse,
|
||||
= do
|
||||
let user_id = req.url.split("/")[1]
|
||||
let name =
|
||||
match user_id
|
||||
| 12345 -> Some("John Smith")
|
||||
| 727 -> Some("Foo Bar")
|
||||
else None
|
||||
|
||||
match name
|
||||
| Some name -> do
|
||||
let user = User(user_id, name)
|
||||
res.statusCode = 200
|
||||
res.setHeader("Content-Type", "application/json")
|
||||
res.write(JSON.stringify(user))
|
||||
end
|
||||
| None -> do
|
||||
res.statusCode = 404
|
||||
res.write("User not found")
|
||||
end
|
||||
|
||||
res.end()
|
||||
end
|
||||
|
||||
fun main = do
|
||||
let
|
||||
port = 8080,
|
||||
server = http.createServer(handle_request),
|
||||
in
|
||||
server.listen(port, fun -> println("HTTP server listening on port 8080"))
|
||||
end
|
|
@ -1,5 +0,0 @@
|
|||
fun main = do
|
||||
1
|
||||
|> \x -> x + 1
|
||||
|> \x -> println(x)
|
||||
end
|
|
@ -1,7 +0,0 @@
|
|||
[package]
|
||||
name = "lower"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
parser = { path = "../parser" }
|
|
@ -1,79 +0,0 @@
|
|||
#![allow(clippy::new_without_default)]
|
||||
pub mod model;
|
||||
use crate::model::{BinaryOp, Expr, Stmt};
|
||||
|
||||
pub struct Lower {}
|
||||
|
||||
impl Lower {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn opt_stmts(&self, ss: Vec<Stmt>) -> Vec<Stmt> {
|
||||
ss.into_iter()
|
||||
.map(|s| self.opt_stmt(s.clone()).unwrap_or(s))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn opt_stmt(&self, s: Stmt) -> Option<Stmt> {
|
||||
match s {
|
||||
Stmt::Fun(name, args, body) => Some(Stmt::Fun(
|
||||
name,
|
||||
args,
|
||||
self.opt_expr(body.clone()).unwrap_or(body),
|
||||
)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn opt_exprs(&self, es: Vec<Expr>) -> Vec<Expr> {
|
||||
es.into_iter()
|
||||
.map(|e| self.opt_expr(e.clone()).unwrap_or(e))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn opt_expr(&self, e: Expr) -> Option<Expr> {
|
||||
match e {
|
||||
Expr::Binary(BinaryOp::Pipe, left, right) => Some(self.fold_pipe(*left, *right)),
|
||||
Expr::Lambda(args, body) => Some(Expr::Lambda(
|
||||
args,
|
||||
Box::new(self.opt_expr(*body.clone()).unwrap_or(*body)),
|
||||
)),
|
||||
Expr::Do(es) => Some(Expr::Do(self.opt_exprs(es))),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_pipe(&self, left: Expr, right: Expr) -> Expr {
|
||||
Expr::Call(
|
||||
Box::new(self.opt_expr(right.clone()).unwrap_or(right)),
|
||||
vec![self.opt_expr(left.clone()).unwrap_or(left)],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::model::convert_expr;
|
||||
use parser::{lex, parse_expr};
|
||||
|
||||
#[test]
|
||||
fn test_fold_pipe() {
|
||||
let s = "1 |> \\x -> x + 1";
|
||||
println!("{}", s);
|
||||
let (ts, es) = lex(s.to_owned());
|
||||
|
||||
assert!(es.is_empty());
|
||||
|
||||
let (ex, es) = parse_expr(ts.unwrap(), s.chars().count());
|
||||
|
||||
assert!(es.is_empty());
|
||||
|
||||
let ex = ex.unwrap();
|
||||
let ex = convert_expr(ex);
|
||||
let l = Lower::new();
|
||||
let ex = l.opt_expr(ex).unwrap();
|
||||
println!("{:?}", ex);
|
||||
}
|
||||
}
|
|
@ -1,129 +0,0 @@
|
|||
use crate::model;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Literal {
|
||||
Num(i64),
|
||||
Bool(bool),
|
||||
Str(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum UnaryOp {
|
||||
Neg,
|
||||
Not,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum BinaryOp {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Lt,
|
||||
Le,
|
||||
Gt,
|
||||
Ge,
|
||||
Eq,
|
||||
Ne,
|
||||
And,
|
||||
Or,
|
||||
Pipe,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Expr {
|
||||
Error,
|
||||
|
||||
Literal(Literal),
|
||||
Sym(String),
|
||||
Vec(Vec<Self>),
|
||||
Unary(UnaryOp, Box<Self>),
|
||||
Binary(BinaryOp, Box<Self>, Box<Self>),
|
||||
Lambda(Vec<String>, Box<Self>),
|
||||
Call(Box<Self>, Vec<Self>),
|
||||
Let(Vec<(String, Self)>, Option<Box<Self>>),
|
||||
If(Box<Self>, Box<Self>, Option<Box<Self>>),
|
||||
Do(Vec<Expr>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Stmt {
|
||||
Fun(String, Vec<String>, Expr),
|
||||
}
|
||||
|
||||
pub fn converts(s: Vec<(parser::Stmt, std::ops::Range<usize>)>) -> Vec<model::Stmt> {
|
||||
s.into_iter().map(|(s, _)| convert(s)).collect()
|
||||
}
|
||||
|
||||
pub fn convert(s: parser::Stmt) -> model::Stmt {
|
||||
match s {
|
||||
parser::Stmt::Fun(name, args, body) => model::Stmt::Fun(name, args, convert_expr(body)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_expr(e: (parser::Expr, std::ops::Range<usize>)) -> model::Expr {
|
||||
match e.0 {
|
||||
parser::Expr::Error => model::Expr::Error,
|
||||
|
||||
parser::Expr::Literal(l) => match l {
|
||||
parser::Literal::Num(n) => model::Expr::Literal(model::Literal::Num(n)),
|
||||
parser::Literal::Bool(b) => model::Expr::Literal(model::Literal::Bool(b)),
|
||||
parser::Literal::Str(s) => model::Expr::Literal(model::Literal::Str(s)),
|
||||
},
|
||||
parser::Expr::Sym(s) => model::Expr::Sym(s),
|
||||
parser::Expr::Vec(es) => model::Expr::Vec(es.into_iter().map(convert_expr).collect()),
|
||||
parser::Expr::Unary(op, e) => {
|
||||
model::Expr::Unary(convert_unary_op(op.0), Box::new(convert_expr(*e)))
|
||||
}
|
||||
parser::Expr::Binary(op, left, right) => model::Expr::Binary(
|
||||
convert_binary_op(op.0),
|
||||
Box::new(convert_expr(*left)),
|
||||
Box::new(convert_expr(*right)),
|
||||
),
|
||||
parser::Expr::Lambda(args, body) => {
|
||||
model::Expr::Lambda(args, Box::new(convert_expr(*body)))
|
||||
}
|
||||
parser::Expr::Call(f, args) => model::Expr::Call(
|
||||
Box::new(convert_expr(*f)),
|
||||
args.into_iter().map(convert_expr).collect(),
|
||||
),
|
||||
parser::Expr::Let(bindings, body) => model::Expr::Let(
|
||||
bindings
|
||||
.into_iter()
|
||||
.map(|(s, e)| (s, convert_expr(e)))
|
||||
.collect(),
|
||||
body.map(|e| Box::new(convert_expr(*e))),
|
||||
),
|
||||
parser::Expr::If(cond, then, else_) => model::Expr::If(
|
||||
Box::new(convert_expr(*cond)),
|
||||
Box::new(convert_expr(*then)),
|
||||
else_.map(|e| Box::new(convert_expr(*e))),
|
||||
),
|
||||
parser::Expr::Do(es) => model::Expr::Do(es.into_iter().map(convert_expr).collect()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_unary_op(op: parser::UnaryOp) -> model::UnaryOp {
|
||||
match op {
|
||||
parser::UnaryOp::Neg => model::UnaryOp::Neg,
|
||||
parser::UnaryOp::Not => model::UnaryOp::Not,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_binary_op(op: parser::BinaryOp) -> model::BinaryOp {
|
||||
match op {
|
||||
parser::BinaryOp::Add => model::BinaryOp::Add,
|
||||
parser::BinaryOp::Sub => model::BinaryOp::Sub,
|
||||
parser::BinaryOp::Mul => model::BinaryOp::Mul,
|
||||
parser::BinaryOp::Div => model::BinaryOp::Div,
|
||||
parser::BinaryOp::Lt => model::BinaryOp::Lt,
|
||||
parser::BinaryOp::Le => model::BinaryOp::Le,
|
||||
parser::BinaryOp::Gt => model::BinaryOp::Gt,
|
||||
parser::BinaryOp::Ge => model::BinaryOp::Ge,
|
||||
parser::BinaryOp::Eq => model::BinaryOp::Eq,
|
||||
parser::BinaryOp::Ne => model::BinaryOp::Ne,
|
||||
parser::BinaryOp::And => model::BinaryOp::And,
|
||||
parser::BinaryOp::Or => model::BinaryOp::Or,
|
||||
parser::BinaryOp::Pipe => model::BinaryOp::Pipe,
|
||||
}
|
||||
}
|
122
parser/Cargo.lock
generated
122
parser/Cargo.lock
generated
|
@ -1,122 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
|
||||
dependencies = [
|
||||
"const-random",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ariadne"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1cb2a2046bea8ce5e875551f5772024882de0b540c7f93dfc5d6cf1ca8b030c"
|
||||
dependencies = [
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chumsky"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d02796e4586c6c41aeb68eae9bfb4558a522c35f1430c14b40136c3706e09e4"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-random"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368a7a772ead6ce7e1de82bfb04c485f3db8ec744f72925af5735e29a22cc18e"
|
||||
dependencies = [
|
||||
"const-random-macro",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-random-macro"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"proc-macro-hack",
|
||||
"tiny-keccak",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.138"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
|
||||
dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||
|
||||
[[package]]
|
||||
name = "yuushi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ariadne",
|
||||
"chumsky",
|
||||
]
|
|
@ -1,8 +0,0 @@
|
|||
[package]
|
||||
name = "parser"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
chumsky = "0.8.0"
|
||||
ariadne = "0.1.5"
|
|
@ -1,606 +0,0 @@
|
|||
#![feature(trait_alias)]
|
||||
#![allow(clippy::type_complexity)]
|
||||
use ariadne::{Color, Fmt, Label, Report, ReportKind, Source};
|
||||
use chumsky::{error, prelude::*, Stream};
|
||||
|
||||
pub type Span = std::ops::Range<usize>;
|
||||
pub type Spanned<T> = (T, Span);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum Delimiter {
|
||||
Paren,
|
||||
Brack,
|
||||
Brace,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum Token {
|
||||
Num(i64),
|
||||
Bool(bool),
|
||||
Str(String),
|
||||
Sym(String),
|
||||
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Lt,
|
||||
Le,
|
||||
Gt,
|
||||
Ge,
|
||||
Eq,
|
||||
Ne,
|
||||
And,
|
||||
Or,
|
||||
Not,
|
||||
Pipe,
|
||||
Assign,
|
||||
Arrow,
|
||||
Backslash,
|
||||
Comma,
|
||||
Semi,
|
||||
Open(Delimiter),
|
||||
Close(Delimiter),
|
||||
|
||||
Fun,
|
||||
Let,
|
||||
In,
|
||||
If,
|
||||
Then,
|
||||
Else,
|
||||
Do,
|
||||
End,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Token {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Token::Num(n) => write!(f, "{}", n),
|
||||
Token::Bool(b) => write!(f, "{}", b),
|
||||
Token::Str(s) => write!(f, "{}", s),
|
||||
Token::Sym(s) => write!(f, "{}", s),
|
||||
|
||||
Token::Add => write!(f, "+"),
|
||||
Token::Sub => write!(f, "-"),
|
||||
Token::Mul => write!(f, "*"),
|
||||
Token::Div => write!(f, "/"),
|
||||
|
||||
Token::Lt => write!(f, "<"),
|
||||
Token::Le => write!(f, "<="),
|
||||
Token::Gt => write!(f, ">"),
|
||||
Token::Ge => write!(f, ">="),
|
||||
Token::Eq => write!(f, "=="),
|
||||
Token::Ne => write!(f, "!="),
|
||||
|
||||
Token::And => write!(f, "&&"),
|
||||
Token::Or => write!(f, "||"),
|
||||
Token::Not => write!(f, "!"),
|
||||
|
||||
Token::Pipe => write!(f, "|>"),
|
||||
|
||||
Token::Assign => write!(f, "="),
|
||||
Token::Arrow => write!(f, "->"),
|
||||
Token::Backslash => write!(f, "\\"),
|
||||
Token::Comma => write!(f, ","),
|
||||
Token::Semi => write!(f, ";"),
|
||||
|
||||
Token::Open(d) => write!(
|
||||
f,
|
||||
"{}",
|
||||
match d {
|
||||
Delimiter::Paren => "(",
|
||||
Delimiter::Brack => "[",
|
||||
Delimiter::Brace => "{",
|
||||
}
|
||||
),
|
||||
Token::Close(d) => write!(
|
||||
f,
|
||||
"{}",
|
||||
match d {
|
||||
Delimiter::Paren => ")",
|
||||
Delimiter::Brack => "]",
|
||||
Delimiter::Brace => "}",
|
||||
}
|
||||
),
|
||||
|
||||
Token::Fun => write!(f, "fun"),
|
||||
Token::Let => write!(f, "let"),
|
||||
Token::In => write!(f, "in"),
|
||||
Token::If => write!(f, "if"),
|
||||
Token::Then => write!(f, "then"),
|
||||
Token::Else => write!(f, "else"),
|
||||
Token::Do => write!(f, "do"),
|
||||
Token::End => write!(f, "end"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> {
|
||||
let int = text::int(10).map(|s: String| Token::Num(s.parse().unwrap()));
|
||||
|
||||
let string = just('"')
|
||||
.ignore_then(filter(|c| *c != '"').repeated())
|
||||
.then_ignore(just('"'))
|
||||
.collect::<String>()
|
||||
.map(Token::Str);
|
||||
|
||||
let symbol = choice((
|
||||
just("->").to(Token::Arrow),
|
||||
just('+').to(Token::Add),
|
||||
just('-').to(Token::Sub),
|
||||
just('*').to(Token::Mul),
|
||||
just('/').to(Token::Div),
|
||||
just("|>").to(Token::Pipe),
|
||||
just("<=").to(Token::Le),
|
||||
just('<').to(Token::Lt),
|
||||
just(">=").to(Token::Ge),
|
||||
just('>').to(Token::Gt),
|
||||
just("!=").to(Token::Ne),
|
||||
just("==").to(Token::Eq),
|
||||
just("&&").to(Token::And),
|
||||
just("||").to(Token::Or),
|
||||
just('!').to(Token::Not),
|
||||
just('=').to(Token::Assign),
|
||||
just('\\').to(Token::Backslash),
|
||||
just(',').to(Token::Comma),
|
||||
just(';').to(Token::Semi),
|
||||
));
|
||||
|
||||
let delim = choice((
|
||||
just('(').to(Token::Open(Delimiter::Paren)),
|
||||
just(')').to(Token::Close(Delimiter::Paren)),
|
||||
just('[').to(Token::Open(Delimiter::Brack)),
|
||||
just(']').to(Token::Close(Delimiter::Brack)),
|
||||
just('{').to(Token::Open(Delimiter::Brace)),
|
||||
just('}').to(Token::Close(Delimiter::Brace)),
|
||||
));
|
||||
|
||||
let keyword = text::ident().map(|s: String| match s.as_str() {
|
||||
"true" => Token::Bool(true),
|
||||
"false" => Token::Bool(false),
|
||||
|
||||
"fun" => Token::Fun,
|
||||
"let" => Token::Let,
|
||||
"in" => Token::In,
|
||||
"if" => Token::If,
|
||||
"then" => Token::Then,
|
||||
"else" => Token::Else,
|
||||
"do" => Token::Do,
|
||||
"end" => Token::End,
|
||||
_ => Token::Sym(s),
|
||||
});
|
||||
|
||||
let token = int
|
||||
.or(string)
|
||||
.or(symbol)
|
||||
.or(delim)
|
||||
.or(keyword)
|
||||
.map_with_span(move |token, span| (token, span))
|
||||
.padded()
|
||||
.recover_with(skip_then_retry_until([]));
|
||||
|
||||
let comments = just('/')
|
||||
.then_ignore(
|
||||
just('*')
|
||||
.ignore_then(take_until(just("*/")).ignored())
|
||||
.or(just('/').ignore_then(none_of('\n').ignored().repeated().ignored())),
|
||||
)
|
||||
.padded()
|
||||
.ignored()
|
||||
.repeated();
|
||||
|
||||
token
|
||||
.padded_by(comments)
|
||||
.repeated()
|
||||
.padded()
|
||||
.then_ignore(end())
|
||||
}
|
||||
|
||||
pub fn lex(src: String) -> (Option<Vec<(Token, Span)>>, Vec<Simple<char>>) {
|
||||
let (tokens, lex_error) = lexer().parse_recovery(src.as_str());
|
||||
(tokens, lex_error)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Literal {
|
||||
Num(i64),
|
||||
Bool(bool),
|
||||
Str(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum UnaryOp {
|
||||
Neg,
|
||||
Not,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum BinaryOp {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Lt,
|
||||
Le,
|
||||
Gt,
|
||||
Ge,
|
||||
Eq,
|
||||
Ne,
|
||||
And,
|
||||
Or,
|
||||
Pipe,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Expr {
|
||||
Error,
|
||||
|
||||
Literal(Literal),
|
||||
Sym(String),
|
||||
Vec(Vec<Spanned<Self>>),
|
||||
Unary(Spanned<UnaryOp>, Box<Spanned<Self>>),
|
||||
Binary(Spanned<BinaryOp>, Box<Spanned<Self>>, Box<Spanned<Self>>),
|
||||
Lambda(Vec<String>, Box<Spanned<Self>>),
|
||||
Call(Box<Spanned<Self>>, Vec<Spanned<Self>>),
|
||||
Let(Vec<(String, Spanned<Self>)>, Option<Box<Spanned<Self>>>),
|
||||
If(
|
||||
Box<Spanned<Self>>,
|
||||
Box<Spanned<Self>>,
|
||||
Option<Box<Spanned<Self>>>,
|
||||
),
|
||||
Do(Vec<Spanned<Expr>>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Stmt {
|
||||
Fun(String, Vec<String>, Spanned<Expr>),
|
||||
}
|
||||
|
||||
pub trait P<T> = chumsky::Parser<Token, T, Error = Simple<Token>> + Clone;
|
||||
|
||||
pub fn literal_parser() -> impl P<Literal> {
|
||||
filter_map(|span, token| match token {
|
||||
Token::Num(i) => Ok(Literal::Num(i)),
|
||||
Token::Bool(b) => Ok(Literal::Bool(b)),
|
||||
Token::Str(s) => Ok(Literal::Str(s)),
|
||||
_ => Err(Simple::expected_input_found(span, Vec::new(), Some(token))),
|
||||
})
|
||||
.labelled("literal")
|
||||
}
|
||||
|
||||
pub fn symbol_parser() -> impl P<String> {
|
||||
filter_map(|span, token| match token {
|
||||
Token::Sym(s) => Ok(s),
|
||||
_ => Err(Simple::expected_input_found(span, Vec::new(), Some(token))),
|
||||
})
|
||||
.labelled("symbol")
|
||||
}
|
||||
|
||||
pub fn nested_parser<'a, T: 'a>(
|
||||
parser: impl P<T> + 'a,
|
||||
delim: Delimiter,
|
||||
f: impl Fn(Span) -> T + Clone + 'a,
|
||||
) -> impl P<T> + 'a {
|
||||
parser
|
||||
.delimited_by(just(Token::Open(delim)), just(Token::Close(delim)))
|
||||
.recover_with(nested_delimiters(
|
||||
Token::Open(delim),
|
||||
Token::Close(delim),
|
||||
[
|
||||
(
|
||||
Token::Open(Delimiter::Paren),
|
||||
Token::Close(Delimiter::Paren),
|
||||
),
|
||||
(
|
||||
Token::Open(Delimiter::Brack),
|
||||
Token::Close(Delimiter::Brack),
|
||||
),
|
||||
(
|
||||
Token::Open(Delimiter::Brace),
|
||||
Token::Close(Delimiter::Brace),
|
||||
),
|
||||
],
|
||||
f,
|
||||
))
|
||||
.boxed()
|
||||
}
|
||||
|
||||
pub fn expr_parser() -> impl P<Spanned<Expr>> {
|
||||
recursive(|expr| {
|
||||
let lit = literal_parser().map(Expr::Literal);
|
||||
let ident = symbol_parser().map(Expr::Sym);
|
||||
|
||||
let vec = nested_parser(
|
||||
expr.clone()
|
||||
.separated_by(just(Token::Comma))
|
||||
.allow_trailing()
|
||||
.map(Some),
|
||||
Delimiter::Brack,
|
||||
|_| None,
|
||||
)
|
||||
.map(|elems| match elems {
|
||||
Some(elems) => Expr::Vec(elems),
|
||||
None => Expr::Vec(Vec::new()),
|
||||
})
|
||||
.labelled("vector");
|
||||
|
||||
let paren_expr = just(Token::Open(Delimiter::Paren))
|
||||
.ignore_then(expr.clone())
|
||||
.then_ignore(just(Token::Close(Delimiter::Paren)))
|
||||
.map(|e| e.0)
|
||||
.labelled("parenthesized expression");
|
||||
|
||||
let lam = just(Token::Backslash)
|
||||
.ignore_then(symbol_parser().repeated())
|
||||
.then_ignore(just(Token::Arrow))
|
||||
.then(expr.clone())
|
||||
.map(|(args, body)| Expr::Lambda(args, Box::new(body)))
|
||||
.labelled("lambda");
|
||||
|
||||
let let_binds = symbol_parser()
|
||||
.then_ignore(just(Token::Assign))
|
||||
.then(expr.clone())
|
||||
.map(|(sym, expr)| (sym, expr))
|
||||
.separated_by(just(Token::Comma))
|
||||
.allow_trailing()
|
||||
.labelled("let bindings");
|
||||
|
||||
let let_in = just(Token::Let)
|
||||
.ignore_then(let_binds.clone())
|
||||
.then_ignore(just(Token::In))
|
||||
.then(expr.clone())
|
||||
.map(|(binds, body)| Expr::Let(binds, Some(Box::new(body))))
|
||||
.boxed()
|
||||
.labelled("let..in");
|
||||
|
||||
let let_def = just(Token::Let)
|
||||
.ignore_then(let_binds)
|
||||
.map(|binds| Expr::Let(binds, None))
|
||||
.labelled("let");
|
||||
|
||||
let if_ = just(Token::If)
|
||||
.ignore_then(expr.clone())
|
||||
.then_ignore(just(Token::Then))
|
||||
.then(expr.clone())
|
||||
.then(just(Token::Else).ignore_then(expr.clone()).or_not())
|
||||
.map(|((cond, then), else_)| {
|
||||
Expr::If(Box::new(cond), Box::new(then), else_.map(Box::new))
|
||||
});
|
||||
|
||||
let block = just(Token::Do)
|
||||
.ignore_then(expr.clone().repeated())
|
||||
.then_ignore(just(Token::End))
|
||||
.map(Expr::Do)
|
||||
.labelled("do block");
|
||||
|
||||
let atom = lit
|
||||
.or(ident)
|
||||
.or(vec)
|
||||
.or(paren_expr)
|
||||
.or(lam)
|
||||
.or(let_in)
|
||||
.or(let_def)
|
||||
.or(if_)
|
||||
.or(block)
|
||||
.map_with_span(|e, s| (e, s))
|
||||
.boxed()
|
||||
.labelled("atom");
|
||||
|
||||
let call = atom
|
||||
.then(
|
||||
nested_parser(
|
||||
expr.clone()
|
||||
.separated_by(just(Token::Comma))
|
||||
.allow_trailing()
|
||||
.map(Some),
|
||||
Delimiter::Paren,
|
||||
|_| None,
|
||||
)
|
||||
.or_not(),
|
||||
)
|
||||
.map_with_span(|(f, args), s| match args {
|
||||
Some(Some(args)) => (Expr::Call(Box::new(f), args), s),
|
||||
Some(None) => (Expr::Error, s),
|
||||
None => f,
|
||||
});
|
||||
|
||||
let unary = choice((
|
||||
just(Token::Sub).to(UnaryOp::Neg),
|
||||
just(Token::Not).to(UnaryOp::Not),
|
||||
))
|
||||
.map_with_span(|op, s| (op, s))
|
||||
.repeated()
|
||||
.then(call)
|
||||
.foldr(|op, expr| {
|
||||
let s = op.1.start()..expr.1.end();
|
||||
(Expr::Unary(op, Box::new(expr)), s)
|
||||
})
|
||||
.boxed();
|
||||
|
||||
let product = unary
|
||||
.clone()
|
||||
.then(
|
||||
choice((
|
||||
just(Token::Mul).to(BinaryOp::Mul),
|
||||
just(Token::Div).to(BinaryOp::Div),
|
||||
))
|
||||
.map_with_span(|op, s| (op, s))
|
||||
.then(unary)
|
||||
.repeated(),
|
||||
)
|
||||
.foldl(|lhs, (op, rhs)| {
|
||||
let s = lhs.1.start()..rhs.1.end();
|
||||
(Expr::Binary(op, Box::new(lhs), Box::new(rhs)), s)
|
||||
})
|
||||
.boxed();
|
||||
|
||||
let sum = product
|
||||
.clone()
|
||||
.then(
|
||||
choice((
|
||||
just(Token::Add).to(BinaryOp::Add),
|
||||
just(Token::Sub).to(BinaryOp::Sub),
|
||||
))
|
||||
.map_with_span(|op, s| (op, s))
|
||||
.then(product)
|
||||
.repeated(),
|
||||
)
|
||||
.foldl(|lhs, (op, rhs)| {
|
||||
let s = lhs.1.start()..rhs.1.end();
|
||||
(Expr::Binary(op, Box::new(lhs), Box::new(rhs)), s)
|
||||
})
|
||||
.boxed();
|
||||
|
||||
let comparison = sum
|
||||
.clone()
|
||||
.then(
|
||||
choice((
|
||||
just(Token::Eq).to(BinaryOp::Eq),
|
||||
just(Token::Ne).to(BinaryOp::Ne),
|
||||
just(Token::Lt).to(BinaryOp::Lt),
|
||||
just(Token::Le).to(BinaryOp::Le),
|
||||
just(Token::Gt).to(BinaryOp::Gt),
|
||||
just(Token::Ge).to(BinaryOp::Ge),
|
||||
))
|
||||
.map_with_span(|op, s| (op, s))
|
||||
.then(sum)
|
||||
.repeated(),
|
||||
)
|
||||
.foldl(|lhs, (op, rhs)| {
|
||||
let s = lhs.1.start()..rhs.1.end();
|
||||
(Expr::Binary(op, Box::new(lhs), Box::new(rhs)), s)
|
||||
})
|
||||
.boxed();
|
||||
|
||||
let logical = comparison
|
||||
.clone()
|
||||
.then(
|
||||
choice((
|
||||
just(Token::And).to(BinaryOp::And),
|
||||
just(Token::Or).to(BinaryOp::Or),
|
||||
))
|
||||
.map_with_span(|op, s| (op, s))
|
||||
.then(comparison)
|
||||
.repeated(),
|
||||
)
|
||||
.foldl(|lhs, (op, rhs)| {
|
||||
let s = lhs.1.start()..rhs.1.end();
|
||||
(Expr::Binary(op, Box::new(lhs), Box::new(rhs)), s)
|
||||
})
|
||||
.boxed();
|
||||
|
||||
logical
|
||||
.clone()
|
||||
.then(
|
||||
just(Token::Pipe)
|
||||
.to(BinaryOp::Pipe)
|
||||
.map_with_span(|op, s| (op, s))
|
||||
.then(logical)
|
||||
.repeated(),
|
||||
)
|
||||
.foldl(|lhs, (op, rhs)| {
|
||||
let s = lhs.1.start()..rhs.1.end();
|
||||
(Expr::Binary(op, Box::new(lhs), Box::new(rhs)), s)
|
||||
})
|
||||
.boxed()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn stmt_parser() -> impl P<Spanned<Stmt>> {
|
||||
let fun = just(Token::Fun)
|
||||
.ignore_then(symbol_parser())
|
||||
.then(symbol_parser().repeated())
|
||||
.then_ignore(just(Token::Assign))
|
||||
.then(expr_parser())
|
||||
.map(|((name, args), body)| Stmt::Fun(name, args, body));
|
||||
|
||||
fun.map_with_span(|e, s| (e, s))
|
||||
}
|
||||
|
||||
pub fn stmts_parser() -> impl P<Vec<Spanned<Stmt>>> {
|
||||
stmt_parser().repeated()
|
||||
}
|
||||
|
||||
pub fn parse(
|
||||
tokens: Vec<Spanned<Token>>,
|
||||
len: usize,
|
||||
) -> (Option<Vec<Spanned<Stmt>>>, Vec<Simple<Token>>) {
|
||||
let (ast, parse_error) = stmts_parser()
|
||||
.then_ignore(end())
|
||||
.parse_recovery(Stream::from_iter(len..len + 1, tokens.into_iter()));
|
||||
|
||||
(ast, parse_error)
|
||||
}
|
||||
|
||||
pub fn parse_expr(
|
||||
tokens: Vec<Spanned<Token>>,
|
||||
len: usize,
|
||||
) -> (Option<Spanned<Expr>>, Vec<Simple<Token>>) {
|
||||
let (ast, parse_error) = expr_parser()
|
||||
.then_ignore(end())
|
||||
.parse_recovery(Stream::from_iter(len..len + 1, tokens.into_iter()));
|
||||
|
||||
(ast, parse_error)
|
||||
}
|
||||
|
||||
pub fn report(e: Simple<String>, src: &str) {
|
||||
let report = Report::build(ReportKind::Error, (), e.span().start());
|
||||
|
||||
let report = match e.reason() {
|
||||
error::SimpleReason::Unclosed { span, delimiter } => report
|
||||
.with_message("Unclosed delimiter")
|
||||
.with_label(
|
||||
Label::new(span.clone())
|
||||
.with_message(format!("Unclosed {}", delimiter.fg(Color::Yellow)))
|
||||
.with_color(Color::Yellow),
|
||||
)
|
||||
.with_label(
|
||||
Label::new(e.span())
|
||||
.with_message(format!(
|
||||
"Delimiter must be closed before {}",
|
||||
e.found()
|
||||
.unwrap_or(&"end of file".to_string())
|
||||
.fg(Color::Red)
|
||||
))
|
||||
.with_color(Color::Red),
|
||||
),
|
||||
|
||||
error::SimpleReason::Unexpected => report
|
||||
.with_message(format!(
|
||||
"Unexpected {}, expected {}",
|
||||
if e.found().is_some() {
|
||||
"token in input"
|
||||
} else {
|
||||
"end of input"
|
||||
},
|
||||
if e.expected().len() == 0 {
|
||||
"something else".to_string()
|
||||
} else {
|
||||
e.expected()
|
||||
.map(|expected| match expected {
|
||||
Some(expected) => expected.to_string(),
|
||||
None => "end of input".to_string(),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
}
|
||||
))
|
||||
.with_label(
|
||||
Label::new(e.span())
|
||||
.with_message(format!(
|
||||
"Unexpected token {}",
|
||||
e.found()
|
||||
.unwrap_or(&"end of file".to_string())
|
||||
.fg(Color::Red)
|
||||
))
|
||||
.with_color(Color::Red),
|
||||
),
|
||||
chumsky::error::SimpleReason::Custom(msg) => report.with_message(msg).with_label(
|
||||
Label::new(e.span())
|
||||
.with_message(format!("{}", msg.fg(Color::Red)))
|
||||
.with_color(Color::Red),
|
||||
),
|
||||
};
|
||||
|
||||
report.finish().eprint(Source::from(&src)).unwrap();
|
||||
}
|
2
rust-toolchain.toml
Normal file
2
rust-toolchain.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[toolchain]
|
||||
channel = "nightly"
|
|
@ -1 +0,0 @@
|
|||
version = "Two"
|
55
src/main.rs
Normal file
55
src/main.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
#![feature(trait_alias)]
|
||||
pub mod parse;
|
||||
pub mod trans;
|
||||
|
||||
use parse::parse::lex;
|
||||
|
||||
fn main() {
|
||||
let input = r#"
|
||||
println((\x: int -> x + 1)(1));
|
||||
"#;
|
||||
|
||||
let tokens = lex(input.to_owned());
|
||||
println!("{:?}", tokens);
|
||||
|
||||
// use parse::past::*;
|
||||
// use trans::ty::Type;
|
||||
// use trans::low::*;
|
||||
|
||||
// let exprs = vec![
|
||||
// PExpr::Call(Box::new(PExpr::Sym("println".to_string())), vec![
|
||||
// PExpr::Str("Hello, world!".to_string()),
|
||||
// ]),
|
||||
// PExpr::Let {
|
||||
// vars: vec![
|
||||
// ("x".to_string(), Type::Num, PExpr::Num(1)),
|
||||
// ],
|
||||
// body: Box::new(PExpr::Sym("x".to_string())),
|
||||
// },
|
||||
// PExpr::Let {
|
||||
// vars: vec![
|
||||
// ("x".to_string(), Type::Num, PExpr::Num(34)),
|
||||
// ("y".to_string(), Type::Num, PExpr::Num(35)),
|
||||
// ],
|
||||
// body: Box::new(PExpr::BinaryOp(
|
||||
// PBinaryOp::Add,
|
||||
// Box::new(PExpr::Sym("x".to_string())),
|
||||
// Box::new(PExpr::Sym("y".to_string())),
|
||||
// )),
|
||||
// },
|
||||
// ];
|
||||
|
||||
// let nexprs = exprs.into_iter().map(translate_expr).collect::<Vec<_>>();
|
||||
|
||||
// for expr in &nexprs {
|
||||
// println!("{}", expr);
|
||||
// }
|
||||
|
||||
// println!("──────────────────────────────────────────────────");
|
||||
|
||||
// let jsexprs = nexprs.into_iter().map(translate_js).collect::<Vec<_>>();
|
||||
|
||||
// for expr in &jsexprs {
|
||||
// println!("{}", expr);
|
||||
// }
|
||||
}
|
2
src/parse/mod.rs
Normal file
2
src/parse/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod parse;
|
||||
pub mod past;
|
204
src/parse/parse.rs
Normal file
204
src/parse/parse.rs
Normal file
|
@ -0,0 +1,204 @@
|
|||
#![allow(clippy::type_complexity)]
|
||||
use chumsky::{error, prelude::*, Stream};
|
||||
use std::fmt::{Display, Formatter, Result as FmtResult};
|
||||
use super::past::*;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum Delim { Paren, Brack, Brace }
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum Token {
|
||||
Num(i64), Str(String), Bool(bool), Sym(String),
|
||||
|
||||
Add, Sub, Mul, Div, Mod,
|
||||
Eq, Neq, Lt, Gt, Lte, Gte,
|
||||
And, Or, Not,
|
||||
|
||||
Assign, Comma, Colon, Semicolon,
|
||||
Open(Delim), Close(Delim),
|
||||
Lambda, Arrow,
|
||||
|
||||
Let, Func,
|
||||
}
|
||||
|
||||
impl Display for Token {
|
||||
fn fmt(&self, f: &mut Formatter) -> FmtResult {
|
||||
match self {
|
||||
Token::Num(n) => write!(f, "{}", n),
|
||||
Token::Str(s) => write!(f, "\"{}\"", s),
|
||||
Token::Bool(b) => write!(f, "{}", b),
|
||||
Token::Sym(s) => write!(f, "{}", s),
|
||||
|
||||
Token::Add => write!(f, "+"),
|
||||
Token::Sub => write!(f, "-"),
|
||||
Token::Mul => write!(f, "*"),
|
||||
Token::Div => write!(f, "/"),
|
||||
Token::Mod => write!(f, "%"),
|
||||
Token::Eq => write!(f, "=="),
|
||||
Token::Neq => write!(f, "!="),
|
||||
Token::Lt => write!(f, "<"),
|
||||
Token::Gt => write!(f, ">"),
|
||||
Token::Lte => write!(f, "<="),
|
||||
Token::Gte => write!(f, ">="),
|
||||
Token::And => write!(f, "&&"),
|
||||
Token::Or => write!(f, "||"),
|
||||
Token::Not => write!(f, "!"),
|
||||
|
||||
Token::Assign => write!(f, "="),
|
||||
Token::Comma => write!(f, ","),
|
||||
Token::Colon => write!(f, ":"),
|
||||
Token::Semicolon => write!(f, ";"),
|
||||
Token::Open(d) => write!(f, "{}", match d {
|
||||
Delim::Paren => "(",
|
||||
Delim::Brack => "[",
|
||||
Delim::Brace => "{",
|
||||
}),
|
||||
Token::Close(d) => write!(f, "{}", match d {
|
||||
Delim::Paren => ")",
|
||||
Delim::Brack => "]",
|
||||
Delim::Brace => "}",
|
||||
}),
|
||||
Token::Lambda => write!(f, "\\"),
|
||||
Token::Arrow => write!(f, "->"),
|
||||
|
||||
Token::Let => write!(f, "let"),
|
||||
Token::Func => write!(f, "func"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Span = std::ops::Range<usize>;
|
||||
pub type Spanned<T> = (T, Span);
|
||||
|
||||
pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> {
|
||||
let num = text::int(10)
|
||||
.map(|s: String| Token::Num(s.parse().unwrap()));
|
||||
|
||||
let string = just('"')
|
||||
.ignore_then(filter(|c| *c != '"').repeated())
|
||||
.then_ignore(just('"'))
|
||||
.collect::<String>()
|
||||
.map(Token::Str);
|
||||
|
||||
let symbol = choice((
|
||||
just("->").to(Token::Arrow),
|
||||
|
||||
just('+').to(Token::Add),
|
||||
just('-').to(Token::Sub),
|
||||
just('*').to(Token::Mul),
|
||||
just('/').to(Token::Div),
|
||||
just('%').to(Token::Mod),
|
||||
just("==").to(Token::Eq),
|
||||
just("!=").to(Token::Neq),
|
||||
just("<=").to(Token::Lte),
|
||||
just(">=").to(Token::Gte),
|
||||
just('<').to(Token::Lt),
|
||||
just('>').to(Token::Gt),
|
||||
just("&&").to(Token::And),
|
||||
just("||").to(Token::Or),
|
||||
just('!').to(Token::Not),
|
||||
|
||||
just('=').to(Token::Assign),
|
||||
just(',').to(Token::Comma),
|
||||
just(':').to(Token::Colon),
|
||||
just(';').to(Token::Semicolon),
|
||||
just('\\').to(Token::Lambda),
|
||||
));
|
||||
|
||||
let delim = choice((
|
||||
just('(').to(Token::Open(Delim::Paren)),
|
||||
just(')').to(Token::Close(Delim::Paren)),
|
||||
just('[').to(Token::Open(Delim::Brack)),
|
||||
just(']').to(Token::Close(Delim::Brack)),
|
||||
just('{').to(Token::Open(Delim::Brace)),
|
||||
just('}').to(Token::Close(Delim::Brace)),
|
||||
));
|
||||
|
||||
let kw = text::ident()
|
||||
.map(|s: String| match s.as_str() {
|
||||
"true" => Token::Bool(true),
|
||||
"false" => Token::Bool(false),
|
||||
"let" => Token::Let,
|
||||
"func" => Token::Func,
|
||||
_ => Token::Sym(s),
|
||||
});
|
||||
|
||||
let token = num
|
||||
.or(string)
|
||||
.or(symbol)
|
||||
.or(delim)
|
||||
.or(kw)
|
||||
.map_with_span(move |token, span| (token, span))
|
||||
.padded()
|
||||
.recover_with(skip_then_retry_until([]));
|
||||
|
||||
let comments = just('/')
|
||||
.then_ignore(
|
||||
just('*')
|
||||
.ignore_then(take_until(just("*/")).ignored())
|
||||
.or(just('/').ignore_then(none_of('\n').ignored().repeated().ignored())),
|
||||
)
|
||||
.padded()
|
||||
.ignored()
|
||||
.repeated();
|
||||
|
||||
token
|
||||
.padded_by(comments)
|
||||
.repeated()
|
||||
.padded()
|
||||
.then_ignore(end())
|
||||
}
|
||||
|
||||
pub fn lex(src: String) -> (Option<Vec<(Token, Span)>>, Vec<Simple<char>>) {
|
||||
let (tokens, lex_error) = lexer().parse_recovery(src.as_str());
|
||||
(tokens, lex_error)
|
||||
}
|
||||
|
||||
pub trait P<T> = chumsky::Parser<Token, T, Error = Simple<Token>> + Clone;
|
||||
|
||||
pub fn literal_parser() -> impl P<PLiteral> {
|
||||
filter_map(|span, token| match token {
|
||||
Token::Num(i) => Ok(PLiteral::Num(i)),
|
||||
Token::Bool(b) => Ok(PLiteral::Bool(b)),
|
||||
Token::Str(s) => Ok(PLiteral::Str(s)),
|
||||
_ => Err(Simple::expected_input_found(span, Vec::new(), Some(token))),
|
||||
})
|
||||
.labelled("literal")
|
||||
}
|
||||
|
||||
pub fn symbol_parser() -> impl P<String> {
|
||||
filter_map(|span, token| match token {
|
||||
Token::Sym(s) => Ok(s),
|
||||
_ => Err(Simple::expected_input_found(span, Vec::new(), Some(token))),
|
||||
})
|
||||
.labelled("symbol")
|
||||
}
|
||||
|
||||
pub fn nested_parser<'a, T: 'a>(
|
||||
parser: impl P<T> + 'a,
|
||||
delim: Delim,
|
||||
f: impl Fn(Span) -> T + Clone + 'a,
|
||||
) -> impl P<T> + 'a {
|
||||
parser
|
||||
.delimited_by(just(Token::Open(delim)), just(Token::Close(delim)))
|
||||
.recover_with(nested_delimiters(
|
||||
Token::Open(delim),
|
||||
Token::Close(delim),
|
||||
[
|
||||
(
|
||||
Token::Open(Delim::Paren),
|
||||
Token::Close(Delim::Paren),
|
||||
),
|
||||
(
|
||||
Token::Open(Delim::Brack),
|
||||
Token::Close(Delim::Brack),
|
||||
),
|
||||
(
|
||||
Token::Open(Delim::Brace),
|
||||
Token::Close(Delim::Brace),
|
||||
),
|
||||
],
|
||||
f,
|
||||
))
|
||||
.boxed()
|
||||
}
|
40
src/parse/past.rs
Normal file
40
src/parse/past.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
use std::fmt::{Display, Formatter, Result as FmtResult};
|
||||
use crate::trans::ty::*;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum PUnaryOp {
|
||||
Neg,
|
||||
Not,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum PBinaryOp {
|
||||
Add, Sub, Mul, Div, Mod,
|
||||
Eq, Neq, Lt, Gt, Lte, Gte,
|
||||
And, Or,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum PLiteral { Num(i64), Str(String), Bool(bool) }
|
||||
|
||||
/// Enum to represent a parsed expression
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum PExpr {
|
||||
Lit(PLiteral),
|
||||
Sym(String),
|
||||
|
||||
Vec(Vec<Self>),
|
||||
|
||||
UnaryOp(PUnaryOp, Box<Self>),
|
||||
BinaryOp(PBinaryOp, Box<Self>, Box<Self>),
|
||||
|
||||
Call(Box<Self>, Vec<Self>),
|
||||
Lambda {
|
||||
args: Vec<(String, Type)>,
|
||||
body: Box<Self>,
|
||||
},
|
||||
Let {
|
||||
vars: Vec<(String, Type, Self)>,
|
||||
body: Box<Self>,
|
||||
}
|
||||
}
|
67
src/trans/ast.rs
Normal file
67
src/trans/ast.rs
Normal file
|
@ -0,0 +1,67 @@
|
|||
use std::fmt::{Display, Formatter, Result as FmtResult};
|
||||
use super::ty::Type;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum UnaryOp {
|
||||
Neg,
|
||||
Not,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum BinaryOp {
|
||||
Add, Sub, Mul, Div, Mod,
|
||||
Eq, Neq, Lt, Gt, Lte, Gte,
|
||||
And, Or,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Literal {
|
||||
Num(i64), Str(String), Bool(bool),
|
||||
}
|
||||
|
||||
/// Enum to represent internal expression
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Expr {
|
||||
Lit(Literal),
|
||||
Sym(String),
|
||||
|
||||
UnaryOp(UnaryOp, Box<Self>),
|
||||
BinaryOp(BinaryOp, Box<Self>, Box<Self>),
|
||||
|
||||
Call(Box<Self>, Vec<Self>),
|
||||
Lambda {
|
||||
args: Vec<(String, Type)>,
|
||||
body: Box<Self>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Display for Expr {
|
||||
fn fmt(&self, f: &mut Formatter) -> FmtResult {
|
||||
match self {
|
||||
Expr::Lit(l) => match l {
|
||||
Literal::Num(n) => write!(f, "{}", n),
|
||||
Literal::Str(s) => write!(f, "\"{}\"", s),
|
||||
Literal::Bool(b) => write!(f, "{}", b),
|
||||
},
|
||||
Expr::Sym(s) => write!(f, "{}", s),
|
||||
|
||||
Expr::UnaryOp(op, e) => write!(f, "({:?} {})", op, e),
|
||||
Expr::BinaryOp(op, e1, e2) => write!(f, "({:?} {} {})", op, e1, e2),
|
||||
|
||||
Expr::Call(c, args) => {
|
||||
write!(f, "({}", c)?;
|
||||
for arg in args {
|
||||
write!(f, " {}", arg)?;
|
||||
}
|
||||
write!(f, ")")
|
||||
},
|
||||
Expr::Lambda { args, body } => {
|
||||
write!(f, "(lambda ")?;
|
||||
for (name, ty) in args {
|
||||
write!(f, "[{} {}]", name, ty)?;
|
||||
}
|
||||
write!(f, " {})", body)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
72
src/trans/js.rs
Normal file
72
src/trans/js.rs
Normal file
|
@ -0,0 +1,72 @@
|
|||
use std::fmt::{Display, Formatter, Result as FmtResult};
|
||||
use super::ty::Type;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum JSLiteral { Num(i64), Str(String), Bool(bool) }
|
||||
|
||||
/// Enum to represent javascript expression
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum JSExpr {
|
||||
Lit(JSLiteral),
|
||||
Sym(String),
|
||||
|
||||
Op(&'static str, Box<Self>, Option<Box<Self>>),
|
||||
|
||||
Call(Box<Self>, Vec<Self>),
|
||||
Method(Box<Self>, String, Vec<Self>),
|
||||
Lambda {
|
||||
args: Vec<(String, Type)>,
|
||||
body: Box<Self>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Display for JSExpr {
|
||||
fn fmt(&self, f: &mut Formatter) -> FmtResult {
|
||||
match self {
|
||||
JSExpr::Lit(l) => match l {
|
||||
JSLiteral::Num(n) => write!(f, "{}", n),
|
||||
JSLiteral::Str(s) => write!(f, "'{}'", s),
|
||||
JSLiteral::Bool(b) => write!(f, "{}", b),
|
||||
},
|
||||
JSExpr::Sym(s) => write!(f, "{}", s),
|
||||
|
||||
JSExpr::Op(op, lhs, rhs) => {
|
||||
match rhs {
|
||||
Some(rhs) => write!(f, "({} {} {})", lhs, op, rhs),
|
||||
None => write!(f, "({} {})", op, lhs),
|
||||
}
|
||||
}
|
||||
|
||||
JSExpr::Call(c, args) => {
|
||||
write!(f, "{}(", c)?;
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", arg)?;
|
||||
}
|
||||
write!(f, ")")
|
||||
},
|
||||
JSExpr::Method(c, m, args) => {
|
||||
write!(f, "{}.{}(", c, m)?;
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", arg)?;
|
||||
}
|
||||
write!(f, ")")
|
||||
},
|
||||
JSExpr::Lambda { args, body } => {
|
||||
write!(f, "((")?;
|
||||
for (i, (name, _ty)) in args.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", name)?;
|
||||
}
|
||||
write!(f, ") => {})", body)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
129
src/trans/low.rs
Normal file
129
src/trans/low.rs
Normal file
|
@ -0,0 +1,129 @@
|
|||
use crate::parse::past::{PExpr, PLiteral, PBinaryOp, PUnaryOp};
|
||||
use super::{
|
||||
ast::{Expr, Literal, BinaryOp, UnaryOp},
|
||||
js::{JSExpr, JSLiteral},
|
||||
};
|
||||
|
||||
pub fn translate_expr(expr: PExpr) -> Expr {
|
||||
match expr {
|
||||
PExpr::Lit(l) => Expr::Lit(match l {
|
||||
PLiteral::Num(n) => Literal::Num(n),
|
||||
PLiteral::Str(s) => Literal::Str(s),
|
||||
PLiteral::Bool(b) => Literal::Bool(b),
|
||||
}),
|
||||
PExpr::Sym(s) => Expr::Sym(s),
|
||||
|
||||
PExpr::UnaryOp(op, e) => Expr::UnaryOp(match op {
|
||||
PUnaryOp::Neg => UnaryOp::Neg,
|
||||
PUnaryOp::Not => UnaryOp::Not,
|
||||
}, Box::new(translate_expr(*e))),
|
||||
PExpr::BinaryOp(op, e1, e2) => Expr::BinaryOp(
|
||||
match op {
|
||||
PBinaryOp::Add => BinaryOp::Add,
|
||||
PBinaryOp::Sub => BinaryOp::Sub,
|
||||
PBinaryOp::Mul => BinaryOp::Mul,
|
||||
PBinaryOp::Div => BinaryOp::Div,
|
||||
PBinaryOp::Mod => BinaryOp::Mod,
|
||||
|
||||
PBinaryOp::Eq => BinaryOp::Eq,
|
||||
PBinaryOp::Neq => BinaryOp::Neq,
|
||||
|
||||
PBinaryOp::Lt => BinaryOp::Lt,
|
||||
PBinaryOp::Gt => BinaryOp::Gt,
|
||||
PBinaryOp::Lte => BinaryOp::Lte,
|
||||
PBinaryOp::Gte => BinaryOp::Gte,
|
||||
|
||||
PBinaryOp::And => BinaryOp::And,
|
||||
PBinaryOp::Or => BinaryOp::Or,
|
||||
},
|
||||
Box::new(translate_expr(*e1)),
|
||||
Box::new(translate_expr(*e2)),
|
||||
),
|
||||
|
||||
PExpr::Call(f, args) => Expr::Call(
|
||||
Box::new(translate_expr(*f)),
|
||||
args.into_iter().map(translate_expr).collect(),
|
||||
),
|
||||
PExpr::Lambda { args, body } => Expr::Lambda {
|
||||
args,
|
||||
body: Box::new(translate_expr(*body)),
|
||||
},
|
||||
PExpr::Let { vars, body } => {
|
||||
let mut expr = *body; // The expression we're building up
|
||||
for (name, ty, val) in vars.into_iter().rev() { // Reverse so we can build up the lambda
|
||||
// e.g.: let x : t = e1 in e2 end => (lambda (x : t) = e2)(e1)
|
||||
|
||||
// Build up the lambda
|
||||
expr = PExpr::Lambda {
|
||||
args: vec![(name, ty)],
|
||||
body: Box::new(expr),
|
||||
};
|
||||
// Call the lambda with the value
|
||||
expr = PExpr::Call(Box::new(expr), vec![val]);
|
||||
}
|
||||
|
||||
translate_expr(expr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn translate_js(expr: Expr) -> JSExpr {
|
||||
match expr {
|
||||
Expr::Lit(l) => match l {
|
||||
Literal::Num(n) => JSExpr::Lit(JSLiteral::Num(n)),
|
||||
Literal::Str(s) => JSExpr::Lit(JSLiteral::Str(s)),
|
||||
Literal::Bool(b) => JSExpr::Lit(JSLiteral::Bool(b)),
|
||||
},
|
||||
Expr::Sym(s) => JSExpr::Sym(s),
|
||||
|
||||
Expr::UnaryOp(op, e) => JSExpr::Op(match op {
|
||||
UnaryOp::Neg => "-",
|
||||
UnaryOp::Not => "!",
|
||||
}, Box::new(translate_js(*e)), None),
|
||||
Expr::BinaryOp(op, e1, e2) => JSExpr::Op(match op {
|
||||
BinaryOp::Add => "+",
|
||||
BinaryOp::Sub => "-",
|
||||
BinaryOp::Mul => "*",
|
||||
BinaryOp::Div => "/",
|
||||
BinaryOp::Mod => "%",
|
||||
|
||||
BinaryOp::Eq => "==",
|
||||
BinaryOp::Neq => "!=",
|
||||
BinaryOp::Lt => "<",
|
||||
BinaryOp::Gt => ">",
|
||||
BinaryOp::Lte => "<=",
|
||||
BinaryOp::Gte => ">=",
|
||||
|
||||
BinaryOp::And => "&&",
|
||||
BinaryOp::Or => "||",
|
||||
}, Box::new(translate_js(*e1)), Some(Box::new(translate_js(*e2)))),
|
||||
|
||||
Expr::Call(f, args) => {
|
||||
match *f {
|
||||
Expr::Sym(ref s) => {
|
||||
match s.as_str() {
|
||||
"println" => {
|
||||
JSExpr::Method(
|
||||
Box::new(JSExpr::Sym("console".to_string())),
|
||||
"log".to_string(),
|
||||
args.into_iter().map(translate_js).collect(),
|
||||
)
|
||||
},
|
||||
_ => JSExpr::Call(
|
||||
Box::new(translate_js(*f)),
|
||||
args.into_iter().map(translate_js).collect(),
|
||||
),
|
||||
}
|
||||
},
|
||||
_ => JSExpr::Call(
|
||||
Box::new(translate_js(*f)),
|
||||
args.into_iter().map(translate_js).collect(),
|
||||
),
|
||||
}
|
||||
}
|
||||
Expr::Lambda { args, body } => JSExpr::Lambda {
|
||||
args,
|
||||
body: Box::new(translate_js(*body)),
|
||||
},
|
||||
}
|
||||
}
|
4
src/trans/mod.rs
Normal file
4
src/trans/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
pub mod ty;
|
||||
pub mod ast;
|
||||
pub mod js;
|
||||
pub mod low;
|
29
src/trans/ty.rs
Normal file
29
src/trans/ty.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
use std::fmt::{Display, Formatter, Result as FmtResult};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Type {
|
||||
Num, Str, Bool,
|
||||
Fun(Vec<Self>, Box<Self>),
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl Display for Type {
|
||||
fn fmt(&self, f: &mut Formatter) -> FmtResult {
|
||||
match self {
|
||||
Type::Num => write!(f, "num"),
|
||||
Type::Str => write!(f, "str"),
|
||||
Type::Bool => write!(f, "bool"),
|
||||
Type::Fun(args, ret) => {
|
||||
write!(f, "(")?;
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", arg)?;
|
||||
}
|
||||
write!(f, ") -> {}", ret)
|
||||
},
|
||||
Type::Unknown => write!(f, "unknown"),
|
||||
}
|
||||
}
|
||||
}
|
16
vm/Cargo.lock
generated
16
vm/Cargo.lock
generated
|
@ -1,16 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "mecha"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
]
|
|
@ -1,7 +0,0 @@
|
|||
[package]
|
||||
name = "vm"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
fnv = "1.0.7"
|
342
vm/src/exec.rs
342
vm/src/exec.rs
|
@ -1,342 +0,0 @@
|
|||
use crate::model::*;
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Executor {
|
||||
pub stack: Vec<Value>,
|
||||
pub env: Rc<RefCell<Env>>,
|
||||
pub outer_env: Option<Rc<RefCell<Env>>>,
|
||||
pub instrs: Vec<Instr>,
|
||||
pub ip: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Error(String, usize);
|
||||
|
||||
impl Error {
|
||||
pub fn make<S: Into<String>>(msg: S, ip: usize) -> Self {
|
||||
Self(msg.into(), ip)
|
||||
}
|
||||
}
|
||||
|
||||
impl Executor {
|
||||
pub fn new(instrs: Vec<Instr>) -> Self {
|
||||
Self {
|
||||
stack: Vec::new(),
|
||||
env: Rc::new(RefCell::new(Env::new())),
|
||||
outer_env: None,
|
||||
instrs,
|
||||
ip: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> Result<(), Error> {
|
||||
while self.ip < self.instrs.len() {
|
||||
self.step()?;
|
||||
self.ip += 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_with<F: Fn(&mut Self) -> Result<(), Error>>(&mut self, f: F) -> Result<(), Error> {
|
||||
while self.ip < self.instrs.len() {
|
||||
self.step()?;
|
||||
self.ip += 1;
|
||||
f(self)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn err(&self, msg: &str) -> Error {
|
||||
Error::make(msg, self.ip)
|
||||
}
|
||||
|
||||
fn push(&mut self, v: Value) -> Result<(), Error> {
|
||||
self.stack.push(v);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pop(&mut self) -> Result<Value, Error> {
|
||||
self.stack.pop().ok_or_else(|| self.err("stack underflow"))
|
||||
}
|
||||
|
||||
fn get(&self, name: &str) -> Result<Value, Error> {
|
||||
self.env
|
||||
.borrow()
|
||||
.get(name)
|
||||
.ok_or_else(|| self.err(format!("unbound variable: {}", name).as_str()))
|
||||
}
|
||||
|
||||
fn set(&mut self, name: &str, v: Value) -> Result<(), Error> {
|
||||
// Set the variable in the current environment if it is defined
|
||||
if self.env.borrow().binds.contains_key(name) {
|
||||
self.env.borrow_mut().binds.insert(name.to_string(), v);
|
||||
// If it is not defined in the current environment then try the outer environment
|
||||
} else if let Some(env) = &self.outer_env {
|
||||
if env.borrow().binds.contains_key(name) {
|
||||
env.borrow_mut().binds.insert(name.to_string(), v);
|
||||
} else {
|
||||
// If not then define it in the current environment
|
||||
self.env.borrow_mut().binds.insert(name.to_string(), v);
|
||||
}
|
||||
} else {
|
||||
self.env.borrow_mut().binds.insert(name.to_string(), v);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn step(&mut self) -> Result<(), Error> {
|
||||
let instr = self.instrs.clone(); // TODO: maybe don't clone here
|
||||
let instr = instr
|
||||
.get(self.ip)
|
||||
.ok_or_else(|| self.err("invalid instruction pointer"))?;
|
||||
|
||||
macro_rules! impl_binop {
|
||||
($op:tt, $inp:ident, $ret:ident) => {
|
||||
match (self.pop()?, self.pop()?) {
|
||||
(Value::$inp(a), Value::$inp(b)) => {
|
||||
self.stack.push(Value::$ret(a $op b));
|
||||
}
|
||||
_ => return Err(Error::make(format!("can't apply operator to non-{}", stringify!($inp)).as_str(), self.ip)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
match instr {
|
||||
Instr::NumPush(x) => {
|
||||
self.push(Value::Num(*x))?;
|
||||
}
|
||||
Instr::NumAdd => impl_binop!(+, Num, Num),
|
||||
Instr::NumSub => impl_binop!(-, Num, Num),
|
||||
Instr::NumMul => impl_binop!(*, Num, Num),
|
||||
Instr::NumDiv => impl_binop!(/, Num, Num),
|
||||
Instr::NumMod => impl_binop!(%, Num, Num),
|
||||
Instr::NumEq => impl_binop!(==, Num, Bool),
|
||||
Instr::NumNe => impl_binop!(!=, Num, Bool),
|
||||
Instr::NumLt => impl_binop!(<, Num, Bool),
|
||||
Instr::NumGt => impl_binop!(>, Num, Bool),
|
||||
Instr::NumLe => impl_binop!(<=, Num, Bool),
|
||||
Instr::NumGe => impl_binop!(>=, Num, Bool),
|
||||
|
||||
Instr::BoolPush(x) => {
|
||||
self.push(Value::Bool(*x))?;
|
||||
}
|
||||
Instr::BoolAnd => impl_binop!(&&, Bool, Bool),
|
||||
Instr::BoolOr => impl_binop!(||, Bool, Bool),
|
||||
Instr::BoolNot => {
|
||||
if let Value::Bool(b) = self.pop()? {
|
||||
self.push(Value::Bool(!b))?;
|
||||
} else {
|
||||
return Err(Error::make("can't apply `not` to non-boolean", self.ip));
|
||||
}
|
||||
}
|
||||
|
||||
Instr::StrPush(x) => {
|
||||
self.push(Value::Str(x.clone()))?;
|
||||
}
|
||||
Instr::StrConcat => {
|
||||
if let (Value::Str(a), Value::Str(b)) = (self.pop()?, self.pop()?) {
|
||||
self.push(Value::Str(a + &b))?;
|
||||
} else {
|
||||
return Err(Error::make("can't concatenate non-strings", self.ip));
|
||||
}
|
||||
}
|
||||
Instr::Pop => {
|
||||
self.pop()?;
|
||||
}
|
||||
Instr::Dup => {
|
||||
let v = self.pop()?;
|
||||
self.push(v.clone())?;
|
||||
self.push(v)?;
|
||||
}
|
||||
|
||||
Instr::ListMake(len) => {
|
||||
let mut list = Vec::new();
|
||||
for _ in 0..*len {
|
||||
list.push(
|
||||
self.pop()
|
||||
.map_err(|_| self.err("not enough arguments to make List"))?,
|
||||
);
|
||||
}
|
||||
list.reverse();
|
||||
self.push(Value::List(list))?;
|
||||
}
|
||||
Instr::ListGet(index) => {
|
||||
if let Value::List(list) = self.pop()? {
|
||||
let v = list
|
||||
.get(*index)
|
||||
.cloned()
|
||||
.ok_or_else(|| self.err("index out of bounds"))?;
|
||||
self.push(v)?;
|
||||
} else {
|
||||
return Err(Error::make("can't get from non-List", self.ip));
|
||||
}
|
||||
}
|
||||
Instr::ListSet(index) => {
|
||||
if let Value::List(mut list) = self.pop()? {
|
||||
let v = self.pop()?;
|
||||
list.get_mut(*index)
|
||||
.ok_or_else(|| self.err("index out of bounds"))?
|
||||
.clone_from(&v);
|
||||
self.push(Value::List(list))?;
|
||||
} else {
|
||||
return Err(Error::make("can't set in non-List", self.ip));
|
||||
}
|
||||
}
|
||||
Instr::ListLen => {
|
||||
if let Value::List(list) = self.pop()? {
|
||||
self.push(Value::Num(list.len() as i64))?;
|
||||
} else {
|
||||
return Err(Error::make("can't get length of non-List", self.ip));
|
||||
}
|
||||
}
|
||||
Instr::ListJoin => {
|
||||
if let (Value::List(mut list1), Value::List(list2)) = (self.pop()?, self.pop()?) {
|
||||
list1.extend(list2);
|
||||
self.push(Value::List(list1))?;
|
||||
} else {
|
||||
return Err(Error::make("can't join non-Lists", self.ip));
|
||||
}
|
||||
}
|
||||
|
||||
Instr::FuncMake(args, instrs) => {
|
||||
let closure = Func::new(args.to_vec(), Rc::clone(&self.env), instrs.clone());
|
||||
self.push(Value::Func(closure))?;
|
||||
}
|
||||
Instr::FuncApply => {
|
||||
let v = self.pop()?;
|
||||
if let Value::Func(closure) = v {
|
||||
// Pop the arguments
|
||||
let mut args = Vec::new();
|
||||
for _ in 0..closure.args.len() {
|
||||
args.push(
|
||||
self.pop()
|
||||
.map_err(|_| self.err("not enough arguments to apply Function"))?,
|
||||
);
|
||||
}
|
||||
args.reverse();
|
||||
|
||||
self.stack.append(&mut closure.run(args)?);
|
||||
} else {
|
||||
return Err(Error::make(
|
||||
format!("can't apply non-Function, got {:?}", v),
|
||||
self.ip,
|
||||
));
|
||||
}
|
||||
}
|
||||
Instr::FuncCall(name) => {
|
||||
if let Value::Func(closure) = self.get(name)? {
|
||||
let mut args = Vec::new();
|
||||
for _ in 0..closure.args.len() {
|
||||
args.push(
|
||||
self.pop()
|
||||
.map_err(|_| self.err("not enough arguments to call Function"))?,
|
||||
);
|
||||
}
|
||||
args.reverse();
|
||||
|
||||
self.stack.append(&mut closure.run(args)?);
|
||||
} else {
|
||||
return Err(Error::make("can't call non-Function", self.ip));
|
||||
}
|
||||
}
|
||||
|
||||
Instr::Get(name) => {
|
||||
let v = self.get(name)?;
|
||||
self.push(v)?;
|
||||
}
|
||||
Instr::Set(name) => {
|
||||
let v = self.pop()?;
|
||||
self.set(name, v)?;
|
||||
}
|
||||
|
||||
Instr::Jump(n) => {
|
||||
self.ip += n;
|
||||
}
|
||||
Instr::JumpIfFalse(n) => {
|
||||
if let Value::Bool(b) = self.pop()? {
|
||||
if !b {
|
||||
self.ip += n;
|
||||
}
|
||||
} else {
|
||||
return Err(Error::make("can't apply `if` to non-boolean", self.ip));
|
||||
}
|
||||
}
|
||||
|
||||
Instr::Print => {
|
||||
let v = self.pop()?;
|
||||
print!("{}", v);
|
||||
}
|
||||
Instr::PrintLn => {
|
||||
let v = self.pop()?;
|
||||
println!("{}", v);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn exec_expect(executor: &mut Executor, expected: Vec<Value>) {
|
||||
match executor.run() {
|
||||
Ok(_) => {
|
||||
assert_eq!(executor.stack, expected);
|
||||
}
|
||||
Err(e) => panic!("{:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sanity() {
|
||||
let mut executor = Executor::new(vec![Instr::NumPush(1), Instr::NumPush(2), Instr::NumAdd]);
|
||||
exec_expect(&mut executor, vec![Value::Num(3)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_pop_underflow() {
|
||||
let mut executor = Executor::new(vec![Instr::NumAdd]);
|
||||
executor.run().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_closure() {
|
||||
let mut executor = Executor::new(vec![
|
||||
Instr::FuncMake(
|
||||
vec![],
|
||||
vec![
|
||||
Instr::NumPush(0),
|
||||
Instr::Set("total".to_string()),
|
||||
Instr::FuncMake(
|
||||
vec![],
|
||||
vec![
|
||||
Instr::Get("total".to_string()),
|
||||
Instr::NumPush(1),
|
||||
Instr::NumAdd,
|
||||
Instr::Set("total".to_string()),
|
||||
Instr::Get("total".to_string()),
|
||||
],
|
||||
),
|
||||
Instr::Set("counter".to_string()),
|
||||
Instr::Get("counter".to_string()),
|
||||
],
|
||||
),
|
||||
Instr::FuncApply,
|
||||
Instr::Set("tally".to_string()),
|
||||
Instr::Get("tally".to_string()),
|
||||
Instr::FuncApply,
|
||||
Instr::Get("tally".to_string()),
|
||||
Instr::FuncApply,
|
||||
Instr::Get("tally".to_string()),
|
||||
Instr::FuncApply,
|
||||
]);
|
||||
exec_expect(
|
||||
&mut executor,
|
||||
vec![Value::Num(1), Value::Num(2), Value::Num(3)],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
#![allow(clippy::new_without_default)]
|
||||
pub mod exec;
|
||||
pub mod model;
|
||||
|
||||
// fn _main() {
|
||||
// let instrs = vec![
|
||||
// Instr::NumPush(34),
|
||||
// Instr::NumPush(34),
|
||||
// Instr::FuncMake(
|
||||
// vec!["abc".to_string()],
|
||||
// vec![
|
||||
// Instr::Get("abc".to_string()),
|
||||
// Instr::NumPush(1),
|
||||
// Instr::NumAdd,
|
||||
// ],
|
||||
// ),
|
||||
// Instr::FuncApply,
|
||||
// Instr::NumAdd,
|
||||
// Instr::Print,
|
||||
// ];
|
||||
|
||||
// // instrs.iter().for_each(|instr| {
|
||||
// // println!(
|
||||
// // "{}",
|
||||
// // instr
|
||||
// // .to_bytes()
|
||||
// // .iter()
|
||||
// // .map(|b| format!("{:02x}", b))
|
||||
// // .collect::<Vec<_>>()
|
||||
// // .join(" ")
|
||||
// // )
|
||||
// // });
|
||||
|
||||
// let mut executor = Executor::new(instrs);
|
||||
// match executor.run() {
|
||||
// Ok(()) => (),
|
||||
// Err(e) => println!("{:?}", e),
|
||||
// }
|
||||
// }
|
359
vm/src/model.rs
359
vm/src/model.rs
|
@ -1,359 +0,0 @@
|
|||
use crate::exec::{Error, Executor};
|
||||
use fnv::FnvHashMap;
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
fmt::{Debug, Display},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub enum Value {
|
||||
Num(i64),
|
||||
Bool(bool),
|
||||
Str(String),
|
||||
List(Vec<Self>),
|
||||
Func(Func),
|
||||
}
|
||||
|
||||
impl Debug for Value {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Value::Num(n) => write!(f, "Num({})", n),
|
||||
Value::Bool(b) => write!(f, "Bool({})", b),
|
||||
Value::Str(s) => write!(f, "Str({})", s),
|
||||
Value::List(xs) => write!(f, "List({:?})", xs),
|
||||
Value::Func(c) => write!(f, "Func({})", c.args.len()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Value {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Value::Num(n) => write!(f, "{}", n),
|
||||
Value::Bool(b) => write!(f, "{}", b),
|
||||
Value::Str(s) => write!(f, "{}", s),
|
||||
Value::List(xs) => write!(
|
||||
f,
|
||||
"[{}]",
|
||||
xs.iter()
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
),
|
||||
Value::Func(_) => write!(f, "<Func>"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Env {
|
||||
pub binds: FnvHashMap<String, Value>,
|
||||
pub parent: Option<Rc<Self>>,
|
||||
}
|
||||
|
||||
impl Env {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
binds: FnvHashMap::default(),
|
||||
parent: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_parent(parent: Rc<Self>) -> Self {
|
||||
Self {
|
||||
binds: FnvHashMap::default(),
|
||||
parent: Some(parent),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, name: &str) -> Option<Value> {
|
||||
// Get the value from the current environment first
|
||||
// and then from the parent environment recursively
|
||||
self.binds
|
||||
.get(name)
|
||||
.cloned()
|
||||
.or_else(|| self.parent.as_ref().and_then(|p| p.get(name)).or(None))
|
||||
}
|
||||
|
||||
pub fn set(&mut self, name: String, value: Value) {
|
||||
// Set the value in the current environment
|
||||
// The handling of deciding whether to create a new binding
|
||||
// is done in the Executor
|
||||
self.binds.insert(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Func {
|
||||
pub args: Vec<String>,
|
||||
pub env: Rc<RefCell<Env>>,
|
||||
pub instrs: Vec<Instr>,
|
||||
}
|
||||
|
||||
impl Func {
|
||||
pub fn new(args: Vec<String>, env: Rc<RefCell<Env>>, instrs: Vec<Instr>) -> Self {
|
||||
Self { args, env, instrs }
|
||||
}
|
||||
|
||||
pub fn run(self, args: Vec<Value>) -> Result<Vec<Value>, Error> {
|
||||
// Create a new environment for the closure
|
||||
let mut closure_env = Env::new();
|
||||
for (arg, val) in self.args.iter().zip(args) {
|
||||
closure_env.binds.insert(arg.clone(), val);
|
||||
}
|
||||
// Set the parent to the current environment
|
||||
closure_env.parent = Some(Rc::new(self.env.borrow().clone()));
|
||||
|
||||
// Execute the closure
|
||||
let mut new_executor = Executor {
|
||||
stack: Vec::new(),
|
||||
env: Rc::new(RefCell::new(closure_env)),
|
||||
outer_env: Some(Rc::clone(&self.env)),
|
||||
instrs: self.instrs,
|
||||
ip: 0,
|
||||
};
|
||||
new_executor.run()?;
|
||||
Ok(new_executor.stack)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Instr {
|
||||
// Example: NumPush -34, NumPush 103, NumAdd
|
||||
// 00 de ff ff ff ff ff ff ff
|
||||
// 00 67 00 00 00 00 00 00 00
|
||||
// 01
|
||||
NumPush(i64), // 9 bytes: 1 byte for the enum, 8 bytes for the i64
|
||||
NumAdd, // ┐ 1 byte
|
||||
NumSub, // │
|
||||
NumMul, // │
|
||||
NumDiv, // │
|
||||
NumMod, // │
|
||||
NumEq, // │
|
||||
NumNe, // │
|
||||
NumLt, // │
|
||||
NumGt, // │
|
||||
NumLe, // │
|
||||
NumGe, // ┘
|
||||
|
||||
BoolPush(bool), // 2 bytes: 1 byte for the enum, 1 byte for the bool
|
||||
BoolAnd, // ┐ 1 byte
|
||||
BoolOr, // │
|
||||
BoolNot, // ┘
|
||||
|
||||
// StrPush:
|
||||
// ┌─┬───╶╶╶┐
|
||||
// │x│ s... [00]
|
||||
// └─┴───╶╶╶┘
|
||||
// where x is the enum (1 byte)
|
||||
// s is the string (n bytes)
|
||||
// Example: StrPush "Hello, World!"
|
||||
// [XX] [48 65 6c 6c 6f 2c 20 57 6f 72 6c 64 21] [00]
|
||||
// └────┼────────────────────────────────────────┼─╼ enum
|
||||
// └────────────────────────────────────────┼─╼ string
|
||||
// └─╼ null delimiter
|
||||
// Total of 15 bytes (1 + 13 + 1)
|
||||
StrPush(String), // 1 + string.len() + 1 bytes
|
||||
StrConcat, // 1 byte
|
||||
|
||||
Pop, // ┐ 1 byte
|
||||
Dup, // ┘
|
||||
|
||||
ListMake(usize), // ┐ 9 bytes: 1 byte for the enum, 8 bytes for the usize (64-bit)
|
||||
ListGet(usize), // │
|
||||
ListSet(usize), // ┘
|
||||
ListLen, // ┐ 1 byte
|
||||
ListJoin, // ┘
|
||||
|
||||
// FuncMake:
|
||||
// ┌─┬───┬───┬─────╶╶╶┬──────╶╶╶╶╶
|
||||
// │x│ n │ m │ a... │ i...
|
||||
// └─┴───┴───┴─────╶╶╶┴──────╶╶╶╶╶
|
||||
// where x is the enum (1 byte)
|
||||
// n is the number of arguments (8 bytes)
|
||||
// m is the number of instructions (8 bytes)
|
||||
// a is the arguments (n bytes, null delimited)
|
||||
// ╴╴┬──────┬────┬╶╶
|
||||
// │ s... │ 00 │ // For example: "a", "bc" -> [61 00 62 63 00]
|
||||
// ╴╴┴──────┴────┴╶╶
|
||||
// i is the instructions (m bytes)
|
||||
// Example: FuncMake ["x", "y"] [Get "x", Get "yz", NumAdd]
|
||||
// [XX] [02 ..] [03 ..] [78 00 79 7a 00] [16 78 00 16 79 7a 00 01]
|
||||
// └────┼───────┼───────┼────────────────┼─╼ enum
|
||||
// └───────┼───────┼────────────────┼─╼ number of arguments
|
||||
// └───────┼────────────────┼─╼ number of instructions
|
||||
// └────────────────┼─╼ arguments (null delimited)
|
||||
// └─╼ instructions
|
||||
FuncMake(Vec<String>, Vec<Instr>), // 1 + 8 + 8 + args.len() + instrs.len() bytes
|
||||
FuncApply, // 1 byte
|
||||
FuncCall(String), // 1 + string.len() + 1 bytes
|
||||
|
||||
Get(String), // ┐ 1 + string.len() + 1 bytes
|
||||
Set(String), // ┘
|
||||
|
||||
Jump(usize), // ┐ 9 bytes: 1 byte for the enum, 8 bytes for the usize (64-bit)
|
||||
JumpIfFalse(usize), // ┘
|
||||
|
||||
Print, // ┐ 1 byte
|
||||
PrintLn, // ┘
|
||||
}
|
||||
|
||||
static mut INSTR_INDEX: Cell<u8> = Cell::new(0);
|
||||
|
||||
impl Instr {
|
||||
pub fn size(&self) -> usize {
|
||||
match self {
|
||||
Instr::NumPush(_) => 1 + std::mem::size_of::<i64>(),
|
||||
Instr::NumAdd
|
||||
| Instr::NumSub
|
||||
| Instr::NumMul
|
||||
| Instr::NumDiv
|
||||
| Instr::NumMod
|
||||
| Instr::NumEq
|
||||
| Instr::NumNe
|
||||
| Instr::NumLt
|
||||
| Instr::NumGt
|
||||
| Instr::NumLe
|
||||
| Instr::NumGe => 1,
|
||||
|
||||
Instr::BoolPush(_) => 1 + std::mem::size_of::<bool>(),
|
||||
Instr::BoolAnd | Instr::BoolOr | Instr::BoolNot => 1,
|
||||
|
||||
Instr::StrPush(s) => 1 + s.len() + 1,
|
||||
Instr::StrConcat => 1,
|
||||
|
||||
Instr::Pop | Instr::Dup => 1,
|
||||
|
||||
Instr::ListMake(_) | Instr::ListGet(_) | Instr::ListSet(_) => {
|
||||
1 + std::mem::size_of::<usize>()
|
||||
}
|
||||
Instr::ListLen | Instr::ListJoin => 1,
|
||||
|
||||
Instr::FuncMake(args, instrs) => {
|
||||
1 + 8
|
||||
+ 8
|
||||
+ args.iter().map(|s| s.len() + 1).sum::<usize>()
|
||||
+ instrs.iter().map(|i| i.size()).sum::<usize>()
|
||||
}
|
||||
Instr::FuncApply => 1,
|
||||
Instr::FuncCall(s) => 1 + s.len() + 1,
|
||||
|
||||
Instr::Get(s) | Instr::Set(s) => 1 + s.len() + 1,
|
||||
|
||||
Instr::Jump(_) | Instr::JumpIfFalse(_) => 1 + std::mem::size_of::<usize>(),
|
||||
|
||||
Instr::Print | Instr::PrintLn => 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
// A macro that will return the next index and increment it
|
||||
// so we don't have to rewrite all the first bytes again when
|
||||
// we changes the order or add new instructions
|
||||
macro_rules! index {
|
||||
() => {
|
||||
unsafe {
|
||||
let i = INSTR_INDEX.get();
|
||||
INSTR_INDEX.set(i + 1);
|
||||
i
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let mut bytes = Vec::new();
|
||||
match self {
|
||||
Instr::NumPush(n) => {
|
||||
bytes.push(index!());
|
||||
bytes.extend(n.to_le_bytes());
|
||||
}
|
||||
Instr::NumAdd
|
||||
| Instr::NumSub
|
||||
| Instr::NumMul
|
||||
| Instr::NumDiv
|
||||
| Instr::NumMod
|
||||
| Instr::NumEq
|
||||
| Instr::NumNe
|
||||
| Instr::NumLt
|
||||
| Instr::NumGt
|
||||
| Instr::NumLe
|
||||
| Instr::NumGe => bytes.push(index!()),
|
||||
|
||||
Instr::BoolPush(b) => {
|
||||
bytes.push(index!());
|
||||
bytes.push(*b as u8);
|
||||
}
|
||||
Instr::BoolAnd => bytes.push(index!()),
|
||||
Instr::BoolOr => bytes.push(index!()),
|
||||
Instr::BoolNot => bytes.push(index!()),
|
||||
|
||||
Instr::StrPush(s) => {
|
||||
bytes.push(index!());
|
||||
bytes.extend(s.as_bytes());
|
||||
bytes.push(0x00);
|
||||
}
|
||||
Instr::StrConcat => bytes.push(index!()),
|
||||
|
||||
Instr::Pop => bytes.push(index!()),
|
||||
Instr::Dup => bytes.push(index!()),
|
||||
|
||||
Instr::ListMake(n) => {
|
||||
bytes.push(index!());
|
||||
bytes.extend(n.to_le_bytes());
|
||||
}
|
||||
Instr::ListGet(n) => {
|
||||
bytes.push(index!());
|
||||
bytes.extend(n.to_le_bytes());
|
||||
}
|
||||
Instr::ListSet(n) => {
|
||||
bytes.push(index!());
|
||||
bytes.extend(n.to_le_bytes());
|
||||
}
|
||||
Instr::ListLen => bytes.push(index!()),
|
||||
Instr::ListJoin => bytes.push(index!()),
|
||||
|
||||
Instr::FuncMake(args, instrs) => {
|
||||
bytes.push(index!());
|
||||
bytes.extend((args.len() as u64).to_le_bytes());
|
||||
bytes.extend((instrs.len() as u64).to_le_bytes());
|
||||
for arg in args {
|
||||
bytes.extend(arg.as_bytes());
|
||||
bytes.push(0x00);
|
||||
}
|
||||
for instr in instrs {
|
||||
bytes.extend(instr.to_bytes());
|
||||
}
|
||||
}
|
||||
Instr::FuncApply => bytes.push(index!()),
|
||||
Instr::FuncCall(s) => {
|
||||
bytes.push(index!());
|
||||
bytes.extend(s.as_bytes());
|
||||
bytes.push(0x00);
|
||||
}
|
||||
|
||||
Instr::Get(s) => {
|
||||
bytes.push(index!());
|
||||
bytes.extend(s.as_bytes());
|
||||
bytes.push(0x00);
|
||||
}
|
||||
Instr::Set(s) => {
|
||||
bytes.push(index!());
|
||||
bytes.extend(s.as_bytes());
|
||||
bytes.push(0x00);
|
||||
}
|
||||
|
||||
Instr::Jump(n) => {
|
||||
bytes.push(index!());
|
||||
bytes.extend(n.to_le_bytes());
|
||||
}
|
||||
Instr::JumpIfFalse(n) => {
|
||||
bytes.push(index!());
|
||||
bytes.extend(n.to_le_bytes());
|
||||
}
|
||||
|
||||
Instr::Print => bytes.push(index!()),
|
||||
Instr::PrintLn => bytes.push(index!()),
|
||||
}
|
||||
bytes
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue