1use self::identity::{
4 NodeIdentity, action_identity, condition_identity, declaration_identity, rule_identity,
5 value_identity,
6};
7use crate::core::error::WorkshopError;
8use crate::settings::Settings;
9use crate::source::{FileId, SourceDocument, SourceFile, Span};
10
11mod conversion;
12mod identity;
13pub(crate) mod shared;
14mod source_map;
15pub use shared::{EventTarget, EventTeam, ModifyOp, PlayerEventKind};
16pub use source_map::{MAPPED_TEXT_V1, MappedText, SourceMap, SourceMapError, TEXT_V1};
17
18#[derive(Debug, Clone, Default)]
20pub struct Program {
21 pub settings: Option<Settings>,
22 pub global_variables: Vec<Variable>,
23 pub player_variables: Vec<Variable>,
24 pub subroutines: Vec<Subroutine>,
25 pub rules: Vec<Rule>,
26 files: Vec<SourceFile>,
27 provenance: Option<Box<ProgramProvenance>>,
28}
29
30#[derive(Debug, Clone, Default)]
31struct ProgramProvenance {
32 global_variables: Vec<DeclarationProvenance>,
33 player_variables: Vec<DeclarationProvenance>,
34 subroutines: Vec<DeclarationProvenance>,
35 rules: Vec<RuleProvenance>,
36}
37
38#[derive(Debug, Clone, Copy, Default)]
39struct DeclarationProvenance {
40 span: Option<Span>,
41 name_span: Option<Span>,
42 identity: NodeIdentity,
43}
44
45#[derive(Debug, Clone, Default)]
46struct RuleProvenance {
47 span: Option<crate::source::Span>,
48 name: Option<Span>,
49 event_name: Option<Span>,
50 identity: NodeIdentity,
51 conditions: Vec<ValueProvenance>,
52 actions: Vec<ActionProvenance>,
53}
54
55impl RuleProvenance {
56 fn reseat(&mut self, identity: NodeIdentity) {
57 if self.identity != identity {
58 self.span = None;
59 self.name = None;
60 self.event_name = None;
61 }
62 self.identity = identity;
63 }
64}
65
66#[derive(Debug, Clone, Default)]
67struct ActionProvenance {
68 span: Option<Span>,
69 identifier: Option<Span>,
70 identity: NodeIdentity,
71 arguments: Vec<ValueProvenance>,
72}
73
74trait SpanSlot {
75 fn span_slot(&mut self) -> &mut Option<Span>;
76 fn reseat(&mut self, identity: NodeIdentity);
77}
78
79impl SpanSlot for ValueProvenance {
80 fn span_slot(&mut self) -> &mut Option<Span> {
81 &mut self.span
82 }
83 fn reseat(&mut self, identity: NodeIdentity) {
84 if self.identity != identity {
85 self.span = None;
86 self.identifier = None;
87 }
88 self.identity = identity;
89 }
90}
91
92impl SpanSlot for ActionProvenance {
93 fn span_slot(&mut self) -> &mut Option<Span> {
94 &mut self.span
95 }
96 fn reseat(&mut self, identity: NodeIdentity) {
97 if self.identity != identity {
98 self.span = None;
99 self.identifier = None;
100 }
101 self.identity = identity;
102 }
103}
104
105fn prune_spans_outside(
106 provenance: &mut ProgramProvenance,
107 file: FileId,
108 document: &SourceDocument,
109) {
110 fn prune_slot(slot: &mut Option<Span>, file: FileId, document: &SourceDocument) {
111 if let Some(span) = slot {
112 if span.file == file && document.byte_range(*span).is_none() {
113 *slot = None;
114 }
115 }
116 }
117 fn prune_value(record: &mut ValueProvenance, file: FileId, document: &SourceDocument) {
118 prune_slot(&mut record.span, file, document);
119 prune_slot(&mut record.identifier, file, document);
120 for child in &mut record.children {
121 prune_value(child, file, document);
122 }
123 }
124 for declaration in provenance
125 .global_variables
126 .iter_mut()
127 .chain(&mut provenance.player_variables)
128 .chain(&mut provenance.subroutines)
129 {
130 prune_slot(&mut declaration.span, file, document);
131 prune_slot(&mut declaration.name_span, file, document);
132 }
133 for rule in &mut provenance.rules {
134 prune_slot(&mut rule.span, file, document);
135 prune_slot(&mut rule.name, file, document);
136 prune_slot(&mut rule.event_name, file, document);
137 for condition in &mut rule.conditions {
138 prune_value(condition, file, document);
139 }
140 for action in &mut rule.actions {
141 prune_slot(&mut action.span, file, document);
142 prune_slot(&mut action.identifier, file, document);
143 for argument in &mut action.arguments {
144 prune_value(argument, file, document);
145 }
146 }
147 }
148}
149
150#[derive(Debug, Clone, Default)]
151struct ValueProvenance {
152 span: Option<Span>,
153 identifier: Option<Span>,
154 identity: NodeIdentity,
155 children: Vec<ValueProvenance>,
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq)]
160#[non_exhaustive]
161pub enum SourceMappingError {
162 UnknownFile(FileId),
163 InvalidSpan(Span),
164 InvalidRule(usize),
165 InvalidCondition {
166 rule: usize,
167 condition: usize,
168 },
169 InvalidAction {
170 rule: usize,
171 action: usize,
172 },
173 InvalidActionArgument {
174 rule: usize,
175 action: usize,
176 argument: usize,
177 },
178 InvalidGlobalVariable(usize),
179 InvalidPlayerVariable(usize),
180 InvalidSubroutine(usize),
181}
182
183impl std::fmt::Display for SourceMappingError {
184 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185 match self {
186 Self::UnknownFile(file) => {
187 write!(formatter, "source span references unknown file {file}")
188 }
189 Self::InvalidSpan(span) => write!(formatter, "invalid source span {span:?}"),
190 Self::InvalidRule(rule) => write!(formatter, "invalid rule index {rule}"),
191 Self::InvalidCondition { rule, condition } => {
192 write!(
193 formatter,
194 "invalid condition index {condition} in rule {rule}"
195 )
196 }
197 Self::InvalidAction { rule, action } => {
198 write!(formatter, "invalid action index {action} in rule {rule}")
199 }
200 Self::InvalidActionArgument {
201 rule,
202 action,
203 argument,
204 } => write!(
205 formatter,
206 "invalid argument index {argument} in action {action} of rule {rule}"
207 ),
208 Self::InvalidGlobalVariable(variable) => {
209 write!(formatter, "invalid global variable index {variable}")
210 }
211 Self::InvalidPlayerVariable(variable) => {
212 write!(formatter, "invalid player variable index {variable}")
213 }
214 Self::InvalidSubroutine(subroutine) => {
215 write!(formatter, "invalid subroutine index {subroutine}")
216 }
217 }
218 }
219}
220
221impl std::error::Error for SourceMappingError {}
222
223impl Program {
224 pub fn new() -> Self {
225 Self::default()
226 }
227
228 pub fn add_file(&mut self, mut file: SourceFile) -> FileId {
230 let id = FileId::from_index(self.files.len());
231 file.bind_file(id);
232 self.files.push(file);
233 id
234 }
235
236 pub fn global_variable(&mut self, variable: Variable) -> &mut Self {
237 self.global_variables.push(variable);
238 self
239 }
240
241 pub fn player_variable(&mut self, variable: Variable) -> &mut Self {
242 self.player_variables.push(variable);
243 self
244 }
245
246 pub fn subroutine(&mut self, subroutine: Subroutine) -> &mut Self {
247 self.subroutines.push(subroutine);
248 self
249 }
250
251 pub fn rule(&mut self, rule: Rule) -> &mut Self {
252 self.rules.push(rule);
253 self
254 }
255
256 pub fn source(&self, file: FileId) -> Option<&SourceDocument> {
258 self.files.get(file.index()).and_then(SourceFile::source)
259 }
260
261 pub fn set_file_source(&mut self, file: FileId, source: impl Into<String>) -> bool {
273 let Some(entry) = self.files.get_mut(file.index()) else {
274 return false;
275 };
276 entry.set_source(source);
277 if let (Some(provenance), Some(document)) = (
278 self.provenance.as_mut(),
279 self.files.get(file.index()).and_then(SourceFile::source),
280 ) {
281 prune_spans_outside(provenance, file, document);
282 }
283 true
284 }
285
286 pub fn set_rule_span(
288 &mut self,
289 rule: usize,
290 span: Option<Span>,
291 ) -> std::result::Result<(), SourceMappingError> {
292 self.validate_span(span)?;
293 self.rule_provenance_mut(rule)?.span = span;
294 Ok(())
295 }
296
297 pub fn set_condition_span(
299 &mut self,
300 rule: usize,
301 condition: usize,
302 span: Option<Span>,
303 ) -> std::result::Result<(), SourceMappingError> {
304 self.set_child_span(
305 rule,
306 condition,
307 span,
308 |rule| rule.conditions.len(),
309 |rule, index| condition_identity(&rule.conditions[index]),
310 |rule, index| SourceMappingError::InvalidCondition {
311 rule,
312 condition: index,
313 },
314 |rule_data| &mut rule_data.conditions,
315 )
316 }
317
318 pub fn set_action_span(
320 &mut self,
321 rule: usize,
322 action: usize,
323 span: Option<Span>,
324 ) -> std::result::Result<(), SourceMappingError> {
325 self.set_child_span(
326 rule,
327 action,
328 span,
329 |rule| rule.actions.len(),
330 |rule, index| action_identity(&rule.actions[index]),
331 |rule, index| SourceMappingError::InvalidAction {
332 rule,
333 action: index,
334 },
335 |rule_data| &mut rule_data.actions,
336 )
337 }
338
339 #[allow(clippy::too_many_arguments)]
340 fn set_child_span<T: SpanSlot + Default>(
341 &mut self,
342 rule: usize,
343 index: usize,
344 span: Option<Span>,
345 count: impl Fn(&crate::Rule) -> usize,
346 identity: impl Fn(&crate::Rule, usize) -> NodeIdentity,
347 invalid: impl Fn(usize, usize) -> SourceMappingError,
348 slots: impl Fn(&mut RuleProvenance) -> &mut Vec<T>,
349 ) -> std::result::Result<(), SourceMappingError> {
350 self.validate_span(span)?;
351 let public = self
352 .rules
353 .get(rule)
354 .ok_or(SourceMappingError::InvalidRule(rule))?;
355 let child_count = count(public);
356 if index >= child_count {
357 return Err(invalid(rule, index));
358 }
359 let identity = identity(public, index);
360 let rule_data = self.rule_provenance_mut(rule)?;
361 fit(slots(rule_data), child_count);
362 let record = &mut slots(rule_data)[index];
363 record.reseat(identity);
364 *record.span_slot() = span;
365 Ok(())
366 }
367
368 pub fn set_action_argument_span(
370 &mut self,
371 rule: usize,
372 action: usize,
373 argument: usize,
374 span: Option<Span>,
375 ) -> std::result::Result<(), SourceMappingError> {
376 self.validate_span(span)?;
377 let (identity, argument_count) = {
378 let action_value = self
379 .rules
380 .get(rule)
381 .ok_or(SourceMappingError::InvalidRule(rule))?
382 .actions
383 .get(action)
384 .ok_or(SourceMappingError::InvalidAction { rule, action })?;
385 let argument_values = action_argument_values(action_value);
386 let Some(&argument_value) = argument_values.get(argument) else {
387 return Err(SourceMappingError::InvalidActionArgument {
388 rule,
389 action,
390 argument,
391 });
392 };
393 (value_identity(argument_value), argument_values.len())
394 };
395 let action_data = self.action_provenance_mut(rule, action)?;
396 fit(&mut action_data.arguments, argument_count);
397 let record = &mut action_data.arguments[argument];
398 record.reseat(identity);
399 record.span = span;
400 Ok(())
401 }
402
403 pub fn set_global_variable_spans(
405 &mut self,
406 variable: usize,
407 span: Option<Span>,
408 name_span: Option<Span>,
409 ) -> std::result::Result<(), SourceMappingError> {
410 self.set_declaration_spans(DeclarationTable::GlobalVariables, variable, span, name_span)
411 }
412
413 pub fn set_player_variable_spans(
415 &mut self,
416 variable: usize,
417 span: Option<Span>,
418 name_span: Option<Span>,
419 ) -> std::result::Result<(), SourceMappingError> {
420 self.set_declaration_spans(DeclarationTable::PlayerVariables, variable, span, name_span)
421 }
422
423 pub fn set_subroutine_spans(
425 &mut self,
426 subroutine: usize,
427 span: Option<Span>,
428 name_span: Option<Span>,
429 ) -> std::result::Result<(), SourceMappingError> {
430 self.set_declaration_spans(DeclarationTable::Subroutines, subroutine, span, name_span)
431 }
432
433 fn set_declaration_spans(
434 &mut self,
435 table: DeclarationTable,
436 index: usize,
437 span: Option<Span>,
438 name_span: Option<Span>,
439 ) -> std::result::Result<(), SourceMappingError> {
440 self.validate_span(span)?;
441 self.validate_span(name_span)?;
442 let count = table.count(self);
443 if index >= count {
444 return Err(table.invalid(index));
445 }
446 let identity = table.identity(self, index);
447 let slots = table.slots(self.provenance_mut());
448 fit(slots, count);
449 slots[index] = DeclarationProvenance {
450 span,
451 name_span,
452 identity,
453 };
454 Ok(())
455 }
456
457 pub fn rule_span(&self, rule: usize) -> Option<crate::source::Span> {
465 self.rule_provenance(rule)?.span
466 }
467
468 pub fn condition_span(&self, rule: usize, condition: usize) -> Option<crate::source::Span> {
470 self.condition_provenance(rule, condition)?.span
471 }
472
473 pub fn action_span(&self, rule: usize, action: usize) -> Option<crate::source::Span> {
475 self.action_provenance(rule, action)?.span
476 }
477
478 pub fn action_argument_span(
480 &self,
481 rule: usize,
482 action: usize,
483 argument: usize,
484 ) -> Option<crate::source::Span> {
485 self.action_argument_provenance(rule, action, argument)?
486 .span
487 }
488
489 pub fn global_variable_name_span(&self, variable: usize) -> Option<Span> {
496 self.declaration_name_span(
497 |provenance| &provenance.global_variables,
498 &self.global_variables,
499 variable,
500 )
501 }
502
503 pub fn player_variable_name_span(&self, variable: usize) -> Option<Span> {
506 self.declaration_name_span(
507 |provenance| &provenance.player_variables,
508 &self.player_variables,
509 variable,
510 )
511 }
512
513 pub fn subroutine_name_span(&self, subroutine: usize) -> Option<Span> {
516 self.declaration_name_span(
517 |provenance| &provenance.subroutines,
518 &self.subroutines,
519 subroutine,
520 )
521 }
522
523 pub fn action_identifier_span(&self, rule: usize, action: usize) -> Option<Span> {
535 self.action_provenance(rule, action)?.identifier
536 }
537
538 pub fn rule_name_span(&self, rule: usize) -> Option<Span> {
541 self.rule_provenance(rule)?.name
542 }
543
544 pub fn rule_event_name_span(&self, rule: usize) -> Option<Span> {
548 self.rule_provenance(rule)?.event_name
549 }
550
551 pub fn condition_value_span(
566 &self,
567 rule: usize,
568 condition: usize,
569 path: &[usize],
570 ) -> Option<crate::source::Span> {
571 let record = self.condition_value_record(rule, condition, path)?;
572 record.identifier.or(record.span)
573 }
574
575 fn condition_value_record(
576 &self,
577 rule: usize,
578 condition: usize,
579 path: &[usize],
580 ) -> Option<&ValueProvenance> {
581 let record = self.condition_record(rule, condition)?;
582 let value = &self.rules[rule].conditions[condition].value;
583 let identity = condition_identity(&self.rules[rule].conditions[condition]);
584 value_provenance_at(record, value, path, identity)
585 }
586
587 pub(crate) fn condition_value_node_span(
588 &self,
589 rule: usize,
590 condition: usize,
591 path: &[usize],
592 ) -> Option<crate::source::Span> {
593 self.condition_value_record(rule, condition, path)?.span
594 }
595
596 pub fn action_argument_value_span(
606 &self,
607 rule: usize,
608 action: usize,
609 argument: usize,
610 path: &[usize],
611 ) -> Option<crate::source::Span> {
612 let record = self.action_argument_value_record(rule, action, argument, path)?;
613 record.identifier.or(record.span)
614 }
615
616 fn action_argument_value_record(
617 &self,
618 rule: usize,
619 action: usize,
620 argument: usize,
621 path: &[usize],
622 ) -> Option<&ValueProvenance> {
623 let record = self.argument_record(rule, action, argument)?;
624 let value = action_argument_values(&self.rules[rule].actions[action])[argument];
625 value_provenance_at(record, value, path, value_identity(value))
626 }
627
628 pub(crate) fn action_argument_value_node_span(
629 &self,
630 rule: usize,
631 action: usize,
632 argument: usize,
633 path: &[usize],
634 ) -> Option<crate::source::Span> {
635 self.action_argument_value_record(rule, action, argument, path)?
636 .span
637 }
638
639 pub fn edit_source(
642 &self,
643 span: crate::source::Span,
644 replacement: impl Into<String>,
645 ) -> std::result::Result<crate::source::SourceEdit, crate::source::SourceEditError> {
646 self.source(span.file)
647 .ok_or(crate::source::SourceEditError::InvalidRange)?
648 .edit_span(span, replacement)
649 }
650
651 pub fn validate(&self) -> std::result::Result<(), WorkshopError> {
656 let storage = self.to_wir()?;
657 storage
658 .validate()
659 .map_err(|error| WorkshopError::malformed(error.to_string(), error.span()))?;
660 if let Some(settings) = &self.settings {
661 if let Some(error) = crate::settings::check_emission(settings).into_iter().next() {
662 return Err(error);
663 }
664 }
665 Ok(())
666 }
667
668 pub fn settings_diagnostics(&self) -> Vec<crate::settings::SettingsDiagnostic> {
678 self.settings
679 .as_ref()
680 .map(crate::settings::check_emission_diagnostics)
681 .unwrap_or_default()
682 }
683
684 pub fn semantic_issues(
694 &self,
695 catalog: &crate::catalog::Catalog,
696 ) -> Vec<crate::rules::SemanticIssue> {
697 crate::analysis::semantic::inspect(self, catalog)
698 }
699
700 pub fn dump(&self) -> String {
702 self.to_wir().map_or_else(
703 |error| format!("invalid program: {error}"),
704 |program| program.dump(),
705 )
706 }
707
708 fn rule_record(&self, rule: usize) -> Option<&RuleProvenance> {
709 let recorded = &self.provenance.as_deref()?.rules;
710 if recorded.len() != self.rules.len() {
711 return None;
712 }
713 recorded.get(rule)
714 }
715
716 fn rule_provenance(&self, rule: usize) -> Option<&RuleProvenance> {
717 self.rule_record(rule)
718 .filter(|record| record.identity == rule_identity(&self.rules[rule]))
719 }
720
721 fn action_record(&self, rule: usize, action: usize) -> Option<&ActionProvenance> {
722 let recorded = self.rule_record(rule)?;
723 if recorded.actions.len() != self.rules[rule].actions.len() {
724 return None;
725 }
726 recorded.actions.get(action)
727 }
728
729 fn action_provenance(&self, rule: usize, action: usize) -> Option<&ActionProvenance> {
730 self.action_record(rule, action)
731 .filter(|record| record.identity == action_identity(&self.rules[rule].actions[action]))
732 }
733
734 fn argument_record(
735 &self,
736 rule: usize,
737 action: usize,
738 argument: usize,
739 ) -> Option<&ValueProvenance> {
740 let recorded = self.action_record(rule, action)?;
741 let values = action_argument_values(&self.rules[rule].actions[action]);
742 if recorded.arguments.len() != values.len() {
743 return None;
744 }
745 recorded.arguments.get(argument)
746 }
747
748 fn action_argument_provenance(
749 &self,
750 rule: usize,
751 action: usize,
752 argument: usize,
753 ) -> Option<&ValueProvenance> {
754 let record = self.argument_record(rule, action, argument)?;
755 let values = action_argument_values(&self.rules[rule].actions[action]);
756 (record.identity == value_identity(values[argument])).then_some(record)
757 }
758
759 fn condition_record(&self, rule: usize, condition: usize) -> Option<&ValueProvenance> {
760 let recorded = self.rule_record(rule)?;
761 if recorded.conditions.len() != self.rules[rule].conditions.len() {
762 return None;
763 }
764 recorded.conditions.get(condition)
765 }
766
767 fn condition_provenance(&self, rule: usize, condition: usize) -> Option<&ValueProvenance> {
768 self.condition_record(rule, condition).filter(|record| {
769 record.identity == condition_identity(&self.rules[rule].conditions[condition])
770 })
771 }
772
773 fn declaration_name_span<T: std::fmt::Debug>(
774 &self,
775 recorded: impl Fn(&ProgramProvenance) -> &[DeclarationProvenance],
776 nodes: &[T],
777 position: usize,
778 ) -> Option<Span> {
779 let declaration = self.declaration_provenance(recorded, nodes, position);
780 declaration.name_span.or(declaration.span)
781 }
782
783 fn declaration_provenance<T: std::fmt::Debug>(
784 &self,
785 recorded: impl Fn(&ProgramProvenance) -> &[DeclarationProvenance],
786 nodes: &[T],
787 position: usize,
788 ) -> DeclarationProvenance {
789 self.provenance
790 .as_deref()
791 .map(recorded)
792 .filter(|recorded| recorded.len() == nodes.len())
793 .and_then(|recorded| recorded.get(position).zip(nodes.get(position)))
794 .filter(|(record, node)| record.identity == declaration_identity(node))
795 .map(|(record, _)| *record)
796 .unwrap_or_default()
797 }
798
799 fn validate_span(&self, span: Option<Span>) -> std::result::Result<(), SourceMappingError> {
800 let Some(span) = span else {
801 return Ok(());
802 };
803 if !span.is_valid() {
804 return Err(SourceMappingError::InvalidSpan(span));
805 }
806 if self.files.get(span.file.index()).is_none() {
807 return Err(SourceMappingError::UnknownFile(span.file));
808 }
809 Ok(())
810 }
811
812 fn provenance_mut(&mut self) -> &mut ProgramProvenance {
813 self.provenance
814 .get_or_insert_with(|| Box::new(ProgramProvenance::default()))
815 .as_mut()
816 }
817
818 fn rule_provenance_mut(
819 &mut self,
820 rule: usize,
821 ) -> std::result::Result<&mut RuleProvenance, SourceMappingError> {
822 if rule >= self.rules.len() {
823 return Err(SourceMappingError::InvalidRule(rule));
824 }
825 let identity = rule_identity(&self.rules[rule]);
826 let rule_count = self.rules.len();
827 let provenance = self.provenance_mut();
828 fit(&mut provenance.rules, rule_count);
829 let record = &mut provenance.rules[rule];
830 record.reseat(identity);
831 Ok(record)
832 }
833
834 fn action_provenance_mut(
835 &mut self,
836 rule: usize,
837 action: usize,
838 ) -> std::result::Result<&mut ActionProvenance, SourceMappingError> {
839 let public = self
840 .rules
841 .get(rule)
842 .ok_or(SourceMappingError::InvalidRule(rule))?;
843 let action_count = public.actions.len();
844 if action >= action_count {
845 return Err(SourceMappingError::InvalidAction { rule, action });
846 }
847 let identity = action_identity(&public.actions[action]);
848 let rule_data = self.rule_provenance_mut(rule)?;
849 fit(&mut rule_data.actions, action_count);
850 let record = &mut rule_data.actions[action];
851 record.reseat(identity);
852 Ok(record)
853 }
854
855 fn record_identities(&mut self) {
856 let Some(mut provenance) = self.provenance.take() else {
857 return;
858 };
859 identity::record_identities(&mut provenance, self);
860 self.provenance = Some(provenance);
861 }
862}
863
864fn value_provenance_at<'a>(
865 record: &'a ValueProvenance,
866 value: &'a Value,
867 path: &[usize],
868 root_identity: NodeIdentity,
869) -> Option<&'a ValueProvenance> {
870 let mut record = record;
871 let mut value = value;
872 let mut identity = root_identity;
873 for &index in path {
874 let children = value_children(value);
875 if record.children.len() != children.len() {
876 return None;
877 }
878 record = record.children.get(index)?;
879 value = children[index];
880 identity = value_identity(value);
881 }
882 (record.identity == identity).then_some(record)
883}
884
885fn fit<T: Default>(items: &mut Vec<T>, len: usize) {
886 items.truncate(len);
887 items.resize_with(len, T::default);
888}
889
890#[derive(Copy, Clone)]
891enum DeclarationTable {
892 GlobalVariables,
893 PlayerVariables,
894 Subroutines,
895}
896
897impl DeclarationTable {
898 fn count(self, program: &Program) -> usize {
899 match self {
900 Self::GlobalVariables => program.global_variables.len(),
901 Self::PlayerVariables => program.player_variables.len(),
902 Self::Subroutines => program.subroutines.len(),
903 }
904 }
905
906 fn invalid(self, index: usize) -> SourceMappingError {
907 match self {
908 Self::GlobalVariables => SourceMappingError::InvalidGlobalVariable(index),
909 Self::PlayerVariables => SourceMappingError::InvalidPlayerVariable(index),
910 Self::Subroutines => SourceMappingError::InvalidSubroutine(index),
911 }
912 }
913
914 fn identity(self, program: &Program, index: usize) -> NodeIdentity {
915 match self {
916 Self::GlobalVariables => declaration_identity(&program.global_variables[index]),
917 Self::PlayerVariables => declaration_identity(&program.player_variables[index]),
918 Self::Subroutines => declaration_identity(&program.subroutines[index]),
919 }
920 }
921
922 fn slots(self, provenance: &mut ProgramProvenance) -> &mut Vec<DeclarationProvenance> {
923 match self {
924 Self::GlobalVariables => &mut provenance.global_variables,
925 Self::PlayerVariables => &mut provenance.player_variables,
926 Self::Subroutines => &mut provenance.subroutines,
927 }
928 }
929}
930
931#[derive(Debug, Clone, PartialEq, Eq)]
933pub struct Variable {
934 pub name: String,
935 pub index: Option<u32>,
937}
938
939impl Variable {
940 pub fn new(name: impl Into<String>) -> Self {
941 Self {
942 name: name.into(),
943 index: None,
944 }
945 }
946
947 pub fn with_index(name: impl Into<String>, index: u32) -> Self {
948 Self {
949 name: name.into(),
950 index: Some(index),
951 }
952 }
953}
954
955#[derive(Debug, Clone, PartialEq, Eq)]
957pub struct Subroutine {
958 pub name: String,
959 pub index: Option<u32>,
961}
962
963impl Subroutine {
964 pub fn new(name: impl Into<String>) -> Self {
965 Self {
966 name: name.into(),
967 index: None,
968 }
969 }
970
971 pub fn with_index(name: impl Into<String>, index: u32) -> Self {
972 Self {
973 name: name.into(),
974 index: Some(index),
975 }
976 }
977}
978
979#[derive(Debug, Clone)]
981#[non_exhaustive]
982pub struct Rule {
983 pub name: String,
984 pub disabled: bool,
985 pub event: Event,
986 pub conditions: Vec<Condition>,
987 pub actions: Vec<Action>,
988}
989
990impl Rule {
991 pub fn new(name: impl Into<String>, event: Event) -> Self {
992 Self {
993 name: name.into(),
994 disabled: false,
995 event,
996 conditions: Vec::new(),
997 actions: Vec::new(),
998 }
999 }
1000
1001 pub fn condition(mut self, condition: impl Into<Condition>) -> Self {
1002 self.conditions.push(condition.into());
1003 self
1004 }
1005
1006 pub fn action(mut self, action: Action) -> Self {
1007 self.actions.push(action);
1008 self
1009 }
1010}
1011
1012#[derive(Debug, Clone)]
1014#[non_exhaustive]
1015pub struct Condition {
1016 pub value: Value,
1017 pub disabled: bool,
1018}
1019
1020impl Condition {
1021 pub fn new(value: Value) -> Self {
1022 Self {
1023 value,
1024 disabled: false,
1025 }
1026 }
1027
1028 pub fn disabled(value: Value) -> Self {
1029 Self {
1030 value,
1031 disabled: true,
1032 }
1033 }
1034}
1035
1036impl From<Value> for Condition {
1037 fn from(value: Value) -> Self {
1038 Self::new(value)
1039 }
1040}
1041
1042pub(crate) fn action_argument_values(action: &Action) -> Vec<&Value> {
1043 match action {
1044 Action::SetGlobalVariable { value, .. } | Action::ModifyGlobalVariable { value, .. } => {
1045 vec![value]
1046 }
1047 Action::SetPlayerVariable { player, value, .. }
1048 | Action::ModifyPlayerVariable { player, value, .. } => vec![player, value],
1049 Action::AssignMember { target, value, .. } => vec![target, value],
1050 Action::If { condition } | Action::ElseIf { condition } | Action::While { condition } => {
1051 vec![condition]
1052 }
1053 Action::ForGlobalVariable {
1054 start, stop, step, ..
1055 } => vec![start, stop, step],
1056 Action::ForPlayerVariable {
1057 player,
1058 start,
1059 stop,
1060 step,
1061 ..
1062 } => vec![player, start, stop, step],
1063 Action::Call { args, .. } => args.iter().collect(),
1064 Action::CallSubroutine { .. } | Action::Else | Action::End => Vec::new(),
1065 Action::Disabled { action } => action_argument_values(action),
1066 }
1067}
1068
1069pub(crate) fn value_children(value: &Value) -> Vec<&Value> {
1070 match value {
1071 Value::Array(values) => values.iter().collect(),
1072 Value::Vector { x, y, z } => vec![x.as_ref(), y.as_ref(), z.as_ref()],
1073 Value::PlayerVariable { player, .. } => vec![player.as_ref()],
1074 Value::Call { args, .. } => args.iter().collect(),
1075 _ => Vec::new(),
1076 }
1077}
1078
1079#[derive(Debug, Clone, PartialEq, Eq)]
1081pub enum Event {
1082 Global,
1083 EachPlayer,
1084 EachPlayerWithFilters {
1085 team: EventTeam,
1086 target: EventTarget,
1087 },
1088 Player {
1089 kind: PlayerEventKind,
1090 team: EventTeam,
1091 target: EventTarget,
1092 },
1093 Subroutine(String),
1094}
1095
1096impl Event {
1097 pub fn catalog_id(&self) -> &'static str {
1102 match self {
1103 Self::Global => "global",
1104 Self::EachPlayer | Self::EachPlayerWithFilters { .. } => "eachPlayer",
1105 Self::Player { kind, .. } => kind.catalog_id(),
1106 Self::Subroutine(_) => "subroutine",
1107 }
1108 }
1109}
1110
1111#[derive(Debug, Clone)]
1114pub enum Action {
1115 SetGlobalVariable {
1116 variable: String,
1117 value: Value,
1118 },
1119 ModifyGlobalVariable {
1120 variable: String,
1121 op: ModifyOp,
1122 value: Value,
1123 },
1124 SetPlayerVariable {
1125 player: Value,
1126 variable: String,
1127 value: Value,
1128 },
1129 ModifyPlayerVariable {
1130 player: Value,
1131 variable: String,
1132 op: ModifyOp,
1133 value: Value,
1134 },
1135 AssignMember {
1136 target: Value,
1137 op: Option<ModifyOp>,
1138 value: Value,
1139 },
1140 CallSubroutine {
1141 subroutine: String,
1142 },
1143 If {
1144 condition: Value,
1145 },
1146 ElseIf {
1147 condition: Value,
1148 },
1149 Else,
1150 While {
1151 condition: Value,
1152 },
1153 ForGlobalVariable {
1154 variable: String,
1155 start: Value,
1156 stop: Value,
1157 step: Value,
1158 },
1159 ForPlayerVariable {
1160 player: Value,
1161 variable: String,
1162 start: Value,
1163 stop: Value,
1164 step: Value,
1165 },
1166 End,
1167 Disabled {
1168 action: Box<Action>,
1169 },
1170 Call {
1171 name: String,
1172 args: Vec<Value>,
1173 },
1174}
1175
1176impl Action {
1177 pub fn disabled(action: Action) -> Self {
1179 Self::Disabled {
1180 action: Box::new(action),
1181 }
1182 }
1183
1184 pub fn call(name: impl Into<String>, args: impl IntoIterator<Item = Value>) -> Self {
1186 Self::Call {
1187 name: name.into(),
1188 args: args.into_iter().collect(),
1189 }
1190 }
1191}
1192
1193#[derive(Debug, Clone)]
1195pub enum Value {
1196 Number(f64),
1197 String(String),
1198 LocalizedString(String),
1199 Bool(bool),
1200 Null,
1201 Array(Vec<Value>),
1202 Vector {
1203 x: Box<Value>,
1204 y: Box<Value>,
1205 z: Box<Value>,
1206 },
1207 Enum {
1208 value_type: String,
1209 value: String,
1210 },
1211 GlobalVariable(String),
1212 PlayerVariable {
1213 player: Box<Value>,
1214 variable: String,
1215 },
1216 Subroutine(String),
1217 EventPlayer,
1218 Call {
1219 name: String,
1220 args: Vec<Value>,
1221 },
1222}
1223
1224impl Value {
1225 pub fn number(value: f64) -> Self {
1227 Self::Number(value)
1228 }
1229
1230 pub fn string(value: impl Into<String>) -> Self {
1232 Self::String(value.into())
1233 }
1234
1235 pub fn global_variable(name: impl Into<String>) -> Self {
1236 Self::GlobalVariable(name.into())
1237 }
1238
1239 pub fn player_variable(player: Value, name: impl Into<String>) -> Self {
1240 Self::PlayerVariable {
1241 player: Box::new(player),
1242 variable: name.into(),
1243 }
1244 }
1245
1246 pub fn call(name: impl Into<String>, args: impl IntoIterator<Item = Value>) -> Self {
1248 Self::Call {
1249 name: name.into(),
1250 args: args.into_iter().collect(),
1251 }
1252 }
1253}
1254
1255impl From<bool> for Value {
1256 fn from(value: bool) -> Self {
1257 Self::Bool(value)
1258 }
1259}
1260
1261impl From<f64> for Value {
1262 fn from(value: f64) -> Self {
1263 Self::Number(value)
1264 }
1265}
1266
1267impl From<f32> for Value {
1268 fn from(value: f32) -> Self {
1269 Self::Number(f64::from(value))
1270 }
1271}
1272
1273macro_rules! impl_integer_value {
1274 ($($type:ty),+ $(,)?) => {
1275 $(
1276 impl From<$type> for Value {
1277 fn from(value: $type) -> Self {
1278 Self::Number(value as f64)
1279 }
1280 }
1281 )+
1282 };
1283}
1284
1285impl_integer_value!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
1286
1287impl From<String> for Value {
1288 fn from(value: String) -> Self {
1289 Self::String(value)
1290 }
1291}
1292
1293impl From<&str> for Value {
1294 fn from(value: &str) -> Self {
1295 Self::String(value.to_string())
1296 }
1297}
1298
1299impl<T: Into<Value>> From<Vec<T>> for Value {
1300 fn from(values: Vec<T>) -> Self {
1301 Self::Array(values.into_iter().map(Into::into).collect())
1302 }
1303}
1304
1305impl<T: Into<Value>, const N: usize> From<[T; N]> for Value {
1306 fn from(values: [T; N]) -> Self {
1307 Self::Array(values.into_iter().map(Into::into).collect())
1308 }
1309}