conjure_cp_essence_parser/parser/
letting.rs1#![allow(clippy::legacy_numeric_constants)]
2use std::collections::BTreeSet;
3
4use tree_sitter::Node;
5
6use super::ParseContext;
7use super::domain::parse_domain;
8use super::util::named_children;
9use crate::diagnostics::diagnostics_api::SymbolKind;
10use crate::diagnostics::source_map::{HoverInfo, span_with_hover};
11use crate::errors::{FatalParseError, RecoverableParseError};
12use crate::expression::parse_expression;
13use crate::field;
14use conjure_cp_core::ast::DeclarationPtr;
15use conjure_cp_core::ast::{Name, SymbolTable};
16
17pub fn parse_letting_statement(
19 ctx: &mut ParseContext,
20 letting_statement: Node,
21) -> Result<Option<SymbolTable>, FatalParseError> {
22 let mut symbol_table = SymbolTable::new();
23
24 let mut temp_symbols = BTreeSet::new();
25
26 let variable_list = field!(letting_statement, "variable_list");
27 for variable in named_children(&variable_list) {
28 let variable_name = &ctx.source_code[variable.start_byte()..variable.end_byte()];
29
30 if temp_symbols.contains(variable_name) {
32 ctx.errors.push(RecoverableParseError::new(
33 format!(
34 "Variable '{}' is already declared in this letting statement",
35 variable_name
36 ),
37 Some(variable.range()),
38 ));
39 continue;
41 }
42
43 if let Some(symbols) = &ctx.symbols
45 && symbols.read().lookup(&Name::user(variable_name)).is_some()
46 {
47 ctx.errors.push(RecoverableParseError::new(
48 format!(
49 "Variable '{}' is already declared in a previous statement",
50 variable_name
51 ),
52 Some(variable.range()),
53 ));
54 continue;
56 }
57
58 temp_symbols.insert(variable_name);
59 let hover = HoverInfo {
60 description: format!("Letting variable: {variable_name}"),
61 kind: Some(SymbolKind::Letting),
62 ty: None,
63 decl_span: None,
64 };
65 span_with_hover(&variable, ctx.source_code, ctx.source_map, hover);
66 }
67
68 let expr_or_domain = field!(letting_statement, "expr_or_domain");
69 match expr_or_domain.kind() {
70 "bool_expr" | "arithmetic_expr" | "atom" => {
71 for name in temp_symbols {
72 let Some(expr) = parse_expression(ctx, expr_or_domain)? else {
73 continue;
74 };
75 symbol_table.insert(DeclarationPtr::new_value_letting(Name::user(name), expr));
76 }
77 }
78 "domain" => {
79 for name in temp_symbols {
80 let Some(domain) = parse_domain(ctx, expr_or_domain)? else {
81 continue;
82 };
83
84 if let Some(entries) = domain.as_record() {
86 for entry in entries {
87 symbol_table.insert(DeclarationPtr::new_record_field(entry.clone()));
89 }
90 }
91
92 symbol_table.insert(DeclarationPtr::new_domain_letting(Name::user(name), domain));
93 }
94 }
95 _ => {
96 return Err(FatalParseError::internal_error(
97 format!(
98 "Expected letting expression, got '{}'",
99 expr_or_domain.kind()
100 ),
101 Some(expr_or_domain.range()),
102 ));
103 }
104 }
105
106 Ok(Some(symbol_table))
107}