From 492e22abf2b82e7278fca6aa557199327daf830b Mon Sep 17 00:00:00 2001 From: azur Date: Sat, 4 Mar 2023 22:34:12 +0700 Subject: [PATCH] init --- .gitignore | 10 +++ Cargo.lock | 131 ++++++++++++++++++++++++++++ Cargo.toml | 7 ++ a.hlm | 43 ++++++++++ b.hlm | 5 ++ rust-toolchain.toml | 2 + src/main.rs | 55 ++++++++++++ src/parse/mod.rs | 2 + src/parse/parse.rs | 204 ++++++++++++++++++++++++++++++++++++++++++++ src/parse/past.rs | 40 +++++++++ src/trans/ast.rs | 67 +++++++++++++++ src/trans/js.rs | 72 ++++++++++++++++ src/trans/low.rs | 129 ++++++++++++++++++++++++++++ src/trans/mod.rs | 4 + src/trans/ty.rs | 29 +++++++ 15 files changed, 800 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 a.hlm create mode 100644 b.hlm create mode 100644 rust-toolchain.toml create mode 100644 src/main.rs create mode 100644 src/parse/mod.rs create mode 100644 src/parse/parse.rs create mode 100644 src/parse/past.rs create mode 100644 src/trans/ast.rs create mode 100644 src/trans/js.rs create mode 100644 src/trans/low.rs create mode 100644 src/trans/mod.rs create mode 100644 src/trans/ty.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..88345f8 --- /dev/null +++ b/.gitignore @@ -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 \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2831ff0 --- /dev/null +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..8ed5624 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "renxi" +version = "0.1.0" +edition = "2021" + +[dependencies] +chumsky = "0.9.0" diff --git a/a.hlm b/a.hlm new file mode 100644 index 0000000..899c35b --- /dev/null +++ b/a.hlm @@ -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; +} \ No newline at end of file diff --git a/b.hlm b/b.hlm new file mode 100644 index 0000000..2142a2b --- /dev/null +++ b/b.hlm @@ -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 \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..271800c --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..12308f2 --- /dev/null +++ b/src/main.rs @@ -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::>(); + + // for expr in &nexprs { + // println!("{}", expr); + // } + + // println!("──────────────────────────────────────────────────"); + + // let jsexprs = nexprs.into_iter().map(translate_js).collect::>(); + + // for expr in &jsexprs { + // println!("{}", expr); + // } +} diff --git a/src/parse/mod.rs b/src/parse/mod.rs new file mode 100644 index 0000000..0c737d8 --- /dev/null +++ b/src/parse/mod.rs @@ -0,0 +1,2 @@ +pub mod parse; +pub mod past; \ No newline at end of file diff --git a/src/parse/parse.rs b/src/parse/parse.rs new file mode 100644 index 0000000..4210668 --- /dev/null +++ b/src/parse/parse.rs @@ -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; +pub type Spanned = (T, Span); + +pub fn lexer() -> impl Parser, Error = Simple> { + 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::() + .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>) { + let (tokens, lex_error) = lexer().parse_recovery(src.as_str()); + (tokens, lex_error) +} + +pub trait P = chumsky::Parser> + Clone; + +pub fn literal_parser() -> impl P { + 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 { + 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 + 'a, + delim: Delim, + f: impl Fn(Span) -> T + Clone + 'a, +) -> impl P + '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() +} diff --git a/src/parse/past.rs b/src/parse/past.rs new file mode 100644 index 0000000..4c02d9c --- /dev/null +++ b/src/parse/past.rs @@ -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), + + UnaryOp(PUnaryOp, Box), + BinaryOp(PBinaryOp, Box, Box), + + Call(Box, Vec), + Lambda { + args: Vec<(String, Type)>, + body: Box, + }, + Let { + vars: Vec<(String, Type, Self)>, + body: Box, + } +} \ No newline at end of file diff --git a/src/trans/ast.rs b/src/trans/ast.rs new file mode 100644 index 0000000..f3485bc --- /dev/null +++ b/src/trans/ast.rs @@ -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), + BinaryOp(BinaryOp, Box, Box), + + Call(Box, Vec), + Lambda { + args: Vec<(String, Type)>, + body: Box, + }, +} + +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) + }, + } + } +} \ No newline at end of file diff --git a/src/trans/js.rs b/src/trans/js.rs new file mode 100644 index 0000000..06c654f --- /dev/null +++ b/src/trans/js.rs @@ -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, Option>), + + Call(Box, Vec), + Method(Box, String, Vec), + Lambda { + args: Vec<(String, Type)>, + body: Box, + }, +} + +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) + }, + } + } +} \ No newline at end of file diff --git a/src/trans/low.rs b/src/trans/low.rs new file mode 100644 index 0000000..224b591 --- /dev/null +++ b/src/trans/low.rs @@ -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)), + }, + } +} \ No newline at end of file diff --git a/src/trans/mod.rs b/src/trans/mod.rs new file mode 100644 index 0000000..e0d5a1b --- /dev/null +++ b/src/trans/mod.rs @@ -0,0 +1,4 @@ +pub mod ty; +pub mod ast; +pub mod js; +pub mod low; \ No newline at end of file diff --git a/src/trans/ty.rs b/src/trans/ty.rs new file mode 100644 index 0000000..74988da --- /dev/null +++ b/src/trans/ty.rs @@ -0,0 +1,29 @@ +use std::fmt::{Display, Formatter, Result as FmtResult}; + +#[derive(Clone, Debug)] +pub enum Type { + Num, Str, Bool, + Fun(Vec, Box), + 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"), + } + } +} \ No newline at end of file