1use super::super::feature::class_expression;
11use super::super::{
12 FeatureExpr, GrammarDeclarations, GrammarExpr, GrammarImportError, GrammarMacro,
13 GrammarScanner, GrammarSourceName, Operation, OperationCategory, PrecedenceEntry,
14 RuleAttributes, UnicodeClassItem,
15};
16use super::feature_forms::{
17 FormCodec, LinksReader, read_links_feature, read_links_operation, render_feature_form,
18 render_operation,
19};
20use super::links::{
21 Node, arity, char_text, decoded_word, link, links_error, parse_node, parts,
22 percent_decode_links_text, percent_encode_links_text, render_links_expression, word,
23};
24
25struct LinksCodec;
27
28impl FormCodec for LinksCodec {
29 fn name(&self, value: &str) -> String {
30 percent_encode_links_text(value)
31 }
32
33 fn text(&self, value: &str) -> String {
34 percent_encode_links_text(value)
35 }
36
37 fn expression(&self, expr: &GrammarExpr) -> String {
38 render_links_expression(expr)
39 }
40
41 fn call(&self, head: &str, parts: Vec<String>) -> String {
42 if parts.is_empty() {
43 return head.to_owned();
44 }
45 let mut all = vec![head.to_owned()];
46 all.extend(parts);
47 link(&all)
48 }
49
50 fn block(&self, word: &str, parts: Vec<String>) -> String {
51 let mut all = vec![word.to_owned()];
52 all.extend(parts);
53 link(&all)
54 }
55
56 fn byte_class(&self, negated: bool, items: Vec<String>) -> String {
57 let mut all = vec![
58 "byteClass".to_owned(),
59 if negated { "negated" } else { "plain" }.to_owned(),
60 ];
61 all.extend(items);
62 link(&all)
63 }
64}
65
66pub(super) fn render_links_feature(feature: &FeatureExpr) -> String {
68 match feature {
69 FeatureExpr::UnicodeClass { negated, items } => {
70 let mut parts = vec![
71 "class".to_owned(),
72 if *negated { "negated" } else { "plain" }.to_owned(),
73 ];
74 parts.extend(items.iter().map(|item| match item {
75 UnicodeClassItem::Char(value) => link(&["char".to_owned(), char_text(*value)]),
76 UnicodeClassItem::Range(start, end) => {
77 link(&["range".to_owned(), char_text(*start), char_text(*end)])
78 }
79 UnicodeClassItem::Category(value) => {
80 link(&["category".to_owned(), percent_encode_links_text(value)])
81 }
82 UnicodeClassItem::Script(value) => {
83 link(&["script".to_owned(), percent_encode_links_text(value)])
84 }
85 }));
86 link(&parts)
87 }
88 FeatureExpr::Call { name, arguments } => {
89 let mut parts = vec!["ref".to_owned(), percent_encode_links_text(name)];
90 parts.extend(arguments.iter().map(render_links_expression));
91 link(&parts)
92 }
93 FeatureExpr::Form(_) | FeatureExpr::ByteClass { .. } => {
94 render_feature_form(feature, &LinksCodec).unwrap_or_default()
95 }
96 }
97}
98
99fn render_names(head: &str, names: &[String]) -> String {
100 let mut parts = vec![head.to_owned()];
101 parts.extend(names.iter().map(|name| percent_encode_links_text(name)));
102 link(&parts)
103}
104
105fn render_operations(head: &str, operations: &[Operation]) -> String {
106 let mut parts = vec![head.to_owned()];
107 parts.extend(
108 operations
109 .iter()
110 .map(|operation| render_operation(operation, &LinksCodec)),
111 );
112 link(&parts)
113}
114
115#[must_use]
117pub fn render_declaration_links(declarations: &GrammarDeclarations) -> Vec<String> {
118 let mut lines = Vec::new();
119 for name in &declarations.imports {
120 lines.push(format!("(import {})", percent_encode_links_text(name)));
121 }
122 for name in &declarations.modes {
123 lines.push(format!("(mode {})", percent_encode_links_text(name)));
124 }
125 for extra in &declarations.extras {
126 lines.push(format!("(extra {})", render_links_expression(extra)));
127 }
128 for group in &declarations.conflicts {
129 lines.push(render_names("conflict", group));
130 }
131 for order in &declarations.precedences {
132 let entries = order.iter().map(|entry| {
133 format!(
134 "({} {})",
135 entry.kind(),
136 percent_encode_links_text(entry.value())
137 )
138 });
139 lines.push(format!(
140 "(precedences {})",
141 entries.collect::<Vec<_>>().join(" ")
142 ));
143 }
144 for declared in &declarations.macros {
145 lines.push(format!(
146 "(macro {} {} {})",
147 percent_encode_links_text(&declared.name),
148 render_names("parameters", &declared.parameters),
149 render_links_expression(&declared.expression)
150 ));
151 }
152 for scanner in &declarations.scanners {
153 lines.push(format!(
154 "(scanner {} {} {})",
155 percent_encode_links_text(&scanner.name),
156 render_names("tokens", &scanner.tokens),
157 render_operations("operations", &scanner.operations)
158 ));
159 }
160 lines
161}
162
163#[must_use]
165pub fn render_rule_fields(attributes: &RuleAttributes) -> Vec<String> {
166 let mut fields = Vec::new();
167 if !attributes.parameters.is_empty() {
168 fields.push(render_names("parameters", &attributes.parameters));
169 }
170 if let Some(channel) = &attributes.channel {
171 fields.push(format!("(channel {})", percent_encode_links_text(channel)));
172 }
173 if let Some(modes) = &attributes.modes {
174 fields.push(render_names("modes", modes));
175 }
176 if let Some(action) = &attributes.action {
177 fields.push(render_operations("action", action));
178 }
179 fields
180}
181
182struct Helpers;
184
185impl LinksReader for Helpers {
186 type Node = Node;
187
188 fn parts<'a>(&self, node: &'a Node) -> Result<(&'a str, &'a [Node]), GrammarImportError> {
189 parts(node)
190 }
191
192 fn word<'a>(&self, node: &'a Node, what: &str) -> Result<&'a str, GrammarImportError> {
193 word(Some(node), what)
194 }
195
196 fn decoded_word(&self, node: &Node, what: &str) -> Result<String, GrammarImportError> {
197 decoded_word(Some(node), what)
198 }
199
200 fn expression(&self, node: &Node) -> Result<GrammarExpr, GrammarImportError> {
201 parse_node(node)
202 }
203
204 fn fail(&self, detail: String) -> GrammarImportError {
205 links_error(detail)
206 }
207}
208
209pub(super) fn read_feature(
211 head: &str,
212 args: &[Node],
213) -> Result<Option<GrammarExpr>, GrammarImportError> {
214 read_links_feature(&Helpers, head, args)
215}
216
217pub(super) fn read_class(negated: bool, items: &[Node]) -> Result<GrammarExpr, GrammarImportError> {
219 let items = items
220 .iter()
221 .map(|item| {
222 let (head, args) = parts(item)?;
223 match head {
224 "char" => {
225 arity(head, args, 1)?;
226 Ok(UnicodeClassItem::Char(super::links::character(
227 args.first(),
228 "a class character",
229 )?))
230 }
231 "range" => {
232 arity(head, args, 2)?;
233 Ok(UnicodeClassItem::Range(
234 super::links::character(args.first(), "a range start")?,
235 super::links::character(args.get(1), "a range end")?,
236 ))
237 }
238 "category" | "script" => {
239 arity(head, args, 1)?;
240 let value = decoded_word(args.first(), &format!("a Unicode {head}"))?;
241 Ok(if head == "category" {
242 UnicodeClassItem::Category(value)
243 } else {
244 UnicodeClassItem::Script(value)
245 })
246 }
247 other => Err(links_error(format!("unknown class item {other}"))),
248 }
249 })
250 .collect::<Result<_, _>>()?;
251 Ok(class_expression(negated, items))
252}
253
254fn names(node: &Node, head: &str) -> Result<Vec<String>, GrammarImportError> {
255 let (found, args) = parts(node)?;
256 if found != head {
257 return Err(links_error(format!("expected ({head} ...), not {found}")));
258 }
259 args.iter()
260 .map(|item| decoded_word(Some(item), &format!("a {head} name")))
261 .collect()
262}
263
264fn operations(node: &Node, head: &str) -> Result<Vec<Operation>, GrammarImportError> {
265 let (found, args) = parts(node)?;
266 if found != head {
267 return Err(links_error(format!("expected ({head} ...), not {found}")));
268 }
269 args.iter()
270 .map(|item| read_links_operation(&Helpers, item, OperationCategory::Statement))
271 .collect()
272}
273
274pub(super) fn read_declaration(
276 head: &str,
277 args: &[Node],
278 declarations: &mut GrammarDeclarations,
279) -> Result<bool, GrammarImportError> {
280 let one = || arity(head, args, 1).map(|()| &args[0]);
281 match head {
282 "import" => declarations
283 .imports
284 .push(decoded_word(Some(one()?), "an imported grammar")?),
285 "mode" => declarations
286 .modes
287 .push(decoded_word(Some(one()?), "a mode")?),
288 "extra" => declarations.extras.push(parse_node(one()?)?),
289 "conflict" => {
290 if args.is_empty() {
291 return Err(links_error("conflict names at least one rule"));
292 }
293 declarations.conflicts.push(
294 args.iter()
295 .map(|item| decoded_word(Some(item), "a rule name"))
296 .collect::<Result<_, _>>()?,
297 );
298 }
299 "precedences" => {
300 if args.len() < 2 {
301 return Err(links_error("precedences orders at least two entries"));
302 }
303 let mut order = Vec::new();
304 for item in args {
305 let (kind, values) = parts(item)?;
306 let what = match kind {
307 "name" => "a precedence name",
308 "rule" => "a rule name",
309 other => {
310 return Err(links_error(format!(
311 "a precedence entry is (name NAME) or (rule NAME), not {other}"
312 )));
313 }
314 };
315 arity(kind, values, 1)?;
316 let value = decoded_word(values.first(), what)?;
317 order.extend(PrecedenceEntry::from_kind(kind, value));
318 }
319 declarations.precedences.push(order);
320 }
321 "macro" => {
322 arity(head, args, 3)?;
323 declarations.macros.push(GrammarMacro {
324 name: decoded_word(args.first(), "a macro name")?,
325 parameters: names(&args[1], "parameters")?,
326 expression: parse_node(&args[2])?,
327 });
328 }
329 "scanner" => {
330 arity(head, args, 3)?;
331 declarations.scanners.push(GrammarScanner {
332 name: decoded_word(args.first(), "a scanner name")?,
333 tokens: names(&args[1], "tokens")?,
334 operations: operations(&args[2], "operations")?,
335 });
336 }
337 _ => return Ok(false),
338 }
339 Ok(true)
340}
341
342const RULE_FIELDS: [&str; 7] = [
345 "parameters",
346 "channel",
347 "modes",
348 "action",
349 "concept",
350 "source-names",
351 "doc",
352];
353
354#[derive(Default)]
356pub(super) struct RuleFields {
357 pub attributes: RuleAttributes,
358 pub concept: Option<String>,
359 pub source_names: Vec<GrammarSourceName>,
360 pub doc: Option<String>,
361}
362
363pub(super) fn read_source_names(
365 args: &[Node],
366) -> Result<Vec<GrammarSourceName>, GrammarImportError> {
367 if args.is_empty() {
368 return Err(links_error("source-names lists at least one source name"));
369 }
370 args.iter()
371 .map(|item| {
372 let (source, name) = parts(item)?;
373 if name.len() != 1 {
374 return Err(links_error("a source name is (SOURCE NAME)"));
375 }
376 Ok(GrammarSourceName {
377 source: percent_decode_links_text(source)?,
378 name: decoded_word(name.first(), "a source name")?,
379 })
380 })
381 .collect()
382}
383
384pub(super) fn read_rule_fields(fields: &[Node]) -> Result<RuleFields, GrammarImportError> {
387 let mut read = RuleFields::default();
388 let mut order = 0;
389 for field in fields {
390 let (head, args) = parts(field)?;
391 let Some(position) = RULE_FIELDS.iter().position(|name| *name == head) else {
392 return Err(links_error(format!("unexpected rule field {head}")));
393 };
394 if position < order {
395 return Err(links_error(format!("rule field {head} is out of order")));
396 }
397 order = position + 1;
398 match head {
399 "parameters" => read.attributes.parameters = names(field, "parameters")?,
400 "modes" => read.attributes.modes = Some(names(field, "modes")?),
401 "action" => read.attributes.action = Some(operations(field, "action")?),
402 "source-names" => read.source_names = read_source_names(args)?,
403 "channel" => {
404 arity(head, args, 1)?;
405 read.attributes.channel = Some(decoded_word(args.first(), "a channel name")?);
406 }
407 "concept" => {
408 arity(head, args, 1)?;
409 read.concept = Some(decoded_word(args.first(), "a concept id")?);
410 }
411 _ => {
412 arity(head, args, 1)?;
413 read.doc = Some(decoded_word(args.first(), "a doc text")?);
414 }
415 }
416 }
417 Ok(read)
418}