Implement js expression emission, tehe
This commit is contained in:
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user