feat: SCC analysis (WIP)
This commit is contained in:
@@ -10,6 +10,10 @@ repl = ["dep:rustyline"]
|
|||||||
name = "repl"
|
name = "repl"
|
||||||
required-features = ["repl"]
|
required-features = ["repl"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "scc"
|
||||||
|
required-features = ["repl"]
|
||||||
|
|
||||||
[profile.perf]
|
[profile.perf]
|
||||||
debug = 2
|
debug = 2
|
||||||
strip = false
|
strip = false
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ fn main() -> Result<()> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
let expr = root.tree().expr().unwrap();
|
let expr = root.tree().expr().unwrap();
|
||||||
let expr = downgrade(expr)?;
|
let (downgraded, _) = downgrade(expr)?;
|
||||||
println!("{}", eval(expr)?);
|
println!("{}", eval(downgraded)?);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
59
src/bin/scc.rs
Normal file
59
src/bin/scc.rs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
use itertools::Itertools;
|
||||||
|
use rustyline::error::ReadlineError;
|
||||||
|
use rustyline::{DefaultEditor, Result};
|
||||||
|
|
||||||
|
use nixjit::error::Error;
|
||||||
|
use nixjit::ir::downgrade;
|
||||||
|
|
||||||
|
macro_rules! unwrap {
|
||||||
|
($e:expr) => {
|
||||||
|
match $e {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => {
|
||||||
|
println!("{err}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let mut rl = DefaultEditor::new()?;
|
||||||
|
loop {
|
||||||
|
let readline = rl.readline("nixjit-scc-analyzer> ");
|
||||||
|
match readline {
|
||||||
|
Ok(expr) => {
|
||||||
|
if expr.trim().is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rl.add_history_entry(expr.as_str())?;
|
||||||
|
let root = rnix::Root::parse(&expr);
|
||||||
|
if !root.errors().is_empty() {
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
Error::ParseError(
|
||||||
|
root.errors().iter().map(|err| err.to_string()).join(";")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let expr = root.tree().expr().unwrap();
|
||||||
|
let (_, graph) = unwrap!(downgrade(expr));
|
||||||
|
println!("{:?}", graph);
|
||||||
|
}
|
||||||
|
Err(ReadlineError::Interrupted) => {
|
||||||
|
println!("CTRL-C");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(ReadlineError::Eof) => {
|
||||||
|
println!("CTRL-D");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!("Error: {err:?}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@ pub struct Engine {
|
|||||||
pub fn eval(downgraded: Downgraded) -> Result<Value> {
|
pub fn eval(downgraded: Downgraded) -> Result<Value> {
|
||||||
let mut engine = Engine::new(downgraded.thunks, downgraded.func_offset);
|
let mut engine = Engine::new(downgraded.thunks, downgraded.func_offset);
|
||||||
engine
|
engine
|
||||||
.eval(downgraded.top_level, &mut VmEnv::new())
|
.eval()
|
||||||
.map(|mut val| {
|
.map(|mut val| {
|
||||||
Ok(val
|
Ok(val
|
||||||
.force(&mut engine)?
|
.force(&mut engine)?
|
||||||
@@ -38,15 +38,15 @@ pub fn eval(downgraded: Downgraded) -> Result<Value> {
|
|||||||
impl Engine {
|
impl Engine {
|
||||||
pub fn new(thunks: Box<[Ir]>, func_offset: usize) -> Self {
|
pub fn new(thunks: Box<[Ir]>, func_offset: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
lru: LruCache::new(thunks.len().clamp(1, usize::MAX).try_into().unwrap()),
|
lru: LruCache::new(thunks.len().try_into().unwrap()),
|
||||||
thunks,
|
thunks,
|
||||||
func_offset,
|
func_offset,
|
||||||
tasks: PriorityQueue::new(),
|
tasks: PriorityQueue::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval(&mut self, expr: Ir, env: &mut VmEnv) -> Result<i::Value> {
|
pub fn eval(&mut self) -> Result<i::Value> {
|
||||||
expr.eval(self, env)
|
self.thunks.last().unwrap().clone().eval(self, &mut VmEnv::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_thunk(&mut self, idx: usize, env: &mut VmEnv) -> Result<i::Value> {
|
pub fn eval_thunk(&mut self, idx: usize, env: &mut VmEnv) -> Result<i::Value> {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use super::eval;
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn test_expr(expr: &str, expected: Value) {
|
fn test_expr(expr: &str, expected: Value) {
|
||||||
let downgraded = downgrade(rnix::Root::parse(expr).tree().expr().unwrap()).unwrap();
|
let (downgraded, _) = downgrade(rnix::Root::parse(expr).tree().expr().unwrap()).unwrap();
|
||||||
assert_eq!(eval(downgraded).unwrap(), expected);
|
assert_eq!(eval(downgraded).unwrap(), expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use inkwell::values::{BasicValueEnum, FunctionValue};
|
use inkwell::values::{StructValue, FunctionValue};
|
||||||
|
|
||||||
use crate::ir::*;
|
use crate::ir::*;
|
||||||
use crate::ty::common as c;
|
use crate::ty::common as c;
|
||||||
@@ -7,35 +7,35 @@ use crate::ty::internal::Value;
|
|||||||
use super::JITContext;
|
use super::JITContext;
|
||||||
|
|
||||||
pub trait JITCompile {
|
pub trait JITCompile {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc>;
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for Attrs {
|
impl JITCompile for Attrs {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc> {
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for List {
|
impl JITCompile for List {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc> {
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for HasAttr {
|
impl JITCompile for HasAttr {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc> {
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for BinOp {
|
impl JITCompile for BinOp {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc> {
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for UnOp {
|
impl JITCompile for UnOp {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc> {
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> {
|
||||||
todo!();
|
todo!();
|
||||||
let rhs = self.rhs.compile(ctx, func);
|
let rhs = self.rhs.compile(ctx, func);
|
||||||
let tag = ctx.get_tag(rhs);
|
let tag = ctx.get_tag(rhs);
|
||||||
@@ -45,60 +45,60 @@ impl JITCompile for UnOp {
|
|||||||
ctx.builder.build_switch(tag, fallback, &[]).unwrap();
|
ctx.builder.build_switch(tag, fallback, &[]).unwrap();
|
||||||
ctx.builder.position_at_end(fallback);
|
ctx.builder.position_at_end(fallback);
|
||||||
ctx.builder.position_at_end(ret);
|
ctx.builder.position_at_end(ret);
|
||||||
ctx.builder.build_load(ctx.helpers.value_type, res, "load_res").unwrap()
|
ctx.builder.build_load(ctx.helpers.value_type, res, "load_res").unwrap().try_into().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for Select {
|
impl JITCompile for Select {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc> {
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for If {
|
impl JITCompile for If {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc> {
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for LoadFunc {
|
impl JITCompile for LoadFunc {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc> {
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for Call {
|
impl JITCompile for Call {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc> {
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for Let {
|
impl JITCompile for Let {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc> {
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for With {
|
impl JITCompile for With {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc> {
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for Assert {
|
impl JITCompile for Assert {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc> {
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for ConcatStrings {
|
impl JITCompile for ConcatStrings {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc> {
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for Const {
|
impl JITCompile for Const {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc> {
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> {
|
||||||
use c::Const::*;
|
use c::Const::*;
|
||||||
match self.val {
|
match self.val {
|
||||||
Bool(x) => ctx.helpers.new_bool(x),
|
Bool(x) => ctx.helpers.new_bool(x),
|
||||||
@@ -110,37 +110,37 @@ impl JITCompile for Const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for String {
|
impl JITCompile for String {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc> {
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for Var {
|
impl JITCompile for Var {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc> {
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for Arg {
|
impl JITCompile for Arg {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc> {
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for LetVar {
|
impl JITCompile for LetVar {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc> {
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for Thunk {
|
impl JITCompile for Thunk {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc> {
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JITCompile for Path {
|
impl JITCompile for Path {
|
||||||
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> BasicValueEnum<'gc> {
|
fn compile<'gc>(self, ctx: &JITContext<'gc>, func: FunctionValue<'gc>) -> StructValue<'gc> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use inkwell::context::Context;
|
|||||||
use inkwell::execution_engine::ExecutionEngine;
|
use inkwell::execution_engine::ExecutionEngine;
|
||||||
use inkwell::module::Module;
|
use inkwell::module::Module;
|
||||||
use inkwell::types::{FloatType, FunctionType, IntType, PointerType, StructType};
|
use inkwell::types::{FloatType, FunctionType, IntType, PointerType, StructType};
|
||||||
use inkwell::values::{BasicValueEnum, FloatValue, FunctionValue, IntValue};
|
use inkwell::values::{BasicValueEnum, FloatValue, FunctionValue, IntValue, StructValue};
|
||||||
|
|
||||||
use crate::env::VmEnv;
|
use crate::env::VmEnv;
|
||||||
use crate::eval::Engine;
|
use crate::eval::Engine;
|
||||||
@@ -180,7 +180,7 @@ impl<'ctx> Helpers<'ctx> {
|
|||||||
self.int_type.const_int(int as _, false)
|
self.int_type.const_int(int as _, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_int(&self, int: i64) -> BasicValueEnum<'ctx> {
|
pub fn new_int(&self, int: i64) -> StructValue<'ctx> {
|
||||||
self.value_type
|
self.value_type
|
||||||
.const_named_struct(&[
|
.const_named_struct(&[
|
||||||
self.int_type.const_int(ValueTag::Int as _, false).into(),
|
self.int_type.const_int(ValueTag::Int as _, false).into(),
|
||||||
@@ -193,7 +193,7 @@ impl<'ctx> Helpers<'ctx> {
|
|||||||
self.float_type.const_float(float)
|
self.float_type.const_float(float)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_float(&self, float: f64) -> BasicValueEnum<'ctx> {
|
pub fn new_float(&self, float: f64) -> StructValue<'ctx> {
|
||||||
self.value_type
|
self.value_type
|
||||||
.const_named_struct(&[
|
.const_named_struct(&[
|
||||||
self.int_type.const_int(ValueTag::Float as _, false).into(),
|
self.int_type.const_int(ValueTag::Float as _, false).into(),
|
||||||
@@ -202,7 +202,7 @@ impl<'ctx> Helpers<'ctx> {
|
|||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_bool(&self, bool: bool) -> BasicValueEnum<'ctx> {
|
pub fn new_bool(&self, bool: bool) -> StructValue<'ctx> {
|
||||||
self.value_type
|
self.value_type
|
||||||
.const_named_struct(&[
|
.const_named_struct(&[
|
||||||
self.int_type.const_int(ValueTag::Bool as _, false).into(),
|
self.int_type.const_int(ValueTag::Bool as _, false).into(),
|
||||||
@@ -211,7 +211,7 @@ impl<'ctx> Helpers<'ctx> {
|
|||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_null(&self) -> BasicValueEnum<'ctx> {
|
pub fn new_null(&self) -> StructValue<'ctx> {
|
||||||
self.value_type
|
self.value_type
|
||||||
.const_named_struct(&[
|
.const_named_struct(&[
|
||||||
self.int_type.const_int(ValueTag::Null as _, false).into(),
|
self.int_type.const_int(ValueTag::Null as _, false).into(),
|
||||||
@@ -220,7 +220,7 @@ impl<'ctx> Helpers<'ctx> {
|
|||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn const_string(&self, string: *const u8) -> BasicValueEnum<'ctx> {
|
pub fn const_string(&self, string: *const u8) -> StructValue<'ctx> {
|
||||||
self.value_type
|
self.value_type
|
||||||
.const_named_struct(&[
|
.const_named_struct(&[
|
||||||
self.int_type.const_int(ValueTag::String as _, false).into(),
|
self.int_type.const_int(ValueTag::String as _, false).into(),
|
||||||
|
|||||||
@@ -139,10 +139,10 @@ impl<'ctx> JITContext<'ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile(&self, ir: Ir) -> JITFunc {
|
pub fn compile(&self, ir: Ir) -> JITFunc {
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_tag(&self, val: BasicValueEnum<'ctx>) -> IntValue<'ctx> {
|
pub fn get_tag(&self, val: StructValue<'ctx>) -> IntValue<'ctx> {
|
||||||
let alloca = self
|
let alloca = self
|
||||||
.builder
|
.builder
|
||||||
.build_alloca(self.helpers.value_type, "get_tag_alloca").unwrap();
|
.build_alloca(self.helpers.value_type, "get_tag_alloca").unwrap();
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use ecow::EcoVec;
|
use ecow::EcoVec;
|
||||||
|
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::env::VmEnv;
|
use crate::env::VmEnv;
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::ir::{self, DynamicAttrPair};
|
use crate::ir::{self, DynAttr}; use crate::ty::common::Const;
|
||||||
use crate::ty::common::Const;
|
|
||||||
use crate::ty::internal::{AttrSet, EnvRef, List, ThunkRef, Value};
|
use crate::ty::internal::{AttrSet, EnvRef, List, ThunkRef, Value};
|
||||||
use crate::ty::public::Symbol;
|
use crate::ty::public::Symbol;
|
||||||
|
|
||||||
@@ -32,7 +29,7 @@ impl Evaluate for ir::Attrs {
|
|||||||
})
|
})
|
||||||
.collect::<Result<_>>()?,
|
.collect::<Result<_>>()?,
|
||||||
);
|
);
|
||||||
for DynamicAttrPair(k, v) in self.dyns {
|
for DynAttr(k, v) in self.dyns {
|
||||||
let mut k = k.eval(engine, env)?;
|
let mut k = k.eval(engine, env)?;
|
||||||
k.force(engine)?.coerce_to_string();
|
k.force(engine)?.coerce_to_string();
|
||||||
attrs.push_attr(k.unwrap_string(), v.eval(engine, env)?);
|
attrs.push_attr(k.unwrap_string(), v.eval(engine, env)?);
|
||||||
@@ -315,6 +312,15 @@ impl Evaluate for ir::Thunk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Evaluate for ir::MaybeThunk {
|
||||||
|
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
|
||||||
|
match self {
|
||||||
|
ir::MaybeThunk::Const(cnst) => cnst.eval(engine, env),
|
||||||
|
ir::MaybeThunk::Thunk(thunk) => thunk.eval(engine, env)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Evaluate for ir::Path {
|
impl Evaluate for ir::Path {
|
||||||
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
|
fn eval(self, engine: &mut Engine, env: &mut VmEnv) -> Result<Value> {
|
||||||
todo!()
|
todo!()
|
||||||
|
|||||||
234
src/ir/ctx.rs
Normal file
234
src/ir/ctx.rs
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
use ecow::EcoString;
|
||||||
|
use hashbrown::{HashMap, HashSet};
|
||||||
|
|
||||||
|
use crate::error::Result;
|
||||||
|
|
||||||
|
use super::{Func, Ir, LoadFunc, MaybeThunk, Thunk};
|
||||||
|
|
||||||
|
pub struct DowngradeContext {
|
||||||
|
pub thunks: Vec<Ir>,
|
||||||
|
pub deps: Vec<HashSet<usize>>,
|
||||||
|
pub funcs: Vec<Func>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Env<'a, 'env> {
|
||||||
|
env: EnvNode<'a>,
|
||||||
|
prev: Option<&'env Env<'a, 'env>>,
|
||||||
|
arg_level: usize,
|
||||||
|
let_level: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EnvNode<'a> {
|
||||||
|
Builtins(&'a HashMap<EcoString, Ir>),
|
||||||
|
Let(&'a Vec<(EcoString, MaybeThunk)>),
|
||||||
|
SingleArg(EcoString),
|
||||||
|
MultiArg(HashMap<EcoString, Option<Ir>>, Option<EcoString>),
|
||||||
|
With,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum LookupResult {
|
||||||
|
Builtin(Ir),
|
||||||
|
MaybeThunk(MaybeThunk),
|
||||||
|
Let { level: usize, idx: usize },
|
||||||
|
SingleArg { level: usize },
|
||||||
|
MultiArg { level: usize, default: Option<Ir> },
|
||||||
|
With,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'env> Env<'a, 'env> {
|
||||||
|
pub fn new(base: &'a HashMap<EcoString, Ir>) -> Self {
|
||||||
|
Self {
|
||||||
|
env: EnvNode::Builtins(base),
|
||||||
|
prev: None,
|
||||||
|
arg_level: 0,
|
||||||
|
let_level: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enter_let(&'env self, map: &'a Vec<(EcoString, MaybeThunk)>) -> Self {
|
||||||
|
Self {
|
||||||
|
env: EnvNode::Let(map),
|
||||||
|
prev: Some(self),
|
||||||
|
arg_level: self.arg_level,
|
||||||
|
let_level: self.let_level + 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enter_single_arg(&'env self, ident: EcoString) -> Self {
|
||||||
|
Self {
|
||||||
|
env: EnvNode::SingleArg(ident),
|
||||||
|
prev: Some(self),
|
||||||
|
arg_level: self.arg_level + 1,
|
||||||
|
let_level: self.let_level,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enter_multi_arg(
|
||||||
|
&'env self,
|
||||||
|
map: HashMap<EcoString, Option<Ir>>,
|
||||||
|
alias: Option<EcoString>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
env: EnvNode::MultiArg(map, alias),
|
||||||
|
prev: Some(self),
|
||||||
|
arg_level: self.arg_level + 1,
|
||||||
|
let_level: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enter_with(&'env self) -> Self {
|
||||||
|
Self {
|
||||||
|
env: EnvNode::With,
|
||||||
|
prev: Some(self),
|
||||||
|
arg_level: self.arg_level,
|
||||||
|
let_level: self.let_level,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _lookup(
|
||||||
|
&self,
|
||||||
|
ident: &EcoString,
|
||||||
|
mut arg_level: usize,
|
||||||
|
mut let_level: usize,
|
||||||
|
has_with: bool,
|
||||||
|
) -> core::result::Result<LookupResult, ()> {
|
||||||
|
use EnvNode::*;
|
||||||
|
let mut has_with = has_with;
|
||||||
|
match &self.env {
|
||||||
|
Builtins(map) => {
|
||||||
|
return if let Some(ir) = map.get(ident) {
|
||||||
|
Ok(LookupResult::Builtin(ir.clone()))
|
||||||
|
} else if has_with {
|
||||||
|
Ok(LookupResult::With)
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Let(map) => {
|
||||||
|
if let Ok(idx) = map.binary_search_by(|(k, _)| k.cmp(ident)) {
|
||||||
|
return Ok(LookupResult::MaybeThunk(map[idx].1))
|
||||||
|
/* return Ok(LookupResult::Let {
|
||||||
|
level: let_level - 1,
|
||||||
|
idx,
|
||||||
|
}); */
|
||||||
|
} else {
|
||||||
|
let_level -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SingleArg(arg) => {
|
||||||
|
if arg == ident {
|
||||||
|
return Ok(LookupResult::SingleArg {
|
||||||
|
level: arg_level - 1,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
arg_level -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MultiArg(set, alias) => {
|
||||||
|
if let Some(default) = set.get(ident) {
|
||||||
|
return Ok(LookupResult::MultiArg {
|
||||||
|
level: arg_level - 1,
|
||||||
|
default: default.clone(),
|
||||||
|
});
|
||||||
|
} else if alias.as_ref() == Some(ident) {
|
||||||
|
return Ok(LookupResult::SingleArg {
|
||||||
|
level: arg_level - 1,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
arg_level -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
With => has_with = true,
|
||||||
|
}
|
||||||
|
self.prev
|
||||||
|
.map(|prev| prev._lookup(ident, arg_level, let_level, has_with))
|
||||||
|
.map_or_else(|| unreachable!(), |x| x)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup(&self, ident: &EcoString) -> core::result::Result<LookupResult, ()> {
|
||||||
|
self._lookup(ident, self.arg_level, self.let_level, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DowngradeContext {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
DowngradeContext {
|
||||||
|
thunks: Vec::new(),
|
||||||
|
deps: Vec::new(),
|
||||||
|
funcs: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DowngradeContext {
|
||||||
|
pub fn new_thunk(&mut self, thunk: Ir) -> Thunk {
|
||||||
|
let idx = self.thunks.len();
|
||||||
|
self.thunks.push(thunk);
|
||||||
|
self.deps.push(HashSet::new());
|
||||||
|
Thunk { idx }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn maybe_thunk(&mut self, ir: Ir) -> MaybeThunk {
|
||||||
|
match ir {
|
||||||
|
Ir::Const(cnst) => MaybeThunk::Const(cnst),
|
||||||
|
Ir::Thunk(thunk) => MaybeThunk::Thunk(thunk),
|
||||||
|
ir => MaybeThunk::Thunk(self.new_thunk(ir))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_dep(&mut self, this: usize, dep: usize) {
|
||||||
|
self.deps[this].insert(dep);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_func(&mut self, func: Func) -> LoadFunc {
|
||||||
|
let idx = self.funcs.len();
|
||||||
|
self.funcs.push(func);
|
||||||
|
LoadFunc { idx }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_func(&mut self, thunk_idx: usize, func_idx: usize, env: &Env) -> Result<()> {
|
||||||
|
let self_ptr = self as *mut Self;
|
||||||
|
self.funcs.get_mut(func_idx).map_or_else(
|
||||||
|
|| unreachable!(),
|
||||||
|
|func| {
|
||||||
|
unsafe {
|
||||||
|
let old = std::ptr::read(func);
|
||||||
|
std::ptr::write(func, old.resolve(thunk_idx, Some(func_idx), self_ptr.as_mut().unwrap(), env)?);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_thunk(&mut self, thunk_idx: usize, func_idx: Option<usize>, env: &Env) -> Result<()> {
|
||||||
|
let self_ptr = self as *mut Self;
|
||||||
|
self.thunks.get_mut(thunk_idx).map_or_else(
|
||||||
|
|| unreachable!(),
|
||||||
|
|thunk| {
|
||||||
|
unsafe {
|
||||||
|
let old = std::ptr::read(thunk);
|
||||||
|
std::ptr::write(thunk, old.resolve(thunk_idx, func_idx, self_ptr.as_mut().unwrap(), env)?);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Downgraded {
|
||||||
|
pub thunks: Box<[Ir]>,
|
||||||
|
pub func_offset: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Downgraded {
|
||||||
|
pub fn new(ctx: DowngradeContext) -> Self {
|
||||||
|
Self {
|
||||||
|
func_offset: ctx.thunks.len(),
|
||||||
|
thunks: ctx
|
||||||
|
.thunks
|
||||||
|
.into_iter()
|
||||||
|
.chain(ctx.funcs.into_iter().map(|Func { body, .. }| *body))
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
623
src/ir/mod.rs
623
src/ir/mod.rs
@@ -1,7 +1,7 @@
|
|||||||
use derive_more::{IsVariant, TryUnwrap, Unwrap};
|
use derive_more::{IsVariant, TryUnwrap, Unwrap};
|
||||||
use ecow::EcoString;
|
use ecow::EcoString;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::{HashMap, HashSet};
|
||||||
use inkwell::values::{StructValue, FunctionValue};
|
use inkwell::values::{FunctionValue, StructValue};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rnix::ast::HasEntry;
|
use rnix::ast::HasEntry;
|
||||||
use rnix::ast::{self, Expr};
|
use rnix::ast::{self, Expr};
|
||||||
@@ -16,24 +16,24 @@ use crate::ty::common as c;
|
|||||||
use crate::ty::internal::Value;
|
use crate::ty::internal::Value;
|
||||||
use crate::ty::public::Symbol;
|
use crate::ty::public::Symbol;
|
||||||
|
|
||||||
|
mod ctx;
|
||||||
|
mod scc;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
use ctx::*;
|
||||||
use utils::*;
|
use utils::*;
|
||||||
|
|
||||||
pub fn downgrade(expr: Expr) -> Result<Downgraded> {
|
pub use ctx::{DowngradeContext, Downgraded};
|
||||||
|
pub use scc::*;
|
||||||
|
|
||||||
|
pub fn downgrade(expr: Expr) -> Result<(Downgraded, Vec<SccNode>)> {
|
||||||
let mut ctx = DowngradeContext::new();
|
let mut ctx = DowngradeContext::new();
|
||||||
let builtins = ir_env(&mut ctx);
|
let builtins = ir_env(&mut ctx);
|
||||||
let env = Env::new(&builtins);
|
let env = Env::new(&builtins);
|
||||||
let ir = expr.downgrade(&mut ctx)?;
|
let top_level = expr.downgrade(&mut ctx)?;
|
||||||
let ir = ir.resolve(&mut ctx, &env)?;
|
let Thunk { idx } = ctx.new_thunk(top_level);
|
||||||
Ok(Downgraded {
|
ctx.resolve_thunk(idx, None, &env)?;
|
||||||
top_level: ir,
|
let scc = SccAnalyzer::new(&ctx).analyze();
|
||||||
func_offset: ctx.thunks.len(),
|
Ok((Downgraded::new(ctx), scc))
|
||||||
thunks: ctx
|
|
||||||
.thunks
|
|
||||||
.into_iter()
|
|
||||||
.chain(ctx.funcs.into_iter().map(|Func { body, .. }| *body))
|
|
||||||
.collect(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! ir {
|
macro_rules! ir {
|
||||||
@@ -83,9 +83,9 @@ macro_rules! ir {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(self, self_idx: usize, func_idx: Option<usize>, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
||||||
match self {
|
match self {
|
||||||
$(Ir::$ty(ir) => ir.resolve(ctx, env),)*
|
$(Ir::$ty(ir) => ir.resolve(self_idx, func_idx, ctx, env),)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,7 +127,7 @@ macro_rules! ir {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ir! {
|
ir! {
|
||||||
Attrs => { stcs: HashMap<EcoString, Ir>, dyns: Vec<DynamicAttrPair> },
|
Attrs => { stcs: HashMap<EcoString, Ir>, dyns: Vec<DynAttr> },
|
||||||
List => { items: Vec<Ir> },
|
List => { items: Vec<Ir> },
|
||||||
HasAttr => { lhs: Box<Ir>, rhs: Vec<Attr> },
|
HasAttr => { lhs: Box<Ir>, rhs: Vec<Attr> },
|
||||||
BinOp => { lhs: Box<Ir>, rhs: Box<Ir>, kind: BinOpKind },
|
BinOp => { lhs: Box<Ir>, rhs: Box<Ir>, kind: BinOpKind },
|
||||||
@@ -137,10 +137,11 @@ ir! {
|
|||||||
LoadFunc => { idx: usize },
|
LoadFunc => { idx: usize },
|
||||||
Call => { func: Box<Ir>, args: Vec<Ir> },
|
Call => { func: Box<Ir>, args: Vec<Ir> },
|
||||||
|
|
||||||
Let => { bindings: Vec<(EcoString, Ir)>, expr: Box<Ir> },
|
Let => { bindings: Vec<(EcoString, MaybeThunk)>, expr: Box<Ir> },
|
||||||
With => { namespace: Box<Ir>, expr: Box<Ir> },
|
With => { namespace: Box<Ir>, expr: Box<Ir> },
|
||||||
Assert => { assertion: Box<Ir>, expr: Box<Ir> },
|
Assert => { assertion: Box<Ir>, expr: Box<Ir> },
|
||||||
ConcatStrings => { parts: Vec<Ir> },
|
ConcatStrings => { parts: Vec<Ir> },
|
||||||
|
#[derive(Copy)]
|
||||||
Const => { val: c::Const },
|
Const => { val: c::Const },
|
||||||
String => { val: EcoString },
|
String => { val: EcoString },
|
||||||
Var => { sym: EcoString },
|
Var => { sym: EcoString },
|
||||||
@@ -165,6 +166,27 @@ impl Ir {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum MaybeThunk {
|
||||||
|
Const(Const),
|
||||||
|
Thunk(Thunk)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MaybeThunk {
|
||||||
|
fn resolve<'a, 'env>(
|
||||||
|
self,
|
||||||
|
self_idx: usize,
|
||||||
|
func_idx: Option<usize>,
|
||||||
|
ctx: &mut DowngradeContext,
|
||||||
|
env: &Env<'a, 'env>,
|
||||||
|
) -> Result<Ir> {
|
||||||
|
match self {
|
||||||
|
MaybeThunk::Thunk(thunk) => thunk.resolve(self_idx, func_idx, ctx, env),
|
||||||
|
MaybeThunk::Const(cnst) => cnst.ir().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Into<c::Const>> From<T> for Const {
|
impl<T: Into<c::Const>> From<T> for Const {
|
||||||
fn from(value: T) -> Self {
|
fn from(value: T) -> Self {
|
||||||
Const { val: value.into() }
|
Const { val: value.into() }
|
||||||
@@ -172,206 +194,7 @@ impl<T: Into<c::Const>> From<T> for Const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct DynamicAttrPair(pub Ir, pub Ir);
|
pub struct DynAttr(pub Ir, pub Ir);
|
||||||
|
|
||||||
pub struct DowngradeContext {
|
|
||||||
thunks: Vec<Ir>,
|
|
||||||
funcs: Vec<Func>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Env<'a, 'env> {
|
|
||||||
env: EnvNode<'a>,
|
|
||||||
prev: Option<&'env Env<'a, 'env>>,
|
|
||||||
arg_level: usize,
|
|
||||||
let_level: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum EnvNode<'a> {
|
|
||||||
Builtins(&'a HashMap<EcoString, Ir>),
|
|
||||||
Let(&'a Vec<EcoString>),
|
|
||||||
SingleArg(EcoString),
|
|
||||||
MultiArg(HashMap<EcoString, Option<Ir>>, Option<EcoString>),
|
|
||||||
With,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum LookupResult {
|
|
||||||
Builtin(Ir),
|
|
||||||
Let { level: usize, idx: usize },
|
|
||||||
SingleArg { level: usize },
|
|
||||||
MultiArg { level: usize, default: Option<Ir> },
|
|
||||||
With,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'env> Env<'a, 'env> {
|
|
||||||
fn new(base: &'a HashMap<EcoString, Ir>) -> Self {
|
|
||||||
Self {
|
|
||||||
env: EnvNode::Builtins(base),
|
|
||||||
prev: None,
|
|
||||||
arg_level: 0,
|
|
||||||
let_level: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enter_let(&'env self, map: &'a Vec<EcoString>) -> Self {
|
|
||||||
Self {
|
|
||||||
env: EnvNode::Let(map),
|
|
||||||
prev: Some(self),
|
|
||||||
arg_level: self.arg_level,
|
|
||||||
let_level: self.let_level + 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enter_single_arg(&'env self, ident: EcoString) -> Self {
|
|
||||||
Self {
|
|
||||||
env: EnvNode::SingleArg(ident),
|
|
||||||
prev: Some(self),
|
|
||||||
arg_level: self.arg_level + 1,
|
|
||||||
let_level: self.let_level,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enter_multi_arg(
|
|
||||||
&'env self,
|
|
||||||
map: HashMap<EcoString, Option<Ir>>,
|
|
||||||
alias: Option<EcoString>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
env: EnvNode::MultiArg(map, alias),
|
|
||||||
prev: Some(self),
|
|
||||||
arg_level: self.arg_level + 1,
|
|
||||||
let_level: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enter_with(&'env self) -> Self {
|
|
||||||
Self {
|
|
||||||
env: EnvNode::With,
|
|
||||||
prev: Some(self),
|
|
||||||
arg_level: self.arg_level,
|
|
||||||
let_level: self.let_level,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _lookup(
|
|
||||||
&self,
|
|
||||||
ident: &EcoString,
|
|
||||||
mut arg_level: usize,
|
|
||||||
mut let_level: usize,
|
|
||||||
has_with: bool,
|
|
||||||
) -> core::result::Result<LookupResult, ()> {
|
|
||||||
use EnvNode::*;
|
|
||||||
let mut has_with = has_with;
|
|
||||||
match &self.env {
|
|
||||||
Builtins(map) => {
|
|
||||||
return if let Some(ir) = map.get(ident) {
|
|
||||||
Ok(LookupResult::Builtin(ir.clone()))
|
|
||||||
} else if has_with {
|
|
||||||
Ok(LookupResult::With)
|
|
||||||
} else {
|
|
||||||
Err(())
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Let(map) => {
|
|
||||||
if let Ok(idx) = map.binary_search(ident) {
|
|
||||||
return Ok(LookupResult::Let {
|
|
||||||
level: let_level - 1,
|
|
||||||
idx,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
let_level -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SingleArg(arg) => {
|
|
||||||
if arg == ident {
|
|
||||||
return Ok(LookupResult::SingleArg {
|
|
||||||
level: arg_level - 1,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
arg_level -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MultiArg(set, alias) => {
|
|
||||||
if let Some(default) = set.get(ident) {
|
|
||||||
return Ok(LookupResult::MultiArg {
|
|
||||||
level: arg_level - 1,
|
|
||||||
default: default.clone(),
|
|
||||||
});
|
|
||||||
} else if alias.as_ref() == Some(ident) {
|
|
||||||
return Ok(LookupResult::SingleArg {
|
|
||||||
level: arg_level - 1,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
arg_level -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
With => has_with = true,
|
|
||||||
}
|
|
||||||
self.prev
|
|
||||||
.map(|prev| prev._lookup(ident, arg_level, let_level, has_with))
|
|
||||||
.map_or_else(|| unreachable!(), |x| x)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lookup(&self, ident: &EcoString) -> core::result::Result<LookupResult, ()> {
|
|
||||||
self._lookup(ident, self.arg_level, self.let_level, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DowngradeContext {
|
|
||||||
fn new() -> Self {
|
|
||||||
DowngradeContext {
|
|
||||||
thunks: Vec::new(),
|
|
||||||
funcs: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Downgraded {
|
|
||||||
pub top_level: Ir,
|
|
||||||
pub thunks: Box<[Ir]>,
|
|
||||||
pub func_offset: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DowngradeContext {
|
|
||||||
fn new_thunk(&mut self, thunk: Ir) -> Thunk {
|
|
||||||
let idx = self.thunks.len();
|
|
||||||
self.thunks.push(thunk);
|
|
||||||
Thunk { idx }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_func(&mut self, func: Func) -> LoadFunc {
|
|
||||||
let idx = self.funcs.len();
|
|
||||||
self.funcs.push(func);
|
|
||||||
LoadFunc { idx }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_func(&mut self, idx: usize, env: &Env) -> Result<()> {
|
|
||||||
let self_ptr = self as *mut Self;
|
|
||||||
self.funcs.get_mut(idx).map_or_else(
|
|
||||||
|| unreachable!(),
|
|
||||||
|func| {
|
|
||||||
unsafe {
|
|
||||||
let old = std::ptr::read(func);
|
|
||||||
std::ptr::write(func, old.resolve(self_ptr.as_mut().unwrap(), env)?);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_thunk(&mut self, idx: usize, env: &Env) -> Result<()> {
|
|
||||||
let self_ptr = self as *mut Self;
|
|
||||||
self.thunks.get_mut(idx).map_or_else(
|
|
||||||
|| unreachable!(),
|
|
||||||
|thunk| {
|
|
||||||
unsafe {
|
|
||||||
let old = std::ptr::read(thunk);
|
|
||||||
std::ptr::write(thunk, old.resolve(self_ptr.as_mut().unwrap(), env)?);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Attrs {
|
impl Attrs {
|
||||||
fn _insert(
|
fn _insert(
|
||||||
@@ -408,7 +231,7 @@ impl Attrs {
|
|||||||
dyns: Vec::new(),
|
dyns: Vec::new(),
|
||||||
};
|
};
|
||||||
attrs._insert(path, name, value, ctx)?;
|
attrs._insert(path, name, value, ctx)?;
|
||||||
self.dyns.push(DynamicAttrPair(string.ir(), attrs.ir()));
|
self.dyns.push(DynAttr(string.ir(), attrs.ir()));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Attr::Dynamic(dynamic) => {
|
Attr::Dynamic(dynamic) => {
|
||||||
@@ -417,7 +240,7 @@ impl Attrs {
|
|||||||
dyns: Vec::new(),
|
dyns: Vec::new(),
|
||||||
};
|
};
|
||||||
attrs._insert(path, name, value, ctx)?;
|
attrs._insert(path, name, value, ctx)?;
|
||||||
self.dyns.push(DynamicAttrPair(dynamic, attrs.ir()));
|
self.dyns.push(DynAttr(dynamic, attrs.ir()));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -432,10 +255,10 @@ impl Attrs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Attr::Strs(string) => {
|
Attr::Strs(string) => {
|
||||||
self.dyns.push(DynamicAttrPair(string.ir(), value));
|
self.dyns.push(DynAttr(string.ir(), value));
|
||||||
}
|
}
|
||||||
Attr::Dynamic(dynamic) => {
|
Attr::Dynamic(dynamic) => {
|
||||||
self.dyns.push(DynamicAttrPair(dynamic, value));
|
self.dyns.push(DynAttr(dynamic, value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -468,6 +291,46 @@ impl Attrs {
|
|||||||
let name = path.next_back().unwrap().clone();
|
let name = path.next_back().unwrap().clone();
|
||||||
self._has_attr(path, name)
|
self._has_attr(path, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn _select(&self, mut path: std::slice::Iter<Attr>, name: Attr) -> Result<Option<Ir>> {
|
||||||
|
match path.next() {
|
||||||
|
Some(Attr::Str(ident)) => self
|
||||||
|
.stcs
|
||||||
|
.get(ident)
|
||||||
|
.and_then(|attrs| attrs.as_ref().try_unwrap_attrs().ok())
|
||||||
|
.ok_or_else(|| {
|
||||||
|
Error::DowngradeError(format!("{} not found", Symbol::from(ident.clone())))
|
||||||
|
})?
|
||||||
|
._select(path, name),
|
||||||
|
None => match name {
|
||||||
|
Attr::Str(ident) => self
|
||||||
|
.stcs
|
||||||
|
.get(&ident)
|
||||||
|
.map(|res| Some(res.clone()))
|
||||||
|
.map_or_else(
|
||||||
|
|| {
|
||||||
|
if self.dyns.len() > 0 {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Err(Error::DowngradeError(format!(
|
||||||
|
"{} not found",
|
||||||
|
Symbol::from(ident.clone())
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Ok,
|
||||||
|
),
|
||||||
|
_ => Ok(None),
|
||||||
|
},
|
||||||
|
_ => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select(&self, path: &[Attr]) -> Result<Option<Ir>> {
|
||||||
|
let mut path = path.iter();
|
||||||
|
let name = path.next_back().unwrap().clone();
|
||||||
|
self._select(path, name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, TryUnwrap)]
|
#[derive(Clone, Debug, TryUnwrap)]
|
||||||
@@ -478,18 +341,31 @@ pub enum Attr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Attr {
|
impl Attr {
|
||||||
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Attr> {
|
fn resolve<'a, 'env>(
|
||||||
|
self,
|
||||||
|
self_idx: usize,
|
||||||
|
func_idx: Option<usize>,
|
||||||
|
ctx: &mut DowngradeContext,
|
||||||
|
env: &Env<'a, 'env>,
|
||||||
|
) -> Result<Attr> {
|
||||||
use Attr::*;
|
use Attr::*;
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
Dynamic(ir) => Dynamic(ir.resolve(ctx, env)?),
|
Dynamic(ir) => Dynamic(ir.resolve(self_idx, func_idx, ctx, env)?),
|
||||||
other => other,
|
other => other,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Thunk {
|
impl Thunk {
|
||||||
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(
|
||||||
ctx.resolve_thunk(self.idx, env)?;
|
self,
|
||||||
|
self_idx: usize,
|
||||||
|
func_idx: Option<usize>,
|
||||||
|
ctx: &mut DowngradeContext,
|
||||||
|
env: &Env<'a, 'env>,
|
||||||
|
) -> Result<Ir> {
|
||||||
|
ctx.new_dep(self_idx, self.idx);
|
||||||
|
ctx.resolve_thunk(self.idx, func_idx, env)?;
|
||||||
self.ir().ok()
|
self.ir().ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -574,8 +450,14 @@ pub enum Param {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl LoadFunc {
|
impl LoadFunc {
|
||||||
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(
|
||||||
ctx.resolve_func(self.idx, env)?;
|
self,
|
||||||
|
self_idx: usize,
|
||||||
|
_: Option<usize>,
|
||||||
|
ctx: &mut DowngradeContext,
|
||||||
|
env: &Env<'a, 'env>,
|
||||||
|
) -> Result<Ir> {
|
||||||
|
ctx.resolve_func(self_idx, self.idx, env)?;
|
||||||
self.ir().ok()
|
self.ir().ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -626,10 +508,19 @@ impl Downgrade for ast::Assert {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Assert {
|
impl Assert {
|
||||||
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(
|
||||||
|
self,
|
||||||
|
self_idx: usize,
|
||||||
|
func_idx: Option<usize>,
|
||||||
|
ctx: &mut DowngradeContext,
|
||||||
|
env: &Env<'a, 'env>,
|
||||||
|
) -> Result<Ir> {
|
||||||
Self {
|
Self {
|
||||||
assertion: self.assertion.resolve(ctx, env)?.boxed(),
|
assertion: self
|
||||||
expr: self.expr.resolve(ctx, env)?.boxed(),
|
.assertion
|
||||||
|
.resolve(self_idx, func_idx, ctx, env)?
|
||||||
|
.boxed(),
|
||||||
|
expr: self.expr.resolve(self_idx, func_idx, ctx, env)?.boxed(),
|
||||||
}
|
}
|
||||||
.ir()
|
.ir()
|
||||||
.ok()
|
.ok()
|
||||||
@@ -649,11 +540,17 @@ impl Downgrade for ast::IfElse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl If {
|
impl If {
|
||||||
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(
|
||||||
|
self,
|
||||||
|
self_idx: usize,
|
||||||
|
func_idx: Option<usize>,
|
||||||
|
ctx: &mut DowngradeContext,
|
||||||
|
env: &Env<'a, 'env>,
|
||||||
|
) -> Result<Ir> {
|
||||||
If {
|
If {
|
||||||
cond: self.cond.resolve(ctx, env)?.boxed(),
|
cond: self.cond.resolve(self_idx, func_idx, ctx, env)?.boxed(),
|
||||||
consq: self.consq.resolve(ctx, env)?.boxed(),
|
consq: self.consq.resolve(self_idx, func_idx, ctx, env)?.boxed(),
|
||||||
alter: self.alter.resolve(ctx, env)?.boxed(),
|
alter: self.alter.resolve(self_idx, func_idx, ctx, env)?.boxed(),
|
||||||
}
|
}
|
||||||
.ir()
|
.ir()
|
||||||
.ok()
|
.ok()
|
||||||
@@ -690,9 +587,15 @@ impl Downgrade for ast::Path {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Path {
|
impl Path {
|
||||||
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(
|
||||||
|
self,
|
||||||
|
self_idx: usize,
|
||||||
|
func_idx: Option<usize>,
|
||||||
|
ctx: &mut DowngradeContext,
|
||||||
|
env: &Env<'a, 'env>,
|
||||||
|
) -> Result<Ir> {
|
||||||
Self {
|
Self {
|
||||||
expr: self.expr.resolve(ctx, env)?.boxed(),
|
expr: self.expr.resolve(self_idx, func_idx, ctx, env)?.boxed(),
|
||||||
}
|
}
|
||||||
.ir()
|
.ir()
|
||||||
.ok()
|
.ok()
|
||||||
@@ -720,12 +623,18 @@ impl Downgrade for ast::Str {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ConcatStrings {
|
impl ConcatStrings {
|
||||||
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(
|
||||||
|
self,
|
||||||
|
self_idx: usize,
|
||||||
|
func_idx: Option<usize>,
|
||||||
|
ctx: &mut DowngradeContext,
|
||||||
|
env: &Env<'a, 'env>,
|
||||||
|
) -> Result<Ir> {
|
||||||
Self {
|
Self {
|
||||||
parts: self
|
parts: self
|
||||||
.parts
|
.parts
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|ir| ir.resolve(ctx, env))
|
.map(|ir| ir.resolve(self_idx, func_idx, ctx, env))
|
||||||
.collect::<Result<Vec<_>>>()?,
|
.collect::<Result<Vec<_>>>()?,
|
||||||
}
|
}
|
||||||
.ir()
|
.ir()
|
||||||
@@ -748,13 +657,25 @@ impl Downgrade for ast::Literal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Const {
|
impl Const {
|
||||||
fn resolve<'a, 'env>(self, _: &mut DowngradeContext, _: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(
|
||||||
|
self,
|
||||||
|
_: usize,
|
||||||
|
_: Option<usize>,
|
||||||
|
_: &mut DowngradeContext,
|
||||||
|
_: &Env<'a, 'env>,
|
||||||
|
) -> Result<Ir> {
|
||||||
self.ir().ok()
|
self.ir().ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl String {
|
impl String {
|
||||||
fn resolve<'a, 'env>(self, _: &mut DowngradeContext, _: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(
|
||||||
|
self,
|
||||||
|
_: usize,
|
||||||
|
_: Option<usize>,
|
||||||
|
_: &mut DowngradeContext,
|
||||||
|
_: &Env<'a, 'env>,
|
||||||
|
) -> Result<Ir> {
|
||||||
self.ir().ok()
|
self.ir().ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -767,7 +688,13 @@ impl Downgrade for ast::Ident {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Var {
|
impl Var {
|
||||||
fn resolve<'a, 'env>(self, _: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(
|
||||||
|
self,
|
||||||
|
self_idx: usize,
|
||||||
|
func_idx: Option<usize>,
|
||||||
|
ctx: &mut DowngradeContext,
|
||||||
|
env: &Env<'a, 'env>,
|
||||||
|
) -> Result<Ir> {
|
||||||
use LookupResult::*;
|
use LookupResult::*;
|
||||||
let Ok(res) = env.lookup(&self.sym) else {
|
let Ok(res) = env.lookup(&self.sym) else {
|
||||||
return Err(Error::DowngradeError(format!(
|
return Err(Error::DowngradeError(format!(
|
||||||
@@ -785,6 +712,7 @@ impl Var {
|
|||||||
default: default.map(Box::new),
|
default: default.map(Box::new),
|
||||||
}
|
}
|
||||||
.ir(),
|
.ir(),
|
||||||
|
MaybeThunk(thunk) => thunk.resolve(self_idx, func_idx, ctx, env)?,
|
||||||
With => self.ir(),
|
With => self.ir(),
|
||||||
}
|
}
|
||||||
.ok()
|
.ok()
|
||||||
@@ -792,14 +720,26 @@ impl Var {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Arg {
|
impl Arg {
|
||||||
fn resolve<'a, 'env>(self, _: &mut DowngradeContext, _: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(
|
||||||
unreachable!()
|
self,
|
||||||
|
_: usize,
|
||||||
|
_: Option<usize>,
|
||||||
|
_: &mut DowngradeContext,
|
||||||
|
_: &Env<'a, 'env>,
|
||||||
|
) -> Result<Ir> {
|
||||||
|
self.ir().ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LetVar {
|
impl LetVar {
|
||||||
fn resolve<'a, 'env>(self, _: &mut DowngradeContext, _: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(
|
||||||
unreachable!()
|
self,
|
||||||
|
_: usize,
|
||||||
|
_: Option<usize>,
|
||||||
|
_: &mut DowngradeContext,
|
||||||
|
_: &Env<'a, 'env>,
|
||||||
|
) -> Result<Ir> {
|
||||||
|
self.ir().ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -812,6 +752,7 @@ impl Downgrade for ast::AttrSet {
|
|||||||
.stcs
|
.stcs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.sorted_by(|(a, _), (b, _)| a.cmp(b))
|
.sorted_by(|(a, _), (b, _)| a.cmp(b))
|
||||||
|
.map(|(k, v)| (k, ctx.maybe_thunk(v)))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let stcs = bindings
|
let stcs = bindings
|
||||||
.iter()
|
.iter()
|
||||||
@@ -830,18 +771,27 @@ impl Downgrade for ast::AttrSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Attrs {
|
impl Attrs {
|
||||||
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(
|
||||||
|
self,
|
||||||
|
self_idx: usize,
|
||||||
|
func_idx: Option<usize>,
|
||||||
|
ctx: &mut DowngradeContext,
|
||||||
|
env: &Env<'a, 'env>,
|
||||||
|
) -> Result<Ir> {
|
||||||
Self {
|
Self {
|
||||||
stcs: self
|
stcs: self
|
||||||
.stcs
|
.stcs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(k, v)| Ok((k, v.resolve(ctx, env)?)))
|
.map(|(k, v)| Ok((k, v.resolve(self_idx, func_idx, ctx, env)?)))
|
||||||
.collect::<Result<_>>()?,
|
.collect::<Result<_>>()?,
|
||||||
dyns: self
|
dyns: self
|
||||||
.dyns
|
.dyns
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|DynamicAttrPair(k, v)| {
|
.map(|DynAttr(k, v)| {
|
||||||
Ok(DynamicAttrPair(k.resolve(ctx, env)?, v.resolve(ctx, env)?))
|
Ok(DynAttr(
|
||||||
|
k.resolve(self_idx, func_idx, ctx, env)?,
|
||||||
|
v.resolve(self_idx, func_idx, ctx, env)?,
|
||||||
|
))
|
||||||
})
|
})
|
||||||
.collect::<Result<_>>()?,
|
.collect::<Result<_>>()?,
|
||||||
}
|
}
|
||||||
@@ -861,12 +811,18 @@ impl Downgrade for ast::List {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl List {
|
impl List {
|
||||||
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(
|
||||||
|
self,
|
||||||
|
self_idx: usize,
|
||||||
|
func_idx: Option<usize>,
|
||||||
|
ctx: &mut DowngradeContext,
|
||||||
|
env: &Env<'a, 'env>,
|
||||||
|
) -> Result<Ir> {
|
||||||
Self {
|
Self {
|
||||||
items: self
|
items: self
|
||||||
.items
|
.items
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|item| item.resolve(ctx, env))
|
.map(|item| item.resolve(self_idx, func_idx, ctx, env))
|
||||||
.collect::<Result<_>>()?,
|
.collect::<Result<_>>()?,
|
||||||
}
|
}
|
||||||
.ir()
|
.ir()
|
||||||
@@ -887,10 +843,16 @@ impl Downgrade for ast::BinOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BinOp {
|
impl BinOp {
|
||||||
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(
|
||||||
|
self,
|
||||||
|
self_idx: usize,
|
||||||
|
func_idx: Option<usize>,
|
||||||
|
ctx: &mut DowngradeContext,
|
||||||
|
env: &Env<'a, 'env>,
|
||||||
|
) -> Result<Ir> {
|
||||||
Self {
|
Self {
|
||||||
lhs: self.lhs.resolve(ctx, env)?.boxed(),
|
lhs: self.lhs.resolve(self_idx, func_idx, ctx, env)?.boxed(),
|
||||||
rhs: self.rhs.resolve(ctx, env)?.boxed(),
|
rhs: self.rhs.resolve(self_idx, func_idx, ctx, env)?.boxed(),
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
.ir()
|
.ir()
|
||||||
@@ -912,13 +874,19 @@ impl Downgrade for ast::HasAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl HasAttr {
|
impl HasAttr {
|
||||||
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(
|
||||||
|
self,
|
||||||
|
self_idx: usize,
|
||||||
|
func_idx: Option<usize>,
|
||||||
|
ctx: &mut DowngradeContext,
|
||||||
|
env: &Env<'a, 'env>,
|
||||||
|
) -> Result<Ir> {
|
||||||
Self {
|
Self {
|
||||||
lhs: self.lhs.resolve(ctx, env)?.boxed(),
|
lhs: self.lhs.resolve(self_idx, func_idx, ctx, env)?.boxed(),
|
||||||
rhs: self
|
rhs: self
|
||||||
.rhs
|
.rhs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|attr| attr.resolve(ctx, env))
|
.map(|attr| attr.resolve(self_idx, func_idx, ctx, env))
|
||||||
.collect::<Result<_>>()?,
|
.collect::<Result<_>>()?,
|
||||||
}
|
}
|
||||||
.ir()
|
.ir()
|
||||||
@@ -938,9 +906,15 @@ impl Downgrade for ast::UnaryOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl UnOp {
|
impl UnOp {
|
||||||
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(
|
||||||
|
self,
|
||||||
|
self_idx: usize,
|
||||||
|
func_idx: Option<usize>,
|
||||||
|
ctx: &mut DowngradeContext,
|
||||||
|
env: &Env<'a, 'env>,
|
||||||
|
) -> Result<Ir> {
|
||||||
Self {
|
Self {
|
||||||
rhs: self.rhs.resolve(ctx, env)?.boxed(),
|
rhs: self.rhs.resolve(self_idx, func_idx, ctx, env)?.boxed(),
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
.ir()
|
.ir()
|
||||||
@@ -964,25 +938,59 @@ impl Downgrade for ast::Select {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Select {
|
impl Select {
|
||||||
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(
|
||||||
let default = if let Some(default) = self.default {
|
self,
|
||||||
Some(default.resolve(ctx, env)?.boxed())
|
self_idx: usize,
|
||||||
} else {
|
func_idx: Option<usize>,
|
||||||
None
|
ctx: &mut DowngradeContext,
|
||||||
};
|
env: &Env<'a, 'env>,
|
||||||
Self {
|
) -> Result<Ir> {
|
||||||
expr: self.expr.resolve(ctx, env)?.boxed(),
|
let expr = self.expr.resolve(self_idx, func_idx, ctx, env)?;
|
||||||
attrpath: self
|
let attrpath = self
|
||||||
.attrpath
|
.attrpath
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|attr| attr.resolve(ctx, env))
|
.map(|attr| attr.resolve(self_idx, func_idx, ctx, env))
|
||||||
.collect::<Result<_>>()?,
|
.collect::<Result<Vec<_>>>()?;
|
||||||
default,
|
let res = match &expr {
|
||||||
|
Ir::Attrs(attrs) => attrs.select(&attrpath)?,
|
||||||
|
&Ir::Thunk(Thunk { idx }) => {
|
||||||
|
let res = ctx.thunks[idx]
|
||||||
|
.as_ref()
|
||||||
|
.try_unwrap_attrs()
|
||||||
|
.map_err(|_| {
|
||||||
|
Error::DowngradeError("can only select from a attribute set".into())
|
||||||
|
})?
|
||||||
|
.select(&attrpath);
|
||||||
|
match res {
|
||||||
|
Err(err) => {
|
||||||
|
if let Some(default) = self.default.clone() {
|
||||||
|
Ok(Some(default.resolve(self_idx, func_idx, ctx, env)?))
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok => ok,
|
||||||
|
}?
|
||||||
|
}
|
||||||
|
_ => return Err(Error::DowngradeError("can not select from <type>".into())),
|
||||||
|
};
|
||||||
|
if let Some(res) = res {
|
||||||
|
res.ok()
|
||||||
|
} else {
|
||||||
|
Select {
|
||||||
|
expr: expr.boxed(),
|
||||||
|
attrpath,
|
||||||
|
default: if let Some(default) = self.default {
|
||||||
|
Some(default.resolve(self_idx, func_idx, ctx, env)?.boxed())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
}
|
}
|
||||||
.ir()
|
.ir()
|
||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Downgrade for ast::LegacyLet {
|
impl Downgrade for ast::LegacyLet {
|
||||||
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
|
||||||
@@ -1058,35 +1066,26 @@ impl Downgrade for ast::LetIn {
|
|||||||
let bindings = bindings
|
let bindings = bindings
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.sorted_by(|(a, _), (b, _)| a.cmp(b))
|
.sorted_by(|(a, _), (b, _)| a.cmp(b))
|
||||||
|
.map(|(k, v)| (k, ctx.maybe_thunk(v)))
|
||||||
.collect();
|
.collect();
|
||||||
Let { bindings, expr }.ir().ok()
|
Let { bindings, expr }.ir().ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Let {
|
impl Let {
|
||||||
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(
|
||||||
|
self,
|
||||||
|
self_idx: usize,
|
||||||
|
func_idx: Option<usize>,
|
||||||
|
ctx: &mut DowngradeContext,
|
||||||
|
env: &Env<'a, 'env>,
|
||||||
|
) -> Result<Ir> {
|
||||||
let map = self
|
let map = self
|
||||||
.bindings
|
.bindings
|
||||||
.iter()
|
.clone();
|
||||||
.map(|(sym, _)| sym.clone())
|
|
||||||
.sorted()
|
|
||||||
.collect();
|
|
||||||
let env = env.enter_let(&map);
|
let env = env.enter_let(&map);
|
||||||
let bindings = self
|
let expr = self.expr.resolve(self_idx, func_idx, ctx, &env)?.boxed();
|
||||||
.bindings
|
Self { expr, ..self }.ir().ok()
|
||||||
.into_iter()
|
|
||||||
.map(|(k, v)| {
|
|
||||||
Ok((
|
|
||||||
k,
|
|
||||||
match v.resolve(ctx, &env)? {
|
|
||||||
ir @ Ir::Const(_) => ir,
|
|
||||||
ir => ctx.new_thunk(ir).ir(),
|
|
||||||
},
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.collect::<Result<_>>()?;
|
|
||||||
let expr = self.expr.resolve(ctx, &env)?.boxed();
|
|
||||||
Self { bindings, expr }.ir().ok()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1099,9 +1098,21 @@ impl Downgrade for ast::With {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl With {
|
impl With {
|
||||||
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(
|
||||||
let namespace = self.namespace.resolve(ctx, env)?.boxed();
|
self,
|
||||||
let expr = self.expr.resolve(ctx, &env.enter_with())?.boxed();
|
self_idx: usize,
|
||||||
|
func_idx: Option<usize>,
|
||||||
|
ctx: &mut DowngradeContext,
|
||||||
|
env: &Env<'a, 'env>,
|
||||||
|
) -> Result<Ir> {
|
||||||
|
let namespace = self
|
||||||
|
.namespace
|
||||||
|
.resolve(self_idx, func_idx, ctx, env)?
|
||||||
|
.boxed();
|
||||||
|
let expr = self
|
||||||
|
.expr
|
||||||
|
.resolve(self_idx, func_idx, ctx, &env.enter_with())?
|
||||||
|
.boxed();
|
||||||
Self { namespace, expr }.ir().ok()
|
Self { namespace, expr }.ir().ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1116,7 +1127,13 @@ impl Downgrade for ast::Lambda {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Func {
|
impl Func {
|
||||||
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Func> {
|
fn resolve<'a, 'env>(
|
||||||
|
self,
|
||||||
|
self_idx: usize,
|
||||||
|
func_idx: Option<usize>,
|
||||||
|
ctx: &mut DowngradeContext,
|
||||||
|
env: &Env<'a, 'env>,
|
||||||
|
) -> Result<Func> {
|
||||||
let env = match self.param.clone() {
|
let env = match self.param.clone() {
|
||||||
Param::Ident(ident) => env.enter_single_arg(ident),
|
Param::Ident(ident) => env.enter_single_arg(ident),
|
||||||
Param::Formals { formals, alias, .. } => env.enter_multi_arg(
|
Param::Formals { formals, alias, .. } => env.enter_multi_arg(
|
||||||
@@ -1128,7 +1145,7 @@ impl Func {
|
|||||||
alias,
|
alias,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
let body = self.body.resolve(ctx, &env)?.boxed();
|
let body = self.body.resolve(self_idx, func_idx, ctx, &env)?.boxed();
|
||||||
Ok(Self { body, ..self })
|
Ok(Self { body, ..self })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1148,13 +1165,19 @@ impl Downgrade for ast::Apply {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Call {
|
impl Call {
|
||||||
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
|
fn resolve<'a, 'env>(
|
||||||
|
self,
|
||||||
|
self_idx: usize,
|
||||||
|
func_idx: Option<usize>,
|
||||||
|
ctx: &mut DowngradeContext,
|
||||||
|
env: &Env<'a, 'env>,
|
||||||
|
) -> Result<Ir> {
|
||||||
Self {
|
Self {
|
||||||
func: self.func.resolve(ctx, env)?.boxed(),
|
func: self.func.resolve(self_idx, func_idx, ctx, env)?.boxed(),
|
||||||
args: self
|
args: self
|
||||||
.args
|
.args
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|arg| arg.resolve(ctx, env))
|
.map(|arg| arg.resolve(self_idx, func_idx, ctx, env))
|
||||||
.collect::<Result<_>>()?,
|
.collect::<Result<_>>()?,
|
||||||
}
|
}
|
||||||
.ir()
|
.ir()
|
||||||
|
|||||||
169
src/ir/scc.rs
Normal file
169
src/ir/scc.rs
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
use hashbrown::{HashMap, HashSet};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SccNode {
|
||||||
|
id: usize,
|
||||||
|
members: Vec<usize>,
|
||||||
|
deps: HashSet<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct SccGraph {
|
||||||
|
nodes: HashMap<usize, SccNode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SccGraph {
|
||||||
|
fn from(ctx: &DowngradeContext, sccs: Vec<Vec<usize>>) -> Self {
|
||||||
|
let mut graph = SccGraph::default();
|
||||||
|
let mut thunk_to_scc = HashMap::new();
|
||||||
|
|
||||||
|
for (id, members) in sccs.iter().enumerate() {
|
||||||
|
for &thunk_id in members {
|
||||||
|
thunk_to_scc.insert(thunk_id, id);
|
||||||
|
}
|
||||||
|
graph.nodes.insert(
|
||||||
|
id,
|
||||||
|
SccNode {
|
||||||
|
id,
|
||||||
|
members: members.clone(),
|
||||||
|
deps: HashSet::new(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (from_node_id, from_deps) in ctx.deps.iter().enumerate() {
|
||||||
|
let from_scc_id = thunk_to_scc[&from_node_id];
|
||||||
|
for &to_node_id in from_deps {
|
||||||
|
let to_scc_id = thunk_to_scc[&to_node_id];
|
||||||
|
if from_scc_id != to_scc_id {
|
||||||
|
graph
|
||||||
|
.nodes
|
||||||
|
.get_mut(&from_scc_id)
|
||||||
|
.unwrap()
|
||||||
|
.deps
|
||||||
|
.insert(to_scc_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
graph
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sorted(self) -> Vec<SccNode> {
|
||||||
|
let mut in_degrees: HashMap<usize, usize> = self.nodes.keys().map(|&id| (id, 0)).collect();
|
||||||
|
for node in self.nodes.values() {
|
||||||
|
in_degrees.insert(node.id, node.deps.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut reverse_adj: HashMap<usize, Vec<usize>> = HashMap::new();
|
||||||
|
for (node_id, node) in &self.nodes {
|
||||||
|
for &dep_id in &node.deps {
|
||||||
|
reverse_adj.entry(dep_id).or_default().push(*node_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut queue: std::collections::VecDeque<usize> = in_degrees
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(&id, °)| if deg == 0 { Some(id) } else { None })
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
queue.make_contiguous().sort();
|
||||||
|
|
||||||
|
let mut sorted_order = Vec::new();
|
||||||
|
while let Some(u) = queue.pop_front() {
|
||||||
|
sorted_order.push(self.nodes[&u].clone());
|
||||||
|
|
||||||
|
if let Some(dependents) = reverse_adj.get(&u) {
|
||||||
|
for &v in dependents {
|
||||||
|
if let Some(degree) = in_degrees.get_mut(&v) {
|
||||||
|
*degree -= 1;
|
||||||
|
if *degree == 0 {
|
||||||
|
queue.push_back(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sorted_order.len() != self.nodes.len() {
|
||||||
|
panic!("Cycle detected in SCC graph, which is impossible!");
|
||||||
|
}
|
||||||
|
|
||||||
|
sorted_order
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SccAnalyzer<'ctx> {
|
||||||
|
ctx: &'ctx DowngradeContext,
|
||||||
|
index: usize,
|
||||||
|
stack: Vec<usize>,
|
||||||
|
on_stack: HashSet<usize>,
|
||||||
|
indices: HashMap<usize, usize>,
|
||||||
|
low_links: HashMap<usize, usize>,
|
||||||
|
sccs: Vec<Vec<usize>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx> SccAnalyzer<'ctx> {
|
||||||
|
pub fn new(ctx: &'ctx DowngradeContext) -> Self {
|
||||||
|
Self {
|
||||||
|
ctx,
|
||||||
|
index: 0,
|
||||||
|
stack: Vec::new(),
|
||||||
|
on_stack: HashSet::new(),
|
||||||
|
indices: HashMap::new(),
|
||||||
|
low_links: HashMap::new(),
|
||||||
|
sccs: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn analyze(mut self) -> Vec<SccNode> {
|
||||||
|
for idx in 0..self.ctx.thunks.len() {
|
||||||
|
if !self.indices.contains_key(&idx) {
|
||||||
|
self.strong_connect(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SccGraph::from(self.ctx, self.sccs).sorted()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn strong_connect(&mut self, v_id: usize) {
|
||||||
|
self.indices.insert(v_id, self.index);
|
||||||
|
self.low_links.insert(v_id, self.index);
|
||||||
|
self.index += 1;
|
||||||
|
self.stack.push(v_id);
|
||||||
|
self.on_stack.insert(v_id);
|
||||||
|
|
||||||
|
if let Some(deps) = self.ctx.deps.get(v_id) {
|
||||||
|
for &w_id in deps {
|
||||||
|
if !self.indices.contains_key(&w_id) {
|
||||||
|
self.strong_connect(w_id);
|
||||||
|
let v_low_link = *self.low_links.get(&v_id).unwrap();
|
||||||
|
let w_low_link = *self.low_links.get(&w_id).unwrap();
|
||||||
|
if w_low_link < v_low_link {
|
||||||
|
self.low_links.insert(v_id, w_low_link);
|
||||||
|
}
|
||||||
|
} else if self.on_stack.contains(&w_id) {
|
||||||
|
let v_low_link = *self.low_links.get(&v_id).unwrap();
|
||||||
|
let w_index = *self.indices.get(&w_id).unwrap();
|
||||||
|
if w_index < v_low_link {
|
||||||
|
self.low_links.insert(v_id, w_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.low_links[&v_id] == self.indices[&v_id] {
|
||||||
|
let mut scc = Vec::new();
|
||||||
|
loop {
|
||||||
|
let w_id = self.stack.pop().unwrap();
|
||||||
|
self.on_stack.remove(&w_id);
|
||||||
|
scc.push(w_id);
|
||||||
|
if w_id == v_id {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.sccs.push(scc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@ impl Display for Catchable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, IsVariant, Unwrap)]
|
#[derive(Debug, Clone, Copy, IsVariant, Unwrap)]
|
||||||
pub enum Const {
|
pub enum Const {
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Int(i64),
|
Int(i64),
|
||||||
@@ -87,8 +87,3 @@ impl PartialEq for Const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for Const {}
|
impl Eq for Const {}
|
||||||
|
|
||||||
pub enum MaybeThunk {
|
|
||||||
Thunk(usize),
|
|
||||||
Const(Const),
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user