mod lower;
#[cfg(test)]
mod tests;
use crate::diagnostic::{Diagnostic, Severity};
use crate::symbol::{
AliasId, Cardinality, EnumId, FieldId, ImportedKind, PrimitiveType, Requiredness, TypeId,
};
use std::collections::HashSet;
#[derive(Debug, Clone)]
pub struct PhenotypeModule {
pub namespace: String,
pub types: Vec<PhenotypeType>,
pub enums: Vec<EnumType>,
pub aliases: Vec<TypeAlias>,
}
#[derive(Debug, Clone)]
pub struct PhenotypeType {
pub id: TypeId,
pub singular_name: String,
pub plural_name: Option<String>,
pub fields: Vec<FieldDef>,
pub render: Vec<RenderNode>,
pub parent_context: Option<String>,
}
#[derive(Debug, Clone)]
pub struct FieldDef {
pub id: FieldId,
pub name: String,
pub requiredness: Requiredness,
pub cardinality: Cardinality,
pub ty: ValueType,
pub annotations: FieldAnnotations,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct FieldAnnotations {
pub volatile: bool,
pub segment: Option<String>,
pub elastic: Option<Elastic>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Elastic {
Drop { priority: u32 },
Truncate { marker: String },
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ValueType {
Primitive(PrimitiveType),
UserSingular(TypeId),
UserPlural { collection_of: TypeId },
Enum(EnumId),
TypeAlias(AliasId),
Imported(ImportedRef),
Union(Vec<ValueType>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ImportedRef {
pub namespace: String,
pub name: String,
pub kind: ImportedKind,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RenderNode {
Text(String),
Emit(FieldId),
ParentFieldRef {
parent_type: String,
field_name: String,
},
Join {
field: FieldId,
separator: SeparatorExpr,
},
Eol { field: Option<FieldId> },
IfSet {
field: FieldId,
body: Vec<RenderNode>,
},
IfNotEmpty {
field: FieldId,
body: Vec<RenderNode>,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SeparatorExpr {
Literal(String),
Field(FieldId),
}
#[derive(Debug, Clone)]
pub struct EnumType {
pub id: EnumId,
pub name: String,
pub members: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct TypeAlias {
pub id: AliasId,
pub name: String,
pub target: ValueType,
}
pub fn referenced_type_id_of(ty: &ValueType) -> Option<TypeId> {
match ty {
ValueType::UserSingular(id) => Some(*id),
ValueType::UserPlural { collection_of } => Some(*collection_of),
_ => None,
}
}
pub fn volatile_type_set(module: &PhenotypeModule) -> HashSet<TypeId> {
let mut volatile: HashSet<TypeId> = HashSet::new();
loop {
let mut grew = false;
for pt in &module.types {
if volatile.contains(&pt.id) {
continue;
}
let is_v = pt.fields.iter().any(|f| {
f.annotations.volatile
|| referenced_type_id_of(&f.ty).is_some_and(|t| volatile.contains(&t))
});
if is_v {
volatile.insert(pt.id);
grew = true;
}
}
if !grew {
return volatile;
}
}
}
pub fn lower(
ast: &crate::parser::phenotyper_actions::File,
table: &crate::symbol::SymbolTable,
file: &str,
) -> (PhenotypeModule, Vec<Diagnostic>) {
lower::lower_module(ast, table, file)
}
fn error(file: &str, summary: String) -> Diagnostic {
Diagnostic {
severity: Severity::Error,
summary,
file: file.to_string(),
line: 0,
col: 0,
explanation: None,
suggestion: None,
}
}