Skip to content

Commit 0bc27ca

Browse files
committed
(parser) add: control flow implementation
1 parent f071efd commit 0bc27ca

File tree

9 files changed

+276
-194
lines changed

9 files changed

+276
-194
lines changed

src/parser/keyword/attributes.rs

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use super::super::tree::literal::{Literal, Variable};
88
use super::super::tree::unary::Unary;
99
use super::super::tree::{FunctionCall, ListInitialiser, Ternary};
1010
use super::Ast;
11-
use super::controlflow::ControlFlow as _;
1211
use super::types::PushInNode;
1312
use crate::lexer::api::Keyword;
1413

@@ -84,8 +83,9 @@ impl PushInNode for AttributeKeyword {
8483
}
8584
| Ternary { success: arg, .. },
8685
) => return self.push_in_node(arg),
87-
Ast::ListInitialiser(ListInitialiser { full: true, .. })
88-
| Ast::FunctionCall(FunctionCall { full: true, .. }) => {
86+
Ast::ControlFlow(_)
87+
| Ast::FunctionCall(FunctionCall { full: true, .. })
88+
| Ast::ListInitialiser(ListInitialiser { full: true, .. }) => {
8989
return Err(format!(
9090
"Attribute {self} can only be placed before variables, but found {node}"
9191
));
@@ -96,15 +96,6 @@ impl PushInNode for AttributeKeyword {
9696
Some(last) => return self.push_in_node(last),
9797
None => elts.push(self.into_node()),
9898
},
99-
Ast::ControlFlow(ctrl) => {
100-
return if let Some(last) = ctrl.last_mut() {
101-
self.push_in_node(last)
102-
} else {
103-
Err(format!(
104-
"Attribute {self} can only be placed before variables, but found {node}"
105-
))
106-
};
107-
}
10899
}
109100
Ok(())
110101
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use core::fmt;
2+
3+
use super::node::ControlFlowNode;
4+
use crate::parser::tree::ast::Ast;
5+
6+
#[derive(Debug, PartialEq, Eq)]
7+
pub enum ControlFlowKeyword {
8+
// cases & loops
9+
Break,
10+
Case,
11+
Continue,
12+
Default,
13+
Do,
14+
Else,
15+
Enum,
16+
For,
17+
Goto,
18+
If,
19+
Return,
20+
Struct,
21+
Switch,
22+
Typedef,
23+
Union,
24+
While,
25+
}
26+
27+
#[expect(clippy::min_ident_chars)]
28+
impl fmt::Display for ControlFlowKeyword {
29+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30+
match self {
31+
Self::Break => "break".fmt(f),
32+
Self::Case => "case".fmt(f),
33+
Self::Continue => "continue".fmt(f),
34+
Self::Default => "default".fmt(f),
35+
Self::Do => "do".fmt(f),
36+
Self::Else => "else".fmt(f),
37+
Self::Enum => "enum".fmt(f),
38+
Self::For => "for".fmt(f),
39+
Self::Goto => "goto".fmt(f),
40+
Self::If => "if".fmt(f),
41+
Self::Return => "return".fmt(f),
42+
Self::Struct => "struct".fmt(f),
43+
Self::Switch => "switch".fmt(f),
44+
Self::Typedef => "typedef".fmt(f),
45+
Self::Union => "union".fmt(f),
46+
Self::While => "while".fmt(f),
47+
}
48+
}
49+
}
50+
51+
impl From<ControlFlowKeyword> for ControlFlowNode {
52+
fn from(keyword: ControlFlowKeyword) -> Self {
53+
match keyword {
54+
ControlFlowKeyword::Break
55+
| ControlFlowKeyword::Return
56+
| ControlFlowKeyword::Continue => Self::SemiColon(keyword),
57+
ControlFlowKeyword::Case | ControlFlowKeyword::Default | ControlFlowKeyword::Goto => {
58+
Self::ColonAst(keyword, None)
59+
}
60+
ControlFlowKeyword::For
61+
| ControlFlowKeyword::While
62+
| ControlFlowKeyword::Switch
63+
| ControlFlowKeyword::If => Self::ParensBlock(keyword, None, None),
64+
// block
65+
ControlFlowKeyword::Do | ControlFlowKeyword::Else => {
66+
Self::Ast(keyword, Box::from(Ast::Empty))
67+
}
68+
// special
69+
ControlFlowKeyword::Typedef => Self::ControlFlow(keyword, None),
70+
71+
ControlFlowKeyword::Enum | ControlFlowKeyword::Union | ControlFlowKeyword::Struct => {
72+
Self::IdentBlock(keyword, None, None)
73+
}
74+
}
75+
}
76+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
pub mod keyword;
2+
pub mod node;
3+
4+
use keyword::ControlFlowKeyword;
5+
use node::ControlFlowNode;
6+
7+
use super::Ast;
8+
use super::types::PushInNode;
9+
use crate::parser::tree::blocks::Block;
10+
11+
impl PushInNode for ControlFlowKeyword {
12+
fn push_in_node(self, node: &mut Ast) -> Result<(), String> {
13+
let block = Ast::Block(Block {
14+
elts: vec![Ast::ControlFlow(ControlFlowNode::from(self))],
15+
full: true,
16+
});
17+
node.push_braced_block(block);
18+
Ok(())
19+
}
20+
}
21+
22+
pub fn is_node_case_context(node: &Ast) -> bool {
23+
match node {
24+
//
25+
//
26+
// true
27+
Ast::ControlFlow(ctrl) if *ctrl.get_keyword() == ControlFlowKeyword::Case && !ctrl.is_full() => true,
28+
//
29+
//
30+
// false
31+
// empty
32+
Ast::Empty
33+
| Ast::Leaf(_)
34+
| Ast::ParensBlock(_)
35+
// control flows are not expressions
36+
| Ast::Unary(_)
37+
| Ast::Binary(_)
38+
| Ast::Ternary(_)
39+
| Ast::FunctionCall(_)
40+
| Ast::ListInitialiser(_) |
41+
Ast::ControlFlow(_)
42+
// content is full
43+
| Ast::Block(Block { full: true, .. }) => false,
44+
//
45+
//
46+
// recurse
47+
Ast::Block(Block { elts, full: false }) => elts.last().is_some_and(is_node_case_context),
48+
}
49+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use super::keyword::ControlFlowKeyword;
2+
use crate::parser::tree::ast::{Ast, ParensBlock};
3+
use crate::parser::tree::blocks::Block;
4+
5+
#[derive(Debug, PartialEq)]
6+
pub enum ControlFlowNode {
7+
Ast(ControlFlowKeyword, Box<Ast>),
8+
ColonAst(ControlFlowKeyword, Option<Box<Ast>>),
9+
ControlFlow(ControlFlowKeyword, Option<Box<ControlFlowNode>>),
10+
IdentBlock(ControlFlowKeyword, Option<String>, Option<Block>),
11+
ParensBlock(ControlFlowKeyword, Option<ParensBlock>, Option<Block>),
12+
SemiColon(ControlFlowKeyword),
13+
}
14+
15+
impl ControlFlowNode {
16+
pub const fn get_keyword(&self) -> &ControlFlowKeyword {
17+
match self {
18+
Self::Ast(control_flow_keyword, _)
19+
| Self::ColonAst(control_flow_keyword, _)
20+
| Self::ControlFlow(control_flow_keyword, _)
21+
| Self::IdentBlock(control_flow_keyword, _, _)
22+
| Self::ParensBlock(control_flow_keyword, _, _)
23+
| Self::SemiColon(control_flow_keyword) => control_flow_keyword,
24+
}
25+
}
26+
27+
pub fn is_full(&self) -> bool {
28+
match self {
29+
Self::Ast(_, ast) => **ast != Ast::Empty,
30+
Self::ColonAst(_, ast) => ast.as_ref().is_some_and(|node| **node != Ast::Empty),
31+
Self::ControlFlow(_, control_flow_node) => control_flow_node
32+
.as_ref()
33+
.is_some_and(|node| node.is_full()),
34+
Self::IdentBlock(_, ident, block) => ident.is_some() && block.is_some(),
35+
Self::ParensBlock(_, parens_block, block) => parens_block.is_some() && block.is_some(),
36+
Self::SemiColon(_) => true,
37+
}
38+
}
39+
40+
pub fn push_block_as_leaf(&mut self, node: Ast) -> Result<(), String> {
41+
#[expect(clippy::wildcard_enum_match_arm)]
42+
match self {
43+
Self::Ast(_, ast) | Self::ColonAst(_, Some(ast)) if **ast == Ast::Empty => {
44+
*ast = Box::new(node);
45+
}
46+
Self::ColonAst(_, None) => return Err("Missing colon after keyword.".to_owned()),
47+
Self::ControlFlow(keyword, old_ctrl @ None) => {
48+
if let Ast::ControlFlow(node_ctrl) = node {
49+
*old_ctrl = Some(Box::from(node_ctrl));
50+
} else {
51+
return Err(format!("{keyword} expected a keyword but found {node}",));
52+
}
53+
}
54+
Self::ParensBlock(keyword, old_parens @ None, _) => {
55+
if let Ast::ParensBlock(node_parens) = node {
56+
*old_parens = Some(node_parens);
57+
} else {
58+
return Err(format!(
59+
"{keyword} expected a parenthesised block but found {node}",
60+
));
61+
}
62+
}
63+
Self::ParensBlock(keyword, _, old_block @ None)
64+
| Self::IdentBlock(keyword, _, old_block @ None) => {
65+
if let Ast::Block(node_block) = node {
66+
*old_block = Some(node_block);
67+
} else {
68+
return Err(format!(
69+
"{keyword} expected a braced block but found {node}",
70+
));
71+
}
72+
}
73+
_ => panic!("Tried to push not on full block, but it is not pushable"),
74+
}
75+
Ok(())
76+
}
77+
78+
pub fn push_colon(&mut self) -> Result<(), String> {
79+
if let Self::ColonAst(_, node @ None) = self {
80+
*node = Some(Box::from(Ast::Empty));
81+
Ok(())
82+
} else {
83+
Err("Found extra colon: illegal in control flow keyword context.".to_owned())
84+
}
85+
}
86+
}

0 commit comments

Comments
 (0)