use crate::ast;
use crate::ast::{Span, Spanned};
use crate::compile::{
ir, Assembly, CompileError, CompileErrorKind, CompileResult, IrBudget, IrCompiler,
IrInterpreter, Item, ItemMeta, Options, PrivMeta,
};
use crate::query::{Named, Query, QueryConstFn, Used};
use crate::runtime::{ConstValue, Inst};
use crate::{Context, Diagnostics, SourceId};
pub(crate) mod assemble;
mod loops;
mod scopes;
pub(crate) use self::loops::{Loop, Loops};
pub(crate) use self::scopes::{Scope, ScopeGuard, Scopes, Var};
#[derive(Debug, Clone, Copy)]
pub(crate) enum Needs {
Type,
Value,
None,
}
impl Needs {
pub(crate) fn value(self) -> bool {
matches!(self, Self::Type | Self::Value)
}
}
pub(crate) struct Assembler<'a> {
pub(crate) source_id: SourceId,
pub(crate) context: &'a Context,
pub(crate) q: Query<'a>,
pub(crate) asm: &'a mut Assembly,
pub(crate) scopes: Scopes,
pub(crate) contexts: Vec<Span>,
pub(crate) loops: Loops,
pub(crate) options: &'a Options,
pub(crate) diagnostics: &'a mut Diagnostics,
}
impl<'a> Assembler<'a> {
pub fn try_lookup_meta(
&mut self,
spanned: Span,
item: &Item,
) -> CompileResult<Option<PrivMeta>> {
tracing::trace!("lookup meta: {:?}", item);
if let Some(meta) = self.q.query_meta(spanned, item, Default::default())? {
tracing::trace!("found in query: {:?}", meta);
self.q
.visitor
.visit_meta(self.source_id, meta.info_ref(), spanned);
return Ok(Some(meta));
}
if let Some(meta) = self.context.lookup_meta(item) {
tracing::trace!("found in context: {:?}", meta);
self.q
.visitor
.visit_meta(self.source_id, meta.info_ref(), spanned);
return Ok(Some(meta));
}
Ok(None)
}
pub fn lookup_meta(&mut self, spanned: Span, item: &Item) -> CompileResult<PrivMeta> {
if let Some(meta) = self.try_lookup_meta(spanned, item)? {
return Ok(meta);
}
Err(CompileError::new(
spanned,
CompileErrorKind::MissingItem { item: item.clone() },
))
}
pub(crate) fn locals_pop(&mut self, total_var_count: usize, span: Span) {
match total_var_count {
0 => (),
1 => {
self.asm.push(Inst::Pop, span);
}
count => {
self.asm.push(Inst::PopN { count }, span);
}
}
}
pub(crate) fn locals_clean(&mut self, total_var_count: usize, span: Span) {
match total_var_count {
0 => (),
count => {
self.asm.push(Inst::Clean { count }, span);
}
}
}
pub(crate) fn convert_path<'ast>(
&mut self,
path: &'ast ast::Path,
) -> CompileResult<Named<'ast>> {
self.q.convert_path(self.context, path)
}
pub(crate) fn clean_last_scope(
&mut self,
span: Span,
expected: ScopeGuard,
needs: Needs,
) -> CompileResult<()> {
let scope = self.scopes.pop(expected, span)?;
if needs.value() {
self.locals_clean(scope.local_var_count, span);
} else {
self.locals_pop(scope.local_var_count, span);
}
Ok(())
}
pub(crate) fn context(&self) -> Option<Span> {
self.contexts.last().copied()
}
pub(crate) fn call_const_fn<S>(
&mut self,
spanned: S,
meta: &PrivMeta,
from: &ItemMeta,
query_const_fn: &QueryConstFn,
args: &[(ast::Expr, Option<T![,]>)],
) -> Result<ConstValue, CompileError>
where
S: Copy + Spanned,
{
if query_const_fn.ir_fn.args.len() != args.len() {
return Err(CompileError::new(
spanned,
CompileErrorKind::UnsupportedArgumentCount {
meta: meta.info(),
expected: query_const_fn.ir_fn.args.len(),
actual: args.len(),
},
));
}
let mut compiler = IrCompiler { q: self.q.borrow() };
let mut compiled = Vec::new();
for ((a, _), name) in args.iter().zip(&query_const_fn.ir_fn.args) {
compiled.push((ir::compile::expr(a, &mut compiler)?, name));
}
let mut interpreter = IrInterpreter {
budget: IrBudget::new(1_000_000),
scopes: Default::default(),
module: &from.module,
item: &from.item,
q: self.q.borrow(),
};
for (ir, name) in compiled {
let value = interpreter.eval_value(&ir, Used::Used)?;
interpreter.scopes.decl(name, value, spanned)?;
}
interpreter.module = &query_const_fn.item.module;
interpreter.item = &query_const_fn.item.item;
let value = interpreter.eval_value(&query_const_fn.ir_fn.ir, Used::Used)?;
Ok(value.into_const(spanned)?)
}
}