regen/core/
param.rs

1//! Core logic for parameters
2use std::fmt::Display;
3
4use crate::core::ExtractFromLiteral;
5use crate::grammar::{pt, Ctx, Tok};
6use crate::sdk::Error;
7
8/// Parameter of a rule derivation.
9///
10/// The parameters are used as derivation steps when generating the Abstract Syntax Tree (AST).
11/// When generating the AST, they are processed in the order they are defined.
12/// They are also used to generate the Parse Tree (PT). The rule's custom body defines what the Parse Tree looks like.
13///
14/// A parameter consistens of:
15/// - Optional semantic annotation
16/// - Name of the parameter
17/// - Type of the parameter
18///
19/// The type can be:
20/// - Another rule Foo (e.g. param: Foo)
21/// - A token type Bar (e.g. param: token Bar)
22/// - A token type that also requires the content to match (e.g. param token Keyword "if")
23///
24/// A parameter can also be optional (e.g. param: optional Foo) or a list (e.g. param: Foo+), or both.
25/// An optional list can be empty while a required list needs to have at least 1 element.
26///
27/// The generated Parse Tree (PT) will have different object types depending on the parameter type.
28#[derive(Debug, Clone)]
29pub struct Param {
30    /// Semantic type of the parameter.
31    pub semantic: Option<String>,
32    /// Name of the parameter.
33    pub name: String,
34    /// Type name of the parameter (either rule type of token type)
35    pub type_name: String,
36    /// Internal type representation of the parameter
37    pub param_type: ParamType,
38}
39
40impl Param {
41    /// Get if the parameter is in the Parse Tree (PT)
42    /// A parameter is excluded from the PT if it is a single, required token with a literal matching
43    #[inline]
44    pub fn is_in_pt(&self) -> bool {
45        !matches!(self.param_type.data, ParamDataType::Flag(_))
46            || !matches!(self.param_type.decor, ParamDecorType::None)
47    }
48}
49
50/// Parser hook for param
51///
52/// Checks if type is defined and if semantic is valid
53pub fn parse_param(pt: &pt::Parameter, ctx: &mut Ctx) -> Option<Param> {
54    // Validate parameter type is defined
55    let param_type = pt.m_type.as_ref().or_else(|| {
56        let msg = "Missing parameter type".to_owned();
57        let help = "Add a type after \":\"".to_owned();
58        ctx.err.push(Error::from_token(&pt.ast.m_2, msg, help));
59        None
60    })?;
61
62    // get semantic annotation
63    // valid only if the syntax exists and contains an identifier
64    // It's ok if semantic is invalid. We add an error and treat it as no semantic
65    let semantic = pt
66        .m_sem_attr
67        .as_ref()
68        .as_ref()
69        .and_then(|sem| match &sem.m_semantic_name {
70            None => {
71                let msg = "Missing semantic annotation".to_owned();
72                let help = "Add semantic annotation inside \"()\"".to_owned();
73                ctx.err.push(Error::from_token(&sem.ast.m_0, msg, help));
74
75                None
76            }
77            x => x.as_ref(),
78        });
79
80    // At this point we can know if the type identifier is a rule or token
81    // Add semantic info
82    let is_token = param_type.m_kw_token;
83    if let Some(ast) = pt.ast.m_type.as_ref() {
84        if is_token {
85            // Token type
86            ctx.tbs.set(&ast.m_id, Tok::SToken);
87        } else {
88            // Rule type
89            ctx.tbs.set(&ast.m_id, Tok::SRule);
90        }
91    }
92
93    let param_t = ParamType {
94        decor: if param_type.m_is_list {
95            ParamDecorType::Vec(param_type.m_kw_optional)
96        } else if param_type.m_kw_optional {
97            ParamDecorType::Optional
98        } else {
99            ParamDecorType::None
100        },
101        data: if is_token {
102            match &param_type.m_token_content {
103                None => ParamDataType::String,
104                Some(content) => ParamDataType::Flag(content.strip_quotes()),
105            }
106        } else {
107            ParamDataType::Rule
108        },
109    };
110
111    let param = Param {
112        semantic: semantic.cloned(),
113        name: pt.m_variable.clone(),
114        type_name: param_type.m_id.clone(),
115        param_type: param_t,
116        // is_token,
117        // is_optional: param_type.m_kw_optional,
118        // match_literal: param_type
119        //   .m_token_content
120        //   .as_ref()
121        //   .map(|x| x.strip_quotes()),
122        // is_list: param_type.m_is_list,
123    };
124
125    if !param.is_in_pt() {
126        ctx.tbs.set(
127            &pt.ast.m_variable,
128            Tok::Decor {
129                tag: "unused".to_owned(),
130                base: Box::new(Tok::SVariable),
131            },
132        );
133    }
134
135    Some(param)
136}
137
138/// The parameter type.
139///
140/// This is parsed from the type definition in the language as follows. `TYPE` refer to any identifier:
141///
142/// |Type|Data|Decoration|Parse Tree Type (Rust)|
143/// |-|-|-|-|
144/// |`TYPE`|`Rule`|`None`|`TYPE`|
145/// |`optional TYPE`|`Rule`|`Optional`|`Option<TYPE>`|
146/// |`TYPE+`|`Rule`|`Vec(false)`|`Vec<TYPE>`|
147/// |`optional TYPE+`|`Rule`|`Vec(true)`|`Vec<TYPE>`|
148/// |`token TYPE`|`String`|`None`|`String`
149/// |`optional token TYPE`|`String`|`Optional`|`Option<String>`|
150/// |`token TYPE+`|`String`|`Vec(false)`|`Vec<String>`|
151/// |`optional token TYPE+`|`String`|`Vec(true)`|`Vec<String>`|
152/// |`token TYPE"literal"`|`Flag`|`None`|N/A|
153/// |`optional token TYPE"literal"`|`Flag`|`Optional`|`bool`|
154/// |`token TYPE"literal"+`|`Flag`|`Vec(false)`|`usize`|
155/// |`optional token TYPE"literal"+`|`Flag`|`Vec(true)`|`usize`|
156#[derive(Debug, Clone, PartialEq)]
157pub struct ParamType {
158    pub decor: ParamDecorType,
159    pub data: ParamDataType,
160}
161
162/// The decoration type of a parameter
163#[derive(Debug, Clone, PartialEq)]
164pub enum ParamDecorType {
165    None,
166    /// The parameter is optional
167    Optional,
168    /// The parameter is a list (defined with `+`)
169    ///
170    /// Optional and required lists both use this type.
171    Vec(bool /* optional */),
172}
173
174/// The data type of a parameter
175#[derive(Debug, Clone, PartialEq)]
176pub enum ParamDataType {
177    /// The parameter type is another rule
178    Rule,
179    /// The parameter type is a token without literal match
180    String,
181    /// The parameter type is a token with literal match
182    Flag(String /* match_literal */),
183}
184
185impl Display for ParamDataType {
186    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187        match self {
188            ParamDataType::Rule => write!(f, "rule"),
189            ParamDataType::String => write!(f, "string"),
190            ParamDataType::Flag(literal) => write!(f, "flag<{literal}>"),
191        }
192    }
193}