devalang_core/core/parser/handler/
loop_.rs

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