Implement js expression emission, tehe

This commit is contained in:
Eugene
2023-12-17 22:51:04 +02:00
parent 626b7f2f2b
commit c91f9ddd6c
2 changed files with 319 additions and 14 deletions
+318
View File
@@ -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<W: Write> {
fn emit(&self, w: &mut W) -> Result<(), EmitError>;
}
pub enum Statement {
Import,
Export,
Declaration,
Expression,
For,
If,
While,
Throw
}
impl<W: Write> Emit<W> 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<Expression>),
Object(Vec<(String, Expression)>),
Unary(String, Box<Expression>),
Binary(String, Box<Expression>, Box<Expression>),
Ternary(Box<Expression>, Box<Expression>, Box<Expression>),
Lambda {
args: Vec<String>,
body: Vec<Statement>,
is_async: bool,
},
}
impl<W: Write> Emit<W> 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 () => {}")
}
+1 -14
View File
@@ -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;