feat: TODO

This commit is contained in:
2025-08-28 18:18:35 +08:00
parent 2fbd2a26a9
commit f7131079e5
26 changed files with 580 additions and 580 deletions

View File

@@ -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()))
}
}

View File

@@ -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))
)));
}
}

View File

@@ -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)?))
}