mirror of
https://github.com/azur1s/bobbylisp.git
synced 2024-10-16 02:37:40 -05:00
:trollface:
This commit is contained in:
parent
3768562c52
commit
89db9e8b1e
136
Cargo.lock
generated
136
Cargo.lock
generated
|
@ -12,14 +12,12 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atty"
|
name = "ariadne"
|
||||||
version = "0.2.14"
|
version = "0.1.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
checksum = "f1cb2a2046bea8ce5e875551f5772024882de0b540c7f93dfc5d6cf1ca8b030c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"yansi",
|
||||||
"libc",
|
|
||||||
"winapi",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -28,13 +26,6 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "checker"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"syntax",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chumsky"
|
name = "chumsky"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
|
@ -45,28 +36,18 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codegen"
|
name = "compiler"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"syntax",
|
"parser",
|
||||||
]
|
"vm",
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "colored"
|
|
||||||
version = "2.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
|
|
||||||
dependencies = [
|
|
||||||
"atty",
|
|
||||||
"lazy_static",
|
|
||||||
"winapi",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-random"
|
name = "const-random"
|
||||||
version = "0.1.13"
|
version = "0.1.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f590d95d011aa80b063ffe3253422ed5aa462af4e9867d43ce8337562bac77c4"
|
checksum = "368a7a772ead6ce7e1de82bfb04c485f3db8ec744f72925af5735e29a22cc18e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"const-random-macro",
|
"const-random-macro",
|
||||||
"proc-macro-hack",
|
"proc-macro-hack",
|
||||||
|
@ -74,12 +55,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-random-macro"
|
name = "const-random-macro"
|
||||||
version = "0.1.13"
|
version = "0.1.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "615f6e27d000a2bffbc7f2f6a8669179378fa27ee4d0a509e985dfc0a7defb40"
|
checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"lazy_static",
|
"once_cell",
|
||||||
"proc-macro-hack",
|
"proc-macro-hack",
|
||||||
"tiny-keccak",
|
"tiny-keccak",
|
||||||
]
|
]
|
||||||
|
@ -91,10 +72,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "entry"
|
||||||
version = "0.2.6"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"compiler",
|
||||||
|
"parser",
|
||||||
|
"vm",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -102,49 +98,31 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "libc"
|
||||||
version = "0.1.19"
|
version = "0.2.138"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hzc"
|
name = "once_cell"
|
||||||
|
version = "1.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parser"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"checker",
|
"ariadne",
|
||||||
"codegen",
|
"chumsky",
|
||||||
"colored",
|
|
||||||
"syntax",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lazy_static"
|
|
||||||
version = "1.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libc"
|
|
||||||
version = "0.2.124"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-hack"
|
name = "proc-macro-hack"
|
||||||
version = "0.5.19"
|
version = "0.5.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syntax"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"chumsky",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tiny-keccak"
|
name = "tiny-keccak"
|
||||||
version = "2.0.2"
|
version = "2.0.2"
|
||||||
|
@ -155,36 +133,20 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "transformer"
|
name = "vm"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"syntax",
|
"fnv",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.10.2+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "yansi"
|
||||||
version = "0.3.9"
|
version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||||
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"
|
|
||||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -1,8 +1,7 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"core",
|
"entry",
|
||||||
"syntax",
|
"parser",
|
||||||
"checker",
|
"compiler",
|
||||||
"transformer",
|
"vm",
|
||||||
"codegen",
|
]
|
||||||
]
|
|
||||||
|
|
34
README.md
34
README.md
|
@ -1,34 +0,0 @@
|
||||||
# Hazure
|
|
||||||
Programming language that compiles to Typescript!
|
|
||||||
|
|
||||||
Note: Everything in this project can be changed at anytime! (I'm still finding out what work best for lots of thing) if you have an idea, feel free to create an issues about it, or even create a PR! (I'd be very happy)
|
|
||||||
|
|
||||||
# Prerequistie
|
|
||||||
- `node`/`deno` for running Typescript
|
|
||||||
- Rust (if you're going to build from source)
|
|
||||||
- (Optional) if you use Vim, you can get the syntax highlighting [here](https://github.com/azur1s/hazure.vim)
|
|
||||||
|
|
||||||
# Installing
|
|
||||||
Currently there is only a build script on linux:
|
|
||||||
```
|
|
||||||
$ curl -s https://raw.githubusercontent.com/azur1s/hazure/master/build.sh | bash -s
|
|
||||||
...
|
|
||||||
$ hzc --help
|
|
||||||
```
|
|
||||||
or if you want to build in debug mode:
|
|
||||||
```
|
|
||||||
curl -s https://raw.githubusercontent.com/azur1s/hazure/master/build.sh | bash -s d
|
|
||||||
...
|
|
||||||
$ hzc --help
|
|
||||||
```
|
|
||||||
|
|
||||||
# Contributing
|
|
||||||
Found a bug? Found a better way to do something? Make a pull request or tell me in the issues tab! Anything contributions helps :D
|
|
||||||
|
|
||||||
Steps to build:
|
|
||||||
1) Clone this repo `https://github.com/azur1s/hazure.git`
|
|
||||||
2) Build executable `cargo build`
|
|
||||||
3) Try running some examples! `hzc compile path/to/file.hz`
|
|
||||||
|
|
||||||
# License
|
|
||||||
Hazure is licensed under both [MIT license](https://github.com/azur1s/hazure/blob/master/LICENSE-MIT) and [Apache License](https://github.com/azur1s/hazure/blob/master/LICENSE-APACHE)
|
|
61
build.sh
61
build.sh
|
@ -1,61 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Exit if subprocess return non-zero exit code
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Log function
|
|
||||||
log () {
|
|
||||||
echo -e "\033[0;32m[LOG]\033[0m $1"
|
|
||||||
}
|
|
||||||
err () {
|
|
||||||
echo -e "\033[0;31m[ERR]\033[0m $1"
|
|
||||||
}
|
|
||||||
|
|
||||||
# This will always be true unless there is
|
|
||||||
# missing executable that we need to use
|
|
||||||
install_pass=true
|
|
||||||
|
|
||||||
# Check if $1 is installed
|
|
||||||
check_installed () {
|
|
||||||
if ! command -v $1 -h &> /dev/null
|
|
||||||
then
|
|
||||||
err "$1 is not installed"
|
|
||||||
if [ install_pass ]; then
|
|
||||||
install_pass=false
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
check_installed cargo
|
|
||||||
check_installed git
|
|
||||||
check_installed deno # deno is required for running transpiled program
|
|
||||||
|
|
||||||
# If all of the above is installed
|
|
||||||
if [ ! install_pass ]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
log "Dependencies is installed. Cloning..."
|
|
||||||
|
|
||||||
rm -rf ~/.cache/hazure/build/
|
|
||||||
git clone https://github.com/azur1s/hazure.git ~/.cache/hazure/build/
|
|
||||||
|
|
||||||
cd ~/.cache/hazure/build/
|
|
||||||
|
|
||||||
if [[ $1 == *"d"* ]]; then
|
|
||||||
log "Building in debug..."
|
|
||||||
cargo build
|
|
||||||
rm ~/bin/hzc -f
|
|
||||||
mv ~/.cache/hazure/build/target/debug/hzc ~/bin/hzc
|
|
||||||
else
|
|
||||||
log "Building..."
|
|
||||||
cargo build --release
|
|
||||||
rm ~/bin/hzc -f
|
|
||||||
mv ~/.cache/hazure/build/target/release/hzc ~/bin/hzc
|
|
||||||
fi
|
|
||||||
|
|
||||||
log "Build done. Cleaning up..."
|
|
||||||
|
|
||||||
rm -rf ~/.cache/hazure/build/
|
|
||||||
|
|
||||||
log "Done."
|
|
||||||
hzc -v
|
|
|
@ -1,7 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "checker"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
syntax = { path = "../syntax" }
|
|
|
@ -1 +0,0 @@
|
||||||
pub mod syntax;
|
|
|
@ -1,7 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "codegen"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
syntax = { path = "../syntax" }
|
|
|
@ -1,192 +0,0 @@
|
||||||
use syntax::{ast::*, lex::Span};
|
|
||||||
|
|
||||||
/// A struct that contains emitted code.
|
|
||||||
pub struct Codegen {
|
|
||||||
/// The emitted code.
|
|
||||||
/// When the codegen is done, this will be joined into a single string
|
|
||||||
pub emitted: Vec<String>,
|
|
||||||
/// Finalized code in bytes
|
|
||||||
pub finalized: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Codegen { fn default() -> Self { Self::new() } }
|
|
||||||
|
|
||||||
impl Codegen {
|
|
||||||
pub fn new() -> Codegen {
|
|
||||||
Codegen { emitted: Vec::new(), finalized: Vec::new() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Emit a string to the output.
|
|
||||||
pub fn emit<S: Into<String>>(&mut self, s: S) {
|
|
||||||
self.emitted.push(s.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn gen(&mut self, ast: Vec<(Expr, Span)>) {
|
|
||||||
for (expr, _) in ast {
|
|
||||||
self.emit(self.gen_expr(&expr, true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gen_expr(&self, expr: &Expr, semicolon: bool) -> String {
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! semicolon { () => { if semicolon { ";" } else { "" } }; }
|
|
||||||
|
|
||||||
match expr {
|
|
||||||
Expr::Literal(lit) => self.gen_literal(&lit.0),
|
|
||||||
Expr::Identifier(name) => { format!("_{}{}", name.0, semicolon!()) },
|
|
||||||
Expr::Tuple(elems) | Expr::Vector(elems) => { format!("[{}{}]", elems.iter().map(|e| self.gen_expr(&e.0, false)).collect::<Vec<_>>().join(", "), semicolon!()) },
|
|
||||||
Expr::Object { fields } => {
|
|
||||||
format!("{{{}}}",
|
|
||||||
fields.iter().map(|(name, expr)| format!("{}: {}", name.0, self.gen_expr(&expr.0, false))).collect::<Vec<_>>().join(",\n "))
|
|
||||||
},
|
|
||||||
|
|
||||||
Expr::Unary { op, rhs } => { format!("{}{}", op, self.gen_expr(&rhs.0, false)) },
|
|
||||||
Expr::Binary { op, lhs, rhs } => {
|
|
||||||
format!("{}{}{}{}", self.gen_expr(&lhs.0, false), op, self.gen_expr(&rhs.0, false), semicolon!())
|
|
||||||
},
|
|
||||||
|
|
||||||
Expr::Call { name, args } => {
|
|
||||||
format!(
|
|
||||||
"{}({}){}",
|
|
||||||
self.gen_expr(&name.0, false),
|
|
||||||
args
|
|
||||||
.iter()
|
|
||||||
.map(|arg| self.gen_expr(&arg.0, false))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(", "),
|
|
||||||
semicolon!())
|
|
||||||
},
|
|
||||||
Expr::Method { obj, name, args } => {
|
|
||||||
format!(
|
|
||||||
"{}.{}({}){}",
|
|
||||||
self.gen_expr(&obj.0, false),
|
|
||||||
self.gen_expr(&name.0, false).trim_start_matches('_'),
|
|
||||||
args
|
|
||||||
.iter()
|
|
||||||
.map(|arg| self.gen_expr(&arg.0, false))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(", "),
|
|
||||||
semicolon!())
|
|
||||||
},
|
|
||||||
Expr::Access { obj, name } => {
|
|
||||||
format!("{}.{}", self.gen_expr(&obj.0, false), self.gen_expr(&name.0, false).trim_start_matches('_'))
|
|
||||||
},
|
|
||||||
Expr::Intrinsic { name, args } => {
|
|
||||||
if let Expr::Identifier(name) = &name.0 {
|
|
||||||
match name.0.as_str() {
|
|
||||||
"write" => { format!("console.log({})", args.iter().map(|arg| self.gen_expr(&arg.0, false)).collect::<Vec<_>>().join(", ")) },
|
|
||||||
_ => unimplemented!(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
panic!("Expected identifier for intrinsic name");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
Expr::Define { name, typehint, value } => {
|
|
||||||
format!(
|
|
||||||
"let _{} : {} = {}{}",
|
|
||||||
name.0,
|
|
||||||
self.gen_typehint(&typehint.0),
|
|
||||||
self.gen_expr(&value.0, false),
|
|
||||||
semicolon!())
|
|
||||||
},
|
|
||||||
Expr::Redefine { name, value } => {
|
|
||||||
format!(
|
|
||||||
"_{} = {}{}",
|
|
||||||
name.0,
|
|
||||||
self.gen_expr(&value.0, false),
|
|
||||||
semicolon!())
|
|
||||||
},
|
|
||||||
|
|
||||||
Expr::Function { name, generics, args, typehint, body } => {
|
|
||||||
format!(
|
|
||||||
"const _{} = {}({}): {} => {{{}}}{}\n",
|
|
||||||
name.0,
|
|
||||||
if generics.is_empty() { "".to_string() } else {
|
|
||||||
format!("<{}>",
|
|
||||||
generics.iter().map(|g| g.0.clone()).collect::<Vec<_>>().join(", "))
|
|
||||||
},
|
|
||||||
args
|
|
||||||
.iter()
|
|
||||||
.map(|arg| format!("_{}: {}", arg.0.0, self.gen_typehint(&arg.1.0)))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(", "),
|
|
||||||
self.gen_typehint(&typehint.0),
|
|
||||||
self.gen_expr(&body.0, false),
|
|
||||||
semicolon!())
|
|
||||||
},
|
|
||||||
|
|
||||||
Expr::If { cond, t, f } => {
|
|
||||||
format!(
|
|
||||||
"if ({}) {{{}}} else {{{}}}",
|
|
||||||
self.gen_expr(&cond.0, false),
|
|
||||||
self.gen_expr(&t.0, false),
|
|
||||||
self.gen_expr(&f.0, false))
|
|
||||||
},
|
|
||||||
|
|
||||||
Expr::Do { body } => {
|
|
||||||
format!(
|
|
||||||
"{{\n{}}}\n",
|
|
||||||
body.0.iter().map(|e| self.gen_expr(&e.0, false)).collect::<Vec<_>>().join("\n"))
|
|
||||||
},
|
|
||||||
|
|
||||||
Expr::Return(expr) => {
|
|
||||||
format!("return {}\n", self.gen_expr(&expr.0, true))
|
|
||||||
},
|
|
||||||
|
|
||||||
#[allow(unreachable_patterns)]
|
|
||||||
_ => { dbg!(expr); todo!() },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gen_literal(&self, lit: &Literal) -> String {
|
|
||||||
match lit {
|
|
||||||
Literal::Int(i) => format!("{}", i),
|
|
||||||
Literal::String(s) => format!("\"{}\"", s),
|
|
||||||
Literal::Boolean(b) => format!("{}", b),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gen_typehint(&self, typehint: &Typehint) -> String {
|
|
||||||
match typehint {
|
|
||||||
Typehint::Builtin(ty) => {
|
|
||||||
match ty {
|
|
||||||
BuiltinType::Any => "any",
|
|
||||||
BuiltinType::Null => "null",
|
|
||||||
BuiltinType::Undefined => "undefined",
|
|
||||||
BuiltinType::Boolean => "boolean",
|
|
||||||
BuiltinType::Int => "number",
|
|
||||||
BuiltinType::String => "string",
|
|
||||||
}.to_string()
|
|
||||||
},
|
|
||||||
Typehint::Single(ty) => ty.clone(),
|
|
||||||
|
|
||||||
Typehint::Tuple(tys) => format!("[{}]", tys
|
|
||||||
.iter()
|
|
||||||
.map(|ty| self.gen_typehint(&ty.0)).collect::<Vec<_>>().join(", ")),
|
|
||||||
Typehint::Vector(ty) => format!("{}[]", self.gen_typehint(&ty.0)),
|
|
||||||
|
|
||||||
Typehint::Function(args, ret) => {
|
|
||||||
let args_ty = args.iter().map(|arg| self.gen_typehint(&arg.0)).collect::<Vec<_>>();
|
|
||||||
let return_ty = self.gen_typehint(&ret.0);
|
|
||||||
format!( "({}) => {}",
|
|
||||||
args_ty
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, arg)| format!("__{}: {}", i, arg)) // Maybe use this in the future
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(", "),
|
|
||||||
return_ty)
|
|
||||||
},
|
|
||||||
|
|
||||||
Typehint::Union(tys) => tys
|
|
||||||
.iter()
|
|
||||||
.map(|ty| self.gen_typehint(&ty.0)).collect::<Vec<_>>().join(" | "),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Finalize the code generation.
|
|
||||||
pub fn finalize(&mut self) {
|
|
||||||
self.finalized = self.emitted.join("\n").as_bytes().to_vec();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "hzc"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
syntax = { path = "../syntax" }
|
|
||||||
checker = { path = "../checker" }
|
|
||||||
codegen = { path = "../codegen" }
|
|
||||||
|
|
||||||
colored = "2.0.0"
|
|
|
@ -1,48 +0,0 @@
|
||||||
use std::{fs::File, io::Write};
|
|
||||||
|
|
||||||
use syntax::{lex::lex, parse::parse};
|
|
||||||
use codegen::Codegen;
|
|
||||||
|
|
||||||
pub mod util;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let path = std::env::args().nth(1).expect("No file specified");
|
|
||||||
let input = std::fs::read_to_string(path).expect("Failed to read file");
|
|
||||||
|
|
||||||
let time = std::time::Instant::now();
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lex
|
|
||||||
//
|
|
||||||
let (tokens, lex_errs) = lex(input.to_string());
|
|
||||||
|
|
||||||
if !lex_errs.is_empty() {
|
|
||||||
println!("Lex error(s): {:#?}", lex_errs);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Parse
|
|
||||||
//
|
|
||||||
let (ast, parse_errs) = parse(tokens.unwrap(), input.chars().count());
|
|
||||||
|
|
||||||
if !parse_errs.is_empty() || ast.is_none() {
|
|
||||||
println!("Parse error(s): {:#?}", parse_errs);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("{:#?}", ast.as_ref().unwrap());
|
|
||||||
info!("Parsed in {}ms", time.elapsed().as_millis());
|
|
||||||
|
|
||||||
//
|
|
||||||
// Codegen
|
|
||||||
//
|
|
||||||
let mut codegen = Codegen::new();
|
|
||||||
codegen.gen(ast.unwrap());
|
|
||||||
codegen.finalize();
|
|
||||||
|
|
||||||
let mut file = File::create("out.ts").unwrap();
|
|
||||||
file.write_all(&codegen.finalized).unwrap();
|
|
||||||
|
|
||||||
info!("Generated {} bytes in {} ms", codegen.finalized.len(), time.elapsed().as_millis());
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
use colored::Colorize;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub enum LogLevel { Debug, Info, Warn, Error }
|
|
||||||
|
|
||||||
fn prefix (level: LogLevel) -> String {
|
|
||||||
match level {
|
|
||||||
LogLevel::Debug => "DEBG ".bright_black(),
|
|
||||||
LogLevel::Info => "INFO ".blue(),
|
|
||||||
LogLevel::Warn => "WARN ".yellow(),
|
|
||||||
LogLevel::Error => "ERRO ".red(),
|
|
||||||
}.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn log <S: Into<String>>(level: LogLevel, message: S) {
|
|
||||||
println!("{}{}", prefix(level), message.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! info {
|
|
||||||
($($arg:tt)*) => {
|
|
||||||
$crate::util::log( $crate::util::LogLevel::Info, format!($($arg)*) );
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! warn {
|
|
||||||
($($arg:tt)*) => {
|
|
||||||
$crate::util::log( $crate::util::LogLevel::Warn, format!($($arg)*) );
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! error {
|
|
||||||
($($arg:tt)*) => {
|
|
||||||
$crate::util::log( $crate::util::LogLevel::Error, format!($($arg)*) );
|
|
||||||
};
|
|
||||||
}
|
|
16
examples/a.sial
Normal file
16
examples/a.sial
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
fun succ x = do
|
||||||
|
let one = 1
|
||||||
|
let y = x + one in y
|
||||||
|
end
|
||||||
|
|
||||||
|
fun double x = x * 2
|
||||||
|
|
||||||
|
fun main = do
|
||||||
|
let add = \x y -> x + y in
|
||||||
|
succ(34)
|
||||||
|
|> \x -> add(34, x)
|
||||||
|
|> \x -> println(x)
|
||||||
|
|
||||||
|
let result = add(34, 35)
|
||||||
|
println(result)
|
||||||
|
end
|
|
@ -1,7 +0,0 @@
|
||||||
factorial (n : int) : int =
|
|
||||||
if n == 0
|
|
||||||
| return 1
|
|
||||||
| return n * factorial(n - 1)
|
|
||||||
|
|
||||||
result : int = factorial(5)
|
|
||||||
@write(result)
|
|
|
@ -1,6 +0,0 @@
|
||||||
print T (x : T) : void = @write(x)
|
|
||||||
|
|
||||||
a : int = 123
|
|
||||||
print(a)
|
|
||||||
b : string = "Hello, World!"
|
|
||||||
print(b)
|
|
42
examples/ht.sial
Normal file
42
examples/ht.sial
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
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
|
8
examples/sim.sial
Normal file
8
examples/sim.sial
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
fun foo = \x -> x
|
||||||
|
|
||||||
|
fun main = do
|
||||||
|
foo(1)
|
||||||
|
[1, 2, 3]
|
||||||
|
true
|
||||||
|
print("Hello, World")
|
||||||
|
end
|
|
@ -1,2 +0,0 @@
|
||||||
[toolchain]
|
|
||||||
channel = "nightly"
|
|
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
version = "Two"
|
|
@ -1,7 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "syntax"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
chumsky = "0.8.0"
|
|
|
@ -1,111 +0,0 @@
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
pub type Spanned<T> = (T, std::ops::Range<usize>);
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum BuiltinType {
|
|
||||||
Any, Null, Undefined,
|
|
||||||
Boolean, Int, String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum Typehint {
|
|
||||||
Builtin(BuiltinType),
|
|
||||||
Single(String),
|
|
||||||
Tuple(Vec<Spanned<Self>>),
|
|
||||||
Vector(Box<Spanned<Self>>),
|
|
||||||
Function(Vec<Spanned<Self>>, Box<Spanned<Self>>),
|
|
||||||
Union(Vec<Spanned<Self>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum Literal {
|
|
||||||
Int(i64),
|
|
||||||
String(String),
|
|
||||||
Boolean(bool),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum UnaryOp { Minus, Not }
|
|
||||||
|
|
||||||
impl Display for UnaryOp {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
UnaryOp::Minus => write!(f, "-"),
|
|
||||||
UnaryOp::Not => write!(f, "!"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum BinaryOp {
|
|
||||||
Plus, Minus, Multiply, Divide, Modulus,
|
|
||||||
Equal, NotEqual, Less, Greater,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for BinaryOp {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
BinaryOp::Plus => write!(f, "+"),
|
|
||||||
BinaryOp::Minus => write!(f, "-"),
|
|
||||||
BinaryOp::Multiply => write!(f, "*"),
|
|
||||||
BinaryOp::Divide => write!(f, "/"),
|
|
||||||
BinaryOp::Modulus => write!(f, "%"),
|
|
||||||
BinaryOp::Equal => write!(f, "==="),
|
|
||||||
BinaryOp::NotEqual => write!(f, "!=="),
|
|
||||||
BinaryOp::Less => write!(f, "<"),
|
|
||||||
BinaryOp::Greater => write!(f, ">"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum Expr {
|
|
||||||
Literal(Spanned<Literal>),
|
|
||||||
Identifier(Spanned<String>),
|
|
||||||
Tuple(Vec<Spanned<Self>>),
|
|
||||||
Vector(Vec<Spanned<Self>>),
|
|
||||||
Object {
|
|
||||||
fields: Vec<(Spanned<String>, Spanned<Self>)>,
|
|
||||||
},
|
|
||||||
|
|
||||||
Unary { op: UnaryOp, rhs: Box<Spanned<Self>> },
|
|
||||||
Binary { op: BinaryOp, lhs: Box<Spanned<Self>>, rhs: Box<Spanned<Self>> },
|
|
||||||
|
|
||||||
Call { name: Box<Spanned<Self>>, args: Vec<Spanned<Self>> },
|
|
||||||
Method { obj: Box<Spanned<Self>>, name: Box<Spanned<Self>>, args: Vec<Spanned<Self>> },
|
|
||||||
Access { obj: Box<Spanned<Self>>, name: Box<Spanned<Self>> },
|
|
||||||
Intrinsic { name: Box<Spanned<Self>>, args: Vec<Spanned<Self>> },
|
|
||||||
|
|
||||||
Define {
|
|
||||||
name: Spanned<String>,
|
|
||||||
typehint: Spanned<Typehint>,
|
|
||||||
value: Box<Spanned<Self>>
|
|
||||||
},
|
|
||||||
Redefine {
|
|
||||||
name: Spanned<String>,
|
|
||||||
value: Box<Spanned<Self>>
|
|
||||||
},
|
|
||||||
Function {
|
|
||||||
name: Spanned<String>,
|
|
||||||
generics: Vec<Spanned<String>>,
|
|
||||||
args: Vec<(Spanned<String>, Spanned<Typehint>)>,
|
|
||||||
typehint: Spanned<Typehint>,
|
|
||||||
body: Box<Spanned<Self>>
|
|
||||||
},
|
|
||||||
|
|
||||||
If {
|
|
||||||
cond: Box<Spanned<Self>>,
|
|
||||||
t: Box<Spanned<Self>>,
|
|
||||||
f: Box<Spanned<Self>>
|
|
||||||
},
|
|
||||||
Do {
|
|
||||||
body: Spanned<Vec<Spanned<Self>>>
|
|
||||||
},
|
|
||||||
|
|
||||||
Return(Box<Spanned<Self>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum Stmt {
|
|
||||||
}
|
|
|
@ -1,121 +0,0 @@
|
||||||
use chumsky::prelude::*;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub enum Delimiter { Paren, Bracket, Brace }
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub enum Token {
|
|
||||||
// Keywords
|
|
||||||
KwFun, KwSet,
|
|
||||||
KwDo, KwEnd,
|
|
||||||
KwIf,
|
|
||||||
KwCase, KwOf,
|
|
||||||
KwReturn,
|
|
||||||
|
|
||||||
// Literals
|
|
||||||
Int(i64), Boolean(bool),
|
|
||||||
String(String), Identifier(String),
|
|
||||||
|
|
||||||
// Operators
|
|
||||||
Plus, Minus, Multiply, Divide, Modulus,
|
|
||||||
Not, Equal, NotEqual, Less, Greater,
|
|
||||||
Arrow, And, Or,
|
|
||||||
|
|
||||||
// Symbols & Delimiters
|
|
||||||
Assign, Dot, Comma, Colon, Semicolon, At, Hash,
|
|
||||||
Open(Delimiter), Close(Delimiter),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Span = std::ops::Range<usize>;
|
|
||||||
pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> {
|
|
||||||
let int = text::int(10)
|
|
||||||
.map(|s: String| Token::Int(s.parse().unwrap()));
|
|
||||||
|
|
||||||
let string = just('"')
|
|
||||||
.ignore_then(filter(|c| *c != '"').repeated())
|
|
||||||
.then_ignore(just('"'))
|
|
||||||
.collect::<String>()
|
|
||||||
.map(Token::String);
|
|
||||||
|
|
||||||
let symbol = choice((
|
|
||||||
just("->").to(Token::Arrow),
|
|
||||||
|
|
||||||
just('+').to(Token::Plus),
|
|
||||||
just('-').to(Token::Minus),
|
|
||||||
just('*').to(Token::Multiply),
|
|
||||||
just('/').to(Token::Divide),
|
|
||||||
just('%').to(Token::Modulus),
|
|
||||||
|
|
||||||
just('&').to(Token::And),
|
|
||||||
just('|').to(Token::Or),
|
|
||||||
|
|
||||||
just("!=").to(Token::NotEqual),
|
|
||||||
just('!').or(just('¬')).to(Token::Not),
|
|
||||||
just("==").to(Token::Equal),
|
|
||||||
|
|
||||||
just('<').to(Token::Less),
|
|
||||||
just('>').to(Token::Greater),
|
|
||||||
|
|
||||||
just('=').to(Token::Assign),
|
|
||||||
just('.').to(Token::Dot),
|
|
||||||
just(',').to(Token::Comma),
|
|
||||||
just(':').to(Token::Colon),
|
|
||||||
just(';').to(Token::Semicolon),
|
|
||||||
just('@').to(Token::At),
|
|
||||||
just('#').to(Token::Hash),
|
|
||||||
));
|
|
||||||
|
|
||||||
let delim = choice((
|
|
||||||
just('(').to(Token::Open(Delimiter::Paren)),
|
|
||||||
just(')').to(Token::Close(Delimiter::Paren)),
|
|
||||||
just('[').to(Token::Open(Delimiter::Bracket)),
|
|
||||||
just(']').to(Token::Close(Delimiter::Bracket)),
|
|
||||||
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::Boolean(true),
|
|
||||||
"false" => Token::Boolean(false),
|
|
||||||
|
|
||||||
"fun" => Token::KwFun,
|
|
||||||
"set" => Token::KwSet,
|
|
||||||
"do" => Token::KwDo,
|
|
||||||
"end" => Token::KwEnd,
|
|
||||||
"if" => Token::KwIf,
|
|
||||||
"case" => Token::KwCase,
|
|
||||||
"of" => Token::KwOf,
|
|
||||||
"return" => Token::KwReturn,
|
|
||||||
_ => Token::Identifier(s),
|
|
||||||
});
|
|
||||||
|
|
||||||
let token = int
|
|
||||||
.or(string)
|
|
||||||
.or(symbol)
|
|
||||||
.or(delim)
|
|
||||||
.or(keyword)
|
|
||||||
.recover_with(skip_then_retry_until([]));
|
|
||||||
|
|
||||||
// let comment = just("--").then(take_until(just('\n'))).padded();
|
|
||||||
let comment = just('-')
|
|
||||||
.then_ignore(just('{')
|
|
||||||
.ignore_then(none_of('}').ignored().repeated())
|
|
||||||
.then_ignore(just("}-"))
|
|
||||||
.or(just('-').ignore_then(none_of('\n').ignored().repeated()))
|
|
||||||
)
|
|
||||||
.padded()
|
|
||||||
.ignored()
|
|
||||||
.repeated();
|
|
||||||
|
|
||||||
token
|
|
||||||
.padded_by(comment)
|
|
||||||
.map_with_span(|token, span| (token, span))
|
|
||||||
.padded()
|
|
||||||
.repeated()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
pub fn lex(src: String) -> (Option<Vec<(Token, std::ops::Range<usize>)>>, Vec<Simple<char>>) {
|
|
||||||
let (tokens, lex_error) = lexer().parse_recovery(src.as_str());
|
|
||||||
(tokens, lex_error)
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
#![feature(trait_alias)]
|
|
||||||
|
|
||||||
pub mod lex;
|
|
||||||
pub mod parse;
|
|
||||||
pub mod ast;
|
|
|
@ -1,3 +0,0 @@
|
||||||
pub mod lex;
|
|
||||||
pub mod parse;
|
|
||||||
pub mod ast;
|
|
|
@ -1,311 +0,0 @@
|
||||||
use super::{*, ast::*, lex::{Token, Delimiter}};
|
|
||||||
use chumsky::{prelude::*, Stream};
|
|
||||||
|
|
||||||
pub trait P<T> = chumsky::Parser<Token, T, Error = Simple<Token>> + Clone;
|
|
||||||
|
|
||||||
fn identifier() -> impl P<Spanned<String>> {
|
|
||||||
filter_map(|span, token| match token {
|
|
||||||
Token::Identifier(s) => Ok((s, span)),
|
|
||||||
_ => Err(Simple::expected_input_found(span, Vec::new(), Some(token))),
|
|
||||||
}).labelled("identifier")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn literal() -> impl P<Spanned<Literal>> {
|
|
||||||
filter_map(|span, token| match token {
|
|
||||||
Token::Int(i) => Ok((ast::Literal::Int(i), span)),
|
|
||||||
Token::Boolean(b) => Ok((ast::Literal::Boolean(b), span)),
|
|
||||||
Token::String(s) => Ok((ast::Literal::String(s), span)),
|
|
||||||
_ => Err(Simple::expected_input_found(span, Vec::new(), Some(token))),
|
|
||||||
}).labelled("literal")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn typehint_parser() -> impl P<Spanned<Typehint>> {
|
|
||||||
recursive(|ty| {
|
|
||||||
|
|
||||||
let single = filter_map(|span, token| match token {
|
|
||||||
Token::Identifier(s) => Ok((
|
|
||||||
match s.as_str() {
|
|
||||||
"any" => ast::Typehint::Builtin(ast::BuiltinType::Any),
|
|
||||||
"null" => ast::Typehint::Builtin(ast::BuiltinType::Null),
|
|
||||||
"undefined" => ast::Typehint::Builtin(ast::BuiltinType::Undefined),
|
|
||||||
"bool" => ast::Typehint::Builtin(ast::BuiltinType::Boolean),
|
|
||||||
"int" => ast::Typehint::Builtin(ast::BuiltinType::Int),
|
|
||||||
"string" => ast::Typehint::Builtin(ast::BuiltinType::String),
|
|
||||||
_ => Typehint::Single(s),
|
|
||||||
}, span)),
|
|
||||||
_ => Err(Simple::expected_input_found(span, Vec::new(), Some(token))),
|
|
||||||
});
|
|
||||||
|
|
||||||
let tuple = single
|
|
||||||
.separated_by(just(Token::Comma))
|
|
||||||
.allow_trailing()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Open(Delimiter::Paren)),
|
|
||||||
just(Token::Close(Delimiter::Paren)))
|
|
||||||
.map_with_span(|args, span| {( Typehint::Tuple(args), span )});
|
|
||||||
|
|
||||||
let vector = single
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Open(Delimiter::Bracket)),
|
|
||||||
just(Token::Close(Delimiter::Bracket)))
|
|
||||||
.map_with_span(|arg, span| {( Typehint::Vector(Box::new(arg)), span )});
|
|
||||||
|
|
||||||
let function = ty.clone()
|
|
||||||
.separated_by(just(Token::Comma))
|
|
||||||
.allow_trailing()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Or),
|
|
||||||
just(Token::Or))
|
|
||||||
.then_ignore(just(Token::Arrow))
|
|
||||||
.then(ty.clone())
|
|
||||||
.map_with_span(|(args, ret), span| {( Typehint::Function(args, Box::new(ret)), span )});
|
|
||||||
|
|
||||||
let union_ty = ty.clone()
|
|
||||||
.separated_by(just(Token::Or))
|
|
||||||
.allow_trailing()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Open(Delimiter::Paren)),
|
|
||||||
just(Token::Close(Delimiter::Paren)))
|
|
||||||
.map_with_span(|args, span| {( Typehint::Union(args), span )});
|
|
||||||
|
|
||||||
single
|
|
||||||
.or(tuple)
|
|
||||||
.or(vector)
|
|
||||||
.or(function)
|
|
||||||
.or(union_ty)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expr_parser() -> impl P<Spanned<Expr>> {
|
|
||||||
recursive(|expr| {
|
|
||||||
|
|
||||||
// Atom ::= Literal
|
|
||||||
// | Identifier
|
|
||||||
// | Vector
|
|
||||||
// | Tuple
|
|
||||||
// | Object
|
|
||||||
|
|
||||||
let args = expr.clone().separated_by(just(Token::Comma)).allow_trailing();
|
|
||||||
|
|
||||||
let vec = args.clone().delimited_by(
|
|
||||||
just(Token::Open(Delimiter::Bracket)),
|
|
||||||
just(Token::Close(Delimiter::Bracket)))
|
|
||||||
.map_with_span(|args, span| {( Expr::Vector(args), span )});
|
|
||||||
|
|
||||||
let tuple = args.clone().delimited_by(
|
|
||||||
just(Token::Open(Delimiter::Paren)),
|
|
||||||
just(Token::Close(Delimiter::Paren)))
|
|
||||||
.map_with_span(|args, span| {( Expr::Tuple(args), span )});
|
|
||||||
|
|
||||||
let object = identifier()
|
|
||||||
.then_ignore(just(Token::Colon))
|
|
||||||
.then(expr.clone())
|
|
||||||
.separated_by(just(Token::Comma))
|
|
||||||
.allow_trailing()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Open(Delimiter::Brace)),
|
|
||||||
just(Token::Close(Delimiter::Brace)))
|
|
||||||
.map_with_span(|args, span| {( Expr::Object { fields: args }, span )});
|
|
||||||
|
|
||||||
let atom = literal().map_with_span(|literal, span| {( Expr::Literal(literal), span )})
|
|
||||||
.or(identifier().map_with_span(|ident, span| {( Expr::Identifier(ident), span )}))
|
|
||||||
.or(vec)
|
|
||||||
.or(tuple)
|
|
||||||
.or(object)
|
|
||||||
.labelled("atom");
|
|
||||||
|
|
||||||
// Call ::= Identifier '(' ( Expr ( ',' Expr )* )? ')'
|
|
||||||
// Method ::= Identifier '.' Identifier ( '(' ( Expr ( ',' Expr )* )? ')' )
|
|
||||||
// Access ::= Identifier '.' Idnetifier
|
|
||||||
// Intrinsic ::= '@' Call
|
|
||||||
// Unary ::= UnaryOp ( Call | Intrinsic )
|
|
||||||
// Binary ::= Unary BinaryOp Unary
|
|
||||||
|
|
||||||
let identexpr = identifier().map_with_span(|ident, span| {( Expr::Identifier(ident), span )});
|
|
||||||
|
|
||||||
let call = atom.clone()
|
|
||||||
.then(
|
|
||||||
args.clone().delimited_by(
|
|
||||||
just(Token::Open(Delimiter::Paren)),
|
|
||||||
just(Token::Close(Delimiter::Paren)))
|
|
||||||
.repeated())
|
|
||||||
.foldl(|name, args| {( Expr::Call { name: Box::new(name.clone()), args }, name.1 )}).labelled("call");
|
|
||||||
|
|
||||||
let intrinsic = just(Token::At).ignore_then(atom.clone())
|
|
||||||
.then(
|
|
||||||
args.clone().delimited_by(
|
|
||||||
just(Token::Open(Delimiter::Paren)),
|
|
||||||
just(Token::Close(Delimiter::Paren)))
|
|
||||||
.repeated())
|
|
||||||
.foldl(|name, args| {( Expr::Intrinsic { name: Box::new(name.clone()), args }, name.1 )}).labelled("intrinsic");
|
|
||||||
|
|
||||||
let method = just(Token::Hash)
|
|
||||||
.ignore_then(identexpr.clone())
|
|
||||||
.then_ignore(just(Token::Dot))
|
|
||||||
.then(atom.clone())
|
|
||||||
.then(
|
|
||||||
args.clone().delimited_by(
|
|
||||||
just(Token::Open(Delimiter::Paren)),
|
|
||||||
just(Token::Close(Delimiter::Paren)))
|
|
||||||
.repeated())
|
|
||||||
.map_with_span(|((name, method), args), span| {( Expr::Method {
|
|
||||||
obj: Box::new(name),
|
|
||||||
name: Box::new(method),
|
|
||||||
args: args.into_iter().flatten().collect()
|
|
||||||
}, span )}).labelled("method");
|
|
||||||
|
|
||||||
let access = just(Token::Colon)
|
|
||||||
.ignore_then(identexpr)
|
|
||||||
.then_ignore(just(Token::Dot))
|
|
||||||
.then(atom.clone())
|
|
||||||
.map_with_span(|(obj, name), span| {( Expr::Access { obj: Box::new(obj), name: Box::new(name) }, span )}).labelled("access");
|
|
||||||
|
|
||||||
let unary = choice((
|
|
||||||
just(Token::Minus).to(UnaryOp::Minus),
|
|
||||||
just(Token::Not).to(UnaryOp::Not)))
|
|
||||||
.repeated()
|
|
||||||
.then(call.or(intrinsic).or(method).or(access))
|
|
||||||
.foldr(|op, rhs| {( Expr::Unary { op, rhs: Box::new(rhs.clone()) }, rhs.1 )});
|
|
||||||
|
|
||||||
let factor = unary.clone().then(
|
|
||||||
choice((
|
|
||||||
just(Token::Multiply).to(BinaryOp::Multiply),
|
|
||||||
just(Token::Divide).to(BinaryOp::Divide),
|
|
||||||
just(Token::Modulus).to(BinaryOp::Modulus)))
|
|
||||||
.then(unary)
|
|
||||||
.repeated())
|
|
||||||
.foldl(|lhs, (op, rhs)| {(
|
|
||||||
Expr::Binary {
|
|
||||||
lhs: Box::new(lhs), op, rhs: Box::new(rhs.clone()),
|
|
||||||
}, rhs.1)});
|
|
||||||
|
|
||||||
let term = factor.clone().then(
|
|
||||||
choice((
|
|
||||||
just(Token::Plus).to(BinaryOp::Plus),
|
|
||||||
just(Token::Minus).to(BinaryOp::Minus)))
|
|
||||||
.then(factor)
|
|
||||||
.repeated())
|
|
||||||
.foldl(|lhs, (op, rhs)| {(
|
|
||||||
Expr::Binary {
|
|
||||||
lhs: Box::new(lhs), op, rhs: Box::new(rhs.clone()),
|
|
||||||
}, rhs.1)});
|
|
||||||
|
|
||||||
let compare = term.clone().then(
|
|
||||||
choice((
|
|
||||||
just(Token::Equal).to(BinaryOp::Equal),
|
|
||||||
just(Token::NotEqual).to(BinaryOp::NotEqual),
|
|
||||||
just(Token::Less).to(BinaryOp::Less),
|
|
||||||
just(Token::Greater).to(BinaryOp::Greater)))
|
|
||||||
.then(term)
|
|
||||||
.repeated())
|
|
||||||
.foldl(|lhs, (op, rhs)| {(
|
|
||||||
Expr::Binary {
|
|
||||||
lhs: Box::new(lhs), op, rhs: Box::new(rhs.clone()),
|
|
||||||
}, rhs.1)});
|
|
||||||
|
|
||||||
// Do ::= 'do' Expr* 'end'
|
|
||||||
// Define ::= Identifier ':' Typehint '=' Expr
|
|
||||||
// Redefine ::= 'set' Identifier '=' Expr
|
|
||||||
// Function ::= 'fun' Identifier ( Identifier* ) '(' ( Identifier ':' Typehint ( ',' Identifier ':' Typehint )* )? ')' ':' Typehint '=' Expr
|
|
||||||
// If ::= 'if' Expr '|' Expr '|' Expr
|
|
||||||
// Return ::= 'return' Expr
|
|
||||||
// Note: This section's `Expr` might actually mean `Expr | Do`
|
|
||||||
|
|
||||||
let do_block = expr.clone().repeated()
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::KwDo),
|
|
||||||
just(Token::KwEnd))
|
|
||||||
.map_with_span(|body, span| {( Expr::Do {body: (body, span.clone())}, span )});
|
|
||||||
|
|
||||||
let define = identifier()
|
|
||||||
// Type hint
|
|
||||||
.then(just(Token::Colon).ignore_then(typehint_parser()))
|
|
||||||
// Body
|
|
||||||
.then(just(Token::Assign).ignore_then(do_block.clone().or(expr.clone())))
|
|
||||||
.map_with_span(|((ident, typehint), expr), span| {
|
|
||||||
(Expr::Define { name: *Box::new(ident), typehint, value: Box::new(expr) }, span)
|
|
||||||
});
|
|
||||||
|
|
||||||
let redefine = just(Token::KwSet)
|
|
||||||
.ignore_then(identifier())
|
|
||||||
// Body
|
|
||||||
.then(just(Token::Assign).ignore_then(do_block.clone().or(expr.clone())))
|
|
||||||
.map_with_span(|(ident, expr), span| {
|
|
||||||
(Expr::Redefine { name: *Box::new(ident), value: Box::new(expr) }, span)
|
|
||||||
});
|
|
||||||
|
|
||||||
let function = identifier()
|
|
||||||
// Generic
|
|
||||||
.then(identifier().repeated())
|
|
||||||
// Arguments
|
|
||||||
.then(
|
|
||||||
identifier()
|
|
||||||
.then_ignore(just(Token::Colon))
|
|
||||||
.then(typehint_parser())
|
|
||||||
.delimited_by(
|
|
||||||
just(Token::Open(Delimiter::Paren)),
|
|
||||||
just(Token::Close(Delimiter::Paren)))
|
|
||||||
.repeated())
|
|
||||||
// Return type hint
|
|
||||||
.then_ignore(just(Token::Colon))
|
|
||||||
.then(typehint_parser())
|
|
||||||
// Body
|
|
||||||
.then_ignore(just(Token::Assign))
|
|
||||||
.then(do_block.clone().or(expr.clone()))
|
|
||||||
.map(|((((name, generics), args), typehint), body)| {
|
|
||||||
( Expr::Function {
|
|
||||||
name: *Box::new(name),
|
|
||||||
generics,
|
|
||||||
args: args.into_iter().map(|(name, typehint)| {
|
|
||||||
(name, *Box::new(typehint))
|
|
||||||
}).collect(),
|
|
||||||
typehint,
|
|
||||||
body: Box::new(body.clone()) }, body.1 )});
|
|
||||||
|
|
||||||
let if_else = just(Token::KwIf)
|
|
||||||
// Condition
|
|
||||||
.ignore_then(expr.clone())
|
|
||||||
// True branch
|
|
||||||
.then_ignore(just(Token::Or))
|
|
||||||
.then(do_block.clone().or(expr.clone()))
|
|
||||||
// False branch
|
|
||||||
.then_ignore(just(Token::Or))
|
|
||||||
.then(do_block.clone().or(expr.clone()))
|
|
||||||
.map_with_span(|((cond, then), else_), span| {
|
|
||||||
(Expr::If { cond: Box::new(cond), t: Box::new(then), f: Box::new(else_) }, span)
|
|
||||||
});
|
|
||||||
|
|
||||||
let return_ = just(Token::KwReturn)
|
|
||||||
.ignore_then(expr.clone())
|
|
||||||
.map_with_span(|expr, span| {( Expr::Return(Box::new(expr)), span )});
|
|
||||||
|
|
||||||
// Expr ::= Define
|
|
||||||
// | Redefine
|
|
||||||
// | Function
|
|
||||||
// | Do
|
|
||||||
// | Return
|
|
||||||
// | If
|
|
||||||
// | Binary
|
|
||||||
|
|
||||||
define
|
|
||||||
.or(redefine)
|
|
||||||
.or(function)
|
|
||||||
.or(do_block)
|
|
||||||
.or(return_)
|
|
||||||
.or(if_else)
|
|
||||||
.or(compare)
|
|
||||||
}).labelled("expression")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
pub fn parse(tokens: Vec<(Token, std::ops::Range<usize>)>, len: usize) -> (Option<Vec<(Expr, std::ops::Range<usize>)>>, Vec<Simple<Token>>) {
|
|
||||||
let (ast, parse_error) = expr_parser()
|
|
||||||
.repeated()
|
|
||||||
.then_ignore(end())
|
|
||||||
.parse_recovery(Stream::from_iter(
|
|
||||||
len..len + 1,
|
|
||||||
tokens.into_iter(),
|
|
||||||
));
|
|
||||||
|
|
||||||
(ast, parse_error)
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "transformer"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
syntax = { path = "../syntax" }
|
|
Loading…
Reference in a new issue