deimos-lang/src/parser/mod.rs
2025-05-18 08:32:16 -05:00

205 lines
4.4 KiB
Rust

use pest_derive::Parser;
#[derive(Parser)]
#[grammar = "parser/deimos.pest"]
pub struct DeimosParser;
#[cfg(test)]
mod deimos_parser_tests {
use crate::parser::{DeimosParser, Rule};
use indoc::indoc;
use pest::Parser;
macro_rules! fail_rule {
($pair: expr; $rule:path) => {{
panic!(
"Expected {} but found {:?}",
stringify!($rule),
$pair.as_rule()
)
}};
}
macro_rules! match_rule {
($pair:expr; $rule:path) => {{
if $rule != $pair.as_rule() {
fail_rule!($pair; $rule);
}
}};
}
fn parses_to(rule: Rule, input: &str) {
let parse_result = DeimosParser::parse(rule, input);
if let Err(e) = parse_result {
panic!("Parsing failed.\n{}", e);
} else {
let mut pairs = parse_result.unwrap();
if input.trim() != pairs.as_str().trim() {
panic!(
"Parsing did not consume entire input. Consumed only:\n{}",
pairs.as_str()
);
}
let first = pairs.next().unwrap();
match_rule!(first; rule)
}
}
#[test]
fn hex_int() {
parses_to(Rule::IntLiteral, "0x1234abcd");
}
#[test]
fn hex_long() {
parses_to(Rule::LongLiteral, "0x123456789abcdefL");
}
#[test]
fn suffix_expression_call_single_identifier() {
parses_to(Rule::SuffixExpression, "foo()");
}
#[test]
fn simple_interface() {
parses_to(Rule::CompilationUnit, "pub int Simple { fn foo() -> Void }");
}
#[test]
fn interface_with_op() {
parses_to(
Rule::CompilationUnit,
"pub int Callable { op () () -> Void }",
);
}
#[test]
fn interface_with_alias() {
parses_to(
Rule::CompilationUnit,
indoc! {"
pub int Callable {
fn call() -> Void
def op () () -> Void alias call
}
"},
);
}
#[test]
fn index_identifier() {
parses_to(Rule::SuffixExpression, "foo[0]");
}
#[test]
fn chained_index_call_on_identifier() {
parses_to(Rule::SuffixExpression, "foo[0]()");
}
#[test]
fn if_statement() {
parses_to(
Rule::IfStatement,
indoc! {"
if (foo == 42) {
bar()
}"},
)
}
#[test]
fn if_else_statement() {
parses_to(
Rule::IfElseStatement,
indoc! {"
if (foo == 42) {
bar()
} else {
baz()
}"},
)
}
#[test]
fn if_else_if_statement() {
parses_to(
Rule::IfElseStatement,
indoc! {"
if (foo == 42) {
bar()
} else if (foo == 16) {
baz()
}"},
)
}
#[test]
fn if_else_if_else_statement() {
parses_to(
Rule::IfElseStatement,
indoc! {"
if (foo == 42) {
foo()
} else if (foo == 16) {
baz()
} else {
fizz()
}"},
)
}
#[test]
fn while_statement() {
parses_to(Rule::WhileStatement, "while (foo) { bar() }");
}
#[test]
fn for_statement() {
parses_to(Rule::ForStatement, "for (foo in bar) { baz(foo); }");
}
#[test]
fn if_statement_with_call_condition() {
parses_to(
Rule::IfStatement,
indoc! {"
if (foo()) {
bar()
}
"},
)
}
#[test]
fn while_statement_with_call_condition() {
parses_to(Rule::WhileStatement, "while (foo()) { bar(); }")
}
#[test]
fn for_statement_with_call_iterator() {
parses_to(
Rule::ForStatement,
indoc! {"
for (foo in bar()) {
baz(foo);
}
"},
)
}
#[test]
fn use_statement() {
parses_to(Rule::UseStatement, "use std::core::println");
}
#[test]
fn use_star() {
parses_to(Rule::UseStatement, "use std::core::*")
}
#[test]
fn use_list() {
parses_to(Rule::UseStatement, "use std::core::{print, println}");
}
}