1use crate::raw::{RawDocument, Reference};
2use cowstr::CowStr;
3use pest::iterators::Pair;
4use pest::Parser;
5use pest_derive::Parser;
6
7#[derive(Parser)]
8#[grammar = "grammars/raw_doc.pest"]
9pub struct RawDocParser;
10
11use crate::code_ast::parse_code_string;
12use crate::code_ast::types::CodeContent;
13use crate::common::Span;
14use crate::raw::{Element, ElementInfo, Parameter, Special, Value};
15use pest::iterators::Pairs;
16use thiserror::Error;
17
18#[derive(Error, Debug)]
19pub enum ParserError {
20 #[error("code cell parsing error")]
21 CodeError(#[from] Box<pest::error::Error<crate::code_ast::Rule>>),
22 #[error("document parsing error")]
23 DocError(#[from] Box<pest::error::Error<Rule>>),
24}
25
26impl RawDocument {
27 fn parse_doc(&mut self, mut pairs: Pairs<Rule>) -> Result<(), ParserError> {
28 let mut elems = pairs.next().expect("no root item").into_inner();
29
30 if let Some(p) = elems.next() {
31 match p.as_rule() {
32 Rule::meta => self.parse_meta(p),
33 _ => {
34 let el = self.parse_element(p);
35 self.src.push(el?)
36 }
37 }
38 }
39
40 let elems = self.parse_elements(elems)?;
41 self.src.extend(elems);
42
43 Ok(())
44 }
45
46 fn parse_elements(&mut self, pairs: Pairs<Rule>) -> Result<Vec<ElementInfo>, ParserError> {
47 pairs.map(|p| self.parse_element(p.clone())).collect()
48 }
49
50 fn parse_element(&mut self, pair: Pair<Rule>) -> Result<ElementInfo, ParserError> {
51 let span = Span::from(pair.as_span());
52
53 let element = match pair.as_rule() {
54 Rule::command => self.parse_command(pair)?,
55 Rule::math_block => self.parse_math_block(pair),
56 Rule::code_def => self.parse_code(pair)?,
57 Rule::verbatim => self.parse_verbatim(pair),
58 Rule::src | Rule::string | Rule::body => self.parse_src(pair),
59 _ => unreachable!(),
60 };
61
62 Ok(ElementInfo { element, span })
63 }
64
65 fn parse_src(&mut self, pair: Pair<Rule>) -> Element {
66 let value = pair.as_str();
67 Element::Markdown(value.into())
68 }
69
70 fn parse_command(&mut self, pair: Pair<Rule>) -> Result<Element, ParserError> {
71 let mut inner = pair.into_inner();
72 let name = inner
73 .next()
74 .expect("empty command")
75 .into_inner()
76 .next()
77 .unwrap()
78 .as_span();
79 let name = self.cowstr_from_span(name);
80
81 let mut parameters = vec![];
82 let mut body = None;
83 let mut label = None;
84
85 for elem in inner {
86 match elem.as_rule() {
87 Rule::parameters => parameters = self.parse_parameters(elem.into_inner())?,
88 Rule::body_def => body = Some(self.parse_elements(elem.into_inner())?),
89 Rule::label => {
90 label = Some(self.cowstr_from_span(elem.into_inner().next().unwrap().as_span()))
91 }
92 _ => unreachable!(),
93 }
94 }
95
96 if let Some(label) = label.clone() {
97 self.references
98 .insert(label, Reference::Command(name.clone(), parameters.clone()));
99 }
100
101 Ok(Element::Special(
102 label,
103 Special::Command {
104 function: name,
105 parameters,
106 body,
107 },
108 ))
109 }
110
111 fn parse_parameters(&mut self, pairs: Pairs<Rule>) -> Result<Vec<Parameter>, ParserError> {
112 pairs
113 .into_iter()
114 .map(|elem| {
115 if let Rule::param = elem.as_rule() {
116 self.parse_param(elem)
117 } else {
118 unreachable!()
119 }
120 })
121 .collect()
122 }
123
124 fn parse_param(&mut self, pair: Pair<Rule>) -> Result<Parameter, ParserError> {
125 let span = Span::from(pair.as_span());
126 let mut pairs = pair.into_inner();
127 let first = pairs.next().expect("empty param");
128
129 Ok(if let Rule::key = first.as_rule() {
130 let value = pairs.next().expect("no value");
131 Parameter::with_key(first.as_str(), self.parse_value(value)?, span)
132 } else {
133 Parameter::with_value(self.parse_value(first)?, span)
134 })
135 }
136
137 fn parse_value(&mut self, pair: Pair<Rule>) -> Result<Value, ParserError> {
138 Ok(match pair.as_rule() {
139 Rule::basic_val | Rule::string => Value::String(pair.as_str().into()),
140 Rule::md_val => Value::Content(self.parse_elements(pair.into_inner())?),
141 Rule::flag => Value::Flag(pair.as_str().into()),
142 _ => unreachable!(),
143 })
144 }
145
146 fn parse_math_block(&mut self, pair: Pair<Rule>) -> Element {
147 let (lvl, src, label) = self.block_parser(pair);
148
149 let src = self.parse_math(src);
150
151 if let Some(label) = label.clone() {
152 self.references.insert(label, Reference::Math(src.clone()));
153 }
154
155 Element::Special(
156 label,
157 Special::Math {
158 inner: src,
159 is_block: lvl.len() != 1,
160 },
161 )
162 }
163
164 fn parse_code_attributes(&mut self, pairs: Pairs<Rule>) -> Vec<CowStr> {
165 pairs
166 .into_iter()
167 .map(|elem| {
168 if let Rule::code_param = elem.as_rule() {
169 self.parse_code_attribute(elem)
170 } else {
171 unreachable!()
172 }
173 })
174 .collect()
175 }
176
177 fn parse_code_attribute(&mut self, pair: Pair<Rule>) -> CowStr {
178 let mut pairs = pair.into_inner();
179 let first = pairs.next().expect("empty param");
180
181 if let Rule::key = first.as_rule() {
182 let value = pairs.next().expect("no value");
183 self.cowstr_from_span(value.as_span())
188 } else {
189 self.cowstr_from_span(first.as_span())
194 }
195 }
196
197 fn parse_code(&mut self, pair: Pair<Rule>) -> Result<Element, ParserError> {
198 let mut inner = pair.into_inner();
199 let lvl = inner.next().expect("missing code_lvl").as_str().to_string();
200
201 let maybe_param = inner.next().expect("missing code_src");
202 let (src_pair, attributes) = if let Rule::code_params = maybe_param.as_rule() {
203 let attributes = self.parse_code_attributes(maybe_param.into_inner());
204 (inner.next().expect("missing code_src"), Some(attributes))
205 } else {
206 (maybe_param, None)
207 };
208
209 let src_span = src_pair.as_span();
210 let src = self.cowstr_from_span(src_span);
211
212 let id = inner.next().map(|val| self.cowstr_from_span(val.as_span()));
213
214 if let Some(label) = id.clone() {
215 self.references.insert(label, Reference::Code(src.clone()));
216 }
217
218 Ok(Element::Special(
219 id,
220 if lvl.len() == 1 {
221 Special::CodeInline { inner: src }
222 } else {
223 let inner = if attributes
224 .as_ref()
225 .map(|a| a.contains(&CowStr::from("no_parse")))
226 .unwrap_or_default()
227 {
228 CodeContent::Plain(src)
229 } else {
230 parse_code_string(src)?
231 };
232
233 Special::CodeBlock {
234 lvl: lvl.len(),
235 inner,
236 attributes: attributes.unwrap_or_default(),
237 }
238 },
239 ))
240 }
241
242 fn parse_verbatim(&mut self, pair: Pair<Rule>) -> Element {
243 let value = pair.as_str();
244 Element::Special(
245 None,
246 Special::Verbatim {
247 inner: value.into(),
248 },
249 )
250 }
251
252 fn parse_meta(&mut self, pair: Pair<Rule>) {
253 self.meta = Some(self.cowstr_from_span(pair.as_span()));
254 }
255
256 fn cowstr_from_span(&self, span: pest::Span) -> CowStr {
257 CowStr::from(&self.input[span.start()..span.end()])
258 }
259
260 fn parse_math(&self, pair: Pair<Rule>) -> CowStr {
261 match pair.as_rule() {
262 Rule::math_chars => self.cowstr_from_span(pair.as_span()),
263 Rule::math_block_curly => cowstr::format!(
264 "{{{}}}",
265 pair.into_inner()
266 .map(|p| self.parse_math(p))
267 .collect::<CowStr>()
268 ),
269 Rule::math_body => pair
272 .into_inner()
273 .map(|p| self.parse_math(p))
274 .collect::<CowStr>(),
275 _ => unreachable!(),
276 }
277 }
278
279 fn block_parser<'a>(&'a self, pair: Pair<'a, Rule>) -> (CowStr, Pair<Rule>, Option<CowStr>) {
280 let mut inner = pair.into_inner();
281 let lvl = self.cowstr_from_span(inner.next().expect("missing code_lvl").as_span());
282 let src = inner.next().expect("missing code_src");
283 let id = inner.next().map(|val| self.cowstr_from_span(val.as_span()));
284 (lvl, src, id)
285 }
286}
287
288pub fn parse_to_doc(input: &str) -> Result<RawDocument, ParserError> {
289 let mut doc = RawDocument::new(input);
290 doc.parse_doc(RawDocParser::parse(Rule::top, input).map_err(Box::new)?)?;
291 Ok(doc)
292}
293
294#[cfg(test)]
295mod tests {
296 use crate::code_ast::types::{CodeContent, CodeElem};
297 use crate::common::Span;
298 use crate::raw::{
299 parse_to_doc, Element, ElementInfo, Parameter, RawDocument, Reference, Special, Value,
300 };
301 use cowstr::CowStr;
302 use std::collections::HashMap;
303
304 macro_rules! doc_tests {
305 ($prefix:ident $($name:ident: $value:expr,)*) => {
306 $(
307 paste::item!{
308 #[test]
309 fn [<$prefix _ $name>]() {
310 let (input, expected) = $value;
311 let doc = RawDocument { input: CowStr::from(input), src: expected, meta: None, references: Default::default() };
312 compare(doc, input);
313 }
314 }
315 )*
316 }
317 }
318
319 #[test]
320 fn test_code() {
321 let input = r#"```
322code
323```"#;
324 let expected = RawDocument {
325 src: vec![ElementInfo {
326 element: Element::Special(
327 None,
328 Special::CodeBlock {
329 lvl: 3,
330 inner: CodeContent::Parsed {
331 blocks: vec![CodeElem::Src("\ncode\n\n".into())],
332 meta: Default::default(),
333 hash: 3750657748055546767,
334 },
335 attributes: vec![],
336 },
337 ),
338 span: Span::new(0, 12),
339 }],
340 input: CowStr::from(input),
341 meta: None,
342 references: Default::default(),
343 };
344
345 compare(expected, input);
346 }
347
348 #[test]
349 fn test_code_param() {
350 let input = r#"```lang, val
351code
352```"#;
353 let expected = RawDocument {
354 src: vec![ElementInfo {
355 element: Element::Special(
356 None,
357 Special::CodeBlock {
358 lvl: 3,
359 inner: CodeContent::Parsed {
360 blocks: vec![CodeElem::Src("code\n\n".into())],
361 meta: Default::default(),
362 hash: 15492099155864206242,
363 },
364 attributes: vec!["lang".into(), "val".into()],
365 },
366 ),
367 span: Span::new(0, 21),
368 }],
369 input: CowStr::from(input),
370 meta: None,
371 references: Default::default(),
372 };
373
374 compare(expected, input);
375 }
376
377 #[test]
378 fn test_math() {
379 let input = "$inline$";
380 let expected = RawDocument {
381 src: vec![ElementInfo {
382 element: Element::Special(
383 None,
384 Special::Math {
385 is_block: false,
386 inner: "inline".into(),
387 },
388 ),
389 span: Span::new(0, 8),
390 }],
391 input: CowStr::from(input),
392 meta: None,
393 references: Default::default(),
394 };
395
396 compare(expected, input);
397 }
398
399 #[test]
400 fn test_verbatim() {
401 let input = "\\{verbatim\\}";
402 let expected = RawDocument {
403 src: vec![ElementInfo {
404 element: Element::Special(
405 None,
406 Special::Verbatim {
407 inner: "verbatim".into(),
408 },
409 ),
410 span: Span::new(2, 10),
411 }],
412 input: CowStr::from(input),
413 meta: None,
414 references: Default::default(),
415 };
416
417 compare(expected, input);
418 }
419
420 #[test]
421 fn test_src() {
422 let input = "just some stuff {} xx--^*# fsdf";
423 let expected = RawDocument {
424 src: vec![ElementInfo {
425 element: Element::Markdown(input.into()),
426 span: Span::new(0, 31),
427 }],
428 input: CowStr::from(input),
429 meta: None,
430 references: Default::default(),
431 };
432
433 compare(expected, input);
434 }
435
436 #[test]
437 fn test_refs() {
438 let input = "#call|id";
439 let expected = RawDocument {
440 src: vec![ElementInfo {
441 element: Element::Special(
442 Some("id".into()),
443 Special::Command {
444 function: "call".into(),
445 parameters: vec![],
446 body: None,
447 },
448 ),
449 span: Span::new(0, 8),
450 }],
451 input: CowStr::from(input),
452 meta: None,
453 references: HashMap::from([("id".into(), Reference::Command("call".into(), vec![]))]),
454 };
455
456 compare(expected, input);
457 }
458
459 const CMD_WITH_PARAMS_NO_BODY: &str =
460 "#func(basic, \"quoted\", {content}, key=basic, key=\"quoted\", key={content}, :flag)";
461
462 doc_tests! {
463 command
464 no_params_no_body: ("#func", vec![
465 ElementInfo {
466 element: Element::Special(None, Special::Command {
467 function: "func".into(),
468 parameters: vec![],
469 body: None,
470 }),
471 span: Span::new(0, 5),
472 }
473 ]),
474 with_params_no_body: (CMD_WITH_PARAMS_NO_BODY, vec![
475 ElementInfo {
476 element: Element::Special(None, Special::Command {
477 function: "func".into(),
478 parameters: vec![
479 Parameter { key: None, value: Value::String("basic".into()), span: Span::new(6, 11) },
480 Parameter { key: None, value: Value::String("quoted".into()), span: Span::new(13, 21) },
481 Parameter { key: None, value: Value::Content(vec![
482 ElementInfo {
483 element: Element::Markdown("content".into()),
484 span: Span::new(24, 31)
485 }
486 ]), span: Span::new(23, 32) },
487 Parameter { key: Some("key".into()), value: Value::String("basic".into()), span: Span::new(34, 43) },
488 Parameter { key: Some("key".into()), value: Value::String("quoted".into()), span: Span::new(45, 57) },
489 Parameter { key: Some("key".into()), value: Value::Content(vec![
490 ElementInfo {
491 element: Element::Markdown("content".into()),
492 span: Span::new(64, 71)
493 }
494 ]), span: Span::new( 59, 72) },
495 Parameter { key: None, value: Value::Flag("flag".into()), span: Span::new(74, 79) }
496 ],
497 body: None,
498 }),
499 span: Span::new(0, 80),
500 }
501 ]),
502 with_params_with_body: ("#func(c){x}", vec![
503 ElementInfo {
504 element: Element::Special(None, Special::Command {
505 function: "func".into(),
506 parameters: vec![
507 Parameter { key: None, value: Value::String("c".into()), span: Span::new(6, 7)}
508 ],
509 body: Some(vec![
510 ElementInfo {
511 element: Element::Markdown("x".into()),
512 span: Span::new(9, 10)
513 }
514 ])
515 }),
516 span: Span::new(0, 11),
517 }
518 ]),
519 no_params_with_body: ("#func{x}", vec![
520 ElementInfo {
521 element: Element::Special(None, Special::Command {
522 function: "func".into(),
523 parameters: vec![],
524 body: Some(vec![
525 ElementInfo {
526 element: Element::Markdown("x".into()),
527 span: Span::new(6, 7)
528 }
529 ])
530 }),
531 span: Span::new(0, 8),
532 }
533 ]),
534 body_nested: ("#func1{#func2}", vec![
535 ElementInfo {
536 element: Element::Special(None, Special::Command {
537 function: "func1".into(),
538 parameters: vec![],
539 body: Some(vec![ElementInfo {
540 element: Element::Special(None, Special::Command{
541 function: "func2".into(),
542 parameters: vec![],
543 body: None,
544 }),
545 span: Span::new(7, 13),
546 }
547 ])
548 }),
549 span: Span::new(0, 14),
550 }
551
552 ]),
553 }
554
555 fn compare(expected: RawDocument, input: &str) {
556 let doc = parse_to_doc(input).expect("Parse error");
557
558 assert_eq!(expected, doc);
559 }
560}