devalang_core/core/parser/handler/
loop_.rs

1use devalang_types::Value;
2
3use crate::core::{
4    lexer::token::TokenKind,
5    parser::{
6        driver::Parser,
7        statement::{Statement, StatementKind},
8    },
9    store::global::GlobalStore,
10};
11
12pub fn parse_loop_token(parser: &mut Parser, global_store: &mut GlobalStore) -> Statement {
13    parser.advance(); // consume 'loop' or 'for' (aliased in lexer)
14    let Some(loop_token) = parser.previous_clone() else {
15        return Statement::unknown();
16    };
17
18    // Support two forms:
19    // 1) loop <count>:
20    // 2) for <ident> in [a,b,c]:
21
22    // Peek next to decide
23    let Some(next_token) = parser.peek_clone() else {
24        return Statement::error_with_pos(
25            loop_token.indent,
26            loop_token.line,
27            loop_token.column,
28            "Expected iterator after loop/for".to_string(),
29        );
30    };
31
32    // Try to detect 'for <ident> in [array]:' form
33    let mut foreach_ident: Option<String> = None;
34    if let TokenKind::Identifier = next_token.kind {
35        // Could be either count identifier (old form) or foreach variable
36        // Look ahead for 'in'
37        let name = next_token.lexeme.clone();
38        // don't consume yet; we'll branch
39        if let Some(t2) = parser.peek_nth(1) {
40            if t2.kind == TokenKind::Identifier && t2.lexeme == "in" {
41                // foreach form
42                foreach_ident = Some(name);
43                // consume ident and 'in'
44                parser.advance();
45                parser.advance();
46            }
47        }
48    }
49
50    if let Some(var_name) = foreach_ident {
51        // Expect [array] OR number OR string OR identifier after 'in'
52        let array_val = if let Some(tok) = parser.peek_clone() {
53            match tok.kind {
54                TokenKind::LBracket => {
55                    if let Some(v) = parser.parse_array_value() {
56                        v
57                    } else {
58                        Value::Array(vec![])
59                    }
60                }
61                TokenKind::Number => {
62                    parser.advance();
63                    let n = tok.lexeme.parse::<f32>().unwrap_or(0.0);
64                    Value::Number(n)
65                }
66                TokenKind::String => {
67                    parser.advance();
68                    Value::String(tok.lexeme.clone())
69                }
70                TokenKind::Identifier => {
71                    parser.advance();
72                    Value::Identifier(tok.lexeme.clone())
73                }
74                _ => {
75                    return Statement::error_with_pos(
76                        loop_token.indent,
77                        loop_token.line,
78                        loop_token.column,
79                        "Expected array, number, string or identifier after 'in'".to_string(),
80                    );
81                }
82            }
83        } else {
84            return Statement::error_with_pos(
85                loop_token.indent,
86                loop_token.line,
87                loop_token.column,
88                "Expected array, number, string or identifier after 'in'".to_string(),
89            );
90        };
91
92        // Expect ':'
93        if !parser.match_token(TokenKind::Colon) {
94            return Statement::error_with_pos(
95                loop_token.indent,
96                loop_token.line,
97                loop_token.column,
98                "Expected ':' after foreach header".to_string(),
99            );
100        }
101
102        let tokens =
103            parser.collect_until(|t| t.kind == TokenKind::Dedent || t.kind == TokenKind::EOF);
104        let loop_body = parser.parse_block(tokens.clone(), global_store);
105        if let Some(token) = parser.peek() {
106            if token.kind == TokenKind::Dedent {
107                parser.advance();
108            }
109        }
110
111        let mut value_map = std::collections::HashMap::new();
112        value_map.insert("foreach".to_string(), Value::Identifier(var_name));
113        value_map.insert("array".to_string(), array_val);
114        value_map.insert("body".to_string(), Value::Block(loop_body.clone()));
115
116        return Statement {
117            kind: StatementKind::Loop,
118            value: Value::Map(value_map),
119            indent: loop_token.indent,
120            line: loop_token.line,
121            column: loop_token.column,
122        };
123    }
124
125    // Fallback to legacy: loop <count>:
126    let Some(iterator_token) = parser.peek_clone() else {
127        return Statement::error_with_pos(
128            loop_token.indent,
129            loop_token.line,
130            loop_token.column,
131            "Expected number or identifier after 'loop'".to_string(),
132        );
133    };
134
135    let iterator_value = match iterator_token.kind {
136        TokenKind::Number => {
137            let val = iterator_token.lexeme.parse::<f32>().unwrap_or(1.0);
138            parser.advance();
139            Value::Number(val)
140        }
141        TokenKind::Identifier => {
142            let val = iterator_token.lexeme.clone();
143            parser.advance();
144            Value::Identifier(val)
145        }
146        TokenKind::String => {
147            // strings that are numeric (e.g. "10")
148            let s = iterator_token.lexeme.clone();
149            parser.advance();
150            Value::String(s)
151        }
152        _ => {
153            return Statement::error_with_pos(
154                iterator_token.clone().indent,
155                iterator_token.clone().line,
156                iterator_token.clone().column,
157                "Expected a number, string or identifier as loop count".to_string(),
158            );
159        }
160    };
161
162    if !parser.match_token(TokenKind::Colon) {
163        let message = format!(
164            "Expected ':' after loop count, got {:?}",
165            parser.peek_kind()
166        );
167        return Statement::error_with_pos(
168            loop_token.clone().indent,
169            loop_token.clone().line,
170            loop_token.clone().column,
171            message,
172        );
173    }
174
175    let tokens = parser.collect_until(|t| t.kind == TokenKind::Dedent || t.kind == TokenKind::EOF);
176    let loop_body = parser.parse_block(tokens.clone(), global_store);
177    if let Some(token) = parser.peek() {
178        if token.kind == TokenKind::Dedent {
179            parser.advance();
180        }
181    }
182
183    let mut value_map = std::collections::HashMap::new();
184    value_map.insert("iterator".to_string(), iterator_value);
185    value_map.insert("body".to_string(), Value::Block(loop_body.clone()));
186
187    Statement {
188        kind: StatementKind::Loop,
189        value: Value::Map(value_map),
190        indent: loop_token.indent,
191        line: loop_token.line,
192        column: loop_token.column,
193    }
194}