pub(crate) mod compile;
pub(crate) use self::compile::IrCompiler;
mod error;
pub use self::error::{IrError, IrErrorKind};
mod eval;
pub(crate) use self::eval::{eval_ir, IrEvalOutcome};
mod interpreter;
pub(crate) use self::interpreter::{IrBudget, IrInterpreter};
mod value;
pub use self::value::IrValue;
use self::eval::IrEvalBreak;
use crate::ast::{Span, Spanned};
use crate::compile::ast;
use crate::compile::ir;
use crate::compile::ItemMeta;
use crate::query::Used;
pub struct IrEvalContext<'a> {
pub(crate) c: IrCompiler<'a>,
pub(crate) item: &'a ItemMeta,
}
pub trait IrEval {
fn eval(&self, ctx: &mut IrEvalContext<'_>) -> Result<IrValue, IrError>;
}
impl IrEval for ast::Expr {
fn eval(&self, ctx: &mut IrEvalContext<'_>) -> Result<IrValue, IrError> {
let ir = compile::expr(self, &mut ctx.c)?;
let mut ir_interpreter = IrInterpreter {
budget: IrBudget::new(1_000_000),
scopes: Default::default(),
module: &ctx.item.module,
item: &ctx.item.item,
q: ctx.c.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, Clone, Spanned)]
pub 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, Clone, Spanned)]
pub struct IrTarget {
#[rune(span)]
pub(crate) span: Span,
pub(crate) kind: IrTargetKind,
}
#[derive(Debug, Clone)]
pub enum IrTargetKind {
Name(Box<str>),
Field(Box<IrTarget>, Box<str>),
Index(Box<IrTarget>, usize),
}
decl_kind! {
#[derive(Debug, Clone)]
pub enum IrKind {
Scope(IrScope),
Binary(IrBinary),
Decl(IrDecl),
Set(IrSet),
Assign(IrAssign),
Template(IrTemplate),
Name(Box<str>),
Target(IrTarget),
Value(IrValue),
Branches(IrBranches),
Loop(IrLoop),
Break(IrBreak),
Vec(IrVec),
Tuple(IrTuple),
Object(IrObject),
Call(IrCall),
}
}
#[derive(Debug, Clone, Spanned)]
pub struct IrFn {
#[rune(span)]
pub(crate) span: Span,
pub(crate) args: Vec<Box<str>>,
pub(crate) ir: Ir,
}
impl IrFn {
pub(crate) fn compile_ast(ast: &ast::ItemFn, c: &mut IrCompiler<'_>) -> Result<Self, IrError> {
let mut args = Vec::new();
for (arg, _) in &ast.args {
if let ast::FnArg::Pat(ast::Pat::PatPath(path)) = arg {
if let Some(ident) = path.path.try_as_ident() {
args.push(c.resolve(ident)?.into());
continue;
}
}
return Err(IrError::msg(arg, "unsupported argument in const fn"));
}
let ir_scope = compile::block(&ast.body, c)?;
Ok(ir::IrFn {
span: ast.span(),
args,
ir: ir::Ir::new(ast.span(), ir_scope),
})
}
}
#[derive(Debug, Clone, Spanned)]
pub struct IrScope {
#[rune(span)]
pub(crate) span: Span,
pub(crate) instructions: Vec<Ir>,
pub(crate) last: Option<Box<Ir>>,
}
#[derive(Debug, Clone, Spanned)]
pub struct IrBinary {
#[rune(span)]
pub(crate) span: Span,
pub(crate) op: IrBinaryOp,
pub(crate) lhs: Box<Ir>,
pub(crate) rhs: Box<Ir>,
}
#[derive(Debug, Clone, Spanned)]
pub struct IrDecl {
#[rune(span)]
pub(crate) span: Span,
pub(crate) name: Box<str>,
pub(crate) value: Box<Ir>,
}
#[derive(Debug, Clone, Spanned)]
pub struct IrSet {
#[rune(span)]
pub(crate) span: Span,
pub(crate) target: IrTarget,
pub(crate) value: Box<Ir>,
}
#[derive(Debug, Clone, Spanned)]
pub struct IrAssign {
#[rune(span)]
pub(crate) span: Span,
pub(crate) target: IrTarget,
pub(crate) value: Box<Ir>,
pub(crate) op: IrAssignOp,
}
#[derive(Debug, Clone, Spanned)]
pub struct IrTemplate {
#[rune(span)]
pub(crate) span: Span,
pub(crate) components: Vec<IrTemplateComponent>,
}
#[derive(Debug, Clone)]
pub enum IrTemplateComponent {
Ir(Ir),
String(Box<str>),
}
#[derive(Debug, Clone)]
pub struct IrBranches {
pub(crate) branches: Vec<(IrCondition, IrScope)>,
pub(crate) default_branch: Option<IrScope>,
}
#[derive(Debug, Clone, Spanned)]
pub enum IrCondition {
Ir(Ir),
Let(IrLet),
}
#[derive(Debug, Clone, Spanned)]
pub struct IrLet {
#[rune(span)]
pub(crate) span: Span,
pub(crate) pat: IrPat,
pub(crate) ir: Ir,
}
#[derive(Debug, Clone)]
pub enum IrPat {
Ignore,
Binding(Box<str>),
}
impl IrPat {
fn compile_ast(ast: &ast::Pat, c: &mut IrCompiler<'_>) -> Result<Self, IrError> {
match ast {
ast::Pat::PatIgnore(..) => return Ok(ir::IrPat::Ignore),
ast::Pat::PatPath(path) => {
if let Some(ident) = path.path.try_as_ident() {
let name = c.resolve(ident)?;
return Ok(ir::IrPat::Binding(name.into()));
}
}
_ => (),
}
Err(IrError::msg(ast, "pattern not supported yet"))
}
fn matches<S>(
&self,
interp: &mut IrInterpreter<'_>,
value: IrValue,
spanned: S,
) -> Result<bool, IrEvalOutcome>
where
S: Spanned,
{
match self {
IrPat::Ignore => Ok(true),
IrPat::Binding(name) => {
interp.scopes.decl(name, value, spanned)?;
Ok(true)
}
}
}
}
#[derive(Debug, Clone, Spanned)]
pub 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, Clone, Spanned)]
pub struct IrBreak {
#[rune(span)]
pub(crate) span: Span,
pub(crate) kind: IrBreakKind,
}
impl IrBreak {
fn compile_ast(ast: &ast::ExprBreak, c: &mut IrCompiler<'_>) -> Result<Self, IrError> {
let span = ast.span();
let kind = match ast.expr.as_deref() {
Some(expr) => match expr {
ast::ExprBreakValue::Expr(e) => ir::IrBreakKind::Ir(Box::new(compile::expr(e, c)?)),
ast::ExprBreakValue::Label(label) => {
ir::IrBreakKind::Label(c.resolve(label)?.into())
}
},
None => ir::IrBreakKind::Inherent,
};
Ok(ir::IrBreak { span, kind })
}
fn as_outcome(&self, interp: &mut IrInterpreter<'_>, used: Used) -> IrEvalOutcome {
let span = self.span();
if let Err(e) = interp.budget.take(span) {
return e.into();
}
match &self.kind {
IrBreakKind::Ir(ir) => match ir::eval_ir(ir, interp, used) {
Ok(value) => IrEvalOutcome::Break(span, IrEvalBreak::Value(value)),
Err(err) => err,
},
IrBreakKind::Label(label) => {
IrEvalOutcome::Break(span, IrEvalBreak::Label(label.clone()))
}
IrBreakKind::Inherent => IrEvalOutcome::Break(span, IrEvalBreak::Inherent),
}
}
}
#[derive(Debug, Clone)]
pub enum IrBreakKind {
Inherent,
Label(Box<str>),
Ir(Box<Ir>),
}
#[derive(Debug, Clone, Spanned)]
pub struct IrTuple {
#[rune(span)]
pub(crate) span: Span,
pub(crate) items: Box<[Ir]>,
}
#[derive(Debug, Clone, Spanned)]
pub struct IrObject {
#[rune(span)]
pub(crate) span: Span,
pub(crate) assignments: Box<[(Box<str>, Ir)]>,
}
#[derive(Debug, Clone, Spanned)]
pub struct IrCall {
#[rune(span)]
pub(crate) span: Span,
pub(crate) target: Box<str>,
pub(crate) args: Vec<Ir>,
}
#[derive(Debug, Clone, Spanned)]
pub struct IrVec {
#[rune(span)]
pub(crate) span: Span,
pub(crate) items: Box<[Ir]>,
}
#[derive(Debug, Clone, Copy)]
pub enum IrBinaryOp {
Add,
Sub,
Mul,
Div,
Shl,
Shr,
Lt,
Lte,
Eq,
Gt,
Gte,
}
#[derive(Debug, Clone, Copy)]
pub enum IrAssignOp {
Add,
Sub,
Mul,
Div,
Shl,
Shr,
}
impl IrAssignOp {
pub(crate) fn assign<S>(
self,
spanned: S,
target: &mut IrValue,
operand: IrValue,
) -> Result<(), IrError>
where
S: Copy + Spanned,
{
if let IrValue::Integer(target) = target {
if let IrValue::Integer(operand) = operand {
return self.assign_int(spanned, target, operand);
}
}
Err(IrError::msg(spanned, "unsupported operands"))
}
fn assign_int<S>(
self,
spanned: S,
target: &mut num::BigInt,
operand: num::BigInt,
) -> Result<(), IrError>
where
S: Copy + Spanned,
{
use std::ops::{AddAssign, MulAssign, ShlAssign, ShrAssign, SubAssign};
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_else(|| IrError::msg(spanned, "division by zero"))?;
}
IrAssignOp::Shl => {
let operand =
u32::try_from(operand).map_err(|_| IrError::msg(spanned, "bad operand"))?;
target.shl_assign(operand);
}
IrAssignOp::Shr => {
let operand =
u32::try_from(operand).map_err(|_| IrError::msg(spanned, "bad operand"))?;
target.shr_assign(operand);
}
}
Ok(())
}
}