devalang_core/core/preprocessor/resolver/
group.rs

1use crate::core::{
2    parser::statement::{Statement, StatementKind},
3    preprocessor::{module::Module, resolver::driver::resolve_statement},
4    store::global::GlobalStore,
5};
6use devalang_types::Value;
7use devalang_utils::logger::{LogLevel, Logger};
8
9pub fn resolve_group(
10    stmt: &Statement,
11    module: &Module,
12    path: &str,
13    global_store: &mut GlobalStore,
14) -> Statement {
15    let logger = Logger::new();
16
17    // Extract identifier from several allowed shapes (map.identifier, bare string, number -> to_string)
18    let identifier = match extract_group_identifier(stmt, &logger, module) {
19        Ok(id) => id,
20        Err(err_stmt) => return err_stmt,
21    };
22
23    // group_map: if the value is a map we clone it, otherwise create an empty map to hold body
24    let group_map = match &stmt.value {
25        Value::Map(m) => m.clone(),
26        _ => std::collections::HashMap::new(),
27    };
28
29    // Ensure the identifier does not already exist
30    if global_store.variables.variables.contains_key(&identifier) {
31        return type_error(
32            &logger,
33            module,
34            stmt,
35            format!("Group identifier '{}' already exists", identifier),
36        );
37    }
38
39    // Resolve statements in the body
40    let mut resolved_map = group_map.clone();
41    if let Some(Value::Block(body)) = group_map.get("body") {
42        let resolved_body = resolve_block_statements(body, module, path, global_store);
43        resolved_map.insert("body".to_string(), Value::Block(resolved_body));
44    } else {
45        logger.log_message(LogLevel::Warning, "Group without a body");
46    }
47
48    // Build a complete Statement for the group
49    let resolved_group_stmt = Statement {
50        kind: StatementKind::Group,
51        value: Value::Map(resolved_map.clone()),
52        ..stmt.clone()
53    };
54
55    // Store the Statement directly in the global variable_table
56    global_store.variables.variables.insert(
57        identifier.clone(),
58        Value::Statement(Box::new(resolved_group_stmt.clone())),
59    );
60
61    resolved_group_stmt
62}
63
64fn resolve_block_statements(
65    body: &[Statement],
66    module: &Module,
67    path: &str,
68    global_store: &mut GlobalStore,
69) -> Vec<Statement> {
70    body.iter()
71        .map(|stmt| resolve_statement(stmt, module, path, global_store))
72        .collect()
73}
74
75fn type_error(logger: &Logger, module: &Module, stmt: &Statement, message: String) -> Statement {
76    let stacktrace = format!("{}:{}:{}", module.path, stmt.line, stmt.column);
77    logger.log_error_with_stacktrace(&message, &stacktrace);
78
79    Statement {
80        kind: StatementKind::Error { message },
81        value: Value::Null,
82        ..stmt.clone()
83    }
84}
85
86// Helper to extract a group identifier from multiple Value forms
87fn extract_group_identifier(
88    stmt: &Statement,
89    logger: &Logger,
90    module: &Module,
91) -> Result<String, Statement> {
92    match &stmt.value {
93        Value::Map(map) => match map.get("identifier") {
94            Some(Value::String(s)) => Ok(s.clone()),
95            Some(Value::Identifier(s)) => Ok(s.clone()),
96            Some(Value::Number(n)) => Ok(n.to_string()),
97            Some(other) => Err(type_error(
98                logger,
99                module,
100                stmt,
101                format!("Unsupported type for 'identifier': {:?}", other),
102            )),
103            None => Err(type_error(
104                logger,
105                module,
106                stmt,
107                "Group statement must have an 'identifier' field".to_string(),
108            )),
109        },
110        Value::String(s) => Ok(s.clone()),
111        Value::Identifier(s) => Ok(s.clone()),
112        other => Err(type_error(
113            logger,
114            module,
115            stmt,
116            format!(
117                "Expected a map or string for group statement, found {:?}",
118                other
119            ),
120        )),
121    }
122}