use std::collections::hash_map::{HashMap, Entry};
use crate::symbol::{self, Symbol};
use super::{
mem::{Capture, FrameInfo, SlotIx},
Error,
SourcePos,
};
#[derive(Debug, Default)]
struct Scope {
variables: HashMap<Symbol, SlotIx>,
}
impl Scope {
fn declare(&mut self, symbol: Symbol, ix: SlotIx) -> bool {
match self.variables.entry(symbol) {
Entry::Occupied(_) => false,
Entry::Vacant(entry) => {
entry.insert(ix);
true
}
}
}
fn resolve(&mut self, symbol: Symbol) -> Option<SlotIx> {
self.variables
.get(&symbol)
.copied()
}
}
#[derive(Debug)]
struct Frame {
slots: SlotIx,
captures: Vec<Capture>,
self_slot: Option<SlotIx>,
scopes: Vec<Scope>,
}
impl Frame {
fn new() -> Self {
Self {
slots: SlotIx(0),
captures: Vec::new(),
self_slot: None,
scopes: Vec::new(),
}
}
fn enter_block(&mut self) {
self.scopes.push(Scope::default());
}
fn exit_block(&mut self) {
self.scopes
.pop()
.expect("attempt to exit empty stack");
}
fn declare(&mut self, symbol: Symbol, pos: SourcePos) -> Result<SlotIx, Error> {
let scope = self.scopes.last_mut().expect("attempt to declare in empty stack");
if scope.declare(symbol, self.slots) {
Ok(self.slots.bump())
} else {
Err(Error::duplicate_variable(symbol, pos))
}
}
fn resolve(&mut self, symbol: Symbol) -> Option<SlotIx> {
self.scopes
.iter_mut()
.rev()
.find_map(
|scope| scope.resolve(symbol)
)
}
fn capture(&mut self, symbol: Symbol, parent_slot_ix: SlotIx) -> SlotIx {
let scope = self.scopes.first_mut().expect("frame missing root scope");
match scope.variables.entry(symbol) {
Entry::Occupied(entry) => *entry.get(),
Entry::Vacant(entry) => {
let slot_ix = self.slots.bump();
entry.insert(slot_ix);
self.captures.push(
Capture {
from: parent_slot_ix,
to: slot_ix,
}
);
slot_ix
}
}
}
fn resolve_or_insert_self(&mut self) -> SlotIx {
match self.self_slot {
Some(slot_ix) => slot_ix,
None => {
let slot_ix = self.slots.bump();
self.self_slot = Some(slot_ix);
slot_ix
}
}
}
}
impl Drop for Frame {
fn drop(&mut self) {
debug_assert!(self.scopes.is_empty())
}
}
impl From<Frame> for FrameInfo {
fn from(mut frame: Frame) -> FrameInfo {
FrameInfo {
slots: frame.slots,
captures: std::mem::take(&mut frame.captures).into(),
self_slot: frame.self_slot,
}
}
}
#[derive(Debug, Default)]
pub struct Stack {
frames: Vec<Frame>,
}
impl Stack {
pub fn enter_frame(&mut self) {
let mut frame = Frame::new();
frame.enter_block();
self.frames.push(frame);
}
pub fn exit_frame(&mut self) -> FrameInfo {
let mut frame = self.frames
.pop()
.expect("attempt to exit empty stack");
frame.exit_block();
debug_assert!(frame.scopes.is_empty());
frame.into()
}
pub fn enter_block(&mut self) {
self.top().enter_block()
}
pub fn exit_block(&mut self) {
self.top().exit_block()
}
pub fn declare(&mut self, symbol: Symbol, pos: SourcePos) -> Result<SlotIx, Error> {
self.top().declare(symbol, pos)
}
pub fn resolve(
&mut self,
symbol: Symbol,
pos: SourcePos,
interner: &mut symbol::Interner,
) -> Result<SlotIx, Error> {
let (frame_ix, mut slot_ix) = self.frames
.iter_mut()
.enumerate()
.rev()
.find_map(
|(frame_ix, frame)| {
let slot_ix = frame.resolve(symbol)?;
Some((frame_ix, slot_ix))
}
)
.ok_or_else(
|| Error::undeclared_variable(symbol, pos)
)?;
if frame_ix == self.frames.len() - 1 { return Ok(slot_ix)
} else { let symbol_captured = {
let mut identifier = interner
.resolve(symbol)
.expect("unresolved symbol")
.to_owned();
identifier.extend(b"@closed");
interner.get_or_intern(identifier)
};
let range = frame_ix + 1 .. self.frames.len();
for frame in &mut self.frames[range] {
slot_ix = frame.capture(symbol_captured, slot_ix);
}
}
Ok(slot_ix)
}
pub fn resolve_or_insert_self(&mut self) -> SlotIx {
self.top().resolve_or_insert_self()
}
fn top(&mut self) -> &mut Frame {
self.frames.last_mut().expect("empty stack")
}
}
impl Drop for Stack {
fn drop(&mut self) {
debug_assert!(self.frames.is_empty())
}
}