mech_syntax/
parser.rs

1// Parser
2// ========
3
4/// Sections:
5///   1. Prelude
6///   2. Parser combinators
7///   3. Recovery functions
8///   4. Public interface
9///   5. Error reporting
10
11// 1. Prelude
12// ------------
13
14use crate::*;
15use crate::functions::function_define;
16
17use mech_core::{MechError, MechErrorKind, ParserErrorContext, ParserErrorReport};
18use mech_core::nodes::*;
19use mech_core::nodes::{SectionElement, MechString, Table};
20
21#[cfg(not(feature = "no-std"))] use core::fmt;
22#[cfg(feature = "no-std")] use alloc::fmt;
23#[cfg(feature = "no-std")] use alloc::string::String;
24#[cfg(feature = "no-std")] use alloc::vec::Vec;
25use nom::{
26  IResult,
27  branch::alt,
28  sequence::tuple as nom_tuple,
29  combinator::{opt, eof},
30  multi::{many1, many_till, many0, separated_list1, separated_list0},
31  Err,
32  Err::Failure
33};
34
35use std::collections::HashMap;
36use colored::*;
37
38//use crate::*;
39use crate::{
40  TextFormatter,
41  ParseError,
42  ParseString,
43  ParseErrorDetail,
44  graphemes,
45  ParseResult,
46};
47
48// 2. Parser combinators
49// -----------------------
50
51/// Convert output of any parser into ParserNode::Null.
52/// Useful for working with `alt` combinator and error recovery functions.
53pub fn null<'a, F, O>(mut parser: F) ->
54  impl FnMut(ParseString<'a>) -> ParseResult<()>
55where
56  F: FnMut(ParseString<'a>) -> ParseResult<O>
57{
58  move |input: ParseString| match parser(input) {
59    Ok((remaining, _)) => Ok((remaining, ())),
60    Err(Err::Error(e)) => Err(Err::Error(e)),
61    Err(Err::Failure(e)) => Err(Err::Failure(e)),
62    x => panic!("Err::Incomplete is not supported"),
63  }
64}
65
66/// For parser p, run p and also output the range that p has matched
67/// upon success.
68pub fn range<'a, F, O>(mut parser: F) ->
69  impl FnMut(ParseString<'a>) -> ParseResult<(O, SourceRange)>
70where
71  F: FnMut(ParseString<'a>) -> ParseResult<O>
72{
73  move |input: ParseString| {
74    let start = input.loc();
75    match parser(input) {
76      Ok((remaining, o)) => {
77        let rng = SourceRange { start, end: remaining.loc(), };
78        Ok((remaining, (o, rng)))
79      },
80      Err(e) => Err(e),
81    }
82  }
83}
84
85#[macro_export]
86macro_rules! label {
87  ($parser:expr, $msg:expr) => {
88    (label_without_recovery($parser, ParseErrorDetail {
89      message: $msg, annotation_rngs: vec![]
90    }))
91  };
92
93  ($parser:expr, $msg:expr, $($rngs:expr),+) => {
94    (label_without_recovery($parser, ParseErrorDetail {
95      message: $msg, annotation_rngs: vec![$($rngs),+]
96    }))
97  };
98}
99
100#[macro_export]
101macro_rules! labelr {
102  ($parser:expr, $recovery_fn:expr, $msg:expr) => {
103    (label_with_recovery($parser, $recovery_fn, ParseErrorDetail {
104      message: $msg, annotation_rngs: vec![]
105    }))
106  };
107
108  ($parser:expr, $recovery_fn:expr, $msg:expr, $($rngs:expr),+) => {
109    (label_with_recovery($parser, $recovery_fn, ParseErrorDetail {
110      message: $msg, annotation_rngs: vec![$($rngs),+]
111    }))
112  };
113}
114
115/// Label without recovery function. Upgrade Err::Error to Err:Failure
116/// and override its context information.
117pub fn label_without_recovery<'a, F, O>(
118  mut parser: F,
119  error_detail: ParseErrorDetail,
120) ->
121  impl FnMut(ParseString<'a>) -> ParseResult<O>
122where
123  F: FnMut(ParseString<'a>) -> ParseResult<O>
124{
125  move |mut input: ParseString| {
126    let start = input.loc();
127    match parser(input) {
128      Err(Err::Error(mut e)) => {
129        e.cause_range = SourceRange { start, end: e.cause_range.end };
130        e.error_detail = error_detail.clone();
131        Err(Err::Failure(e))
132      }
133      x => x,
134    }
135  }
136}
137
138/// Label with recovery function. In addition to upgrading errors, the
139/// error is logged and recovery function will be run as an attempt to
140/// synchronize parser state.
141pub fn label_with_recovery<'a, F, O>(
142  mut parser: F,
143  mut recovery_fn: fn(ParseString<'a>) -> ParseResult<O>,
144  error_detail: ParseErrorDetail,
145) ->
146  impl FnMut(ParseString<'a>) -> ParseResult<O>
147where
148  F: FnMut(ParseString<'a>) -> ParseResult<O>
149{
150  move |mut input: ParseString| {
151    let start = input.loc();
152    match parser(input) {
153      Err(Err::Error(mut e)) => {
154        e.cause_range = SourceRange { start, end: e.cause_range.end };
155        e.error_detail = error_detail.clone();
156        e.log();
157        recovery_fn(e.remaining_input)
158      }
159      Err(Err::Failure(mut e)) => {
160        e.log();
161        recovery_fn(e.remaining_input)
162      },
163      x => x,
164    }
165  }
166}
167
168/// For parser p, return the `!!p` peek parsing expression.
169pub fn is<'a, F, O>(mut parser: F) ->
170  impl FnMut(ParseString<'a>) -> ParseResult<O>
171where
172  F: FnMut(ParseString<'a>) -> ParseResult<O>
173{
174  move |input: ParseString| {
175    let input_clone = input.clone();
176    match parser(input_clone) {
177      Ok((_, o)) => Ok((input, o)),
178      _ => Err(Err::Error(ParseError::new(input, "Unexpected character"))),
179    }
180  }
181}
182
183/// For parser p, return the `!p` peek parsing expression.
184pub fn is_not<'a, F, E>(mut parser: F) ->
185  impl FnMut(ParseString<'a>) -> ParseResult<()>
186where
187  F: FnMut(ParseString<'a>) -> ParseResult<E>
188{
189  move |input: ParseString| {
190    let input_clone = input.clone();
191    match parser(input_clone) {
192      Err(Err::Failure(_)) |
193      Err(Err::Error(_)) => Ok((input, ())),
194      _ => Err(Err::Error(ParseError::new(input, "Unexpected character")))
195    }
196  }
197}
198
199/// Return a terminal parsing expression that consumes `tag` from input.
200pub fn tag(tag: &'static str) -> impl Fn(ParseString) -> ParseResult<String> {
201  move |mut input: ParseString| {
202    if input.is_empty() {
203      return Err(nom::Err::Error(ParseError::new(input, "Unexpected eof")));
204    }
205    if let Some(matched) = input.consume_tag(tag) {
206      Ok((input, matched))
207    } else {
208      Err(nom::Err::Error(ParseError::new(input, "Unexpected char")))
209    }
210  }
211}
212
213// 3. Recovery functions
214// -----------------------
215
216// skip_till_eol := (!new_line, any)* ;
217pub fn skip_till_eol(input: ParseString) -> ParseResult<()> {
218  let (input, _) = many0(nom_tuple((
219    is_not(new_line),
220    any,
221  )))(input)?;
222  Ok((input, ()))
223}
224
225// skip_past_eol := skip_till_eol, new_line ;
226fn skip_past_eol(input: ParseString) -> ParseResult<()> {
227  let (input, _) = skip_till_eol(input)?;
228  let (input, _) = new_line(input)?;
229  Ok((input, ()))
230}
231
232// skip_till_section_element := skip_past_eol, (!section_element, skip_past_eol)* ;
233fn skip_till_section_element(input: ParseString) -> ParseResult<()> {
234  if input.is_empty() {
235    return Ok((input, ()));
236  }
237  let (input, _) = skip_past_eol(input)?;
238  let (input, _) = many0(nom_tuple((
239    is_not(section_element),
240    skip_past_eol,
241  )))(input)?;
242  Ok((input, ()))
243}
244
245/*
246fn skip_till_section_element2(input: ParseString) -> ParseResult<ParserNode> {
247  if input.len() == 0 {
248    return Ok((input, ParserNode::Error));
249  }
250  let (input, _) = skip_past_eol(input)?;
251  let (input, _) = many0(nom_tuple((
252    is_not(section_element2),
253    skip_past_eol,
254  )))(input)?;
255  Ok((input, ParserNode::Error))
256}
257
258fn skip_till_section_element3(input: ParseString) -> ParseResult<ParserNode> {
259  if input.len() == 0 {
260    return Ok((input, ParserNode::Error));
261  }
262  let (input, _) = skip_past_eol(input)?;
263  let (input, _) = many0(nom_tuple((
264    is_not(section_element3),
265    skip_past_eol,
266  )))(input)?;
267  Ok((input, ParserNode::Error))
268}*/
269
270// skip_spaces := space* ;
271pub fn skip_spaces(input: ParseString) -> ParseResult<()> {
272  let (input, _) = many0(space)(input)?;
273  Ok((input, ()))
274}
275
276// skip_nil := ;
277pub fn skip_nil(input: ParseString) -> ParseResult<()> {
278  Ok((input, ()))
279}
280
281// skip_empty_mech_directive := ;
282pub fn skip_empty_mech_directive(input: ParseString) -> ParseResult<String> {
283  Ok((input, String::from("mech:")))
284}
285
286// 4. Public interface
287// ---------------------
288
289// mech_code_alt := fsm_specification | fsm_implementation | function_define | statement | expression | comment ;
290pub fn mech_code_alt(input: ParseString) -> ParseResult<MechCode> {
291  let (input, _) = whitespace0(input)?;
292  /*match fsm_specification(input.clone()) {
293    Ok((input, fsm_spec)) => {return Ok((input, MechCode::FsmSpecification(fsm_spec)));},
294    //Err(Failure(err)) => { return Err(Failure(err)); }
295    _ => () 
296  }
297  match fsm_implementation(input.clone()) {
298    Ok((input, fsm_impl)) => {return Ok((input, MechCode::FsmImplementation(fsm_impl)));},
299    //Err(Failure(err)) => { return Err(Failure(err)); }
300    _ => ()
301  }
302  match function_define(input.clone()) {
303    Ok((input, fxn_def)) => {return Ok((input, MechCode::FunctionDefine(fxn_def)));},
304    //Err(Failure(err)) => { return Err(Failure(err)); }
305    _ => () 
306  }*/
307  match statement(input.clone()) {
308    Ok((input, stmt)) => {return Ok((input, MechCode::Statement(stmt)));},
309    //Err(Failure(err)) => { return Err(Failure(err)); }
310    _ => ()
311  }
312  match expression(input.clone()) {
313    Ok((input, expr)) => {return Ok((input, MechCode::Expression(expr)));},
314    _ => ()
315  }
316  match comment(input.clone()) {
317    Ok((input, cmnt)) => {return Ok((input, MechCode::Comment(cmnt)));},
318    Err(err) => {return Err(err);}
319  }
320}
321
322// mech_code := mech_code_alt, ("\n" | ";" | comment) ;
323pub fn mech_code(input: ParseString) -> ParseResult<(MechCode,Option<Comment>)> {
324  let (input, code) = mech_code_alt(input)?;
325  if input.is_empty() {
326    return Ok((input, (code, None)));
327  }
328  let (input, _) = many0(space_tab)(input)?;
329  let (input, cmmnt) = opt(tuple((opt(semicolon),many0(space_tab),comment)))(input)?;
330  let (input, _) = alt((null(new_line), null(semicolon)))(input)?;
331  let (input, _) = whitespace0(input)?;
332  let cmmt = match cmmnt {
333    Some((_, _, cmnt)) => Some(cmnt),
334    None => None,
335  };
336  Ok((input,(code,cmmt)))
337}
338
339
340// program := title?, body ;
341pub fn program(input: ParseString) -> ParseResult<Program> {
342  let msg = "Expects program body";
343  let (input, _) = whitespace0(input)?;
344  let (input, title) = opt(title)(input)?;
345  //let (input, body) = labelr!(body, skip_nil, msg)(input)?;
346  let (input, body) = body(input)?;
347  let (input, _) = whitespace0(input)?;
348  Ok((input, Program{title, body}))
349}
350
351// parse_mech := program | statement ;
352pub fn parse_mech(input: ParseString) -> ParseResult<Program> {
353  //let (input, mech) = alt((program, statement))(input)?;
354  //Ok((input, ParserNode::Root { children: vec![mech] }))
355  let (input, mech) = program(input)?;
356  Ok((input, mech))
357}
358
359// 5. Error Reporting
360// --------------------
361
362/// Print formatted error message.
363pub fn print_err_report(text: &str, report: &ParserErrorReport) {
364  let msg = TextFormatter::new(text).format_error(report);
365  println!("{}", msg);
366}
367
368pub fn parse_grammar(text: &str) -> MResult<Grammar> {
369  // remove all whitespace from the input string
370  let text_no_Ws = &text.replace(" ", "").replace("\n", "").replace("\r", "").replace("\t", "");
371  let graphemes = graphemes::init_source(text_no_Ws);
372  let mut result_node = None;
373  let mut error_log: Vec<(SourceRange, ParseErrorDetail)> = vec![];
374
375  // Do parse
376  let remaining: ParseString = match grammar(ParseString::new(&graphemes)) {
377    // Got a parse tree, however there may be errors
378    Ok((mut remaining_input, parse_tree)) => {
379      error_log.append(&mut remaining_input.error_log);
380      result_node = Some(parse_tree);
381      remaining_input
382    },
383    // Parsing failed and could not be recovered. No parse tree was created in this case
384    Err(err) => {
385      match err {
386        Err::Error(mut e) | Err::Failure(mut e) => {
387          error_log.append(&mut e.remaining_input.error_log);
388          error_log.push((e.cause_range, e.error_detail));
389          e.remaining_input
390        },
391        Err::Incomplete(_) => panic!("nom::Err::Incomplete is not supported!"),
392      }
393    },
394  };
395  // Check if all inputs were parsed
396  if remaining.len() != 0 {
397    let e = ParseError::new(remaining, "Inputs since here are not parsed");
398    error_log.push((e.cause_range, e.error_detail));
399  }
400
401  // Construct result
402  if error_log.is_empty() {
403    Ok(result_node.unwrap())
404  } else {
405    let report: ParserErrorReport = error_log.into_iter().map(|e| ParserErrorContext {
406      cause_rng: e.0,
407      err_message: String::from(e.1.message),
408      annotation_rngs: e.1.annotation_rngs,
409    }).collect();
410    let msg = TextFormatter::new(text).format_error(&report);
411    Err(MechError{file: file!().to_string(), tokens: vec![], msg: "".to_string(), id: 3202, kind: MechErrorKind::ParserError(report, msg)})
412  }
413}
414
415
416pub fn parse(text: &str) -> MResult<Program> {
417  let graphemes = graphemes::init_source(text);
418  let mut result_node = None;
419  let mut error_log: Vec<(SourceRange, ParseErrorDetail)> = vec![];
420
421  // Do parse
422  let remaining: ParseString = match parse_mech(ParseString::new(&graphemes)) {
423    // Got a parse tree, however there may be errors
424    Ok((mut remaining_input, parse_tree)) => {
425      error_log.append(&mut remaining_input.error_log);
426      result_node = Some(parse_tree);
427      remaining_input
428    },
429    // Parsing failed and could not be recovered. No parse tree was created in this case
430    Err(err) => {
431      match err {
432        Err::Error(mut e) | Err::Failure(mut e) => {
433          error_log.append(&mut e.remaining_input.error_log);
434          error_log.push((e.cause_range, e.error_detail));
435          e.remaining_input
436        },
437        Err::Incomplete(_) => panic!("nom::Err::Incomplete is not supported!"),
438      }
439    },
440  };
441
442  // Check if all inputs were parsed
443  if remaining.len() != 0 {
444    let e = ParseError::new(remaining, "Inputs since here are not parsed");
445    error_log.push((e.cause_range, e.error_detail));
446  }
447
448  // Construct result
449  if error_log.is_empty() {
450    Ok(result_node.unwrap())
451  } else {
452    let report: ParserErrorReport = error_log.into_iter().map(|e| ParserErrorContext {
453      cause_rng: e.0,
454      err_message: String::from(e.1.message),
455      annotation_rngs: e.1.annotation_rngs,
456    }).collect();
457    let msg = TextFormatter::new(text).format_error(&report);
458    Err(MechError{file: file!().to_string(), tokens: vec![], msg: "".to_string(), id: 3202, kind: MechErrorKind::ParserError(report, msg)})
459  }
460}