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