pub(crate) mod compiler;
mod eval;
mod interpreter;
pub(crate) mod scopes;
use core::ops::{AddAssign, MulAssign, ShlAssign, ShrAssign, SubAssign};
use crate as rune;
use crate::alloc::prelude::*;
use crate::alloc::{Box, Vec};
use crate::ast::{self, Span, Spanned};
use crate::compile::ir;
use crate::compile::{self, ItemId, WithSpan};
use crate::hir;
use crate::indexing::index;
use crate::macros::MacroContext;
use crate::query::Used;
use crate::runtime::{Inline, Value};
pub(crate) use self::compiler::Ctxt;
pub(crate) use self::eval::{eval_ir, EvalOutcome};
pub(crate) use self::interpreter::{Budget, Interpreter};
pub(crate) use self::scopes::Scopes;
impl ast::Expr {
pub(crate) fn eval(&self, cx: &mut MacroContext<'_, '_, '_>) -> compile::Result<Value> {
let mut expr = self.try_clone()?;
index::expr(cx.idx, &mut expr)?;
let ir = {
let arena = hir::Arena::new();
let mut hir_ctx =
hir::Ctxt::with_const(&arena, cx.idx.q.borrow(), cx.item_meta.location.source_id)?;
let hir = hir::lowering::expr(&mut hir_ctx, &expr)?;
let mut cx = Ctxt {
source_id: cx.item_meta.location.source_id,
q: cx.idx.q.borrow(),
};
compiler::expr(&hir, &mut cx)?
};
let mut ir_interpreter = Interpreter {
budget: Budget::new(1_000_000),
scopes: Scopes::new()?,
module: cx.item_meta.module,
item: cx.item_meta.item,
q: cx.idx.q.borrow(),
};
ir_interpreter.eval_value(&ir, Used::Used)
}
}
macro_rules! decl_kind {
(
$(#[$meta:meta])*
$vis:vis enum $name:ident {
$($(#[$field_meta:meta])* $variant:ident($ty:ty)),* $(,)?
}
) => {
$(#[$meta])*
$vis enum $name {
$($(#[$field_meta])* $variant($ty),)*
}
$(
impl From<$ty> for $name {
fn from(value: $ty) -> $name {
$name::$variant(value)
}
}
)*
}
}
#[derive(Debug, TryClone, Spanned)]
pub(crate) struct Ir {
#[rune(span)]
pub(crate) span: Span,
pub(crate) kind: IrKind,
}
impl Ir {
pub(crate) fn new<S, K>(spanned: S, kind: K) -> Self
where
S: Spanned,
IrKind: From<K>,
{
Self {
span: spanned.span(),
kind: IrKind::from(kind),
}
}
}
#[derive(Debug, TryClone, Spanned)]
pub(crate) struct IrTarget {
#[rune(span)]
pub(crate) span: Span,
pub(crate) kind: IrTargetKind,
}
#[derive(Debug, TryClone)]
pub(crate) enum IrTargetKind {
Name(hir::Variable),
Field(Box<IrTarget>, Box<str>),
Index(Box<IrTarget>, usize),
}
decl_kind! {
#[derive(Debug, TryClone)]
pub(crate) enum IrKind {
Scope(IrScope),
Binary(IrBinary),
Decl(IrDecl),
Set(IrSet),
Assign(IrAssign),
Template(IrTemplate),
Name(hir::Variable),
Target(IrTarget),
Value(Value),
Branches(IrBranches),
Loop(IrLoop),
Break(IrBreak),
Vec(IrVec),
Tuple(Tuple),
Object(IrObject),
Call(IrCall),
}
}
#[derive(Debug, TryClone, Spanned)]
pub(crate) struct IrFn {
#[rune(span)]
pub(crate) span: Span,
pub(crate) args: Vec<hir::Variable>,
pub(crate) ir: Ir,
}
impl IrFn {
pub(crate) fn compile_ast(
hir: &hir::ItemFn<'_>,
cx: &mut Ctxt<'_, '_>,
) -> compile::Result<Self> {
let mut args = Vec::new();
for arg in hir.args {
if let hir::FnArg::Pat(hir::PatBinding {
pat:
hir::Pat {
kind: hir::PatKind::Path(&hir::PatPathKind::Ident(name)),
..
},
..
}) = arg
{
args.try_push(name)?;
continue;
}
return Err(compile::Error::msg(arg, "Unsupported argument in const fn"));
}
let ir_scope = compiler::block(&hir.body, cx)?;
Ok(ir::IrFn {
span: hir.span(),
args,
ir: ir::Ir::new(hir.span(), ir_scope),
})
}
}
#[derive(Debug, TryClone, Spanned)]
pub(crate) struct IrScope {
#[rune(span)]
pub(crate) span: Span,
pub(crate) instructions: Vec<Ir>,
pub(crate) last: Option<Box<Ir>>,
}
#[derive(Debug, TryClone, Spanned)]
pub(crate) struct IrBinary {
#[rune(span)]
pub(crate) span: Span,
pub(crate) op: IrBinaryOp,
pub(crate) lhs: Box<Ir>,
pub(crate) rhs: Box<Ir>,
}
#[derive(Debug, TryClone, Spanned)]
pub(crate) struct IrDecl {
#[rune(span)]
pub(crate) span: Span,
pub(crate) name: hir::Variable,
pub(crate) value: Box<Ir>,
}
#[derive(Debug, TryClone, Spanned)]
pub(crate) struct IrSet {
#[rune(span)]
pub(crate) span: Span,
pub(crate) target: IrTarget,
pub(crate) value: Box<Ir>,
}
#[derive(Debug, TryClone, Spanned)]
pub(crate) struct IrAssign {
#[rune(span)]
pub(crate) span: Span,
pub(crate) target: IrTarget,
pub(crate) value: Box<Ir>,
pub(crate) op: IrAssignOp,
}
#[derive(Debug, TryClone, Spanned)]
pub(crate) struct IrTemplate {
#[rune(span)]
pub(crate) span: Span,
pub(crate) components: Vec<IrTemplateComponent>,
}
#[derive(Debug, TryClone)]
pub(crate) enum IrTemplateComponent {
Ir(Ir),
String(Box<str>),
}
#[derive(Debug, TryClone, Spanned)]
pub(crate) struct IrBranches {
#[rune(span)]
pub(crate) span: Span,
pub(crate) branches: Vec<(IrCondition, IrScope)>,
pub(crate) default_branch: Option<IrScope>,
}
#[derive(Debug, TryClone, Spanned)]
pub(crate) enum IrCondition {
Ir(Ir),
Let(IrLet),
}
#[derive(Debug, TryClone, Spanned)]
pub(crate) struct IrLet {
#[rune(span)]
pub(crate) span: Span,
pub(crate) pat: IrPat,
pub(crate) ir: Ir,
}
#[derive(Debug, TryClone)]
pub(crate) enum IrPat {
Ignore,
Binding(hir::Variable),
}
impl IrPat {
fn compile_ast(hir: &hir::Pat<'_>) -> compile::Result<Self> {
match hir.kind {
hir::PatKind::Ignore => return Ok(ir::IrPat::Ignore),
hir::PatKind::Path(&hir::PatPathKind::Ident(name)) => {
return Ok(ir::IrPat::Binding(name));
}
_ => (),
}
Err(compile::Error::msg(hir, "pattern not supported yet"))
}
fn matches<S>(
&self,
interp: &mut Interpreter<'_, '_>,
value: Value,
spanned: S,
) -> Result<bool, ir::EvalOutcome>
where
S: Spanned,
{
match self {
IrPat::Ignore => Ok(true),
IrPat::Binding(name) => {
interp.scopes.decl(*name, value).with_span(spanned)?;
Ok(true)
}
}
}
}
#[derive(Debug, TryClone, Spanned)]
pub(crate) struct IrLoop {
#[rune(span)]
pub(crate) span: Span,
pub(crate) label: Option<Box<str>>,
pub(crate) condition: Option<Box<IrCondition>>,
pub(crate) body: IrScope,
}
#[derive(Debug, TryClone, Spanned)]
pub(crate) struct IrBreak {
#[rune(span)]
pub(crate) span: Span,
pub(crate) label: Option<Box<str>>,
pub(crate) expr: Option<Box<Ir>>,
}
impl IrBreak {
fn compile_ast(
span: Span,
cx: &mut Ctxt<'_, '_>,
hir: &hir::ExprBreak,
) -> compile::Result<Self> {
let label = hir.label.map(TryInto::try_into).transpose()?;
let expr = match hir.expr {
Some(e) => Some(Box::try_new(compiler::expr(e, cx)?)?),
None => None,
};
Ok(ir::IrBreak { span, label, expr })
}
fn as_outcome(&self, interp: &mut Interpreter<'_, '_>, used: Used) -> ir::EvalOutcome {
let span = self.span();
if let Err(e) = interp.budget.take(span) {
return e.into();
}
let expr = match &self.expr {
Some(ir) => match ir::eval_ir(ir, interp, used) {
Ok(value) => Some(value),
Err(err) => return err,
},
None => None,
};
let label = match self.label.try_clone() {
Ok(label) => label,
Err(error) => return error.into(),
};
ir::EvalOutcome::Break(span, label, expr)
}
}
#[derive(Debug, TryClone, Spanned)]
pub(crate) struct Tuple {
#[rune(span)]
pub(crate) span: Span,
pub(crate) items: Box<[Ir]>,
}
#[derive(Debug, TryClone, Spanned)]
pub(crate) struct IrObject {
#[rune(span)]
pub(crate) span: Span,
pub(crate) assignments: Box<[(Box<str>, Ir)]>,
}
#[derive(Debug, TryClone, Spanned)]
pub(crate) struct IrCall {
#[rune(span)]
pub(crate) span: Span,
pub(crate) args: Vec<Ir>,
pub(crate) id: ItemId,
}
#[derive(Debug, TryClone, Spanned)]
pub(crate) struct IrVec {
#[rune(span)]
pub(crate) span: Span,
pub(crate) items: Box<[Ir]>,
}
#[derive(Debug, TryClone, Clone, Copy)]
#[try_clone(copy)]
pub(crate) enum IrBinaryOp {
Add,
Sub,
Mul,
Div,
Shl,
Shr,
Lt,
Lte,
Eq,
Gt,
Gte,
}
#[derive(Debug, TryClone, Clone, Copy)]
#[try_clone(copy)]
pub(crate) enum IrAssignOp {
Add,
Sub,
Mul,
Div,
Shl,
Shr,
}
impl IrAssignOp {
pub(crate) fn assign<S>(
self,
spanned: S,
target: &mut Value,
operand: Value,
) -> compile::Result<()>
where
S: Copy + Spanned,
{
if let Some(Inline::Signed(target)) = target.as_inline_mut() {
if let Some(Inline::Signed(operand)) = operand.as_inline() {
return self.assign_int(spanned, target, *operand);
}
}
Err(compile::Error::msg(spanned, "unsupported operands"))
}
fn assign_int<S>(self, spanned: S, target: &mut i64, operand: i64) -> compile::Result<()>
where
S: Copy + Spanned,
{
match self {
IrAssignOp::Add => {
target.add_assign(operand);
}
IrAssignOp::Sub => {
target.sub_assign(operand);
}
IrAssignOp::Mul => {
target.mul_assign(operand);
}
IrAssignOp::Div => {
*target = target
.checked_div(operand)
.ok_or("division by zero")
.with_span(spanned)?;
}
IrAssignOp::Shl => {
let operand = u32::try_from(operand)
.map_err(|_| "bad operand")
.with_span(spanned)?;
target.shl_assign(operand);
}
IrAssignOp::Shr => {
let operand = u32::try_from(operand)
.map_err(|_| "bad operand")
.with_span(spanned)?;
target.shr_assign(operand);
}
}
Ok(())
}
}