Implement if/if-else statements in grammar, build, unparse, and pretty-print.
This commit is contained in:
parent
35d616a538
commit
bf06407d16
9
sketching/may_2025/if.dm
Normal file
9
sketching/may_2025/if.dm
Normal 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');
|
||||
}
|
||||
}
|
206
src/ast/build.rs
206
src/ast/build.rs
@ -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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
@ -1223,21 +1289,17 @@ fn build_closure(file_id: usize, closure_pair: Pair<Rule>) -> Closure {
|
||||
|
||||
for inner_pair in closure_pair.into_inner() {
|
||||
match inner_pair.as_rule() {
|
||||
Rule::Cons => {
|
||||
modifier = Some(ClosureModifier::Cons)
|
||||
},
|
||||
Rule::Mut => {
|
||||
modifier = Some(ClosureModifier::Mut)
|
||||
},
|
||||
Rule::Cons => modifier = Some(ClosureModifier::Cons),
|
||||
Rule::Mut => modifier = Some(ClosureModifier::Mut),
|
||||
Rule::ClosureCaptures => {
|
||||
captures = build_closure_captures(file_id, inner_pair);
|
||||
},
|
||||
}
|
||||
Rule::ClosureParameters => {
|
||||
parameters = build_closure_parameters(file_id, inner_pair);
|
||||
},
|
||||
}
|
||||
Rule::Statement => {
|
||||
statements.push(build_statement(file_id, inner_pair));
|
||||
},
|
||||
}
|
||||
Rule::Expression => {
|
||||
expression = Some(Box::new(build_expression(file_id, inner_pair)));
|
||||
}
|
||||
@ -1256,9 +1318,19 @@ fn build_closure(file_id: usize, closure_pair: Pair<Rule>) -> Closure {
|
||||
}
|
||||
|
||||
fn build_closure_captures(file_id: usize, captures_pair: Pair<Rule>) -> ClosureCaptures {
|
||||
ClosureCaptures(captures_pair.into_inner().map(|capture_pair| {
|
||||
expect_and_use(file_id, capture_pair, Rule::ClosureCapture, build_closure_capture)
|
||||
}).collect())
|
||||
ClosureCaptures(
|
||||
captures_pair
|
||||
.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 {
|
||||
@ -1270,10 +1342,10 @@ fn build_closure_capture(file_id: usize, capture_pair: Pair<Rule>) -> ClosureCap
|
||||
match inner_pair.as_rule() {
|
||||
Rule::Borrow => {
|
||||
borrow_count += 1;
|
||||
},
|
||||
}
|
||||
Rule::Mut => {
|
||||
is_mutable = true;
|
||||
},
|
||||
}
|
||||
Rule::Identifier => {
|
||||
identifier = Some(Box::new(build_identifier(file_id, inner_pair)));
|
||||
}
|
||||
@ -1284,27 +1356,47 @@ fn build_closure_capture(file_id: usize, capture_pair: Pair<Rule>) -> ClosureCap
|
||||
ClosureCapture {
|
||||
borrow_count,
|
||||
is_mutable,
|
||||
identifier: identifier.unwrap()
|
||||
identifier: identifier.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_closure_parameters(file_id: usize, parameters_pair: Pair<Rule>) -> ClosureParameters {
|
||||
ClosureParameters(parameters_pair.into_inner().map(|parameter_pair| {
|
||||
expect_and_use(file_id, parameter_pair, Rule::ClosureParameter, build_closure_parameter)
|
||||
}).collect())
|
||||
ClosureParameters(
|
||||
parameters_pair
|
||||
.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 {
|
||||
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() {
|
||||
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 {
|
||||
None
|
||||
};
|
||||
ClosureParameter {
|
||||
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 {
|
||||
let mut parts: Vec<DStringPart> = vec![];
|
||||
handle_d_backtick_strings_inner(
|
||||
Rule::DStringInner,
|
||||
file_id,
|
||||
pair,
|
||||
&mut parts,
|
||||
);
|
||||
handle_d_backtick_strings_inner(Rule::DStringInner, file_id, pair, &mut parts);
|
||||
if parts.len() == 1 && parts[0].is_string() {
|
||||
Literal::String(parts.pop().unwrap().unwrap_string())
|
||||
} 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 {
|
||||
let mut parts: Vec<DStringPart> = vec![];
|
||||
handle_d_backtick_strings_inner(
|
||||
Rule::BacktickInner,
|
||||
file_id,
|
||||
pair,
|
||||
&mut parts,
|
||||
);
|
||||
handle_d_backtick_strings_inner(Rule::BacktickInner, file_id, pair, &mut parts);
|
||||
if parts.len() == 1 && parts[0].is_string() {
|
||||
Literal::String(parts.pop().unwrap().unwrap_string())
|
||||
} else {
|
||||
@ -1489,4 +1571,56 @@ mod tests {
|
||||
fn d_string_literal_expression_literal() {
|
||||
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
|
||||
}
|
||||
}
|
||||
"})
|
||||
}
|
||||
}
|
||||
|
@ -495,15 +495,17 @@ pub struct IfStatement {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IfElseStatement {
|
||||
pub condition: Expression,
|
||||
pub then_block: BlockStatement,
|
||||
pub if_statement: IfStatement,
|
||||
pub else_ifs: ElseIfs,
|
||||
pub else_block: BlockStatement,
|
||||
pub else_block: Option<ElseBlock>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ElseIfs(pub Vec<IfStatement>);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ElseBlock(pub BlockStatement);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WhileStatement {
|
||||
pub condition: Expression,
|
||||
@ -587,7 +589,7 @@ pub struct Closure {
|
||||
#[derive(Debug)]
|
||||
pub enum ClosureModifier {
|
||||
Cons,
|
||||
Mut
|
||||
Mut,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
@ -663,6 +663,7 @@ impl PrettyPrint for IfStatement {
|
||||
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
|
||||
writer.writeln_indented("IfStatement")?;
|
||||
writer.increase_indent();
|
||||
writer.increase_indent();
|
||||
self.condition.pretty_print(writer)?;
|
||||
self.then_block.pretty_print(writer)?;
|
||||
writer.decrease_indent();
|
||||
@ -674,13 +675,33 @@ impl PrettyPrint for IfElseStatement {
|
||||
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
|
||||
writer.writeln_indented("IfElseStatement")?;
|
||||
writer.increase_indent();
|
||||
self.condition.pretty_print(writer)?;
|
||||
self.then_block.pretty_print(writer)?;
|
||||
for else_if in &self.else_ifs.0 {
|
||||
else_if.condition.pretty_print(writer)?;
|
||||
else_if.then_block.pretty_print(writer)?;
|
||||
self.if_statement.pretty_print(writer)?;
|
||||
self.else_ifs.pretty_print(writer)?;
|
||||
if let Some(else_block) = &self.else_block {
|
||||
else_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();
|
||||
Ok(())
|
||||
}
|
||||
@ -845,10 +866,13 @@ impl PrettyPrint for Closure {
|
||||
|
||||
impl PrettyPrint for ClosureModifier {
|
||||
fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
|
||||
writer.writeln_indented(&format!("ClosureModifier({})", match self {
|
||||
ClosureModifier::Mut => "mut",
|
||||
ClosureModifier::Cons => "cons"
|
||||
}))
|
||||
writer.writeln_indented(&format!(
|
||||
"ClosureModifier({})",
|
||||
match self {
|
||||
ClosureModifier::Mut => "mut",
|
||||
ClosureModifier::Cons => "cons",
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -835,15 +835,11 @@ impl Unparse for IfStatement {
|
||||
|
||||
impl Unparse for IfElseStatement {
|
||||
fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
|
||||
writer.write_indented("if ")?;
|
||||
self.condition.unparse(writer)?;
|
||||
writer.writeln(" {")?;
|
||||
self.then_block.unparse_inner(writer)?;
|
||||
writer.write_indented("} ")?;
|
||||
self.if_statement.unparse(writer)?;
|
||||
self.else_ifs.unparse(writer)?;
|
||||
writer.writeln("else {")?;
|
||||
self.else_block.unparse_inner(writer)?;
|
||||
writer.writeln_indented("}")?;
|
||||
if let Some(else_block) = &self.else_block {
|
||||
else_block.unparse(writer)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -851,16 +847,24 @@ 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(" else if ")?;
|
||||
writer.write_indented("else if ")?;
|
||||
if_statement.condition.unparse(writer)?;
|
||||
writer.writeln(" {")?;
|
||||
if_statement.then_block.unparse_inner(writer)?;
|
||||
writer.write_indented("} ")?;
|
||||
writer.writeln_indented("}")?;
|
||||
}
|
||||
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 {
|
||||
fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> {
|
||||
writer.write_indented("while ")?;
|
||||
|
@ -488,17 +488,19 @@ BlockStatement = {
|
||||
|
||||
Statement = {
|
||||
(
|
||||
BlockStatement
|
||||
| VariableDeclaration
|
||||
VariableDeclaration
|
||||
| AssignmentStatement
|
||||
| CallStatement
|
||||
| ReturnStatement
|
||||
| IfStatement
|
||||
)
|
||||
~ Semicolon
|
||||
| (
|
||||
BlockStatement
|
||||
| IfElseStatement
|
||||
| IfStatement
|
||||
| WhileStatement
|
||||
| ForStatement
|
||||
)?
|
||||
~ Semicolon
|
||||
)
|
||||
}
|
||||
|
||||
VariableDeclaration = {
|
||||
@ -539,11 +541,18 @@ IfStatement = {
|
||||
}
|
||||
|
||||
IfElseStatement = {
|
||||
If
|
||||
~ Expression
|
||||
~ BlockStatement
|
||||
~ ( Else ~ IfStatement )*
|
||||
~ Else
|
||||
IfStatement
|
||||
~ ElseIf*
|
||||
~ ElseBlock?
|
||||
}
|
||||
|
||||
ElseIf = {
|
||||
Else
|
||||
~ IfStatement
|
||||
}
|
||||
|
||||
ElseBlock = {
|
||||
Else
|
||||
~ BlockStatement
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,12 @@ mod deimos_parser_tests {
|
||||
panic!("Parsing failed.\n{}", e);
|
||||
} else {
|
||||
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();
|
||||
match_rule!(first; rule)
|
||||
}
|
||||
@ -89,4 +95,56 @@ mod deimos_parser_tests {
|
||||
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()
|
||||
}"},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user