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::expressions::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  match fsm_specification(input.clone()) {
292    Ok((input, fsm_spec)) => {return Ok((input, MechCode::FsmSpecification(fsm_spec)));},
293    //Err(Failure(err)) => { return Err(Failure(err)); }
294    _ => () 
295  }
296  match fsm_implementation(input.clone()) {
297    Ok((input, fsm_impl)) => {return Ok((input, MechCode::FsmImplementation(fsm_impl)));},
298    //Err(Failure(err)) => { return Err(Failure(err)); }
299    _ => ()
300  }
301  match function_define(input.clone()) {
302    Ok((input, fxn_def)) => {return Ok((input, MechCode::FunctionDefine(fxn_def)));},
303    //Err(Failure(err)) => { return Err(Failure(err)); }
304    _ => () 
305  }
306  match statement(input.clone()) {
307    Ok((input, stmt)) => { return Ok((input, MechCode::Statement(stmt)));},
308    //Err(Failure(err)) => { return Err(Failure(err)); }
309    _ => ()
310  }
311  match expression(input.clone()) {
312    Ok((input, expr)) => {return Ok((input, MechCode::Expression(expr)));},
313    _ => ()
314  }
315  match comment(input.clone()) {
316    Ok((input, cmnt)) => {return Ok((input, MechCode::Comment(cmnt)));},
317    Err(err) => {return Err(err);}
318  }
319}
320
321// This is here to satisfy the type checker for this: alt((new_line, semicolon, comment_token))(input)?;
322pub fn comment_token(input: ParseString) -> ParseResult<Token> {
323  let (input, c) = comment(input)?;
324  Ok((input, c.text))
325}
326
327// mech_code := mech_code_alt, ("\n" | ";" | comment) ;
328pub fn mech_code(input: ParseString) -> ParseResult<MechCode> {
329  let (input, code) = mech_code_alt(input)?;
330  let (input, _) = many0(space_tab)(input)?;
331  let (input, _) = alt((new_line, semicolon, comment_token))(input)?;
332  let (input, _) = whitespace0(input)?;
333  Ok((input, code))
334}
335
336
337// program := title?, body ;
338pub fn program(input: ParseString) -> ParseResult<Program> {
339  let msg = "Expects program body";
340  let (input, _) = whitespace0(input)?;
341  let (input, title) = opt(title)(input)?;
342  //let (input, body) = labelr!(body, skip_nil, msg)(input)?;
343  let (input, body) = body(input)?;
344  let (input, _) = whitespace0(input)?;
345  Ok((input, Program{title, body}))
346}
347
348// parse_mech := program | statement ;
349pub fn parse_mech(input: ParseString) -> ParseResult<Program> {
350  //let (input, mech) = alt((program, statement))(input)?;
351  //Ok((input, ParserNode::Root { children: vec![mech] }))
352  let (input, mech) = program(input)?;
353  Ok((input, mech))
354}
355
356// 5. Error Reporting
357// --------------------
358
359/// Print formatted error message.
360pub fn print_err_report(text: &str, report: &ParserErrorReport) {
361  let msg = TextFormatter::new(text).format_error(report);
362  println!("{}", msg);
363}
364
365pub fn parse(text: &str) -> Result<Program, MechError> {
366  let graphemes = graphemes::init_source(text);
367  let mut result_node = None;
368  let mut error_log: Vec<(SourceRange, ParseErrorDetail)> = vec![];
369
370  // Do parse
371  let remaining: ParseString = match parse_mech(ParseString::new(&graphemes)) {
372    // Got a parse tree, however there may be errors
373    Ok((mut remaining_input, parse_tree)) => {
374      error_log.append(&mut remaining_input.error_log);
375      result_node = Some(parse_tree);
376      remaining_input
377    },
378    // Parsing failed and could not be recovered. No parse tree was created in this case
379    Err(err) => {
380      match err {
381        Err::Error(mut e) | Err::Failure(mut e) => {
382          error_log.append(&mut e.remaining_input.error_log);
383          error_log.push((e.cause_range, e.error_detail));
384          e.remaining_input
385        },
386        Err::Incomplete(_) => panic!("nom::Err::Incomplete is not supported!"),
387      }
388    },
389  };
390
391  // Check if all inputs were parsed
392  if remaining.len() != 0 {
393    let e = ParseError::new(remaining, "Inputs since here are not parsed");
394    error_log.push((e.cause_range, e.error_detail));
395  }
396
397  // Construct result
398  if error_log.is_empty() {
399    Ok(result_node.unwrap())
400  } else {
401    let report: ParserErrorReport = error_log.into_iter().map(|e| ParserErrorContext {
402      cause_rng: e.0,
403      err_message: String::from(e.1.message),
404      annotation_rngs: e.1.annotation_rngs,
405    }).collect();
406    let msg = TextFormatter::new(text).format_error(&report);
407    Err(MechError{file: file!().to_string(), tokens: vec![], msg: "".to_string(), id: 3202, kind: MechErrorKind::ParserError(report, msg)})
408  }
409}