mod action;
mod dump;
mod event;
mod rule;
mod validate;
mod value;
pub(crate) mod error;
pub(crate) use action::{Action, IfBranch, ModifyOp};
pub(crate) use event::{Event, EventTarget, EventTeam, PlayerEventKind};
pub(crate) use rule::{Rule, WorkshopSubroutine, WorkshopVariable};
pub(crate) use value::{Value, ValueNode};
use crate::core::arena::Arena;
use crate::core::ids::Id;
use crate::core::source::SourceFile;
pub(crate) type GlobalVarId = Id<WorkshopVariable>;
pub(crate) type PlayerVarId = Id<WorkshopVariable>;
pub(crate) type SubroutineId = Id<WorkshopSubroutine>;
pub(crate) type RuleId = Id<Rule>;
pub(crate) type ActionId = Id<Action>;
pub(crate) type ValueId = Id<ValueNode>;
pub(crate) const AMBIGUOUS_ENUM_CALL: &str = "__ambiguous_enum";
#[derive(Debug, Clone)]
pub(crate) struct Program {
pub(crate) files: Arena<SourceFile>,
pub(crate) settings: Option<crate::settings::Settings>,
pub(crate) global_variables: Arena<WorkshopVariable>,
pub(crate) player_variables: Arena<WorkshopVariable>,
pub(crate) subroutines: Arena<WorkshopSubroutine>,
pub(crate) rules: Arena<Rule>,
pub(crate) values: Arena<ValueNode>,
pub(crate) actions: Arena<Action>,
}
impl Default for Program {
fn default() -> Self {
Program {
files: Arena::new(),
settings: None,
global_variables: Arena::new(),
player_variables: Arena::new(),
subroutines: Arena::new(),
rules: Arena::new(),
values: Arena::new(),
actions: Arena::new(),
}
}
}
impl Program {
pub(crate) fn add_file(&mut self, file: SourceFile) -> crate::source::FileId {
let id = self.files.push(file);
self.files.get_mut(id).unwrap().bind_file(id);
id
}
#[cfg(test)]
pub(crate) fn source(
&self,
file: crate::source::FileId,
) -> Option<&crate::source::SourceDocument> {
self.files.get(file).and_then(SourceFile::source)
}
#[cfg(test)]
pub(crate) fn semantic_issues(
&self,
catalog: &crate::catalog::Catalog,
) -> Vec<crate::rules::SemanticIssue> {
crate::analysis::semantic::inspect_wir(self, catalog)
}
pub(crate) fn validate(&self) -> Result<(), error::IrError> {
validate::validate(self)
}
pub(crate) fn dump(&self) -> String {
dump::dump(self)
}
}
pub(crate) fn ambiguous_enum_parts(
program: &Program,
value_id: ValueId,
) -> Option<(&str, &[ValueId])> {
let Value::Call { name, args } = &program.values.get(value_id)?.value else {
return None;
};
if name != AMBIGUOUS_ENUM_CALL || args.len() != 2 {
return None;
}
let Value::String(spelling) = &program.values.get(args[0])?.value else {
return None;
};
let Value::Array(candidates) = &program.values.get(args[1])?.value else {
return None;
};
Some((spelling, candidates))
}
pub(crate) fn ambiguous_enum_parts_by_args<'a>(
program: &'a Program,
args: &[ValueId],
) -> Option<(&'a str, Vec<(String, String)>)> {
if args.len() != 2 {
return None;
}
let Value::String(spelling) = &program.values.get(args[0])?.value else {
return None;
};
let Value::Array(candidate_ids) = &program.values.get(args[1])?.value else {
return None;
};
let candidates = candidate_ids
.iter()
.map(|candidate_id| {
let Value::Enum { value_type, value } = &program.values.get(*candidate_id)?.value
else {
return None;
};
Some((value_type.clone(), value.clone()))
})
.collect::<Option<Vec<_>>>()?;
Some((spelling, candidates))
}