1
use std::collections::BTreeMap;
2
use std::sync::{Arc, RwLock};
3
use std::{fs, vec};
4

            
5
use conjure_cp_core::Model;
6
use conjure_cp_core::ast::DeclarationPtr;
7
use conjure_cp_core::ast::assertions::debug_assert_model_well_formed;
8
use conjure_cp_core::context::Context;
9
#[allow(unused)]
10
use uniplate::Uniplate;
11

            
12
use super::ParseContext;
13
use super::dominance::parse_dominance_relation;
14
use super::find::{parse_find_statement, parse_given_statement};
15
use super::letting::parse_letting_statement;
16
use super::util::{TypecheckingContext, get_tree};
17
use crate::diagnostics::source_map::SourceMap;
18
use crate::errors::{FatalParseError, ParseErrorCollection, RecoverableParseError};
19
use crate::expression::parse_expression;
20
use crate::syntax_errors::detect_syntactic_errors;
21
use tree_sitter::Tree;
22

            
23
/// Parse an Essence file into a Model using the tree-sitter parser.
24
18632
pub fn parse_essence_file_native(
25
18632
    path: &str,
26
18632
    context: Arc<RwLock<Context<'static>>>,
27
18632
) -> Result<Model, Box<ParseErrorCollection>> {
28
18632
    let source_code = fs::read_to_string(path)
29
18632
        .unwrap_or_else(|_| panic!("Failed to read the source code file {path}"));
30

            
31
18632
    let mut errors = vec![];
32
18632
    let model = parse_essence_with_context(&source_code, context, &mut errors);
33

            
34
18632
    match model {
35
17818
        Ok(Some(m)) => {
36
17818
            debug_assert_model_well_formed(&m, "tree-sitter");
37
17818
            Ok(m)
38
        }
39
        Ok(None) => {
40
            // Recoverable errors were found, return them as a ParseErrorCollection
41
814
            Err(Box::new(ParseErrorCollection::multiple(
42
814
                errors,
43
814
                Some(source_code),
44
814
                Some(path.to_string()),
45
814
            )))
46
        }
47
        Err(fatal) => {
48
            // Fatal error - wrap in ParseErrorCollection::Fatal
49
            Err(Box::new(ParseErrorCollection::fatal(fatal)))
50
        }
51
    }
52
18632
}
53

            
54
18632
pub fn parse_essence_with_context(
55
18632
    src: &str,
56
18632
    context: Arc<RwLock<Context<'static>>>,
57
18632
    errors: &mut Vec<RecoverableParseError>,
58
18632
) -> Result<Option<Model>, FatalParseError> {
59
18632
    match parse_essence_with_context_and_map(src, context, errors, None)? {
60
17818
        (Some(model), _source_map) => Ok(Some(model)),
61
814
        (None, _source_map) => Ok(None),
62
    }
63
18632
}
64

            
65
/*
66
    this function is used by both the file-based parser and the LSP parser (which needs the source map)
67
    the LSP parser can also optionally pass in a pre-parsed tree to avoid parsing twice (which is how caching is implemented)
68
    if the tree is not passed in, we will parse it from scratch (this is what the file-based parser does)
69
    when cache is dirty, LSP has to call parse_essence_with_context_and_map with None for the tree,
70
    which will cause it to re-parse the source code and update the cache (Model = ast, SorceMap = map)
71
*/
72
19105
pub fn parse_essence_with_context_and_map(
73
19105
    src: &str,
74
19105
    context: Arc<RwLock<Context<'static>>>,
75
19105
    errors: &mut Vec<RecoverableParseError>,
76
19105
    tree: Option<&Tree>,
77
19105
) -> Result<(Option<Model>, SourceMap), FatalParseError> {
78
19105
    let (tree, source_code) = if let Some(tree) = tree {
79
468
        (tree.clone(), src.to_string())
80
    } else {
81
18637
        match get_tree(src) {
82
18637
            Some(tree) => tree,
83
            None => {
84
                return Err(FatalParseError::TreeSitterError(
85
                    "Failed to parse source code".to_string(),
86
                ));
87
            }
88
        }
89
    };
90

            
91
19105
    let has_syntax_errors = tree.root_node().has_error();
92
19105
    if has_syntax_errors {
93
723
        detect_syntactic_errors(src, &tree, errors);
94
18382
    }
95

            
96
    // don't detect semantic errors if there are syntactic errors, but still parse for source map.
97
19105
    let mut suppressed_semantic_errors = Vec::new();
98
19105
    let semantic_errors: &mut Vec<RecoverableParseError> = if has_syntax_errors {
99
723
        &mut suppressed_semantic_errors
100
    } else {
101
18382
        errors
102
    };
103

            
104
19105
    let mut model = Model::new(context);
105
19105
    let mut source_map = SourceMap::default();
106
19105
    let mut declaration_spans = BTreeMap::new();
107
19105
    let root_node = tree.root_node();
108

            
109
    // Create a ParseContext
110
19105
    let mut ctx = ParseContext::new(
111
19105
        &source_code,
112
19105
        &root_node,
113
19105
        Some(model.symbols_ptr_unchecked().clone()),
114
19105
        semantic_errors,
115
19105
        &mut source_map,
116
19105
        &mut declaration_spans,
117
    );
118

            
119
19105
    let mut cursor = root_node.walk();
120
202811
    for statement in root_node.children(&mut cursor) {
121
202811
        if !statement.is_named() || statement.is_error() || statement.kind() == "ERROR" {
122
35381
            continue;
123
167430
        }
124

            
125
167430
        ctx.typechecking_context = TypecheckingContext::Unknown;
126
167430
        ctx.inner_typechecking_context = TypecheckingContext::Unknown;
127

            
128
167430
        match statement.kind() {
129
167430
            "single_line_comment" => {}
130
88254
            "language_declaration" => {}
131
81740
            "find_statement" => {
132
38501
                let var_hashmap = parse_find_statement(&mut ctx, statement)?;
133
45206
                for (name, domain) in var_hashmap {
134
45206
                    model
135
45206
                        .symbols_mut()
136
45206
                        .insert(DeclarationPtr::new_find(name, domain));
137
45206
                }
138
            }
139
43239
            "given_statement" => {
140
232
                let var_hashmap = parse_given_statement(&mut ctx, statement)?;
141
232
                for (name, domain) in var_hashmap {
142
219
                    model
143
219
                        .symbols_mut()
144
219
                        .insert(DeclarationPtr::new_given(name, domain));
145
219
                }
146
            }
147
43007
            "bool_expr" | "atom" | "comparison_expr" => {
148
34801
                ctx.typechecking_context = TypecheckingContext::Boolean;
149
34801
                let Some(expr) = parse_expression(&mut ctx, statement)? else {
150
559
                    continue;
151
                };
152
34242
                model.add_constraint(expr);
153
            }
154
8206
            "language_label" => {}
155
8206
            "letting_statement" => {
156
2509
                let Some(letting_vars) = parse_letting_statement(&mut ctx, statement)? else {
157
                    continue;
158
                };
159
2509
                model.symbols_mut().extend(letting_vars);
160
            }
161
5697
            "dominance_relation" => {
162
5697
                let Some(dominance) = parse_dominance_relation(&mut ctx, &statement)? else {
163
                    continue;
164
                };
165
5697
                if model.dominance.is_some() {
166
                    ctx.record_error(RecoverableParseError::new(
167
                        "Duplicate dominance relation".to_string(),
168
                        None,
169
                    ));
170
                    continue;
171
5697
                }
172
5697
                model.dominance = Some(dominance);
173
            }
174
            _ => {
175
                ctx.record_error(RecoverableParseError::new(
176
                    format!("Unexpected top-level statement: {}", statement.kind()),
177
                    Some(statement.range()),
178
                ));
179
                continue;
180
            }
181
        }
182
    }
183

            
184
    // Check if there were any recoverable errors
185
19105
    if !errors.is_empty() {
186
1269
        return Ok((None, source_map));
187
17836
    }
188
    // otherwise return the model
189
17836
    Ok((Some(model), source_map))
190
19105
}
191

            
192
5
pub fn parse_essence(src: &str) -> Result<(Model, SourceMap), Box<ParseErrorCollection>> {
193
5
    let context = Arc::new(RwLock::new(Context::default()));
194
5
    let mut errors = vec![];
195
5
    match parse_essence_with_context_and_map(src, context, &mut errors, None) {
196
5
        Ok((Some(model), source_map)) => {
197
5
            debug_assert_model_well_formed(&model, "tree-sitter");
198
5
            Ok((model, source_map))
199
        }
200
        Ok((None, _source_map)) => {
201
            // Recoverable errors were found, return them as a ParseErrorCollection
202
            Err(Box::new(ParseErrorCollection::multiple(
203
                errors,
204
                Some(src.to_string()),
205
                None,
206
            )))
207
        }
208
        Err(fatal) => Err(Box::new(ParseErrorCollection::fatal(fatal))),
209
    }
210
5
}
211

            
212
mod test {
213
    #[allow(unused_imports)]
214
    use crate::parse_essence;
215
    #[allow(unused_imports)]
216
    use conjure_cp_core::ast::{Atom, Expression, Metadata, Moo, Name};
217
    #[allow(unused_imports)]
218
    use conjure_cp_core::{domain_int, matrix_expr, range};
219
    #[allow(unused_imports)]
220
    use std::ops::Deref;
221

            
222
    #[test]
223
1
    pub fn test_parse_xyz() {
224
1
        let src = "
225
1
        find x, y, z : int(1..4)
226
1
        such that x + y + z = 4
227
1
        such that x >= y
228
1
        ";
229

            
230
1
        let (model, _source_map) = parse_essence(src).unwrap();
231

            
232
1
        let st = model.symbols();
233
1
        let x = st.lookup(&Name::user("x")).unwrap();
234
1
        let y = st.lookup(&Name::user("y")).unwrap();
235
1
        let z = st.lookup(&Name::user("z")).unwrap();
236
1
        assert_eq!(x.domain(), Some(domain_int!(1..4)));
237
1
        assert_eq!(y.domain(), Some(domain_int!(1..4)));
238
1
        assert_eq!(z.domain(), Some(domain_int!(1..4)));
239

            
240
1
        let constraints = model.constraints();
241
1
        assert_eq!(constraints.len(), 2);
242

            
243
1
        let c1 = constraints[0].clone();
244
1
        let x_e = Expression::Atomic(Metadata::new(), Atom::new_ref(x));
245
1
        let y_e = Expression::Atomic(Metadata::new(), Atom::new_ref(y));
246
1
        let z_e = Expression::Atomic(Metadata::new(), Atom::new_ref(z));
247
1
        assert_eq!(
248
            c1,
249
1
            Expression::Eq(
250
1
                Metadata::new(),
251
1
                Moo::new(Expression::Sum(
252
1
                    Metadata::new(),
253
1
                    Moo::new(matrix_expr!(
254
1
                        Expression::Sum(
255
1
                            Metadata::new(),
256
1
                            Moo::new(matrix_expr!(x_e.clone(), y_e.clone()))
257
1
                        ),
258
1
                        z_e
259
1
                    ))
260
1
                )),
261
1
                Moo::new(Expression::Atomic(Metadata::new(), 4.into()))
262
1
            )
263
        );
264

            
265
1
        let c2 = constraints[1].clone();
266
1
        assert_eq!(
267
            c2,
268
1
            Expression::Geq(Metadata::new(), Moo::new(x_e), Moo::new(y_e))
269
        );
270
1
    }
271

            
272
    #[test]
273
1
    pub fn test_parse_letting_index() {
274
1
        let src = "
275
1
        letting a be [ [ 1,2,3 ; int(1,2,4) ], [ 1,3,2 ; int(1,2,4) ], [ 3,2,1 ; int(1,2,4) ] ; int(-2..0) ]
276
1
        find b: int(1..5)
277
1
        such that
278
1
        b < a[-2,2],
279
1
        allDiff(a[-2,..])
280
1
        ";
281

            
282
1
        let (model, _source_map) = parse_essence(src).unwrap();
283
1
        let st = model.symbols();
284
1
        let a_decl = st.lookup(&Name::user("a")).unwrap();
285
1
        let a = a_decl.as_value_letting().unwrap().deref().clone();
286
1
        assert_eq!(
287
            a,
288
1
            matrix_expr!(
289
1
                matrix_expr!(1.into(), 2.into(), 3.into() ; domain_int!(1, 2, 4)),
290
1
                matrix_expr!(1.into(), 3.into(), 2.into() ; domain_int!(1, 2, 4)),
291
1
                matrix_expr!(3.into(), 2.into(), 1.into() ; domain_int!(1, 2, 4));
292
1
                domain_int!(-2..0)
293
            )
294
        )
295
1
    }
296

            
297
    #[test]
298
1
    pub fn test_parse_pareto_in_dominance_relation() {
299
1
        let src = "
300
1
        find x : int(0..3)
301
1

            
302
1
        dominance relation
303
1
            pareto(minimising x)
304
1
        ";
305

            
306
1
        let (model, _source_map) = parse_essence(src).unwrap();
307
1
        let st = model.symbols();
308
1
        let x = st.lookup(&Name::user("x")).unwrap();
309
1
        let x_e = Expression::Atomic(Metadata::new(), Atom::new_ref(x.clone()));
310
1
        let x_prev = Expression::FromSolution(Metadata::new(), Moo::new(Atom::new_ref(x)));
311

            
312
1
        assert_eq!(
313
            model.dominance,
314
1
            Some(Expression::DominanceRelation(
315
1
                Metadata::new(),
316
1
                Moo::new(Expression::And(
317
1
                    Metadata::new(),
318
1
                    Moo::new(matrix_expr!(
319
1
                        Expression::Leq(
320
1
                            Metadata::new(),
321
1
                            Moo::new(x_e.clone()),
322
1
                            Moo::new(x_prev.clone())
323
1
                        ),
324
1
                        Expression::Lt(Metadata::new(), Moo::new(x_e), Moo::new(x_prev))
325
1
                    ))
326
1
                ))
327
1
            ))
328
        );
329
1
    }
330

            
331
    #[test]
332
1
    pub fn test_parse_pareto_with_mixed_directions() {
333
1
        let src = "
334
1
        find x : int(0..3)
335
1
        find y : int(0..3)
336
1

            
337
1
        dominance relation
338
1
            pareto(minimising x, maximising y)
339
1
        ";
340

            
341
1
        let (model, _source_map) = parse_essence(src).unwrap();
342
1
        let st = model.symbols();
343
1
        let x = st.lookup(&Name::user("x")).unwrap();
344
1
        let y = st.lookup(&Name::user("y")).unwrap();
345
1
        let x_e = Expression::Atomic(Metadata::new(), Atom::new_ref(x.clone()));
346
1
        let y_e = Expression::Atomic(Metadata::new(), Atom::new_ref(y.clone()));
347
1
        let x_prev = Expression::FromSolution(Metadata::new(), Moo::new(Atom::new_ref(x)));
348
1
        let y_prev = Expression::FromSolution(Metadata::new(), Moo::new(Atom::new_ref(y)));
349

            
350
1
        assert_eq!(
351
            model.dominance,
352
1
            Some(Expression::DominanceRelation(
353
1
                Metadata::new(),
354
1
                Moo::new(Expression::And(
355
1
                    Metadata::new(),
356
1
                    Moo::new(matrix_expr!(
357
1
                        Expression::Leq(
358
1
                            Metadata::new(),
359
1
                            Moo::new(x_e.clone()),
360
1
                            Moo::new(x_prev.clone())
361
1
                        ),
362
1
                        Expression::Geq(
363
1
                            Metadata::new(),
364
1
                            Moo::new(y_e.clone()),
365
1
                            Moo::new(y_prev.clone())
366
1
                        ),
367
1
                        Expression::Or(
368
1
                            Metadata::new(),
369
1
                            Moo::new(matrix_expr!(
370
1
                                Expression::Lt(Metadata::new(), Moo::new(x_e), Moo::new(x_prev)),
371
1
                                Expression::Gt(Metadata::new(), Moo::new(y_e), Moo::new(y_prev))
372
1
                            ))
373
1
                        )
374
1
                    ))
375
1
                ))
376
1
            ))
377
        );
378
1
    }
379

            
380
    #[test]
381
1
    pub fn test_parse_pareto_over_expression_component() {
382
1
        let src = "
383
1
        find x : int(0..3)
384
1

            
385
1
        dominance relation
386
1
            pareto(minimising x + 1)
387
1
        ";
388

            
389
1
        let (model, _source_map) = parse_essence(src).unwrap();
390
1
        let st = model.symbols();
391
1
        let x = st.lookup(&Name::user("x")).unwrap();
392
1
        let x_e = Expression::Atomic(Metadata::new(), Atom::new_ref(x.clone()));
393
1
        let x_prev = Expression::FromSolution(Metadata::new(), Moo::new(Atom::new_ref(x)));
394
1
        let one = Expression::Atomic(Metadata::new(), 1.into());
395
1
        let current = Expression::Sum(
396
1
            Metadata::new(),
397
1
            Moo::new(matrix_expr!(x_e.clone(), one.clone())),
398
1
        );
399
1
        let previous = Expression::Sum(Metadata::new(), Moo::new(matrix_expr!(x_prev, one)));
400

            
401
1
        assert_eq!(
402
            model.dominance,
403
1
            Some(Expression::DominanceRelation(
404
1
                Metadata::new(),
405
1
                Moo::new(Expression::And(
406
1
                    Metadata::new(),
407
1
                    Moo::new(matrix_expr!(
408
1
                        Expression::Leq(
409
1
                            Metadata::new(),
410
1
                            Moo::new(current.clone()),
411
1
                            Moo::new(previous.clone())
412
1
                        ),
413
1
                        Expression::Lt(Metadata::new(), Moo::new(current), Moo::new(previous))
414
1
                    ))
415
1
                ))
416
1
            ))
417
        );
418
1
    }
419
}