jia_parse/jia_lang/parser/
domain.rs1use super::Parser;
16use crate::error::ParseError;
17use crate::jia_lang::ast::{Domain, DomainStmt};
18use crate::jia_lang::lexer::TokenKind;
19
20#[derive(Clone, Copy)]
21enum IntervalAttr {
22 Duration,
23 Start,
24 End,
25}
26
27impl IntervalAttr {
28 fn from_name(name: &str) -> Option<Self> {
29 match name {
30 "duration" => Some(Self::Duration),
31 "start" => Some(Self::Start),
32 "end" => Some(Self::End),
33 _ => None,
34 }
35 }
36
37 fn to_stmt(self, intervals: Vec<String>, domain: Domain) -> DomainStmt {
38 match self {
39 Self::Duration => DomainStmt::IntervalDuration { intervals, domain },
40 Self::Start => DomainStmt::IntervalStart { intervals, domain },
41 Self::End => DomainStmt::IntervalEnd { intervals, domain },
42 }
43 }
44}
45
46impl<'a> Parser<'a> {
47 pub fn parse_domains_block(&mut self) -> Result<Vec<DomainStmt>, ParseError> {
49 self.expect_ident_matching("domains")?;
50 self.expect_token(TokenKind::LBrace)?;
51
52 let mut stmts = Vec::new();
53 while self.peek_kind() != Some(TokenKind::RBrace) {
54 stmts.push(self.parse_domain_stmt()?);
55 }
56 self.expect_token(TokenKind::RBrace)?;
57 Ok(stmts)
58 }
59
60 fn parse_domain_stmt(&mut self) -> Result<DomainStmt, ParseError> {
62 let name = match self.peek_kind() {
63 Some(TokenKind::Ident(s)) => s,
64 _ => {
65 return Err(ParseError::new(
66 "expected domain statement",
67 self.current_span(),
68 ))
69 }
70 };
71
72 if let Some(attr) = IntervalAttr::from_name(name.as_str()) {
73 return self.parse_interval_attr_stmt(attr);
74 }
75
76 match name.as_str() {
77 "optional" => self.parse_optional_stmt(),
78 "demand" => self.parse_demand_stmt(),
79 _ => {
80 let ident = self.expect_ident()?;
82 match self.peek_kind() {
83 Some(TokenKind::Ident(kw)) if kw.as_str() == "in" => {
84 self.parse_integer_domain(ident)
85 }
86 Some(TokenKind::Eq) => self.parse_set_domain(ident),
87 _ => Err(ParseError::new(
88 format!("expected 'in' or '=' after '{ident}'"),
89 self.current_span(),
90 )),
91 }
92 }
93 }
94 }
95
96 fn parse_interval_attr_stmt(&mut self, attr: IntervalAttr) -> Result<DomainStmt, ParseError> {
98 self.expect_ident()?;
99 self.expect_token(TokenKind::LParen)?;
100 let intervals = self.parse_ident_list()?;
101 self.expect_token(TokenKind::RParen)?;
102 let domain = self.parse_domain_spec()?;
103
104 Ok(attr.to_stmt(intervals, domain))
105 }
106
107 fn parse_optional_stmt(&mut self) -> Result<DomainStmt, ParseError> {
109 self.expect_ident_matching("optional")?;
110 self.expect_token(TokenKind::LParen)?;
111 let intervals = self.parse_ident_list()?;
112 self.expect_token(TokenKind::RParen)?;
113 Ok(DomainStmt::IntervalOptional { intervals })
114 }
115
116 fn parse_demand_stmt(&mut self) -> Result<DomainStmt, ParseError> {
118 self.expect_ident_matching("demand")?;
119 self.expect_token(TokenKind::LParen)?;
120 let interval = self.expect_ident()?;
121 self.expect_token(TokenKind::Comma)?;
122 let set = self.expect_ident()?;
123 self.expect_token(TokenKind::RParen)?;
124 self.expect_token(TokenKind::Eq)?;
125 let value = self.expect_number()?;
126 Ok(DomainStmt::Demand {
127 interval,
128 set,
129 value,
130 })
131 }
132
133 fn parse_integer_domain(&mut self, name: String) -> Result<DomainStmt, ParseError> {
136 self.expect_ident_matching("in")?;
137 let domain = self.parse_domain_value()?;
138 Ok(DomainStmt::IntegerDomain { name, domain })
139 }
140
141 fn parse_set_domain(&mut self, name: String) -> Result<DomainStmt, ParseError> {
144 self.expect_token(TokenKind::Eq)?;
145 self.expect_token(TokenKind::LBrace)?;
146 let members = self.parse_ident_list()?;
147 self.expect_token(TokenKind::RBrace)?;
148 Ok(DomainStmt::SetDomain { name, members })
149 }
150
151 fn parse_domain_spec(&mut self) -> Result<Domain, ParseError> {
153 match self.peek_kind() {
154 Some(TokenKind::Eq) => {
155 self.advance();
156 match self.peek_kind() {
157 Some(TokenKind::Number(n)) => {
158 self.advance();
159 Ok(Domain::Fixed(n))
160 }
161 Some(TokenKind::Float(f)) => {
162 self.advance();
163 Ok(Domain::RealFixed(f))
164 }
165 _ => Err(ParseError::new(
166 "expected number after '='",
167 self.current_span(),
168 )),
169 }
170 }
171 Some(TokenKind::Ident(kw)) if kw.as_str() == "in" => {
172 self.advance();
173 self.parse_domain_value()
174 }
175 _ => Err(ParseError::new(
176 "expected '=' or 'in' in domain specification",
177 self.current_span(),
178 )),
179 }
180 }
181
182 fn parse_bound(&mut self) -> Result<f64, ParseError> {
184 let negate = if self.peek_kind() == Some(TokenKind::Minus) {
186 self.advance();
187 true
188 } else {
189 false
190 };
191 match self.peek_kind() {
192 Some(TokenKind::Number(n)) => {
193 self.advance();
194 Ok(if negate { -(n as f64) } else { n as f64 })
195 }
196 Some(TokenKind::Float(f)) => {
197 self.advance();
198 Ok(if negate { -f } else { f })
199 }
200 Some(TokenKind::Ident(s)) if s == "inf" => {
201 self.advance();
202 Ok(if negate {
203 f64::NEG_INFINITY
204 } else {
205 f64::INFINITY
206 })
207 }
208 _ => Err(ParseError::new(
209 "expected number, float, or 'inf'",
210 self.current_span(),
211 )),
212 }
213 }
214
215 fn parse_domain_value(&mut self) -> Result<Domain, ParseError> {
217 match self.peek_kind() {
218 Some(TokenKind::LBrace) => {
219 self.advance();
221 let mut values = vec![self.expect_number()?];
222 while self.peek_kind() == Some(TokenKind::Comma) {
223 self.advance();
224 values.push(self.expect_number()?);
225 }
226 self.expect_token(TokenKind::RBrace)?;
227 Ok(Domain::Enumerated(values))
228 }
229 Some(TokenKind::Number(_)) | Some(TokenKind::Float(_)) | Some(TokenKind::Minus) => {
230 let min = self.parse_bound()?;
232 self.expect_token(TokenKind::DotDot)?;
233 let max = self.parse_bound()?;
234 if min.fract() == 0.0 && max.fract() == 0.0 && min.is_finite() && max.is_finite() {
236 Ok(Domain::Range {
237 min: min as i64,
238 max: max as i64,
239 })
240 } else {
241 Ok(Domain::RealRange { min, max })
242 }
243 }
244 Some(TokenKind::Ident(s)) if s == "inf" || s == "free" => {
245 let kw = s.clone();
246 if kw == "free" {
247 self.advance();
248 return Ok(Domain::RealRange {
249 min: f64::NEG_INFINITY,
250 max: f64::INFINITY,
251 });
252 }
253 let min = self.parse_bound()?;
255 self.expect_token(TokenKind::DotDot)?;
256 let max = self.parse_bound()?;
257 Ok(Domain::RealRange { min, max })
258 }
259 _ => Err(ParseError::new(
260 "expected range (min..max), enumeration ({v1, v2, ...}), or 'free'",
261 self.current_span(),
262 )),
263 }
264 }
265}
266
267#[cfg(test)]
268mod tests {
269 use crate::jia_lang::ast::{Domain, DomainStmt};
270 use crate::jia_lang::lexer::tokenize;
271 use crate::jia_lang::parser::Parser;
272
273 fn parse_domains(input: &str) -> Vec<DomainStmt> {
274 let tokens = tokenize(input).unwrap();
275 let mut parser = Parser::new(&tokens);
276 parser.parse_domains_block().unwrap()
277 }
278
279 #[test]
280 fn test_fixed_duration() {
281 let stmts = parse_domains("domains { duration(a) = 3 }");
282 assert_eq!(
283 stmts[0],
284 DomainStmt::IntervalDuration {
285 intervals: vec!["a".to_string()],
286 domain: Domain::Fixed(3),
287 }
288 );
289 }
290
291 #[test]
292 fn test_range_duration() {
293 let stmts = parse_domains("domains { duration(a) in 1..4 }");
294 assert_eq!(
295 stmts[0],
296 DomainStmt::IntervalDuration {
297 intervals: vec!["a".to_string()],
298 domain: Domain::Range { min: 1, max: 4 },
299 }
300 );
301 }
302
303 #[test]
304 fn test_enumerated_duration() {
305 let stmts = parse_domains("domains { duration(a) in {1, 3, 5} }");
306 assert_eq!(
307 stmts[0],
308 DomainStmt::IntervalDuration {
309 intervals: vec!["a".to_string()],
310 domain: Domain::Enumerated(vec![1, 3, 5]),
311 }
312 );
313 }
314
315 #[test]
316 fn test_multi_interval_attr() {
317 let stmts = parse_domains("domains { start(a, b) in 0..20 }");
318 assert_eq!(
319 stmts[0],
320 DomainStmt::IntervalStart {
321 intervals: vec!["a".to_string(), "b".to_string()],
322 domain: Domain::Range { min: 0, max: 20 },
323 }
324 );
325 }
326
327 #[test]
328 fn test_optional() {
329 let stmts = parse_domains("domains { optional(x, y, z) }");
330 assert_eq!(
331 stmts[0],
332 DomainStmt::IntervalOptional {
333 intervals: vec!["x".to_string(), "y".to_string(), "z".to_string()],
334 }
335 );
336 }
337
338 #[test]
339 fn test_integer_domain() {
340 let stmts = parse_domains("domains { makespan in 0..100 }");
341 assert_eq!(
342 stmts[0],
343 DomainStmt::IntegerDomain {
344 name: "makespan".to_string(),
345 domain: Domain::Range { min: 0, max: 100 },
346 }
347 );
348 }
349
350 #[test]
351 fn test_set_domain() {
352 let stmts = parse_domains("domains { machine1 = {a, b, c} }");
353 assert_eq!(
354 stmts[0],
355 DomainStmt::SetDomain {
356 name: "machine1".to_string(),
357 members: vec!["a".to_string(), "b".to_string(), "c".to_string()],
358 }
359 );
360 }
361
362 #[test]
363 fn test_demand() {
364 let stmts = parse_domains("domains { demand(a1, res1) = 2 }");
365 assert_eq!(
366 stmts[0],
367 DomainStmt::Demand {
368 interval: "a1".to_string(),
369 set: "res1".to_string(),
370 value: 2,
371 }
372 );
373 }
374
375 #[test]
376 fn test_domain_error_branches() {
377 for input in [
378 "domains { 1 }",
379 "domains { x }",
380 "domains { duration(a) }",
381 "domains { duration(a) = x }",
382 "domains { x in nope }",
383 "domains { x in 0..nope }",
384 "domains { x in 0.. }",
385 ] {
386 let tokens = tokenize(input).unwrap();
387 let mut parser = Parser::new(&tokens);
388 assert!(parser.parse_domains_block().is_err(), "{input}");
389 }
390 }
391
392 #[test]
393 fn test_domain_spec_inf_range_start() {
394 let stmts = parse_domains("domains { x in inf..10 }");
395 assert!(matches!(
396 stmts[0],
397 DomainStmt::IntegerDomain {
398 domain: Domain::RealRange { min, max },
399 ..
400 } if min.is_infinite() && min.is_sign_positive() && max == 10.0
401 ));
402 }
403}