use crate::ast::*;
use crate::generator::{Generator, GeneratorResult};
use std::collections::HashMap;
use types::Type;
pub struct JsGenerator;
impl Generator for JsGenerator {
fn generate(prog: Module) -> GeneratorResult<String> {
let mut code = String::new();
let raw_builtins =
crate::Builtins::get("builtin.js").expect("Could not locate builtin functions");
code += std::str::from_utf8(raw_builtins.as_ref())
.expect("Unable to interpret builtin functions");
let structs: String = prog
.structs
.clone()
.into_iter()
.map(generate_struct_definition)
.collect();
code += &structs;
let funcs: String = prog.func.into_iter().map(generate_function).collect();
code += &funcs;
code += "main();";
Ok(code)
}
}
fn generate_arguments(args: Vec<Variable>) -> String {
args.into_iter()
.map(|var| var.name)
.collect::<Vec<String>>()
.join(", ")
}
fn generate_function(func: Function) -> String {
let arguments: String = generate_arguments(func.arguments);
let mut raw = format!("function {N}({A})", N = func.name, A = arguments);
raw += &generate_block(func.body, None);
raw += "\n";
raw
}
fn generate_method(subject: String, func: Function) -> String {
let mut buf = format!(
"{}.prototype.{} = function({})",
subject,
func.name,
generate_arguments(func.arguments)
);
buf += &generate_block(func.body, None);
buf += "\n";
buf
}
fn generate_struct_definition(struct_def: StructDef) -> String {
let mut buf = format!("function {}(args) {{\n", &struct_def.name);
for field in &struct_def.fields {
buf += &format!("this.{N} = args.{N};\n", N = field.name);
}
buf += "}\n";
for method in &struct_def.methods {
buf += &generate_method(struct_def.name.clone(), method.to_owned());
}
buf
}
fn generate_block(block: Statement, prepend: Option<String>) -> String {
let mut generated = String::from("{\n");
if let Some(pre) = prepend {
generated += ⪯
}
let statements = match block {
Statement::Block {
statements,
scope: _,
} => statements,
_ => panic!("Block body should be of type Statement::Block"),
};
for statement in statements {
generated += &generate_statement(statement);
}
generated += "}\n";
generated
}
fn generate_statement(statement: Statement) -> String {
let state = match statement {
Statement::Return(ret) => generate_return(ret),
Statement::Declare { variable, value } => generate_declare(variable, value),
Statement::Exp(val) => generate_expression(val),
Statement::If {
condition,
body,
else_branch,
} => generate_conditional(condition, *body, else_branch.map(|x| *x)),
Statement::Assign { lhs, rhs } => generate_assign(*lhs, *rhs),
Statement::Block {
statements: _,
scope: _,
} => generate_block(statement, None),
Statement::While { condition, body } => generate_while_loop(condition, *body),
Statement::For { ident, expr, body } => generate_for_loop(ident, expr, *body),
Statement::Continue => generate_continue(),
Statement::Break => generate_break(),
Statement::Match { subject, arms } => generate_match(subject, arms),
};
format!("{};\n", state)
}
fn generate_expression(expr: Expression) -> String {
match expr {
Expression::Int(val) => val.to_string(),
Expression::Selff => "this".to_string(),
Expression::Str(val) => super::string_syntax(val),
Expression::Variable(val) => val,
Expression::Bool(b) => b.to_string(),
Expression::FunctionCall { fn_name, args } => generate_function_call(fn_name, args),
Expression::Array {
capacity: _,
elements,
} => generate_array(elements),
Expression::ArrayAccess { name, index } => generate_array_access(name, *index),
Expression::BinOp { lhs, op, rhs } => generate_bin_op(*lhs, op, *rhs),
Expression::StructInitialization { name, fields } => {
generate_struct_initialization(name, fields)
}
Expression::FieldAccess { expr, field } => generate_field_access(*expr, *field),
}
}
fn generate_while_loop(expr: Expression, body: Statement) -> String {
let mut out_str = String::from("while (");
out_str += &generate_expression(expr);
out_str += ") ";
out_str += &generate_block(body, None);
out_str
}
fn generate_for_loop(ident: Variable, expr: Expression, body: Statement) -> String {
let mut expr_ident = ident.clone();
expr_ident.name = format!("loop_orig_{}", ident.name);
let mut out_str = format!("{};\n", generate_declare(&expr_ident, Some(expr)));
out_str += &format!(
"for (let iter_{I} = 0; iter_{I} < {E}.length; iter_{I}++)",
I = ident.name,
E = expr_ident.name
);
out_str += &generate_block(
body,
Some(format!(
"let {I} = {E}[iter_{I}];\n",
I = ident.name,
E = expr_ident.name,
)),
);
out_str
}
fn generate_break() -> String {
"break;\n".into()
}
fn generate_continue() -> String {
"continue;\n".into()
}
fn generate_match(subject: Expression, arms: Vec<MatchArm>) -> String {
let mut out_str = format!("switch ({E}) {{\n", E = generate_expression(subject));
for arm in arms {
match arm {
MatchArm::Case(expr, statement) => {
out_str += &format!("case {}:\n", generate_expression(expr));
out_str += &format!("{}\n", &generate_statement(statement));
out_str += "break;";
}
MatchArm::Else(statement) => {
out_str += "default:\n";
out_str += &format!("{}\n", &generate_statement(statement));
}
}
}
out_str += "}";
out_str
}
fn generate_array(elements: Vec<Expression>) -> String {
let mut out_str = String::from("[");
out_str += &elements
.iter()
.map(|el| generate_expression(el.clone()))
.collect::<Vec<String>>()
.join(", ");
out_str += "]";
out_str
}
fn generate_array_access(name: String, expr: Expression) -> String {
format!("{n}[{e}]", n = name, e = generate_expression(expr))
}
fn generate_conditional(
expr: Expression,
if_state: Statement,
else_state: Option<Statement>,
) -> String {
let expr_str = generate_expression(expr);
let body = match if_state {
Statement::Block {
statements,
scope: _,
} => statements,
_ => panic!("Conditional body should be of type block"),
};
let mut outcome = format!("if ({})", expr_str);
outcome += "{\n";
for statement in body {
outcome += &generate_statement(statement);
}
outcome += "}";
if let Some(else_state) = else_state {
outcome += "else ";
outcome += &generate_statement(else_state);
}
outcome
}
fn generate_declare<V: AsRef<Variable>>(identifier: V, val: Option<Expression>) -> String {
let ident = identifier.as_ref();
match val {
Some(expr) => format!("var {} = {}", ident.name, generate_expression(expr)),
None => match ident.ty {
Some(Type::Array(_, _)) => format!("var {} = []", ident.name),
_ => format!("var {}", ident.name),
},
}
}
fn generate_function_call(func: String, args: Vec<Expression>) -> String {
let formatted_args = args
.into_iter()
.map(|arg| match arg {
Expression::Int(i) => i.to_string(),
Expression::Bool(v) => v.to_string(),
Expression::Selff => "this".to_string(),
Expression::ArrayAccess { name, index } => generate_array_access(name, *index),
Expression::FunctionCall { fn_name, args } => generate_function_call(fn_name, args),
Expression::Str(s) => super::string_syntax(s),
Expression::Variable(s) => s,
Expression::Array {
capacity: _,
elements,
} => generate_array(elements),
Expression::BinOp { lhs, op, rhs } => generate_bin_op(*lhs, op, *rhs),
Expression::StructInitialization { name, fields } => {
generate_struct_initialization(name, fields)
}
Expression::FieldAccess { expr, field } => generate_field_access(*expr, *field),
})
.collect::<Vec<String>>()
.join(",");
format!("{N}({A})", N = func, A = formatted_args)
}
fn generate_return(ret: Option<Expression>) -> String {
match ret {
Some(expr) => format!("return {}", generate_expression(expr)),
None => "return".to_string(),
}
}
fn generate_bin_op(left: Expression, op: BinOp, right: Expression) -> String {
let op_str = match op {
BinOp::Addition => "+",
BinOp::And => "&&",
BinOp::Division => "/",
BinOp::Equal => "===",
BinOp::GreaterThan => ">",
BinOp::GreaterThanOrEqual => ">=",
BinOp::LessThan => "<",
BinOp::LessThanOrEqual => "<=",
BinOp::Modulus => "%",
BinOp::Multiplication => "*",
BinOp::NotEqual => "!==",
BinOp::Or => "||",
BinOp::Subtraction => "-",
BinOp::AddAssign => "+=",
BinOp::SubtractAssign => "-=",
BinOp::MultiplyAssign => "*=",
BinOp::DivideAssign => "/=",
};
format!(
"{l} {op} {r}",
l = generate_expression(left),
op = op_str,
r = generate_expression(right)
)
}
fn generate_struct_initialization(
name: String,
fields: HashMap<String, Box<Expression>>,
) -> String {
let mut out_str = format!("new {}({{", name);
for (key, value) in fields {
out_str += &format!("{}: {},", key, generate_expression(*value));
}
out_str += "})";
out_str
}
fn generate_field_access(expr: Expression, field: Expression) -> String {
format!(
"{}.{}",
generate_expression(expr),
generate_expression(field)
)
}
fn generate_assign(name: Expression, expr: Expression) -> String {
format!(
"{} = {}",
generate_expression(name),
generate_expression(expr)
)
}