use std::cell::Cell;
use std::marker::PhantomData;
use hermes_support::location::{SMLoc, SMRange};
use crate::context::{GCLock, NodeListElement};
use crate::NodeId;
use crate::node::{EmptyStatement, Node};
use crate::visitor::{Path, TransformResult, VisitorMut};
pub type NodeLabel = hermes_atom_table::AtomBytes;
pub type NodeString = hermes_atom_table::AtomBytes;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Strictness {
NotSet,
NonStrictMode,
StrictMode,
}
pub const INVALID_LABEL: u32 = u32::MAX;
#[derive(Debug)]
pub struct NodeMetadata<'gc> {
pub(crate) phantom: PhantomData<&'gc Node<'gc>>,
pub range: Cell<SMRange>,
pub debug_loc: Cell<SMLoc>,
pub parens: Cell<u8>,
pub id: Cell<NodeId>,
}
impl<'gc> NodeMetadata<'gc> {
pub fn new(range: SMRange) -> Self {
NodeMetadata {
phantom: PhantomData,
range: Cell::new(range),
debug_loc: Cell::new(range.start),
parens: Cell::new(0),
id: Cell::new(NodeId::UNASSIGNED),
}
}
pub fn new_with_debug(range: SMRange, debug_loc: SMLoc) -> Self {
NodeMetadata {
phantom: PhantomData,
range: Cell::new(range),
debug_loc: Cell::new(debug_loc),
parens: Cell::new(0),
id: Cell::new(NodeId::UNASSIGNED),
}
}
pub(crate) fn duplicate(&self) -> NodeMetadata<'gc> {
NodeMetadata {
phantom: self.phantom,
range: Cell::new(self.range.get()),
debug_loc: Cell::new(self.debug_loc.get()),
parens: Cell::new(self.parens.get()),
id: Cell::new(NodeId::UNASSIGNED),
}
}
#[doc(hidden)]
pub fn duplicate_pub_for_test(&self) -> NodeMetadata<'gc> {
self.duplicate()
}
}
#[derive(Debug, Copy, Clone)]
pub struct NodeList<'gc> {
pub(crate) head: *const NodeListElement<'gc>,
}
impl<'gc> NodeList<'gc> {
pub fn empty() -> Self {
NodeList {
head: std::ptr::null(),
}
}
pub fn from_iter<'a, I: IntoIterator<Item = &'a Node<'a>>>(
lock: &'a GCLock<'_, '_>,
nodes: I,
) -> NodeList<'a> {
let mut it = nodes.into_iter();
match it.next() {
Some(first) => {
let head_elem: &'a NodeListElement<'a> =
lock.append_list_element(None, first);
let mut prev_elem = head_elem;
for next in it {
let next_elem =
lock.append_list_element(Some(prev_elem), next);
prev_elem = next_elem;
}
NodeList { head: head_elem }
}
_ => {
NodeList::empty()
}
}
}
pub fn is_empty(&self) -> bool {
self.head.is_null()
}
pub fn iter(self) -> NodeListIter<'gc> {
NodeListIter {
ptr: self.head,
_pd: PhantomData,
}
}
}
impl<'gc> IntoIterator for NodeList<'gc> {
type Item = &'gc Node<'gc>;
type IntoIter = NodeListIter<'gc>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
pub struct NodeListIter<'gc> {
ptr: *const NodeListElement<'gc>,
_pd: PhantomData<&'gc Node<'gc>>,
}
impl<'gc> Iterator for NodeListIter<'gc> {
type Item = &'gc Node<'gc>;
fn next(&mut self) -> Option<&'gc Node<'gc>> {
if self.ptr.is_null() {
None
} else {
let (node, next) = crate::context::list_elem_parts(self.ptr);
self.ptr = next;
Some(node)
}
}
}
fn empty_statement<'gc>(gc: &'gc GCLock<'_, '_>, at: SMRange) -> &'gc Node<'gc> {
let range = SMRange {
start: at.start,
end: at.start,
};
gc.alloc(Node::EmptyStatement(EmptyStatement::new(NodeMetadata::new(range))))
}
pub(crate) trait NodeChild<'gc>: Sized {
type Out;
fn visit_child_mut<V: VisitorMut<'gc>>(
self,
ctx: &'gc GCLock<'_, '_>,
visitor: &mut V,
path: Path<'gc>,
) -> TransformResult<Self::Out>;
fn duplicate(self) -> Self::Out;
}
impl<'gc> NodeChild<'gc> for &'gc Node<'gc> {
type Out = &'gc Node<'gc>;
fn visit_child_mut<V: VisitorMut<'gc>>(
self,
ctx: &'gc GCLock<'_, '_>,
visitor: &mut V,
path: Path<'gc>,
) -> TransformResult<Self::Out> {
match visitor.call(ctx, self, Some(path)) {
TransformResult::Removed => {
TransformResult::Changed(empty_statement(ctx, self.range()))
}
TransformResult::Expanded(_) => {
panic!("cannot expand a single required child into multiple nodes")
}
other => other,
}
}
fn duplicate(self) -> Self::Out {
self
}
}
impl<'gc> NodeChild<'gc> for Option<&'gc Node<'gc>> {
type Out = Option<&'gc Node<'gc>>;
fn visit_child_mut<V: VisitorMut<'gc>>(
self,
ctx: &'gc GCLock<'_, '_>,
visitor: &mut V,
path: Path<'gc>,
) -> TransformResult<Self::Out> {
use TransformResult::*;
match self {
None => Unchanged,
Some(inner) => match visitor.call(ctx, inner, Some(path)) {
Unchanged => Unchanged,
Removed => Changed(None),
Changed(new_node) => Changed(Some(new_node)),
Expanded(_) => {
panic!("cannot expand a single optional child into multiple nodes")
}
},
}
}
fn duplicate(self) -> Self::Out {
self
}
}
impl<'gc> NodeChild<'gc> for NodeList<'gc> {
type Out = NodeList<'gc>;
fn visit_child_mut<V: VisitorMut<'gc>>(
self,
ctx: &'gc GCLock<'_, '_>,
visitor: &mut V,
path: Path<'gc>,
) -> TransformResult<Self::Out> {
use TransformResult::*;
let mut index = 0usize;
let mut it = self.iter();
while let Some(elem) = it.next() {
let res = visitor.call(ctx, elem, Some(path));
if let Unchanged = res {
index += 1;
continue;
}
let mut result: Vec<&'gc Node<'gc>> = self.iter().take(index).collect();
match res {
Changed(new_node) => result.push(new_node),
Expanded(new_nodes) => result.extend(new_nodes),
Removed => {}
Unchanged => unreachable!("checked above"),
}
for elem in it.by_ref() {
match visitor.call(ctx, elem, Some(path)) {
Unchanged => result.push(elem),
Changed(new_node) => result.push(new_node),
Expanded(new_nodes) => result.extend(new_nodes),
Removed => {}
}
}
return Changed(NodeList::from_iter(ctx, result));
}
Unchanged
}
fn duplicate(self) -> Self::Out {
self
}
}
impl<'gc> Node<'gc> {
pub fn visit_mut<V: VisitorMut<'gc>>(
&'gc self,
ctx: &'gc GCLock<'_, '_>,
visitor: &mut V,
path: Option<Path<'gc>>,
) -> Option<&'gc Node<'gc>> {
match visitor.call(ctx, self, path) {
TransformResult::Unchanged => Some(self),
TransformResult::Removed => None,
TransformResult::Changed(new_node) => Some(new_node),
TransformResult::Expanded(_) => panic!("cannot expand the root node into multiple"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn strictness_and_constants() {
assert_eq!(INVALID_LABEL, u32::MAX);
assert_ne!(Strictness::StrictMode, Strictness::NotSet);
fn _same(_a: NodeString, b: NodeLabel) -> NodeString { b }
}
}