Skip to main content

sim_codec_algol/
parse.rs

1//! Parsing root for the Algol codec, aggregating its `tokenize`, `state`,
2//! `origin`, and `rewrite` submodules and exposing the `decode_algol_located`
3//! decode entry points built on top of the Pratt parser.
4
5mod origin;
6mod rewrite;
7mod state;
8mod tokenize;
9
10use crate::pratt::{PrattParser, default_pratt_table};
11use sim_codec::{DecodeBudget, DecodeLimits};
12use sim_kernel::{Expr, LocatedExpr, PrattTable, Result};
13
14pub(crate) use origin::{extend_tree_trivia, tree_origin, with_origin_span};
15pub use state::ParseCx;
16pub use tokenize::{SpannedToken, tokenize_algol_spanned, tokenize_algol_spanned_with_budget};
17
18pub(crate) use rewrite::{raw_number_expr, raw_number_tag};
19
20/// Decodes Algol source into a [`LocatedExpr`], attaching span and trivia
21/// origin so source layout round-trips.
22///
23/// Uses [`crate::default_pratt_table`] and a default decode budget; call
24/// [`decode_algol_located_with_budget`] to supply an explicit budget. Raw
25/// number literals are carried as a tagged extension form and lowered to
26/// concrete number domains later by the runtime decoder.
27pub fn decode_algol_located(
28    codec: sim_kernel::CodecId,
29    source_id: impl Into<String>,
30    source: &str,
31) -> Result<LocatedExpr> {
32    let mut budget = DecodeBudget::new(DecodeLimits::default());
33    decode_algol_located_with_budget(codec, source_id, source, &mut budget)
34}
35
36/// Decodes Algol source into a [`LocatedExpr`] under an explicit decode
37/// `budget`.
38///
39/// The budget bounds token count, nesting depth, and string/trivia sizes so a
40/// hostile input cannot exhaust resources. Otherwise behaves like
41/// [`decode_algol_located`].
42pub fn decode_algol_located_with_budget(
43    codec: sim_kernel::CodecId,
44    source_id: impl Into<String>,
45    source: &str,
46    budget: &mut DecodeBudget,
47) -> Result<LocatedExpr> {
48    let parser = PrattParser::new(default_pratt_table());
49    let source_id = sim_kernel::SourceId(source_id.into());
50    let mut tree =
51        parser.parse_text_tree_with_budget(codec, source_id.0.clone(), source, budget)?;
52    tree.origin = Some(origin::origin_from_algol_source(codec, source_id, source)?);
53    Ok(tree.located())
54}
55
56/// Parses Algol `source` into a bare [`Expr`] using a caller-supplied operator
57/// `table`, lowering raw number literals through `cx`.
58///
59/// This is the entry point the `Shape` engine uses to parse infix grammar with
60/// a custom operator table rather than [`crate::default_pratt_table`]. Number
61/// literals are lowered lossily: a literal no number domain accepts is left as
62/// the tagged raw form rather than raising an error.
63///
64/// # Examples
65///
66/// ```
67/// use sim_codec_algol::{default_pratt_table, parse_algol_expr_with_table};
68/// use sim_kernel::Expr;
69/// use sim_test_support::{core_cx, register_f64_number_domain};
70///
71/// let mut cx = core_cx();
72/// register_f64_number_domain(&mut cx);
73/// let expr = parse_algol_expr_with_table(&mut cx, default_pratt_table(), "1 + 2 * 3").unwrap();
74/// assert!(matches!(expr, Expr::Infix { .. }));
75/// ```
76pub fn parse_algol_expr_with_table(
77    cx: &mut sim_kernel::Cx,
78    table: PrattTable,
79    source: &str,
80) -> Result<Expr> {
81    let mut budget = DecodeBudget::new(DecodeLimits::default());
82    parse_algol_expr_with_table_and_budget(cx, table, source, &mut budget)
83}
84
85/// Parses Algol `source` into a bare [`Expr`] under an explicit decode `budget`,
86/// using a caller-supplied operator `table`.
87///
88/// Identical to [`parse_algol_expr_with_table`] but honors caller limits rather
89/// than hardcoding [`DecodeLimits::default`], so a Shape grammar driven from a
90/// limited runtime context bounds the same way the runtime decode path does.
91pub fn parse_algol_expr_with_table_and_budget(
92    cx: &mut sim_kernel::Cx,
93    table: PrattTable,
94    source: &str,
95    budget: &mut DecodeBudget,
96) -> Result<Expr> {
97    let mut tree = PrattParser::new(table).parse_text_tree_with_budget(
98        sim_kernel::CodecId(0),
99        "<shape>",
100        source,
101        budget,
102    )?;
103    rewrite::rewrite_number_domains_tree_lossy(cx, &mut tree)?;
104    Ok(tree.expr)
105}