iowo/crates/json-pawarser/src/grammar.rs

78 lines
2 KiB
Rust

use array::array;
use enumset::{enum_set, EnumSet};
use pawarser::parser::ParserBuilder;
use crate::{
syntax_error::SyntaxError,
syntax_kind::{lex, SyntaxKind},
};
use self::object::object;
mod array;
mod object;
pub(crate) type Parser<'src> = pawarser::Parser<'src, SyntaxKind, SyntaxError>;
pub(crate) type CompletedMarker = pawarser::CompletedMarker<SyntaxKind, SyntaxError>;
const BASIC_VALUE_TOKENS: EnumSet<SyntaxKind> =
enum_set!(SyntaxKind::BOOL | SyntaxKind::NULL | SyntaxKind::NUMBER | SyntaxKind::STRING);
pub fn value(p: &mut Parser) -> bool {
if BASIC_VALUE_TOKENS.contains(p.current()) {
p.do_bump();
return true;
} else {
object(p).or_else(|| array(p)).is_some()
}
}
#[cfg(test)]
mod tests {
use super::{
test_utils::{check_parser, gen_checks},
value,
};
#[test]
fn value_lit() {
gen_checks! {value;
r#""helo world""# => r#"ROOT { STRING "\"helo world\""; }"#,
"42" => r#"ROOT { NUMBER "42"; }"#,
"null" => r#"ROOT { NULL "null"; }"#,
"true" => r#"ROOT { BOOL "true"; }"#,
"false" => r#"ROOT { BOOL "false"; }"#
};
}
}
#[cfg(test)]
mod test_utils {
use pawarser::parser::ParserBuilder;
use crate::syntax_kind::{lex, SyntaxKind};
use super::Parser;
macro_rules! gen_checks {
($fn_to_test:ident; $($in:literal => $out:literal),+) => {
$(crate::grammar::test_utils::check_parser($in, |p| { $fn_to_test(p); }, $out);)+
}
}
pub(super) use gen_checks;
pub(super) fn check_parser(input: &str, parser_fn: fn(&mut Parser), expected_output: &str) {
let toks = lex(input);
let mut p: Parser = ParserBuilder::new(toks)
.add_meaningless(SyntaxKind::WHITESPACE)
.add_meaningless(SyntaxKind::NEWLINE)
.build();
parser_fn(&mut p);
let out = p.finish();
assert_eq!(format!("{out:?}").trim_end(), expected_output);
}
}