#[macro_use]
extern crate log;
use resast::prelude::*;
use ress::{prelude::Comment, tokens::CommentKind};
use std::io::{Error as IoError, Write};
pub mod write_str;
pub struct Writer<T: Write> {
current_indent: usize,
at_top_level: bool,
in_for_init: bool,
new_line: String,
indent: String,
quote: Option<char>,
out: T,
}
pub struct Builder<T: Write> {
new_line: String,
quote: Option<char>,
indent: String,
p: ::std::marker::PhantomData<T>,
}
impl<T: Write> Builder<T> {
pub fn new() -> Self {
Self {
new_line: "\n".to_string(),
quote: None,
indent: " ".repeat(4),
p: ::std::marker::PhantomData,
}
}
pub fn new_line(&mut self, new_line: &str) -> &mut Self {
self.set_new_line(new_line);
self
}
pub fn set_new_line(&mut self, new_line: &str) {
self.new_line = new_line.to_string();
}
pub fn quote(&mut self, quote: char) -> &mut Self {
self.set_quote(quote);
self
}
pub fn set_quote(&mut self, quote: char) {
self.quote = Some(quote);
}
pub fn indent(&mut self, indent: &str) -> &mut Self {
self.set_indent(indent);
self
}
pub fn set_indent(&mut self, indent: &str) {
self.indent = indent.to_string();
}
pub fn build(&self, destination: T) -> Writer<T> {
Writer::create(
destination,
self.new_line.clone(),
self.quote.clone(),
self.indent.clone(),
)
}
}
type Res = Result<(), IoError>;
impl<T: Write> Writer<T> {
pub fn new(out: T) -> Self {
trace!("new");
Self::create(out, "\n".to_string(), None, " ".repeat(4))
}
pub fn create(out: T, new_line: String, quote: Option<char>, indent: String) -> Self {
Self {
current_indent: 0,
at_top_level: true,
in_for_init: false,
out,
new_line,
quote,
indent,
}
}
pub fn builder() -> Builder<T> {
Builder {
new_line: String::from("\n"),
quote: None,
indent: " ".repeat(4),
p: ::std::marker::PhantomData,
}
}
pub fn write_program(&mut self, program: &Program) -> Res {
let parts = match program {
Program::Script(ref parts) => parts,
Program::Mod(ref parts) => parts,
};
for ref part in parts {
self.write_part(part)?;
}
Ok(())
}
pub fn write_part(&mut self, part: &ProgramPart) -> Res {
trace!("write_part: {:#?}", part);
self.at_top_level = true;
self._write_part(part)?;
self.write_new_line()?;
Ok(())
}
fn _write_part(&mut self, part: &ProgramPart) -> Res {
trace!("_write_part");
self.write_leading_whitespace()?;
match part {
ProgramPart::Decl(decl) => self.write_decl(decl)?,
ProgramPart::Dir(dir) => self.write_directive(dir)?,
ProgramPart::Stmt(stmt) => self.write_stmt(stmt)?,
}
Ok(())
}
pub fn write_decl(&mut self, decl: &Decl) -> Res {
trace!("write_decl");
match decl {
Decl::Var(ref kind, ref decls) => self.write_variable_decls(kind, decls)?,
Decl::Class(ref class) => {
self.at_top_level = false;
self.write_class(class)?;
self.write_new_line()?;
}
Decl::Func(ref func) => {
self.at_top_level = false;
self.write_function(func)?;
self.write_new_line()?;
}
Decl::Export(ref exp) => self.write_export_decl(exp)?,
Decl::Import(ref imp) => self.write_import_decl(imp)?,
};
Ok(())
}
pub fn write_variable_decls(&mut self, kind: &VarKind, decls: &[VarDecl]) -> Res {
trace!("write_variable_decls");
self.write_variable_kind(kind)?;
let mut after_first = false;
for decl in decls {
if after_first {
self.write(", ")?;
} else {
after_first = true;
}
self.write_variable_decl(decl)?;
}
self.write_empty_stmt()?;
self.write_new_line()
}
pub fn write_class(&mut self, class: &Class) -> Res {
trace!("write_class");
self.write("class ")?;
if let Some(ref id) = class.id {
self.write_ident(id)?;
self.write(" ")?;
}
if let Some(ref ex) = class.super_class {
self.write("extends ")?;
self.write_expr(ex)?;
self.write(" ")?;
}
self.write_open_brace()?;
self.write_new_line()?;
for ref part in &class.body.0 {
self.write_new_line()?;
self.write_leading_whitespace()?;
self.write_property(part)?;
self.write_new_line()?;
}
self.write_close_brace()?;
Ok(())
}
pub fn write_export_decl(&mut self, exp: &ModExport) -> Res {
trace!("write_export_decl");
self.write("export ")?;
match exp {
ModExport::All(ref a) => self.write_all_export(a)?,
ModExport::Default(ref d) => self.write_default_export(d)?,
ModExport::Named(ref n) => self.write_named_export(n)?,
}
Ok(())
}
pub fn write_all_export(&mut self, exp: &Lit) -> Res {
trace!("write_all_export");
self.write("* from ")?;
self.write_literal(exp)?;
Ok(())
}
pub fn write_default_export(&mut self, exp: &DefaultExportDecl) -> Res {
trace!("write_default_export");
self.write("default ")?;
match exp {
DefaultExportDecl::Decl(ref d) => self.write_decl(d)?,
DefaultExportDecl::Expr(ref e) => self.write_expr(e)?,
}
Ok(())
}
pub fn write_named_export(&mut self, exp: &NamedExportDecl) -> Res {
trace!("write_named_export");
match exp {
NamedExportDecl::Decl(ref d) => self.write_decl(d)?,
NamedExportDecl::Specifier(ref s, ref from) => self.write_export_specifiers(s, from)?,
}
Ok(())
}
pub fn write_export_specifiers(
&mut self,
specifiers: &[ExportSpecifier],
from: &Option<Lit>,
) -> Res {
trace!("write_export_specifiers");
self.write("{")?;
let mut after_first = false;
for s in specifiers {
if after_first {
self.write(", ")?;
}
self.write_ident(&s.local)?;
self.write(" as ")?;
self.write(&s.exported.name)?;
after_first = true;
}
self.write("}")?;
if let Some(ref from) = from {
self.write(" from ")?;
self.write_literal(from)?;
}
Ok(())
}
pub fn write_import_decl(&mut self, imp: &ModImport) -> Res {
trace!("write_import_decl");
self.write("import ")?;
if imp.specifiers.len() == 0 {
self.write("{}")?;
}
let mut opened_brace = false;
let mut specifiers = imp.specifiers.iter();
if let Some(ref first) = specifiers.next() {
if let ImportSpecifier::Default(ref ident) = first {
self.write_ident(ident)?;
} else if let ImportSpecifier::Namespace(ref imp) = first {
self.write_namespace_import(imp)?;
} else {
self.write("{ ")?;
opened_brace = true;
self.write_import_specificer(first)?;
}
}
if !opened_brace {
if let Some(ref next) = specifiers.next() {
if let ImportSpecifier::Namespace(ref name) = next {
self.write(", ")?;
self.write_namespace_import(name)?;
} else {
self.write(", { ")?;
self.write_import_specificer(next)?;
opened_brace = true;
}
}
}
while let Some(ref s) = specifiers.next() {
self.write(", ")?;
self.write_import_specificer(s)?;
}
if opened_brace {
self.write(" }")?;
}
self.write(" from ")?;
self.write_literal(&imp.source)?;
self.write_empty_stmt()?;
Ok(())
}
pub fn write_import_specificer(&mut self, spec: &ImportSpecifier) -> Res {
trace!("write_import_specificer");
match spec {
ImportSpecifier::Default(ref i) => self.write_ident(i)?,
ImportSpecifier::Namespace(ref n) => self.write_namespace_import(n)?,
ImportSpecifier::Normal(ref n) => self.write_normal_import(&n.imported, &n.local)?,
}
Ok(())
}
pub fn write_namespace_import(&mut self, name: &Ident) -> Res {
trace!("write_namespace_import");
self.write("* as ")?;
self.write_ident(name)?;
Ok(())
}
pub fn write_normal_import(&mut self, name: &Ident, local: &Ident) -> Res {
trace!("write_normal_import");
self.write_ident(&name)?;
self.write(" as ")?;
self.write_ident(&local)?;
Ok(())
}
pub fn write_directive(&mut self, dir: &Dir) -> Res {
trace!("write_directive");
self.write_literal(&dir.expr)?;
self.write_empty_stmt()?;
self.write_new_line()?;
Ok(())
}
pub fn write_variable_decl(&mut self, decl: &VarDecl) -> Res {
trace!("write_variable_decl");
self.write_pattern(&decl.id)?;
if let Some(ref init) = decl.init {
self.write(" = ")?;
self.write_expr(init)?;
}
Ok(())
}
pub fn write_variable_kind(&mut self, kind: &VarKind) -> Res {
trace!("write_variable_kind");
let s = match kind {
VarKind::Const => "const ",
VarKind::Let => "let ",
VarKind::Var => "var ",
};
self.write(s)
}
pub fn write_stmt(&mut self, stmt: &Stmt) -> Res {
trace!("write_stmt");
let mut semi = true;
let mut new_line = true;
let cached_state = self.at_top_level;
match stmt {
Stmt::Empty => {
new_line = false;
}
Stmt::Debugger => self.write_debugger_stmt()?,
Stmt::Expr(ref stmt) => {
let wrap = match stmt {
Expr::Lit(_) | Expr::Obj(_) | Expr::Func(_) | Expr::Binary(_) => true,
_ => false,
};
if wrap {
self.write_wrapped_expr(stmt)?
} else {
self.write_expr(stmt)?
}
}
Stmt::Block(ref stmt) => {
self.at_top_level = false;
self.write_block_stmt(&stmt.0)?;
semi = false;
new_line = false;
self.at_top_level = cached_state;
}
Stmt::With(ref stmt) => {
self.write_with_stmt(stmt)?;
semi = false;
}
Stmt::Return(ref stmt) => self.write_return_stmt(stmt)?,
Stmt::Labeled(ref stmt) => {
self.write_labeled_stmt(stmt)?;
semi = false;
}
Stmt::Break(ref stmt) => self.write_break_stmt(stmt)?,
Stmt::Continue(ref stmt) => self.write_continue_stmt(stmt)?,
Stmt::If(ref stmt) => {
self.write_if_stmt(stmt)?;
semi = false;
}
Stmt::Switch(ref stmt) => {
self.at_top_level = false;
self.write_switch_stmt(stmt)?;
semi = false;
}
Stmt::Throw(ref stmt) => self.write_throw_stmt(stmt)?,
Stmt::Try(ref stmt) => {
self.write_try_stmt(stmt)?;
semi = false;
}
Stmt::While(ref stmt) => {
new_line = self.write_while_stmt(stmt)?;
semi = false;
}
Stmt::DoWhile(ref stmt) => self.write_do_while_stmt(stmt)?,
Stmt::For(ref stmt) => {
self.at_top_level = false;
new_line = self.write_for_stmt(stmt)?;
semi = false;
}
Stmt::ForIn(ref stmt) => {
self.at_top_level = false;
new_line = self.write_for_in_stmt(stmt)?;
semi = false;
}
Stmt::ForOf(ref stmt) => {
self.at_top_level = false;
new_line = self.write_for_of_stmt(stmt)?;
semi = false;
}
Stmt::Var(ref stmt) => self.write_var_stmt(stmt)?,
};
if semi {
self.write_empty_stmt()?;
}
if new_line {
self.write_new_line()?;
}
self.at_top_level = cached_state;
Ok(())
}
pub fn write_debugger_stmt(&mut self) -> Res {
trace!("write_debugger_stmt");
self.write("debugger")
}
pub fn write_block_stmt(&mut self, block: &[ProgramPart]) -> Res {
trace!("write_block_stmt");
self.write_open_brace()?;
if block.len() == 0 {
self.write_new_line()?;
self.write_leading_whitespace()?;
self.write_new_line()?;
}
for ref part in block {
self.write_new_line()?;
self._write_part(part)?;
}
self.write_close_brace()?;
Ok(())
}
pub fn write_with_stmt(&mut self, expr: &WithStmt) -> Res {
trace!("write_with_stmt");
self.write("with (")?;
self.write_expr(&expr.object)?;
self.write(") ")?;
self.write_stmt(&expr.body)?;
Ok(())
}
pub fn write_return_stmt(&mut self, expr: &Option<Expr>) -> Res {
trace!("write_return_stmt");
self.write("return")?;
if let Some(ref e) = expr {
self.write(" ")?;
self.write_expr(e)?;
}
Ok(())
}
pub fn write_labeled_stmt(&mut self, expr: &LabeledStmt) -> Res {
trace!("write_labeled_stmt");
self.write_ident(&expr.label)?;
self.write(": ")?;
self.write_stmt(&expr.body)?;
Ok(())
}
pub fn write_break_stmt(&mut self, expr: &Option<Ident>) -> Res {
trace!("write_break_stmt");
self.write("break")?;
if let Some(ref i) = expr {
self.write(" ")?;
self.write_ident(i)?;
}
Ok(())
}
pub fn write_continue_stmt(&mut self, expr: &Option<Ident>) -> Res {
trace!("write_continue_stmt");
self.write("continue")?;
if let Some(ref i) = expr {
self.write(" ")?;
self.write_ident(i)?;
}
Ok(())
}
pub fn write_if_stmt(&mut self, expr: &IfStmt) -> Res {
trace!("write_if_stmt");
self.write("if (")?;
self.write_expr(&expr.test)?;
self.write(") ")?;
if let Stmt::Empty = &*expr.consequent {
self.write_block_stmt(&[])?;
} else {
self.write_stmt(&expr.consequent)?;
}
if let Some(ref alt) = &expr.alternate {
self.write(" else ")?;
if let Stmt::Empty = &**alt {
self.write_block_stmt(&[])?;
} else {
self.write_stmt(alt)?;
}
}
Ok(())
}
pub fn write_switch_stmt(&mut self, switch: &SwitchStmt) -> Res {
trace!("write_switch_stmt");
self.write("switch (")?;
self.write_expr(&switch.discriminant)?;
self.write(") ")?;
if switch.cases.len() == 0 {
self.write("{ }")?;
return Ok(());
}
self.write_open_brace()?;
if switch.cases.len() > 0 {
self.write_new_line()?;
}
for ref case in &switch.cases {
self.write_switch_case(case)?;
}
self.write_close_brace()?;
Ok(())
}
pub fn write_switch_case(&mut self, case: &SwitchCase) -> Res {
trace!("write_switch_case");
self.write_leading_whitespace()?;
if let Some(ref t) = &case.test {
self.write("case ")?;
self.write_expr(t)?;
} else {
self.write("default")?;
}
self.write(":")?;
self.write_new_line()?;
self.current_indent += 1;
let mut decrease_indent = true;
for ref part in &case.consequent {
if let ProgramPart::Stmt(Stmt::Break(_)) = part {
self.current_indent = self.current_indent.saturating_sub(1);
decrease_indent = false;
}
self._write_part(part)?;
self.write_new_line()?;
}
if decrease_indent {
self.current_indent = self.current_indent.saturating_sub(1);
}
Ok(())
}
pub fn write_throw_stmt(&mut self, expr: &Expr) -> Res {
trace!("write_throw_stmt");
self.write("throw ")?;
self.write_expr(&expr)?;
Ok(())
}
pub fn write_try_stmt(&mut self, stmt: &TryStmt) -> Res {
trace!("write_try_stmt");
self.write("try ")?;
self.write_block_stmt(&stmt.block.0)?;
if let Some(ref c) = &stmt.handler {
self.write(" catch")?;
if let Some(ref param) = &c.param {
self.write(" (")?;
self.write_pattern(param)?;
self.write(") ")?;
}
self.write_block_stmt(&c.body.0)?;
}
if let Some(ref f) = &stmt.finalizer {
self.write(" finally ")?;
self.write_block_stmt(&f.0)?;
}
Ok(())
}
pub fn write_while_stmt(&mut self, stmt: &WhileStmt) -> Result<bool, IoError> {
trace!("write_while_stmt");
let mut ret = false;
self.write("while (")?;
self.write_expr(&stmt.test)?;
self.write(") ")?;
if let Stmt::Block(_) = &*stmt.body {
ret = true;
}
self.write_stmt(&stmt.body)?;
Ok(ret)
}
pub fn write_do_while_stmt(&mut self, stmt: &DoWhileStmt) -> Res {
trace!("write_do_while_stmt");
self.write("do")?;
if let Stmt::Empty = &*stmt.body {
self.write("; ")?;
} else {
self.write(" ")?;
self.write_stmt(&stmt.body)?;
self.write(" ")?;
}
self.write("while (")?;
self.write_expr(&stmt.test)?;
self.write(")")?;
Ok(())
}
pub fn write_for_stmt(&mut self, stmt: &ForStmt) -> Result<bool, IoError> {
trace!("write_for_stmt");
self.write("for (")?;
if let Some(ref init) = &stmt.init {
self.write_loop_init(init)?;
}
self.write_empty_stmt()?;
if let Some(ref test) = &stmt.test {
self.write_expr(test)?;
}
self.write_empty_stmt()?;
if let Some(ref update) = &stmt.update {
self.write_expr(update)?;
}
self.write(") ")?;
let ret = if let Stmt::Block(_) = &*stmt.body {
true
} else {
false
};
self.write_stmt(&stmt.body)?;
Ok(ret)
}
pub fn write_loop_init(&mut self, init: &LoopInit) -> Res {
self.in_for_init = true;
match init {
LoopInit::Expr(ref e) => self.write_expr(e)?,
LoopInit::Variable(ref kind, ref v) => {
self.write_variable_kind(kind)?;
let mut after_first = false;
for ref d in v {
if after_first {
self.write(", ")?;
}
self.write_variable_decl(d)?;
after_first = true;
}
}
}
self.in_for_init = false;
Ok(())
}
pub fn write_for_in_stmt(&mut self, stmt: &ForInStmt) -> Result<bool, IoError> {
trace!("write_for_in_stmt");
self.write("for (")?;
self.write_loop_left(&stmt.left)?;
self.write(" in ")?;
self.write_expr(&stmt.right)?;
self.write(") ")?;
self.write_stmt(&stmt.body)?;
let ret = if let Stmt::Block(_) = &*stmt.body {
true
} else {
false
};
Ok(ret)
}
pub fn write_for_of_stmt(&mut self, stmt: &ForOfStmt) -> Result<bool, IoError> {
trace!("write_for_of_stmt");
self.write("for (")?;
self.write_loop_left(&stmt.left)?;
self.write(" of ")?;
self.write_expr(&stmt.right)?;
self.write(") ")?;
self.write_stmt(&stmt.body)?;
let ret = if let Stmt::Block(_) = &*stmt.body {
true
} else {
false
};
Ok(ret)
}
pub fn write_loop_left(&mut self, left: &LoopLeft) -> Res {
match left {
LoopLeft::Pat(ref pat) => self.write_pattern(pat)?,
LoopLeft::Variable(ref kind, ref var) => {
self.write_variable_kind(kind)?;
self.write_variable_decl(var)?;
}
LoopLeft::Expr(ref expr) => self.write_expr(expr)?,
}
Ok(())
}
pub fn write_var_stmt(&mut self, expr: &[VarDecl]) -> Res {
trace!("write_var_stmt");
self.write("var ")?;
let mut after_first = false;
for ref d in expr {
if after_first {
self.write(", ")?;
}
self.write_variable_decl(d)?;
after_first = true;
}
Ok(())
}
pub fn write_pattern(&mut self, pattern: &Pat) -> Res {
trace!("write_pattern");
match pattern {
Pat::Ident(ref i) => self.write(&i.name),
Pat::Obj(ref o) => self.write_object_pattern(o),
Pat::Array(ref a) => self.write_array_pattern(a.as_slice()),
Pat::RestElement(ref r) => self.write_rest_element(r),
Pat::Assign(ref a) => self.write_assignment_pattern(a),
}
}
pub fn write_object_pattern(&mut self, obj: &ObjPat) -> Res {
trace!("write_object_pattern");
if obj.len() == 0 {
self.write("{}")?;
return Ok(());
}
self.write_open_brace()?;
let mut after_first = false;
for ref part in obj {
if after_first {
self.write(", ")?;
} else {
after_first = true;
}
match part {
ObjPatPart::Assign(ref prop) => self.write_property(prop)?,
ObjPatPart::Rest(ref pat) => self.write_rest_pattern_part(pat)?,
}
}
self.write_close_brace()?;
Ok(())
}
pub fn write_property(&mut self, prop: &Prop) -> Res {
trace!("write_property");
if prop.is_static {
self.write("static ")?;
}
match &prop.kind {
PropKind::Init => {
if prop.method {
self.write_property_method(prop)
} else {
self.write_init_property(prop)
}
}
PropKind::Get => self.write_get_property(prop),
PropKind::Set => self.write_set_property(prop),
PropKind::Method => self.write_property_method(prop),
PropKind::Ctor => self.write_ctor_property(prop),
}
}
pub fn write_init_property(&mut self, prop: &Prop) -> Res {
trace!("write_init_property");
self.write_property_key(&prop.key, prop.computed)?;
if prop.value != PropValue::None {
self.write(": ")?;
}
match &prop.value {
PropValue::None => (),
PropValue::Expr(_) | PropValue::Pat(_) => {
self.write_property_value(&prop.value)?;
}
}
Ok(())
}
pub fn write_get_property(&mut self, prop: &Prop) -> Res {
trace!("write_get_property");
self.write("get ")?;
self.write_property_method(prop)
}
pub fn write_set_property(&mut self, prop: &Prop) -> Res {
trace!("write_set_property");
self.write("set ")?;
self.write_property_method(prop)
}
pub fn write_property_method(&mut self, prop: &Prop) -> Res {
trace!("write_property_method");
if let PropValue::Expr(Expr::Func(ref func)) = prop.value {
if func.is_async {
self.write("async ")?;
}
if func.generator {
self.write("*")?;
}
self.write_property_key(&prop.key, prop.computed)?;
self.write_function_args(&func.params)?;
self.write_function_body(&func.body)?;
} else {
panic!("property method value must be a function expression");
}
Ok(())
}
pub fn write_function_args(&mut self, args: &[FuncArg]) -> Res {
trace!("write_function_args");
self.write("(")?;
let mut after_first = false;
for ref arg in args {
if after_first {
self.write(", ")?;
} else {
after_first = true;
}
self.write_function_arg(arg)?;
}
self.write(")")?;
Ok(())
}
pub fn write_function_arg(&mut self, arg: &FuncArg) -> Res {
trace!("write_function_arg");
match arg {
FuncArg::Expr(ref ex) => self.write_expr(ex)?,
FuncArg::Pat(ref pa) => self.write_pattern(pa)?,
}
Ok(())
}
pub fn write_function_body(&mut self, body: &FuncBody) -> Res {
trace!("write_function_body");
if body.0.len() == 0 {
self.write("{ ")?;
} else {
self.write_open_brace()?;
self.write_new_line()?;
}
for ref part in &body.0 {
self._write_part(part)?;
}
if body.0.len() == 0 {
self.write("}")?;
} else {
self.write_close_brace()?;
}
Ok(())
}
pub fn write_ctor_property(&mut self, prop: &Prop) -> Res {
trace!("write_ctor_property");
self.write("constructor")?;
if let PropValue::Expr(Expr::Func(ref func)) = prop.value {
self.write_function_args(&func.params)?;
self.write_function_body(&func.body)?;
} else {
panic!("constructor's value must be a function expression");
}
Ok(())
}
pub fn write_property_key(&mut self, key: &PropKey, computed: bool) -> Res {
trace!("write_property_key");
if computed {
self.write("[")?;
}
match key {
PropKey::Expr(ref e) => self.write_expr(e)?,
PropKey::Lit(ref l) => self.write_literal(l)?,
PropKey::Pat(ref p) => self.write_pattern(p)?,
}
if computed {
self.write("]")?;
}
Ok(())
}
pub fn write_property_value(&mut self, value: &PropValue) -> Res {
trace!("write_property_value");
match value {
PropValue::Expr(ref e) => self.write_expr(e)?,
PropValue::Pat(ref p) => self.write_pattern(p)?,
PropValue::None => (),
}
Ok(())
}
pub fn write_rest_pattern_part(&mut self, pat: &Pat) -> Res {
trace!("write_rest_pattern_part");
self.write_pattern(pat)?;
Ok(())
}
pub fn write_array_pattern(&mut self, arr: &[Option<ArrayPatPart>]) -> Res {
trace!("write_array_pattern");
if arr.len() == 0 {
self.write("[]")?;
return Ok(());
}
self.write("[")?;
let last_idx = arr.len() - 1;
for (i, ref p) in arr.iter().enumerate() {
if let Some(ref part) = p {
match &part {
ArrayPatPart::Expr(e) => self.write_expr(e)?,
ArrayPatPart::Pat(p) => self.write_pattern(p)?,
}
}
if i < last_idx {
self.write(", ")?;
}
}
self.write("]")?;
Ok(())
}
pub fn write_rest_element(&mut self, pat: &Pat) -> Res {
trace!("write_rest_element");
self.write("...")?;
self.write_pattern(pat)?;
Ok(())
}
pub fn write_assignment_pattern(&mut self, assignment: &AssignPat) -> Res {
trace!("write_assignment_pattern");
self.write_pattern(&assignment.left)?;
self.write(" = ")?;
self.write_expr(&assignment.right)?;
Ok(())
}
pub fn write_wrapped_expr(&mut self, expr: &Expr) -> Res {
self.write("(")?;
self.write_expr(expr)?;
self.write(")")?;
Ok(())
}
pub fn write_expr(&mut self, expr: &Expr) -> Res {
trace!("write_expr");
let cached_state = self.at_top_level;
match expr {
Expr::Lit(ref expr) => self.write_literal(expr)?,
Expr::This => self.write_this_expr()?,
Expr::Super => self.write_super_expr()?,
Expr::Array(ref expr) => self.write_array_expr(expr)?,
Expr::Obj(ref expr) => self.write_object_expr(expr)?,
Expr::Func(ref expr) => {
self.at_top_level = false;
self.write_function(expr)?;
self.at_top_level = cached_state;
}
Expr::Unary(ref expr) => self.write_unary_expr(expr)?,
Expr::Update(ref expr) => self.write_update_expr(expr)?,
Expr::Binary(ref expr) => self.write_binary_expr(expr)?,
Expr::Assign(ref expr) => {
self.at_top_level = false;
self.write_assignment_expr(expr)?
}
Expr::Logical(ref expr) => self.write_logical_expr(expr)?,
Expr::Member(ref expr) => self.write_member_expr(expr)?,
Expr::Conditional(ref expr) => self.write_conditional_expr(expr)?,
Expr::Call(ref expr) => self.write_call_expr(expr)?,
Expr::New(ref expr) => self.write_new_expr(expr)?,
Expr::Sequence(ref expr) => self.write_sequence_expr(expr)?,
Expr::Spread(ref expr) => self.write_spread_expr(expr)?,
Expr::ArrowFunc(ref expr) => {
self.at_top_level = false;
self.write_arrow_function_expr(expr)?;
self.at_top_level = cached_state;
}
Expr::Yield(ref expr) => self.write_yield_expr(expr)?,
Expr::Class(ref expr) => {
self.at_top_level = false;
self.write_class(expr)?;
self.at_top_level = cached_state;
}
Expr::MetaProp(ref expr) => self.write_meta_property(expr)?,
Expr::Await(ref expr) => self.write_await_expr(expr)?,
Expr::Ident(ref expr) => self.write_ident(expr)?,
Expr::TaggedTemplate(ref expr) => self.write_tagged_template(expr)?,
_ => unreachable!(),
}
Ok(())
}
pub fn write_this_expr(&mut self) -> Res {
trace!("write_this_expr");
self.write("this")?;
Ok(())
}
pub fn write_super_expr(&mut self) -> Res {
trace!("write_super_expr");
self.write("super")?;
Ok(())
}
pub fn write_array_expr(&mut self, arr: &ArrayExpr) -> Res {
trace!("write_array_expr");
if arr.len() == 0 {
self.write("[]")?;
return Ok(());
}
self.write("[")?;
let last_idx = arr.len() - 1;
for (i, ref e) in arr.iter().enumerate() {
if let Some(ref e) = e {
self.write_expr(e)?;
if i < last_idx {
self.write(", ")?;
}
} else {
self.write(",")?;
}
}
self.write("]")?;
Ok(())
}
pub fn write_object_expr(&mut self, obj: &ObjExpr) -> Res {
trace!("write_object_expr");
if obj.len() == 0 {
self.write("{}")?;
return Ok(());
}
self.write("{")?;
let mut after_first = false;
for ref prop in obj {
if after_first {
self.write(", ")?;
} else {
after_first = true;
}
match prop {
ObjProp::Prop(ref p) => self.write_property(p),
ObjProp::Spread(ref e) => self.write_expr(e),
}?;
}
self.write("}")?;
Ok(())
}
pub fn write_function(&mut self, func: &Func) -> Res {
trace!("write_function");
if func.is_async {
self.write("async ")?;
}
self.write("function")?;
if let Some(ref id) = func.id {
self.write(" ")?;
if func.generator {
self.write("*")?;
}
self.write(&id.name)?;
} else if func.generator {
self.write("*")?;
}
self.write_function_args(&func.params)?;
self.write(" ")?;
self.write_function_body(&func.body)
}
pub fn write_unary_expr(&mut self, unary: &UnaryExpr) -> Res {
trace!("write_unary_expr");
if unary.prefix {
self.write_unary_operator(&unary.operator)?;
}
match &*unary.argument {
Expr::Assign(_)
| Expr::Binary(_)
| Expr::Logical(_)
| Expr::Conditional(_)
| Expr::ArrowFunc(_)
| Expr::Func(_) => self.write_wrapped_expr(&unary.argument)?,
Expr::Unary(_) | Expr::Update(_) => {
self.write(" ")?;
self.write_expr(&unary.argument)?;
}
_ => self.write_expr(&unary.argument)?,
}
if !unary.prefix {
self.write_unary_operator(&unary.operator)?;
}
Ok(())
}
pub fn write_unary_operator(&mut self, op: &UnaryOp) -> Res {
trace!("write_unary_operator");
match op {
UnaryOp::Delete => self.write("delete "),
UnaryOp::Minus => self.write("-"),
UnaryOp::Not => self.write("!"),
UnaryOp::Plus => self.write("+"),
UnaryOp::Tilde => self.write("~"),
UnaryOp::TypeOf => self.write("typeof "),
UnaryOp::Void => self.write("void "),
}?;
Ok(())
}
pub fn write_update_expr(&mut self, update: &UpdateExpr) -> Res {
trace!("write_update_expr");
if update.prefix {
self.write_update_operator(&update.operator)?;
}
self.write_expr(&update.argument)?;
if !update.prefix {
self.write_update_operator(&update.operator)?;
}
Ok(())
}
pub fn write_update_operator(&mut self, op: &UpdateOp) -> Res {
let s = match op {
UpdateOp::Decrement => "--",
UpdateOp::Increment => "++",
};
self.write(s)?;
Ok(())
}
pub fn write_binary_expr(&mut self, binary: &BinaryExpr) -> Res {
trace!("write_binary_expr {:#?}", binary);
let wrap = self.in_for_init && binary.operator == BinaryOp::In;
if wrap {
self.write("(")?;
}
self.write_binary_side(&*binary.left)?;
self.write(" ")?;
self.write_binary_operator(&binary.operator)?;
self.write(" ")?;
self.write_binary_side(&*binary.right)?;
if wrap {
self.write(")")?;
}
Ok(())
}
pub fn write_binary_side(&mut self, side: &Expr) -> Res {
match &*side {
Expr::Assign(_)
| Expr::Conditional(_)
| Expr::Logical(_)
| Expr::Func(_)
| Expr::Binary(_)
| Expr::ArrowFunc(_) => self.write_wrapped_expr(side),
_ => self.write_expr(side),
}
}
pub fn write_binary_operator(&mut self, op: &BinaryOp) -> Res {
let s = match op {
BinaryOp::And => "&",
BinaryOp::Equal => "==",
BinaryOp::GreaterThan => ">",
BinaryOp::GreaterThanEqual => ">=",
BinaryOp::In => "in",
BinaryOp::InstanceOf => "instanceof",
BinaryOp::LeftShift => "<<",
BinaryOp::LessThan => "<",
BinaryOp::LessThanEqual => "<=",
BinaryOp::Minus => "-",
BinaryOp::Mod => "%",
BinaryOp::NotEqual => "!=",
BinaryOp::Or => "|",
BinaryOp::Over => "/",
BinaryOp::Plus => "+",
BinaryOp::PowerOf => "**",
BinaryOp::RightShift => ">>",
BinaryOp::StrictEqual => "===",
BinaryOp::StrictNotEqual => "!==",
BinaryOp::Times => "*",
BinaryOp::UnsignedRightShift => ">>>",
BinaryOp::XOr => "^",
};
self.write(s)?;
Ok(())
}
pub fn write_assignment_expr(&mut self, assignment: &AssignExpr) -> Res {
trace!("write_assignment_expr");
let wrap_self = match &assignment.left {
AssignLeft::Expr(ref e) => match &**e {
Expr::Obj(_) | Expr::Array(_) => true,
_ => false,
},
AssignLeft::Pat(ref p) => match p {
Pat::Array(_) => true,
Pat::Obj(_) => true,
_ => false,
},
};
if wrap_self {
self.write("(")?;
}
match &assignment.left {
AssignLeft::Expr(ref e) => self.write_expr(e)?,
AssignLeft::Pat(ref p) => self.write_pattern(p)?,
}
self.write(" ")?;
self.write_assignment_operator(&assignment.operator)?;
self.write(" ")?;
self.write_expr(&assignment.right)?;
if wrap_self {
self.write(")")?;
}
Ok(())
}
pub fn write_assignment_operator(&mut self, op: &AssignOp) -> Res {
let s = match op {
AssignOp::AndEqual => "&=",
AssignOp::DivEqual => "/=",
AssignOp::Equal => "=",
AssignOp::LeftShiftEqual => "<<=",
AssignOp::MinusEqual => "-=",
AssignOp::ModEqual => "%=",
AssignOp::OrEqual => "|=",
AssignOp::PlusEqual => "+=",
AssignOp::PowerOfEqual => "**=",
AssignOp::RightShiftEqual => ">>=",
AssignOp::TimesEqual => "*=",
AssignOp::UnsignedRightShiftEqual => ">>>=",
AssignOp::XOrEqual => "^=",
};
self.write(s)?;
Ok(())
}
pub fn write_logical_expr(&mut self, logical: &LogicalExpr) -> Res {
trace!("write_logical_expr {:#?}", logical);
let wrap_left = match &*logical.left {
Expr::Logical(ref l) => l.operator == LogicalOp::Or,
Expr::Assign(_) | Expr::Conditional(_) => true,
_ => false,
};
if wrap_left {
self.write_wrapped_expr(&logical.left)?;
} else {
self.write_expr(&logical.left)?;
}
self.write(" ")?;
self.write_logical_operator(&logical.operator)?;
let wrap_right = match &*logical.right {
Expr::Logical(ref _l) => true,
Expr::Assign(_) | Expr::Conditional(_) => true,
_ => false,
};
self.write(" ")?;
if wrap_right {
self.write_wrapped_expr(&logical.right)?;
} else {
self.write_expr(&logical.right)?;
}
Ok(())
}
pub fn write_logical_operator(&mut self, op: &LogicalOp) -> Res {
trace!("write_logical_operator");
let s = match op {
LogicalOp::And => "&&",
LogicalOp::Or => "||",
};
self.write(s)?;
Ok(())
}
pub fn write_member_expr(&mut self, member: &MemberExpr) -> Res {
trace!("write_member_expr");
match &*member.object {
Expr::Assign(_)
| Expr::Lit(Lit::Number(_))
| Expr::Conditional(_)
| Expr::Logical(_)
| Expr::Func(_)
| Expr::ArrowFunc(_)
| Expr::Obj(_)
| Expr::Binary(_)
| Expr::Unary(_)
| Expr::Update(_) => self.write_wrapped_expr(&member.object)?,
_ => self.write_expr(&member.object)?,
}
if member.computed {
self.write("[")?;
} else {
self.write(".")?;
}
self.write_expr(&member.property)?;
if member.computed {
self.write("]")?;
}
Ok(())
}
pub fn write_conditional_expr(&mut self, conditional: &ConditionalExpr) -> Res {
trace!("write_conditional_expr");
self.write_expr(&conditional.test)?;
self.write(" ? ")?;
if let Expr::Logical(_) = &*conditional.consequent {
self.write_wrapped_expr(&conditional.consequent)?;
} else {
self.write_expr(&conditional.consequent)?;
}
self.write(" : ")?;
self.write_expr(&conditional.alternate)?;
Ok(())
}
pub fn write_call_expr(&mut self, call: &CallExpr) -> Res {
trace!("write_call_expr");
match &*call.callee {
Expr::Func(_) | Expr::ArrowFunc(_) => self.write_wrapped_expr(&call.callee)?,
_ => self.write_expr(&call.callee)?,
}
self.write_sequence_expr(&call.arguments)?;
Ok(())
}
pub fn write_new_expr(&mut self, new: &NewExpr) -> Res {
trace!("write_new_expr");
self.write("new ")?;
match &*new.callee {
Expr::Assign(_) | Expr::Call(_) => self.write_wrapped_expr(&new.callee)?,
_ => self.write_expr(&new.callee)?,
}
self.write_sequence_expr(&new.arguments)?;
Ok(())
}
pub fn write_sequence_expr(&mut self, sequence: &[Expr]) -> Res {
trace!("write_sequence_expr");
let mut after_first = false;
self.write("(")?;
for ref e in sequence {
if after_first {
self.write(", ")?;
}
self.write_expr(e)?;
after_first = true;
}
self.write(")")?;
Ok(())
}
pub fn write_spread_expr(&mut self, spread: &Expr) -> Res {
trace!("write_spread_expr");
self.write("...")?;
self.write_expr(spread)?;
Ok(())
}
pub fn write_arrow_function_expr(&mut self, func: &ArrowFuncExpr) -> Res {
trace!("write_arrow_function_expr");
if func.is_async {
self.write("async ")?;
}
if func.params.len() == 1 {
match &func.params[0] {
FuncArg::Expr(ref arg) => match arg {
Expr::Ident(_) => self.write_function_arg(&func.params[0])?,
_ => self.write_function_args(&func.params)?,
},
FuncArg::Pat(ref arg) => match arg {
Pat::Ident(_) => self.write_function_arg(&func.params[0])?,
_ => self.write_function_args(&func.params)?,
},
}
} else {
self.write_function_args(&func.params)?;
}
self.write(" => ")?;
match &func.body {
ArrowFuncBody::FuncBody(ref b) => self.write_function_body(b)?,
ArrowFuncBody::Expr(ref e) => match &**e {
Expr::Obj(_) | Expr::Binary(_) => self.write_wrapped_expr(e)?,
_ => self.write_expr(e)?,
},
}
Ok(())
}
pub fn write_yield_expr(&mut self, expr: &YieldExpr) -> Res {
trace!("write_yield_expr");
self.write("yield")?;
if expr.argument.is_some() {
self.write(" ")?;
}
if expr.delegate {
self.write("*")?;
}
if let Some(ref arg) = &expr.argument {
self.write_expr(arg)?;
}
Ok(())
}
pub fn write_meta_property(&mut self, meta: &MetaProp) -> Res {
trace!("write_meta_property");
self.write_ident(&meta.meta)?;
self.write(".")?;
self.write_ident(&meta.property)?;
Ok(())
}
pub fn write_await_expr(&mut self, expr: &Expr) -> Res {
trace!("write_await_expr");
self.write("await ")?;
self.write_expr(expr)?;
Ok(())
}
pub fn write_ident(&mut self, ident: &Ident<'_>) -> Res {
trace!("write_ident");
self.write(&ident.name)
}
pub fn write_tagged_template(&mut self, template: &TaggedTemplateExpr) -> Res {
trace!("write_tagged_template");
self.write_expr(&template.tag)?;
self.write_template(&template.quasi)?;
Ok(())
}
pub fn write_literal(&mut self, lit: &Lit) -> Res {
trace!("write_literal");
match lit {
Lit::Boolean(b) => self.write_bool(*b),
Lit::Null => self.write("null"),
Lit::Number(n) => self.write(&n),
Lit::String(s) => self.write_string(s),
Lit::RegEx(r) => self.write_regex(r),
Lit::Template(t) => self.write_template(t),
}
}
pub fn write_bool(&mut self, boolean: bool) -> Res {
trace!("write_bool");
if boolean {
self.write("true")
} else {
self.write("false")
}
}
pub fn write_string(&mut self, s: &StringLit) -> Res {
trace!("write_string");
if let Some(c) = self.quote {
self.write_char(c)?;
match s {
StringLit::Double(s) | StringLit::Single(s) => self.write(s)?,
}
self.write_char(c)?;
} else {
let (c, s) = match s {
StringLit::Single(s) => ('\'', s),
StringLit::Double(s) => ('"', s),
};
self.write_char(c)?;
self.write(s)?;
self.write_char(c)?;
}
Ok(())
}
pub fn write_regex(&mut self, regex: &RegEx) -> Res {
trace!("write_regex {:?}", regex);
self.write("/")?;
self.write(®ex.pattern)?;
self.write("/")?;
self.write(®ex.flags)?;
Ok(())
}
pub fn write_template(&mut self, template: &TemplateLit) -> Res {
trace!("write_template");
let mut quasis = template.quasis.iter();
let mut exprs = template.expressions.iter();
while let Some(quasi) = quasis.next() {
self.write(&quasi.raw)?;
if let Some(exp) = exprs.next() {
self.write_expr(exp)?;
}
}
Ok(())
}
pub fn write_empty_stmt(&mut self) -> Res {
trace!("write_empty_stmt");
self.write(";")
}
pub fn write_open_brace(&mut self) -> Res {
trace!("write_open_brace");
self.write("{")?;
self.current_indent += 1;
Ok(())
}
pub fn write_close_brace(&mut self) -> Res {
trace!("write_close_brace");
self.current_indent -= 1;
self.write_leading_whitespace()?;
self.write("}")?;
Ok(())
}
pub fn write_leading_whitespace(&mut self) -> Res {
trace!("write_leading_whitespace");
self.write(&self.indent.repeat(self.current_indent))?;
Ok(())
}
pub fn write_new_line(&mut self) -> Res {
trace!("write_new_line");
self.write(&self.new_line.clone())?;
Ok(())
}
fn write(&mut self, s: &str) -> Res {
let _ = self.out.write(s.as_bytes())?;
Ok(())
}
fn write_char(&mut self, c: char) -> Res {
let mut buf = [0u8; 4];
let _ = self.out.write(c.encode_utf8(&mut buf).as_bytes())?;
Ok(())
}
pub fn write_comment(&mut self, comment: Comment<&str>) -> Res {
match comment.kind {
CommentKind::Single => self.write(&format!("//{}", comment.content))?,
CommentKind::Multi => self.write(&format!("/*{}\n*/", comment.content))?,
CommentKind::Html => self.write(&format!(
"<!--{}-->{}",
comment.content,
comment.tail_content.unwrap_or("")
))?,
CommentKind::Hashbang => self.write(&format!("#! {}", comment.content))?,
}
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn write_empty_expression() {
let mut f = write_str::WriteString::new();
let mut w = Writer::new(f.generate_child());
w.write_empty_stmt().unwrap();
let out = f.get_string_lossy();
assert_eq!(out, ";".to_string());
}
#[test]
fn write_debugger_stmt() {
let mut f = write_str::WriteString::new();
let mut w = Writer::new(f.generate_child());
w.write_debugger_stmt().unwrap();
let s = f.get_string_lossy();
assert_eq!(s, "debugger");
}
#[test]
fn write_variable_decls() {
let mut f = write_str::WriteString::new();
let mut w = Writer::new(f.generate_child());
w.write_variable_decls(
&VarKind::Var,
&[VarDecl {
id: Pat::ident_from("thing"),
init: Some(Expr::Lit(Lit::Boolean(false))),
}],
)
.unwrap();
let s = f.get_string_lossy();
assert_eq!(s, "var thing = false;\n".to_string());
let mut f = write_str::WriteString::new();
let mut w = Writer::new(f.generate_child());
w.write_variable_decls(
&VarKind::Let,
&[
VarDecl {
id: Pat::ident_from("stuff"),
init: None,
},
VarDecl {
id: Pat::ident_from("places"),
init: None,
},
VarDecl {
id: Pat::ident_from("thing"),
init: Some(Expr::Lit(Lit::Boolean(false))),
},
],
)
.unwrap();
let s = f.get_string_lossy();
assert_eq!(s, "let stuff, places, thing = false;\n");
}
}