1
#![allow(clippy::legacy_numeric_constants)]
2
use std::collections::BTreeSet;
3

            
4
use tree_sitter::Node;
5

            
6
use super::domain::parse_domain;
7
use super::util::named_children;
8
use crate::errors::{FatalParseError, RecoverableParseError};
9
use crate::expression::parse_expression;
10
use conjure_cp_core::ast::DeclarationPtr;
11
use conjure_cp_core::ast::{Name, SymbolTable, SymbolTablePtr};
12

            
13
/// Parse a letting statement into a SymbolTable containing the declared symbols
14
276
pub fn parse_letting_statement(
15
276
    letting_statement: Node,
16
276
    source_code: &str,
17
276
    existing_symbols_ptr: Option<SymbolTablePtr>,
18
585
    errors: &mut Vec<RecoverableParseError>,
19
585
) -> Result<SymbolTable, FatalParseError> {
20
585
    let mut symbol_table = SymbolTable::new();
21
309

            
22
585
    let mut temp_symbols = BTreeSet::new();
23

            
24
585
    let variable_list = letting_statement
25
276
        .child_by_field_name("variable_list")
26
585
        .expect("No variable list found");
27
585
    for variable in named_children(&variable_list) {
28
585
        let variable_name = &source_code[variable.start_byte()..variable.end_byte()];
29
585
        temp_symbols.insert(variable_name);
30
585
    }
31
309

            
32
585
    let expr_or_domain = letting_statement
33
585
        .child_by_field_name("expr_or_domain")
34
585
        .expect("No domain or expression found for letting statement");
35
585
    match expr_or_domain.kind() {
36
585
        "bool_expr" | "arithmetic_expr" | "atom" => {
37
585
            for name in temp_symbols {
38
276
                symbol_table.insert(DeclarationPtr::new_value_letting(
39
585
                    Name::user(name),
40
585
                    parse_expression(
41
585
                        expr_or_domain,
42
574
                        source_code,
43
574
                        &letting_statement,
44
276
                        existing_symbols_ptr.clone(),
45
276
                        errors,
46
298
                    )?,
47
                ));
48
            }
49
11
        }
50
11
        "domain" => {
51
11
            for name in temp_symbols {
52
                let domain = parse_domain(
53
                    expr_or_domain,
54
                    source_code,
55
                    existing_symbols_ptr.clone(),
56
11
                    errors,
57
                )?;
58

            
59
                // If it's a record domain, add the field names to the symbol table
60
                if let Some(entries) = domain.as_record() {
61
11
                    for entry in entries {
62
                        // Add each field name as a record field declaration
63
11
                        symbol_table.insert(DeclarationPtr::new_record_field(entry.clone()));
64
                    }
65
                }
66

            
67
                symbol_table.insert(DeclarationPtr::new_domain_letting(Name::user(name), domain));
68
            }
69
        }
70
        _ => {
71
            return Err(FatalParseError::syntax_error(
72
                format!(
73
                    "Expected letting expression, got '{}'",
74
                    expr_or_domain.kind()
75
                ),
76
                Some(expr_or_domain.range()),
77
309
            ));
78
309
        }
79
    }
80

            
81
276
    Ok(symbol_table)
82
276
}