Lines
64.71 %
Functions
20 %
#![allow(clippy::legacy_numeric_constants)]
use std::collections::BTreeSet;
use tree_sitter::Node;
use super::ParseContext;
use super::domain::parse_domain;
use super::util::named_children;
use crate::diagnostics::diagnostics_api::SymbolKind;
use crate::diagnostics::source_map::{HoverInfo, span_with_hover};
use crate::errors::{FatalParseError, RecoverableParseError};
use crate::expression::parse_expression;
use crate::field;
use conjure_cp_core::ast::DeclarationPtr;
use conjure_cp_core::ast::{Name, SymbolTable};
/// Parse a letting statement into a SymbolTable containing the declared symbols
pub fn parse_letting_statement(
ctx: &mut ParseContext,
letting_statement: Node,
) -> Result<Option<SymbolTable>, FatalParseError> {
let mut symbol_table = SymbolTable::new();
let mut temp_symbols = BTreeSet::new();
let variable_list = field!(letting_statement, "variable_list");
for variable in named_children(&variable_list) {
let variable_name = &ctx.source_code[variable.start_byte()..variable.end_byte()];
// Check for duplicate within the same statement
if temp_symbols.contains(variable_name) {
ctx.errors.push(RecoverableParseError::new(
format!(
"Variable '{}' is already declared in this letting statement",
variable_name
),
Some(variable.range()),
));
// don't return here, as we can still add the other variables to the symbol table
continue;
}
// Check for duplicate declaration across statements
if let Some(symbols) = &ctx.symbols
&& symbols.read().lookup(&Name::user(variable_name)).is_some()
{
"Variable '{}' is already declared in a previous statement",
temp_symbols.insert(variable_name);
let hover = HoverInfo {
description: format!("Letting variable: {variable_name}"),
kind: Some(SymbolKind::Letting),
ty: None,
decl_span: None,
};
span_with_hover(&variable, ctx.source_code, ctx.source_map, hover);
let expr_or_domain = field!(letting_statement, "expr_or_domain");
match expr_or_domain.kind() {
"bool_expr" | "arithmetic_expr" | "atom" => {
for name in temp_symbols {
let Some(expr) = parse_expression(ctx, expr_or_domain)? else {
symbol_table.insert(DeclarationPtr::new_value_letting(Name::user(name), expr));
"domain" => {
let Some(domain) = parse_domain(ctx, expr_or_domain)? else {
// If it's a record domain, add the field names to the symbol table
if let Some(entries) = domain.as_record() {
for entry in entries {
// Add each field name as a record field declaration
symbol_table.insert(DeclarationPtr::new_record_field(entry.clone()));
symbol_table.insert(DeclarationPtr::new_domain_letting(Name::user(name), domain));
_ => {
return Err(FatalParseError::internal_error(
"Expected letting expression, got '{}'",
expr_or_domain.kind()
Some(expr_or_domain.range()),
Ok(Some(symbol_table))