Skip to main content

hugr_model/v0/ast/
parse.rs

1// NOTE: We use the `pest` library for parsing. This library is convenient, but
2// performance is mediocre. In the case that we find that parsing is too slow,
3// we can replace the parser.
4
5// NOTE: The `pest` library returns a parsed AST which we then transform into
6// our AST data structures. The `pest` AST is guaranteed to conform to the
7// grammar, but this is not automatically visible from the types. Therefore this
8// module contains many `unwrap`s and `unreachable!`s, which will not fail on
9// any input unless there is a bug in the parser. This is perhaps aesthetically
10// unsatisfying but it is aligned with the intended usage pattern of `pest`.
11
12// NOTE: The `parse_` functions are implementation details since they refer to
13// `pest` data structures. We expose parsing via implementations of the
14// `FromStr` trait.
15
16use std::str::FromStr;
17use std::sync::Arc;
18
19use base64::Engine as _;
20use base64::prelude::BASE64_STANDARD;
21use ordered_float::OrderedFloat;
22use pest::Parser as _;
23use pest::iterators::{Pair, Pairs};
24use pest_parser::{HugrParser, Rule};
25use smol_str::SmolStr;
26use thiserror::Error;
27
28use crate::v0::ast::{LinkName, Module, Operation, SeqPart, SymbolIdent};
29use crate::v0::{Literal, RegionKind};
30
31use super::{Node, Package, Param, Region, Symbol, VarName, Visibility};
32use super::{SymbolName, Term};
33
34mod pest_parser {
35    use pest_derive::Parser;
36
37    // NOTE: The pest derive macro generates a `Rule` enum. We do not want this to be
38    // part of the public API, and so we hide it within this private module.
39
40    #[derive(Parser)]
41    #[grammar = "v0/ast/hugr.pest"]
42    pub struct HugrParser;
43}
44
45fn parse_symbol_name(pair: Pair<Rule>) -> ParseResult<SymbolName> {
46    debug_assert_eq!(Rule::symbol_name, pair.as_rule());
47    Ok(SymbolName(pair.as_str().into()))
48}
49
50fn parse_version(pair: Pair<Rule>) -> ParseResult<semver::Version> {
51    debug_assert_eq!(Rule::version, pair.as_rule());
52    pair.as_str()
53        .parse()
54        .map_err(|_| ParseError::custom("invalid semver version", pair.as_span()))
55}
56
57fn parse_symbol_ident(pair: Pair<Rule>) -> ParseResult<SymbolIdent> {
58    debug_assert_eq!(Rule::symbol_ident, pair.as_rule());
59    let mut pairs = pair.into_inner();
60    let name = parse_symbol_name(pairs.next().unwrap())?;
61    let version = pairs.next().map(parse_version).transpose()?;
62    Ok(SymbolIdent { name, version })
63}
64
65fn parse_var_name(pair: Pair<Rule>) -> ParseResult<VarName> {
66    debug_assert_eq!(Rule::term_var, pair.as_rule());
67    Ok(VarName(pair.as_str()[1..].into()))
68}
69
70fn parse_link_name(pair: Pair<Rule>) -> ParseResult<LinkName> {
71    debug_assert_eq!(Rule::link_name, pair.as_rule());
72    Ok(LinkName(pair.as_str()[1..].into()))
73}
74
75fn parse_term(pair: Pair<Rule>) -> ParseResult<Term> {
76    debug_assert_eq!(Rule::term, pair.as_rule());
77    let pair = pair.into_inner().next().unwrap();
78
79    Ok(match pair.as_rule() {
80        Rule::term_wildcard => Term::Wildcard,
81        Rule::term_var => Term::Var(parse_var_name(pair)?),
82        Rule::term_apply => {
83            let mut pairs = pair.into_inner();
84            let symbol = parse_symbol_ident(pairs.next().unwrap())?;
85            let terms = pairs.map(parse_term).collect::<ParseResult<_>>()?;
86            Term::Apply(symbol, terms)
87        }
88        Rule::term_list => {
89            let pairs = pair.into_inner();
90            let parts = pairs.map(parse_seq_part).collect::<ParseResult<_>>()?;
91            Term::List(parts)
92        }
93        Rule::term_tuple => {
94            let pairs = pair.into_inner();
95            let parts = pairs.map(parse_seq_part).collect::<ParseResult<_>>()?;
96            Term::Tuple(parts)
97        }
98        Rule::literal => {
99            let literal = parse_literal(pair)?;
100            Term::Literal(literal)
101        }
102        Rule::term_const_func => {
103            let mut pairs = pair.into_inner();
104            let region = parse_region(pairs.next().unwrap())?;
105            Term::Func(Arc::new(region))
106        }
107        _ => unreachable!(),
108    })
109}
110
111fn parse_literal(pair: Pair<Rule>) -> ParseResult<Literal> {
112    debug_assert_eq!(pair.as_rule(), Rule::literal);
113    let pair = pair.into_inner().next().unwrap();
114
115    Ok(match pair.as_rule() {
116        Rule::literal_string => Literal::Str(parse_string(pair)?),
117        Rule::literal_nat => Literal::Nat(parse_nat(pair)?),
118        Rule::literal_bytes => Literal::Bytes(parse_bytes(pair)?),
119        Rule::literal_float => Literal::Float(parse_float(pair)?),
120        _ => unreachable!("expected literal"),
121    })
122}
123
124fn parse_seq_part(pair: Pair<Rule>) -> ParseResult<SeqPart> {
125    debug_assert_eq!(pair.as_rule(), Rule::part);
126    let pair = pair.into_inner().next().unwrap();
127
128    Ok(match pair.as_rule() {
129        Rule::term => SeqPart::Item(parse_term(pair)?),
130        Rule::spliced_term => {
131            let mut pairs = pair.into_inner();
132            let term = parse_term(pairs.next().unwrap())?;
133            SeqPart::Splice(term)
134        }
135        _ => unreachable!("expected term or spliced term"),
136    })
137}
138
139fn parse_package(pair: Pair<Rule>) -> ParseResult<Package> {
140    debug_assert_eq!(pair.as_rule(), Rule::package);
141    let mut pairs = pair.into_inner();
142
143    let modules = take_rule(&mut pairs, Rule::module)
144        .map(parse_module)
145        .collect::<ParseResult<_>>()?;
146
147    Ok(Package { modules })
148}
149
150fn parse_module(pair: Pair<Rule>) -> ParseResult<Module> {
151    debug_assert_eq!(pair.as_rule(), Rule::module);
152    let mut pairs = pair.into_inner();
153    let meta = parse_meta_items(&mut pairs)?;
154    let children = parse_nodes(&mut pairs)?;
155
156    Ok(Module {
157        root: Region {
158            kind: RegionKind::Module,
159            children,
160            meta,
161            ..Default::default()
162        },
163    })
164}
165
166fn parse_region(pair: Pair<Rule>) -> ParseResult<Region> {
167    debug_assert_eq!(pair.as_rule(), Rule::region);
168    let mut pairs = pair.into_inner();
169
170    let kind = parse_region_kind(pairs.next().unwrap())?;
171    let sources = parse_port_list(&mut pairs)?;
172    let targets = parse_port_list(&mut pairs)?;
173    let signature = parse_optional_signature(&mut pairs)?;
174    let meta = parse_meta_items(&mut pairs)?;
175    let children = parse_nodes(&mut pairs)?;
176
177    Ok(Region {
178        kind,
179        sources,
180        targets,
181        children,
182        meta,
183        signature,
184    })
185}
186
187fn parse_region_kind(pair: Pair<Rule>) -> ParseResult<RegionKind> {
188    debug_assert_eq!(pair.as_rule(), Rule::region_kind);
189
190    Ok(match pair.as_str() {
191        "dfg" => RegionKind::DataFlow,
192        "cfg" => RegionKind::ControlFlow,
193        "mod" => RegionKind::Module,
194        _ => unreachable!(),
195    })
196}
197
198fn parse_nodes(pairs: &mut Pairs<Rule>) -> ParseResult<Box<[Node]>> {
199    take_rule(pairs, Rule::node).map(parse_node).collect()
200}
201
202fn parse_node(pair: Pair<Rule>) -> ParseResult<Node> {
203    debug_assert_eq!(pair.as_rule(), Rule::node);
204    let mut pairs = pair.into_inner();
205    let pair = pairs.next().unwrap();
206    let rule = pair.as_rule();
207    let mut pairs = pair.into_inner();
208
209    let operation = match rule {
210        Rule::node_dfg => Operation::Dfg,
211        Rule::node_cfg => Operation::Cfg,
212        Rule::node_block => Operation::Block,
213        Rule::node_tail_loop => Operation::TailLoop,
214        Rule::node_cond => Operation::Conditional,
215
216        Rule::node_import => {
217            let symbol_ident = parse_symbol_ident(pairs.next().unwrap())?;
218            Operation::Import(symbol_ident)
219        }
220
221        Rule::node_custom => {
222            let term = parse_term(pairs.next().unwrap())?;
223            Operation::Custom(term)
224        }
225
226        Rule::node_define_func => {
227            let symbol = parse_symbol(pairs.next().unwrap())?;
228            Operation::DefineFunc(Box::new(symbol))
229        }
230        Rule::node_declare_func => {
231            let symbol = parse_symbol(pairs.next().unwrap())?;
232            Operation::DeclareFunc(Box::new(symbol))
233        }
234        Rule::node_define_alias => {
235            let symbol = parse_symbol(pairs.next().unwrap())?;
236            let value = parse_term(pairs.next().unwrap())?;
237            Operation::DefineAlias(Box::new(symbol), value)
238        }
239        Rule::node_declare_alias => {
240            let symbol = parse_symbol(pairs.next().unwrap())?;
241            Operation::DeclareAlias(Box::new(symbol))
242        }
243        Rule::node_declare_ctr => {
244            let symbol = parse_symbol(pairs.next().unwrap())?;
245            Operation::DeclareConstructor(Box::new(symbol))
246        }
247        Rule::node_declare_operation => {
248            let symbol = parse_symbol(pairs.next().unwrap())?;
249            Operation::DeclareOperation(Box::new(symbol))
250        }
251
252        _ => unreachable!(),
253    };
254
255    let inputs = parse_port_list(&mut pairs)?;
256    let outputs = parse_port_list(&mut pairs)?;
257    let signature = parse_optional_signature(&mut pairs)?;
258    let meta = parse_meta_items(&mut pairs)?;
259    let regions = pairs
260        .map(|pair| parse_region(pair))
261        .collect::<ParseResult<_>>()?;
262
263    Ok(Node {
264        operation,
265        inputs,
266        outputs,
267        regions,
268        meta,
269        signature,
270    })
271}
272
273fn parse_meta_items(pairs: &mut Pairs<Rule>) -> ParseResult<Box<[Term]>> {
274    take_rule(pairs, Rule::meta).map(parse_meta_item).collect()
275}
276
277fn parse_meta_item(pair: Pair<Rule>) -> ParseResult<Term> {
278    debug_assert_eq!(pair.as_rule(), Rule::meta);
279    let mut pairs = pair.into_inner();
280    parse_term(pairs.next().unwrap())
281}
282
283fn parse_optional_signature(pairs: &mut Pairs<Rule>) -> ParseResult<Option<Term>> {
284    match take_rule(pairs, Rule::signature).next() {
285        Some(pair) => Ok(Some(parse_signature(pair)?)),
286        _ => Ok(None),
287    }
288}
289
290fn parse_signature(pair: Pair<Rule>) -> ParseResult<Term> {
291    debug_assert_eq!(Rule::signature, pair.as_rule());
292    let mut pairs = pair.into_inner();
293    parse_term(pairs.next().unwrap())
294}
295
296fn parse_params(pairs: &mut Pairs<Rule>) -> ParseResult<Box<[Param]>> {
297    take_rule(pairs, Rule::param).map(parse_param).collect()
298}
299
300fn parse_param(pair: Pair<Rule>) -> ParseResult<Param> {
301    debug_assert_eq!(Rule::param, pair.as_rule());
302    let mut pairs = pair.into_inner();
303    let name = parse_var_name(pairs.next().unwrap())?;
304    let r#type = parse_term(pairs.next().unwrap())?;
305    Ok(Param { name, r#type })
306}
307
308fn parse_symbol(pair: Pair<Rule>) -> ParseResult<Symbol> {
309    debug_assert_eq!(Rule::symbol, pair.as_rule());
310
311    let mut pairs = pair.into_inner();
312    let visibility = take_rule(&mut pairs, Rule::visibility)
313        .next()
314        .map(|pair| match pair.as_str() {
315            "public" => Ok(Visibility::Public),
316            "private" => Ok(Visibility::Private),
317            _ => unreachable!("Expected 'public' or 'private', got {}", pair.as_str()),
318        })
319        .transpose()?;
320    let SymbolIdent { name, version } = parse_symbol_ident(pairs.next().unwrap())?;
321    let params = parse_params(&mut pairs)?;
322    let constraints = parse_constraints(&mut pairs)?;
323    let signature = parse_term(pairs.next().unwrap())?;
324
325    Ok(Symbol {
326        visibility,
327        name,
328        version,
329        params,
330        constraints,
331        signature,
332    })
333}
334
335fn parse_constraints(pairs: &mut Pairs<Rule>) -> ParseResult<Box<[Term]>> {
336    take_rule(pairs, Rule::where_clause)
337        .map(parse_constraint)
338        .collect()
339}
340
341fn parse_constraint(pair: Pair<Rule>) -> ParseResult<Term> {
342    debug_assert_eq!(Rule::where_clause, pair.as_rule());
343    let mut pairs = pair.into_inner();
344    parse_term(pairs.next().unwrap())
345}
346
347fn parse_port_list(pairs: &mut Pairs<Rule>) -> ParseResult<Box<[LinkName]>> {
348    let Some(pair) = take_rule(pairs, Rule::port_list).next() else {
349        return Ok(Default::default());
350    };
351
352    let pairs = pair.into_inner();
353    pairs.map(parse_link_name).collect()
354}
355
356fn parse_string(pair: Pair<Rule>) -> ParseResult<SmolStr> {
357    debug_assert_eq!(pair.as_rule(), Rule::literal_string);
358
359    // Any escape sequence is longer than the character it represents.
360    // Therefore the length of this token (minus 2 for the quotes on either
361    // side) is an upper bound for the length of the string.
362    let capacity = pair.as_str().len() - 2;
363    let mut string = String::with_capacity(capacity);
364    let pairs = pair.into_inner();
365
366    for pair in pairs {
367        match pair.as_rule() {
368            Rule::literal_string_raw => string.push_str(pair.as_str()),
369            Rule::literal_string_escape => match pair.as_str().chars().nth(1).unwrap() {
370                '"' => string.push('"'),
371                '\\' => string.push('\\'),
372                'n' => string.push('\n'),
373                'r' => string.push('\r'),
374                't' => string.push('\t'),
375                _ => unreachable!(),
376            },
377            Rule::literal_string_unicode => {
378                let token_str = pair.as_str();
379                debug_assert_eq!(&token_str[0..3], r"\u{");
380                debug_assert_eq!(&token_str[token_str.len() - 1..], "}");
381                let code_str = &token_str[3..token_str.len() - 1];
382                let code = u32::from_str_radix(code_str, 16).map_err(|_| {
383                    ParseError::custom("invalid unicode escape sequence", pair.as_span())
384                })?;
385                let char = std::char::from_u32(code).ok_or_else(|| {
386                    ParseError::custom("invalid unicode code point", pair.as_span())
387                })?;
388                string.push(char);
389            }
390            _ => unreachable!(),
391        }
392    }
393
394    Ok(string.into())
395}
396
397fn parse_bytes(pair: Pair<Rule>) -> ParseResult<Arc<[u8]>> {
398    debug_assert_eq!(pair.as_rule(), Rule::literal_bytes);
399    let pair = pair.into_inner().next().unwrap();
400    debug_assert_eq!(pair.as_rule(), Rule::base64_string);
401
402    let slice = pair.as_str().as_bytes();
403
404    // Remove the quotes
405    let slice = &slice[1..slice.len() - 1];
406
407    let data = BASE64_STANDARD
408        .decode(slice)
409        .map_err(|_| ParseError::custom("invalid base64 encoding", pair.as_span()))?;
410
411    Ok(data.into())
412}
413
414fn parse_nat(pair: Pair<Rule>) -> ParseResult<u64> {
415    debug_assert_eq!(pair.as_rule(), Rule::literal_nat);
416    let value = pair.as_str().trim().parse().unwrap();
417    Ok(value)
418}
419
420fn parse_float(pair: Pair<Rule>) -> ParseResult<OrderedFloat<f64>> {
421    debug_assert_eq!(pair.as_rule(), Rule::literal_float);
422    let value = pair.as_str().trim().parse().unwrap();
423    Ok(OrderedFloat(value))
424}
425
426fn take_rule<'a, 'i>(
427    pairs: &'i mut Pairs<'a, Rule>,
428    rule: Rule,
429) -> impl Iterator<Item = Pair<'a, Rule>> + 'i {
430    std::iter::from_fn(move || {
431        if pairs.peek()?.as_rule() == rule {
432            pairs.next()
433        } else {
434            None
435        }
436    })
437}
438
439type ParseResult<T> = Result<T, ParseError>;
440
441/// An error that occurred during parsing.
442#[derive(Debug, Clone, Error)]
443#[error("{0}")]
444pub struct ParseError(Box<pest::error::Error<Rule>>);
445
446impl ParseError {
447    fn custom(message: &str, span: pest::Span) -> Self {
448        let error = pest::error::Error::new_from_span(
449            pest::error::ErrorVariant::CustomError {
450                message: message.to_string(),
451            },
452            span,
453        );
454        ParseError(Box::new(error))
455    }
456}
457
458macro_rules! impl_from_str {
459    ($ident:ident, $rule:ident, $parse:expr) => {
460        impl FromStr for $ident {
461            type Err = ParseError;
462
463            fn from_str(s: &str) -> Result<Self, Self::Err> {
464                let mut pairs =
465                    HugrParser::parse(Rule::$rule, s).map_err(|err| ParseError(Box::new(err)))?;
466                $parse(pairs.next().unwrap())
467            }
468        }
469    };
470}
471
472impl_from_str!(SymbolName, symbol_name, parse_symbol_name);
473impl_from_str!(SymbolIdent, symbol_ident, parse_symbol_ident);
474impl_from_str!(VarName, term_var, parse_var_name);
475impl_from_str!(LinkName, link_name, parse_link_name);
476impl_from_str!(Term, term, parse_term);
477impl_from_str!(Node, node, parse_node);
478impl_from_str!(Region, region, parse_region);
479impl_from_str!(Param, param, parse_param);
480impl_from_str!(Package, package, parse_package);
481impl_from_str!(Module, module, parse_module);
482impl_from_str!(SeqPart, part, parse_seq_part);
483impl_from_str!(Literal, literal, parse_literal);
484impl_from_str!(Symbol, symbol, parse_symbol);