use sui_intern::Symbol;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ExprId(pub u32);
impl ExprId {
#[must_use]
pub fn index(self) -> usize {
self.0 as usize
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Span {
pub start: u32,
pub end: u32,
}
#[derive(Debug, Clone, PartialEq)]
pub enum StrPart {
Literal(String),
Interp(ExprId),
}
#[derive(Debug, Clone, PartialEq)]
pub enum PathPart {
Literal(String),
Interp(ExprId),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PathKind {
Abs,
Rel,
Home,
}
#[derive(Debug, Clone, PartialEq)]
pub enum AttrName {
Ident(Symbol),
Str(Vec<StrPart>),
Dynamic(ExprId),
}
#[derive(Debug, Clone, PartialEq)]
pub enum Binding {
Path { path: Vec<AttrName>, value: ExprId },
Inherit {
from: Option<ExprId>,
attrs: Vec<AttrName>,
},
}
#[derive(Debug, Clone, PartialEq)]
pub struct PatternEntry {
pub name: Symbol,
pub default: Option<ExprId>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Param {
Ident(Symbol),
Pattern {
entries: Vec<PatternEntry>,
ellipsis: bool,
bind: Option<Symbol>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BinOp {
Concat,
Update,
Add,
Sub,
Mul,
Div,
And,
Equal,
Implication,
Less,
LessOrEq,
More,
MoreOrEq,
NotEqual,
Or,
PipeRight,
PipeLeft,
}
impl From<rnix::ast::BinOpKind> for BinOp {
fn from(k: rnix::ast::BinOpKind) -> Self {
use rnix::ast::BinOpKind as K;
match k {
K::Concat => BinOp::Concat,
K::Update => BinOp::Update,
K::Add => BinOp::Add,
K::Sub => BinOp::Sub,
K::Mul => BinOp::Mul,
K::Div => BinOp::Div,
K::And => BinOp::And,
K::Equal => BinOp::Equal,
K::Implication => BinOp::Implication,
K::Less => BinOp::Less,
K::LessOrEq => BinOp::LessOrEq,
K::More => BinOp::More,
K::MoreOrEq => BinOp::MoreOrEq,
K::NotEqual => BinOp::NotEqual,
K::Or => BinOp::Or,
K::PipeRight => BinOp::PipeRight,
K::PipeLeft => BinOp::PipeLeft,
}
}
}
impl BinOp {
#[must_use]
pub fn name(self) -> &'static str {
match self {
BinOp::Concat => "concat",
BinOp::Update => "update",
BinOp::Add => "add",
BinOp::Sub => "sub",
BinOp::Mul => "mul",
BinOp::Div => "div",
BinOp::And => "and",
BinOp::Equal => "eq",
BinOp::Implication => "impl",
BinOp::Less => "lt",
BinOp::LessOrEq => "le",
BinOp::More => "gt",
BinOp::MoreOrEq => "ge",
BinOp::NotEqual => "ne",
BinOp::Or => "or",
BinOp::PipeRight => "pipe-right",
BinOp::PipeLeft => "pipe-left",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum UnaryOp {
Invert,
Negate,
}
impl From<rnix::ast::UnaryOpKind> for UnaryOp {
fn from(k: rnix::ast::UnaryOpKind) -> Self {
match k {
rnix::ast::UnaryOpKind::Invert => UnaryOp::Invert,
rnix::ast::UnaryOpKind::Negate => UnaryOp::Negate,
}
}
}
impl UnaryOp {
#[must_use]
pub fn name(self) -> &'static str {
match self {
UnaryOp::Invert => "invert",
UnaryOp::Negate => "negate",
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Ir {
Int(i64),
Float(f64),
Uri(String),
Ident(Symbol),
Str(Vec<StrPart>),
Path { kind: PathKind, parts: Vec<PathPart> },
SearchPath(String),
Select {
subject: ExprId,
path: Vec<AttrName>,
or_default: Option<ExprId>,
},
HasAttr {
subject: ExprId,
path: Vec<AttrName>,
},
Apply { func: ExprId, arg: ExprId },
Lambda { param: Param, body: ExprId },
LetIn { bindings: Vec<Binding>, body: ExprId },
LegacyLet { bindings: Vec<Binding> },
AttrSet { rec: bool, bindings: Vec<Binding> },
List(Vec<ExprId>),
BinOp {
op: BinOp,
lhs: ExprId,
rhs: ExprId,
},
UnaryOp { op: UnaryOp, expr: ExprId },
IfElse {
condition: ExprId,
then_body: ExprId,
else_body: ExprId,
},
With { namespace: ExprId, body: ExprId },
Assert { condition: ExprId, body: ExprId },
Paren(ExprId),
CurPos,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Program {
pub exprs: Vec<Ir>,
pub spans: Vec<Span>,
pub root: ExprId,
}
impl Program {
#[must_use]
pub fn expr(&self, id: ExprId) -> &Ir {
&self.exprs[id.index()]
}
#[must_use]
pub fn span(&self, id: ExprId) -> Span {
self.spans[id.index()]
}
#[must_use]
pub fn len(&self) -> usize {
self.exprs.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.exprs.is_empty()
}
#[must_use]
pub fn children(&self, id: ExprId) -> Vec<ExprId> {
let mut out = Vec::new();
let push_attrname = |out: &mut Vec<ExprId>, a: &AttrName| match a {
AttrName::Ident(_) => {}
AttrName::Str(parts) => {
for p in parts {
if let StrPart::Interp(e) = p {
out.push(*e);
}
}
}
AttrName::Dynamic(e) => out.push(*e),
};
let push_bindings = |out: &mut Vec<ExprId>, bindings: &[Binding]| {
for b in bindings {
match b {
Binding::Path { path, value } => {
for a in path {
push_attrname(out, a);
}
out.push(*value);
}
Binding::Inherit { from, attrs } => {
if let Some(f) = from {
out.push(*f);
}
for a in attrs {
push_attrname(out, a);
}
}
}
}
};
match self.expr(id) {
Ir::Int(_)
| Ir::Float(_)
| Ir::Uri(_)
| Ir::Ident(_)
| Ir::SearchPath(_)
| Ir::CurPos => {}
Ir::Str(parts) => {
for p in parts {
if let StrPart::Interp(e) = p {
out.push(*e);
}
}
}
Ir::Path { parts, .. } => {
for p in parts {
if let PathPart::Interp(e) = p {
out.push(*e);
}
}
}
Ir::Select {
subject,
path,
or_default,
} => {
out.push(*subject);
for a in path {
push_attrname(&mut out, a);
}
if let Some(d) = or_default {
out.push(*d);
}
}
Ir::HasAttr { subject, path } => {
out.push(*subject);
for a in path {
push_attrname(&mut out, a);
}
}
Ir::Apply { func, arg } => {
out.push(*func);
out.push(*arg);
}
Ir::Lambda { param, body } => {
if let Param::Pattern { entries, .. } = param {
for e in entries {
if let Some(d) = e.default {
out.push(d);
}
}
}
out.push(*body);
}
Ir::LetIn { bindings, body } => {
push_bindings(&mut out, bindings);
out.push(*body);
}
Ir::LegacyLet { bindings } => push_bindings(&mut out, bindings),
Ir::AttrSet { bindings, .. } => push_bindings(&mut out, bindings),
Ir::List(items) => out.extend(items.iter().copied()),
Ir::BinOp { lhs, rhs, .. } => {
out.push(*lhs);
out.push(*rhs);
}
Ir::UnaryOp { expr, .. } => out.push(*expr),
Ir::IfElse {
condition,
then_body,
else_body,
} => {
out.push(*condition);
out.push(*then_body);
out.push(*else_body);
}
Ir::With { namespace, body } => {
out.push(*namespace);
out.push(*body);
}
Ir::Assert { condition, body } => {
out.push(*condition);
out.push(*body);
}
Ir::Paren(e) => out.push(*e),
}
out
}
}