From c91f9ddd6cbf833ad024b95d59236168da2ca71c Mon Sep 17 00:00:00 2001 From: Eugene Date: Sun, 17 Dec 2023 22:51:04 +0200 Subject: [PATCH] Implement js expression emission, tehe --- backend/src/codegen.rs | 318 +++++++++++++++++++++++++++++++++++++++++ backend/src/lib.rs | 15 +- 2 files changed, 319 insertions(+), 14 deletions(-) create mode 100644 backend/src/codegen.rs diff --git a/backend/src/codegen.rs b/backend/src/codegen.rs new file mode 100644 index 0000000..36d4111 --- /dev/null +++ b/backend/src/codegen.rs @@ -0,0 +1,318 @@ +// There are the following two main types: +// Expressions and Statements + +use std::fmt::Write; + +#[derive(Debug)] +pub enum EmitError { + WriterError(std::fmt::Error), +} + +pub trait Emit { + fn emit(&self, w: &mut W) -> Result<(), EmitError>; +} + +pub enum Statement { + Import, + Export, + Declaration, + Expression, + For, + If, + While, + Throw +} + +impl Emit for Statement { + fn emit(&self, w: &mut W) -> Result<(), EmitError> { + Ok(()) + } +} + +pub enum Expression { + Null, + Undefined, + Bool(bool), + Number(f64), + String(String), + Reference(String), + Array(Vec), + Object(Vec<(String, Expression)>), + Unary(String, Box), + Binary(String, Box, Box), + Ternary(Box, Box, Box), + Lambda { + args: Vec, + body: Vec, + is_async: bool, + }, +} + +impl Emit for Expression { + fn emit(&self, w: &mut W) -> Result<(), EmitError> { + match self { + Expression::Null => write!(w, "null").map_err(EmitError::WriterError), + Expression::Undefined => write!(w, "undefined").map_err(EmitError::WriterError), + Expression::Bool(x) => write!(w, "{}", x).map_err(EmitError::WriterError), + Expression::Number(x) => write!(w, "{}", x).map_err(EmitError::WriterError), + Expression::String(x) => write!(w, "{:?}", x).map_err(EmitError::WriterError), + Expression::Reference(x) => write!(w, "{}", x).map_err(EmitError::WriterError), + Expression::Array(xs) => { + write!(w, "[").map_err(EmitError::WriterError)?; + for (i, x) in xs.iter().enumerate() { + x.emit(w)?; + if i + 1 != xs.len() { + write!(w, ", ").map_err(EmitError::WriterError)?; + } + } + write!(w, "]").map_err(EmitError::WriterError) + } + Expression::Object(xs) => { + write!(w, "{}", "{").map_err(EmitError::WriterError)?; + for (i, (k, x)) in xs.iter().enumerate() { + Expression::String(k.clone()).emit(w)?; + write!(w, ": ").map_err(EmitError::WriterError)?; + x.emit(w)?; + if i + 1 != xs.len() { + write!(w, ", ").map_err(EmitError::WriterError)?; + } + } + write!(w, "{}", "}").map_err(EmitError::WriterError) + } + Expression::Unary(op, x) => match op.as_str() { + "--" | "++" => { + write!(w, "(").map_err(EmitError::WriterError)?; + x.emit(w)?; + write!(w, " {}", op).map_err(EmitError::WriterError)?; + write!(w, ")").map_err(EmitError::WriterError) + } + _ => { + write!(w, "(").map_err(EmitError::WriterError)?; + write!(w, "{} ", op).map_err(EmitError::WriterError)?; + x.emit(w)?; + write!(w, ")").map_err(EmitError::WriterError) + } + }, + Expression::Binary(op, left, right) => { + write!(w, "(").map_err(EmitError::WriterError)?; + left.emit(w)?; + write!(w, " {} ", op).map_err(EmitError::WriterError)?; + right.emit(w)?; + write!(w, ")").map_err(EmitError::WriterError) + } + Expression::Ternary(condition, then_, else_) => { + write!(w, "(").map_err(EmitError::WriterError)?; + condition.emit(w)?; + write!(w, " ? ").map_err(EmitError::WriterError)?; + then_.emit(w)?; + write!(w, " : ").map_err(EmitError::WriterError)?; + else_.emit(w)?; + write!(w, ")").map_err(EmitError::WriterError) + } + Expression::Lambda { + args, + body, + is_async, + } => { + if *is_async { + write!(w, "async ").map_err(EmitError::WriterError)?; + } + write!(w, "(").map_err(EmitError::WriterError)?; + for (i, arg) in args.iter().enumerate() { + write!(w, "{}", arg).map_err(EmitError::WriterError)?; + if i + 1 != args.len() { + write!(w, ", ").map_err(EmitError::WriterError)?; + } + } + write!(w, ") => {}", "{").map_err(EmitError::WriterError)?; + for (i, st) in body.iter().enumerate() { + st.emit(w)?; + if i + 1 != args.len() { + write!(w, "; ").map_err(EmitError::WriterError)?; + } + } + write!(w, "{}", "}").map_err(EmitError::WriterError) + } + } + } +} + +#[test] +fn null_emit() { + let null = Expression::Null; + let mut res = String::new(); + null.emit(&mut res).unwrap(); + assert_eq!(res, "null") +} + +#[test] +fn undefined_emit() { + let undefined = Expression::Undefined; + let mut res = String::new(); + undefined.emit(&mut res).unwrap(); + assert_eq!(res, "undefined") +} + +#[test] +fn true_emit() { + let true_ = Expression::Bool(true); + let mut res = String::new(); + true_.emit(&mut res).unwrap(); + assert_eq!(res, "true") +} + +#[test] +fn false_emit() { + let false_ = Expression::Bool(false); + let mut res = String::new(); + false_.emit(&mut res).unwrap(); + assert_eq!(res, "false") +} + +#[test] +fn number_emit() { + let one = Expression::Number(1.0); + let mut res = String::new(); + one.emit(&mut res).unwrap(); + assert_eq!(res, "1") +} + +#[test] +fn negative_number_emit() { + let maybe_one = Expression::Number(-1.5); + let mut res = String::new(); + maybe_one.emit(&mut res).unwrap(); + assert_eq!(res, "-1.5") +} + +#[test] +fn string_emit() { + let string = Expression::String("test".to_owned()); + let mut res = String::new(); + string.emit(&mut res).unwrap(); + assert_eq!(res, "\"test\"") +} + +#[test] +fn reference_emit() { + let string = Expression::Reference("x".to_owned()); + let mut res = String::new(); + string.emit(&mut res).unwrap(); + assert_eq!(res, "x") +} + +#[test] +fn empty_array_emit() { + let array = Expression::Array(Vec::new()); + let mut res = String::new(); + array.emit(&mut res).unwrap(); + assert_eq!(res, "[]") +} + +#[test] +fn one_elem_array_emit() { + let array = Expression::Array(vec![Expression::Null]); + let mut res = String::new(); + array.emit(&mut res).unwrap(); + assert_eq!(res, "[null]") +} + +#[test] +fn two_elems_array_emit() { + let array = Expression::Array(vec![Expression::Null, Expression::Undefined]); + let mut res = String::new(); + array.emit(&mut res).unwrap(); + assert_eq!(res, "[null, undefined]") +} + +#[test] +fn empty_object_emit() { + let object = Expression::Object(Vec::new()); + let mut res = String::new(); + object.emit(&mut res).unwrap(); + assert_eq!(res, "{}") +} + +#[test] +fn one_elem_object_emit() { + let object = Expression::Object(vec![("x".to_owned(), Expression::Null)]); + let mut res = String::new(); + object.emit(&mut res).unwrap(); + assert_eq!(res, "{\"x\": null}") +} + +#[test] +fn two_elems_object_emit() { + let object = Expression::Object(vec![ + ("x".to_owned(), Expression::Null), + ("y as z".to_owned(), Expression::Undefined), + ]); + let mut res = String::new(); + object.emit(&mut res).unwrap(); + assert_eq!(res, "{\"x\": null, \"y as z\": undefined}") +} + +#[test] +fn unary_plus_emit() { + let unary = Expression::Unary("+".to_owned(), Box::new(Expression::Null)); + let mut res = String::new(); + unary.emit(&mut res).unwrap(); + assert_eq!(res, "(+ null)") +} + +#[test] +fn unary_plus_plus_emit() { + let unary = Expression::Unary("++".to_owned(), Box::new(Expression::Null)); + let mut res = String::new(); + unary.emit(&mut res).unwrap(); + assert_eq!(res, "(null ++)") +} + +#[test] +fn binary_plus_emit() { + let binary = Expression::Binary( + "+".to_owned(), + Box::new(Expression::Null), + Box::new(Expression::Undefined), + ); + let mut res = String::new(); + binary.emit(&mut res).unwrap(); + assert_eq!(res, "(null + undefined)") +} + +#[test] +fn ternary_emit() { + let ternary = Expression::Ternary( + Box::new(Expression::Bool(true)), + Box::new(Expression::Null), + Box::new(Expression::Undefined), + ); + let mut res = String::new(); + ternary.emit(&mut res).unwrap(); + assert_eq!(res, "(true ? null : undefined)") +} + +#[test] +fn empty_lambda_emit() { + let lambda = Expression::Lambda { + args: Vec::new(), + body: Vec::new(), + is_async: false, + }; + let mut res = String::new(); + lambda.emit(&mut res).unwrap(); + assert_eq!(res, "() => {}") +} + + +#[test] +fn empty_async_lambda_emit() { + let lambda = Expression::Lambda { + args: Vec::new(), + body: Vec::new(), + is_async: true, + }; + let mut res = String::new(); + lambda.emit(&mut res).unwrap(); + assert_eq!(res, "async () => {}") +} diff --git a/backend/src/lib.rs b/backend/src/lib.rs index 7d12d9a..24ccbdd 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -1,14 +1 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +pub mod codegen;