microcad_lang/
parser.rs

1// Copyright © 2024-2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! µcad Code Parser
5
6#![allow(missing_docs)]
7
8/// include grammar from file
9#[derive(pest_derive::Parser)]
10#[grammar = "grammar.pest"]
11pub struct Parser;
12
13use crate::{parse::*, src_ref::*};
14
15#[derive(Debug, Clone)]
16pub struct Pair<'i>(pest::iterators::Pair<'i, Rule>, u64);
17
18impl<'i> Pair<'i> {
19    pub fn new(pest_pair: pest::iterators::Pair<'i, Rule>, source_hash: u64) -> Self {
20        Self(pest_pair, source_hash)
21    }
22
23    pub fn source_hash(&self) -> u64 {
24        self.1
25    }
26
27    pub fn set_source_hash(&mut self, hash: u64) {
28        self.1 = hash
29    }
30
31    pub fn pest_pair(&self) -> &pest::iterators::Pair<'i, Rule> {
32        &self.0
33    }
34
35    pub fn inner(&'i self) -> impl Iterator<Item = Self> {
36        self.0.clone().into_inner().map(|p| Self(p, self.1))
37    }
38
39    /// Find an inner pair by rule
40    pub fn find<T: Parse>(&'i self, rule: Rule) -> Option<T> {
41        match self
42            .inner()
43            .find(|pair| pair.as_rule() == rule)
44            .map(T::parse)
45        {
46            Some(Err(_)) | None => None,
47            Some(Ok(x)) => Some(x),
48        }
49    }
50}
51
52impl SrcReferrer for Pair<'_> {
53    fn src_ref(&self) -> SrcRef {
54        let pair = &self.0;
55        let (line, col) = pair.line_col();
56        SrcRef::new(
57            pair.as_span().start()..pair.as_span().end(),
58            line,
59            col,
60            self.1,
61        )
62    }
63}
64
65impl<'i> std::ops::Deref for Pair<'i> {
66    type Target = pest::iterators::Pair<'i, Rule>;
67
68    fn deref(&self) -> &Self::Target {
69        &self.0
70    }
71}
72
73pub type Pairs<'i> = pest::iterators::Pairs<'i, Rule>;
74
75pub trait Parse: Sized {
76    fn parse(pair: Pair) -> ParseResult<Self>;
77}
78
79impl Parser {
80    /// Helper function to parse a vector of pairs into a vector of T
81    ///
82    /// # Arguments
83    ///
84    /// - `pairs`: The pairs to parse
85    /// - `f`: The function to parse the pair into `T`
86    ///
87    /// Returns a vector of `T`
88    pub fn vec<'a, T>(pair: Pair<'a>, f: impl Fn(Pair<'a>) -> ParseResult<T>) -> ParseResult<Vec<T>>
89    where
90        T: Clone,
91    {
92        pair.0.into_inner().map(|p| f(Pair(p, pair.1))).collect()
93    }
94
95    /// Parse a rule for type `T`
96    pub fn parse_rule<T>(rule: Rule, input: &str, src_hash: u64) -> ParseResult<T>
97    where
98        T: Parse + Clone,
99    {
100        use pest::Parser as _;
101
102        match Parser::parse(rule, input.trim()) {
103            Ok(mut pairs) => match pairs.next() {
104                Some(pair) => Ok(T::parse(Pair(pair, src_hash))?),
105                None => Err(ParseError::RuleNotFoundError(Box::new(rule))),
106            },
107            Err(err) => Err(ParseError::Parser(err.into())),
108        }
109    }
110
111    pub fn ensure_rule(pair: &Pair, expected: Rule) {
112        let rule = pair.as_rule();
113        assert_eq!(rule, expected, "Unexpected rule: {rule:?}");
114    }
115
116    pub fn ensure_rules(pair: &Pair, rules: &[Rule]) {
117        for rule in rules {
118            if *rule == pair.as_rule() {
119                return;
120            }
121        }
122
123        panic!(
124            "Unexpected rules: expected {rules:?}, got {rule:?}",
125            rule = pair.as_rule()
126        );
127    }
128}