use crate::syntax::ast::{
constant::Const,
op::{BinOp, Operator, UnaryOp},
};
use gc_derive::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "serde-ast")]
use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub enum Node {
ArrayDecl(Vec<Node>),
ArrowFunctionDecl(Vec<FormalParameter>, Box<Node>),
Assign(Box<Node>, Box<Node>),
BinOp(BinOp, Box<Node>, Box<Node>),
Block(Vec<Node>),
Break(Option<String>),
Call(Box<Node>, Vec<Node>),
ConditionalOp(Box<Node>, Box<Node>, Box<Node>),
Const(Const),
ConstDecl(Vec<(String, Node)>),
Continue(Option<String>),
FunctionDecl(Option<String>, Vec<FormalParameter>, Box<Node>),
GetConstField(Box<Node>, String),
GetField(Box<Node>, Box<Node>),
ForLoop(
Option<Box<Node>>,
Option<Box<Node>>,
Option<Box<Node>>,
Box<Node>,
),
If(Box<Node>, Box<Node>, Option<Box<Node>>),
LetDecl(Vec<(String, Option<Node>)>),
Local(String),
New(Box<Node>),
Object(Vec<PropertyDefinition>),
Return(Option<Box<Node>>),
Switch(Box<Node>, Vec<(Node, Vec<Node>)>, Option<Box<Node>>),
Spread(Box<Node>),
StatementList(Vec<Node>),
Throw(Box<Node>),
TypeOf(Box<Node>),
Try(
Box<Node>,
Option<Box<Node>>,
Option<Box<Node>>,
Option<Box<Node>>,
),
This,
UnaryOp(UnaryOp, Box<Node>),
VarDecl(Vec<(String, Option<Node>)>),
WhileLoop(Box<Node>, Box<Node>),
}
impl Operator for Node {
fn get_assoc(&self) -> bool {
match *self {
Node::UnaryOp(_, _) | Node::TypeOf(_) | Node::If(_, _, _) | Node::Assign(_, _) => false,
_ => true,
}
}
fn get_precedence(&self) -> u64 {
match self {
Node::GetField(_, _) | Node::GetConstField(_, _) => 1,
Node::Call(_, _) => 2,
Node::UnaryOp(UnaryOp::IncrementPost, _)
| Node::UnaryOp(UnaryOp::IncrementPre, _)
| Node::UnaryOp(UnaryOp::DecrementPost, _)
| Node::UnaryOp(UnaryOp::DecrementPre, _) => 3,
Node::UnaryOp(UnaryOp::Not, _)
| Node::UnaryOp(UnaryOp::Tilde, _)
| Node::UnaryOp(UnaryOp::Minus, _)
| Node::TypeOf(_) => 4,
Node::BinOp(op, _, _) => op.get_precedence(),
Node::If(_, _, _) => 15,
Node::Assign(_, _) => 17,
_ => 19,
}
}
}
impl fmt::Display for Node {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}
impl Node {
fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
let indent = " ".repeat(indentation);
match *self {
Self::Block(_) => {}
_ => write!(f, "{}", indent)?,
}
match *self {
Self::Const(ref c) => write!(f, "{}", c),
Self::ConditionalOp(_, _, _) => write!(f, "Conditional op"),
Self::ForLoop(_, _, _, _) => write!(f, "for loop"),
Self::This => write!(f, "this"),
Self::Try(_, _, _, _) => write!(f, "try/catch/finally"),
Self::Break(_) => write!(f, "break"),
Self::Continue(_) => write!(f, "continue"),
Self::Spread(ref node) => write!(f, "...{}", node),
Self::Block(ref block) => {
writeln!(f, "{{")?;
for node in block.iter() {
node.display(f, indentation + 1)?;
match node {
Self::Block(_)
| Self::If(_, _, _)
| Self::Switch(_, _, _)
| Self::FunctionDecl(_, _, _)
| Self::WhileLoop(_, _)
| Self::StatementList(_) => {}
_ => write!(f, ";")?,
}
writeln!(f)?;
}
write!(f, "{}}}", indent)
}
Node::StatementList(ref list) => {
for node in list.iter() {
node.display(f, indentation + 1)?;
match node {
Self::Block(_)
| Self::If(_, _, _)
| Self::Switch(_, _, _)
| Self::FunctionDecl(_, _, _)
| Self::WhileLoop(_, _)
| Self::StatementList(_) => {}
_ => write!(f, ";")?,
}
writeln!(f)?;
}
Ok(())
}
Self::Local(ref s) => write!(f, "{}", s),
Self::GetConstField(ref ex, ref field) => write!(f, "{}.{}", ex, field),
Self::GetField(ref ex, ref field) => write!(f, "{}[{}]", ex, field),
Self::Call(ref ex, ref args) => {
write!(f, "{}(", ex)?;
let arg_strs: Vec<String> = args.iter().map(ToString::to_string).collect();
write!(f, "{})", arg_strs.join(", "))
}
Self::New(ref call) => {
let (func, args) = match call.as_ref() {
Node::Call(func, args) => (func, args),
_ => unreachable!("Node::New(ref call): 'call' must only be Node::Call type."),
};
write!(f, "new {}", func)?;
f.write_str("(")?;
let mut first = true;
for e in args.iter() {
if !first {
f.write_str(", ")?;
}
first = false;
write!(f, "{}", e)?;
}
f.write_str(")")
}
Self::WhileLoop(ref cond, ref node) => {
write!(f, "while ({}) ", cond)?;
node.display(f, indentation)
}
Self::If(ref cond, ref node, None) => {
write!(f, "if ({}) ", cond)?;
node.display(f, indentation)
}
Self::If(ref cond, ref node, Some(ref else_e)) => {
write!(f, "if ({}) ", cond)?;
node.display(f, indentation)?;
f.write_str(" else ")?;
else_e.display(f, indentation)
}
Self::Switch(ref val, ref vals, None) => {
writeln!(f, "switch ({}) {{", val)?;
for e in vals.iter() {
writeln!(f, "{}case {}:", indent, e.0)?;
join_nodes(f, &e.1)?;
}
writeln!(f, "{}}}", indent)
}
Self::Switch(ref val, ref vals, Some(ref def)) => {
writeln!(f, "switch ({}) {{", val)?;
for e in vals.iter() {
writeln!(f, "{}case {}:", indent, e.0)?;
join_nodes(f, &e.1)?;
}
writeln!(f, "{}default:", indent)?;
def.display(f, indentation + 1)?;
write!(f, "{}}}", indent)
}
Self::Object(ref properties) => {
f.write_str("{\n")?;
for property in properties {
match property {
PropertyDefinition::IdentifierReference(key) => {
write!(f, "{} {},", indent, key)?;
}
PropertyDefinition::Property(key, value) => {
write!(f, "{} {}: {},", indent, key, value)?;
}
PropertyDefinition::SpreadObject(key) => {
write!(f, "{} ...{},", indent, key)?;
}
PropertyDefinition::MethodDefinition(_kind, _key, _node) => {
unimplemented!("Display for PropertyDefinition::MethodDefinition");
}
}
}
f.write_str("}")
}
Self::ArrayDecl(ref arr) => {
f.write_str("[")?;
join_nodes(f, arr)?;
f.write_str("]")
}
Self::FunctionDecl(ref name, ref _args, ref node) => {
write!(f, "function ")?;
if let Some(func_name) = name {
write!(f, "{}", func_name)?;
}
write!(f, "{{")?;
f.write_str("} ")?;
node.display(f, indentation + 1)
}
Self::ArrowFunctionDecl(ref _args, ref node) => {
write!(f, "(")?;
f.write_str(") => ")?;
node.display(f, indentation)
}
Self::BinOp(ref op, ref a, ref b) => write!(f, "{} {} {}", a, op, b),
Self::UnaryOp(ref op, ref a) => write!(f, "{}{}", op, a),
Self::Return(Some(ref ex)) => write!(f, "return {}", ex),
Self::Return(None) => write!(f, "return"),
Self::Throw(ref ex) => write!(f, "throw {}", ex),
Self::Assign(ref ref_e, ref val) => write!(f, "{} = {}", ref_e, val),
Self::VarDecl(ref vars) | Self::LetDecl(ref vars) => {
if let Self::VarDecl(_) = *self {
f.write_str("var ")?;
} else {
f.write_str("let ")?;
}
for (key, val) in vars.iter() {
match val {
Some(x) => write!(f, "{} = {}", key, x)?,
None => write!(f, "{}", key)?,
}
}
Ok(())
}
Self::ConstDecl(ref vars) => {
f.write_str("const ")?;
for (key, val) in vars.iter() {
write!(f, "{} = {}", key, val)?
}
Ok(())
}
Self::TypeOf(ref e) => write!(f, "typeof {}", e),
}
}
}
fn join_nodes(f: &mut fmt::Formatter<'_>, nodes: &[Node]) -> fmt::Result {
let mut first = true;
for e in nodes {
if !first {
f.write_str(", ")?;
}
first = false;
write!(f, "{}", e)?;
}
Ok(())
}
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Trace, Finalize)]
pub struct FormalParameter {
pub name: String,
pub init: Option<Box<Node>>,
pub is_rest_param: bool,
}
pub type FormalParameters = Vec<FormalParameter>;
impl FormalParameter {
pub fn new(name: String, init: Option<Box<Node>>, is_rest_param: bool) -> FormalParameter {
FormalParameter {
name,
init,
is_rest_param,
}
}
}
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Trace, Finalize)]
pub enum PropertyDefinition {
IdentifierReference(String),
Property(String, Node),
MethodDefinition(MethodDefinitionKind, String, Node),
SpreadObject(Node),
}
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Trace, Finalize)]
pub enum MethodDefinitionKind {
Get,
Set,
Ordinary,
}