cfs-synapse-codegen-cfs 0.2.9

cFS C and Rust code generator for Synapse IDL files
Documentation
use std::collections::HashMap;

use synapse_parser::ast::{ConstDecl, Item, Literal, SynFile};

use crate::{types::ResolvedConstants, util::file_namespace};

pub(crate) struct ConstContext<'a> {
    pub(crate) local_defs: HashMap<Vec<String>, &'a ConstDecl>,
    imported_values: &'a ResolvedConstants,
}

pub(crate) fn const_context<'a>(
    file: &'a SynFile,
    imported_values: &'a ResolvedConstants,
) -> ConstContext<'a> {
    let namespace = file_namespace(file);
    let mut local_defs = HashMap::new();
    for item in &file.items {
        if let Item::Const(c) = item {
            local_defs.insert(vec![c.name.clone()], c);
            if !namespace.is_empty() {
                let mut qualified = namespace.clone();
                qualified.push(c.name.clone());
                local_defs.insert(qualified, c);
            }
        }
    }
    ConstContext {
        local_defs,
        imported_values,
    }
}

impl ConstContext<'_> {
    pub(crate) fn resolved_local_constants(&self) -> ResolvedConstants {
        self.local_defs
            .keys()
            .filter_map(|segments| {
                resolve_ident_to_u64(segments, self).map(|value| (segments.clone(), value))
            })
            .collect()
    }

    pub(crate) fn is_local_bare_ident(&self, segments: &[String]) -> bool {
        segments.len() == 1 && self.local_defs.contains_key(segments)
    }
}

pub(crate) fn resolve_literal_to_u64(lit: &Literal, constants: &ConstContext<'_>) -> Option<u64> {
    resolve_literal_to_u64_inner(lit, constants, &mut Vec::new())
}

fn resolve_literal_to_u64_inner(
    lit: &Literal,
    constants: &ConstContext<'_>,
    seen: &mut Vec<Vec<String>>,
) -> Option<u64> {
    match lit {
        Literal::Ident(segments) => resolve_ident_to_u64_inner(segments, constants, seen),
        other => literal_to_u64(other),
    }
}

pub(crate) fn resolve_ident_to_u64(
    segments: &[String],
    constants: &ConstContext<'_>,
) -> Option<u64> {
    resolve_ident_to_u64_inner(segments, constants, &mut Vec::new())
}

fn resolve_ident_to_u64_inner(
    segments: &[String],
    constants: &ConstContext<'_>,
    seen: &mut Vec<Vec<String>>,
) -> Option<u64> {
    if seen.iter().any(|s| s == segments) {
        return None;
    }
    if let Some(c) = constants.local_defs.get(segments) {
        seen.push(segments.to_vec());
        let resolved = resolve_literal_to_u64_inner(&c.value, constants, seen);
        seen.pop();
        return resolved;
    }
    constants.imported_values.get(segments).copied()
}

fn literal_to_u64(lit: &Literal) -> Option<u64> {
    match lit {
        Literal::Hex(n) => Some(*n),
        Literal::Int(n) if *n >= 0 => Some(*n as u64),
        _ => None,
    }
}