use core::ops::ControlFlow;
use std::convert::TryFrom;
use crate::try_break;
use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{
expression::{Expression, Identifier},
join_nodes,
pattern::Pattern,
Statement,
};
use boa_interner::{Interner, ToInternedString};
use super::Declaration;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct VarDeclaration(pub VariableList);
impl From<VarDeclaration> for Statement {
fn from(var: VarDeclaration) -> Self {
Self::Var(var)
}
}
impl ToInternedString for VarDeclaration {
fn to_interned_string(&self, interner: &Interner) -> String {
format!("var {}", self.0.to_interned_string(interner))
}
}
impl VisitWith for VarDeclaration {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
visitor.visit_variable_list(&self.0)
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
visitor.visit_variable_list_mut(&mut self.0)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum LexicalDeclaration {
Const(VariableList),
Let(VariableList),
}
impl LexicalDeclaration {
#[must_use]
pub const fn variable_list(&self) -> &VariableList {
match self {
Self::Const(list) | Self::Let(list) => list,
}
}
#[must_use]
pub const fn is_const(&self) -> bool {
matches!(self, Self::Const(_))
}
}
impl From<LexicalDeclaration> for Declaration {
fn from(lex: LexicalDeclaration) -> Self {
Self::Lexical(lex)
}
}
impl ToInternedString for LexicalDeclaration {
fn to_interned_string(&self, interner: &Interner) -> String {
format!(
"{} {}",
match &self {
Self::Let(_) => "let",
Self::Const(_) => "const",
},
self.variable_list().to_interned_string(interner)
)
}
}
impl VisitWith for LexicalDeclaration {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
match self {
Self::Const(vars) | Self::Let(vars) => visitor.visit_variable_list(vars),
}
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
match self {
Self::Const(vars) | Self::Let(vars) => visitor.visit_variable_list_mut(vars),
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct VariableList {
list: Box<[Variable]>,
}
impl VariableList {
#[must_use]
pub fn new(list: Box<[Variable]>) -> Option<Self> {
if list.is_empty() {
return None;
}
Some(Self { list })
}
}
impl AsRef<[Variable]> for VariableList {
fn as_ref(&self) -> &[Variable] {
&self.list
}
}
impl ToInternedString for VariableList {
fn to_interned_string(&self, interner: &Interner) -> String {
join_nodes(interner, self.list.as_ref())
}
}
impl VisitWith for VariableList {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
for variable in &*self.list {
try_break!(visitor.visit_variable(variable));
}
ControlFlow::Continue(())
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
for variable in &mut *self.list {
try_break!(visitor.visit_variable_mut(variable));
}
ControlFlow::Continue(())
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct TryFromVariableListError(());
impl std::fmt::Display for TryFromVariableListError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
"provided list of variables cannot be empty".fmt(f)
}
}
impl TryFrom<Box<[Variable]>> for VariableList {
type Error = TryFromVariableListError;
fn try_from(value: Box<[Variable]>) -> Result<Self, Self::Error> {
Self::new(value).ok_or(TryFromVariableListError(()))
}
}
impl TryFrom<Vec<Variable>> for VariableList {
type Error = TryFromVariableListError;
fn try_from(value: Vec<Variable>) -> Result<Self, Self::Error> {
Self::try_from(value.into_boxed_slice())
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Variable {
binding: Binding,
init: Option<Expression>,
}
impl ToInternedString for Variable {
fn to_interned_string(&self, interner: &Interner) -> String {
let mut buf = self.binding.to_interned_string(interner);
if let Some(ref init) = self.init {
buf.push_str(&format!(" = {}", init.to_interned_string(interner)));
}
buf
}
}
impl Variable {
#[inline]
#[must_use]
pub const fn from_identifier(ident: Identifier, init: Option<Expression>) -> Self {
Self {
binding: Binding::Identifier(ident),
init,
}
}
#[inline]
#[must_use]
pub const fn from_pattern(pattern: Pattern, init: Option<Expression>) -> Self {
Self {
binding: Binding::Pattern(pattern),
init,
}
}
#[must_use]
pub const fn binding(&self) -> &Binding {
&self.binding
}
#[inline]
#[must_use]
pub const fn init(&self) -> Option<&Expression> {
self.init.as_ref()
}
}
impl VisitWith for Variable {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
try_break!(visitor.visit_binding(&self.binding));
if let Some(init) = &self.init {
try_break!(visitor.visit_expression(init));
}
ControlFlow::Continue(())
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
try_break!(visitor.visit_binding_mut(&mut self.binding));
if let Some(init) = &mut self.init {
try_break!(visitor.visit_expression_mut(init));
}
ControlFlow::Continue(())
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum Binding {
Identifier(Identifier),
Pattern(Pattern),
}
impl From<Identifier> for Binding {
fn from(id: Identifier) -> Self {
Self::Identifier(id)
}
}
impl From<Pattern> for Binding {
fn from(pat: Pattern) -> Self {
Self::Pattern(pat)
}
}
impl ToInternedString for Binding {
fn to_interned_string(&self, interner: &Interner) -> String {
match self {
Self::Identifier(id) => id.to_interned_string(interner),
Self::Pattern(ref pattern) => pattern.to_interned_string(interner),
}
}
}
impl VisitWith for Binding {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
match self {
Self::Identifier(id) => visitor.visit_identifier(id),
Self::Pattern(pattern) => visitor.visit_pattern(pattern),
}
}
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
match self {
Self::Identifier(id) => visitor.visit_identifier_mut(id),
Self::Pattern(pattern) => visitor.visit_pattern_mut(pattern),
}
}
}