Skip to main content

workshop_rs/wir/
mod.rs

1//! The Workshop IR model.
2//!
3//! Workshop IR models the lower-level workshop program structure: variables
4//! with indexes, subroutines with indexes, and rules with events, conditions,
5//! actions, and values. It is locale-independent (canonical catalog ids only,
6//! never localized spellings) and protocol-agnostic.
7//!
8//! Name policy: call/value `name` fields keep the canonical catalog ids
9//! (`countOf`, `wait`, `createBeamEffect`); mapping those to localized
10//! Workshop presentation spellings is an emission concern.
11//!
12//! Extracted from the Wright-authored `wright-ir` crate (the `wir`,
13//! `settings`, and `source` modules); see
14//! [`docs/provenance.md`](https://github.com/wrightkit/workshop-rs/blob/main/docs/provenance.md).
15
16mod action;
17mod dump;
18mod event;
19mod rule;
20mod validate;
21mod value;
22
23pub mod error;
24
25pub use action::{Action, IfBranch, ModifyOp};
26pub use event::{Event, EventTarget, EventTeam, PlayerEventKind};
27pub use rule::{Rule, WorkshopSubroutine, WorkshopVariable};
28pub use value::{Value, ValueNode};
29
30/// The WIR-owned capability surface used by the canonical census. Providers
31/// do not contribute source-language inventories to this registry.
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum CensusCapabilityKind {
34    Variable,
35    PlayerVariable,
36    Subroutine,
37    ControlFlow,
38    String,
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub struct CensusCapability {
43    pub kind: CensusCapabilityKind,
44    pub name: &'static str,
45}
46
47pub const CENSUS_CAPABILITIES: &[CensusCapability] = &[
48    CensusCapability {
49        kind: CensusCapabilityKind::Variable,
50        name: "global",
51    },
52    CensusCapability {
53        kind: CensusCapabilityKind::PlayerVariable,
54        name: "player",
55    },
56    CensusCapability {
57        kind: CensusCapabilityKind::Subroutine,
58        name: "declaration-and-call",
59    },
60    CensusCapability {
61        kind: CensusCapabilityKind::ControlFlow,
62        name: "if",
63    },
64    CensusCapability {
65        kind: CensusCapabilityKind::ControlFlow,
66        name: "else-if",
67    },
68    CensusCapability {
69        kind: CensusCapabilityKind::ControlFlow,
70        name: "else",
71    },
72    CensusCapability {
73        kind: CensusCapabilityKind::ControlFlow,
74        name: "while",
75    },
76    CensusCapability {
77        kind: CensusCapabilityKind::ControlFlow,
78        name: "for-global-variable",
79    },
80    CensusCapability {
81        kind: CensusCapabilityKind::String,
82        name: "custom-string",
83    },
84];
85
86use crate::core::arena::Arena;
87use crate::core::ids::Id;
88use crate::core::source::SourceFile;
89
90/// A typed ID referencing a [`WorkshopVariable`] in the global table.
91pub type GlobalVarId = Id<WorkshopVariable>;
92/// A typed ID referencing a [`WorkshopVariable`] in the player table.
93pub type PlayerVarId = Id<WorkshopVariable>;
94/// A typed ID referencing a [`WorkshopSubroutine`].
95pub type SubroutineId = Id<WorkshopSubroutine>;
96/// A typed ID referencing a [`Rule`].
97pub type RuleId = Id<Rule>;
98/// A typed ID referencing an [`Action`] in the action arena.
99pub type ActionId = Id<Action>;
100/// A typed ID referencing a [`ValueNode`] in the value arena.
101pub type ValueId = Id<ValueNode>;
102
103/// The Workshop IR program: tables and arenas produced by lowering.
104#[derive(Debug, Clone)]
105pub struct Program {
106    /// The source-file registry, copied from the source HIR so spans remain
107    /// resolvable for diagnostics.
108    pub files: Arena<SourceFile>,
109    /// The custom-game-settings carrier, copied inertly from the source HIR
110    /// (emitted verbatim, never lowered, #86).
111    pub settings: Option<crate::settings::Settings>,
112    pub global_variables: Arena<WorkshopVariable>,
113    pub player_variables: Arena<WorkshopVariable>,
114    pub subroutines: Arena<WorkshopSubroutine>,
115    pub rules: Arena<Rule>,
116    pub values: Arena<ValueNode>,
117    pub actions: Arena<Action>,
118}
119
120impl Default for Program {
121    fn default() -> Self {
122        Program {
123            files: Arena::new(),
124            settings: None,
125            global_variables: Arena::new(),
126            player_variables: Arena::new(),
127            subroutines: Arena::new(),
128            rules: Arena::new(),
129            values: Arena::new(),
130            actions: Arena::new(),
131        }
132    }
133}
134
135impl Program {
136    /// Add a source file and bind its optional source metadata to the returned
137    /// file ID.
138    pub fn add_file(&mut self, file: SourceFile) -> crate::source::FileId {
139        let id = self.files.push(file);
140        self.files.get_mut(id).unwrap().bind_file(id);
141        id
142    }
143
144    /// Return retained authored source for a registered file, when available.
145    pub fn source(&self, file: crate::source::FileId) -> Option<&crate::source::SourceDocument> {
146        self.files.get(file).and_then(SourceFile::source)
147    }
148
149    /// Validate structural invariants: every ID resolves and every span is
150    /// valid. Returns the first violation as a structured [`IrError`].
151    ///
152    /// [`IrError`]: crate::wir::error::IrError
153    pub fn validate(&self) -> Result<(), error::IrError> {
154        validate::validate(self)
155    }
156
157    /// Report preserved or unknown constructs separately from structural
158    /// validation so consumers cannot present analysis as definitive.
159    pub fn semantic_issues(
160        &self,
161        catalog: &crate::catalog::Catalog,
162    ) -> Vec<crate::analysis::semantic::SemanticIssue> {
163        crate::analysis::semantic::inspect(self, catalog)
164    }
165
166    /// Render a deterministic debug dump of the workshop program.
167    pub fn dump(&self) -> String {
168        dump::dump(self)
169    }
170}