devalang_core/core/parser/handler/
arrow_call.rs

1use crate::core::{
2    lexer::token::TokenKind,
3    parser::{
4        driver::parser::Parser,
5        statement::{Statement, StatementKind},
6    },
7    store::global::GlobalStore,
8};
9use devalang_types::Value;
10
11fn parse_map_literal(parser: &mut Parser) -> Value {
12    // Assumes '{' has already been consumed by caller
13    let mut map = std::collections::HashMap::new();
14    loop {
15        let Some(inner_token) = parser.peek_clone() else {
16            break;
17        };
18
19        match inner_token.kind {
20            TokenKind::RBrace => {
21                parser.advance(); // consume '}'
22                break;
23            }
24            TokenKind::Newline | TokenKind::Comma => {
25                parser.advance();
26                continue;
27            }
28            _ => {}
29        }
30
31        // Key
32        parser.advance();
33        let key = inner_token.lexeme.clone();
34
35        // Expect ':'
36        if let Some(colon_token) = parser.peek_clone() {
37            if colon_token.kind == TokenKind::Colon {
38                parser.advance(); // consume ':'
39
40                // Value
41                if let Some(value_token) = parser.peek_clone() {
42                    match value_token.kind {
43                        TokenKind::LBrace => {
44                            parser.advance(); // consume '{'
45                            let nested = parse_map_literal(parser);
46                            map.insert(key, nested);
47                        }
48                        TokenKind::Identifier => {
49                            parser.advance();
50                            let v = if value_token.lexeme == "true" {
51                                Value::Boolean(true)
52                            } else if value_token.lexeme == "false" {
53                                Value::Boolean(false)
54                            } else {
55                                Value::Identifier(value_token.lexeme.clone())
56                            };
57                            map.insert(key, v);
58                        }
59                        TokenKind::String => {
60                            parser.advance();
61                            map.insert(key, Value::String(value_token.lexeme.clone()));
62                        }
63                        TokenKind::Number => {
64                            parser.advance();
65                            // Beat fraction support: NUMBER '/' NUMBER
66                            if let Some(TokenKind::Slash) = parser.peek_kind() {
67                                parser.advance(); // '/'
68                                if let Some(den) = parser.peek_clone() {
69                                    if den.kind == TokenKind::Number
70                                        || den.kind == TokenKind::Identifier
71                                    {
72                                        parser.advance();
73                                        let beat = format!("{}/{}", value_token.lexeme, den.lexeme);
74                                        map.insert(
75                                            key,
76                                            Value::Duration(devalang_types::Duration::Beat(beat)),
77                                        );
78                                        continue;
79                                    }
80                                }
81                            }
82                            // Decimal support NUMBER '.' NUMBER
83                            if let Some(next) = parser.peek_clone() {
84                                if next.kind == TokenKind::Dot {
85                                    parser.advance(); // '.'
86                                    if let Some(after) = parser.peek_clone() {
87                                        if after.kind == TokenKind::Number {
88                                            parser.advance();
89                                            let combined =
90                                                format!("{}.{}", value_token.lexeme, after.lexeme);
91                                            map.insert(
92                                                key,
93                                                Value::Number(
94                                                    combined.parse::<f32>().unwrap_or(0.0),
95                                                ),
96                                            );
97                                            continue;
98                                        }
99                                    }
100                                }
101                            }
102                            map.insert(
103                                key,
104                                Value::Number(value_token.lexeme.parse::<f32>().unwrap_or(0.0)),
105                            );
106                        }
107                        TokenKind::Boolean => {
108                            parser.advance();
109                            map.insert(
110                                key,
111                                Value::Boolean(value_token.lexeme.parse::<bool>().unwrap_or(false)),
112                            );
113                        }
114                        _ => {
115                            // Unknown value type, consume and store Unknown
116                            parser.advance();
117                            map.insert(key, Value::Unknown);
118                        }
119                    }
120                }
121            }
122        }
123    }
124    Value::Map(map)
125}
126
127pub fn parse_arrow_call(parser: &mut Parser, _global_store: &mut GlobalStore) -> Statement {
128    let Some(target_token) = parser.peek_clone() else {
129        return Statement::unknown();
130    };
131
132    if target_token.kind != TokenKind::Identifier {
133        parser.advance(); // consume target token
134        return Statement::unknown_with_pos(
135            target_token.indent,
136            target_token.line,
137            target_token.column,
138        );
139    }
140
141    let Some(arrow_token) = parser.peek_nth(1).cloned() else {
142        parser.advance(); // consume arrow token
143        return Statement::unknown_with_pos(
144            target_token.indent,
145            target_token.line,
146            target_token.column,
147        );
148    };
149
150    if arrow_token.kind != TokenKind::Arrow {
151        parser.advance(); // consume method token
152        return Statement::unknown_with_pos(
153            target_token.indent,
154            target_token.line,
155            target_token.column,
156        );
157    }
158
159    // We have a valid arrow call, so we consume the arrow token
160    let Some(method_token) = parser.peek_nth(2).cloned() else {
161        parser.advance();
162        return Statement::unknown_with_pos(
163            target_token.indent,
164            target_token.line,
165            target_token.column,
166        );
167    };
168
169    if method_token.kind != TokenKind::Identifier {
170        parser.advance();
171        return Statement::unknown_with_pos(
172            method_token.indent,
173            method_token.line,
174            method_token.column,
175        );
176    }
177
178    // Consume the tokens for target, arrow, and method
179    parser.advance(); // target
180    parser.advance(); // ->
181    parser.advance(); // method
182
183    let args = parse_arrow_args(parser);
184    Statement {
185        kind: StatementKind::ArrowCall {
186            target: target_token.lexeme.clone(),
187            method: method_token.lexeme.clone(),
188            args,
189        },
190        value: Value::Null,
191        indent: target_token.indent,
192        line: target_token.line,
193        column: target_token.column,
194    }
195}
196
197// Parse args after the method token. Reused by continuation parser.
198fn parse_arrow_args(parser: &mut Parser) -> Vec<Value> {
199    let mut args = Vec::new();
200    let mut paren_depth = 0;
201    let mut map_depth = 0;
202
203    while let Some(token) = parser.peek_clone() {
204        if token.kind == TokenKind::Newline || token.kind == TokenKind::EOF {
205            break;
206        }
207        if token.kind == TokenKind::LParen {
208            paren_depth += 1;
209        }
210        if token.kind == TokenKind::RParen {
211            if paren_depth > 0 {
212                paren_depth -= 1;
213                parser.advance();
214                if paren_depth == 0 {
215                    break;
216                }
217                continue;
218            } else {
219                break;
220            }
221        }
222        if token.kind == TokenKind::LBrace {
223            map_depth += 1;
224        }
225        if token.kind == TokenKind::RBrace {
226            if map_depth > 0 {
227                map_depth -= 1;
228                parser.advance();
229                if map_depth == 0 {
230                    continue;
231                }
232                continue;
233            } else {
234                break;
235            }
236        }
237
238        parser.advance();
239
240        let value = match token.kind {
241            TokenKind::Identifier => Value::Identifier(token.lexeme.clone()),
242            TokenKind::String => Value::String(token.lexeme.clone()),
243            TokenKind::Number => {
244                if let Some(TokenKind::Slash) = parser.peek_kind() {
245                    parser.advance(); // consume '/'
246                    if let Some(den) = parser.peek_clone() {
247                        if den.kind == TokenKind::Number || den.kind == TokenKind::Identifier {
248                            parser.advance();
249                            let beat = format!("{}/{}", token.lexeme, den.lexeme);
250                            Value::Duration(devalang_types::Duration::Beat(beat))
251                        } else {
252                            Value::Number(token.lexeme.parse::<f32>().unwrap_or(0.0))
253                        }
254                    } else {
255                        Value::Number(token.lexeme.parse::<f32>().unwrap_or(0.0))
256                    }
257                } else {
258                    Value::Number(token.lexeme.parse::<f32>().unwrap_or(0.0))
259                }
260            }
261            TokenKind::LBrace => parse_map_literal(parser),
262            _ => Value::Unknown,
263        };
264
265        args.push(value);
266
267        if paren_depth == 0 && (token.kind == TokenKind::RParen || token.kind == TokenKind::RBrace)
268        {
269            break;
270        }
271    }
272
273    args
274}
275
276// Parse an arrow continuation that begins with an Arrow token. If prev_target is Some,
277// use it as the call target; otherwise produce an Unknown statement.
278pub fn parse_arrow_continuation(
279    parser: &mut Parser,
280    _global_store: &mut GlobalStore,
281    prev_target: Option<String>,
282) -> Statement {
283    // We expect current token to be Arrow
284    let arrow_tok = parser.peek_clone();
285    if arrow_tok.is_none() {
286        return Statement::unknown();
287    }
288
289    // If there is no previous target, consume arrow and return unknown
290    let Some(target) = prev_target else {
291        parser.advance(); // consume Arrow
292        return Statement::unknown();
293    };
294
295    // consume '->'
296    parser.advance();
297
298    // next token should be method identifier
299    let Some(method_token) = parser.peek_nth(0).cloned() else {
300        return Statement::unknown();
301    };
302
303    if method_token.kind != TokenKind::Identifier {
304        parser.advance();
305        return Statement::unknown_with_pos(
306            method_token.indent,
307            method_token.line,
308            method_token.column,
309        );
310    }
311
312    // consume method
313    parser.advance();
314
315    let args = parse_arrow_args(parser);
316
317    Statement {
318        kind: StatementKind::ArrowCall {
319            target,
320            method: method_token.lexeme.clone(),
321            args,
322        },
323        value: Value::Null,
324        indent: method_token.indent,
325        line: method_token.line,
326        column: method_token.column,
327    }
328}