use crate::ast::Span;
use crate::collections::HashMap;
use crate::compile::v1::Assembler;
use crate::compile::{Assembly, CompileError, CompileErrorKind, CompileResult, CompileVisitor};
use crate::runtime::Inst;
use crate::SourceId;
#[derive(Debug, Clone, Copy)]
pub struct Var {
pub(crate) offset: usize,
span: Span,
moved_at: Option<Span>,
}
impl Var {
pub(crate) fn copy<C>(&self, c: &mut Assembler<'_>, span: Span, comment: C)
where
C: AsRef<str>,
{
c.asm.push_with_comment(
Inst::Copy {
offset: self.offset,
},
span,
comment,
);
}
pub(crate) fn do_move<C>(&self, asm: &mut Assembly, span: Span, comment: C)
where
C: AsRef<str>,
{
asm.push_with_comment(
Inst::Move {
offset: self.offset,
},
span,
comment,
);
}
}
#[derive(Debug, Clone)]
pub(crate) struct Scope {
locals: HashMap<String, Var>,
pub(crate) total_var_count: usize,
pub(crate) local_var_count: usize,
}
impl Scope {
fn new() -> Scope {
Self {
locals: HashMap::new(),
total_var_count: 0,
local_var_count: 0,
}
}
fn child(&self) -> Self {
Self {
locals: HashMap::new(),
total_var_count: self.total_var_count,
local_var_count: 0,
}
}
fn new_var(&mut self, name: &str, span: Span) -> CompileResult<usize> {
let offset = self.total_var_count;
let local = Var {
offset,
span,
moved_at: None,
};
self.total_var_count += 1;
self.local_var_count += 1;
if let Some(old) = self.locals.insert(name.to_owned(), local) {
return Err(CompileError::new(
span,
CompileErrorKind::VariableConflict {
name: name.to_owned(),
existing_span: old.span,
},
));
}
Ok(offset)
}
fn decl_var(&mut self, name: &str, span: Span) -> usize {
let offset = self.total_var_count;
tracing::trace!("decl {} => {}", name, offset);
self.locals.insert(
name.to_owned(),
Var {
offset,
span,
moved_at: None,
},
);
self.total_var_count += 1;
self.local_var_count += 1;
offset
}
fn decl_anon(&mut self, _span: Span) -> usize {
let offset = self.total_var_count;
self.total_var_count += 1;
self.local_var_count += 1;
offset
}
pub(crate) fn undecl_anon(&mut self, span: Span, n: usize) -> CompileResult<()> {
self.total_var_count = self
.total_var_count
.checked_sub(n)
.ok_or_else(|| CompileError::msg(&span, "totals out of bounds"))?;
self.local_var_count = self
.local_var_count
.checked_sub(n)
.ok_or_else(|| CompileError::msg(&span, "locals out of bounds"))?;
Ok(())
}
fn get(&self, name: &str, span: Span) -> CompileResult<Option<Var>> {
if let Some(var) = self.locals.get(name) {
if let Some(moved_at) = var.moved_at {
return Err(CompileError::new(
span,
CompileErrorKind::VariableMoved { moved_at },
));
}
return Ok(Some(*var));
}
Ok(None)
}
fn take(&mut self, name: &str, span: Span) -> CompileResult<Option<&Var>> {
if let Some(var) = self.locals.get_mut(name) {
if let Some(moved_at) = var.moved_at {
return Err(CompileError::new(
span,
CompileErrorKind::VariableMoved { moved_at },
));
}
var.moved_at = Some(span);
return Ok(Some(var));
}
Ok(None)
}
}
#[must_use]
pub(crate) struct ScopeGuard(usize);
pub(crate) struct Scopes {
scopes: Vec<Scope>,
}
impl Scopes {
pub(crate) fn new() -> Self {
Self {
scopes: vec![Scope::new()],
}
}
pub(crate) fn try_get_var(
&self,
visitor: &mut dyn CompileVisitor,
name: &str,
source_id: SourceId,
span: Span,
) -> CompileResult<Option<Var>> {
tracing::trace!("get var: {}", name);
for scope in self.scopes.iter().rev() {
if let Some(var) = scope.get(name, span)? {
tracing::trace!("found var: {} => {:?}", name, var);
visitor.visit_variable_use(source_id, var.span, span);
return Ok(Some(var));
}
}
Ok(None)
}
pub(crate) fn try_take_var(
&mut self,
visitor: &mut dyn CompileVisitor,
name: &str,
source_id: SourceId,
span: Span,
) -> CompileResult<Option<&Var>> {
tracing::trace!("get var: {}", name);
for scope in self.scopes.iter_mut().rev() {
if let Some(var) = scope.take(name, span)? {
tracing::trace!("found var: {} => {:?}", name, var);
visitor.visit_variable_use(source_id, var.span, span);
return Ok(Some(var));
}
}
Ok(None)
}
pub(crate) fn get_var(
&self,
visitor: &mut dyn CompileVisitor,
name: &str,
source_id: SourceId,
span: Span,
) -> CompileResult<Var> {
match self.try_get_var(visitor, name, source_id, span)? {
Some(var) => Ok(var),
None => Err(CompileError::new(
span,
CompileErrorKind::MissingLocal {
name: name.to_owned(),
},
)),
}
}
pub(crate) fn take_var(
&mut self,
visitor: &mut dyn CompileVisitor,
name: &str,
source_id: SourceId,
span: Span,
) -> CompileResult<&Var> {
match self.try_take_var(visitor, name, source_id, span)? {
Some(var) => Ok(var),
None => Err(CompileError::new(
span,
CompileErrorKind::MissingLocal {
name: name.to_owned(),
},
)),
}
}
pub(crate) fn new_var(&mut self, name: &str, span: Span) -> CompileResult<usize> {
self.last_mut(span)?.new_var(name, span)
}
pub(crate) fn decl_var(&mut self, name: &str, span: Span) -> CompileResult<usize> {
Ok(self.last_mut(span)?.decl_var(name, span))
}
pub(crate) fn decl_anon(&mut self, span: Span) -> CompileResult<usize> {
Ok(self.last_mut(span)?.decl_anon(span))
}
pub(crate) fn undecl_anon(&mut self, span: Span, n: usize) -> CompileResult<()> {
self.last_mut(span)?.undecl_anon(span, n)
}
pub(crate) fn push(&mut self, scope: Scope) -> ScopeGuard {
self.scopes.push(scope);
ScopeGuard(self.scopes.len())
}
pub(crate) fn pop(&mut self, expected: ScopeGuard, span: Span) -> CompileResult<Scope> {
let ScopeGuard(expected) = expected;
if self.scopes.len() != expected {
return Err(CompileError::msg(
&span,
"the number of scopes do not match",
));
}
self.pop_unchecked(span)
}
pub(crate) fn pop_last(&mut self, span: Span) -> CompileResult<Scope> {
self.pop(ScopeGuard(1), span)
}
pub(crate) fn pop_unchecked(&mut self, span: Span) -> CompileResult<Scope> {
let scope = self
.scopes
.pop()
.ok_or_else(|| CompileError::msg(&span, "missing parent scope"))?;
Ok(scope)
}
pub(crate) fn push_child(&mut self, span: Span) -> CompileResult<ScopeGuard> {
let scope = self.last(span)?.child();
Ok(self.push(scope))
}
pub(crate) fn child(&mut self, span: Span) -> CompileResult<Scope> {
Ok(self.last(span)?.child())
}
pub(crate) fn local_var_count(&self, span: Span) -> CompileResult<usize> {
Ok(self.last(span)?.local_var_count)
}
pub(crate) fn total_var_count(&self, span: Span) -> CompileResult<usize> {
Ok(self.last(span)?.total_var_count)
}
fn last(&self, span: Span) -> CompileResult<&Scope> {
self.scopes
.last()
.ok_or_else(|| CompileError::msg(&span, "missing head of locals"))
}
fn last_mut(&mut self, span: Span) -> CompileResult<&mut Scope> {
self.scopes
.last_mut()
.ok_or_else(|| CompileError::msg(&span, "missing head of locals"))
}
}