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]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
name = "ariadne"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
checksum = "f1cb2a2046bea8ce5e875551f5772024882de0b540c7f93dfc5d6cf1ca8b030c"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -28,13 +26,6 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "checker"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chumsky"
|
||||
version = "0.8.0"
|
||||
|
@ -45,28 +36,18 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "codegen"
|
||||
name = "compiler"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"lazy_static",
|
||||
"winapi",
|
||||
"parser",
|
||||
"vm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-random"
|
||||
version = "0.1.13"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f590d95d011aa80b063ffe3253422ed5aa462af4e9867d43ce8337562bac77c4"
|
||||
checksum = "368a7a772ead6ce7e1de82bfb04c485f3db8ec744f72925af5735e29a22cc18e"
|
||||
dependencies = [
|
||||
"const-random-macro",
|
||||
"proc-macro-hack",
|
||||
|
@ -74,12 +55,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "const-random-macro"
|
||||
version = "0.1.13"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "615f6e27d000a2bffbc7f2f6a8669179378fa27ee4d0a509e985dfc0a7defb40"
|
||||
checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"lazy_static",
|
||||
"once_cell",
|
||||
"proc-macro-hack",
|
||||
"tiny-keccak",
|
||||
]
|
||||
|
@ -91,10 +72,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.6"
|
||||
name = "entry"
|
||||
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"
|
||||
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
|
@ -102,49 +98,31 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
name = "libc"
|
||||
version = "0.2.138"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||
|
||||
[[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"
|
||||
dependencies = [
|
||||
"checker",
|
||||
"codegen",
|
||||
"colored",
|
||||
"syntax",
|
||||
"ariadne",
|
||||
"chumsky",
|
||||
]
|
||||
|
||||
[[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]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
||||
|
||||
[[package]]
|
||||
name = "syntax"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chumsky",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
|
@ -155,36 +133,20 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "transformer"
|
||||
name = "vm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"syntax",
|
||||
"fnv",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
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"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -1,8 +1,7 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"core",
|
||||
"syntax",
|
||||
"checker",
|
||||
"transformer",
|
||||
"codegen",
|
||||
]
|
||||
"entry",
|
||||
"parser",
|
||||
"compiler",
|
||||
"vm",
|
||||
]
|
||||
|
|
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