use std::{
collections::HashMap,
fmt::{self, Write},
sync::{Arc, Weak},
};
use gramatika::{Span, Spanned, Substr, Token as _};
use parking_lot::RwLock;
use crate::{decl::Decl, traversal::Walk, SyntaxTree, Token, TokenKind};
mod builder;
use builder::ScopeBuilder;
pub type Bindings = HashMap<(Substr, TokenKind), Arc<Decl>>;
pub struct Scope {
span: Span,
parent: Option<Weak<Scope>>,
children: RwLock<Vec<Arc<Scope>>>,
bindings: Arc<RwLock<Bindings>>,
}
impl Spanned for Scope {
fn span(&self) -> Span {
self.span
}
}
trait PrintKeys {
fn print_keys(&self, indent: usize) -> Result<String, fmt::Error>;
}
impl PrintKeys for Bindings {
fn print_keys(&self, indent: usize) -> Result<String, fmt::Error> {
let spacing = " ".repeat(indent);
let mut out = String::new();
let f = &mut out;
for (lexeme, kind) in self.keys() {
write!(f, "`{}` ({:?}), ", lexeme, kind)?;
}
out.pop();
out.pop();
if out.is_empty() {
return Ok(out);
}
out = out.replace(", ", &format!("\n {spacing}"));
out = std::format!("\n {spacing}{out}");
Ok(out)
}
}
impl fmt::Debug for Scope {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.print(0)?)
}
}
pub fn build(syntax_tree: &SyntaxTree) -> Arc<Scope> {
let span = syntax_tree.span();
let mut builder = ScopeBuilder::new(span);
syntax_tree.walk(&mut builder);
builder.build()
}
impl Scope {
pub fn new(span: Span) -> Self {
Self {
span,
parent: None,
children: RwLock::new(vec![]),
bindings: Arc::new(RwLock::new(HashMap::default())),
}
}
pub fn with_parent(span: Span, parent: Arc<Scope>) -> Arc<Self> {
let mut this = Self::new(span);
this.parent = Some(Arc::downgrade(&parent));
let arc_this = Arc::new(this);
parent.children.write().push(Arc::clone(&arc_this));
arc_this
}
pub fn parent(&self) -> Option<Arc<Scope>> {
self.parent.as_ref().and_then(|env| env.upgrade())
}
pub fn find_decl(
&self,
token: &Token,
debug: bool, ) -> Option<Arc<Decl>> {
let (lexeme, span) = token.as_inner();
let kind = token.kind();
if !self.span.contains(span) {
None
} else {
if debug {
eprintln!(
"Looking for `{lexeme}` ({kind:?}) ({span:?}) in scope:\n ({:?}){}",
self.span(),
self.bindings.read().print_keys(1).unwrap(),
);
}
if self.bindings.read().contains_key(&(lexeme.clone(), kind)) {
self.bindings.read().get(&(lexeme, kind)).cloned()
} else {
self.children
.read()
.iter()
.find_map(|env| env.find_decl(token, debug))
.clone()
}
}
}
pub fn find_field_decl(&self, token: &Token, struct_name: &Token) -> Option<Arc<Decl>> {
let (lexeme, span) = token.as_inner();
let field_key = &(lexeme, token.kind());
let struct_key = &(struct_name.lexeme(), TokenKind::Ident);
if !self.span.contains(span) {
None
} else if self.bindings.read().contains_key(field_key)
&& self.bindings.read().contains_key(struct_key)
{
self.bindings.read().get(field_key).cloned()
} else {
self.children
.read()
.iter()
.find_map(|env| env.find_field_decl(token, struct_name))
.clone()
}
}
pub fn find(&self, token: &Token) -> Option<Arc<Scope>> {
let (lexeme, span) = token.as_inner();
let kind = token.kind();
if !self.span.contains(span) {
return None;
}
self.children
.read()
.iter()
.find_map(|scope| {
if scope.span.contains(span)
&& scope.bindings.read().contains_key(&(lexeme.clone(), kind))
{
Some(Arc::clone(scope))
} else {
None
}
})
.or_else(|| {
self.children
.read()
.iter()
.find_map(|scope| scope.find(token))
})
}
fn define(&self, ident: (Substr, TokenKind), decl: Decl) {
self.bindings.write().insert(ident, Arc::new(decl));
}
fn print(&self, indent: usize) -> Result<String, fmt::Error> {
let mut out = String::new();
let spacing = " ".repeat(indent);
writeln!(
&mut out,
"{spacing}({:?}) {}",
self.span,
self.bindings.read().print_keys(indent)?
)?;
for child in self.children.read().iter() {
write!(&mut out, "{}", child.print(indent + 1)?)?;
}
Ok(out)
}
}