pull/5/head
azur 2023-03-04 22:34:12 +07:00
commit 492e22abf2
15 changed files with 800 additions and 0 deletions

10
.gitignore vendored Normal file
View 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
View 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
View File

@ -0,0 +1,7 @@
[package]
name = "renxi"
version = "0.1.0"
edition = "2021"
[dependencies]
chumsky = "0.9.0"

43
a.hlm Normal file
View 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
View 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
View File

@ -0,0 +1,2 @@
[toolchain]
channel = "nightly"

55
src/main.rs Normal file
View 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
View File

@ -0,0 +1,2 @@
pub mod parse;
pub mod past;

204
src/parse/parse.rs Normal file
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,4 @@
pub mod ty;
pub mod ast;
pub mod js;
pub mod low;

29
src/trans/ty.rs Normal file
View 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"),
}
}
}