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}