mirror of
https://github.com/azur1s/bobbylisp.git
synced 2024-10-16 02:37:40 -05:00
init
This commit is contained in:
commit
492e22abf2
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
/target
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
# Generated by the compiler
|
||||
/*.js
|
131
Cargo.lock
generated
Normal file
131
Cargo.lock
generated
Normal file
|
@ -0,0 +1,131 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
|
||||
[[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.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4d619fba796986dd538d82660b76e0b9756c6e19b2e4d4559ba5a57f9f00810"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
"stacker",
|
||||
]
|
||||
|
||||
[[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 = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.139"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||
|
||||
[[package]]
|
||||
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 = [
|
||||
"chumsky",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stacker"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"psm",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
|
@ -0,0 +1,7 @@
|
|||
[package]
|
||||
name = "renxi"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
chumsky = "0.9.0"
|
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
|
2
rust-toolchain.toml
Normal file
2
rust-toolchain.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[toolchain]
|
||||
channel = "nightly"
|
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"),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue