/home/noah/src/ruchy/src/frontend/parser/actors.rs
Line | Count | Source |
1 | | //! Actor system parsing |
2 | | |
3 | | use super::{ParserState, *}; |
4 | | use crate::frontend::ast::{ActorHandler, StructField}; |
5 | | |
6 | | /// # Errors |
7 | | /// |
8 | | /// Returns an error if the operation fails |
9 | | /// # Errors |
10 | | /// |
11 | | /// Returns an error if the operation fails |
12 | 0 | pub fn parse_actor(state: &mut ParserState) -> Result<Expr> { |
13 | 0 | let start_span = state.tokens.advance().expect("checked by parser logic").1; // consume actor |
14 | | |
15 | | // Parse actor name |
16 | 0 | let name = if let Some((Token::Identifier(n), _)) = state.tokens.peek() { |
17 | 0 | let name = n.clone(); |
18 | 0 | state.tokens.advance(); |
19 | 0 | name |
20 | | } else { |
21 | 0 | bail!("Expected actor name"); |
22 | | }; |
23 | | |
24 | 0 | state.tokens.expect(&Token::LeftBrace)?; |
25 | | |
26 | 0 | let mut state_fields = Vec::new(); |
27 | 0 | let mut handlers = Vec::new(); |
28 | | |
29 | | // Parse actor body (state fields and receive block) |
30 | 0 | while !matches!(state.tokens.peek(), Some((Token::RightBrace, _))) { |
31 | 0 | if matches!(state.tokens.peek(), Some((Token::State, _))) { |
32 | | // Parse state block |
33 | 0 | state.tokens.advance(); // consume 'state' |
34 | 0 | state.tokens.expect(&Token::LeftBrace)?; |
35 | | |
36 | | // Parse state fields |
37 | 0 | while !matches!(state.tokens.peek(), Some((Token::RightBrace, _))) { |
38 | 0 | let field_name = if let Some((Token::Identifier(name), _)) = state.tokens.peek() { |
39 | 0 | let name = name.clone(); |
40 | 0 | state.tokens.advance(); |
41 | 0 | name |
42 | | } else { |
43 | 0 | bail!("Expected field name in state block"); |
44 | | }; |
45 | | |
46 | 0 | state.tokens.expect(&Token::Colon)?; |
47 | 0 | let ty = utils::parse_type(state)?; |
48 | | |
49 | 0 | state_fields.push(StructField { |
50 | 0 | name: field_name, |
51 | 0 | ty, |
52 | 0 | is_pub: false, |
53 | 0 | }); |
54 | | |
55 | | // Optional comma or semicolon |
56 | 0 | if matches!( |
57 | 0 | state.tokens.peek(), |
58 | | Some((Token::Comma | Token::Semicolon, _)) |
59 | 0 | ) { |
60 | 0 | state.tokens.advance(); |
61 | 0 | } |
62 | | } |
63 | | |
64 | 0 | state.tokens.expect(&Token::RightBrace)?; // Close state block |
65 | 0 | } else if matches!(state.tokens.peek(), Some((Token::Receive, _))) { |
66 | 0 | state.tokens.advance(); // consume 'receive' |
67 | | |
68 | | // Check if it's a receive block or individual handler |
69 | 0 | if matches!(state.tokens.peek(), Some((Token::LeftBrace, _))) { |
70 | | // Parse receive block with multiple handlers |
71 | 0 | state.tokens.advance(); // consume { |
72 | | |
73 | 0 | while !matches!(state.tokens.peek(), Some((Token::RightBrace, _))) { |
74 | | // Parse message pattern |
75 | 0 | let message_type = |
76 | 0 | if let Some((Token::Identifier(name), _)) = state.tokens.peek() { |
77 | 0 | let name = name.clone(); |
78 | 0 | state.tokens.advance(); |
79 | 0 | name |
80 | | } else { |
81 | 0 | bail!("Expected message type in receive block"); |
82 | | }; |
83 | | |
84 | | // Parse optional parameters |
85 | 0 | let params = if matches!(state.tokens.peek(), Some((Token::LeftParen, _))) { |
86 | 0 | utils::parse_params(state)? |
87 | | } else { |
88 | 0 | Vec::new() |
89 | | }; |
90 | | |
91 | | // Expect => for handler body |
92 | 0 | state.tokens.expect(&Token::FatArrow)?; |
93 | | |
94 | | // Parse handler body |
95 | 0 | let body = if matches!(state.tokens.peek(), Some((Token::LeftBrace, _))) { |
96 | 0 | Box::new(collections::parse_block(state)?) |
97 | | } else { |
98 | | // Single expression, not a block |
99 | 0 | Box::new(super::parse_expr_recursive(state)?) |
100 | | }; |
101 | | |
102 | 0 | handlers.push(ActorHandler { |
103 | 0 | message_type, |
104 | 0 | params, |
105 | 0 | body, |
106 | 0 | }); |
107 | | |
108 | | // Optional separator (comma or newline already consumed) |
109 | 0 | if matches!(state.tokens.peek(), Some((Token::Comma, _))) { |
110 | 0 | state.tokens.advance(); |
111 | 0 | } |
112 | | } |
113 | | |
114 | 0 | state.tokens.expect(&Token::RightBrace)?; // Close receive block |
115 | | } else { |
116 | | // Parse individual receive handler |
117 | | // Parse message pattern (e.g., MessageType(params)) |
118 | 0 | let message_type = if let Some((Token::Identifier(name), _)) = state.tokens.peek() { |
119 | 0 | let name = name.clone(); |
120 | 0 | state.tokens.advance(); |
121 | 0 | name |
122 | | } else { |
123 | 0 | bail!("Expected message type after receive"); |
124 | | }; |
125 | | |
126 | | // Parse optional parameters |
127 | 0 | let params = if matches!(state.tokens.peek(), Some((Token::LeftParen, _))) { |
128 | 0 | utils::parse_params(state)? |
129 | | } else { |
130 | 0 | Vec::new() |
131 | | }; |
132 | | |
133 | | // Parse optional return type |
134 | 0 | let _return_type = if matches!(state.tokens.peek(), Some((Token::Arrow, _))) { |
135 | 0 | state.tokens.advance(); // consume -> |
136 | 0 | Some(utils::parse_type(state)?) |
137 | | } else { |
138 | 0 | None |
139 | | }; |
140 | | |
141 | | // Parse handler body - expect block |
142 | 0 | let body = Box::new(collections::parse_block(state)?); |
143 | | |
144 | 0 | handlers.push(ActorHandler { |
145 | 0 | message_type, |
146 | 0 | params, |
147 | 0 | body, |
148 | 0 | }); |
149 | | } |
150 | | } else { |
151 | | // State field |
152 | 0 | let field_name = if let Some((Token::Identifier(name), _)) = state.tokens.peek() { |
153 | 0 | let name = name.clone(); |
154 | 0 | state.tokens.advance(); |
155 | 0 | name |
156 | | } else { |
157 | 0 | bail!("Expected field name"); |
158 | | }; |
159 | | |
160 | 0 | state.tokens.expect(&Token::Colon)?; |
161 | 0 | let ty = utils::parse_type(state)?; |
162 | | |
163 | | // Parse optional default value |
164 | 0 | if matches!(state.tokens.peek(), Some((Token::Equal, _))) { |
165 | | // Skip the default value for now (not stored in AST yet) |
166 | 0 | state.tokens.advance(); // consume = |
167 | 0 | let _default_value = super::parse_expr_recursive(state)?; |
168 | 0 | } |
169 | | |
170 | 0 | state_fields.push(StructField { |
171 | 0 | name: field_name, |
172 | 0 | ty, |
173 | 0 | is_pub: false, |
174 | 0 | }); |
175 | | |
176 | | // Optional comma or semicolon |
177 | 0 | if matches!( |
178 | 0 | state.tokens.peek(), |
179 | | Some((Token::Comma | Token::Semicolon, _)) |
180 | 0 | ) { |
181 | 0 | state.tokens.advance(); |
182 | 0 | } |
183 | | } |
184 | | } |
185 | | |
186 | 0 | state.tokens.expect(&Token::RightBrace)?; |
187 | | |
188 | 0 | Ok(Expr::new( |
189 | 0 | ExprKind::Actor { |
190 | 0 | name, |
191 | 0 | state: state_fields, |
192 | 0 | handlers, |
193 | 0 | }, |
194 | 0 | start_span, |
195 | 0 | )) |
196 | 0 | } |
197 | | |
198 | | #[cfg(test)] |
199 | | mod tests { |
200 | | use super::*; |
201 | | |
202 | | #[test] |
203 | 1 | fn test_parse_actor_function_signature() { |
204 | | // This test just verifies the function signature compiles and exists |
205 | | // Actual functionality testing is done via integration tests due to |
206 | | // complex parser infrastructure requirements |
207 | | |
208 | | // Verify function exists with correct signature |
209 | 1 | let _f: fn(&mut ParserState) -> Result<Expr> = parse_actor; |
210 | | |
211 | | // Basic compilation test passed |
212 | 1 | assert!(true, "Function signature is correct"0 ); |
213 | 1 | } |
214 | | } |