use crate::error::ForgeResult; use crate::lexer::{script_to_tokens, ScriptTokenType}; use crate::parser::ast::{Expression, Program}; use crate::TokenError; use peg::Parse; pub type InputSpan<'a, Loc, Tok> = Result<(Loc, Tok, Loc), String>; type ExprSpan<'a> = InputSpan<'a, usize, ScriptTokenType>; macro_rules! export_grammar_fn { ($name:ident = $output:ty => $part: tt) => { pub fn $name(source: &str) -> Result<$output, String> { let tokens = script_to_tokens(source) .map_err(|e| format!("{}", e))? .iter() .map(|tok| { Ok(( tok.position.location_offset(), tok.token_type.clone(), tok.position.location_offset() + tok.token_type.len(), )) }) .collect::<Vec<ExprSpan>>(); let value = super::forge_grammar::$part::new() .parse::<ExprSpan, Vec<ExprSpan>>(tokens) .map_err(|e| format!("{}", e))?; Ok(value) } }; } export_grammar_fn!(parse_program = Program => ProgramParser); export_grammar_fn!(parse_expression = Expression => ExpressionParser); #[cfg(test)] mod grammar_test { use super::{parse_expression, parse_program}; use crate::parse::ast::Expression; use crate::parser::ast::Program; use test_case::test_case; #[test_case("123" => matches Ok(_) ; "Parse literal number")] #[test_case(r#""Basic String""# => matches Ok(_) ; "Parse literal string")] #[test_case("false" => matches Ok(_) ; "Parse literal false")] #[test_case("true" => matches Ok(_) ; "Parse literal true")] #[test_case("null" => matches Ok(_) ; "Parse literal null")] #[test_case("if foo {}" => matches Ok(_) ; "Parse conditional")] #[test_case("if foo {} else if bar {}" => matches Ok(_) ; "Parse multi-conditional")] #[test_case("if foo {} else if bar {} else {}" => matches Ok(_) ; "Parse multi-conditional with else")] #[test_case("2 * 4 - 3" => matches Ok(_) ; "Parse arithmetic")] #[test_case("10 - 2 * 4 + 3" => matches Ok(_) ; "Parse arithmetic reverse")] #[test_case("foo.bar" => matches Ok(_) ; "Basic property accessor")] #[test_case("foo.bar()" => matches Ok(_) ; "function property accessor")] #[test_case("foo.bar().baz" => matches Ok(_) ; "nested property accessor")] #[test_case("if something_else {}.bar" => matches Ok(_) ; "expression property accessor")] #[test_case("123 + if if foo { bar } else { baz } { 200 } else { 300 }" => matches Ok(_) ; "Parse inline condition")] #[test_case("fn add(a, b) { a + b }" => matches Ok(_) ; "Declare basic fn")] #[test_case("fn has_no_params() {}" => matches Ok(_) ; "Declare empty params fn")] #[test_case("fn do_some_stuff(foo = 123, bar, baz = \"My Default String\") {}" => matches Ok(_) ; "Declare complex fn")] #[test_case("let som_value = fn do_some_stuff(foo = 123, bar, baz = \"My Default String\") {}" => matches Ok(_) ; "First class fn")] fn expression_parsing(prog: &str) -> Result<Expression, String> { parse_expression(prog).map(|expr| { dbg!(&expr); expr }) } #[test_case("10 - 2 * 4 + 3; false; 12 + 14" => matches Ok(_) ; "Parse value expr list")] #[test_case("10 - 2 * 4 + 3; false; 12 + 14;" => matches Ok(_) ; "Parse void expr list")] #[test_case("10 - 2 * 4 + 3;; false; 12 + 14;;;;;" => matches Ok(_) ; "Infinite semicolons")] fn program_parsing(prog: &str) -> Result<Program, String> { parse_program(prog).map(|expr| { dbg!(&expr); expr }) } }