Implement if/if-else statements in grammar, build, unparse, and pretty-print.

This commit is contained in:
Jesse Brault 2025-05-17 12:36:29 -05:00
parent 35d616a538
commit bf06407d16
7 changed files with 316 additions and 76 deletions

9
sketching/may_2025/if.dm Normal file
View File

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

View File

@ -863,15 +863,81 @@ fn build_call_statement(file_id: usize, call_statement_pair: Pair<Rule>) -> Call
} }
fn build_return_statement(file_id: usize, return_statement_pair: Pair<Rule>) -> ReturnStatement { fn build_return_statement(file_id: usize, return_statement_pair: Pair<Rule>) -> ReturnStatement {
todo!() let mut inner = return_statement_pair.into_inner();
if inner.len() == 2 {
inner.next().unwrap(); // return
let expression = expect_and_use(
file_id,
inner.next().unwrap(),
Rule::Expression,
build_expression,
);
ReturnStatement(Some(expression))
} else {
ReturnStatement(None)
}
} }
fn build_if_statement(file_id: usize, if_statement_pair: Pair<Rule>) -> IfStatement { fn build_if_statement(file_id: usize, if_statement_pair: Pair<Rule>) -> IfStatement {
todo!() let mut inner = if_statement_pair.into_inner();
inner.next().unwrap(); // if
let condition = expect_and_use(
file_id,
inner.next().unwrap(),
Rule::Expression,
build_expression,
);
let then_block = expect_and_use(
file_id,
inner.next().unwrap(),
Rule::BlockStatement,
build_block_statement,
);
IfStatement {
condition,
then_block,
}
} }
fn build_if_else_statement(file_id: usize, if_else_statement_pair: Pair<Rule>) -> IfElseStatement { fn build_if_else_statement(file_id: usize, if_else_statement_pair: Pair<Rule>) -> IfElseStatement {
todo!() let mut if_statement = None;
let mut else_ifs = ElseIfs::default();
let mut else_block = None;
for inner_pair in if_else_statement_pair.into_inner() {
match inner_pair.as_rule() {
Rule::IfStatement => {
if_statement = Some(build_if_statement(file_id, inner_pair));
}
Rule::ElseIf => {
let mut else_if_inner = inner_pair.into_inner();
else_if_inner.next().unwrap(); // else
else_ifs.0.push(expect_and_use(
file_id,
else_if_inner.next().unwrap(),
Rule::IfStatement,
build_if_statement,
));
}
Rule::ElseBlock => {
let mut else_inner = inner_pair.into_inner();
else_inner.next().unwrap(); // else
else_block = Some(ElseBlock(expect_and_use(
file_id,
else_inner.next().unwrap(),
Rule::BlockStatement,
build_block_statement,
)));
}
_ => unreachable!(),
}
}
IfElseStatement {
if_statement: if_statement.unwrap(),
else_ifs,
else_block,
}
} }
fn build_while_statement(file_id: usize, while_statement_pair: Pair<Rule>) -> WhileStatement { fn build_while_statement(file_id: usize, while_statement_pair: Pair<Rule>) -> WhileStatement {
@ -1220,31 +1286,27 @@ fn build_closure(file_id: usize, closure_pair: Pair<Rule>) -> Closure {
let mut parameters = ClosureParameters::default(); let mut parameters = ClosureParameters::default();
let mut statements = vec![]; let mut statements = vec![];
let mut expression = None; let mut expression = None;
for inner_pair in closure_pair.into_inner() { for inner_pair in closure_pair.into_inner() {
match inner_pair.as_rule() { match inner_pair.as_rule() {
Rule::Cons => { Rule::Cons => modifier = Some(ClosureModifier::Cons),
modifier = Some(ClosureModifier::Cons) Rule::Mut => modifier = Some(ClosureModifier::Mut),
},
Rule::Mut => {
modifier = Some(ClosureModifier::Mut)
},
Rule::ClosureCaptures => { Rule::ClosureCaptures => {
captures = build_closure_captures(file_id, inner_pair); captures = build_closure_captures(file_id, inner_pair);
}, }
Rule::ClosureParameters => { Rule::ClosureParameters => {
parameters = build_closure_parameters(file_id, inner_pair); parameters = build_closure_parameters(file_id, inner_pair);
}, }
Rule::Statement => { Rule::Statement => {
statements.push(build_statement(file_id, inner_pair)); statements.push(build_statement(file_id, inner_pair));
}, }
Rule::Expression => { Rule::Expression => {
expression = Some(Box::new(build_expression(file_id, inner_pair))); expression = Some(Box::new(build_expression(file_id, inner_pair)));
} }
_ => unreachable!(), _ => unreachable!(),
} }
} }
Closure { Closure {
modifier, modifier,
is_move, is_move,
@ -1256,55 +1318,85 @@ fn build_closure(file_id: usize, closure_pair: Pair<Rule>) -> Closure {
} }
fn build_closure_captures(file_id: usize, captures_pair: Pair<Rule>) -> ClosureCaptures { fn build_closure_captures(file_id: usize, captures_pair: Pair<Rule>) -> ClosureCaptures {
ClosureCaptures(captures_pair.into_inner().map(|capture_pair| { ClosureCaptures(
expect_and_use(file_id, capture_pair, Rule::ClosureCapture, build_closure_capture) captures_pair
}).collect()) .into_inner()
.map(|capture_pair| {
expect_and_use(
file_id,
capture_pair,
Rule::ClosureCapture,
build_closure_capture,
)
})
.collect(),
)
} }
fn build_closure_capture(file_id: usize, capture_pair: Pair<Rule>) -> ClosureCapture { fn build_closure_capture(file_id: usize, capture_pair: Pair<Rule>) -> ClosureCapture {
let mut borrow_count = 0; let mut borrow_count = 0;
let mut is_mutable = false; let mut is_mutable = false;
let mut identifier = None; let mut identifier = None;
for inner_pair in capture_pair.into_inner() { for inner_pair in capture_pair.into_inner() {
match inner_pair.as_rule() { match inner_pair.as_rule() {
Rule::Borrow => { Rule::Borrow => {
borrow_count += 1; borrow_count += 1;
}, }
Rule::Mut => { Rule::Mut => {
is_mutable = true; is_mutable = true;
}, }
Rule::Identifier => { Rule::Identifier => {
identifier = Some(Box::new(build_identifier(file_id, inner_pair))); identifier = Some(Box::new(build_identifier(file_id, inner_pair)));
} }
_ => unreachable!(), _ => unreachable!(),
} }
} }
ClosureCapture { ClosureCapture {
borrow_count, borrow_count,
is_mutable, is_mutable,
identifier: identifier.unwrap() identifier: identifier.unwrap(),
} }
} }
fn build_closure_parameters(file_id: usize, parameters_pair: Pair<Rule>) -> ClosureParameters { fn build_closure_parameters(file_id: usize, parameters_pair: Pair<Rule>) -> ClosureParameters {
ClosureParameters(parameters_pair.into_inner().map(|parameter_pair| { ClosureParameters(
expect_and_use(file_id, parameter_pair, Rule::ClosureParameter, build_closure_parameter) parameters_pair
}).collect()) .into_inner()
.map(|parameter_pair| {
expect_and_use(
file_id,
parameter_pair,
Rule::ClosureParameter,
build_closure_parameter,
)
})
.collect(),
)
} }
fn build_closure_parameter(file_id: usize, parameter_pair: Pair<Rule>) -> ClosureParameter { fn build_closure_parameter(file_id: usize, parameter_pair: Pair<Rule>) -> ClosureParameter {
let mut inner = parameter_pair.into_inner(); let mut inner = parameter_pair.into_inner();
let identifier = expect_and_use(file_id, inner.next().unwrap(), Rule::Identifier, build_identifier); let identifier = expect_and_use(
file_id,
inner.next().unwrap(),
Rule::Identifier,
build_identifier,
);
let type_use = if let Some(type_use_pair) = inner.next() { let type_use = if let Some(type_use_pair) = inner.next() {
Some(expect_and_use(file_id, type_use_pair, Rule::TypeUse, build_type_use)) Some(expect_and_use(
file_id,
type_use_pair,
Rule::TypeUse,
build_type_use,
))
} else { } else {
None None
}; };
ClosureParameter { ClosureParameter {
identifier, identifier,
type_use type_use,
} }
} }
@ -1368,12 +1460,7 @@ fn build_single_quote_string(file_id: usize, pair: Pair<Rule>) -> Literal {
fn build_double_quote_string(file_id: usize, pair: Pair<Rule>) -> Literal { fn build_double_quote_string(file_id: usize, pair: Pair<Rule>) -> Literal {
let mut parts: Vec<DStringPart> = vec![]; let mut parts: Vec<DStringPart> = vec![];
handle_d_backtick_strings_inner( handle_d_backtick_strings_inner(Rule::DStringInner, file_id, pair, &mut parts);
Rule::DStringInner,
file_id,
pair,
&mut parts,
);
if parts.len() == 1 && parts[0].is_string() { if parts.len() == 1 && parts[0].is_string() {
Literal::String(parts.pop().unwrap().unwrap_string()) Literal::String(parts.pop().unwrap().unwrap_string())
} else { } else {
@ -1383,12 +1470,7 @@ fn build_double_quote_string(file_id: usize, pair: Pair<Rule>) -> Literal {
fn build_backtick_string(file_id: usize, pair: Pair<Rule>) -> Literal { fn build_backtick_string(file_id: usize, pair: Pair<Rule>) -> Literal {
let mut parts: Vec<DStringPart> = vec![]; let mut parts: Vec<DStringPart> = vec![];
handle_d_backtick_strings_inner( handle_d_backtick_strings_inner(Rule::BacktickInner, file_id, pair, &mut parts);
Rule::BacktickInner,
file_id,
pair,
&mut parts,
);
if parts.len() == 1 && parts[0].is_string() { if parts.len() == 1 && parts[0].is_string() {
Literal::String(parts.pop().unwrap().unwrap_string()) Literal::String(parts.pop().unwrap().unwrap_string())
} else { } else {
@ -1484,9 +1566,61 @@ mod tests {
fn d_string_literal_and_expression() { fn d_string_literal_and_expression() {
assert_builds("fn main() { \"Hello, ${foo}\" }"); assert_builds("fn main() { \"Hello, ${foo}\" }");
} }
#[test] #[test]
fn d_string_literal_expression_literal() { fn d_string_literal_expression_literal() {
assert_builds("fn main() { \"Hello, ${foo}! It is a nice day.\" }"); assert_builds("fn main() { \"Hello, ${foo}! It is a nice day.\" }");
} }
#[test]
fn if_statement() {
assert_builds(indoc! {"
fn main() {
if true {
foo
}
}
"})
}
#[test]
fn if_else_statement() {
assert_builds(indoc! {"
fn main() {
if true {
foo
} else {
bar
}
}
"})
}
#[test]
fn if_else_if_statement() {
assert_builds(indoc! {"
fn main() {
if true {
foo
} else if false {
bar
}
}
"})
}
#[test]
fn if_else_if_else_statement() {
assert_builds(indoc! {"
fn main() {
if true {
foo
} else if false {
bar
} else {
buzz
}
}
"})
}
} }

View File

@ -495,15 +495,17 @@ pub struct IfStatement {
#[derive(Debug)] #[derive(Debug)]
pub struct IfElseStatement { pub struct IfElseStatement {
pub condition: Expression, pub if_statement: IfStatement,
pub then_block: BlockStatement,
pub else_ifs: ElseIfs, pub else_ifs: ElseIfs,
pub else_block: BlockStatement, pub else_block: Option<ElseBlock>,
} }
#[derive(Debug)] #[derive(Debug, Default)]
pub struct ElseIfs(pub Vec<IfStatement>); pub struct ElseIfs(pub Vec<IfStatement>);
#[derive(Debug)]
pub struct ElseBlock(pub BlockStatement);
#[derive(Debug)] #[derive(Debug)]
pub struct WhileStatement { pub struct WhileStatement {
pub condition: Expression, pub condition: Expression,
@ -587,7 +589,7 @@ pub struct Closure {
#[derive(Debug)] #[derive(Debug)]
pub enum ClosureModifier { pub enum ClosureModifier {
Cons, Cons,
Mut Mut,
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]

View File

@ -663,6 +663,7 @@ impl PrettyPrint for IfStatement {
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
writer.writeln_indented("IfStatement")?; writer.writeln_indented("IfStatement")?;
writer.increase_indent(); writer.increase_indent();
writer.increase_indent();
self.condition.pretty_print(writer)?; self.condition.pretty_print(writer)?;
self.then_block.pretty_print(writer)?; self.then_block.pretty_print(writer)?;
writer.decrease_indent(); writer.decrease_indent();
@ -674,13 +675,33 @@ impl PrettyPrint for IfElseStatement {
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
writer.writeln_indented("IfElseStatement")?; writer.writeln_indented("IfElseStatement")?;
writer.increase_indent(); writer.increase_indent();
self.condition.pretty_print(writer)?; self.if_statement.pretty_print(writer)?;
self.then_block.pretty_print(writer)?; self.else_ifs.pretty_print(writer)?;
for else_if in &self.else_ifs.0 { if let Some(else_block) = &self.else_block {
else_if.condition.pretty_print(writer)?; else_block.pretty_print(writer)?;
else_if.then_block.pretty_print(writer)?;
} }
self.else_block.pretty_print(writer)?; writer.decrease_indent();
Ok(())
}
}
impl PrettyPrint for ElseIfs {
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
writer.writeln_indented("ElseIfs")?;
writer.increase_indent();
for if_statement in &self.0 {
if_statement.pretty_print(writer)?;
}
writer.decrease_indent();
Ok(())
}
}
impl PrettyPrint for ElseBlock {
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
writer.writeln_indented("ElseBlock")?;
writer.increase_indent();
self.0.pretty_print(writer)?;
writer.decrease_indent(); writer.decrease_indent();
Ok(()) Ok(())
} }
@ -845,10 +866,13 @@ impl PrettyPrint for Closure {
impl PrettyPrint for ClosureModifier { impl PrettyPrint for ClosureModifier {
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
writer.writeln_indented(&format!("ClosureModifier({})", match self { writer.writeln_indented(&format!(
ClosureModifier::Mut => "mut", "ClosureModifier({})",
ClosureModifier::Cons => "cons" match self {
})) ClosureModifier::Mut => "mut",
ClosureModifier::Cons => "cons",
}
))
} }
} }

View File

@ -835,15 +835,11 @@ impl Unparse for IfStatement {
impl Unparse for IfElseStatement { impl Unparse for IfElseStatement {
fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
writer.write_indented("if ")?; self.if_statement.unparse(writer)?;
self.condition.unparse(writer)?;
writer.writeln(" {")?;
self.then_block.unparse_inner(writer)?;
writer.write_indented("} ")?;
self.else_ifs.unparse(writer)?; self.else_ifs.unparse(writer)?;
writer.writeln("else {")?; if let Some(else_block) = &self.else_block {
self.else_block.unparse_inner(writer)?; else_block.unparse(writer)?;
writer.writeln_indented("}")?; }
Ok(()) Ok(())
} }
} }
@ -851,16 +847,24 @@ impl Unparse for IfElseStatement {
impl Unparse for ElseIfs { impl Unparse for ElseIfs {
fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
for if_statement in &self.0 { for if_statement in &self.0 {
writer.write(" else if ")?; writer.write_indented("else if ")?;
if_statement.condition.unparse(writer)?; if_statement.condition.unparse(writer)?;
writer.writeln(" {")?; writer.writeln(" {")?;
if_statement.then_block.unparse_inner(writer)?; if_statement.then_block.unparse_inner(writer)?;
writer.write_indented("} ")?; writer.writeln_indented("}")?;
} }
Ok(()) Ok(())
} }
} }
impl Unparse for ElseBlock {
fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
writer.write_indented("else ")?;
self.0.unparse(writer)?;
Ok(())
}
}
impl Unparse for WhileStatement { impl Unparse for WhileStatement {
fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
writer.write_indented("while ")?; writer.write_indented("while ")?;

View File

@ -488,17 +488,19 @@ BlockStatement = {
Statement = { Statement = {
( (
BlockStatement VariableDeclaration
| VariableDeclaration
| AssignmentStatement | AssignmentStatement
| CallStatement | CallStatement
| ReturnStatement | ReturnStatement
| IfStatement )
~ Semicolon
| (
BlockStatement
| IfElseStatement | IfElseStatement
| IfStatement
| WhileStatement | WhileStatement
| ForStatement | ForStatement
)? )
~ Semicolon
} }
VariableDeclaration = { VariableDeclaration = {
@ -539,11 +541,18 @@ IfStatement = {
} }
IfElseStatement = { IfElseStatement = {
If IfStatement
~ Expression ~ ElseIf*
~ BlockStatement ~ ElseBlock?
~ ( Else ~ IfStatement )* }
~ Else
ElseIf = {
Else
~ IfStatement
}
ElseBlock = {
Else
~ BlockStatement ~ BlockStatement
} }

View File

@ -34,6 +34,12 @@ mod deimos_parser_tests {
panic!("Parsing failed.\n{}", e); panic!("Parsing failed.\n{}", e);
} else { } else {
let mut pairs = parse_result.unwrap(); let mut pairs = parse_result.unwrap();
if input != pairs.as_str() {
panic!(
"Parsing did not consume entire input. Consumed only:\n{}",
pairs.as_str()
);
}
let first = pairs.next().unwrap(); let first = pairs.next().unwrap();
match_rule!(first; rule) match_rule!(first; rule)
} }
@ -89,4 +95,56 @@ mod deimos_parser_tests {
fn chained_index_call_on_identifier() { fn chained_index_call_on_identifier() {
parses_to(Rule::SuffixExpression, "foo[0]()"); 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()
}"},
)
}
} }