arrow-parser 0.0.2

Parser for the Arrow programming language
Documentation
use rustc_hash::FxHashMap;
use rustc_hash::FxHashSet;
use serde::Deserialize;
use serde::Serialize;
use std::cell::Cell;
use std::cell::RefCell;
use std::cell::RefMut;
use std::rc::Rc;

#[derive(Clone, Default)]
pub struct SymbolIdGenerator(Rc<Cell<usize>>);

impl SymbolIdGenerator {
  pub fn next(&self) -> usize {
    let id = self.0.get();
    self.0.set(id + 1);
    id
  }
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct Symbol {
  pub id: usize, // Variable names aren't good enough; they can be shadowed, redeclared, etc. We want something to uniquely identify every declared variable within a program.
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum ScopeType {
  Module,
  Closure,
  Block,
}

#[derive(Default)]
pub struct ScopeData {
  parent: Option<Scope>,
  pub symbols: FxHashMap<String, Symbol>,
  pub boxed_var_decls: FxHashSet<Symbol>, // Used on closure and block scope types. These variables must allocate a new box every time they reach their declaration. Entries should also exist as keys in `symbols`.
  pub boxed_var_captures: FxHashSet<Symbol>, // Used only on closure scope types. Before entering the closure associated with this scope, capture these boxes from the present scope.
}

#[derive(Clone)]
pub struct Scope {
  pub typ: ScopeType,
  symbol_id_generator: SymbolIdGenerator,
  data: Rc<RefCell<ScopeData>>,
}

impl Scope {
  pub fn new(typ: ScopeType) -> Self {
    Self {
      typ,
      data: Default::default(),
      symbol_id_generator: Default::default(),
    }
  }

  pub fn new_child(&self, typ: ScopeType) -> Self {
    Self {
      typ,
      symbol_id_generator: self.symbol_id_generator.clone(),
      data: Rc::new(RefCell::new(ScopeData {
        parent: Some(self.clone()),
        ..Default::default()
      })),
    }
  }

  pub fn add_symbol(&self, name: String) -> bool {
    self
      .data
      .borrow_mut()
      .symbols
      .insert(name, Symbol {
        id: self.symbol_id_generator.next(),
      })
      .is_none()
  }

  pub fn has_symbol(&self, name: &str) -> bool {
    self.data.borrow().symbols.contains_key(name)
  }

  pub fn get_symbol(&self, name: &str) -> Symbol {
    self.data.borrow().symbols.get(name).unwrap().clone()
  }

  pub fn declaration_is_boxed(&self, symbol: Symbol) -> bool {
    self.data.borrow().boxed_var_decls.contains(&symbol)
  }

  pub fn captures_box_of(&self, symbol: Symbol) -> bool {
    self.data.borrow().boxed_var_captures.contains(&symbol)
  }

  pub fn borrow(&self) -> RefMut<ScopeData> {
    self.data.borrow_mut()
  }

  pub fn borrow_mut(&self) -> RefMut<ScopeData> {
    self.data.borrow_mut()
  }

  pub fn find_symbol_declaration(&self, name: &str) -> Option<(Scope, Symbol)> {
    let mut cur = Some(self.clone());
    while let Some(scope) = cur {
      if let Some(symbol) = scope.data.borrow_mut().symbols.get(name) {
        return Some((scope.clone(), *symbol));
      };
      cur = scope.parent();
    }
    None
  }

  pub fn find_nearest_scope<P: Fn(ScopeType) -> bool>(&self, pred: P) -> Option<Scope> {
    let mut cur = Some(self.clone());
    while let Some(scope) = cur {
      if pred(scope.typ) {
        return Some(scope.clone());
      };
      cur = scope.parent();
    }
    None
  }

  pub fn find_nearest_closure(&self) -> Option<Scope> {
    self.find_nearest_scope(|t| t == ScopeType::Closure)
  }

  pub fn parent(&self) -> Option<Scope> {
    self.data.borrow().parent.clone()
  }
}

impl PartialEq for Scope {
  fn eq(&self, other: &Self) -> bool {
    Rc::ptr_eq(&self.data, &other.data)
  }
}

impl Eq for Scope {}