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 ¶m_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}