use crate::ast::{Span, Spanned};
use crate::compile::ir;
use crate::compile::{IrError, IrErrorKind, IrEvalOutcome, IrValue, Item, ModMeta, PrivMetaKind};
use crate::query::{Query, Used};
use crate::runtime::{ConstValue, Object, Tuple};
pub(crate) type IrScopes = crate::shared::Scopes<IrValue>;
pub struct IrInterpreter<'a> {
pub(crate) budget: IrBudget,
pub(crate) module: &'a ModMeta,
pub(crate) item: &'a Item,
pub(crate) scopes: IrScopes,
pub(crate) q: Query<'a>,
}
impl IrInterpreter<'_> {
pub(crate) fn eval_const(&mut self, ir: &ir::Ir, used: Used) -> Result<ConstValue, IrError> {
tracing::trace!("processing constant: {}", self.item);
if let Some(const_value) = self.q.consts.get(self.item) {
return Ok(const_value.clone());
}
if !self.q.consts.mark(self.item) {
return Err(IrError::new(ir, IrErrorKind::ConstCycle));
}
let ir_value = match ir::eval_ir(ir, self, used) {
Ok(ir_value) => ir_value,
Err(outcome) => match outcome {
IrEvalOutcome::Error(error) => {
return Err(error);
}
IrEvalOutcome::NotConst(span) => {
return Err(IrError::new(span, IrErrorKind::NotConst))
}
IrEvalOutcome::Break(span, _) => {
return Err(IrError::new(span, IrErrorKind::BreakOutsideOfLoop))
}
},
};
let const_value = ir_value.into_const(ir)?;
if self
.q
.consts
.insert(self.item.clone(), const_value.clone())
.is_some()
{
return Err(IrError::new(ir, IrErrorKind::ConstCycle));
}
Ok(const_value)
}
pub(crate) fn eval_value(&mut self, ir: &ir::Ir, used: Used) -> Result<IrValue, IrError> {
match ir::eval_ir(ir, self, used) {
Ok(ir_value) => Ok(ir_value),
Err(outcome) => match outcome {
IrEvalOutcome::Error(error) => Err(error),
IrEvalOutcome::NotConst(span) => Err(IrError::new(span, IrErrorKind::NotConst)),
IrEvalOutcome::Break(span, _) => {
Err(IrError::new(span, IrErrorKind::BreakOutsideOfLoop))
}
},
}
}
pub(crate) fn resolve_var(
&mut self,
spanned: Span,
name: &str,
used: Used,
) -> Result<IrValue, IrError> {
if let Some(ir_value) = self.scopes.try_get(name) {
return Ok(ir_value.clone());
}
let mut base = self.item.clone();
loop {
let item = base.extended(name);
if let Some(const_value) = self.q.consts.get(&item) {
return Ok(IrValue::from_const(const_value));
}
if let Some(meta) = self.q.query_meta(spanned, &item, used)? {
match &meta.kind {
PrivMetaKind::Const { const_value, .. } => {
return Ok(IrValue::from_const(const_value));
}
_ => {
return Err(IrError::new(
spanned,
IrErrorKind::UnsupportedMeta { meta: meta.info() },
));
}
}
}
if base.is_empty() {
break;
}
base.pop();
}
if name.starts_with(char::is_lowercase) {
Err(IrError::new(
spanned,
IrErrorKind::MissingLocal { name: name.into() },
))
} else {
Err(IrError::new(
spanned,
IrErrorKind::MissingConst { name: name.into() },
))
}
}
pub(crate) fn call_const_fn<S>(
&mut self,
spanned: S,
target: &str,
args: Vec<IrValue>,
used: Used,
) -> Result<IrValue, IrError>
where
S: Copy + Spanned,
{
let span = spanned.span();
let mut base = self.item.clone();
let id = loop {
let item = base.extended(target);
if let Some(meta) = self.q.query_meta(span, &item, used)? {
match &meta.kind {
PrivMetaKind::ConstFn { id, .. } => {
break *id;
}
_ => {
return Err(IrError::new(
span,
IrErrorKind::UnsupportedMeta { meta: meta.info() },
));
}
}
}
if base.is_empty() {
return Err(IrError::new(spanned, IrErrorKind::FnNotFound));
}
base.pop();
};
let const_fn = self.q.const_fn_for((spanned.span(), id))?;
if const_fn.ir_fn.args.len() != args.len() {
return Err(IrError::new(
spanned,
IrErrorKind::ArgumentCountMismatch {
actual: args.len(),
expected: const_fn.ir_fn.args.len(),
},
));
}
let guard = self.scopes.isolate();
for (name, value) in const_fn.ir_fn.args.iter().zip(args) {
self.scopes.decl(&**name, value, spanned)?;
}
let value = self.eval_value(&const_fn.ir_fn.ir, used)?;
self.scopes.pop(spanned, guard)?;
Ok(value)
}
}
impl IrScopes {
pub(crate) fn get_target(&mut self, ir_target: &ir::IrTarget) -> Result<IrValue, IrError> {
match &ir_target.kind {
ir::IrTargetKind::Name(name) => Ok(self.get_name(name, ir_target)?.clone()),
ir::IrTargetKind::Field(ir_target, field) => {
let value = self.get_target(ir_target)?;
match value {
IrValue::Object(object) => {
let object = object.borrow_ref().map_err(IrError::access(ir_target))?;
if let Some(value) = object.get(field.as_ref()).cloned() {
return Ok(value);
}
}
actual => return Err(IrError::expected::<_, Tuple>(ir_target, &actual)),
};
Err(IrError::new(
ir_target,
IrErrorKind::MissingField {
field: field.clone(),
},
))
}
ir::IrTargetKind::Index(target, index) => {
let value = self.get_target(target)?;
match value {
IrValue::Vec(vec) => {
let vec = vec.borrow_ref().map_err(IrError::access(ir_target))?;
if let Some(value) = vec.get(*index).cloned() {
return Ok(value);
}
}
IrValue::Tuple(tuple) => {
let tuple = tuple.borrow_ref().map_err(IrError::access(ir_target))?;
if let Some(value) = tuple.get(*index).cloned() {
return Ok(value);
}
}
actual => return Err(IrError::expected::<_, Tuple>(ir_target, &actual)),
};
Err(IrError::new(
ir_target,
IrErrorKind::MissingIndex { index: *index },
))
}
}
}
pub(crate) fn set_target(
&mut self,
ir_target: &ir::IrTarget,
value: IrValue,
) -> Result<(), IrError> {
match &ir_target.kind {
ir::IrTargetKind::Name(name) => {
*self.get_name_mut(name.as_ref(), ir_target)? = value;
Ok(())
}
ir::IrTargetKind::Field(target, field) => {
let current = self.get_target(target)?;
match current {
IrValue::Object(object) => {
let mut object = object.borrow_mut().map_err(IrError::access(ir_target))?;
object.insert(field.as_ref().to_owned(), value);
}
actual => {
return Err(IrError::expected::<_, Object>(ir_target, &actual));
}
}
Ok(())
}
ir::IrTargetKind::Index(target, index) => {
let current = self.get_target(target)?;
match current {
IrValue::Vec(vec) => {
let mut vec = vec.borrow_mut().map_err(IrError::access(ir_target))?;
if let Some(current) = vec.get_mut(*index) {
*current = value;
return Ok(());
}
}
IrValue::Tuple(tuple) => {
let mut tuple = tuple.borrow_mut().map_err(IrError::access(ir_target))?;
if let Some(current) = tuple.get_mut(*index) {
*current = value;
return Ok(());
}
}
actual => {
return Err(IrError::expected::<_, Tuple>(ir_target, &actual));
}
};
Err(IrError::msg(ir_target, "missing index"))
}
}
}
pub(crate) fn mut_target(
&mut self,
ir_target: &ir::IrTarget,
op: impl FnOnce(&mut IrValue) -> Result<(), IrError>,
) -> Result<(), IrError> {
match &ir_target.kind {
ir::IrTargetKind::Name(name) => {
let value = self.get_name_mut(name.as_ref(), ir_target)?;
op(value)
}
ir::IrTargetKind::Field(target, field) => {
let current = self.get_target(target)?;
match current {
IrValue::Object(object) => {
let mut object = object.borrow_mut().map_err(IrError::access(ir_target))?;
let value = object.get_mut(field.as_ref()).ok_or_else(|| {
IrError::new(
ir_target,
IrErrorKind::MissingField {
field: field.clone(),
},
)
})?;
op(value)
}
actual => Err(IrError::expected::<_, Object>(ir_target, &actual)),
}
}
ir::IrTargetKind::Index(target, index) => {
let current = self.get_target(target)?;
match current {
IrValue::Vec(vec) => {
let mut vec = vec.borrow_mut().map_err(IrError::access(ir_target))?;
let value = vec.get_mut(*index).ok_or_else(|| {
IrError::new(ir_target, IrErrorKind::MissingIndex { index: *index })
})?;
op(value)
}
IrValue::Tuple(tuple) => {
let mut tuple = tuple.borrow_mut().map_err(IrError::access(ir_target))?;
let value = tuple.get_mut(*index).ok_or_else(|| {
IrError::new(ir_target, IrErrorKind::MissingIndex { index: *index })
})?;
op(value)
}
actual => Err(IrError::expected::<_, Tuple>(ir_target, &actual)),
}
}
}
}
}
pub(crate) struct IrBudget {
budget: usize,
}
impl IrBudget {
pub(crate) fn new(budget: usize) -> Self {
Self { budget }
}
pub(crate) fn take<S>(&mut self, spanned: S) -> Result<(), IrError>
where
S: Spanned,
{
if self.budget == 0 {
return Err(IrError::new(spanned, IrErrorKind::BudgetExceeded));
}
self.budget -= 1;
Ok(())
}
}