Change grammar to properly allow if, while, and for loops (without confusing it with closures).

This commit is contained in:
Jesse Brault 2025-05-17 17:29:06 -05:00
parent bf06407d16
commit 692411e232
7 changed files with 177 additions and 39 deletions

24
Cargo.lock generated
View File

@ -218,9 +218,9 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "pest"
version = "2.7.14"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442"
checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6"
dependencies = [
"memchr",
"thiserror",
@ -229,9 +229,9 @@ dependencies = [
[[package]]
name = "pest_derive"
version = "2.7.14"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd"
checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5"
dependencies = [
"pest",
"pest_generator",
@ -239,9 +239,9 @@ dependencies = [
[[package]]
name = "pest_generator"
version = "2.7.14"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e"
checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841"
dependencies = [
"pest",
"pest_meta",
@ -252,9 +252,9 @@ dependencies = [
[[package]]
name = "pest_meta"
version = "2.7.14"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d"
checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0"
dependencies = [
"once_cell",
"pest",
@ -338,18 +338,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.69"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",

View File

@ -12,9 +12,9 @@ name = "dmc"
path = "src/bin/dmc/main.rs"
[dependencies]
pest = "2.7.14"
pest = { version = "2.8.0" }
clap = { version = "4.5.23", features = ["derive"] }
pest_derive = "2.7.14"
pest_derive = { version = "2.8.0", features = ["grammar-extras"] }
codespan-reporting = "0.12.0"
log = "0.4.27"
indoc = "2.0.6"

View File

@ -1,7 +1,7 @@
fn main(args: Array<String>) {
if args[0] == 'test' {
if (args[0] == 'test') {
println('test');
} else if args[0] == 'foo' {
} else if (args[0] == 'foo') {
println('foo');
} else {
println('not test');

View File

@ -941,11 +941,51 @@ fn build_if_else_statement(file_id: usize, if_else_statement_pair: Pair<Rule>) -
}
fn build_while_statement(file_id: usize, while_statement_pair: Pair<Rule>) -> WhileStatement {
todo!()
let mut inner = while_statement_pair.into_inner();
inner.next().unwrap(); // while
let condition = expect_and_use(
file_id,
inner.next().unwrap(),
Rule::Expression,
build_expression,
);
let body = expect_and_use(
file_id,
inner.next().unwrap(),
Rule::BlockStatement,
build_block_statement,
);
WhileStatement { condition, body }
}
fn build_for_statement(file_id: usize, for_statement_pair: Pair<Rule>) -> ForStatement {
todo!()
let mut inner = for_statement_pair.into_inner();
inner.next().unwrap(); // for
let variable = expect_and_use(
file_id,
inner.next().unwrap(),
Rule::Identifier,
build_identifier,
);
inner.next().unwrap(); // in
let iterator = expect_and_use(
file_id,
inner.next().unwrap(),
Rule::Expression,
build_expression,
);
let body = expect_and_use(
file_id,
inner.next().unwrap(),
Rule::BlockStatement,
build_block_statement,
);
ForStatement {
variable,
iterator,
body,
}
}
fn build_expression(file_id: usize, expression_pair: Pair<Rule>) -> Expression {
@ -1514,7 +1554,11 @@ mod tests {
if let Err(e) = parse_result {
panic!("Parsing failed.\n{}", e)
} else {
let ast = build_ast(0, parse_result.unwrap().next().unwrap());
let mut pairs = parse_result.unwrap();
if pairs.as_str().trim() != src.trim() {
panic!("Parsing did not consume entire input.");
}
let ast = build_ast(0, pairs.next().unwrap());
dbg!(ast);
}
}
@ -1576,7 +1620,7 @@ mod tests {
fn if_statement() {
assert_builds(indoc! {"
fn main() {
if true {
if (true) {
foo
}
}
@ -1587,7 +1631,7 @@ mod tests {
fn if_else_statement() {
assert_builds(indoc! {"
fn main() {
if true {
if (true) {
foo
} else {
bar
@ -1600,9 +1644,9 @@ mod tests {
fn if_else_if_statement() {
assert_builds(indoc! {"
fn main() {
if true {
if (true) {
foo
} else if false {
} else if (false) {
bar
}
}
@ -1613,9 +1657,9 @@ mod tests {
fn if_else_if_else_statement() {
assert_builds(indoc! {"
fn main() {
if true {
if (true) {
foo
} else if false {
} else if (false) {
bar
} else {
buzz
@ -1623,4 +1667,59 @@ mod tests {
}
"})
}
#[test]
fn while_statement() {
assert_builds(indoc! {"
fn main() {
while (true) {
foo()
}
}
"})
}
#[test]
fn for_statement() {
assert_builds(indoc! {"
fn main(args: Array<String>) {
for (arg in args) {
foo(arg);
}
}
"})
}
#[test]
fn if_with_call_condition() {
assert_builds(indoc! {"
fn main() {
if (foo()) {
bar()
}
}
"})
}
#[test]
fn while_with_call_condition() {
assert_builds(indoc! {"
fn main() {
while (foo()) {
bar()
}
}
"})
}
#[test]
fn for_with_call_iterator() {
assert_builds(indoc! {"
fn main() {
for (foo in bar()) {
baz(foo)
}
}
"})
}
}

View File

@ -824,9 +824,9 @@ impl Unparse for ReturnStatement {
impl Unparse for IfStatement {
fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
writer.write_indented("if ")?;
writer.write_indented("if (")?;
self.condition.unparse(writer)?;
writer.writeln(" {")?;
writer.writeln(") {")?;
self.then_block.unparse_inner(writer)?;
writer.writeln_indented("}")?;
Ok(())
@ -847,9 +847,9 @@ impl Unparse for IfElseStatement {
impl Unparse for ElseIfs {
fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
for if_statement in &self.0 {
writer.write_indented("else if ")?;
writer.write_indented("else if (")?;
if_statement.condition.unparse(writer)?;
writer.writeln(" {")?;
writer.writeln(") {")?;
if_statement.then_block.unparse_inner(writer)?;
writer.writeln_indented("}")?;
}

View File

@ -536,7 +536,9 @@ ReturnStatement = {
IfStatement = {
If
~ "("
~ Expression
~ ")"
~ BlockStatement
}
@ -558,15 +560,19 @@ ElseBlock = {
WhileStatement = {
While
~ "("
~ Expression
~ ")"
~ BlockStatement
}
ForStatement = {
For
~ Expression
~ "("
~ Identifier
~ In
~ Expression
~ ")"
~ BlockStatement
}

View File

@ -8,7 +8,7 @@ pub struct DeimosParser;
mod deimos_parser_tests {
use crate::parser::{DeimosParser, Rule};
use indoc::indoc;
use pest::Parser;
use pest::{parses_to, Parser};
macro_rules! fail_rule {
($pair: expr; $rule:path) => {{
@ -34,7 +34,7 @@ mod deimos_parser_tests {
panic!("Parsing failed.\n{}", e);
} else {
let mut pairs = parse_result.unwrap();
if input != pairs.as_str() {
if input.trim() != pairs.as_str().trim() {
panic!(
"Parsing did not consume entire input. Consumed only:\n{}",
pairs.as_str()
@ -101,7 +101,7 @@ mod deimos_parser_tests {
parses_to(
Rule::IfStatement,
indoc! {"
if foo == 42 {
if (foo == 42) {
bar()
}"},
)
@ -112,7 +112,7 @@ mod deimos_parser_tests {
parses_to(
Rule::IfElseStatement,
indoc! {"
if foo == 42 {
if (foo == 42) {
bar()
} else {
baz()
@ -125,9 +125,9 @@ mod deimos_parser_tests {
parses_to(
Rule::IfElseStatement,
indoc! {"
if foo == 42 {
if (foo == 42) {
bar()
} else if foo == 16 {
} else if (foo == 16) {
baz()
}"},
)
@ -138,13 +138,46 @@ mod deimos_parser_tests {
parses_to(
Rule::IfElseStatement,
indoc! {"
if foo == 42 {
if (foo == 42) {
foo()
} else if foo == 16 {
} 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);
}
"})
}
}