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,
44}
45
46#[derive(Debug, Clone, Default)]
47struct RuleProvenance {
48 span: Option<crate::source::Span>,
49 name: Option<Span>,
51 event_name: Option<Span>,
53 identity: NodeIdentity,
55 conditions: Vec<ValueProvenance>,
56 actions: Vec<ActionProvenance>,
57}
58
59impl RuleProvenance {
60 fn reseat(&mut self, identity: NodeIdentity) {
64 if self.identity != identity {
65 self.span = None;
66 self.name = None;
67 self.event_name = None;
68 }
69 self.identity = identity;
70 }
71}
72
73#[derive(Debug, Clone, Default)]
74struct ActionProvenance {
75 span: Option<Span>,
76 identifier: Option<Span>,
79 identity: NodeIdentity,
81 arguments: Vec<ValueProvenance>,
82}
83
84trait SpanSlot {
87 fn span_slot(&mut self) -> &mut Option<Span>;
88 fn reseat(&mut self, identity: NodeIdentity);
92}
93
94impl SpanSlot for ValueProvenance {
95 fn span_slot(&mut self) -> &mut Option<Span> {
96 &mut self.span
97 }
98 fn reseat(&mut self, identity: NodeIdentity) {
99 if self.identity != identity {
100 self.span = None;
101 self.identifier = None;
102 }
103 self.identity = identity;
104 }
105}
106
107impl SpanSlot for ActionProvenance {
108 fn span_slot(&mut self) -> &mut Option<Span> {
109 &mut self.span
110 }
111 fn reseat(&mut self, identity: NodeIdentity) {
112 if self.identity != identity {
113 self.span = None;
114 self.identifier = None;
115 }
116 self.identity = identity;
117 }
118}
119
120#[derive(Debug, Clone, Default)]
123struct ValueProvenance {
124 span: Option<Span>,
125 identifier: Option<Span>,
128 identity: NodeIdentity,
130 children: Vec<ValueProvenance>,
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135#[non_exhaustive]
136pub enum SourceMappingError {
137 UnknownFile(FileId),
138 InvalidSpan(Span),
139 InvalidRule(usize),
140 InvalidCondition {
141 rule: usize,
142 condition: usize,
143 },
144 InvalidAction {
145 rule: usize,
146 action: usize,
147 },
148 InvalidActionArgument {
149 rule: usize,
150 action: usize,
151 argument: usize,
152 },
153 InvalidGlobalVariable(usize),
154 InvalidPlayerVariable(usize),
155 InvalidSubroutine(usize),
156}
157
158impl std::fmt::Display for SourceMappingError {
159 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160 match self {
161 Self::UnknownFile(file) => {
162 write!(formatter, "source span references unknown file {file}")
163 }
164 Self::InvalidSpan(span) => write!(formatter, "invalid source span {span:?}"),
165 Self::InvalidRule(rule) => write!(formatter, "invalid rule index {rule}"),
166 Self::InvalidCondition { rule, condition } => {
167 write!(
168 formatter,
169 "invalid condition index {condition} in rule {rule}"
170 )
171 }
172 Self::InvalidAction { rule, action } => {
173 write!(formatter, "invalid action index {action} in rule {rule}")
174 }
175 Self::InvalidActionArgument {
176 rule,
177 action,
178 argument,
179 } => write!(
180 formatter,
181 "invalid argument index {argument} in action {action} of rule {rule}"
182 ),
183 Self::InvalidGlobalVariable(variable) => {
184 write!(formatter, "invalid global variable index {variable}")
185 }
186 Self::InvalidPlayerVariable(variable) => {
187 write!(formatter, "invalid player variable index {variable}")
188 }
189 Self::InvalidSubroutine(subroutine) => {
190 write!(formatter, "invalid subroutine index {subroutine}")
191 }
192 }
193 }
194}
195
196impl std::error::Error for SourceMappingError {}
197
198impl Program {
199 pub fn new() -> Self {
200 Self::default()
201 }
202
203 pub fn add_file(&mut self, mut file: SourceFile) -> FileId {
205 let id = FileId::from_index(self.files.len());
206 file.bind_file(id);
207 self.files.push(file);
208 id
209 }
210
211 pub fn global_variable(&mut self, variable: Variable) -> &mut Self {
212 self.global_variables.push(variable);
213 self
214 }
215
216 pub fn player_variable(&mut self, variable: Variable) -> &mut Self {
217 self.player_variables.push(variable);
218 self
219 }
220
221 pub fn subroutine(&mut self, subroutine: Subroutine) -> &mut Self {
222 self.subroutines.push(subroutine);
223 self
224 }
225
226 pub fn rule(&mut self, rule: Rule) -> &mut Self {
227 self.rules.push(rule);
228 self
229 }
230
231 pub fn source(&self, file: FileId) -> Option<&SourceDocument> {
233 self.files.get(file.index()).and_then(SourceFile::source)
234 }
235
236 pub fn set_rule_span(
238 &mut self,
239 rule: usize,
240 span: Option<Span>,
241 ) -> std::result::Result<(), SourceMappingError> {
242 self.validate_span(span)?;
243 self.rule_provenance_mut(rule)?.span = span;
244 Ok(())
245 }
246
247 pub fn set_condition_span(
249 &mut self,
250 rule: usize,
251 condition: usize,
252 span: Option<Span>,
253 ) -> std::result::Result<(), SourceMappingError> {
254 self.set_child_span(
255 rule,
256 condition,
257 span,
258 |rule| rule.conditions.len(),
259 |rule, index| condition_identity(&rule.conditions[index]),
260 |rule, index| SourceMappingError::InvalidCondition {
261 rule,
262 condition: index,
263 },
264 |rule_data| &mut rule_data.conditions,
265 )
266 }
267
268 pub fn set_action_span(
270 &mut self,
271 rule: usize,
272 action: usize,
273 span: Option<Span>,
274 ) -> std::result::Result<(), SourceMappingError> {
275 self.set_child_span(
276 rule,
277 action,
278 span,
279 |rule| rule.actions.len(),
280 |rule, index| action_identity(&rule.actions[index]),
281 |rule, index| SourceMappingError::InvalidAction {
282 rule,
283 action: index,
284 },
285 |rule_data| &mut rule_data.actions,
286 )
287 }
288
289 #[allow(clippy::too_many_arguments)]
295 fn set_child_span<T: SpanSlot + Default>(
296 &mut self,
297 rule: usize,
298 index: usize,
299 span: Option<Span>,
300 count: impl Fn(&crate::Rule) -> usize,
301 identity: impl Fn(&crate::Rule, usize) -> NodeIdentity,
302 invalid: impl Fn(usize, usize) -> SourceMappingError,
303 slots: impl Fn(&mut RuleProvenance) -> &mut Vec<T>,
304 ) -> std::result::Result<(), SourceMappingError> {
305 self.validate_span(span)?;
306 let public = self
307 .rules
308 .get(rule)
309 .ok_or(SourceMappingError::InvalidRule(rule))?;
310 let child_count = count(public);
311 if index >= child_count {
312 return Err(invalid(rule, index));
313 }
314 let identity = identity(public, index);
315 let rule_data = self.rule_provenance_mut(rule)?;
316 fit(slots(rule_data), child_count);
317 let record = &mut slots(rule_data)[index];
318 record.reseat(identity);
319 *record.span_slot() = span;
320 Ok(())
321 }
322
323 pub fn set_action_argument_span(
325 &mut self,
326 rule: usize,
327 action: usize,
328 argument: usize,
329 span: Option<Span>,
330 ) -> std::result::Result<(), SourceMappingError> {
331 self.validate_span(span)?;
332 let (identity, argument_count) = {
333 let action_value = self
334 .rules
335 .get(rule)
336 .ok_or(SourceMappingError::InvalidRule(rule))?
337 .actions
338 .get(action)
339 .ok_or(SourceMappingError::InvalidAction { rule, action })?;
340 let argument_values = action_argument_values(action_value);
341 let Some(&argument_value) = argument_values.get(argument) else {
342 return Err(SourceMappingError::InvalidActionArgument {
343 rule,
344 action,
345 argument,
346 });
347 };
348 (value_identity(argument_value), argument_values.len())
349 };
350 let action_data = self.action_provenance_mut(rule, action)?;
351 fit(&mut action_data.arguments, argument_count);
352 let record = &mut action_data.arguments[argument];
353 record.reseat(identity);
354 record.span = span;
355 Ok(())
356 }
357
358 pub fn set_global_variable_spans(
360 &mut self,
361 variable: usize,
362 span: Option<Span>,
363 name_span: Option<Span>,
364 ) -> std::result::Result<(), SourceMappingError> {
365 self.set_declaration_spans(DeclarationTable::GlobalVariables, variable, span, name_span)
366 }
367
368 pub fn set_player_variable_spans(
370 &mut self,
371 variable: usize,
372 span: Option<Span>,
373 name_span: Option<Span>,
374 ) -> std::result::Result<(), SourceMappingError> {
375 self.set_declaration_spans(DeclarationTable::PlayerVariables, variable, span, name_span)
376 }
377
378 pub fn set_subroutine_spans(
380 &mut self,
381 subroutine: usize,
382 span: Option<Span>,
383 name_span: Option<Span>,
384 ) -> std::result::Result<(), SourceMappingError> {
385 self.set_declaration_spans(DeclarationTable::Subroutines, subroutine, span, name_span)
386 }
387
388 fn set_declaration_spans(
389 &mut self,
390 table: DeclarationTable,
391 index: usize,
392 span: Option<Span>,
393 name_span: Option<Span>,
394 ) -> std::result::Result<(), SourceMappingError> {
395 self.validate_span(span)?;
396 self.validate_span(name_span)?;
397 let count = table.count(self);
398 if index >= count {
399 return Err(table.invalid(index));
400 }
401 let identity = table.identity(self, index);
402 let slots = table.slots(self.provenance_mut());
403 fit(slots, count);
404 slots[index] = DeclarationProvenance {
405 span,
406 name_span,
407 identity,
408 };
409 Ok(())
410 }
411
412 pub fn rule_span(&self, rule: usize) -> Option<crate::source::Span> {
420 self.rule_provenance(rule)?.span
421 }
422
423 pub fn condition_span(&self, rule: usize, condition: usize) -> Option<crate::source::Span> {
425 self.condition_provenance(rule, condition)?.span
426 }
427
428 pub fn action_span(&self, rule: usize, action: usize) -> Option<crate::source::Span> {
430 self.action_provenance(rule, action)?.span
431 }
432
433 pub fn action_argument_span(
435 &self,
436 rule: usize,
437 action: usize,
438 argument: usize,
439 ) -> Option<crate::source::Span> {
440 self.action_argument_provenance(rule, action, argument)?
441 .span
442 }
443
444 pub fn global_variable_name_span(&self, variable: usize) -> Option<Span> {
451 self.declaration_name_span(
452 |provenance| &provenance.global_variables,
453 &self.global_variables,
454 variable,
455 )
456 }
457
458 pub fn player_variable_name_span(&self, variable: usize) -> Option<Span> {
461 self.declaration_name_span(
462 |provenance| &provenance.player_variables,
463 &self.player_variables,
464 variable,
465 )
466 }
467
468 pub fn subroutine_name_span(&self, subroutine: usize) -> Option<Span> {
471 self.declaration_name_span(
472 |provenance| &provenance.subroutines,
473 &self.subroutines,
474 subroutine,
475 )
476 }
477
478 pub fn action_identifier_span(&self, rule: usize, action: usize) -> Option<Span> {
490 self.action_provenance(rule, action)?.identifier
491 }
492
493 pub fn rule_name_span(&self, rule: usize) -> Option<Span> {
496 self.rule_provenance(rule)?.name
497 }
498
499 pub fn rule_event_name_span(&self, rule: usize) -> Option<Span> {
503 self.rule_provenance(rule)?.event_name
504 }
505
506 pub fn condition_value_span(
521 &self,
522 rule: usize,
523 condition: usize,
524 path: &[usize],
525 ) -> Option<crate::source::Span> {
526 let record = self.condition_value_record(rule, condition, path)?;
527 record.identifier.or(record.span)
528 }
529
530 fn condition_value_record(
535 &self,
536 rule: usize,
537 condition: usize,
538 path: &[usize],
539 ) -> Option<&ValueProvenance> {
540 let record = self.condition_record(rule, condition)?;
541 let value = &self.rules[rule].conditions[condition].value;
542 let identity = condition_identity(&self.rules[rule].conditions[condition]);
543 value_provenance_at(record, value, path, identity)
544 }
545
546 pub(crate) fn condition_value_node_span(
551 &self,
552 rule: usize,
553 condition: usize,
554 path: &[usize],
555 ) -> Option<crate::source::Span> {
556 self.condition_value_record(rule, condition, path)?.span
557 }
558
559 pub fn action_argument_value_span(
569 &self,
570 rule: usize,
571 action: usize,
572 argument: usize,
573 path: &[usize],
574 ) -> Option<crate::source::Span> {
575 let record = self.action_argument_value_record(rule, action, argument, path)?;
576 record.identifier.or(record.span)
577 }
578
579 fn action_argument_value_record(
583 &self,
584 rule: usize,
585 action: usize,
586 argument: usize,
587 path: &[usize],
588 ) -> Option<&ValueProvenance> {
589 let record = self.argument_record(rule, action, argument)?;
590 let value = action_argument_values(&self.rules[rule].actions[action])[argument];
591 value_provenance_at(record, value, path, value_identity(value))
592 }
593
594 pub(crate) fn action_argument_value_node_span(
599 &self,
600 rule: usize,
601 action: usize,
602 argument: usize,
603 path: &[usize],
604 ) -> Option<crate::source::Span> {
605 self.action_argument_value_record(rule, action, argument, path)?
606 .span
607 }
608
609 pub fn edit_source(
612 &self,
613 span: crate::source::Span,
614 replacement: impl Into<String>,
615 ) -> std::result::Result<crate::source::SourceEdit, crate::source::SourceEditError> {
616 self.source(span.file)
617 .ok_or(crate::source::SourceEditError::InvalidRange)?
618 .edit_span(span, replacement)
619 }
620
621 pub fn validate(&self) -> std::result::Result<(), WorkshopError> {
626 let storage = self.to_wir()?;
627 storage
628 .validate()
629 .map_err(|error| WorkshopError::malformed(error.to_string(), error.span()))?;
630 if let Some(settings) = &self.settings {
631 if let Some(error) = crate::settings::check_emission(settings).into_iter().next() {
632 return Err(error);
633 }
634 }
635 Ok(())
636 }
637
638 pub fn settings_diagnostics(&self) -> Vec<crate::settings::SettingsDiagnostic> {
648 self.settings
649 .as_ref()
650 .map(crate::settings::check_emission_diagnostics)
651 .unwrap_or_default()
652 }
653
654 pub fn semantic_issues(
664 &self,
665 catalog: &crate::catalog::Catalog,
666 ) -> Vec<crate::rules::SemanticIssue> {
667 crate::analysis::semantic::inspect(self, catalog)
668 }
669
670 pub fn dump(&self) -> String {
672 self.to_wir().map_or_else(
673 |error| format!("invalid program: {error}"),
674 |program| program.dump(),
675 )
676 }
677
678 fn rule_record(&self, rule: usize) -> Option<&RuleProvenance> {
684 let recorded = &self.provenance.as_deref()?.rules;
685 if recorded.len() != self.rules.len() {
686 return None;
687 }
688 recorded.get(rule)
689 }
690
691 fn rule_provenance(&self, rule: usize) -> Option<&RuleProvenance> {
695 self.rule_record(rule)
696 .filter(|record| record.identity == rule_identity(&self.rules[rule]))
697 }
698
699 fn action_record(&self, rule: usize, action: usize) -> Option<&ActionProvenance> {
700 let recorded = self.rule_record(rule)?;
701 if recorded.actions.len() != self.rules[rule].actions.len() {
702 return None;
703 }
704 recorded.actions.get(action)
705 }
706
707 fn action_provenance(&self, rule: usize, action: usize) -> Option<&ActionProvenance> {
711 self.action_record(rule, action)
712 .filter(|record| record.identity == action_identity(&self.rules[rule].actions[action]))
713 }
714
715 fn argument_record(
716 &self,
717 rule: usize,
718 action: usize,
719 argument: usize,
720 ) -> Option<&ValueProvenance> {
721 let recorded = self.action_record(rule, action)?;
722 let values = action_argument_values(&self.rules[rule].actions[action]);
723 if recorded.arguments.len() != values.len() {
724 return None;
725 }
726 recorded.arguments.get(argument)
727 }
728
729 fn action_argument_provenance(
733 &self,
734 rule: usize,
735 action: usize,
736 argument: usize,
737 ) -> Option<&ValueProvenance> {
738 let record = self.argument_record(rule, action, argument)?;
739 let values = action_argument_values(&self.rules[rule].actions[action]);
740 (record.identity == value_identity(values[argument])).then_some(record)
741 }
742
743 fn condition_record(&self, rule: usize, condition: usize) -> Option<&ValueProvenance> {
744 let recorded = self.rule_record(rule)?;
745 if recorded.conditions.len() != self.rules[rule].conditions.len() {
746 return None;
747 }
748 recorded.conditions.get(condition)
749 }
750
751 fn condition_provenance(&self, rule: usize, condition: usize) -> Option<&ValueProvenance> {
755 self.condition_record(rule, condition).filter(|record| {
756 record.identity == condition_identity(&self.rules[rule].conditions[condition])
757 })
758 }
759
760 fn declaration_name_span<T: std::fmt::Debug>(
761 &self,
762 recorded: impl Fn(&ProgramProvenance) -> &[DeclarationProvenance],
763 nodes: &[T],
764 position: usize,
765 ) -> Option<Span> {
766 let declaration = self.declaration_provenance(recorded, nodes, position);
767 declaration.name_span.or(declaration.span)
768 }
769
770 fn declaration_provenance<T: std::fmt::Debug>(
771 &self,
772 recorded: impl Fn(&ProgramProvenance) -> &[DeclarationProvenance],
773 nodes: &[T],
774 position: usize,
775 ) -> DeclarationProvenance {
776 self.provenance
777 .as_deref()
778 .map(recorded)
779 .filter(|recorded| recorded.len() == nodes.len())
780 .and_then(|recorded| recorded.get(position).zip(nodes.get(position)))
781 .filter(|(record, node)| record.identity == declaration_identity(node))
782 .map(|(record, _)| *record)
783 .unwrap_or_default()
784 }
785
786 fn validate_span(&self, span: Option<Span>) -> std::result::Result<(), SourceMappingError> {
787 let Some(span) = span else {
788 return Ok(());
789 };
790 if !span.is_valid() {
791 return Err(SourceMappingError::InvalidSpan(span));
792 }
793 if self.files.get(span.file.index()).is_none() {
794 return Err(SourceMappingError::UnknownFile(span.file));
795 }
796 Ok(())
797 }
798
799 fn provenance_mut(&mut self) -> &mut ProgramProvenance {
800 self.provenance
801 .get_or_insert_with(|| Box::new(ProgramProvenance::default()))
802 .as_mut()
803 }
804
805 fn rule_provenance_mut(
809 &mut self,
810 rule: usize,
811 ) -> std::result::Result<&mut RuleProvenance, SourceMappingError> {
812 if rule >= self.rules.len() {
813 return Err(SourceMappingError::InvalidRule(rule));
814 }
815 let identity = rule_identity(&self.rules[rule]);
816 let rule_count = self.rules.len();
817 let provenance = self.provenance_mut();
818 fit(&mut provenance.rules, rule_count);
819 let record = &mut provenance.rules[rule];
820 record.reseat(identity);
821 Ok(record)
822 }
823
824 fn action_provenance_mut(
825 &mut self,
826 rule: usize,
827 action: usize,
828 ) -> std::result::Result<&mut ActionProvenance, SourceMappingError> {
829 let public = self
830 .rules
831 .get(rule)
832 .ok_or(SourceMappingError::InvalidRule(rule))?;
833 let action_count = public.actions.len();
834 if action >= action_count {
835 return Err(SourceMappingError::InvalidAction { rule, action });
836 }
837 let identity = action_identity(&public.actions[action]);
838 let rule_data = self.rule_provenance_mut(rule)?;
839 fit(&mut rule_data.actions, action_count);
840 let record = &mut rule_data.actions[action];
841 record.reseat(identity);
842 Ok(record)
843 }
844
845 fn record_identities(&mut self) {
849 let Some(mut provenance) = self.provenance.take() else {
850 return;
851 };
852 identity::record_identities(&mut provenance, self);
853 self.provenance = Some(provenance);
854 }
855}
856
857fn value_provenance_at<'a>(
864 record: &'a ValueProvenance,
865 value: &'a Value,
866 path: &[usize],
867 root_identity: NodeIdentity,
868) -> Option<&'a ValueProvenance> {
869 let mut record = record;
870 let mut value = value;
871 let mut identity = root_identity;
872 for &index in path {
873 let children = value_children(value);
874 if record.children.len() != children.len() {
875 return None;
876 }
877 record = record.children.get(index)?;
878 value = children[index];
879 identity = value_identity(value);
880 }
881 (record.identity == identity).then_some(record)
882}
883
884fn fit<T: Default>(items: &mut Vec<T>, len: usize) {
885 items.truncate(len);
886 items.resize_with(len, T::default);
887}
888
889#[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> {
1046 match action {
1047 Action::SetGlobalVariable { value, .. } | Action::ModifyGlobalVariable { value, .. } => {
1048 vec![value]
1049 }
1050 Action::SetPlayerVariable { player, value, .. }
1051 | Action::ModifyPlayerVariable { player, value, .. } => vec![player, value],
1052 Action::AssignMember { target, value, .. } => vec![target, value],
1053 Action::If { condition } | Action::ElseIf { condition } | Action::While { condition } => {
1054 vec![condition]
1055 }
1056 Action::ForGlobalVariable {
1057 start, stop, step, ..
1058 } => vec![start, stop, step],
1059 Action::ForPlayerVariable {
1060 player,
1061 start,
1062 stop,
1063 step,
1064 ..
1065 } => vec![player, start, stop, step],
1066 Action::Call { args, .. } => args.iter().collect(),
1067 Action::CallSubroutine { .. } | Action::Else | Action::End => Vec::new(),
1068 Action::Disabled { action } => action_argument_values(action),
1069 }
1070}
1071
1072pub(crate) fn value_children(value: &Value) -> Vec<&Value> {
1075 match value {
1076 Value::Array(values) => values.iter().collect(),
1077 Value::Vector { x, y, z } => vec![x.as_ref(), y.as_ref(), z.as_ref()],
1078 Value::PlayerVariable { player, .. } => vec![player.as_ref()],
1079 Value::Call { args, .. } => args.iter().collect(),
1080 _ => Vec::new(),
1081 }
1082}
1083
1084#[derive(Debug, Clone, PartialEq, Eq)]
1086pub enum Event {
1087 Global,
1088 EachPlayer,
1089 EachPlayerWithFilters {
1090 team: EventTeam,
1091 target: EventTarget,
1092 },
1093 Player {
1094 kind: PlayerEventKind,
1095 team: EventTeam,
1096 target: EventTarget,
1097 },
1098 Subroutine(String),
1099}
1100
1101#[derive(Debug, Clone)]
1104pub enum Action {
1105 SetGlobalVariable {
1106 variable: String,
1107 value: Value,
1108 },
1109 ModifyGlobalVariable {
1110 variable: String,
1111 op: ModifyOp,
1112 value: Value,
1113 },
1114 SetPlayerVariable {
1115 player: Value,
1116 variable: String,
1117 value: Value,
1118 },
1119 ModifyPlayerVariable {
1120 player: Value,
1121 variable: String,
1122 op: ModifyOp,
1123 value: Value,
1124 },
1125 AssignMember {
1126 target: Value,
1127 op: Option<ModifyOp>,
1128 value: Value,
1129 },
1130 CallSubroutine {
1131 subroutine: String,
1132 },
1133 If {
1134 condition: Value,
1135 },
1136 ElseIf {
1137 condition: Value,
1138 },
1139 Else,
1140 While {
1141 condition: Value,
1142 },
1143 ForGlobalVariable {
1144 variable: String,
1145 start: Value,
1146 stop: Value,
1147 step: Value,
1148 },
1149 ForPlayerVariable {
1150 player: Value,
1151 variable: String,
1152 start: Value,
1153 stop: Value,
1154 step: Value,
1155 },
1156 End,
1157 Disabled {
1158 action: Box<Action>,
1159 },
1160 Call {
1161 name: String,
1162 args: Vec<Value>,
1163 },
1164}
1165
1166impl Action {
1167 pub fn disabled(action: Action) -> Self {
1169 Self::Disabled {
1170 action: Box::new(action),
1171 }
1172 }
1173
1174 pub fn call(name: impl Into<String>, args: impl IntoIterator<Item = Value>) -> Self {
1176 Self::Call {
1177 name: name.into(),
1178 args: args.into_iter().collect(),
1179 }
1180 }
1181}
1182
1183#[derive(Debug, Clone)]
1185pub enum Value {
1186 Number(f64),
1187 String(String),
1188 LocalizedString(String),
1189 Bool(bool),
1190 Null,
1191 Array(Vec<Value>),
1192 Vector {
1193 x: Box<Value>,
1194 y: Box<Value>,
1195 z: Box<Value>,
1196 },
1197 Enum {
1198 value_type: String,
1199 value: String,
1200 },
1201 GlobalVariable(String),
1202 PlayerVariable {
1203 player: Box<Value>,
1204 variable: String,
1205 },
1206 Subroutine(String),
1207 EventPlayer,
1208 Call {
1209 name: String,
1210 args: Vec<Value>,
1211 },
1212}
1213
1214impl Value {
1215 pub fn number(value: f64) -> Self {
1217 Self::Number(value)
1218 }
1219
1220 pub fn string(value: impl Into<String>) -> Self {
1222 Self::String(value.into())
1223 }
1224
1225 pub fn global_variable(name: impl Into<String>) -> Self {
1226 Self::GlobalVariable(name.into())
1227 }
1228
1229 pub fn player_variable(player: Value, name: impl Into<String>) -> Self {
1230 Self::PlayerVariable {
1231 player: Box::new(player),
1232 variable: name.into(),
1233 }
1234 }
1235
1236 pub fn call(name: impl Into<String>, args: impl IntoIterator<Item = Value>) -> Self {
1238 Self::Call {
1239 name: name.into(),
1240 args: args.into_iter().collect(),
1241 }
1242 }
1243}
1244
1245impl From<bool> for Value {
1246 fn from(value: bool) -> Self {
1247 Self::Bool(value)
1248 }
1249}
1250
1251impl From<f64> for Value {
1252 fn from(value: f64) -> Self {
1253 Self::Number(value)
1254 }
1255}
1256
1257impl From<f32> for Value {
1258 fn from(value: f32) -> Self {
1259 Self::Number(f64::from(value))
1260 }
1261}
1262
1263macro_rules! impl_integer_value {
1264 ($($type:ty),+ $(,)?) => {
1265 $(
1266 impl From<$type> for Value {
1267 fn from(value: $type) -> Self {
1268 Self::Number(value as f64)
1269 }
1270 }
1271 )+
1272 };
1273}
1274
1275impl_integer_value!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
1276
1277impl From<String> for Value {
1278 fn from(value: String) -> Self {
1279 Self::String(value)
1280 }
1281}
1282
1283impl From<&str> for Value {
1284 fn from(value: &str) -> Self {
1285 Self::String(value.to_string())
1286 }
1287}
1288
1289impl<T: Into<Value>> From<Vec<T>> for Value {
1290 fn from(values: Vec<T>) -> Self {
1291 Self::Array(values.into_iter().map(Into::into).collect())
1292 }
1293}
1294
1295impl<T: Into<Value>, const N: usize> From<[T; N]> for Value {
1296 fn from(values: [T; N]) -> Self {
1297 Self::Array(values.into_iter().map(Into::into).collect())
1298 }
1299}