refactor
This commit is contained in:
@@ -2,6 +2,7 @@ use std::fmt::{self, Write as _};
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::ir::*;
|
use crate::ir::*;
|
||||||
|
use crate::value::Symbol;
|
||||||
|
|
||||||
pub(crate) struct CodeBuffer {
|
pub(crate) struct CodeBuffer {
|
||||||
buf: String,
|
buf: String,
|
||||||
@@ -83,7 +84,7 @@ fn joined<Ctx: CodegenContext, I: Iterator, F: Fn(&Ctx, &mut CodeBuffer, I::Item
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! code {
|
macro_rules! code {
|
||||||
($buf:expr, $ctx:expr; $($item:expr),* $(,)?) => {{
|
($buf:expr, $ctx:expr; $($item:expr)*) => {{
|
||||||
$(
|
$(
|
||||||
($item).compile($ctx, $buf);
|
($item).compile($ctx, $buf);
|
||||||
)*
|
)*
|
||||||
@@ -99,8 +100,8 @@ macro_rules! code {
|
|||||||
write!($buf, $fmt, $($arg)*).unwrap()
|
write!($buf, $fmt, $($arg)*).unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
($buf:expr, $lit:literal) => {
|
($buf:expr, $fmt:literal) => {
|
||||||
$buf.push_str($lit)
|
write!($buf, $fmt).unwrap()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +207,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for rnix::TextRange {
|
|||||||
|
|
||||||
pub(crate) trait CodegenContext {
|
pub(crate) trait CodegenContext {
|
||||||
fn get_ir(&self, id: ExprId) -> &Ir;
|
fn get_ir(&self, id: ExprId) -> &Ir;
|
||||||
fn get_sym(&self, id: SymId) -> &str;
|
fn get_sym(&self, id: SymId) -> Symbol<'_>;
|
||||||
fn get_current_dir(&self) -> &Path;
|
fn get_current_dir(&self) -> &Path;
|
||||||
fn get_store_dir(&self) -> &str;
|
fn get_store_dir(&self) -> &str;
|
||||||
fn get_current_source_id(&self) -> usize;
|
fn get_current_source_id(&self) -> usize;
|
||||||
@@ -219,6 +220,12 @@ impl<Ctx: CodegenContext> Compile<Ctx> for ExprId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<Ctx: CodegenContext> Compile<Ctx> for Symbol<'_> {
|
||||||
|
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
||||||
|
quoted(self).compile(ctx, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
||||||
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
||||||
match self {
|
match self {
|
||||||
@@ -260,8 +267,8 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
|||||||
}
|
}
|
||||||
&Ir::Builtin(Builtin { inner: name, .. }) => {
|
&Ir::Builtin(Builtin { inner: name, .. }) => {
|
||||||
code!(buf, ctx;
|
code!(buf, ctx;
|
||||||
"Nix.builtins[",
|
"Nix.builtins["
|
||||||
quoted(ctx.get_sym(name)),
|
ctx.get_sym(name)
|
||||||
"]"
|
"]"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -277,37 +284,37 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
|||||||
let assertion_span = assertion_ir.span();
|
let assertion_span = assertion_ir.span();
|
||||||
|
|
||||||
code!(buf, ctx;
|
code!(buf, ctx;
|
||||||
"Nix.assert(Nix.withContext(\"while evaluating the condition of the assert statement\",",
|
"Nix.assert(Nix.withContext(\"while evaluating the condition of the assert statement\","
|
||||||
assertion_span,
|
assertion_span
|
||||||
",()=>(",
|
",()=>("
|
||||||
assertion_ir,
|
assertion_ir
|
||||||
")),",
|
")),"
|
||||||
ctx.get_ir(expr),
|
ctx.get_ir(expr)
|
||||||
",",
|
","
|
||||||
quoted(assertion_raw),
|
quoted(assertion_raw)
|
||||||
",",
|
","
|
||||||
assert_span,
|
assert_span
|
||||||
")"
|
")"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Ir::CurPos(cur_pos) => {
|
Ir::CurPos(cur_pos) => {
|
||||||
code!(buf, ctx;
|
code!(buf, ctx;
|
||||||
"Nix.mkPos(",
|
"Nix.mkPos("
|
||||||
cur_pos.span,
|
cur_pos.span
|
||||||
")"
|
")"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
&Ir::ReplBinding(ReplBinding { inner: name, .. }) => {
|
&Ir::ReplBinding(ReplBinding { inner: name, .. }) => {
|
||||||
code!(buf, ctx;
|
code!(buf, ctx;
|
||||||
"Nix.getReplBinding("
|
"Nix.getReplBinding("
|
||||||
quoted(ctx.get_sym(name))
|
ctx.get_sym(name)
|
||||||
")"
|
")"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
&Ir::ScopedImportBinding(ScopedImportBinding { inner: name, .. }) => {
|
&Ir::ScopedImportBinding(ScopedImportBinding { inner: name, .. }) => {
|
||||||
code!(buf, ctx;
|
code!(buf, ctx;
|
||||||
"__scope["
|
"__scope["
|
||||||
quoted(ctx.get_sym(name))
|
ctx.get_sym(name)
|
||||||
"]"
|
"]"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -397,22 +404,10 @@ impl<Ctx: CodegenContext> Compile<Ctx> for BinOp {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
PipeL => {
|
PipeL => {
|
||||||
code!(buf, ctx;
|
code!(buf, ctx; "Nix.call(" rhs "," lhs ")");
|
||||||
"Nix.call(",
|
|
||||||
rhs,
|
|
||||||
",",
|
|
||||||
lhs,
|
|
||||||
")"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
PipeR => {
|
PipeR => {
|
||||||
code!(buf, ctx;
|
code!(buf, ctx; "Nix.call(" lhs "," rhs ")");
|
||||||
"Nix.call(",
|
|
||||||
lhs,
|
|
||||||
",",
|
|
||||||
rhs,
|
|
||||||
")"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -421,20 +416,13 @@ impl<Ctx: CodegenContext> Compile<Ctx> for BinOp {
|
|||||||
impl<Ctx: CodegenContext> Compile<Ctx> for UnOp {
|
impl<Ctx: CodegenContext> Compile<Ctx> for UnOp {
|
||||||
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
||||||
use UnOpKind::*;
|
use UnOpKind::*;
|
||||||
|
let rhs = ctx.get_ir(self.rhs);
|
||||||
match self.kind {
|
match self.kind {
|
||||||
Neg => {
|
Neg => {
|
||||||
code!(buf, ctx;
|
code!(buf, ctx; "Nix.op.sub(0n," rhs ")");
|
||||||
"Nix.op.sub(0n,",
|
|
||||||
ctx.get_ir(self.rhs),
|
|
||||||
")"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Not => {
|
Not => {
|
||||||
code!(buf, ctx;
|
code!(buf, ctx; "Nix.op.bnot(" ctx.get_ir(self.rhs) ")");
|
||||||
"Nix.op.bnot(",
|
|
||||||
ctx.get_ir(self.rhs),
|
|
||||||
")"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -454,32 +442,33 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Func {
|
|||||||
{
|
{
|
||||||
code!(buf, "Nix.mkFunction(arg{}=>", id);
|
code!(buf, "Nix.mkFunction(arg{}=>", id);
|
||||||
if has_thunks {
|
if has_thunks {
|
||||||
code!(buf, ctx; "{", &self.thunks, "return ", self.body, "}");
|
code!(buf, ctx; "{" self.thunks "return " self.body "}");
|
||||||
} else {
|
} else {
|
||||||
code!(buf, ctx; "(", self.body, ")");
|
code!(buf, ctx; "(" self.body ")");
|
||||||
}
|
}
|
||||||
code!(buf, ctx;
|
code!(buf, ctx;
|
||||||
",["
|
",["
|
||||||
joined(required.iter(), ",", |ctx: &Ctx, buf, &(sym, _)| {
|
joined(required.iter(), ",", |ctx: &Ctx, buf, &(sym, _)| {
|
||||||
code!(buf, ctx; quoted(ctx.get_sym(sym)));
|
code!(buf, ctx; ctx.get_sym(sym));
|
||||||
})
|
})
|
||||||
"],["
|
"],["
|
||||||
joined(optional.iter(), ",", |ctx: &Ctx, buf, &(sym, _)| {
|
joined(optional.iter(), ",", |ctx: &Ctx, buf, &(sym, _)| {
|
||||||
code!(buf, ctx; quoted(ctx.get_sym(sym)));
|
code!(buf, ctx; ctx.get_sym(sym));
|
||||||
})
|
})
|
||||||
"],{"
|
"],{"
|
||||||
joined(required.iter().chain(optional.iter()), ",", |ctx: &Ctx, buf, &(sym, span)| {
|
joined(required.iter().chain(optional.iter()), ",", |ctx: &Ctx, buf, &(sym, span)| {
|
||||||
code!(buf, ctx; quoted(ctx.get_sym(sym)), ":", span);
|
code!(buf, ctx; ctx.get_sym(sym) ":" span);
|
||||||
})
|
})
|
||||||
"},"
|
"},"
|
||||||
|
ellipsis
|
||||||
|
")"
|
||||||
);
|
);
|
||||||
code!(buf, "{})", ellipsis);
|
|
||||||
} else {
|
} else {
|
||||||
code!(buf, "arg{}=>", id);
|
code!(buf, "arg{}=>", id);
|
||||||
if has_thunks {
|
if has_thunks {
|
||||||
code!(buf, ctx; "{", &self.thunks, "return ", self.body, "}");
|
code!(buf, ctx; "{" self.thunks "return " self.body "}");
|
||||||
} else {
|
} else {
|
||||||
code!(buf, ctx; "(", self.body, ")");
|
code!(buf, ctx; "(" self.body ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -488,12 +477,12 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Func {
|
|||||||
impl<Ctx: CodegenContext> Compile<Ctx> for Call {
|
impl<Ctx: CodegenContext> Compile<Ctx> for Call {
|
||||||
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
||||||
code!(buf, ctx;
|
code!(buf, ctx;
|
||||||
"Nix.call(",
|
"Nix.call("
|
||||||
ctx.get_ir(self.func),
|
ctx.get_ir(self.func)
|
||||||
",",
|
","
|
||||||
ctx.get_ir(self.arg),
|
ctx.get_ir(self.arg)
|
||||||
",",
|
","
|
||||||
self.span,
|
self.span
|
||||||
")"
|
")"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -527,11 +516,8 @@ impl<Ctx: CodegenContext> Compile<Ctx> for TopLevel {
|
|||||||
if self.thunks.is_empty() {
|
if self.thunks.is_empty() {
|
||||||
ctx.get_ir(self.body).compile(ctx, buf);
|
ctx.get_ir(self.body).compile(ctx, buf);
|
||||||
} else {
|
} else {
|
||||||
code!(buf, "(()=>{");
|
let body = ctx.get_ir(self.body);
|
||||||
code!(buf, ctx; &self.thunks);
|
code!(buf, ctx; "(()=>{" self.thunks "return " body "})()");
|
||||||
code!(buf, "return ");
|
|
||||||
ctx.get_ir(self.body).compile(ctx, buf);
|
|
||||||
code!(buf, "})()");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -545,7 +531,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Select {
|
|||||||
",["
|
",["
|
||||||
joined(self.attrpath.iter(), ",", |ctx: &Ctx, buf, attr| {
|
joined(self.attrpath.iter(), ",", |ctx: &Ctx, buf, attr| {
|
||||||
match attr {
|
match attr {
|
||||||
Attr::Str(sym, _) => code!(buf, ctx; quoted(ctx.get_sym(*sym))),
|
Attr::Str(sym, _) => code!(buf, ctx; ctx.get_sym(*sym)),
|
||||||
Attr::Dynamic(expr_id, _) => code!(buf, ctx; ctx.get_ir(*expr_id)),
|
Attr::Dynamic(expr_id, _) => code!(buf, ctx; ctx.get_ir(*expr_id)),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -562,7 +548,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Select {
|
|||||||
",["
|
",["
|
||||||
joined(self.attrpath.iter(), ",", |ctx: &Ctx, buf, attr| {
|
joined(self.attrpath.iter(), ",", |ctx: &Ctx, buf, attr| {
|
||||||
match attr {
|
match attr {
|
||||||
Attr::Str(sym, _) => code!(buf, ctx; quoted(ctx.get_sym(*sym))),
|
Attr::Str(sym, _) => code!(buf, ctx; ctx.get_sym(*sym)),
|
||||||
Attr::Dynamic(expr_id, _) => code!(buf, ctx; ctx.get_ir(*expr_id)),
|
Attr::Dynamic(expr_id, _) => code!(buf, ctx; ctx.get_ir(*expr_id)),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -585,12 +571,12 @@ impl<Ctx: CodegenContext> Compile<Ctx> for AttrSet {
|
|||||||
|
|
||||||
code!(
|
code!(
|
||||||
buf, ctx;
|
buf, ctx;
|
||||||
quoted(key) ":Nix.withContext(\"while evaluating the attribute '" escaped(key) "'\"," val.span() ",()=>(" val "))"
|
key ":Nix.withContext(\"while evaluating the attribute '" escaped(&key) "'\"," val.span() ",()=>(" val "))"
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
"},{"
|
"},{"
|
||||||
joined(self.stcs.iter(), ",", |ctx: &Ctx, buf, (&sym, &(_, span))| {
|
joined(self.stcs.iter(), ",", |ctx: &Ctx, buf, (&sym, &(_, span))| {
|
||||||
code!(buf, ctx; quoted(ctx.get_sym(sym)) ":" span);
|
code!(buf, ctx; ctx.get_sym(sym) ":" span);
|
||||||
})
|
})
|
||||||
"},{dynKeys:["
|
"},{dynKeys:["
|
||||||
joined(self.dyns.iter(), ",", |ctx: &Ctx, buf, (key, _, _)| {
|
joined(self.dyns.iter(), ",", |ctx: &Ctx, buf, (key, _, _)| {
|
||||||
@@ -619,12 +605,12 @@ impl<Ctx: CodegenContext> Compile<Ctx> for AttrSet {
|
|||||||
|
|
||||||
code!(
|
code!(
|
||||||
buf, ctx;
|
buf, ctx;
|
||||||
quoted(key) ":Nix.withContext(\"while evaluating the attribute '" escaped(key) "'\"," val.span() ",()=>(" val "))"
|
key ":Nix.withContext(\"while evaluating the attribute '" escaped(&key) "'\"," val.span() ",()=>(" val "))"
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
"},{"
|
"},{"
|
||||||
joined(self.stcs.iter(), ",", |ctx: &Ctx, buf, (&sym, &(_, span))| {
|
joined(self.stcs.iter(), ",", |ctx: &Ctx, buf, (&sym, &(_, span))| {
|
||||||
code!(buf, ctx; quoted(ctx.get_sym(sym)) ":" span);
|
code!(buf, ctx; ctx.get_sym(sym) ":" span);
|
||||||
})
|
})
|
||||||
"})"
|
"})"
|
||||||
);
|
);
|
||||||
@@ -674,7 +660,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for HasAttr {
|
|||||||
",["
|
",["
|
||||||
joined(self.rhs.iter(), ",", |ctx: &Ctx, buf, attr| {
|
joined(self.rhs.iter(), ",", |ctx: &Ctx, buf, attr| {
|
||||||
match attr {
|
match attr {
|
||||||
Attr::Str(sym, _) => code!(buf, ctx; quoted(ctx.get_sym(*sym))),
|
Attr::Str(sym, _) => code!(buf, ctx; ctx.get_sym(*sym)),
|
||||||
Attr::Dynamic(expr_id, _) => code!(buf, ctx; ctx.get_ir(*expr_id)),
|
Attr::Dynamic(expr_id, _) => code!(buf, ctx; ctx.get_ir(*expr_id)),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,14 +7,15 @@ use rnix::TextRange;
|
|||||||
use string_interner::DefaultStringInterner;
|
use string_interner::DefaultStringInterner;
|
||||||
|
|
||||||
use crate::codegen::{CodegenContext, compile};
|
use crate::codegen::{CodegenContext, compile};
|
||||||
|
use crate::downgrade::*;
|
||||||
use crate::error::{Error, Result, Source};
|
use crate::error::{Error, Result, Source};
|
||||||
use crate::ir::{
|
use crate::ir::{
|
||||||
Arg, ArgId, Bool, Builtin, Downgrade as _, DowngradeContext, ExprId, Ir, Null, ReplBinding,
|
Arg, ArgId, Bool, Builtin, ExprId, Ir, Null, ReplBinding, ScopedImportBinding, SymId, Thunk,
|
||||||
ScopedImportBinding, SymId, Thunk, ToIr as _,
|
ToIr as _,
|
||||||
};
|
};
|
||||||
use crate::runtime::{Runtime, RuntimeContext};
|
use crate::runtime::{Runtime, RuntimeContext};
|
||||||
use crate::store::{Store, StoreBackend, StoreConfig};
|
use crate::store::{Store, StoreBackend, StoreConfig};
|
||||||
use crate::value::Value;
|
use crate::value::{Symbol, Value};
|
||||||
|
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
ctx: Ctx,
|
ctx: Ctx,
|
||||||
@@ -54,7 +55,8 @@ impl Context {
|
|||||||
let code = self.ctx.compile(source, Some(Scope::Repl(scope)))?;
|
let code = self.ctx.compile(source, Some(Scope::Repl(scope)))?;
|
||||||
|
|
||||||
tracing::debug!("Executing JavaScript");
|
tracing::debug!("Executing JavaScript");
|
||||||
self.runtime.eval(format!("Nix.forceShallow({})", code), &mut self.ctx)
|
self.runtime
|
||||||
|
.eval(format!("Nix.forceShallow({})", code), &mut self.ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile(&mut self, source: Source) -> Result<String> {
|
pub fn compile(&mut self, source: Source) -> Result<String> {
|
||||||
@@ -65,7 +67,12 @@ impl Context {
|
|||||||
self.ctx.get_store_dir()
|
self.ctx.get_store_dir()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_binding<'a>(&'a mut self, name: &str, expr: &str, scope: &'a mut HashSet<SymId>) -> Result<Value> {
|
pub fn add_binding<'a>(
|
||||||
|
&'a mut self,
|
||||||
|
name: &str,
|
||||||
|
expr: &str,
|
||||||
|
scope: &'a mut HashSet<SymId>,
|
||||||
|
) -> Result<Value> {
|
||||||
let source = Source::new_repl(expr.to_string())?;
|
let source = Source::new_repl(expr.to_string())?;
|
||||||
let code = self.ctx.compile(source, Some(Scope::Repl(scope)))?;
|
let code = self.ctx.compile(source, Some(Scope::Repl(scope)))?;
|
||||||
|
|
||||||
@@ -186,10 +193,7 @@ impl Ctx {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn downgrade_ctx<'a>(
|
fn downgrade_ctx<'a>(&'a mut self, extra_scope: Option<Scope<'a>>) -> DowngradeCtx<'a> {
|
||||||
&'a mut self,
|
|
||||||
extra_scope: Option<Scope<'a>>,
|
|
||||||
) -> DowngradeCtx<'a> {
|
|
||||||
let global_ref = unsafe { self.global.as_ref() };
|
let global_ref = unsafe { self.global.as_ref() };
|
||||||
DowngradeCtx::new(self, global_ref, extra_scope)
|
DowngradeCtx::new(self, global_ref, extra_scope)
|
||||||
}
|
}
|
||||||
@@ -236,11 +240,7 @@ impl Ctx {
|
|||||||
Ok(code)
|
Ok(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compile_scoped(
|
pub(crate) fn compile_scoped(&mut self, source: Source, scope: Vec<String>) -> Result<String> {
|
||||||
&mut self,
|
|
||||||
source: Source,
|
|
||||||
scope: Vec<String>,
|
|
||||||
) -> Result<String> {
|
|
||||||
use crate::codegen::compile_scoped;
|
use crate::codegen::compile_scoped;
|
||||||
|
|
||||||
tracing::debug!("Parsing Nix expression for scoped import");
|
tracing::debug!("Parsing Nix expression for scoped import");
|
||||||
@@ -262,7 +262,9 @@ impl Ctx {
|
|||||||
);
|
);
|
||||||
|
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
let root = self.downgrade_ctx(Some(scope)).downgrade(root.tree().expr().unwrap())?;
|
let root = self
|
||||||
|
.downgrade_ctx(Some(scope))
|
||||||
|
.downgrade(root.tree().expr().unwrap())?;
|
||||||
|
|
||||||
tracing::debug!("Generating JavaScript code for scoped import");
|
tracing::debug!("Generating JavaScript code for scoped import");
|
||||||
let code = compile_scoped(self.get_ir(root), self);
|
let code = compile_scoped(self.get_ir(root), self);
|
||||||
@@ -275,8 +277,11 @@ impl CodegenContext for Ctx {
|
|||||||
fn get_ir(&self, id: ExprId) -> &Ir {
|
fn get_ir(&self, id: ExprId) -> &Ir {
|
||||||
self.irs.get(id.0).expect("ExprId out of bounds")
|
self.irs.get(id.0).expect("ExprId out of bounds")
|
||||||
}
|
}
|
||||||
fn get_sym(&self, id: SymId) -> &str {
|
fn get_sym(&self, id: SymId) -> Symbol<'_> {
|
||||||
self.symbols.resolve(id).expect("SymId out of bounds")
|
self.symbols
|
||||||
|
.resolve(id)
|
||||||
|
.expect("SymId out of bounds")
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
fn get_current_dir(&self) -> &std::path::Path {
|
fn get_current_dir(&self) -> &std::path::Path {
|
||||||
self.get_current_dir()
|
self.get_current_dir()
|
||||||
@@ -420,7 +425,7 @@ impl DowngradeContext for DowngradeCtx<'_> {
|
|||||||
self.ctx.symbols.get_or_intern(sym)
|
self.ctx.symbols.get_or_intern(sym)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sym(&self, id: SymId) -> &str {
|
fn get_sym(&self, id: SymId) -> Symbol<'_> {
|
||||||
self.ctx.get_sym(id)
|
self.ctx.get_sym(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,14 +5,488 @@ use hashbrown::hash_map::Entry;
|
|||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use itertools::Itertools as _;
|
use itertools::Itertools as _;
|
||||||
use rnix::TextRange;
|
use rnix::TextRange;
|
||||||
use rnix::ast::{self, HasEntry};
|
use rnix::ast::{self, AstToken, Expr, HasEntry};
|
||||||
use rowan::ast::AstNode;
|
use rowan::ast::AstNode;
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result, Source};
|
||||||
use crate::ir::{Attr, AttrSet, ConcatStrings, ExprId, Select, Str, SymId};
|
use crate::ir::*;
|
||||||
use crate::value::format_symbol;
|
use crate::value::Symbol;
|
||||||
|
|
||||||
use super::*;
|
pub trait DowngradeContext {
|
||||||
|
fn downgrade(self, expr: rnix::ast::Expr) -> Result<ExprId>;
|
||||||
|
|
||||||
|
fn new_expr(&mut self, expr: Ir) -> ExprId;
|
||||||
|
fn new_arg(&mut self, span: TextRange) -> ExprId;
|
||||||
|
fn maybe_thunk(&mut self, id: ExprId) -> ExprId;
|
||||||
|
|
||||||
|
fn new_sym(&mut self, sym: String) -> SymId;
|
||||||
|
fn get_sym(&self, id: SymId) -> Symbol<'_>;
|
||||||
|
fn lookup(&mut self, sym: SymId, span: TextRange) -> Result<ExprId>;
|
||||||
|
|
||||||
|
fn get_ir(&self, id: ExprId) -> &Ir;
|
||||||
|
fn replace_ir(&mut self, id: ExprId, expr: Ir);
|
||||||
|
fn reserve_slots(&mut self, slots: usize) -> impl Iterator<Item = ExprId> + Clone + use<Self>;
|
||||||
|
fn get_current_source(&self) -> Source;
|
||||||
|
|
||||||
|
fn with_param_scope<F, R>(&mut self, param: SymId, arg: ExprId, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self) -> R;
|
||||||
|
fn with_let_scope<F, R>(&mut self, bindings: HashMap<SymId, ExprId>, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self) -> R;
|
||||||
|
fn with_with_scope<F, R>(&mut self, namespace: ExprId, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Self) -> R;
|
||||||
|
|
||||||
|
fn push_thunk_scope(&mut self);
|
||||||
|
fn pop_thunk_scope(&mut self) -> Vec<(ExprId, ExprId)>;
|
||||||
|
fn register_thunk(&mut self, slot: ExprId, inner: ExprId);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Downgrade<Ctx: DowngradeContext> {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for Expr {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
use Expr::*;
|
||||||
|
match self {
|
||||||
|
Apply(apply) => apply.downgrade(ctx),
|
||||||
|
Assert(assert) => assert.downgrade(ctx),
|
||||||
|
Error(error) => {
|
||||||
|
let span = error.syntax().text_range();
|
||||||
|
Err(self::Error::downgrade_error(
|
||||||
|
error.to_string(),
|
||||||
|
ctx.get_current_source(),
|
||||||
|
span,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
IfElse(ifelse) => ifelse.downgrade(ctx),
|
||||||
|
Select(select) => select.downgrade(ctx),
|
||||||
|
Str(str) => str.downgrade(ctx),
|
||||||
|
Path(path) => path.downgrade(ctx),
|
||||||
|
Literal(lit) => lit.downgrade(ctx),
|
||||||
|
Lambda(lambda) => lambda.downgrade(ctx),
|
||||||
|
LegacyLet(let_) => let_.downgrade(ctx),
|
||||||
|
LetIn(letin) => letin.downgrade(ctx),
|
||||||
|
List(list) => list.downgrade(ctx),
|
||||||
|
BinOp(op) => op.downgrade(ctx),
|
||||||
|
AttrSet(attrs) => attrs.downgrade(ctx),
|
||||||
|
UnaryOp(op) => op.downgrade(ctx),
|
||||||
|
Ident(ident) => ident.downgrade(ctx),
|
||||||
|
With(with) => with.downgrade(ctx),
|
||||||
|
HasAttr(has) => has.downgrade(ctx),
|
||||||
|
Paren(paren) => paren.expr().unwrap().downgrade(ctx),
|
||||||
|
Root(root) => root.expr().unwrap().downgrade(ctx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Assert {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let assertion = self.condition().unwrap();
|
||||||
|
let assertion_raw = assertion.to_string();
|
||||||
|
let assertion = assertion.downgrade(ctx)?;
|
||||||
|
let expr = self.body().unwrap().downgrade(ctx)?;
|
||||||
|
let span = self.syntax().text_range();
|
||||||
|
Ok(ctx.new_expr(
|
||||||
|
Assert {
|
||||||
|
assertion,
|
||||||
|
expr,
|
||||||
|
assertion_raw,
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
.to_ir(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::IfElse {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let cond = self.condition().unwrap().downgrade(ctx)?;
|
||||||
|
let consq = self.body().unwrap().downgrade(ctx)?;
|
||||||
|
let alter = self.else_body().unwrap().downgrade(ctx)?;
|
||||||
|
let span = self.syntax().text_range();
|
||||||
|
Ok(ctx.new_expr(
|
||||||
|
If {
|
||||||
|
cond,
|
||||||
|
consq,
|
||||||
|
alter,
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
.to_ir(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Path {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let span = self.syntax().text_range();
|
||||||
|
let parts = self
|
||||||
|
.parts()
|
||||||
|
.map(|part| match part {
|
||||||
|
ast::InterpolPart::Literal(lit) => Ok(ctx.new_expr(
|
||||||
|
Str {
|
||||||
|
val: lit.to_string(),
|
||||||
|
span: lit.syntax().text_range(),
|
||||||
|
}
|
||||||
|
.to_ir(),
|
||||||
|
)),
|
||||||
|
ast::InterpolPart::Interpolation(interpol) => {
|
||||||
|
interpol.expr().unwrap().downgrade(ctx)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
|
let expr = if parts.len() == 1 {
|
||||||
|
let part = parts.into_iter().next().unwrap();
|
||||||
|
if let &Ir::Str(Str { ref val, span }) = ctx.get_ir(part)
|
||||||
|
&& let Some(path) = val.strip_prefix("<").map(|path| &path[..path.len() - 1])
|
||||||
|
{
|
||||||
|
ctx.replace_ir(
|
||||||
|
part,
|
||||||
|
Str {
|
||||||
|
val: path.to_string(),
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
.to_ir(),
|
||||||
|
);
|
||||||
|
let sym = ctx.new_sym("findFile".into());
|
||||||
|
let find_file = ctx.new_expr(Builtin { inner: sym, span }.to_ir());
|
||||||
|
let sym = ctx.new_sym("nixPath".into());
|
||||||
|
let nix_path = ctx.new_expr(Builtin { inner: sym, span }.to_ir());
|
||||||
|
let call = ctx.new_expr(
|
||||||
|
Call {
|
||||||
|
func: find_file,
|
||||||
|
arg: nix_path,
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
.to_ir(),
|
||||||
|
);
|
||||||
|
return Ok(ctx.new_expr(
|
||||||
|
Call {
|
||||||
|
func: call,
|
||||||
|
arg: part,
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
.to_ir(),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
part
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.new_expr(
|
||||||
|
ConcatStrings {
|
||||||
|
parts,
|
||||||
|
span,
|
||||||
|
force_string: false,
|
||||||
|
}
|
||||||
|
.to_ir(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
Ok(ctx.new_expr(Path { expr, span }.to_ir()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Str {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let span = self.syntax().text_range();
|
||||||
|
let normalized = self.normalized_parts();
|
||||||
|
let is_single_literal = normalized.len() == 1
|
||||||
|
&& matches!(normalized.first(), Some(ast::InterpolPart::Literal(_)));
|
||||||
|
|
||||||
|
let parts = normalized
|
||||||
|
.into_iter()
|
||||||
|
.map(|part| match part {
|
||||||
|
ast::InterpolPart::Literal(lit) => Ok(ctx.new_expr(Str { val: lit, span }.to_ir())),
|
||||||
|
ast::InterpolPart::Interpolation(interpol) => {
|
||||||
|
let inner = interpol.expr().unwrap().downgrade(ctx)?;
|
||||||
|
Ok(ctx.maybe_thunk(inner))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
|
Ok(if is_single_literal {
|
||||||
|
parts.into_iter().next().unwrap()
|
||||||
|
} else {
|
||||||
|
ctx.new_expr(
|
||||||
|
ConcatStrings {
|
||||||
|
parts,
|
||||||
|
span,
|
||||||
|
force_string: true,
|
||||||
|
}
|
||||||
|
.to_ir(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Literal {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let span = self.syntax().text_range();
|
||||||
|
Ok(ctx.new_expr(match self.kind() {
|
||||||
|
ast::LiteralKind::Integer(int) => Int {
|
||||||
|
inner: int.value().unwrap(),
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
.to_ir(),
|
||||||
|
ast::LiteralKind::Float(float) => Float {
|
||||||
|
inner: float.value().unwrap(),
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
.to_ir(),
|
||||||
|
ast::LiteralKind::Uri(uri) => Str {
|
||||||
|
val: uri.to_string(),
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
.to_ir(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Ident {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let text = self.ident_token().unwrap().to_string();
|
||||||
|
let span = self.syntax().text_range();
|
||||||
|
|
||||||
|
if text == "__curPos" {
|
||||||
|
return Ok(ctx.new_expr(CurPos { span }.to_ir()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let sym = ctx.new_sym(text);
|
||||||
|
ctx.lookup(sym, span)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::AttrSet {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let rec = self.rec_token().is_some();
|
||||||
|
let span = self.syntax().text_range();
|
||||||
|
|
||||||
|
if !rec {
|
||||||
|
let attrs = downgrade_attrs(self, ctx)?;
|
||||||
|
return Ok(ctx.new_expr(attrs.to_ir()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// rec { a = 1; b = a; } => let a = 1; b = a; in { inherit a b; }
|
||||||
|
let entries: Vec<_> = self.entries().collect();
|
||||||
|
downgrade_rec_bindings(entries, ctx, span)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Downgrades a list.
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::List {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let items = self
|
||||||
|
.items()
|
||||||
|
.map(|item| {
|
||||||
|
let id = item.downgrade(ctx)?;
|
||||||
|
Ok(ctx.maybe_thunk(id))
|
||||||
|
})
|
||||||
|
.collect::<Result<_>>()?;
|
||||||
|
let span = self.syntax().text_range();
|
||||||
|
Ok(ctx.new_expr(List { items, span }.to_ir()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Downgrades a binary operation.
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::BinOp {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let lhs = self.lhs().unwrap().downgrade(ctx)?;
|
||||||
|
let rhs = self.rhs().unwrap().downgrade(ctx)?;
|
||||||
|
let kind = self.operator().unwrap().into();
|
||||||
|
let span = self.syntax().text_range();
|
||||||
|
Ok(ctx.new_expr(
|
||||||
|
BinOp {
|
||||||
|
lhs,
|
||||||
|
rhs,
|
||||||
|
kind,
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
.to_ir(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Downgrades a "has attribute" (`?`) expression.
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::HasAttr {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let lhs = self.expr().unwrap().downgrade(ctx)?;
|
||||||
|
let rhs = downgrade_attrpath(self.attrpath().unwrap(), ctx)?;
|
||||||
|
let span = self.syntax().text_range();
|
||||||
|
Ok(ctx.new_expr(HasAttr { lhs, rhs, span }.to_ir()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Downgrades a unary operation.
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::UnaryOp {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let rhs = self.expr().unwrap().downgrade(ctx)?;
|
||||||
|
let kind = self.operator().unwrap().into();
|
||||||
|
let span = self.syntax().text_range();
|
||||||
|
Ok(ctx.new_expr(UnOp { rhs, kind, span }.to_ir()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Downgrades an attribute selection (`.`).
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Select {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let expr = self.expr().unwrap().downgrade(ctx)?;
|
||||||
|
let attrpath = downgrade_attrpath(self.attrpath().unwrap(), ctx)?;
|
||||||
|
let default = if let Some(default) = self.default_expr() {
|
||||||
|
let default_expr = default.downgrade(ctx)?;
|
||||||
|
Some(ctx.maybe_thunk(default_expr))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let span = self.syntax().text_range();
|
||||||
|
Ok(ctx.new_expr(
|
||||||
|
Select {
|
||||||
|
expr,
|
||||||
|
attrpath,
|
||||||
|
default,
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
.to_ir(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Downgrades a `legacy let`, which is essentially a recursive attribute set.
|
||||||
|
/// The body of the `let` is accessed via `let.body`.
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::LegacyLet {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let span = self.syntax().text_range();
|
||||||
|
let entries: Vec<_> = self.entries().collect();
|
||||||
|
let attrset_expr = downgrade_let_bindings(entries, ctx, span, |ctx, binding_keys| {
|
||||||
|
// Create plain attrset as body with inherit
|
||||||
|
let mut attrs = AttrSet {
|
||||||
|
stcs: HashMap::new(),
|
||||||
|
dyns: Vec::new(),
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
|
||||||
|
for sym in binding_keys {
|
||||||
|
let expr = ctx.lookup(*sym, rnix::TextRange::default())?;
|
||||||
|
attrs.stcs.insert(*sym, (expr, rnix::TextRange::default()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ctx.new_expr(attrs.to_ir()))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let body_sym = ctx.new_sym("body".to_string());
|
||||||
|
let select = Select {
|
||||||
|
expr: attrset_expr,
|
||||||
|
attrpath: vec![Attr::Str(body_sym, rnix::TextRange::default())],
|
||||||
|
default: None,
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ctx.new_expr(select.to_ir()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Downgrades a `let ... in ...` expression.
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::LetIn {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let entries: Vec<_> = self.entries().collect();
|
||||||
|
let body_expr = self.body().unwrap();
|
||||||
|
let span = self.syntax().text_range();
|
||||||
|
|
||||||
|
downgrade_let_bindings(entries, ctx, span, |ctx, _binding_keys| {
|
||||||
|
body_expr.downgrade(ctx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Downgrades a `with` expression.
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::With {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
// with namespace; expr
|
||||||
|
let namespace = self.namespace().unwrap().downgrade(ctx)?;
|
||||||
|
|
||||||
|
// Downgrade body in With scope
|
||||||
|
let expr = ctx.with_with_scope(namespace, |ctx| self.body().unwrap().downgrade(ctx))?;
|
||||||
|
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Downgrades a lambda (function) expression.
|
||||||
|
/// This involves desugaring pattern-matching arguments into `let` bindings.
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Lambda {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let raw_param = self.param().unwrap();
|
||||||
|
let arg = ctx.new_arg(raw_param.syntax().text_range());
|
||||||
|
|
||||||
|
ctx.push_thunk_scope();
|
||||||
|
|
||||||
|
let param;
|
||||||
|
let body;
|
||||||
|
|
||||||
|
match raw_param {
|
||||||
|
ast::Param::IdentParam(id) => {
|
||||||
|
// Simple case: `x: body`
|
||||||
|
let param_sym = ctx.new_sym(id.to_string());
|
||||||
|
param = None;
|
||||||
|
|
||||||
|
// Downgrade body in Param scope
|
||||||
|
body = ctx
|
||||||
|
.with_param_scope(param_sym, arg, |ctx| self.body().unwrap().downgrade(ctx))?;
|
||||||
|
}
|
||||||
|
ast::Param::Pattern(pattern) => {
|
||||||
|
let alias = pattern
|
||||||
|
.pat_bind()
|
||||||
|
.map(|alias| ctx.new_sym(alias.ident().unwrap().to_string()));
|
||||||
|
|
||||||
|
let ellipsis = pattern.ellipsis_token().is_some();
|
||||||
|
let pat_entries = pattern.pat_entries();
|
||||||
|
|
||||||
|
let PatternBindings {
|
||||||
|
body: inner_body,
|
||||||
|
required,
|
||||||
|
optional,
|
||||||
|
} = downgrade_pattern_bindings(pat_entries, alias, arg, ctx, |ctx, _| {
|
||||||
|
self.body().unwrap().downgrade(ctx)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
param = Some(Param {
|
||||||
|
required,
|
||||||
|
optional,
|
||||||
|
ellipsis,
|
||||||
|
});
|
||||||
|
|
||||||
|
body = inner_body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let thunks = ctx.pop_thunk_scope();
|
||||||
|
let span = self.syntax().text_range();
|
||||||
|
Ok(ctx.new_expr(
|
||||||
|
Func {
|
||||||
|
body,
|
||||||
|
param,
|
||||||
|
arg,
|
||||||
|
thunks,
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
.to_ir(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Downgrades a function application.
|
||||||
|
/// In Nix, function application is left-associative, so `f a b` should be parsed as `((f a) b)`.
|
||||||
|
/// Each Apply node represents a single function call with one argument.
|
||||||
|
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Apply {
|
||||||
|
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||||
|
let func = self.lambda().unwrap().downgrade(ctx)?;
|
||||||
|
let arg = self.argument().unwrap().downgrade(ctx)?;
|
||||||
|
let arg = ctx.maybe_thunk(arg);
|
||||||
|
let span = self.syntax().text_range();
|
||||||
|
Ok(ctx.new_expr(Call { func, arg, span }.to_ir()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum PendingValue {
|
enum PendingValue {
|
||||||
Expr(ast::Expr),
|
Expr(ast::Expr),
|
||||||
@@ -321,10 +795,7 @@ impl PendingAttrSet {
|
|||||||
|
|
||||||
if self.stcs.contains_key(&sym) {
|
if self.stcs.contains_key(&sym) {
|
||||||
return Err(Error::downgrade_error(
|
return Err(Error::downgrade_error(
|
||||||
format!(
|
format!("attribute '{}' already defined", ctx.get_sym(sym)),
|
||||||
"attribute '{}' already defined",
|
|
||||||
format_symbol(ctx.get_sym(sym))
|
|
||||||
),
|
|
||||||
ctx.get_current_source(),
|
ctx.get_current_source(),
|
||||||
span,
|
span,
|
||||||
));
|
));
|
||||||
@@ -401,7 +872,7 @@ fn make_attrpath_value_entry(path: Vec<ast::Attr>, value: ast::Expr) -> ast::Ent
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Downgrades the entries of a non-recursive attribute set.
|
/// Downgrades the entries of a non-recursive attribute set.
|
||||||
pub fn downgrade_attrs(
|
fn downgrade_attrs(
|
||||||
attrs: impl ast::HasEntry + AstNode,
|
attrs: impl ast::HasEntry + AstNode,
|
||||||
ctx: &mut impl DowngradeContext,
|
ctx: &mut impl DowngradeContext,
|
||||||
) -> Result<AttrSet> {
|
) -> Result<AttrSet> {
|
||||||
@@ -412,7 +883,7 @@ pub fn downgrade_attrs(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Downgrades a single attribute key (part of an attribute path).
|
/// Downgrades a single attribute key (part of an attribute path).
|
||||||
pub fn downgrade_attr(attr: ast::Attr, ctx: &mut impl DowngradeContext) -> Result<Attr> {
|
fn downgrade_attr(attr: ast::Attr, ctx: &mut impl DowngradeContext) -> Result<Attr> {
|
||||||
use ast::Attr::*;
|
use ast::Attr::*;
|
||||||
use ast::InterpolPart::*;
|
use ast::InterpolPart::*;
|
||||||
match attr {
|
match attr {
|
||||||
@@ -462,7 +933,7 @@ pub fn downgrade_attr(attr: ast::Attr, ctx: &mut impl DowngradeContext) -> Resul
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Downgrades an attribute path (e.g., `a.b."${c}".d`) into a `Vec<Attr>`.
|
/// Downgrades an attribute path (e.g., `a.b."${c}".d`) into a `Vec<Attr>`.
|
||||||
pub fn downgrade_attrpath(
|
fn downgrade_attrpath(
|
||||||
attrpath: ast::Attrpath,
|
attrpath: ast::Attrpath,
|
||||||
ctx: &mut impl DowngradeContext,
|
ctx: &mut impl DowngradeContext,
|
||||||
) -> Result<Vec<Attr>> {
|
) -> Result<Vec<Attr>> {
|
||||||
@@ -472,14 +943,14 @@ pub fn downgrade_attrpath(
|
|||||||
.collect::<Result<Vec<_>>>()
|
.collect::<Result<Vec<_>>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PatternBindings {
|
struct PatternBindings {
|
||||||
pub body: ExprId,
|
body: ExprId,
|
||||||
pub required: Vec<(SymId, TextRange)>,
|
required: Vec<(SymId, TextRange)>,
|
||||||
pub optional: Vec<(SymId, TextRange)>,
|
optional: Vec<(SymId, TextRange)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function for Lambda pattern parameters.
|
/// Helper function for Lambda pattern parameters.
|
||||||
pub fn downgrade_pattern_bindings<Ctx>(
|
fn downgrade_pattern_bindings<Ctx>(
|
||||||
pat_entries: impl Iterator<Item = ast::PatEntry>,
|
pat_entries: impl Iterator<Item = ast::PatEntry>,
|
||||||
alias: Option<SymId>,
|
alias: Option<SymId>,
|
||||||
arg: ExprId,
|
arg: ExprId,
|
||||||
@@ -507,7 +978,7 @@ where
|
|||||||
|
|
||||||
if !seen_params.insert(sym) {
|
if !seen_params.insert(sym) {
|
||||||
return Err(Error::downgrade_error(
|
return Err(Error::downgrade_error(
|
||||||
format!("duplicate parameter '{}'", format_symbol(ctx.get_sym(sym))),
|
format!("duplicate parameter '{}'", ctx.get_sym(sym)),
|
||||||
ctx.get_current_source(),
|
ctx.get_current_source(),
|
||||||
span,
|
span,
|
||||||
));
|
));
|
||||||
@@ -603,7 +1074,7 @@ where
|
|||||||
|
|
||||||
/// Downgrades a `let...in` expression. This is a special case of rec attrs
|
/// Downgrades a `let...in` expression. This is a special case of rec attrs
|
||||||
/// that disallows dynamic attributes and has a body expression.
|
/// that disallows dynamic attributes and has a body expression.
|
||||||
pub fn downgrade_let_bindings<Ctx, F>(
|
fn downgrade_let_bindings<Ctx, F>(
|
||||||
entries: Vec<ast::Entry>,
|
entries: Vec<ast::Entry>,
|
||||||
ctx: &mut Ctx,
|
ctx: &mut Ctx,
|
||||||
span: TextRange,
|
span: TextRange,
|
||||||
@@ -619,7 +1090,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Downgrades a `rec` attribute set.
|
/// Downgrades a `rec` attribute set.
|
||||||
pub fn downgrade_rec_bindings<Ctx>(
|
fn downgrade_rec_bindings<Ctx>(
|
||||||
entries: Vec<ast::Entry>,
|
entries: Vec<ast::Entry>,
|
||||||
ctx: &mut Ctx,
|
ctx: &mut Ctx,
|
||||||
span: TextRange,
|
span: TextRange,
|
||||||
@@ -691,7 +1162,7 @@ where
|
|||||||
} else {
|
} else {
|
||||||
return Err(Error::internal(format!(
|
return Err(Error::internal(format!(
|
||||||
"binding '{}' not found",
|
"binding '{}' not found",
|
||||||
format_symbol(ctx.get_sym(sym))
|
ctx.get_sym(sym)
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -733,10 +1204,7 @@ fn collect_binding_syms<Ctx: DowngradeContext, const ALLOW_DYN: bool>(
|
|||||||
for (sym, (_, span)) in &pending.stcs {
|
for (sym, (_, span)) in &pending.stcs {
|
||||||
if !binding_syms.insert(*sym) {
|
if !binding_syms.insert(*sym) {
|
||||||
return Err(Error::downgrade_error(
|
return Err(Error::downgrade_error(
|
||||||
format!(
|
format!("attribute '{}' already defined", ctx.get_sym(*sym)),
|
||||||
"attribute '{}' already defined",
|
|
||||||
format_symbol(ctx.get_sym(*sym))
|
|
||||||
),
|
|
||||||
ctx.get_current_source(),
|
ctx.get_current_source(),
|
||||||
*span,
|
*span,
|
||||||
));
|
));
|
||||||
@@ -3,47 +3,8 @@ use hashbrown::HashMap;
|
|||||||
use rnix::{TextRange, ast};
|
use rnix::{TextRange, ast};
|
||||||
use string_interner::symbol::SymbolU32;
|
use string_interner::symbol::SymbolU32;
|
||||||
|
|
||||||
use crate::error::{Result, Source};
|
|
||||||
use nix_js_macros::ir;
|
use nix_js_macros::ir;
|
||||||
|
|
||||||
mod downgrade;
|
|
||||||
mod utils;
|
|
||||||
|
|
||||||
use utils::*;
|
|
||||||
|
|
||||||
pub use downgrade::Downgrade;
|
|
||||||
|
|
||||||
pub trait DowngradeContext {
|
|
||||||
fn downgrade(self, expr: rnix::ast::Expr) -> Result<ExprId>;
|
|
||||||
|
|
||||||
fn new_expr(&mut self, expr: Ir) -> ExprId;
|
|
||||||
fn new_arg(&mut self, span: TextRange) -> ExprId;
|
|
||||||
fn maybe_thunk(&mut self, id: ExprId) -> ExprId;
|
|
||||||
|
|
||||||
fn new_sym(&mut self, sym: String) -> SymId;
|
|
||||||
fn get_sym(&self, id: SymId) -> &str;
|
|
||||||
fn lookup(&mut self, sym: SymId, span: TextRange) -> Result<ExprId>;
|
|
||||||
|
|
||||||
fn get_ir(&self, id: ExprId) -> &Ir;
|
|
||||||
fn replace_ir(&mut self, id: ExprId, expr: Ir);
|
|
||||||
fn reserve_slots(&mut self, slots: usize) -> impl Iterator<Item = ExprId> + Clone + use<Self>;
|
|
||||||
fn get_current_source(&self) -> Source;
|
|
||||||
|
|
||||||
fn with_param_scope<F, R>(&mut self, param: SymId, arg: ExprId, f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut Self) -> R;
|
|
||||||
fn with_let_scope<F, R>(&mut self, bindings: HashMap<SymId, ExprId>, f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut Self) -> R;
|
|
||||||
fn with_with_scope<F, R>(&mut self, namespace: ExprId, f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut Self) -> R;
|
|
||||||
|
|
||||||
fn push_thunk_scope(&mut self);
|
|
||||||
fn pop_thunk_scope(&mut self) -> Vec<(ExprId, ExprId)>;
|
|
||||||
fn register_thunk(&mut self, slot: ExprId, inner: ExprId);
|
|
||||||
}
|
|
||||||
|
|
||||||
ir! {
|
ir! {
|
||||||
Ir,
|
Ir,
|
||||||
|
|
||||||
|
|||||||
@@ -1,453 +0,0 @@
|
|||||||
// Assume no parse error
|
|
||||||
#![allow(clippy::unwrap_used)]
|
|
||||||
|
|
||||||
use rnix::ast::{self, AstToken, Expr, HasEntry};
|
|
||||||
use rowan::ast::AstNode;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use crate::error::{Error, Result};
|
|
||||||
|
|
||||||
pub trait Downgrade<Ctx: DowngradeContext> {
|
|
||||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for Expr {
|
|
||||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
|
||||||
use Expr::*;
|
|
||||||
match self {
|
|
||||||
Apply(apply) => apply.downgrade(ctx),
|
|
||||||
Assert(assert) => assert.downgrade(ctx),
|
|
||||||
Error(error) => {
|
|
||||||
let span = error.syntax().text_range();
|
|
||||||
Err(self::Error::downgrade_error(
|
|
||||||
error.to_string(),
|
|
||||||
ctx.get_current_source(),
|
|
||||||
span,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
IfElse(ifelse) => ifelse.downgrade(ctx),
|
|
||||||
Select(select) => select.downgrade(ctx),
|
|
||||||
Str(str) => str.downgrade(ctx),
|
|
||||||
Path(path) => path.downgrade(ctx),
|
|
||||||
Literal(lit) => lit.downgrade(ctx),
|
|
||||||
Lambda(lambda) => lambda.downgrade(ctx),
|
|
||||||
LegacyLet(let_) => let_.downgrade(ctx),
|
|
||||||
LetIn(letin) => letin.downgrade(ctx),
|
|
||||||
List(list) => list.downgrade(ctx),
|
|
||||||
BinOp(op) => op.downgrade(ctx),
|
|
||||||
AttrSet(attrs) => attrs.downgrade(ctx),
|
|
||||||
UnaryOp(op) => op.downgrade(ctx),
|
|
||||||
Ident(ident) => ident.downgrade(ctx),
|
|
||||||
With(with) => with.downgrade(ctx),
|
|
||||||
HasAttr(has) => has.downgrade(ctx),
|
|
||||||
Paren(paren) => paren.expr().unwrap().downgrade(ctx),
|
|
||||||
Root(root) => root.expr().unwrap().downgrade(ctx),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Assert {
|
|
||||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
|
||||||
let assertion = self.condition().unwrap();
|
|
||||||
let assertion_raw = assertion.to_string();
|
|
||||||
let assertion = assertion.downgrade(ctx)?;
|
|
||||||
let expr = self.body().unwrap().downgrade(ctx)?;
|
|
||||||
let span = self.syntax().text_range();
|
|
||||||
Ok(ctx.new_expr(
|
|
||||||
Assert {
|
|
||||||
assertion,
|
|
||||||
expr,
|
|
||||||
assertion_raw,
|
|
||||||
span,
|
|
||||||
}
|
|
||||||
.to_ir(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::IfElse {
|
|
||||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
|
||||||
let cond = self.condition().unwrap().downgrade(ctx)?;
|
|
||||||
let consq = self.body().unwrap().downgrade(ctx)?;
|
|
||||||
let alter = self.else_body().unwrap().downgrade(ctx)?;
|
|
||||||
let span = self.syntax().text_range();
|
|
||||||
Ok(ctx.new_expr(
|
|
||||||
If {
|
|
||||||
cond,
|
|
||||||
consq,
|
|
||||||
alter,
|
|
||||||
span,
|
|
||||||
}
|
|
||||||
.to_ir(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Path {
|
|
||||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
|
||||||
let span = self.syntax().text_range();
|
|
||||||
let parts = self
|
|
||||||
.parts()
|
|
||||||
.map(|part| match part {
|
|
||||||
ast::InterpolPart::Literal(lit) => Ok(ctx.new_expr(
|
|
||||||
Str {
|
|
||||||
val: lit.to_string(),
|
|
||||||
span: lit.syntax().text_range(),
|
|
||||||
}
|
|
||||||
.to_ir(),
|
|
||||||
)),
|
|
||||||
ast::InterpolPart::Interpolation(interpol) => {
|
|
||||||
interpol.expr().unwrap().downgrade(ctx)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>>>()?;
|
|
||||||
|
|
||||||
let expr = if parts.len() == 1 {
|
|
||||||
let part = parts.into_iter().next().unwrap();
|
|
||||||
if let &Ir::Str(Str { ref val, span }) = ctx.get_ir(part)
|
|
||||||
&& let Some(path) = val.strip_prefix("<").map(|path| &path[..path.len() - 1])
|
|
||||||
{
|
|
||||||
ctx.replace_ir(
|
|
||||||
part,
|
|
||||||
Str {
|
|
||||||
val: path.to_string(),
|
|
||||||
span,
|
|
||||||
}
|
|
||||||
.to_ir(),
|
|
||||||
);
|
|
||||||
let sym = ctx.new_sym("findFile".into());
|
|
||||||
let find_file = ctx.new_expr(Builtin { inner: sym, span }.to_ir());
|
|
||||||
let sym = ctx.new_sym("nixPath".into());
|
|
||||||
let nix_path = ctx.new_expr(Builtin { inner: sym, span }.to_ir());
|
|
||||||
let call = ctx.new_expr(
|
|
||||||
Call {
|
|
||||||
func: find_file,
|
|
||||||
arg: nix_path,
|
|
||||||
span,
|
|
||||||
}
|
|
||||||
.to_ir(),
|
|
||||||
);
|
|
||||||
return Ok(ctx.new_expr(
|
|
||||||
Call {
|
|
||||||
func: call,
|
|
||||||
arg: part,
|
|
||||||
span,
|
|
||||||
}
|
|
||||||
.to_ir(),
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
part
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ctx.new_expr(
|
|
||||||
ConcatStrings {
|
|
||||||
parts,
|
|
||||||
span,
|
|
||||||
force_string: false,
|
|
||||||
}
|
|
||||||
.to_ir(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
Ok(ctx.new_expr(Path { expr, span }.to_ir()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Str {
|
|
||||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
|
||||||
let span = self.syntax().text_range();
|
|
||||||
let normalized = self.normalized_parts();
|
|
||||||
let is_single_literal = normalized.len() == 1
|
|
||||||
&& matches!(normalized.first(), Some(ast::InterpolPart::Literal(_)));
|
|
||||||
|
|
||||||
let parts = normalized
|
|
||||||
.into_iter()
|
|
||||||
.map(|part| match part {
|
|
||||||
ast::InterpolPart::Literal(lit) => Ok(ctx.new_expr(Str { val: lit, span }.to_ir())),
|
|
||||||
ast::InterpolPart::Interpolation(interpol) => {
|
|
||||||
let inner = interpol.expr().unwrap().downgrade(ctx)?;
|
|
||||||
Ok(ctx.maybe_thunk(inner))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>>>()?;
|
|
||||||
|
|
||||||
Ok(if is_single_literal {
|
|
||||||
parts.into_iter().next().unwrap()
|
|
||||||
} else {
|
|
||||||
ctx.new_expr(
|
|
||||||
ConcatStrings {
|
|
||||||
parts,
|
|
||||||
span,
|
|
||||||
force_string: true,
|
|
||||||
}
|
|
||||||
.to_ir(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Literal {
|
|
||||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
|
||||||
let span = self.syntax().text_range();
|
|
||||||
Ok(ctx.new_expr(match self.kind() {
|
|
||||||
ast::LiteralKind::Integer(int) => Int {
|
|
||||||
inner: int.value().unwrap(),
|
|
||||||
span,
|
|
||||||
}
|
|
||||||
.to_ir(),
|
|
||||||
ast::LiteralKind::Float(float) => Float {
|
|
||||||
inner: float.value().unwrap(),
|
|
||||||
span,
|
|
||||||
}
|
|
||||||
.to_ir(),
|
|
||||||
ast::LiteralKind::Uri(uri) => Str {
|
|
||||||
val: uri.to_string(),
|
|
||||||
span,
|
|
||||||
}
|
|
||||||
.to_ir(),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Ident {
|
|
||||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
|
||||||
let text = self.ident_token().unwrap().to_string();
|
|
||||||
let span = self.syntax().text_range();
|
|
||||||
|
|
||||||
if text == "__curPos" {
|
|
||||||
return Ok(ctx.new_expr(CurPos { span }.to_ir()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let sym = ctx.new_sym(text);
|
|
||||||
ctx.lookup(sym, span)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::AttrSet {
|
|
||||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
|
||||||
let rec = self.rec_token().is_some();
|
|
||||||
let span = self.syntax().text_range();
|
|
||||||
|
|
||||||
if !rec {
|
|
||||||
let attrs = downgrade_attrs(self, ctx)?;
|
|
||||||
return Ok(ctx.new_expr(attrs.to_ir()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// rec { a = 1; b = a; } => let a = 1; b = a; in { inherit a b; }
|
|
||||||
let entries: Vec<_> = self.entries().collect();
|
|
||||||
downgrade_rec_bindings(entries, ctx, span)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Downgrades a list.
|
|
||||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::List {
|
|
||||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
|
||||||
let items = self
|
|
||||||
.items()
|
|
||||||
.map(|item| {
|
|
||||||
let id = item.downgrade(ctx)?;
|
|
||||||
Ok(ctx.maybe_thunk(id))
|
|
||||||
})
|
|
||||||
.collect::<Result<_>>()?;
|
|
||||||
let span = self.syntax().text_range();
|
|
||||||
Ok(ctx.new_expr(List { items, span }.to_ir()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Downgrades a binary operation.
|
|
||||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::BinOp {
|
|
||||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
|
||||||
let lhs = self.lhs().unwrap().downgrade(ctx)?;
|
|
||||||
let rhs = self.rhs().unwrap().downgrade(ctx)?;
|
|
||||||
let kind = self.operator().unwrap().into();
|
|
||||||
let span = self.syntax().text_range();
|
|
||||||
Ok(ctx.new_expr(
|
|
||||||
BinOp {
|
|
||||||
lhs,
|
|
||||||
rhs,
|
|
||||||
kind,
|
|
||||||
span,
|
|
||||||
}
|
|
||||||
.to_ir(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Downgrades a "has attribute" (`?`) expression.
|
|
||||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::HasAttr {
|
|
||||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
|
||||||
let lhs = self.expr().unwrap().downgrade(ctx)?;
|
|
||||||
let rhs = downgrade_attrpath(self.attrpath().unwrap(), ctx)?;
|
|
||||||
let span = self.syntax().text_range();
|
|
||||||
Ok(ctx.new_expr(HasAttr { lhs, rhs, span }.to_ir()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Downgrades a unary operation.
|
|
||||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::UnaryOp {
|
|
||||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
|
||||||
let rhs = self.expr().unwrap().downgrade(ctx)?;
|
|
||||||
let kind = self.operator().unwrap().into();
|
|
||||||
let span = self.syntax().text_range();
|
|
||||||
Ok(ctx.new_expr(UnOp { rhs, kind, span }.to_ir()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Downgrades an attribute selection (`.`).
|
|
||||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Select {
|
|
||||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
|
||||||
let expr = self.expr().unwrap().downgrade(ctx)?;
|
|
||||||
let attrpath = downgrade_attrpath(self.attrpath().unwrap(), ctx)?;
|
|
||||||
let default = if let Some(default) = self.default_expr() {
|
|
||||||
let default_expr = default.downgrade(ctx)?;
|
|
||||||
Some(ctx.maybe_thunk(default_expr))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let span = self.syntax().text_range();
|
|
||||||
Ok(ctx.new_expr(
|
|
||||||
Select {
|
|
||||||
expr,
|
|
||||||
attrpath,
|
|
||||||
default,
|
|
||||||
span,
|
|
||||||
}
|
|
||||||
.to_ir(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Downgrades a `legacy let`, which is essentially a recursive attribute set.
|
|
||||||
/// The body of the `let` is accessed via `let.body`.
|
|
||||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::LegacyLet {
|
|
||||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
|
||||||
let span = self.syntax().text_range();
|
|
||||||
let entries: Vec<_> = self.entries().collect();
|
|
||||||
let attrset_expr = downgrade_let_bindings(entries, ctx, span, |ctx, binding_keys| {
|
|
||||||
// Create plain attrset as body with inherit
|
|
||||||
let mut attrs = AttrSet {
|
|
||||||
stcs: HashMap::new(),
|
|
||||||
dyns: Vec::new(),
|
|
||||||
span,
|
|
||||||
};
|
|
||||||
|
|
||||||
for sym in binding_keys {
|
|
||||||
let expr = ctx.lookup(*sym, rnix::TextRange::default())?;
|
|
||||||
attrs.stcs.insert(*sym, (expr, rnix::TextRange::default()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(ctx.new_expr(attrs.to_ir()))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let body_sym = ctx.new_sym("body".to_string());
|
|
||||||
let select = Select {
|
|
||||||
expr: attrset_expr,
|
|
||||||
attrpath: vec![Attr::Str(body_sym, rnix::TextRange::default())],
|
|
||||||
default: None,
|
|
||||||
span,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(ctx.new_expr(select.to_ir()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Downgrades a `let ... in ...` expression.
|
|
||||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::LetIn {
|
|
||||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
|
||||||
let entries: Vec<_> = self.entries().collect();
|
|
||||||
let body_expr = self.body().unwrap();
|
|
||||||
let span = self.syntax().text_range();
|
|
||||||
|
|
||||||
downgrade_let_bindings(entries, ctx, span, |ctx, _binding_keys| {
|
|
||||||
body_expr.downgrade(ctx)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Downgrades a `with` expression.
|
|
||||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::With {
|
|
||||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
|
||||||
// with namespace; expr
|
|
||||||
let namespace = self.namespace().unwrap().downgrade(ctx)?;
|
|
||||||
|
|
||||||
// Downgrade body in With scope
|
|
||||||
let expr = ctx.with_with_scope(namespace, |ctx| self.body().unwrap().downgrade(ctx))?;
|
|
||||||
|
|
||||||
Ok(expr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Downgrades a lambda (function) expression.
|
|
||||||
/// This involves desugaring pattern-matching arguments into `let` bindings.
|
|
||||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Lambda {
|
|
||||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
|
||||||
let raw_param = self.param().unwrap();
|
|
||||||
let arg = ctx.new_arg(raw_param.syntax().text_range());
|
|
||||||
|
|
||||||
ctx.push_thunk_scope();
|
|
||||||
|
|
||||||
let param;
|
|
||||||
let body;
|
|
||||||
|
|
||||||
match raw_param {
|
|
||||||
ast::Param::IdentParam(id) => {
|
|
||||||
// Simple case: `x: body`
|
|
||||||
let param_sym = ctx.new_sym(id.to_string());
|
|
||||||
param = None;
|
|
||||||
|
|
||||||
// Downgrade body in Param scope
|
|
||||||
body = ctx
|
|
||||||
.with_param_scope(param_sym, arg, |ctx| self.body().unwrap().downgrade(ctx))?;
|
|
||||||
}
|
|
||||||
ast::Param::Pattern(pattern) => {
|
|
||||||
let alias = pattern
|
|
||||||
.pat_bind()
|
|
||||||
.map(|alias| ctx.new_sym(alias.ident().unwrap().to_string()));
|
|
||||||
|
|
||||||
let ellipsis = pattern.ellipsis_token().is_some();
|
|
||||||
let pat_entries = pattern.pat_entries();
|
|
||||||
|
|
||||||
let PatternBindings {
|
|
||||||
body: inner_body,
|
|
||||||
required,
|
|
||||||
optional,
|
|
||||||
} = downgrade_pattern_bindings(pat_entries, alias, arg, ctx, |ctx, _| {
|
|
||||||
self.body().unwrap().downgrade(ctx)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
param = Some(Param {
|
|
||||||
required,
|
|
||||||
optional,
|
|
||||||
ellipsis,
|
|
||||||
});
|
|
||||||
|
|
||||||
body = inner_body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let thunks = ctx.pop_thunk_scope();
|
|
||||||
let span = self.syntax().text_range();
|
|
||||||
Ok(ctx.new_expr(
|
|
||||||
Func {
|
|
||||||
body,
|
|
||||||
param,
|
|
||||||
arg,
|
|
||||||
thunks,
|
|
||||||
span,
|
|
||||||
}
|
|
||||||
.to_ir(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Downgrades a function application.
|
|
||||||
/// In Nix, function application is left-associative, so `f a b` should be parsed as `((f a) b)`.
|
|
||||||
/// Each Apply node represents a single function call with one argument.
|
|
||||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Apply {
|
|
||||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
|
||||||
let func = self.lambda().unwrap().downgrade(ctx)?;
|
|
||||||
let arg = self.argument().unwrap().downgrade(ctx)?;
|
|
||||||
let arg = ctx.maybe_thunk(arg);
|
|
||||||
let span = self.syntax().text_range();
|
|
||||||
Ok(ctx.new_expr(Call { func, arg, span }.to_ir()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,7 @@ pub mod logging;
|
|||||||
pub mod value;
|
pub mod value;
|
||||||
|
|
||||||
mod codegen;
|
mod codegen;
|
||||||
|
mod downgrade;
|
||||||
mod fetcher;
|
mod fetcher;
|
||||||
mod ir;
|
mod ir;
|
||||||
mod nar;
|
mod nar;
|
||||||
|
|||||||
@@ -332,7 +332,7 @@ fn to_value<'a>(
|
|||||||
let val = val.get(scope, key).expect("infallible operation");
|
let val = val.get(scope, key).expect("infallible operation");
|
||||||
let key = key.to_rust_string_lossy(scope);
|
let key = key.to_rust_string_lossy(scope);
|
||||||
(
|
(
|
||||||
Symbol::new(key),
|
Symbol::from(key),
|
||||||
to_value(
|
to_value(
|
||||||
val,
|
val,
|
||||||
scope,
|
scope,
|
||||||
|
|||||||
@@ -10,11 +10,19 @@ use derive_more::{Constructor, IsVariant, Unwrap};
|
|||||||
|
|
||||||
/// Represents a Nix symbol, which is used as a key in attribute sets.
|
/// Represents a Nix symbol, which is used as a key in attribute sets.
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Constructor)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Constructor)]
|
||||||
pub struct Symbol(String);
|
pub struct Symbol<'a>(Cow<'a, str>);
|
||||||
|
|
||||||
impl<T: Into<String>> From<T> for Symbol {
|
pub type StaticSymbol = Symbol<'static>;
|
||||||
fn from(value: T) -> Self {
|
|
||||||
Symbol(value.into())
|
impl From<String> for Symbol<'_> {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Symbol(Cow::Owned(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a str> for Symbol<'a> {
|
||||||
|
fn from(value: &'a str) -> Self {
|
||||||
|
Symbol(Cow::Borrowed(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +36,7 @@ pub fn format_symbol<'a>(sym: impl Into<Cow<'a, str>>) -> Cow<'a, str> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Symbol {
|
impl Display for Symbol<'_> {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||||
if self.normal() {
|
if self.normal() {
|
||||||
write!(f, "{}", self.0)
|
write!(f, "{}", self.0)
|
||||||
@@ -38,7 +46,7 @@ impl Display for Symbol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Symbol {
|
impl Symbol<'_> {
|
||||||
const NORMAL_REGEX: ere::Regex<1> = ere::compile_regex!("^[a-zA-Z_][a-zA-Z0-9_'-]*$");
|
const NORMAL_REGEX: ere::Regex<1> = ere::compile_regex!("^[a-zA-Z_][a-zA-Z0-9_'-]*$");
|
||||||
/// Checks if the symbol is a "normal" identifier that doesn't require quotes.
|
/// Checks if the symbol is a "normal" identifier that doesn't require quotes.
|
||||||
fn normal(&self) -> bool {
|
fn normal(&self) -> bool {
|
||||||
@@ -46,45 +54,33 @@ impl Symbol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Symbol {
|
impl Deref for Symbol<'_> {
|
||||||
type Target = str;
|
type Target = str;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Symbol {
|
|
||||||
/// Consumes the `Symbol`, returning its inner `String`.
|
|
||||||
pub fn into_inner(self) -> String {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a reference to the inner `String`.
|
|
||||||
pub fn as_inner(&self) -> &String {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a Nix attribute set, which is a map from symbols to values.
|
/// Represents a Nix attribute set, which is a map from symbols to values.
|
||||||
#[derive(Constructor, Default, Clone, PartialEq)]
|
#[derive(Constructor, Default, Clone, PartialEq)]
|
||||||
pub struct AttrSet {
|
pub struct AttrSet {
|
||||||
data: BTreeMap<Symbol, Value>,
|
data: BTreeMap<StaticSymbol, Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AttrSet {
|
impl AttrSet {
|
||||||
/// Gets a value by key (string or Symbol).
|
/// Gets a value by key (string or Symbol).
|
||||||
pub fn get(&self, key: impl Into<Symbol>) -> Option<&Value> {
|
pub fn get<'a, 'sym: 'a>(&'a self, key: impl Into<Symbol<'sym>>) -> Option<&'a Value> {
|
||||||
self.data.get(&key.into())
|
self.data.get(&key.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if a key exists in the attribute set.
|
/// Checks if a key exists in the attribute set.
|
||||||
pub fn contains_key(&self, key: impl Into<Symbol>) -> bool {
|
pub fn contains_key<'a, 'sym: 'a>(&'a self, key: impl Into<Symbol<'sym>>) -> bool {
|
||||||
self.data.contains_key(&key.into())
|
self.data.contains_key(&key.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for AttrSet {
|
impl Deref for AttrSet {
|
||||||
type Target = BTreeMap<Symbol, Value>;
|
type Target = BTreeMap<StaticSymbol, Value>;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.data
|
&self.data
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user