Skip to main content

rustyfi_lang/
quoted.rs

1//! Quoted text in its **compiled** form — what `{ … }` / `'< … >` / `${ … }`
2//! become once `crate::compile` has lowered them, and what
3//! [`crate::value::Value`]'s `InlineText`/`BlockText`/`MathText` variants
4//! carry.
5//!
6//! # Why these exist
7//!
8//! The compiler *does* know the lexical scope at a quote site — it is exactly
9//! the scope stack at that point — so command names and embedded expressions
10//! are resolved there, once, like any other expression, rather than lazily by
11//! string at layout time. What survives into the runtime is this name-free
12//! tree: every `Cmd` already holds the `CompiledExpr` that yields its
13//! command value, and every argument is already compiled. Nothing here is ever
14//! looked up by name.
15//!
16//! The captured `Env` is still needed — a compiled node resolves its *locals*
17//! against the environment it runs in.
18//!
19//! # Shape
20//!
21//! Deliberately mirrors [`crate::ast`]'s `IText`/`BText`/`MathElem`/`CmdArg`
22//! one-for-one, so the structural walks in `primitives.rs` (`read_inline`,
23//! `read_block`, `reflect_math_elem*`, `layout_math_elem`, …) are unchanged
24//! apart from how a `Cmd` obtains its command and how an argument is
25//! evaluated. Only two fields differ: a `Cmd`'s `name: String` became a
26//! resolved `cmd: CompiledExpr`, and an `Embed`'s `expr: Ast` became a
27//! compiled one.
28
29use crate::ast::Ast;
30use crate::compile::CompiledExpr;
31use crate::value::BaseEnv;
32use rustyfi_syntax::Span;
33use std::rc::Rc;
34
35/// One command-application argument: the positional argument plus its
36/// (usually empty) `?(l = e, …)` labeled-optional bundle. Labels stay text —
37/// they are matched against a closure's declared labels, not looked up in an
38/// environment.
39#[allow(private_interfaces)]
40#[derive(Clone, Debug)]
41pub struct CmdArg {
42    pub opts: Vec<(String, CompiledExpr)>,
43    pub arg: CompiledExpr,
44}
45
46/// One inline-text element (the compiled mirror of [`crate::ast::IText`]).
47#[allow(private_interfaces)]
48#[derive(Clone, Debug)]
49pub enum IText {
50    Text(String),
51    /// A backtick literal, dispatched at box-building time through the
52    /// context's `code_text_command` (see `read_inline`).
53    CodeText(String),
54    Cmd {
55        /// Yields the command's value — the `\emph` binding, already resolved
56        /// against the quote site's lexical scope. Running it can still fail
57        /// with "unbound inline command '…' at run time", for the defensive
58        /// case where the name was in no compile-time scope.
59        cmd: CompiledExpr,
60        args: Vec<CmdArg>,
61    },
62    /// `#expr;` — an embedded expression evaluating to inline-text.
63    Embed {
64        expr: CompiledExpr,
65        span: Span,
66    },
67    /// `${…}` embedded math.
68    EmbedMath {
69        elems: Rc<Vec<MathElem>>,
70        span: Span,
71    },
72}
73
74/// One block-text element (the compiled mirror of [`crate::ast::BText`]).
75#[allow(private_interfaces)]
76#[derive(Clone, Debug)]
77pub enum BText {
78    Cmd {
79        /// See [`IText::Cmd::cmd`].
80        cmd: CompiledExpr,
81        args: Vec<CmdArg>,
82    },
83    Embed {
84        expr: CompiledExpr,
85        span: Span,
86    },
87}
88
89/// One quoted-math element (the compiled mirror of [`crate::ast::MathElem`]).
90#[allow(private_interfaces)]
91#[derive(Clone, Debug)]
92pub enum MathElem {
93    Chars(String),
94    Group(Vec<MathElem>),
95    Sub(Box<MathElem>, Vec<MathElem>),
96    Sup(Box<MathElem>, Vec<MathElem>),
97    Primes(Box<MathElem>, usize),
98    Cmd {
99        /// See [`IText::Cmd::cmd`].
100        cmd: CompiledExpr,
101        /// Kept purely for diagnostics: two `reflect_*` arms name the command
102        /// in a "not valid here" error. Never used to look anything up.
103        name: Rc<str>,
104        span: Span,
105        args: Vec<CmdArg>,
106    },
107    Embed {
108        expr: CompiledExpr,
109        span: Span,
110    },
111}
112
113impl IText {
114    /// [`crate::primitives::read_inline`] is public and takes these elements,
115    /// so there has to be a way to build one from outside the crate — but
116    /// `CompiledExpr` is deliberately crate-internal, so the compile step
117    /// happens here rather than in the caller. `env` plays the role the
118    /// enclosing compiler's lexical scope plays for a real quote site: it is
119    /// what free names in `expr` are resolved against.
120    pub fn embed(expr: &Ast, env: &BaseEnv, span: Span) -> IText {
121        IText::Embed {
122            expr: crate::compile::compile_program(expr, env),
123            span,
124        }
125    }
126}