feat: TODO
This commit is contained in:
@@ -150,6 +150,7 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Literal {
|
||||
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Ident {
|
||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||
let sym = self.ident_token().unwrap().to_string();
|
||||
let sym = ctx.new_sym(sym);
|
||||
Ok(ctx.new_expr(Var { sym }.to_hir()))
|
||||
}
|
||||
}
|
||||
@@ -237,8 +238,9 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::LegacyLet {
|
||||
let bindings = attrs.stcs.clone();
|
||||
let body = ctx.new_expr(attrs.to_hir());
|
||||
let expr = ctx.new_expr(Let { bindings, body }.to_hir());
|
||||
let sym = ctx.new_sym("body".into());
|
||||
// The result of a `legacy let` is the `body` attribute of the resulting set.
|
||||
let attrpath = vec![Attr::Str("body".into())];
|
||||
let attrpath = vec![Attr::Str(sym)];
|
||||
Ok(ctx.new_expr(
|
||||
Select {
|
||||
expr,
|
||||
@@ -274,6 +276,7 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Lambda {
|
||||
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
|
||||
let param = downgrade_param(self.param().unwrap(), ctx)?;
|
||||
let mut body = self.body().unwrap().downgrade(ctx)?;
|
||||
let arg = ctx.new_expr(Hir::Arg(()));
|
||||
|
||||
let ident;
|
||||
let required;
|
||||
@@ -281,7 +284,7 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Lambda {
|
||||
match param {
|
||||
Param::Ident(id) => {
|
||||
// Simple case: `x: body`
|
||||
ident = Some(id);
|
||||
ident = Some(ctx.new_sym(id));
|
||||
required = None;
|
||||
allowed = None;
|
||||
}
|
||||
@@ -291,35 +294,36 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Lambda {
|
||||
alias,
|
||||
} => {
|
||||
// Complex case: `{ a, b ? 2, ... }@args: body`
|
||||
ident = alias.clone();
|
||||
let alias = alias.map(|sym| ctx.new_sym(sym));
|
||||
ident = alias;
|
||||
required = Some(
|
||||
formals
|
||||
.iter()
|
||||
.filter(|(_, default)| default.is_none())
|
||||
.map(|(k, _)| k.clone())
|
||||
.map(|(k, _)| ctx.new_sym(k.clone()))
|
||||
.collect(),
|
||||
);
|
||||
allowed = if ellipsis {
|
||||
None // `...` means any attribute is allowed.
|
||||
} else {
|
||||
Some(formals.iter().map(|(k, _)| k.clone()).collect())
|
||||
Some(formals.iter().map(|(k, _)| ctx.new_sym(k.clone())).collect())
|
||||
};
|
||||
|
||||
// Desugar pattern matching in function arguments into a `let` expression.
|
||||
// For example, `({ a, b ? 2 }): a + b` is desugared into:
|
||||
// `arg: let a = arg.a; b = arg.b or 2; in a + b`
|
||||
let arg = ctx.new_expr(Hir::Arg(Arg));
|
||||
let mut bindings: HashMap<_, _> = formals
|
||||
.into_iter()
|
||||
.map(|(k, default)| {
|
||||
// For each formal parameter, create a `Select` expression to extract it from the argument set.
|
||||
// `Arg` represents the raw argument (the attribute set) passed to the function.
|
||||
let k = ctx.new_sym(k);
|
||||
(
|
||||
k.clone(),
|
||||
k,
|
||||
ctx.new_expr(
|
||||
Select {
|
||||
expr: arg,
|
||||
attrpath: vec![Attr::Str(k.clone())],
|
||||
attrpath: vec![Attr::Str(k)],
|
||||
default,
|
||||
}
|
||||
.to_hir(),
|
||||
@@ -329,7 +333,7 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Lambda {
|
||||
.collect();
|
||||
// If there's an alias (`... }@alias`), bind the alias name to the raw argument set.
|
||||
if let Some(alias) = alias {
|
||||
bindings.insert(alias.clone(), arg);
|
||||
bindings.insert(alias, arg);
|
||||
}
|
||||
// Wrap the original function body in the new `let` expression.
|
||||
let let_ = Let { bindings, body };
|
||||
@@ -343,7 +347,7 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Lambda {
|
||||
allowed,
|
||||
};
|
||||
// The function's body and parameters are now stored directly in the `Func` node.
|
||||
Ok(ctx.new_expr(Func { body, param }.to_hir()))
|
||||
Ok(ctx.new_expr(Func { body, param, arg }.to_hir()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,7 @@ use hashbrown::HashMap;
|
||||
|
||||
use nixjit_error::{Error, Result};
|
||||
use nixjit_ir::{
|
||||
Assert, Attr, AttrSet, BinOp, Call, ConcatStrings, Const, ExprId, Func, HasAttr, If, List,
|
||||
Param as IrParam, Path, Select, Str, UnOp, Var, With,
|
||||
Assert, Attr, AttrSet, BinOp, Call, ConcatStrings, Const, ExprId, Func, HasAttr, If, List, Param as IrParam, Path, Select, Str, SymId, UnOp, Var, With
|
||||
};
|
||||
use nixjit_macros::ir;
|
||||
use nixjit_value::format_symbol;
|
||||
@@ -37,6 +36,10 @@ pub trait DowngradeContext {
|
||||
/// Allocates a new HIR expression in the context and returns its ID.
|
||||
fn new_expr(&mut self, expr: Hir) -> ExprId;
|
||||
|
||||
fn new_sym(&mut self, sym: String) -> SymId;
|
||||
|
||||
fn get_sym(&self, id: SymId) -> &str;
|
||||
|
||||
/// Provides temporary mutable access to an expression.
|
||||
fn with_expr_mut<T>(&mut self, id: ExprId, f: impl FnOnce(&mut Hir, &mut Self) -> T) -> T;
|
||||
|
||||
@@ -81,17 +84,12 @@ ir! {
|
||||
// Represents a path expression.
|
||||
Path,
|
||||
// Represents a `let ... in ...` binding.
|
||||
Let { pub bindings: HashMap<String, ExprId>, pub body: ExprId },
|
||||
Let { pub bindings: HashMap<SymId, ExprId>, pub body: ExprId },
|
||||
// Represents a function argument lookup within the body of a function.
|
||||
Arg,
|
||||
Arg(()),
|
||||
Thunk(ExprId)
|
||||
}
|
||||
|
||||
/// A placeholder struct for the `Arg` HIR variant. It signifies that at this point
|
||||
/// in the expression tree, we should be looking up a function argument.
|
||||
#[derive(Debug)]
|
||||
pub struct Arg;
|
||||
|
||||
/// A trait defining operations on attribute sets within the HIR.
|
||||
trait Attrs {
|
||||
/// Inserts a value into the attribute set at a given path.
|
||||
@@ -137,7 +135,7 @@ impl Attrs for AttrSet {
|
||||
// This path segment exists but is not an attrset, which is an error.
|
||||
Error::downgrade_error(format!(
|
||||
"attribute '{}' already defined but is not an attribute set",
|
||||
format_symbol(ident)
|
||||
format_symbol(ctx.get_sym(ident))
|
||||
))
|
||||
})
|
||||
.and_then(|attrs| attrs._insert(path, name, value, ctx))
|
||||
@@ -164,10 +162,10 @@ impl Attrs for AttrSet {
|
||||
// This is the final attribute in the path, so insert the value here.
|
||||
match name {
|
||||
Attr::Str(ident) => {
|
||||
if self.stcs.insert(ident.clone(), value).is_some() {
|
||||
if self.stcs.insert(ident, value).is_some() {
|
||||
return Err(Error::downgrade_error(format!(
|
||||
"attribute '{}' already defined",
|
||||
format_symbol(ident)
|
||||
format_symbol(ctx.get_sym(ident))
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use nixjit_value::format_symbol;
|
||||
use rnix::ast;
|
||||
|
||||
use nixjit_error::{Error, Result};
|
||||
use nixjit_ir::{Attr, AttrSet, ConcatStrings, ExprId, Select, Str, Var};
|
||||
use nixjit_ir::{Attr, AttrSet, ConcatStrings, ExprId, Select, Str, SymId, Var};
|
||||
|
||||
use crate::Hir;
|
||||
|
||||
@@ -121,7 +121,7 @@ pub fn downgrade_attrs(
|
||||
pub fn downgrade_static_attrs(
|
||||
attrs: impl ast::HasEntry,
|
||||
ctx: &mut impl DowngradeContext,
|
||||
) -> Result<HashMap<String, ExprId>> {
|
||||
) -> Result<HashMap<SymId, ExprId>> {
|
||||
let entries = attrs.entries();
|
||||
let mut attrs = AttrSet {
|
||||
stcs: HashMap::new(),
|
||||
@@ -145,7 +145,7 @@ pub fn downgrade_static_attrs(
|
||||
/// `inherit a b;` is translated into `a = a; b = b;` (i.e., bringing variables into scope).
|
||||
pub fn downgrade_inherit(
|
||||
inherit: ast::Inherit,
|
||||
stcs: &mut HashMap<String, ExprId>,
|
||||
stcs: &mut HashMap<SymId, ExprId>,
|
||||
ctx: &mut impl DowngradeContext,
|
||||
) -> Result<()> {
|
||||
// Downgrade the `from` expression if it exists.
|
||||
@@ -181,7 +181,7 @@ pub fn downgrade_inherit(
|
||||
Entry::Occupied(occupied) => {
|
||||
return Err(Error::eval_error(format!(
|
||||
"attribute '{}' already defined",
|
||||
format_symbol(occupied.key())
|
||||
format_symbol(ctx.get_sym(*occupied.key()))
|
||||
)));
|
||||
}
|
||||
Entry::Vacant(vacant) => vacant.insert(ctx.new_expr(expr)),
|
||||
@@ -196,15 +196,15 @@ pub fn downgrade_attr(attr: ast::Attr, ctx: &mut impl DowngradeContext) -> Resul
|
||||
use ast::Attr::*;
|
||||
use ast::InterpolPart::*;
|
||||
match attr {
|
||||
Ident(ident) => Ok(Attr::Str(ident.to_string())),
|
||||
Ident(ident) => Ok(Attr::Str(ctx.new_sym(ident.to_string()))),
|
||||
Str(string) => {
|
||||
let parts = string.normalized_parts();
|
||||
if parts.is_empty() {
|
||||
Ok(Attr::Str("".into()))
|
||||
Ok(Attr::Str(ctx.new_sym("".into())))
|
||||
} else if parts.len() == 1 {
|
||||
// If the string has only one part, it's either a literal or a single interpolation.
|
||||
match parts.into_iter().next().unwrap() {
|
||||
Literal(ident) => Ok(Attr::Str(ident)),
|
||||
Literal(ident) => Ok(Attr::Str(ctx.new_sym(ident))),
|
||||
Interpolation(interpol) => {
|
||||
Ok(Attr::Dynamic(interpol.expr().unwrap().downgrade(ctx)?))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user