redid the parser, patched up the project too.

This commit is contained in:
2022-07-03 18:01:40 +02:00
parent 6fb4b581b1
commit 2b55fae10d
30 changed files with 967 additions and 570 deletions

View File

@@ -1,12 +1,14 @@
use chumsky::{self, prelude::*, Parser};
/// Matches any one of the passed operators, longest-first
fn op_parser<'a>(ops: &[&'a str]) -> BoxedParser<'a, char, String, Simple<char>> {
let mut sorted_ops = ops.to_vec();
fn op_parser<'a, T: AsRef<str> + Clone>(ops: &[T]) -> BoxedParser<'a, char, String, Simple<char>> {
let mut sorted_ops: Vec<String> = ops.iter().map(|t| t.as_ref().to_string()).collect();
sorted_ops.sort_by(|a, b| b.len().cmp(&a.len()));
sorted_ops.into_iter()
.map(|op| just(op.to_string()).boxed())
.reduce(|a, b| a.or(b).boxed()).unwrap()
.map(|op| just(op).boxed())
.reduce(|a, b| a.or(b).boxed())
.unwrap_or(empty().map(|()| panic!("Empty isn't meant to match")).boxed())
.labelled("operator").boxed()
}
/// Matches anything that's allowed as an operator
@@ -27,20 +29,31 @@ fn op_parser<'a>(ops: &[&'a str]) -> BoxedParser<'a, char, String, Simple<char>>
/// TODO: `.` could possibly be parsed as an operator depending on context. This operator is very
/// common in maths so it's worth a try. Investigate.
pub fn modname_parser<'a>() -> impl Parser<char, String, Error = Simple<char>> + 'a {
let not_name_char: Vec<char> = vec![':', '\\', '@', '"', '\'', '(', ')', '.'];
let not_name_char: Vec<char> = vec![':', '\\', '@', '"', '\'', '(', ')', ','];
filter(move |c| !not_name_char.contains(c) && !c.is_whitespace())
.repeated().at_least(1)
.collect()
.labelled("modname")
}
/// Parse an operator or name. Failing both, parse everything up to the next whitespace or
/// blacklisted character as a new operator.
pub fn name_parser<'a>(
ops: &[&'a str]
) -> impl Parser<char, Vec<String>, Error = Simple<char>> + 'a {
pub fn name_parser<'a, T: AsRef<str> + Clone>(
ops: &[T]
) -> impl Parser<char, String, Error = Simple<char>> + 'a {
choice((
op_parser(ops), // First try to parse a known operator
text::ident(), // Failing that, parse plain text
text::ident().labelled("plain text"), // Failing that, parse plain text
modname_parser() // Finally parse everything until tne next terminal as a new operator
)).padded().separated_by(just("::")).padded()
))
.labelled("name")
}
/// Decide if a string can be an operator. Operators can include digits and text, just not at the
/// start.
pub fn is_op<T: AsRef<str>>(s: T) -> bool {
return match s.as_ref().chars().next() {
Some(x) => !x.is_alphanumeric(),
None => false
}
}