use tree_sitter::Node;
use super::walk::{Spec, dispatch};
use super::{IntroKind, Model, Write};
pub trait Backend {
fn spec(&self) -> &'static Spec;
fn model(&mut self) -> &mut Model;
fn text_of(&self, n: Node) -> &str;
fn custom(&mut self, n: Node, scope: usize);
fn bind_var(&mut self, name_node: Node, scope: usize, w: Write, intro: IntroKind) {
let name = self.text_of(name_node).to_string();
self.model().bind(scope, &name, w, intro);
}
fn bind_declarator_with_rhs_field(&mut self, n: Node, scope: usize)
where
Self: Sized,
{
if let Some(name) = n.child_by_field_name("name")
&& name.kind() == "identifier"
{
self.bind_var(
name,
scope,
Write::assign(
name.start_byte(),
name.id(),
n.child_by_field_name("value")
.map(|v| v.id())
.or_else(|| rhs_after_eq(self as &dyn Backend, n).map(|v| v.id())),
),
IntroKind::Assign,
);
}
}
fn walk_children(&mut self, n: Node, scope: usize)
where
Self: Sized,
{
let mut cursor = n.walk();
for child in n.children(&mut cursor) {
dispatch(self, child, scope);
}
}
fn walk_children_excluding_field(&mut self, n: Node, scope: usize, field: &str)
where
Self: Sized,
{
let excluded = n.child_by_field_name(field).map(|c| c.id());
let mut cursor = n.walk();
for child in n.children(&mut cursor) {
if Some(child.id()) != excluded {
dispatch(self, child, scope);
}
}
}
fn rebind_local(&mut self, left: Node, scope: usize, plain: bool, rhs: Option<usize>) -> bool {
let name = self.text_of(left).to_string();
if self
.model()
.lookup(scope, left.start_byte(), &name)
.is_none()
{
return false;
}
let (w, intro) = write_for_rebind(left, plain, rhs);
self.model().bind(scope, &name, w, intro);
if !plain {
self.model().record_read(scope, &name, left.end_byte());
}
true
}
}
fn write_for_rebind(left: Node, plain: bool, rhs: Option<usize>) -> (Write, IntroKind) {
if plain {
(
Write::assign(left.start_byte(), left.id(), rhs),
IntroKind::Assign,
)
} else {
(
Write::rewrite(left.start_byte(), left.id()),
IntroKind::Binding,
)
}
}
fn rhs_after_eq<'a>(b: &'a dyn Backend, n: Node<'a>) -> Option<Node<'a>> {
let mut cursor = n.walk();
let mut after_eq = false;
n.children(&mut cursor).find(|ch| {
if !ch.is_named() && b.text_of(*ch) == "=" {
after_eq = true;
false
} else {
after_eq && ch.is_named()
}
})
}