devalang_core/core/parser/driver/parse_map.rs
1use crate::core::lexer::token::TokenKind;
2use devalang_types::Value;
3
4pub fn parse_map_value(parser: &mut crate::core::parser::driver::parser::Parser) -> Option<Value> {
5 let logger = devalang_utils::logger::Logger::new();
6 use devalang_utils::logger::LogLevel;
7 if !parser.match_token(TokenKind::LBrace) {
8 return None;
9 }
10
11 let mut map = std::collections::HashMap::new();
12
13 while !parser.check_token(TokenKind::RBrace) && !parser.is_eof() {
14 // Skip separators and formatting before the key
15 while parser.check_token(TokenKind::Newline)
16 || parser.check_token(TokenKind::Whitespace)
17 || parser.check_token(TokenKind::Indent)
18 || parser.check_token(TokenKind::Dedent)
19 || parser.check_token(TokenKind::Comma)
20 {
21 parser.advance();
22 }
23
24 // Check if we are at the closing brace of the map
25 if parser.check_token(TokenKind::RBrace) {
26 break;
27 }
28
29 let key = if let Some(token) = parser.advance() {
30 match token.kind {
31 TokenKind::Whitespace
32 | TokenKind::Indent
33 | TokenKind::Dedent
34 | TokenKind::Newline => {
35 continue;
36 }
37 _ => token.lexeme.clone(),
38 }
39 } else {
40 break;
41 };
42
43 // Skip newlines and whitespace before colon
44 while parser.check_token(TokenKind::Newline) || parser.check_token(TokenKind::Whitespace) {
45 parser.advance();
46 }
47
48 if !parser.match_token(TokenKind::Colon) {
49 logger.log_message(
50 LogLevel::Error,
51 &format!("Expected ':' after map key '{}'", key),
52 );
53 break;
54 }
55
56 // Skip separators and formatting before value
57 while parser.check_token(TokenKind::Newline)
58 || parser.check_token(TokenKind::Whitespace)
59 || parser.check_token(TokenKind::Indent)
60 || parser.check_token(TokenKind::Dedent)
61 || parser.check_token(TokenKind::Comma)
62 {
63 parser.advance();
64 }
65
66 let value = if let Some(token) = parser.peek_clone() {
67 match token.kind {
68 TokenKind::String => {
69 parser.advance();
70 Value::String(token.lexeme.clone())
71 }
72 TokenKind::Number => {
73 // Handle number, decimal number and optional fraction form (e.g., 1/4)
74 let mut number_str = token.lexeme.clone();
75 parser.advance(); // consume the first number
76
77 // decimal support: number '.' number
78 if let Some(dot_token) = parser.peek_clone() {
79 if dot_token.kind == TokenKind::Dot {
80 parser.advance(); // consume the dot
81
82 if let Some(decimal_token) = parser.peek_clone() {
83 if decimal_token.kind == TokenKind::Number {
84 parser.advance(); // consume the number after the dot
85 number_str.push('.');
86 number_str.push_str(&decimal_token.lexeme);
87 } else {
88 logger.log_message(
89 LogLevel::Error,
90 &format!(
91 "Expected number after dot, got {:?}",
92 decimal_token
93 ),
94 );
95 return Some(Value::Null);
96 }
97 } else {
98 logger.log_message(
99 LogLevel::Error,
100 "Expected number after dot, but reached EOF",
101 );
102 return Some(Value::Null);
103 }
104 }
105 }
106
107 // Fraction support: number '/' number -> Duration::Beat("num/den")
108 if let Some(slash_tok) = parser.peek_clone() {
109 if slash_tok.kind == TokenKind::Slash {
110 // consume '/'
111 parser.advance();
112 if let Some(den_tok) = parser.peek_clone() {
113 match den_tok.kind {
114 TokenKind::Number | TokenKind::Identifier => {
115 let frac = format!("{}/{}", number_str, den_tok.lexeme);
116 parser.advance();
117 return Some(Value::Duration(
118 devalang_types::Duration::Beat(frac),
119 ));
120 }
121 _ => {
122 logger.log_message(
123 LogLevel::Error,
124 &format!(
125 "Expected number or identifier after '/', got {:?}",
126 den_tok
127 ),
128 );
129 return Some(Value::Null);
130 }
131 }
132 } else {
133 logger.log_message(
134 LogLevel::Error,
135 "Expected denominator after '/', but reached EOF",
136 );
137 return Some(Value::Null);
138 }
139 }
140 }
141
142 Value::Number(number_str.parse::<f32>().unwrap_or(0.0))
143 }
144
145 TokenKind::Identifier => {
146 // Support dotted identifiers in map values: alias.param or nested
147 let current_line = token.line;
148 let mut parts: Vec<String> = vec![token.lexeme.clone()];
149 parser.advance();
150 loop {
151 let Some(next) = parser.peek_clone() else {
152 break;
153 };
154 if next.line != current_line {
155 break;
156 }
157 if next.kind == TokenKind::Dot {
158 // Consume '.' and the following identifier/number on same line
159 parser.advance(); // dot
160 if let Some(id2) = parser.peek_clone() {
161 if id2.line == current_line
162 && (id2.kind == TokenKind::Identifier
163 || id2.kind == TokenKind::Number)
164 {
165 parts.push(id2.lexeme.clone());
166 parser.advance(); // consume part
167 continue;
168 }
169 }
170 break;
171 } else {
172 break;
173 }
174 }
175 Value::Identifier(parts.join("."))
176 }
177 TokenKind::LBracket => {
178 // Allow arrays as map values
179 if let Some(v) =
180 crate::core::parser::driver::parse_array::parse_array_value(parser)
181 {
182 v
183 } else {
184 Value::Null
185 }
186 }
187 TokenKind::LBrace => {
188 // Allow inline nested maps as map values
189 if let Some(v) = parse_map_value(parser) {
190 v
191 } else {
192 Value::Null
193 }
194 }
195 _ => {
196 logger.log_message(
197 LogLevel::Error,
198 &format!("Unexpected token in map value: {:?}", token),
199 );
200 Value::Null
201 }
202 }
203 } else {
204 Value::Null
205 };
206
207 map.insert(key, value);
208
209 // Optionally skip a trailing comma after the value
210 while parser.check_token(TokenKind::Comma)
211 || parser.check_token(TokenKind::Whitespace)
212 || parser.check_token(TokenKind::Newline)
213 {
214 parser.advance();
215 }
216 }
217
218 if !parser.match_token(TokenKind::RBrace) {
219 logger.log_message(LogLevel::Error, "Expected '}' at end of map");
220 }
221
222 Some(Value::Map(map))
223}